基于减法操作除法器的算法---Verilog实现
引言
除法器在FPGA里怎么实现呢?当然不是让用“/”和“%”实现。
在Verilog HDL语言中虽然有除的运算指令,但是除运算符中的除数必须是2的幂,因此无法实现除数为任意整数的除法,很大程度上限制了它的使用领域。并且多数综合工具对于除运算指令不能综合出令人满意的结果,有些甚至不能给予综合。即使可以综合,也需要比较多的资源。对于这种情况,一般使用相应的算法来实现除法,分为两类,基于减法操作和基于乘法操作的算法。
2.1 实现算法
基于减法的除法器的算法:
对于32的无符号除法,被除数a除以除数b,他们的商和余数一定不会超过32位。首先将a转换成高32位为0,低32位为a的temp_a。把b转换成高32位为b,低32位为0的temp_b。在每个周期开始时,先将temp_a左移一位,末尾补0,然后与b比较,是否大于b,是则temp_a减去temp_b将且加上1,否则继续往下执行。上面的移位、比较和减法(视具体情况而定)要执行32次,执行结束后temp_a的高32位即为余数,低32位即为商。
假设4bit的两数相除 a/b,商和余数最多只有4位 (假设1101/0010也就是13除以2得6余1)
我们先自己做二进制除法,则首先看a的MSB,若比除数小则看前两位,大则减除数,然后看余数,以此类推直到最后看到LSB;而上述算法道理一样,a左移进前四位目的就在于从a本身的MSB开始看起,移4次则是看到LSB为止,期间若比除数大,则减去除数,注意减完以后正是此时所剩的余数。而商呢则加到了这个数的末尾,因为只要比除数大,商就是1,而商0则是直接左移了,因为会自动补0。这里比较巧因为商可以随此时的a继续左移,然后新的商会继续加到末尾。经过比对会发现移4位后左右两边分别就是余数和商。
画个简单的图:
2.2 时序逻辑verilog HDL代码
module div_timing_logic(
input I_clk,
input I_rst_p,
input I_data_valid,
input [7:0] I_data_a,
input [7:0] I_data_b,
output reg O_data_valid,
output reg [7:0] O_data_shang,
output reg [7:0] O_data_yushu
);
reg [7:0] tempa;
reg [7:0] tempb;
reg [15:0] temp_a;
reg [15:0] temp_b;
reg div_start;
reg div_start_d1;
wire div_start_neg;
reg [4:0] div_cnt;
aaa@qq.com(posedge I_clk or posedge I_rst_p)
begin
if(I_rst_p)
begin
tempa <= 8'h0;
tempb <= 8'h0;
end
else if(I_data_valid)
begin
tempa <= I_data_a;
tempb <= I_data_b;
end
else
begin
tempa <= tempa;
tempb <= tempb;
end
end
aaa@qq.com(posedge I_clk or posedge I_rst_p)
begin
if(I_rst_p)
div_start <= 1'b0;
else if(I_data_valid && div_start == 1'b0)
div_start <= 1'b1;
else if(div_cnt == 5'd16 )
div_start <= 1'b0;
else
div_start <= div_start;
end
//========================================================div_cnt
aaa@qq.com(posedge I_clk or posedge I_rst_p)
if(I_rst_p)
div_cnt <= 5'd0;
else if(div_start)
div_cnt <= div_cnt + 1;
else
div_cnt <= 5'd0;
//=======================================================
aaa@qq.com(posedge I_clk or posedge I_rst_p)
begin
if(I_rst_p)
begin
temp_a <= 16'h0;
temp_b <= 16'h0;
end
else if(div_start )
if(div_cnt == 4'd0)
begin
temp_a <= {8'h0,tempa};
temp_b <= {tempb,8'h0};
end
else if(div_cnt[0] == 1'b1)
begin
temp_a <= {temp_a[14:0],1'b0};
end
else
begin
temp_a <= (temp_a[15:8] >= temp_b[15:8])?(temp_a - temp_b + 1):temp_a;
end
else
begin
temp_a <= 16'h0;
temp_b <= 16'h0;
end
end
aaa@qq.com(posedge I_clk)
begin
div_start_d1 <= div_start;
end
assign div_start_neg = div_start_d1 & (~div_start);
aaa@qq.com(posedge I_clk or posedge I_rst_p)
begin
if(I_rst_p)
begin
O_data_valid <= 1'b0;
O_data_shang <= 1'b0;
O_data_yushu <= 1'b0;
end
else if(div_start_neg)
begin
O_data_valid <= 1'b1;
O_data_shang <= temp_a[7:0];
O_data_yushu <= temp_a[15:8];
end
else
begin
O_data_valid <= 1'b0;
O_data_shang <= 1'b0;
O_data_yushu <= 1'b0;
end
end
endmodule
testbench代码
module tb_div_timing_logic(
);
reg I_clk;
reg I_rst_p;
reg I_data_valid;
reg [7:0] I_data_a;
reg [7:0] I_data_b;
wire O_data_valid;
wire [7:0] O_data_shang;
wire [7:0] O_data_yushu;
div_timing_logic div_timing_logic_inst(
.I_clk(I_clk),
.I_rst_p(I_rst_p),
.I_data_valid(I_data_valid),
.I_data_a(I_data_a),
.I_data_b(I_data_b),
.O_data_valid(O_data_valid),
.O_data_shang(O_data_shang),
.O_data_yushu(O_data_yushu)
);
always #5 I_clk <= ~I_clk;
initial begin
I_clk = 0;
I_rst_p = 1;
I_data_valid = 0;
I_data_a = 0;
I_data_b = 0;
#10;
I_rst_p = 0;
#30;
@(posedge I_clk )
begin
I_data_valid = 1;
I_data_a = {$random}%256;
I_data_b = {$random}%20;
end
repeat(18) @(posedge I_clk) I_data_valid = 0;
@(posedge I_clk )
begin
I_data_valid = 1;
I_data_a = {$random}%256;
I_data_b = {$random}%20;
end
repeat(18) @(posedge I_clk) I_data_valid = 0;
@(posedge I_clk )
begin
I_data_valid = 1;
I_data_a = {$random}%256;
I_data_b = {$random}%20;
end
repeat(10) @(posedge I_clk) I_data_valid = 0;
@(posedge I_clk )
begin
I_data_valid = 1;
I_data_a = {$random}%256;
I_data_b = {$random}%20;
end
repeat(18) @(posedge I_clk) I_data_valid = 0;;
end
endmodule
仿真:
2.2 组合逻辑verilog HDL代码
module div_combinatory_logic
(
input [7:0] a,//被除数
input [7:0] b,//除数
output reg [7:0] y_shang,//商
output reg [7:0] y_yushu//余数
);
reg [7:0] tempa;
reg [7:0] tempb;
reg [15:0] temp_a;
reg [15:0] temp_b;
integer i;
aaa@qq.com(*)
begin
tempa = a;
tempb = b;
end
aaa@qq.com(*)
begin
temp_a = {8'h0,tempa};
temp_b = {tempb,8'h0};
for(i = 0; i < 8; i = i+1) //注意是移动8次
begin:shift_left
// temp_a = temp_a << 1 ;
temp_a = {temp_a[14:0],1'b0} ;
if(temp_a[15:8] >= temp_b[15:8] )
temp_a = temp_a - temp_b + 1;
else
temp_a = temp_a;
end
y_shang = temp_a[7:0];//商在低位
y_yushu = temp_a[15:8];//余数在高位
end
endmodule
testbench代码
module div_combinatory_logic_tb;
reg [7:0] a;
reg [7:0] b;
wire [7:0] y_shang;
wire [7:0] y_yushu;
initial begin
#10
a = {$random}%256;
b = {$random}%20;
#100
a = {$random}%256;
b = {$random}%20;
#100
a = {$random}%256;
b = {$random}%20;
#100;
a = 8'h8;
b = 8'h10;
#1000
$stop;
end
div_combinatory_logic div_inst
(
.a (a),
.b (b),
.y_shang (y_shang),
.y_yushu (y_yushu)
);
endmodule
仿真: