HDLFPGA学习笔记十五:verilog 8位无符号除法器实现

    科技2022-07-10  156

    一、算法(非原创)

    参考链接:https://www.cnblogs.com/lyc-seu/p/12507760.html

    在Verilog HDL语言中虽然有除的运算指令,但是除运算符中的除数必须是2的幂,因此无法实现除数为任意整数的除法,很大程度上限制了它的使用领域。并且多数综合工具对于除运算指令不能综合出令人满意的结果,有些甚至不能给予综合。即使可以综合,也需要比较多的资源。

    最简单的方法就是减法实现除法器(比如十进制中的a/b,可先比较a与b的大小,如果a>b,则商加1,a<=a-b,再进行比较大小,直到a<b,商不变,余数为a)。但这种方式通常速度比较慢,实际上更快速的方法是模拟手算除法的过程: 实际上上图演示的是二进制除法运算,跟十进制的没多大区别,只不过十进制的除法商的每一位都有0-9十种可能,因此如果采用十进制来编写除法器需要采用二分法逐个判断商的每一位上的数字,而二进制因为只有两种可能所以不需要那么麻烦(但其实两者的本质是一样的,算法的时间复杂度相同) 流程图: 根据以上所述的32位除法器的算法原理,下面来实现一下8位除法器。

    二、代码实现

    1、组合逻辑方式实现

    RTL代码 module div1 #( parameter DATAWIDTH=8 ) ( a, b, enable, shang, yushu ); input [DATAWIDTH-1:0] a; input [DATAWIDTH-1:0] b; input enable; output shang; output yushu; wire enable; reg [DATAWIDTH-1:0] shang; reg [DATAWIDTH-1:0] yushu; reg [DATAWIDTH-1:0] tempa; reg [DATAWIDTH-1:0] tempb; reg [2*DATAWIDTH-1:0] temp_a; reg [2*DATAWIDTH-1:0] temp_b; integer i; always @(a or b) begin tempa <= a; tempb <= b; end always @(tempa or tempb) begin if(enable) begin temp_a = {{DATAWIDTH{1'b0}},tempa}; temp_b = {tempb,{DATAWIDTH{1'b0}}}; for(i = 0;i < DATAWIDTH;i = i + 1) begin temp_a = temp_a<<1; if(temp_a>= temp_b) temp_a = temp_a - temp_b + 1'b1; else temp_a = temp_a; end shang = temp_a[DATAWIDTH-1:0]; yushu = temp_a[DATAWIDTH*2-1:DATAWIDTH]; end end endmodule 仿真程序 `timescale 1ns/1ns module tb_div1(); parameter DATAWIDTH = 8; reg [DATAWIDTH-1:0] a; reg [DATAWIDTH-1:0] b; reg enable; wire [DATAWIDTH-1:0] shang; wire [DATAWIDTH-1:0] yushu; initial begin enable=1; #10 a = 15; b = 6; #10 a = 165; b = 30; #10 a = 250; b =60; #10 a = 63; b = 7; #10 a = 80; b = 9; #10 a = 130; b =50; $stop; end div1 #( .DATAWIDTH ( DATAWIDTH )) u1 ( .a (a), .b (b), .enable(enable), .shang (shang), .yushu (yushu) ); endmodule 仿真结果

    可以看出,由于是组合逻辑,因此输入与输出数据之间没有延时!!!

    2、时序逻辑方式实现

    RTL代码 `timescale 1ns/1ps module div_fsm #( parameter DATAWIDTH=8 ) ( input clk , input rstn , input en , output wire ready , input [DATAWIDTH-1:0] dividend , input [DATAWIDTH-1:0] divisor , output wire [DATAWIDTH-1:0] quotient , output wire [DATAWIDTH-1:0] remainder, output wire vld_out ); parameter IDLE =0; parameter SUB =1; parameter SHIFT=2 ; parameter DONE =3; reg [DATAWIDTH*2-1:0] dividend_e ; reg [DATAWIDTH*2-1:0] divisor_e ; reg [DATAWIDTH-1:0] quotient_e ; reg [DATAWIDTH-1:0] remainder_e; reg [1:0] current_state,next_state; reg [DATAWIDTH-1:0] count; always@(posedge clk or negedge rstn) if(!rstn) current_state <= IDLE; else current_state <= next_state; always @(*) begin next_state <= 2'bx; case(current_state) IDLE: if(en) next_state <= SUB; else next_state <= IDLE; SUB: next_state <= SHIFT; SHIFT:if(count<DATAWIDTH) next_state <= SUB; else next_state <= DONE; DONE: next_state <= IDLE; endcase end always@(posedge clk or negedge rstn) begin if(!rstn)begin dividend_e <= 0; divisor_e <= 0; quotient_e <= 0; remainder_e <= 0; count <= 0; end else begin case(current_state) IDLE:begin dividend_e <= {{DATAWIDTH{1'b0}},dividend}; divisor_e <= {divisor,{DATAWIDTH{1'b0}}}; end SUB:begin if(dividend_e>=divisor_e)begin dividend_e <= dividend_e-divisor_e+1'b1; end else begin dividend_e <= dividend_e; end end SHIFT:begin if(count<DATAWIDTH)begin dividend_e <= dividend_e<<1; count <= count+1; end else begin quotient_e<= dividend_e[DATAWIDTH-1:0]; remainder_e <= dividend_e[DATAWIDTH*2-1:DATAWIDTH]; end end DONE:begin count <= 0; end endcase end end assign quotient = quotient_e; assign remainder = remainder_e; assign ready=(current_state==IDLE)? 1'b1:1'b0; assign vld_out=(current_state==DONE)? 1'b1:1'b0; endmodule 仿真程序 `timescale 1ns/1ps module div_fsm_tb(); parameter DATAWIDTH = 8; reg clk; reg rstn; reg en; wire ready; wire vld_out; reg [DATAWIDTH-1:0] dividend; reg [DATAWIDTH-1:0] divisor; wire [DATAWIDTH-1:0] quotient; wire [DATAWIDTH-1:0] remainder; always #1 clk = ~clk; integer i; initial begin clk = 1; rstn = 1; en = 0; #2 rstn = 0; #2 rstn = 1; repeat(2) @(posedge clk); for(i=0;i<10;i=i+1) begin en <= 1; dividend <= $urandom()%200; divisor <= $urandom()%100; wait (ready == 1); wait (vld_out == 1); end end div_fsm #( .DATAWIDTH ( DATAWIDTH )) u1( .clk ( clk ), .rstn ( rstn ), .en ( en ), .ready ( ready ), .dividend ( dividend ), .divisor ( divisor ), .quotient ( quotient ), .remainder ( remainder ), .vld_out ( vld_out ) ); endmodule 仿真结果

    可以看出,由于使用的是时序逻辑,因此输出与输入数据之间有一定的延时,该程序中,延时为20个时钟周期!!!

    Processed: 0.011, SQL: 8