Verilog 除法器设计
除法运算过程如下:
- (1) 取被除数的高几位数据,位宽和除数相同(实例中是 3bit 数据)。
- (2) 将被除数高位数据与除数作比较,如果前者不小于后者,则可得到对应位的商为 1,两者做差得到第一步的余数;否则得到对应的商为 0,将前者直接作为余数。
- (3) 将上一步中的余数与被除数剩余最高位 1bit 数据拼接成新的数据,然后再和除数做比较。可以得到新的商和余数。
- (4) 重复过程 (3),直到被除数最低位数据也参与计算。
和十进制除法类似,计算 27 除以 5 的过程如下所示:
需要说明的是,商的位宽应该与被除数保持一致,因为除数有可能为1。所以上述手动计算除法的实例中,第一步做比较时,应该取数字 27 最高位 1 (3'b001) 与 3'b101 做比较。 根据此计算过程,设计位宽可配置的流水线式除法器,流水延迟周期个数与被除数位宽一致。
1. 除法器设计
单步运算设计
单步除法计算时,单步被除数位宽(信号 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 个输入数据在延迟了和被除数相同位宽的周期数以后,输出了正确的除法结果。而且可流水式无延迟输出,符合设计。
下一章:Verilog 并行 FIR 滤波器设计
FIR(Finite Impulse Response)滤波器是一种有限长单位冲激响应滤波器,又称为非递归型滤波器。</p><p>FIR 滤波器具有严格的线性相频特性,同时其单位响应是有限长的,因而是稳定的系统,在数字通信、图像处理等领域都有着广泛的应用。1. FIR 滤波器原理:FIR 滤波器是有限长单位冲击响应滤波器。