天天看點

如何用Verilog實作最小公倍數和最大公約數的計算

題目

描述

設計一個時序電路,輸入2個無符号數,位寬可以通過參數DATA_W确定,輸出兩個數的最小公倍數和最大公約數。子產品的接口信号圖如下:

如何用Verilog實作最小公倍數和最大公約數的計算

要求使用Verilog HDL語言實作,并編寫testbench驗證子產品的功能。

輸入描述

clk:時鐘信号

rst_n:複位信号,低電平有效

A:輸入信号,位寬可以通過DATA_W指定

B:輸入信号,位寬可以通過DATA_W指定

vid_in:輸入資料有效的訓示信号

輸出描述

icm_out:輸出最小公倍數

mcd_out:輸出最大公約數

vid_out:輸出資料有效的訓示信号

分析

  • 題目要求計算最小公倍數和最大公約數,其中最大公約數可以利用輾轉相除法計算得到;
  • 而最小公倍數可以先将兩數相乘再除最大公約數得到;
  • 此外,因為輾轉相除法中用了取模和除法的運算,目前高版本的DC雖然已經支援了‘%’和‘/’的綜合,但是在實際工程中還是建議使用IP來完成計算,例如蒙哥馬利算法取模、移位減法的除法器;
  • 需要注意的是,不論是取模、除法或是輾轉相除法本身都有可能需要多時鐘周期才能完成,是以如果vld_in信号過快的有效,将影響子產品的計算,本方案中使用busy信号來屏蔽多餘的vld_in。

代碼

module example(
clk,
rst_n,
A,
B,
vld_in,
lcm_out,
mcd_out,
vld_out
);

parameter       DATA_W      =       8;

input                   clk;
input                   rst_n;
input   [DATA_W-1:0]    A;
input   [DATA_W-1:0]    B;
input                   vld_in;

output  [DATA_W-1:0]    lcm_out;
output  [DATA_W-1:0]    mcd_out;
output                  vld_out;

reg     [DATA_W-1:0]    a;
reg     [DATA_W-1:0]    b;
reg                     busy;
reg                     busy_dly;
reg     [DATA_W-1:0]    mod;

reg     [DATA_W-1:0]    mcd_out;
reg     [DATA_W*2-1:0]  lcm_out_tmp;
reg                     vld_out;


//sample A B
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        a[DATA_W-1:0] <= {(DATA_W){1'b0}};
        b[DATA_W-1:0] <= {(DATA_W){1'b0}};
    end
    else if(vld_in & !busy)begin
        a[DATA_W-1:0] <= A[DATA_W-1:0]>B[DATA_W-1:0] ? A[DATA_W-1:0] : B[DATA_W-1:0];
        b[DATA_W-1:0] <= A[DATA_W-1:0]>B[DATA_W-1:0] ? B[DATA_W-1:0] : A[DATA_W-1:0];
    end
    else if(busy_dly & busy)begin
        a[DATA_W-1:0] <= b[DATA_W-1:0];
        b[DATA_W-1:0] <= mod[DATA_W-1:0];
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        busy <= 1'b0;
    else if(busy & !(|b[DATA_W-1:0]))
        busy <= 1'b0;
    else if(vld_in & !busy)
        busy <= 1'b1;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        busy_dly <= 1'b0;
    else
        busy_dly <= busy;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        mod[DATA_W-1:0] <= {(DATA_W){1'b0}};
    else if(busy & (|b[DATA_W-1:0]))
        mod[DATA_W-1:0] <= a % b;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        mcd_out[DATA_W-1:0] <= {(DATA_W){1'b0}};
    else if(busy & !(|b[DATA_W-1:0]))
        mcd_out[DATA_W-1:0] <= a[DATA_W-1:0];
end

assign lcm_out[DATA_W-1:0] = lcm_out_tmp[DATA_W-1:0];

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        lcm_out_tmp[DATA_W*2-1:0] <= {(DATA_W*2){1'b0}};
    else if(vld_in)
        lcm_out_tmp[DATA_W*2-1:0] <= A[DATA_W-1:0] * B[DATA_W-1:0];
    else if(!busy & busy_dly)
        lcm_out_tmp[DATA_W*2-1:0] <= lcm_out_tmp[DATA_W*2-1:0] / mcd_out[DATA_W-1:0];
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        vld_out <= 1'b0;
    else if(!busy & busy_dly)
        vld_out <= 1'b1;
    else
        vld_out <= 1'b0;
end

endmodule           

仿真結果

如何用Verilog實作最小公倍數和最大公約數的計算