參考: https://blog.csdn.net/rill_zhen/article/details/7961937
https://www.cnblogs.com/moranhuishou0315/p/11344725.html
Verilog -- 無符號整數除法器(一)
在不使用除法的前提下,如何設計一個快速高效的除法器?
在Verilog HDL語言中雖然有除的運算指令,但是除運算符中的除數必須是2的冪,因此無法實現除數為任意整數的除法,很大程度上限制了它的使用領域。並且多數綜合工具對於除運算指令不能綜合出令人滿意的結果,有些甚至不能給予綜合。即使可以綜合,也需要比較多的資源。
最簡單的方法就是減法實現除法器(比如十進制中的a/b,可先比較a與b的大小,如果a>b,則商加1,a<=a-b,再進行比較大小,直到a<b,商不變,余數為a)。但這種方式通常速度比較慢,實際上更快速的方法是模擬手算除法的過程:
實際上上圖演示的是二進制除法運算,跟十進制的沒多大區別,只不過十進制的除法商的每一位都有0-9十種可能,因此如果采用十進制來編寫除法器需要采用二分法逐個判斷商的每一位上的數字,而二進制因為只有兩種可能所以不需要那么麻煩(但其實兩者的本質是一樣的,算法的時間復雜度相同)
流程圖:
graph LR id0(32位整數a除以b)-->id1(a的高位擴展32位) id0(32位整數a除以b)-->id2(b的低位擴展32位) id1 --> id3(a左移一位) id2 --> id4{a>=b?} id3 --> id4 id4 -- 是 --> id5(a = a-b+1) id5 -->id6{移位次數<32} id4 --否-->id6 id6 --是--> id3 id6 --否--> id7(輸出a)
最后輸出的商=a高32位,余數=a低32位
module int_div
(
input[31:0] a,
input[31:0] b,
output reg [31:0] yshang,
output reg [31:0] yyushu
);
reg[31:0] tempa;
reg[31:0] tempb;
reg[63:0] temp_a;
reg[63:0] temp_b;
integer i;
always @(a or b)
begin
tempa <= a;
tempb <= b;
end
always @(tempa or tempb)
begin
temp_a = {32'h00000000,tempa};
temp_b = {tempb,32'h00000000};
for(i = 0;i < 32;i = i + 1)
begin
temp_a = {temp_a[62:0],1'b0};
if(temp_a[63:32] >= tempb)
temp_a = temp_a - temp_b + 1'b1;
else
temp_a = temp_a;
end
yshang <= temp_a[31:0];
yyushu <= temp_a[63:32];
end
endmodule
testbench:
`timescale 1ns/1ns
module int_div_tb;
reg [31:0] a;
reg [31:0] b;
wire [31:0] yshang;
wire [31:0] yyushu;
initial
begin
#10 a = $random()%10000;
b = $random()%1000;
#100 a = $random()%1000;
b = $random()%100;
#100 a = $random()%100;
b = $random()%10;
#1000;
end
int_div div
(
.a (a),
.b (b),
.yshang (yshang),
.yyushu (yyushu)
);
initial begin
$fsdbDumpvars();
$dumpvars();
#1000 $finish;
end
endmodule
波形:
上面這種寫法其實不是一個好的寫法,因為純組合邏輯實現可能會有較大的延時,並且可能會綜合出鎖存器,因此將其設計為時序邏輯是更好的選擇,詳見下一篇“Verilog -- 任意整數除法器(二)”。
