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