來源:數(shù)字站
FPGA可以通過除號直接實現(xiàn)除法,但是當(dāng)除數(shù)或被除數(shù)位寬較大時,計算會變得緩慢,導(dǎo)致時序約束不能通過。此時可以通過在除法IP中加入流水線來提高最大時鐘頻率,這種方式提高時鐘頻率也很有限。如果還不能達到要求,就只能把除法器拆分,來提高系統(tǒng)時鐘頻率。
其實最簡單的方式是使用計數(shù)器對除數(shù)進行累加,并且把累加的次數(shù)寄存,當(dāng)累加結(jié)果大于等于被除數(shù)時,此時寄存的累加次數(shù)就是商,而被除數(shù)減去累加結(jié)果就得到余數(shù)。
但這種方式存在一種弊端,當(dāng)除數(shù)很小的時候,被除數(shù)特別大時,需要經(jīng)過很多個時鐘周期才能計算除結(jié)果。比如被除數(shù)為100,除數(shù)為1,就需要100個時鐘左右才能計算出結(jié)果,效率無疑是低下的。因此一般不會使用這種方式實現(xiàn)除法器。
01除法器原理
首先觀察圖1中二進制數(shù)10011001除以1010的計算過程,除數(shù)與被除數(shù)位寬相差4,4位除數(shù)(1010)大于被除數(shù)的高4位(1001),小于被除數(shù)的高5位(10011),所以首先是被除數(shù)的高5位減去除數(shù)(如圖紫色數(shù)字),得到01001。然后被除數(shù)的第2位到6位數(shù)據(jù)依舊大于除數(shù),則在次減去除數(shù),就這樣依次相減,最后得到商為1111,余數(shù)為3。


圖1 二進制除法
其實通過上面講述,就可以根據(jù)這個規(guī)律實現(xiàn)除法器了,每次從被除數(shù)高位取除數(shù)相同數(shù)據(jù)位寬的數(shù)據(jù)與除數(shù)進行比較,大于等于時直接做減法,商加1。小于時被除數(shù)數(shù)據(jù)位寬向低位移動,被除數(shù)每右移動1位,商左移1位,直到被除數(shù)移動到最低位時結(jié)束運算。
以上描述的方法在FPGA上不太好實現(xiàn),最好是改進一下,將被除數(shù)和除數(shù)比較的位進行固定,那樣就會好實現(xiàn)很多。
如圖2所示,還是前文的8位10011001除以4位1010,通過一個9位的寄存器存儲8位被除數(shù),始終比較被除數(shù)寄存器的第4到8位數(shù)據(jù)與除數(shù)的大小,如果大于除數(shù),則進行減法運算,否則將被除數(shù)左移1位,然后在次進行判斷。
首先是5’b01001小于除數(shù)4’b1010,則將被除數(shù)左移1位。然后被除數(shù)高五位數(shù)據(jù)5’b10010大于除數(shù),則商1,兩者相減得到01001。得到數(shù)據(jù)作為被除數(shù)運算位,此時被除數(shù)高5位還是比除數(shù)小,將被除數(shù)左移1位,相當(dāng)于運算被除數(shù)的低位數(shù)據(jù),同時也要把商左移1位。

圖2 除法運算
就如同上圖循環(huán)判斷被除數(shù)高5位數(shù)據(jù)與除數(shù)的大小關(guān)系,然后對被除數(shù)進行移位和減法運算,就能夠計算出商和余數(shù)。當(dāng)被除數(shù)的最低位數(shù)據(jù)被移入高5位時,在通過判斷被除數(shù)高5位數(shù)據(jù)與除數(shù)大小并進行相應(yīng)減法后,運算結(jié)束。也就是說,這種情況下,被除數(shù)位寬為8,除數(shù)位寬為4,則被除數(shù)左移4次后計算結(jié)束,余數(shù)等于最后被除數(shù)的數(shù)值右移4位(被除數(shù)總共左移的次數(shù))。
這種運算FPGA實現(xiàn)就較為簡單,例如被除數(shù)位寬為M,除數(shù)位寬為N,滿足M>=N,只需要比較被除數(shù)[M:M-N]與除數(shù)的大小,被除數(shù)左移M-N次后運算結(jié)束,將最終的被除數(shù)右移M-N位得到余數(shù)。
上述只適用于一般情況,還會出現(xiàn)圖3這種,被除數(shù)依舊是8位10000001,4位除數(shù)為0010,此時如果還拿被除數(shù)高5位減去除數(shù),得到的結(jié)果仍然大于除數(shù),會導(dǎo)致計算錯誤。


圖3 除法運算
此時可以把除數(shù)進行左移,直到除數(shù)的最高位為1或者除數(shù)左移后大于被除數(shù)的高5位為止,需要考慮除數(shù)左移1位,相當(dāng)于增大2倍,要保持最終結(jié)果不變,被除數(shù)左移次數(shù)需要增加除數(shù)左移的次數(shù),并且最后余數(shù)右移的次數(shù)也會相應(yīng)增加。所以上述計算變?yōu)閳D4所示。


圖4 除法運算
首先除數(shù)根據(jù)條件需要左移2位都不會大于被除數(shù)的高5位,且不會溢出,所以除數(shù)就先左移2位,然后就是判斷被除數(shù)高5位是否大于除數(shù),大于除數(shù)就進行減法運算且商加1,減法運算結(jié)果小于除數(shù)取代被除數(shù)參與運算的數(shù)據(jù)位。如果被除數(shù)高5位小于除數(shù),則被除數(shù)和商一起左移,然后在次判斷。最終當(dāng)被除數(shù)左移次數(shù)達到除數(shù)左移次數(shù)加被除數(shù)與除數(shù)位寬之差時計算結(jié)束。02除法器實現(xiàn)
經(jīng)過上述分析,首先當(dāng)輸入數(shù)據(jù)有效時,需要判斷除數(shù)和被除數(shù)是否為0,其中一個為0,則輸出均為0,且除數(shù)為0時,應(yīng)該報錯。該模塊采用一個狀態(tài)機作為架構(gòu),包括空閑狀態(tài),左移除數(shù),除法運算三個狀態(tài)。假設(shè)除數(shù)位寬L_DIVR,被除數(shù)位寬L_DIVN,且L_DIVN>=L_DIVR。
模塊初始處于空閑狀態(tài),只有上游模塊把開始計算信號拉高且被除數(shù)與除數(shù)均不為0時,狀態(tài)機跳轉(zhuǎn)到左移除數(shù)狀態(tài)。此時需要把除數(shù)和被除數(shù)存入對應(yīng)寄存器,把除數(shù)左移次數(shù)計數(shù)器、被除數(shù)左移次數(shù)計數(shù)器、商和余數(shù)暫存器清零。
開始信號只有在狀態(tài)機處于空閑狀態(tài)下才會有效,其余時間不會接收上游模塊的除數(shù)、被除數(shù)等數(shù)據(jù)。
在左移除數(shù)的狀態(tài)下,如果除數(shù)最高位為0,且除數(shù)左移1位比被除數(shù)寄存器的高L_DIVR+1位小,那么將除數(shù)左移1位,除數(shù)左移次數(shù)計數(shù)器加1。否則狀態(tài)機跳轉(zhuǎn)到除法運算狀態(tài)。
狀態(tài)機處于除法運算狀態(tài)時,需要計算被除數(shù)高L_DIVR+1減去除數(shù)是否大于0。如果大于等于0,則將減法結(jié)果作為被除數(shù)的高L_DIVR+1位,商的最低位賦值為1,也就是加1。如果小于0且被除數(shù)左移次數(shù)小于除數(shù)左移次數(shù)加L_DIVN-L_DIVR時,把被除數(shù)和商均左移1位,被除數(shù)左移次數(shù)計數(shù)器加1。
如果被除數(shù)左移次數(shù)等于除數(shù)左移次數(shù)加L_DIVN-L_DIVR,則狀態(tài)機跳轉(zhuǎn)到空閑狀態(tài),將被除數(shù)右移除數(shù)左移次數(shù)加L_DIVN-L_DIVR得到余數(shù)。
狀態(tài)機從除法狀態(tài)跳轉(zhuǎn)到空閑狀態(tài)時,把商、余數(shù)進行輸出。
以下代碼是模塊端口信號:
//當(dāng)輸入除數(shù)為0時,error信號拉高,且商和余數(shù)為0; //當(dāng)ready信號為低電平時,不能將開始信號start拉高,此時拉高start信號會被忽略。 modulediv#( parameter L_DIVN = 8 ,//被除數(shù)的位寬; parameter L_DIVR = 4 //除數(shù)的位寬; )( input clk ,//時鐘信號; input rst_n ,//復(fù)位信號,低電平有效; inputstart ,//開始計算信號,高電平有效,必須在ready信號為高電平時輸入才有效。 input [L_DIVN -1:0] dividend ,//被除數(shù)輸入; input [L_DIVR -1:0] divisor ,//除數(shù)輸入; output reg ready ,//高電平表示此模塊空閑。 output reg error ,//高電平表示輸入除數(shù)為0,輸入數(shù)據(jù)錯誤。 output reg quotient_vld ,//商和余數(shù)輸出有效指示信號,高電平有效; output reg [L_DIVR -1:0] remainder ,//余數(shù),余數(shù)的大小不會超過除數(shù)大小。 output reg [L_DIVN -1:0] quotient //商。 );
以下是狀態(tài)機狀態(tài)編碼和中間信號的定義,以及通過自動計算位寬函數(shù)取計算計數(shù)器的位寬。
localparam L_CNT = clogb2(L_DIVN) ;//利用函數(shù)自動計算移位次數(shù)計數(shù)器的位寬。 localparam IDLE = 3'b001 ;//狀態(tài)機空閑狀態(tài)的編碼; localparam ADIVR = 3'b010 ;//狀態(tài)機移動除數(shù)狀態(tài)的編碼; localparam DIV = 3'b100 ;//狀態(tài)機進行減法計算和移動被除數(shù)狀態(tài)的編碼; reg vld ;// reg [2:0] state_c ;//狀態(tài)機的現(xiàn)態(tài); reg [2:0] state_n ;//狀態(tài)機的次態(tài); reg [L_DIVN :0] dividend_r ;//保存被除數(shù); reg [L_DIVR -1:0] divisor_r ;//保存除數(shù)。 reg [L_DIVN -1:0] quotient_r ;//保存商。 reg [L_CNT -1:0] shift_dividend ;//用于記錄被除數(shù)左移的次數(shù)。 reg [L_CNT -1:0] shift_divisor ;//用于記錄除數(shù)左移的次數(shù)。 wire [L_DIVR :0] comparison ;//被除數(shù)的高位減去除數(shù)。 wire max ;//高電平表示被除數(shù)左移次數(shù)已經(jīng)用完,除法運算基本結(jié)束,可能還需要進行一次減法運算。 //自動計算計數(shù)器位寬函數(shù)。 function integerclogb2(input integer depth);begin if(depth ==0) clogb2 =1; elseif(depth !=0) for(clogb2=0; depth>0; clogb2=clogb2+1) depth=depth >>1; end endfunction
max用來判斷被除數(shù)左移次數(shù)是否等于除數(shù)于被除數(shù)位寬差加除數(shù)左移次數(shù)。而comparison在除數(shù)左移狀態(tài)時,需要計算被除數(shù)高位與除數(shù)左移后相減的結(jié)果,在其余狀態(tài)下,計算被除數(shù)高位與除數(shù)相減的結(jié)果。
//max為高電平表示被除數(shù)左移的次數(shù)等于除數(shù)左移次數(shù)加上被除數(shù)與除數(shù)的位寬差;
assign max = (shift_dividend == (L_DIVN - L_DIVR) + shift_divisor);
//用來判斷除數(shù)和被除數(shù)第一次做減法的高位兩者的大小,當(dāng)被除數(shù)高位大于等于除數(shù)時,comparison最高位為0,反之為1。
//comparison的計算結(jié)果還能表示被除數(shù)高位與除數(shù)減法運算的結(jié)果。
//在移動除數(shù)時,判斷的是除數(shù)左移一位后與被除數(shù)高位的大小關(guān)系,進而判斷能不能把除數(shù)進行左移。
assign comparison = ((divisor[L_DIVR-1] ==0) && ((state_c == ADIVR))) ?
dividend_r[L_DIVN : L_DIVN - L_DIVR] - {divisor_r[L_DIVR-2:0],1'b0} :
dividend_r[L_DIVN : L_DIVN - L_DIVR] - divisor_r;//計算被除數(shù)高位減去除數(shù),如果計算結(jié)果最高位為0,表示被除數(shù)高位大于等于除數(shù),如果等于1表示被除數(shù)高位小于除數(shù)。
下面是狀態(tài)機跳轉(zhuǎn)代碼。
//狀態(tài)機次態(tài)到現(xiàn)態(tài)的轉(zhuǎn)換; always@(posedge clkornegedge rst_n)begin if(rst_n==1'b0)begin//初始值為空閑狀態(tài); state_c <= IDLE; ? ? end else?begin//狀態(tài)機次態(tài)到現(xiàn)態(tài)的轉(zhuǎn)換; ? ? ? state_c <= state_n; ? ? end ? end //狀態(tài)機的次態(tài)變化。 ? always@(*)begin case(state_c) IDLE : begin//如果開始計算信號為高電平且除數(shù)和被除數(shù)均不等于0。 if(start & (dividend !=?0) & (divisor !=?0))begin ? ? ? ? ? state_n = ADIVR; ? ? ? ? end else?begin//如果開始條件無效或者除數(shù)、被除數(shù)為0,則繼續(xù)處于空閑狀態(tài)。 ? ? ? ? ? state_n = state_c; ? ? ? ? end ? ? ? end ADIVR : begin//如果除數(shù)的最高位為高電平或者除數(shù)左移一位大于被除數(shù)的高位,則跳轉(zhuǎn)到除法運算狀態(tài); if(divisor_r[L_DIVR-1] | comparison[L_DIVR])begin ? ? ? ? ? state_n = DIV; ? ? ? ? end else?begin ? ? ? ? ? state_n = state_c; ? ? ? ? end ? ? ? end DIV :?begin if(max)begin//如果被除數(shù)移動次數(shù)達到最大值,則狀態(tài)機回到空閑狀態(tài),計算完成。 ? ? ? ? ? state_n = IDLE; ? ? ? ? end else?begin ? ? ? ? ? state_n = state_c; ? ? ? ? end ? ? ? end default?: begin//狀態(tài)機跳轉(zhuǎn)到空閑狀態(tài); ? ? ? ? state_n = IDLE; ? ? ? end ? ? endcase ? end
下面是除數(shù)和被除數(shù)、商的計算過程,與前文描述一致,不再贅述。
//對被除數(shù)進行移位或進行減法運算。 //初始時需要加載除數(shù)和被除數(shù),然后需要判斷除數(shù)和被除數(shù)的高位,確定除數(shù)是否需要移位。 //然后根據(jù)除數(shù)和被除數(shù)高位的大小,確認(rèn)被除數(shù)是移位還是與除數(shù)進行減法運算,注意被除數(shù)移動時,為了保證結(jié)果不變,商也會左移一位。 //如果被除數(shù)高位與除數(shù)進行減法運算,則商的最低位變?yōu)?,好比此時商1進行的減法運算。經(jīng)減法結(jié)果賦值到被除數(shù)對應(yīng)位。 always@(posedge clkornegedge rst_n)begin if(rst_n==1'b0)begin//初始值為0; divisor_r <=?0; ? ? ? dividend_r <=?0; ? ? ? quotient_r <=?0; ? ? ? shift_divisor <=?0; ? ? ? shift_dividend <=?0; ? ? end//狀態(tài)機處于加載狀態(tài)時,將除數(shù)和被除數(shù)加載到對應(yīng)寄存器,開始計算; elseif(state_c == IDLE && start && (dividend !=?0) & (divisor !=?0))begin ? ? ? dividend_r <= dividend;//加載被除數(shù)到寄存器; ? ? ? divisor_r <= divisor;//加載除數(shù)到寄存器; ? ? ? quotient_r <=?0;//將商清零; ? ? ? shift_dividend <=?0;//將移位的被除數(shù)寄存器清零; ? ? ? shift_divisor <=?0;?//將移位的除數(shù)寄存器清零; ? ? end//狀態(tài)機處于除數(shù)左移狀態(tài),且除數(shù)左移后小于等于被除數(shù)高位且除數(shù)最高位為0。 elseif(state_c == ADIVR && (~comparison[L_DIVR]) && (~divisor_r[L_DIVR-1]))begin ? ? ? divisor_r <= divisor_r <1;//將除數(shù)左移1位; ? ? ? shift_divisor <= shift_divisor +?1;//除數(shù)總共被左移的次數(shù)加1; ? ??end elseif(state_c == DIV)begin//該狀態(tài)需要完成被除數(shù)移位和減法運算。 if(comparison[L_DIVR] && (~max))begin//當(dāng)除數(shù)大于被除數(shù)高位時,被除數(shù)需要移位。 ? ? ? ? dividend_r <= dividend_r <1;//將被除數(shù)左移1位; ? ? ? ? quotient_r <= quotient_r <1;//同時把商左移1位; ? ? ? ? shift_dividend <= shift_dividend +?1;//被除數(shù)總共被左移的次數(shù)加1; ? ? ??end elseif(~comparison[L_DIVR])begin//當(dāng)除數(shù)小于等于被除數(shù)高位時,被除數(shù)高位減去除數(shù)作為新的被除數(shù)高位。 ? ? ? ? dividend_r[L_DIVN : L_DIVN - L_DIVR] <= comparison;//減法結(jié)果賦值給被除數(shù)進行減法運算的相應(yīng)位。 ? ? ? ? quotient_r[0] <=?1;//因為做了一次減法,則商加1。 ? ? ? end ? ? end ? end
參考代碼如下所示:
//注意此模塊默認(rèn)被除數(shù)的位寬大于等于除數(shù)的位寬。
//當(dāng)quotient_vld信號為高電平且error為低電平時,輸出的數(shù)據(jù)是除法計算的正確結(jié)果。
//當(dāng)輸入除數(shù)為0時,error信號拉高,且商和余數(shù)為0;
//當(dāng)ready信號為低電平時,不能將開始信號start拉高,此時拉高start信號會被忽略。
module div#(
parameter L_DIVN = 8 ,//被除數(shù)的位寬;
parameter L_DIVR = 4//除數(shù)的位寬;
)(
input clk ,//時鐘信號;
input rst_n ,//復(fù)位信號,低電平有效;
input start ,//開始計算信號,高電平有效,必須在ready信號為高電平時輸入才有效。
input [L_DIVN -1:0] dividend ,//被除數(shù)輸入;
input [L_DIVR -1:0] divisor ,//除數(shù)輸入;
output reg ready ,//高電平表示此模塊空閑。
output reg error ,//高電平表示輸入除數(shù)為0,輸入數(shù)據(jù)錯誤。
output reg quotient_vld ,//商和余數(shù)輸出有效指示信號,高電平有效;
output reg [L_DIVR -1:0] remainder ,//余數(shù),余數(shù)的大小不會超過除數(shù)大小。
output reg [L_DIVN -1:0] quotient //商。
);
localparam L_CNT = clogb2(L_DIVN) ;//利用函數(shù)自動計算移位次數(shù)計數(shù)器的位寬。
localparam IDLE = 3'b001 ;//狀態(tài)機空閑狀態(tài)的編碼;
localparam ADIVR = 3'b010 ;//狀態(tài)機移動除數(shù)狀態(tài)的編碼;
localparam DIV = 3'b100 ;//狀態(tài)機進行減法計算和移動被除數(shù)狀態(tài)的編碼;
reg vld ;//
reg [2:0] state_c ;//狀態(tài)機的現(xiàn)態(tài);
reg [2:0] state_n ;//狀態(tài)機的次態(tài);
reg [L_DIVN :0] dividend_r ;//保存被除數(shù);
reg [L_DIVR -1:0] divisor_r ;//保存除數(shù)。
reg [L_DIVN -1:0] quotient_r ;//保存商。
reg [L_CNT -1:0] shift_dividend ;//用于記錄被除數(shù)左移的次數(shù)。
reg [L_CNT -1:0] shift_divisor ;//用于記錄除數(shù)左移的次數(shù)。
wire [L_DIVR :0] comparison ;//被除數(shù)的高位減去除數(shù)。
wire max ;//高電平表示被除數(shù)左移次數(shù)已經(jīng)用完,除法運算基本結(jié)束,可能還需要進行一次減法運算。
//自動計算計數(shù)器位寬函數(shù)。
function integerclogb2(input integer depth);begin
if(depth ==0)
clogb2 =1;
elseif(depth !=0)
for(clogb2=0; depth>0; clogb2=clogb2+1)
depth=depth >>1;
end
endfunction
//max為高電平表示被除數(shù)左移的次數(shù)等于除數(shù)左移次數(shù)加上被除數(shù)與除數(shù)的位寬差;
assign max = (shift_dividend == (L_DIVN - L_DIVR) + shift_divisor);
//用來判斷除數(shù)和被除數(shù)第一次做減法的高位兩者的大小,當(dāng)被除數(shù)高位大于等于除數(shù)時,comparison最高位為0,反之為1。
//comparison的計算結(jié)果還能表示被除數(shù)高位與除數(shù)減法運算的結(jié)果。
//在移動除數(shù)時,判斷的是除數(shù)左移一位后與被除數(shù)高位的大小關(guān)系,進而判斷能不能把除數(shù)進行左移。
assign comparison = ((divisor[L_DIVR-1] ==0) && ((state_c == ADIVR))) ?
dividend_r[L_DIVN : L_DIVN - L_DIVR] - {divisor_r[L_DIVR-2:0],1'b0} :
dividend_r[L_DIVN : L_DIVN - L_DIVR] - divisor_r;//計算被除數(shù)高位減去除數(shù),如果計算結(jié)果最高位為0,表示被除數(shù)高位大于等于除數(shù),如果等于1表示被除數(shù)高位小于除數(shù)。
//狀態(tài)機次態(tài)到現(xiàn)態(tài)的轉(zhuǎn)換;
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin//初始值為空閑狀態(tài);
state_c <= IDLE;
? ? end
else?begin//狀態(tài)機次態(tài)到現(xiàn)態(tài)的轉(zhuǎn)換;
? ? ? state_c <= state_n;
? ? end
? end
//狀態(tài)機的次態(tài)變化。
? always@(*)begin
case(state_c)
IDLE : begin//如果開始計算信號為高電平且除數(shù)和被除數(shù)均不等于0。
if(start & (dividend !=?0) & (divisor !=?0))begin
? ? ? ? ? state_n = ADIVR;
? ? ? ? end
else?begin//如果開始條件無效或者除數(shù)、被除數(shù)為0,則繼續(xù)處于空閑狀態(tài)。
? ? ? ? ? state_n = state_c;
? ? ? ? end
? ? ? end
ADIVR : begin//如果除數(shù)的最高位為高電平或者除數(shù)左移一位大于被除數(shù)的高位,則跳轉(zhuǎn)到除法運算狀態(tài);
if(divisor_r[L_DIVR-1] | comparison[L_DIVR])begin
? ? ? ? ? state_n = DIV;
? ? ? ? end
else?begin
? ? ? ? ? state_n = state_c;
? ? ? ? end
? ? ? end
DIV :?begin
if(max)begin//如果被除數(shù)移動次數(shù)達到最大值,則狀態(tài)機回到空閑狀態(tài),計算完成。
? ? ? ? ? state_n = IDLE;
? ? ? ? end
else?begin
? ? ? ? ? state_n = state_c;
? ? ? ? end
? ? ? end
default?: begin//狀態(tài)機跳轉(zhuǎn)到空閑狀態(tài);
? ? ? ? state_n = IDLE;
? ? ? end
? ? endcase
? end
//對被除數(shù)進行移位或進行減法運算。
//初始時需要加載除數(shù)和被除數(shù),然后需要判斷除數(shù)和被除數(shù)的高位,確定除數(shù)是否需要移位。
//然后根據(jù)除數(shù)和被除數(shù)高位的大小,確認(rèn)被除數(shù)是移位還是與除數(shù)進行減法運算,注意被除數(shù)移動時,為了保證結(jié)果不變,商也會左移一位。
//如果被除數(shù)高位與除數(shù)進行減法運算,則商的最低位變?yōu)?,好比此時商1進行的減法運算。經(jīng)減法結(jié)果賦值到被除數(shù)對應(yīng)位。
? always@(posedge clk?or?negedge rst_n)begin
if(rst_n==1'b0)begin//初始值為0;
? ? ? divisor_r <=?0;
? ? ? dividend_r <=?0;
? ? ? quotient_r <=?0;
? ? ? shift_divisor <=?0;
? ? ? shift_dividend <=?0;
? ? end//狀態(tài)機處于加載狀態(tài)時,將除數(shù)和被除數(shù)加載到對應(yīng)寄存器,開始計算;
elseif(state_c == IDLE && start && (dividend !=?0) & (divisor !=?0))begin
? ? ? dividend_r <= dividend;//加載被除數(shù)到寄存器;
? ? ? divisor_r <= divisor;//加載除數(shù)到寄存器;
? ? ? quotient_r <=?0;//將商清零;
? ? ? shift_dividend <=?0;//將移位的被除數(shù)寄存器清零;
? ? ? shift_divisor <=?0;?//將移位的除數(shù)寄存器清零;
? ? end//狀態(tài)機處于除數(shù)左移狀態(tài),且除數(shù)左移后小于等于被除數(shù)高位且除數(shù)最高位為0。
elseif(state_c == ADIVR && (~comparison[L_DIVR]) && (~divisor_r[L_DIVR-1]))begin
? ? ? divisor_r <= divisor_r <1;//將除數(shù)左移1位;
? ? ? shift_divisor <= shift_divisor +?1;//除數(shù)總共被左移的次數(shù)加1;
? ??end
elseif(state_c == DIV)begin//該狀態(tài)需要完成被除數(shù)移位和減法運算。
if(comparison[L_DIVR] && (~max))begin//當(dāng)除數(shù)大于被除數(shù)高位時,被除數(shù)需要移位。
? ? ? ? dividend_r <= dividend_r <1;//將被除數(shù)左移1位;
? ? ? ? quotient_r <= quotient_r <1;//同時把商左移1位;
? ? ? ? shift_dividend <= shift_dividend +?1;//被除數(shù)總共被左移的次數(shù)加1;
? ? ??end
elseif(~comparison[L_DIVR])begin//當(dāng)除數(shù)小于等于被除數(shù)高位時,被除數(shù)高位減去除數(shù)作為新的被除數(shù)高位。
? ? ? ? dividend_r[L_DIVN : L_DIVN - L_DIVR] <= comparison;//減法結(jié)果賦值給被除數(shù)進行減法運算的相應(yīng)位。
? ? ? ? quotient_r[0] <=?1;//因為做了一次減法,則商加1。
? ? ? end
? ? end
? end
//生成狀態(tài)機從計算除結(jié)果的狀態(tài)跳轉(zhuǎn)到空閑狀態(tài)的指示信號,用于輔助設(shè)計輸出有效指示信號。
? always@(posedge clk)begin
? ? vld <= (state_c == DIV) && (state_n == IDLE);
? end
//生成商、余數(shù)及有效指示信號;
? always@(posedge clk?or?negedge rst_n)begin
if(rst_n==1'b0)begin//初始值為0;
? ? ? quotient <=?0;
? ? ? remainder <=?0;
? ? ? quotient_vld <=?1'b0;
? ? end//如果開始計算時,發(fā)現(xiàn)除數(shù)或者被除數(shù)為0,則商和余數(shù)均輸出0,且將輸出有效信號拉高。
elseif(state_c == IDLE && start && ((dividend==?0) || (divisor==0)))begin
? ? ? quotient <=?0;
? ? ? remainder <=?0;
? ? ? quotient_vld <=?1'b1;
? ??end
elseif(vld)begin//當(dāng)計算完成時。
? ? ? quotient <= quotient_r;//把計算得到的商輸出。
? ? ? quotient_vld <=?1'b1;//把商有效是指信號拉高。
//移動剩余部分以補償對齊變化,計算得到余數(shù);
? ? ? remainder <= (dividend_r[L_DIVN -?1?:?0]) >> shift_dividend;
end
elsebegin
quotient_vld <=?1'b0;
? ? end
? end
//當(dāng)輸入除數(shù)為0時,將錯誤指示信號拉高,其余時間均為低電平。
? always@(posedge clk?or?negedge rst_n)begin
if(rst_n==1'b0)begin//初始值為0;
? ? ? error <=?1'b0;
? ??end
elseif(state_c == IDLE && start)begin
if(divisor==0)//開始計算時,如果除數(shù)為0,把錯誤指示信號拉高。
? ? ? ? error <=?1'b1;
else//開始計算時,如果除數(shù)不為0,把錯誤指示信號拉低。
? ? ? ? error <=?1'b0;
? ? end
? end
//狀態(tài)機處于空閑且不處于復(fù)位狀態(tài);
? always@(*)begin
if(start || state_c != IDLE || vld)
? ? ? ready =?1'b0;
else
? ? ? ready =?1'b1;
? end
endmodule
03
除法器仿真
對應(yīng)的TestBench文件如下所示:
`timescale1ns/1ns
moduletest();
localparam L_DIVN = 8 ;//被除數(shù)的位寬;
localparam L_DIVR = 4 ;//除數(shù)的位寬;
localparam CYCLE = 20 ;//時鐘周期;
reg clk ;//時鐘信號;
reg rst_n ;//復(fù)位信號,低電平有效;
reg start ;//開始計算信號,高電平有效,
reg [L_DIVN -1:0] dividend ;//被除數(shù)輸入;
reg [L_DIVR -1:0] divisor ;//除數(shù)輸入;
wire [L_DIVN -1:0] quotient ;//商。
wire [L_DIVR -1:0] remainder ;//余數(shù),余數(shù)的大小不會超過除數(shù)大小。
wire quotient_vld ;//商和余數(shù)輸出有效指示信號,高電平有效;
wire ready ;//高電平表示此模塊空閑。
wire error ;//高電平表示輸入除數(shù)為0,輸入數(shù)據(jù)錯誤。
reg quotient_error ;
reg rem_error ;
//例化待測試的除法器模塊;
div#(
.L_DIVN ( L_DIVN ),//被除數(shù)的位寬;
.L_DIVR ( L_DIVR )//除數(shù)的位寬;
)
u_div(
.clk ( clk ),//時鐘信號;
.rst_n ( rst_n ),//復(fù)位信號,低電平有效;
.start ( start ),//開始計算信號,高電平有效,
.dividend ( dividend ),//被除數(shù)輸入;
.divisor ( divisor ),//除數(shù)輸入;
.quotient ( quotient ),//商。
.remainder ( remainder ),//余數(shù),余數(shù)的大小不會超過除數(shù)大小。
.ready ( ready ),//高電平表示此模塊空閑。
.error ( error ),//高電平表示輸入除數(shù)為0,輸入數(shù)據(jù)錯誤。
.quotient_vld ( quotient_vld )//商和余數(shù)輸出有效指示信號,高電平有效;
);
//生成時鐘信號;
initial begin
clk =0;
forever#(CYCLE/2) clk = ~clk;
end
//生成復(fù)位信號;
initial begin
rst_n =1;
#2;
rst_n =0;//開始時復(fù)位5個時鐘;
#(5*CYCLE);
rst_n =1;
end
initial begin
#1;
dividend =121; divisor =13;start=0;
#(8*CYCLE);
repeat(20)begin//循環(huán)進行20次運算;
#(5*CYCLE);
dividend = {$random};//產(chǎn)生隨機數(shù)作為被除數(shù);
divisor = {$random} ;//產(chǎn)生隨機數(shù)作為除數(shù);
start =1;
#(CYCLE);
start =0;
@(negedge quotient_vld);
#1;
#(3*CYCLE);
end
#(5*CYCLE);
$stop;//停止仿真;
end
//對模塊輸出的數(shù)據(jù)進行判斷。
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin//初始值為0;
quotient_error <=?1'b0;
? ? ? ? ? ? rem_error <=?1'b0;
? ? ? ??end
? ? ? ??else?if(quotient_vld && (divisor!=0))begin
? ? ? ? ? ? quotient_error <= ((dividend / divisor) != quotient);
? ? ? ? ? ? rem_error <= ((dividend % divisor) != remainder);
? ? ? ? end
? ? end
endmodule
仿真結(jié)果如下所示,商和余數(shù)的錯誤指示信號均為低電平,表示除法運算的結(jié)果沒有什么問題。

圖5 仿真結(jié)果
模塊內(nèi)部信號的詳細仿真如圖6、7所示,具體含義與前文介紹一致,就不再贅述了。

圖6 除法模塊詳細仿真

圖7 除法模塊詳細仿真
04總結(jié)
使用該模塊需要注意幾點:
1、被除數(shù)位寬必須大于等于除數(shù)位寬。
2、當(dāng)error為高電平時,表示輸入除數(shù)為0,此時輸出的商和余數(shù)均為0且無效。 3、只有當(dāng)quotient_vld為高電平時,模塊輸出的商和余數(shù)才是有效的。
4、只有當(dāng)模塊處于空閑(ready為高電平)時,外部輸入的start和除數(shù)、被除數(shù)才能有效。 如果需要源文件,在公眾號后臺回復(fù)“基于FPGA的除法器”(不包括引號)即可。由于我只使用vscode和modelsim對工程進行仿真,所以沒有vivado和quartus工程。下面視頻就是通過vscode調(diào)用modelsim進行仿真的視頻。
-
FPGA
+關(guān)注
關(guān)注
1656文章
22299瀏覽量
630535 -
計數(shù)器
+關(guān)注
關(guān)注
32文章
2306瀏覽量
97602 -
除法器
+關(guān)注
關(guān)注
2文章
15瀏覽量
14112 -
狀態(tài)機
+關(guān)注
關(guān)注
2文章
497瀏覽量
28867
原文標(biāo)題:基于FPGA的高效除法器
文章出處:【微信號:FPGA研究院,微信公眾號:FPGA研究院】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
基于FPGA的除法器純邏輯設(shè)計案例
除法器工作原理介紹
如何對蜂鳥e203內(nèi)核乘除法器進行優(yōu)化
ise的除法器modelsim仿不了?
基于FPGA的除法器
高速硬件除法器
除法器的設(shè)計資料分享
并行除法器 ,并行除法器結(jié)構(gòu)原理是什么?
除法器對數(shù)運算電路的應(yīng)用
實例九— 除法器設(shè)計
FPGA基于線性迭代法的除法器設(shè)計

基于FPGA的高效除法器設(shè)計
評論