和十進(jìn)制除法類似,計(jì)算 27 除以 5 的過(guò)程如下所示:
除法運(yùn)算過(guò)程如下:
需要說(shuō)明的是,商的位寬應(yīng)該與被除數(shù)保持一致,因?yàn)槌龜?shù)有可能為1。所以上述手動(dòng)計(jì)算除法的實(shí)例中,第一步做比較時(shí),應(yīng)該取數(shù)字 27 最高位 1 (?3'b001
?) 與 ?3'b101
? 做比較。 根據(jù)此計(jì)算過(guò)程,設(shè)計(jì)位寬可配置的流水線式除法器,流水延遲周期個(gè)數(shù)與被除數(shù)位寬一致。
單步除法計(jì)算時(shí),單步被除數(shù)位寬(信號(hào) ?dividend
?)需比原始除數(shù)(信號(hào) ?divisor
?)位寬多 1bit 才不至于溢出。
為了便于流水,輸出端需要有寄存器來(lái)存儲(chǔ)原始的除數(shù)(信號(hào) ?divisor
?和 ?divisor_kp
?)和被除數(shù)信息(信號(hào) ?dividend_ci
?和 ?dividend_kp
?)。
單步的運(yùn)算結(jié)果就是得到新的 1bit 商數(shù)據(jù)(信號(hào) ?merchant
?)和余數(shù)(信號(hào) ?remainder
?)。
為了得到最后的除法結(jié)果,新的 1bit 商數(shù)據(jù)(信號(hào) ?merchant
?)還需要與上一周期的商結(jié)果(?merchant_ci
?)進(jìn)行移位累加。
單步運(yùn)算單元設(shè)計(jì)如下(文件名 divider_cell.v):
// parameter M means the actual width of divisor
module divider_cell
#(parameter N=5,
parameter M=3)
(
input clk,
input rstn,
input en,
input [M:0] dividend,
input [M-1:0] divisor,
input [N-M:0] merchant_ci , //上一級(jí)輸出的商
input [N-M-1:0] dividend_ci , //原始除數(shù)
output reg [N-M-1:0] dividend_kp, //原始被除數(shù)信息
output reg [M-1:0] divisor_kp, //原始除數(shù)信息
output reg rdy ,
output reg [N-M:0] merchant , //運(yùn)算單元輸出商
output reg [M-1:0] remainder //運(yùn)算單元輸出余數(shù)
);
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
rdy <= 'b0 ;
merchant <= 'b0 ;
remainder <= 'b0 ;
divisor_kp <= 'b0 ;
dividend_kp <= 'b0 ;
end
else if (en) begin
rdy <= 1'b1 ;
divisor_kp <= divisor ; //原始除數(shù)保持不變
dividend_kp <= dividend_ci ; //原始被除數(shù)傳遞
if (dividend >= {1'b0, divisor}) begin
merchant <= (merchant_ci<<1) + 1'b1 ; //商為1
remainder <= dividend - {1'b0, divisor} ; //求余
end
else begin
merchant <= merchant_ci<<1 ; //商為0
remainder <= dividend ; //余數(shù)不變
end
end // if (en)
else begin
rdy <= 'b0 ;
merchant <= 'b0 ;
remainder <= 'b0 ;
divisor_kp <= 'b0 ;
dividend_kp <= 'b0 ;
end
end
endmodule
將單步計(jì)算的余數(shù)(信號(hào) ?remainder
?)和原始被除數(shù)(信號(hào) ?dividend
?)對(duì)應(yīng)位的 1bit 數(shù)據(jù)重新拼接,作為新的單步被除數(shù)輸入到下一級(jí)單步除法計(jì)算單元。
其中,被除數(shù)、除數(shù)、及商的數(shù)據(jù)信息也要在下一級(jí)運(yùn)算單元中傳遞。
流水級(jí)模塊例化完成除法的設(shè)計(jì)如下(文件名 divider_man.v):
//parameter N means the actual width of dividend
//using 29/5=5...4
module divider_man
#(parameter N=5,
parameter M=3,
parameter N_ACT = M+N-1)
(
input clk,
input rstn,
input data_rdy , //數(shù)據(jù)使能
input [N-1:0] dividend, //被除數(shù)
input [M-1:0] divisor, //除數(shù)
output res_rdy ,
output [N_ACT-M:0] merchant , //商位寬:N
output [M-1:0] remainder ); //最終余數(shù)
wire [N_ACT-M-1:0] dividend_t [N_ACT-M:0] ;
wire [M-1:0] divisor_t [N_ACT-M:0] ;
wire [M-1:0] remainder_t [N_ACT-M:0];
wire [N_ACT-M:0] rdy_t ;
wire [N_ACT-M:0] merchant_t [N_ACT-M:0] ;
//初始化首個(gè)運(yùn)算單元
divider_cell #(.N(N_ACT), .M(M))
u_divider_step0
( .clk (clk),
.rstn (rstn),
.en (data_rdy),
//用被除數(shù)最高位 1bit 數(shù)據(jù)做第一次單步運(yùn)算的被除數(shù),高位補(bǔ)0
.dividend ({{(M){1'b0}}, dividend[N-1]}),
.divisor (divisor),
.merchant_ci ({(N_ACT-M+1){1'b0}}), //商初始為0
.dividend_ci (dividend[N_ACT-M-1:0]), //原始被除數(shù)
//output
.dividend_kp (dividend_t[N_ACT-M]), //原始被除數(shù)信息傳遞
.divisor_kp (divisor_t[N_ACT-M]), //原始除數(shù)信息傳遞
.rdy (rdy_t[N_ACT-M]),
.merchant (merchant_t[N_ACT-M]), //第一次商結(jié)果
.remainder (remainder_t[N_ACT-M]) //第一次余數(shù)
);
genvar i ;
generate
for(i=1; i<=N_ACT-M; i=i+1) begin: sqrt_stepx
divider_cell #(.N(N_ACT), .M(M))
u_divider_step
(.clk (clk),
.rstn (rstn),
.en (rdy_t[N_ACT-M-i+1]),
.dividend ({remainder_t[N_ACT-M-i+1], dividend_t[N_ACT-M-i+1][N_ACT-M-i]}), //余數(shù)與原始被除數(shù)單bit數(shù)據(jù)拼接
.divisor (divisor_t[N_ACT-M-i+1]),
.merchant_ci (merchant_t[N_ACT-M-i+1]),
.dividend_ci (dividend_t[N_ACT-M-i+1]),
//output
.divisor_kp (divisor_t[N_ACT-M-i]),
.dividend_kp (dividend_t[N_ACT-M-i]),
.rdy (rdy_t[N_ACT-M-i]),
.merchant (merchant_t[N_ACT-M-i]),
.remainder (remainder_t[N_ACT-M-i])
);
end // block: sqrt_stepx
endgenerate
assign res_rdy = rdy_t[0];
assign merchant = merchant_t[0]; //最后一次商結(jié)果作為最終的商
assign remainder = remainder_t[0]; //最后一次余數(shù)作為最終的余數(shù)
endmodule
testbench
取被除數(shù)位寬為 5,除數(shù)位寬為 3,testbench 中加入自校驗(yàn),描述如下:
`timescale 1ns/1ns
module test ;
parameter N = 5 ;
parameter M = 3 ;
reg clk;
reg rstn ;
reg data_rdy ;
reg [N-1:0] dividend ;
reg [M-1:0] divisor ;
wire res_rdy ;
wire [N-1:0] merchant ;
wire [M-1:0] remainder ;
//clock
always begin
clk = 0 ; #5 ;
clk = 1 ; #5 ;
end
//driver
initial begin
rstn = 1'b0 ;
#8 ;
rstn = 1'b1 ;
#55 ;
@(negedge clk ) ;
data_rdy = 1'b1 ;
dividend = 25; divisor = 5;
#10 ; dividend = 16; divisor = 3;
#10 ; dividend = 10; divisor = 4;
#10 ; dividend = 15; divisor = 1;
repeat(32) #10 dividend = dividend + 1 ;
divisor = 7;
repeat(32) #10 dividend = dividend + 1 ;
divisor = 5;
repeat(32) #10 dividend = dividend + 1 ;
divisor = 4;
repeat(32) #10 dividend = dividend + 1 ;
divisor = 6;
repeat(32) #10 dividend = dividend + 1 ;
end
//對(duì)輸入延遲,便于數(shù)據(jù)結(jié)果同周期對(duì)比,完成自校驗(yàn)
reg [N-1:0] dividend_ref [N-1:0];
reg [M-1:0] divisor_ref [N-1:0];
always @(posedge clk) begin
dividend_ref[0] <= dividend ;
divisor_ref[0] <= divisor ;
end
genvar i ;
generate
for(i=1; i<=N-1; i=i+1) begin
always @(posedge clk) begin
dividend_ref[i] <= dividend_ref[i-1];
divisor_ref[i] <= divisor_ref[i-1];
end
end
endgenerate
//自校驗(yàn)
reg error_flag ;
always @(posedge clk) begin
# 1 ;
if (merchant * divisor_ref[N-1] + remainder != dividend_ref[N-1] && res_rdy) beginb //testbench 中可直接用乘號(hào)而不考慮運(yùn)算周期
error_flag <= 1'b1 ;
end
else begin
error_flag <= 1'b0 ;
end
end
//module instantiation
divider_man #(.N(N), .M(M))
u_divider
(
.clk (clk),
.rstn (rstn),
.data_rdy (data_rdy),
.dividend (dividend),
.divisor (divisor),
.res_rdy (res_rdy),
.merchant (merchant),
.remainder (remainder));
//simulation finish
initial begin
forever begin
#100;
if ($time >= 10000) $finish ;
end
end
endmodule // test
由圖可知,2 個(gè)輸入數(shù)據(jù)在延遲了和被除數(shù)相同位寬的周期數(shù)以后,輸出了正確的除法結(jié)果。而且可流水式無(wú)延遲輸出,符合設(shè)計(jì)。
點(diǎn)擊這里下載源碼
更多建議: