除法器在FPGA裏怎麼實現呢?當然不是讓用“/”和“%”實現;
在Verilog HDL語言中雖然有除的運算指令,但是除運算符中的除數必須是2的冪,因此無法實現除數爲任意整數的除法,很大程度上限制了它的使用領域。並且多數綜合工具對於除運算指令不能綜合出令人滿意的結果,有些甚至不能給予綜合。即使可以綜合,也需要比較多的資源。對於這種情況,一般使用相應的算法來實現除法,分爲兩類:基於減法操作和基於乘法操作的算法;
實現算法
基於減法的除法器的算法:
對於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位後左右兩邊分別就是餘數和商。
畫個簡單的圖:
1.時序邏輯除法
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;
always@(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
always@(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
always@(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;
//=======================================================
always@(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;//如果比temp_b大,則減去temp_b然後加1
end
else
begin
temp_a <= 16'h0;
temp_b <= 16'h0;
end
end
always@(posedge I_clk)
begin
div_start_d1 <= div_start;
end
assign div_start_neg = div_start_d1 & (~div_start);//下降沿檢測
always@(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
2.純組合邏輯
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;
always@(*)
begin
tempa = a;
tempb = b;
end
always@(*)
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_b的低8位都是0,所以只需要比較高八位
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