題目
描述
設計一個時序電路,輸入2個無符号數,位寬可以通過參數DATA_W确定,輸出兩個數的最小公倍數和最大公約數。子產品的接口信号圖如下:
要求使用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