7.1 Verilog 除法器设计

7.1 Verilog 除法器设计

除法器原理(定点)

和十进制除法类似,计算 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 // test

仿真结果

由图可知,2 个输入数据在延迟了和被除数相同位宽的周期数以后,输出了正确的除法结果。而且可流水式无延迟输出,符合设计。

源码下载

Download

相关推荐

马克龙“预测”法国进世界杯四强,还许下一个承诺
手机365体育网站经常打不开

马克龙“预测”法国进世界杯四强,还许下一个承诺

📅 07-10 👁️ 5252
职普融通加持,中职学校受热捧,汕头中博职校投档分数连年走高
手机365体育网站经常打不开

职普融通加持,中职学校受热捧,汕头中博职校投档分数连年走高

📅 09-26 👁️ 2354