除法器原理(定點)
和十進制除法類似,計算 27 除以 5 的過程如下所示:
除法運算過程如下:
(1) 取被除數的高幾位數據,位寬和除數相同(實例中是 3bit 數據)。
(2) 將被除數高位數據與除數作比較,如果前者不小于后者,則可得到對應位的商為 1,兩者做差得到第一步的余數;否則得到對應為的商為 0,將前者直接作為余數。
(3) 將上一步中的余數與被除數剩余最高位 1bit 數據拼接成新的數據,然后再和除數做比較??梢缘玫叫碌纳毯陀鄶?。
(4) 重復過程 (3),直到被除數最低位數據也參與計算。
需要說明的是,商的位寬應該與被除數保持一致,因為除數有可能為1。所以上述手動計算除法的實例中,第一步做比較時,應該取數字 27 最高位 1 (3'b001) 與 3'b101 做比較。
根據此計算過程,設計位寬可配置的流水線式除法器,流水延遲周期個數與被除數位寬一致。
除法器設計
◆ 單步運算設計
單步除法計算時,單步被除數位寬(信號 dividend)需比原始除數(信號 divisor)位寬多 1bit 才不至于溢出。
為了便于流水,輸出端需要有寄存器來存儲原始的除數(信號 divisor 和 divisor_kp)和被除數信息(信號 dividend_ci 和 dividend_kp)。
單步的運算結果就是得到新的 1bit 商數據(信號 merchant)和余數(信號 remainder)。
為了得到最后的除法結果,新的 1bit 商數據(信號 merchant)還需要與上一周期的商結果(merchant_ci)進行移位累加。
單步運算單元設計如下(文件名 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 , //上一級輸出的商
input [N-M-1:0] dividend_ci , //原始除數
output reg [N-M-1:0] dividend_kp, //原始被除數信息
output reg [M-1:0] divisor_kp, //原始除數信息
output reg rdy ,
output reg [N-M:0] merchant , //運算單元輸出商
output reg [M-1:0] remainder //運算單元輸出余數
);
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 ; //原始除數保持不變
dividend_kp <= dividend_ci ; //原始被除數傳遞
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 ; //余數不變
end
end // if (en)
else begin
rdy <= 'b0 ;
merchant <= 'b0 ;
remainder <= 'b0 ;
divisor_kp <= 'b0 ;
dividend_kp <= 'b0 ;
end
end
endmodule
◆ 流水級例化
將單步計算的余數(信號 remainder)和原始被除數(信號 dividend)對應位的 1bit 數據重新拼接,作為新的單步被除數輸入到下一級單步除法計算單元。
其中,被除數、除數、及商的數據信息也要在下一級運算單元中傳遞。
流水級模塊例化完成除法的設計如下(文件名 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 , //數據使能
input [N-1:0] dividend, //被除數
input [M-1:0] divisor, //除數
output res_rdy ,
output [N_ACT-M:0] merchant , //商位寬:N
output [M-1:0] remainder ); //最終余數
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] ;
//初始化首個運算單元
divider_cell #(.N(N_ACT), .M(M))
u_divider_step0
( .clk (clk),
.rstn (rstn),
.en (data_rdy),
//用被除數最高位 1bit 數據做第一次單步運算的被除數,高位補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]), //原始被除數
//output
.dividend_kp (dividend_t[N_ACT-M]), //原始被除數信息傳遞
.divisor_kp (divisor_t[N_ACT-M]), //原始除數信息傳遞
.rdy (rdy_t[N_ACT-M]),
.merchant (merchant_t[N_ACT-M]), //第一次商結果
.remainder (remainder_t[N_ACT-M]) //第一次余數
);
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]}), //余數與原始被除數單bit數據拼接
.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]; //最后一次商結果作為最終的商
assign remainder = remainder_t[0]; //最后一次余數作為最終的余數
endmodule
◆testbench
取被除數位寬為 5,除數位寬為 3,testbench 中加入自校驗,描述如下:
`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
//對輸入延遲,便于數據結果同周期對比,完成自校驗
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
//自校驗
reg error_flag ;
always @(posedge clk) begin
# 1 ;
if (merchant * divisor_ref[N-1] + remainder != dividend_ref[N-1] && res_rdy) beginb //testbench 中可直接用乘號而不考慮運算周期
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
◆仿真結果
由圖可知,2 個輸入數據在延遲了和被除數相同位寬的周期數以后,輸出了正確的除法結果。而且可流水式無延遲輸出,符合設計。
-
寄存器
+關注
關注
31文章
5317瀏覽量
120003 -
Verilog
+關注
關注
28文章
1343瀏覽量
109983 -
除法器
+關注
關注
2文章
14瀏覽量
13888 -
CLK
+關注
關注
0文章
127瀏覽量
17125
發布評論請先 登錄
相關推薦
評論