基於減法操作除法器的演算法---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;
[email protected](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
[email protected](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
[email protected](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;
//=======================================================
[email protected](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
[email protected](posedge I_clk)
begin
div_start_d1 <= div_start;
end
assign div_start_neg = div_start_d1 & (~div_start);
[email protected](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;
[email protected](*)
begin
tempa = a;
tempb = b;
end
[email protected](*)
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
模擬:
引言
除法器在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程式碼