verilog 实现8位无符号除法器

一、算法(非原创)

在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个时钟周期!!!

posted @ 2020-10-03 13:51  耐心的小黑  阅读(436)  评论(0编辑  收藏  举报