乘法運算
乘法運算在數字信號處理中被廣泛應用,如濾波器以及各種變換等。這里討論乘法器的各種設計方法。盡管Verilog語言中有關鍵字signed(沒有unsingned),借助其可方便地用"\(*\)"描述無符號數乘法和有符號乘法,但同樣可根據目標需求(速度優先還是資源優先)采用其他方式實現乘法運算,以達到系統的最佳配置。
1. 二進制乘法原理
二進制乘法原理與十進制乘法原理類似,都是將乘數的每一位分別與被乘數相乘,除此之外,二進制乘法還有其自身的特點,這對於硬件設計極為關鍵。
二進制乘法可分為兩種情況:無符號數乘法和有符號數乘法。無符號數相乘較為簡單,如圖1.1所示為兩個無符號數(3)和(6)相乘。這里,將它們分別用3bits二進制表示為(011)和(110),相乘結果為(18),以二進制表示為(10010)。從圖1.1中可以看出,整個相乘過程可分解為一系列的移位、相加操作。
圖1.1 兩個無符號數相乘
對有符號數乘法,可分為以下四種情況:
(1)正數 x 正數;(2)正數 x 負數;(3)負數 x 正數;(4)負數 x 負數。
1.1 正數 x 正數
以(3)x(6)為例,其中,\((3)_{10}=(0011)_{2C}\)(3以4bits二進制補碼表示),\((6)_{10}=(0110)_{2C}\)。運算過程如圖1.2所示。
圖1.2 兩個正數相乘
1.2 正數 x 負數
以(3) x (-6)為例,其中\((3)_{10}=(0011)_{2C},(-6)_{10}=(1010)_{2C}\)。為了保證結果的正確,首先要將兩個數進行符號位擴展,擴展為8bits。運算過程如圖1.3所示。運算結果取低8位。-18以二進制補碼表示時至少需要6bits。這樣,-18的6bits二進制補碼為(101110),可見結果是正確的,可認為將(101110)進行符號位擴展得(11101110)。
圖1.3 正數與負數相乘
1.3 負數 x 正數
以(-3)x(6)為例,其中\((-3)_{10}=(1101)_{2C},(6)_{10}=(0110)_{2C}\)。為了保證結果的正確,首先要將兩個數進行符號位擴展,擴展為8bits。運算過程如圖1.4所示。運算結果取低8位。不難驗證結果的正確性。
圖1.4 負數與正數相乘
1.4 負數 x 負數
以(-3)x(-6)為例,其中\((-3)_{10}=(1101)_{2C},(-6)_{10}=(1010)_{2C}\)。為了保證結果的正確,首先要將兩個數進行符號位擴展,擴展為8bits。運算過程如圖1.5所示。運算結果取低8位。
圖1.5 負數與負數相乘
假定被乘數和乘數位寬為\(M_{1}\)和\(M_{2}\),根據以上分析可得如下結論:
(1)無論是無符號數相乘還是有符號數相乘,其乘積位寬必定為\(M_{1}+M_{2}\);
(2)如果被乘數和乘數均為有符號數,那么相乘之前要進行符號位擴展,將被乘數和乘數均擴展為\(M_{1}+M_{2}\)位。
2. 基於移位相加的乘法器
根據上面乘法運算的過程可以發現:乘法運算最終可分解為一系列的移位、相加操作。這就是移位相加型乘法器的設計依據。
以兩個無符號數(3),(6)相乘分析說明。\((6)_{10}=(110)_{2}\),設定 \(b_{2}=1,b_{1}=1,b_{0}=0\),分別表示了(6)的二進制補碼的第2位,第1位和第0位,則(3)x(6)可表示為:
\(3\times 2^2\)表示將3左移兩位,\(3\times 2^1\)表示將3左移一位,由此可得如圖2.1所示的硬件結構。從圖中可以看出\(b_{i}(i=0,1,2)\)在乘法運算過程中發揮的作用,它將決定MUX的輸出是0還是移位后的結果。首先要對被乘數和乘數高位補零,使補零后的位寬為6bits。圖中SL(Shift Left)為左移操作,將輸入數據左移一位。三個MUX控制端分別為\(b_{0},b_{1},b_{2}\)相連。當控制端為1時,MUX輸出移位后的結果,否則輸出全零。移位的結果相加為最終乘積。
圖2.1 移位相加型無符號數乘法器硬件結構
從另一角度看,圖2.1中SL和加法器可分時復用,那么就形成了如圖2.2所示的硬件結構。圖中依然有左移操作模塊SL和數據選擇器MUX,此外增加了右移操作模塊SR和位選擇模塊BG(Bit Get)。SR模塊的目的是將另一輸入數據逐步右移以獲取\(b_{0},b_{1},b_{2}\)。BG模塊則是選擇右移結果的最低位輸出作為MUX的控制端。nd為ain和bin更新標記信號,高有效。這個結構存在的問題是有組合邏輯反饋支路,所以,最好在SL和SR輸出端添加寄存器,這樣的nd的周期將變為4個時鍾周期。
圖2.2 移位相加型無符號數乘法器分時復用硬件結構
與圖2.2相應的時序如圖2.3所示。圖中nd(new data)為新輸入數據標志信號,高有效。ain,bin為輸入數據,其中需要對ain進行高位補零,使最終位寬與乘積位寬一致。該時序顯示了(3)x(6)與(5)x(5)的求積過程。ain左移得到ain_sl,bin右移得到bin_sr,而sel則是bin_sr的最低位。當sel為1時,節點B輸出ain_sl,否則輸出0。節點S顯示了累加的過程。捕獲信號capture為高時將乘積結果輸出至prod端。
圖2.3 移位相加型無符號數乘法器分時復用結構的時序圖
從時序圖中可以看出,從輸入到輸出的Latency與輸入數據的位寬有關,這意味着輸入數據以慢速率進行,而內部運算則是以快速率進行。以\(f_{in}\)表示輸入數據速率,以\(f_{clk}\)表示內部運算速率,以\(W_{din}\)表示輸入數據位寬,則它們之間的關系可表示為:
而這也正反映了nd與capture周期的來歷,二者周期均取決於輸入數據的位寬。顯然這是一種串行結構,使得輸入數據速率與內部運算速率無法達到一致。為此,可采用全並行的結構,如圖2.4所示。圖中SLi(i=0,1,2)表示對輸入數據ain左移i位。BGi(i=0,1,2)表示獲取輸入數據bin的第i位。整個結構式一個全流水結構,輸入數據速率可以和內部運算速率完全一致,但付出的代價是資源的增加。
圖2.4 全並行移位相加型乘法器硬件結構
對於有符號數的相乘仍然可以采用上述結構,以(-3)x(6)為例。(-3)以4位二進制補碼表示為(1101),(6)以4位二進制補碼表示為(0110),則
故可得如圖2.5所示的硬件結構。與圖2.6相比,首先需要對輸入數據ain符號位擴展為8bits;其次,bin的最高位除了作為MUX的控制端外,還用作相應的加法器的控制端,當其為1時,加法器執行減法操作,否則執行加法操作,這在式\((2.3)\)有所體現。
圖2.5 移位相加型有符號數乘法器硬件結構
3. 基於ROM的乘法器
乘法器的另一種實現思想是采用ROM的方式,即將被乘數與乘數連接起來拼成地址,把兩者所有可能的乘積按照地址號存在放ROM的地址空間內,兩個數相乘時,根據兩者構成的地址從ROM中索取乘積結果。由於ROM可采用分布式邏輯資源實現,也可采用嵌入式Block RAM實現,各有優勢,可滿足不同場合的需求,故該方法具有一定的靈活性。
兩個N bit二進制數相乘,其結果為2N bits,這意味着ROM的深度為\(2^{2N}\),寬度為2N,占用存儲空間大小為\(2^{2N}\times 2N\)。以兩個4bit數相乘為例,其存儲空間大小為256x8bit,如圖3.1所示。無論是有符號數相乘還是無符號數相乘都適用。顯然,隨着位寬的增長存儲空間將以指數速度膨脹。這正是這種方法的一個弊端。
圖3.1 以ROM方式實現兩個4bit數相乘
由於上述弊端,一種改進方法就是進行位分解,將兩個大位寬的數相乘分解為多個小位寬的數相乘。例如,兩個4bit數相乘,可將4bit拆分為兩個2bit,從而變成四個2bit數相乘。為了保證最終結果的正確,需要對中間四個乘積結果進行移位處理。為方便起見,以兩個無符號數(11)x(6)對應二進制(1011)x(0110)相乘為例,以\(H_{1},H_{2}\)分別表示為(1011)和(0110)的高兩位,以\(L_{1},L_{2}\)分別表示其低兩位,故
根據式\((3.1)\)可得其處理流程如圖3.2所示。
圖3.2 相乘的位分解處理流程
圖3.2中,\("<<n"\) 表示將輸入數據左移n位。基於此設計思想,可得如圖3.31所示的硬件電路結構。
圖3.3 利用位分解以多個ROM實現兩個4bit無符號數相乘的硬件電路
圖3.3中BG模塊用於分選出數據的高兩位(H)和低兩位(L)。四個ROM完全相同,深度均為16,寬度均為4,存儲內容均為兩個無符號數相乘的結果,如表3.1所示。
表3.1 位分解方式無符號數ROM中存儲的內容
|
內容 |
|
內容 | ||
00 | 00 | 0000 | 10 | 00 | 0000 |
00 | 01 | 0000 | 10 | 01 | 0010 |
00 | 10 | 0000 | 10 | 10 | 0100 |
00 | 11 | 0000 | 10 | 11 | 0110 |
01 | 00 | 0000 | 11 | 00 | 0000 |
01 | 01 | 0001 | 11 | 01 | 0011 |
01 | 10 | 0010 | 11 | 10 | 0110 |
01 | 11 | 0011 | 11 | 11 | 1001 |
對ROM輸出的4bit數據首先要進行高位補零,將其擴展為8bit,以免后續移位操作使有效位移出,這和前文所述乘法原理是完全一致的。SL0表示對數據左移0位,SL2表示對數據左移2位,SL4表示左移4位。得到的部分乘積相加位最終結果。整個結構采用了全流水的方式,可高速運行。與圖3.1相比,存儲空間由原來的256x8bit變為16x4x4bit,縮減為原來的1/8。可見,此方法對於減小存儲空間是非常有利的,此外,也減小了制作ROM存儲數據的工作量。
對於有符號數,仍然可以用位分解方式,相應的硬件結構與圖3.3完全一致,只是ROM中存儲的內容有所不同。以兩個4bit有符號數相乘為例,此時需要將每個數的高兩位視為有符號數,低兩位視為無符號數。ROM中所存儲的數據如表3.2所示。可見,\(H_{a}L_{b}\)和\(H_{b}L_{a}\)對應的ROM所存儲的內容是一致的。
表3.3 位分解方式有符號數ROM中存儲的內容
|
內容 |
|
內容 |
|
內容 |
|
內容 | ||||
Ha | Hb | Ha | Lb | Hb | La | La | Lb | ||||
00 | 00 | 0000 | 00 | 00 | 0000 | 00 | 00 | 0000 | 00 | 00 | 0000 |
00 | 01 | 0000 | 00 | 01 | 0000 | 00 | 01 | 0000 | 00 | 01 | 0000 |
00 | 10 | 0000 | 00 | 10 | 0000 | 00 | 10 | 0000 | 00 | 10 | 0000 |
00 | 11 | 0000 | 00 | 11 | 0000 | 00 | 11 | 0000 | 00 | 11 | 0000 |
01 | 00 | 0000 | 01 | 00 | 0000 | 01 | 00 | 0000 | 01 | 00 | 0000 |
01 | 01 | 0001 | 01 | 01 | 0001 | 01 | 01 | 0001 | 01 | 01 | 0001 |
01 | 10 | 1110 | 01 | 10 | 0010 | 01 | 10 | 0010 | 01 | 10 | 0010 |
01 | 11 | 1111 | 01 | 11 | 0011 | 01 | 11 | 0011 | 01 | 11 | 0011 |
10 | 00 | 0000 | 10 | 00 | 0000 | 10 | 00 | 0000 | 10 | 00 | 0000 |
10 | 01 | 1110 | 10 | 01 | 1110 | 10 | 01 | 1110 | 10 | 01 | 0010 |
10 | 10 | 0100 | 10 | 10 | 1100 | 10 | 10 | 1100 | 10 | 10 | 0100 |
10 | 11 | 0010 | 10 | 11 | 1010 | 10 | 11 | 1010 | 10 | 11 | 0110 |
11 | 00 | 0000 | 11 | 00 | 0000 | 11 | 00 | 0000 | 11 | 00 | 0000 |
11 | 01 | 1111 | 11 | 01 | 1111 | 11 | 01 | 1111 | 11 | 01 | 0011 |
11 | 10 | 0010 | 11 | 10 | 1110 | 11 | 10 | 1110 | 11 | 10 | 0110 |
11 | 11 | 0001 | 11 | 11 | 1101 | 11 | 11 | 1101 | 11 | 11 | 1001 |
對於有符號數,另一種方法是將其轉換為無符號數相乘,使得ROM中所存儲數據的制作較為容易。對於最終結果,將兩個數的符號位判斷是否進行補處理。相應的硬件結構如圖3.32所示。這里仍以兩個4bit數相乘為例,將其分解為4個2bit數相乘。根據A和B的符號位進行判斷,如果符號位為1,則前級兩個MUX分別輸出A和B求補后結果,否則分別輸出A和B。可見,前級兩個MUX總是輸出|A|和|B|,從而,后續乘法的被乘數和乘數即可當作無符號數,實現了由有符號數相乘到無符號數相乘的轉換。這就使得圖3.4中的ROM與圖3.3中的ROM完全一致,即存儲的均為兩個2bit無符號數相乘的結果。最終結果prod則要根據A和B的符號位異或(XOR)的結果進行判斷。當異或結果為1時,說明A和B有一個為正數,而另一個為負數,故將p求補的結果賦給prod。當異或結果為0時,說明A和B同為正數或同為負數,故將p賦值給prod。
圖3.4 采用位分解的兩個4bit有符號數相乘的硬件結構
圖3.4中四個ROM完全相同:具有相同的深度、相同的寬度和相同的存儲內容。從而,可以通過分時復用達到資源共享的目的,節省了資源,而付出的代價就是系統速度的降低。為實現分時復用就需要額外的控制邏輯,以便從輸入數據中分時提取ROM地址以索取部分積。整個電路的硬件結構如圖3.5所示。圖中對輸入數據所采取的措施與圖3.4中的完全一致,都為同一個目的,即保證第一級MUX輸出的是輸入數據的絕對值。第二級MUX共四個,每兩個一組,在控制信號sel_a和sel_b的作用下分選出前級MUX輸出數據的數據位,分時形成四個地址送入ROM的地址輸入端。其中,\(A_{i}(i=0,1,2,3)\)是數據|A|的第i位數據,且第0位為LSB,第3位為MSB;相應地,\(B_{i}(i=0,1,2,3)\)是數據|B|的第i位數據。控制模塊control輸出四個控制信號:第二級MUX的選擇信號sel_a和sel_b、移位模塊的控制信號shift(控制輸入數據左移位數)和捕獲寄存器捕獲信號capture(捕獲累加結果)。
圖3.5 采用分時復用位分解的兩個4bit有符號數相乘的硬件結構
圖3.5中,控制模塊輸出時序如圖3.6所示。控制模塊實質是一個模值為4的計數器cnt4,sel_a為cnt4的MSB,sel_b為cnt4的LSB,從而控制四個MUX輸出相應的數據構成ROM的地址addr。對cnt4譯碼產生移位模塊的控制信號shift。譯碼規則為當cnt4為0時,輸入數據左移0位;當cnt4為1或2時,輸入數據左移2位;當cnt4為3時,輸入數據左移4位。捕獲信號capture也將通過對cnt4譯碼產生。此外,需要注意的是此電路后續運算速率是輸入數據速率的4倍,這一點在圖3.6中也有所體現,這也正是為節省資源所付出的代價。
圖3.5電路中控制模塊輸出時序
采用ROM的方式實現乘法實質上是用“人工計算”代替了“FPGA內部計算”,即預先將所有可能的結果存儲在ROM中,在通過索引的方式查找相應的乘積。這就必須保證ROM中所存數據的正確性以及數據與地址的匹配性。這種方式也體現了FPGA內部資源配置的靈活性,盡管ROM還是用來存儲數據的,但最終實現的卻是運算功能。在某些需要較多乘法器的場合,這不失為一種好的選擇。
4. 與固定系數相乘的乘法器(KCM)
事實上,在乘法器的使用過程中經常會出現被乘數或者乘數為固定常數的情形,如固定系數的FIR濾波器。以K表示此固定常數,稱此類乘法器為固定系數乘法器KCM(K-Constant Coefficient Multiplier)。對於KCM,可根據K的數值特性,設計出具有針對性的乘法器而避免浪費硬線乘法器資源。
采用移位相加的方法可實現KCM。此時,如果K為\(2^n\)或者非常接近\(2^n\),此方法非常適用。例如,K=9,可將其分解為(\(2^3+1\)),則A與K相乘,即變為將A左移3位再與A相加。假定A為4bit二進制補碼,則乘積結果的范圍為[-72,63],可用8bit二進制補碼表示。確定了乘積的位寬也就確定了中間數值(移位后的數值)的位寬。圖4.1顯示了A分別為3和-5的求解過程。圖中\("<<3"\)表示將輸入數據左移3位,SE表示符號位擴展(Signed Extend)。對於A與K均為無符號數的情形,只需將圖中的SE替換為高位補零操作即可。
圖4.1 K為正數時移位相加的求解過程
當K=-9時,仍可采用上述流程,但需要將上述結果求補,如圖4.2所示。
圖4.2 K為負數時移位相加的求解過程
當有多個固定系數時,可根據系數特征共享移位單元。
采用ROM的方式也可實現KCM。例如,兩個4bit二進制補碼數A與K相乘,K為固定常數,假定為7。此時,將A作為ROM的地址輸入端,ROM內與地址相對應存儲着乘積結果,如圖4.3所示。可知乘積結果的取值范圍為[-56,49],只需要7位二進制補碼表示,與常規的兩個4bit數相乘結果為8bit相比,節省了一位。此外,與圖3.1相比,ROM的存儲空間也從256x8bit變為16x7bit,有很大程度的縮減。當A與K均為無符號數時,仍可采用圖4.3所示結構,只需要改動ROM內的存儲數據即可。
圖4.3 采用ROM的方式實現KCM
為進一步縮減存儲空間大小,可采取分解的方法。對於無符號數相乘的KCM,仍可采用圖3.2所示的處理流程,而對於有符號數相乘的KCM,則需要對其進行一定的改動。為便於說明,仍以兩個4bit數A與K相乘為例,且取K=-5,其4位二進制補碼為(1011)。將A分為高兩位H和低兩位L。把L視為無符號數,則L可能的取值為0,1,2和3,L與-5想乘結果分別為0、-5,-10和-15,將它們相應的6位二進制補碼存儲在ROM_L中。把H視為有符號數,則H可能的取值為0,1,-2和-1,H與-5相乘的結果分別為0,-5,10和5,將它們相應的6位二進制補碼存儲在ROM_H中。計算時,以L和H分別作為ROM_L和ROM_H的地址索引相應的部分積,對部分積再做進一步處理得到最終結果。整個硬件結構如圖4.4所示。如果A=-7,其4位二進制補碼為(1001),則L=01,H=10,故ROM_L輸出(11011),ROM_H輸出(01010)。進一步,SE模塊輸出(1111011),SL模塊輸出(1110001),SL模塊輸出(1101100),兩者相加為(1011101),對應十進制-35。可見,位分解的實質仍是二進制乘法原理。從圖中可以看出存儲空間大小為\(4\times 5 \times 2 = 40\)bit。如果不采用位分解,則存儲空間大小為\(16\times 7\)=112bit。這也證明了位分解可有效縮減存儲空間。
圖4.4 采用位分解的KCM
圖4.4 所示的方法需要對符號位部分和權值位部分分別制作ROM,顯得過於煩瑣。為此,可做如下改進,如圖4.5所示。其思想是,將A取絕對值與K相乘,對乘積結果根據A的符號位進行修正。輸入數據A轉換為正數可通過圖中的MUX實現,即當A的符號位為1時,MUX輸出A求補后的結果,否則輸出A,那么MUX輸出的數據必然為正數。兩個ROM完全一致,存儲數據均為0,-5,-10和-15。對於乘積結果,仍需要根據A的符號位判斷。當A為負數時,輸出prod_temp的補碼,否則輸出prod_temp。顯然,該方法對於K為正數也是適用的。此外,由於圖中兩個ROM完全一致,故也可采用類似圖3.5所示的結構對ROM進行分時復用或者采用雙端口ROM以節省資源。
圖4.5 將有符號數轉換為無符號數采用位分解實現KCM
參考資料:
- 高亞軍,《基於FPGA的數字信號處理》(第二版),2015;