(添加於20180812)對於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位后左右兩邊分別就是余數和商。
畫個簡單的圖:
我們都知道,FPGA是由多個LE構成的集成電路,邏輯電路的特點就是只能輸出0或者1,也就是說只能用來表示整數而不是小數,那么對於除法器這種可能會出現小數的運算如何處理呢?簡單的思路就是將被除數擴大,從而得到量化后的商值以及余數。
那么采用基於減法的算法有兩種實現方式(這里采用除數和被除數都是21位的情況對計算的精確度以及占用的資源做了整理)
1.組合邏輯的實現法
RTL編程
module div ( input[31:0] a, input[31:0] b, input enable, output reg [31:0] yshang, output reg [31:0] yyushu, output reg done ); 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 if(enable) begin temp_a = {32'h00000000,tempa}; temp_b = {tempb,32'h00000000}; done = 0; 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]; done = 1; end end endmodule
testbeach
`timescale 1ns/1ns module test(); reg [31:0] a; reg [31:0] b; reg enable; wire [31:0] yshang; wire [31:0] yyushu; wire done; initial begin enable=1; #10 a = $random()%10000; b = $random()%1000; #100 a = $random()%1000; b = $random()%100; #100 a = $random()%100; b = $random()%10; #1000 $stop; end div DIV_RILL ( .a (a), .b (b), .enable(enable), .yshang (yshang), .yyushu (yyushu), .done(done) ); endmodule
仿真波形
資源占用
2.時序邏輯的實現法
RTL編程
`timescale 1ns / 1ps module div_rill #( parameter N=21) ( input clk, input rst, input enable, input [N-1:0] a, input [N-1:0] b, output reg [N-1:0] yshang, output reg [N-1:0] yyushu, output reg done ); parameter S=N<<1; reg[N-1:0] tempa; reg[N-1:0] tempb; reg[S-1:0] temp_a; reg[S-1:0] temp_b; reg [5:0] status; parameter s_idle = 6'b000000; parameter s_init = 6'b000001; parameter s_calc1 = 6'b000010; parameter s_calc2 = 6'b000100; parameter s_done = 6'b001000; reg [N-1:0] i; always @(posedge clk) begin if(rst) begin i <= 21'h0; tempa <= 21'h1; tempb <= 21'h1; yshang <= 21'h1; yyushu <= 21'h1; done <= 1'b0; status <= s_idle; end else begin case (status) s_idle: begin if(enable) begin tempa <= a; tempb <= b; status <= s_init; end else begin i <= 21'h0; tempa <= 21'h1; tempb <= 21'h1; yshang <= 21'h1; yyushu <= 21'h1; done <= 1'b0; status <= s_idle; end end s_init: begin temp_a <= {21'h00000000,tempa}; temp_b <= {tempb,21'h00000000}; status <= s_calc1; end s_calc1: begin if(i < N) begin temp_a <= {temp_a[S-2:0],1'b0}; status <= s_calc2; end else begin status <= s_done; end end s_calc2: begin if(temp_a[S-1:N] >= tempb) begin temp_a <= temp_a - temp_b + 1'b1; end else begin temp_a <= temp_a; end i <= i + 1'b1; status <= s_calc1; end s_done: begin yshang <= temp_a[N-1:0]; yyushu <= temp_a[S-1:N]; done <= 1'b1; status <= s_idle; end default: begin status <= s_idle; end endcase end end endmodule
testbeach
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2018/07/27 22:16:33 // Design Name: // Module Name: test // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module test(); reg clk; reg rst; reg enable; reg [20:0] a; reg [20:0] b; wire [20:0] yshang; wire [20:0] yyushu; wire done; initial begin clk = 0; #10 rst = 1; #20 rst = 0; #15 enable =1; a = $random()%10000; b = $random()%1000; #10 enable =0; #1000 enable =1; a = $random()%1000; b = $random()%100; #10 enable =0; #1000 enable =1; a = $random()%100; b = $random()%10; #10 enable =0; #1000 $stop; end always # 5 clk = ~clk; always #1000 a=a+8; always #1000 b=b+9; div_rill DIV_RILL ( .clk (clk), .rst (rst), .enable (enable), .a (a), .b (b), .yshang (yshang), .yyushu (yyushu), .done (done) ); endmodule
仿真波形(可以看出,該設計下從開始做除法到完成一共需要(2515-2065)/10=45個時鍾周期)每做一次計算的延時較長。
資源占用
結論:可以看出我們通常的設計中一般建議采用時序電路的實現方法而不是組合邏輯,雖然時序邏輯的實現方法會導致計算延時,但是因為組合邏輯占資源較多而且可能在時序約束部分出現錯誤,要遠遠大於延時的錯誤。