基于FPGA 的解決方案具有眾多優(yōu)勢,其中之一就是能夠針對眼前的問題采用最佳的方式來進行數(shù)學(xué)算法。例如,如果響應(yīng)時間至關(guān)重要,我們就簡化數(shù)學(xué)運算步驟。如果注重運算結(jié)果的精度,我們就使用更多的位來確保達到預(yù)期的精度。當然,很多新型FPGA 還具有嵌入式乘法器和DSP slice 的優(yōu)勢,可用于在目標器件中獲得最佳的實現(xiàn)性能。
讓我們了解一下在FPGA 或其它可編程器件內(nèi)開發(fā)數(shù)學(xué)函數(shù)所使用的規(guī)則與方法。
數(shù)字的表示方式
在一種設(shè)計方案中可以使用兩種數(shù)字表示方式,即定點數(shù)與浮點數(shù)。定點表示法中小數(shù)點位置固定不變,可以直接進行算數(shù)運算。定點數(shù)的主要缺點是如果要表示一個較大的數(shù)或者得到一個更精確的小數(shù)值,就需要使用若干個位。定點數(shù)由兩部分構(gòu)成:整數(shù)和小數(shù)。
浮點表示法中小數(shù)點位置隨數(shù)值的大小在不同位置浮動。浮點數(shù)同樣也可分為兩部分:指數(shù)和尾數(shù)。這種表示方法類似于科學(xué)計數(shù)法,科學(xué)技術(shù)法是將一個數(shù)表示為A 乘以10 的B 次冪,其中A 為尾數(shù)、B 為指數(shù)。但在浮點數(shù)中,指數(shù)部分的基數(shù)是2,即A 乘以2 的B次冪。IEEE/ANSI 754-1985 標準對浮點數(shù)表示法進行了標準化?;綢EEE 浮點數(shù)使用8 位指數(shù)和24 位尾數(shù)。
由于浮點數(shù)的表示法存在一定的復(fù)雜性,我們作為設(shè)計人員應(yīng)盡可能多地采用定點表示法。上述浮點數(shù)采用補碼表示法, 其無符號數(shù)表示范圍介于0.0 ~255.9906375 之間,有符號數(shù)表示范圍介于-128.9906375~ 127.9906375 之間。您在一種設(shè)計方案中既可以使用無符號數(shù)也可以使用有符號數(shù),這通常取決于您所用的算法。無符號數(shù)的表示范圍為0 ~ 2n-1,始終表示正數(shù)。
相比之下,有符號數(shù)的表示范圍則取決于所采用的編碼方案,即符號數(shù)值表示法(即原碼)、1 的補碼(即反碼)或2 的補碼(即補碼)。
原碼中最左邊的位表示數(shù)的符號(0 為正,1 為負)。其余的位表示數(shù)值的大小。在這種表示方法中,正數(shù)和負數(shù)的絕對值相同,但是符號位不同。因此,原碼方案中存在正零和負零。
正數(shù)的反碼與其原碼的無符號數(shù)相同。負數(shù)的反碼為正數(shù)按位取反。
補碼是使用最廣泛的有符號數(shù)編碼方案。這里與其它兩種編碼方案一樣,正數(shù)與無符號數(shù)的表示形式相同,而負數(shù)的二進制表達式與絕對值相同的正數(shù)相加后等于0。計算負數(shù)補碼時,首先將正數(shù)按位取反,然后再加1。補碼允許您將兩個數(shù)的減法按照加法來處理。補碼可以表示的范圍是:
將一個數(shù)轉(zhuǎn)換為補碼格式的方法是按從右至左的順序按位遍歷,從遇到的第一個“1”開始將二進制位按位取反,而之前的二進制位保持不變。
定點運算
在定點數(shù)中,通常用x 和y 來區(qū)分整數(shù)位和小數(shù)位,其中x 表示整數(shù)位的數(shù)量,y 表示小數(shù)位的數(shù)量。例如,8,8 表示8 個整數(shù)位和8 個小數(shù)位;16,0 表示16 個整數(shù)位和0 個小數(shù)位。在很多情況下,您通常需要在設(shè)計階段根據(jù)浮點算法轉(zhuǎn)換來確定所需的整數(shù)和小數(shù)位數(shù)量。得益于FPGA 的靈活性,我們可以表達任意二進制長度的定點數(shù);整數(shù)位的數(shù)量取決于需要存儲的最大整數(shù)值,而小數(shù)位的數(shù)量取決于最終結(jié)果的精度。我們利用以下公式來確定整數(shù)位的數(shù)量:
例如,要表示0.0 ~ 423.0 范圍內(nèi)的數(shù)值,所需整數(shù)位的數(shù)量為:
這表示您需要9 個整數(shù)位,可以代表0 ~ 511 范圍內(nèi)的數(shù)。利用16 個位來表示這個數(shù)時,可以有7 個位用于表示小數(shù)。利用下面的等式計算這種表達方式所能提供的精度:
您可以增加小數(shù)位的數(shù)量,進而提高定點數(shù)的精度。在設(shè)計過程中,我們有時希望只存儲小數(shù)(0,16), 這主要取決于您希望將精度提高到多少。利用216 進行擴展可能依然無法達到足夠高的精度。這種情況下,您可以用2 的冪次方來放大這個數(shù),使這個數(shù)可以用16 個位來表示。然后,您可以在下一階段刪除這個比例因子。例如,為了用16 個位來表示1.45309806319x10-4,第一步需要將這個數(shù)與216 相乘。
只存儲結(jié)果的整數(shù)部分(9)將導(dǎo)致這個數(shù)的實際存儲值為1.37329101563x10-4(9 / 65536)。需要存儲的數(shù)值與實際存儲的數(shù)值之間差值較大,可能導(dǎo)致出現(xiàn)無法接受的錯誤計算結(jié)果。您可以按照比例因子2 來放大這個數(shù),以獲取更精確的結(jié)果。結(jié)果介于32768-65535之間,因此仍然可以用一個16 位的數(shù)字來存儲。利用此前存儲1.45309806319x10-4 的實例,將這個數(shù)與比例因子228 相乘將產(chǎn)生一個可以用16 個位來存儲的數(shù),并使預(yù)期的數(shù)值具有更高的精度。
假定在接下來的計算過程中您可以解決用比例因子228進行放大的問題, 那么結(jié)果的整數(shù)部分將給予您1.45308673382x10-4 的存儲結(jié)果,并使得計算結(jié)果具有更高精度。例如,將已擴展的數(shù)與一個16 個位格式為4,12 的數(shù)相乘,產(chǎn)生了4,40(28 + 12)形式的結(jié)果。但是,這個結(jié)果將以32 位來存儲。
定點規(guī)則
在執(zhí)行加法、減法或除法時,2 個數(shù)的小數(shù)點必須對齊。這就是說您只可以將一個表示格式為x,8 的數(shù)與另一個表示格式也為x,8 的數(shù)相加、相減或相除。對具有不同格式的x 和y 進行算術(shù)運算時,您首先應(yīng)保證小數(shù)點對齊。為了對齊不同格式的數(shù)字,您有兩個選擇:將帶有更多整數(shù)位的數(shù)與2X 相乘,或者將具有最小整數(shù)位的數(shù)除以2X。但是,除法會降低結(jié)果的精度,還可能導(dǎo)致結(jié)果超出容許公差。由于所有的數(shù)都可以利用兩種形式來存儲,這樣您在FPGA 中通過移位操作可以很方便地對數(shù)進行放大或縮小,其中左移或右移1 位分別放大或縮小了1 倍,實現(xiàn)十進制小數(shù)點的對齊。為了對兩個格式分別為8,8和9,7 的兩個數(shù)相加,如果可以接受最低有效位的丟失,則您可以利用比例因子21 來放大格式為9,7 的數(shù),也可以將格式為8,8 的數(shù)縮小至格式為9,7。
例如,您打算將234.58 和312.732 這兩個數(shù)相加,而它們分別以8,8 和9,7 的格式來存儲。第一步,確定實際相加的16 位數(shù)。
從上可以看出,兩個加數(shù)分別為60052 和40029。但是,在相加之前,您必須對齊小數(shù)點。通過放大帶有更多整數(shù)位的數(shù)來對齊十進制小數(shù)點,您必須利用因子21 來放大9,7 格式的數(shù)。
然后,您通過執(zhí)行加法來計算結(jié)果:
以10,8 格式(140110 / 28)表示,則為547.3046875。
當兩個數(shù)相乘時,您無需對齊小數(shù)點,因為乘法提供了范圍是X1 + X2,Y1 + Y2 的結(jié)果。將格式分別為14,2 和10,6 的兩個數(shù)相乘將得出一個整數(shù)位為24,小數(shù)位為8 的結(jié)果。
通過與除數(shù)的倒數(shù)相乘這種方法,在一個式子中您可以采用與小數(shù)相乘來代替除法。這種途徑可以顯著降低設(shè)計的復(fù)雜性。例如,將212.732(以9,7(40029)格式來表示)除以15,第一步是計算除數(shù)的倒數(shù)。
這個倒數(shù)必須被放大,以16 位數(shù)的形式來表示。
將這兩個數(shù)相乘,得出格式為9,23 的結(jié)果。
相除結(jié)果為:
當預(yù)期的結(jié)果是20.8488,如果結(jié)果的精度不夠高,則您可以利用一個更大的比例因子來放大這個倒數(shù),以得到更精確的結(jié)果。因此,當可以與一個數(shù)的倒數(shù)相乘時,永遠不要除以這個數(shù)。
溢出問題
在實現(xiàn)算法時,結(jié)果必須不大于結(jié)果寄存器可以存儲的最大值。否則,就會發(fā)生溢出。當溢出發(fā)生時,存儲結(jié)果就會有誤,最高幾位會丟失。溢出的最簡單實例是將2個16 位的數(shù)相加,每個數(shù)的值都是65535,然后將結(jié)果存儲在16 位寄存器中。
上述計算將使得這個16 位結(jié)果寄存器中的值為65534,但這個結(jié)果不正確。防止溢出的最簡單方式是確定數(shù)學(xué)運算允許的最大值,利用這個方程來確定所需結(jié)果寄存器的大小。
如果您正在開發(fā)一個平均器,計算50 個16 位輸入值的平均值,則可以計算所需結(jié)果寄存器的大小。
仍然利用同一個方程,需要一個22 位結(jié)果寄存器來防止溢出的發(fā)生。您也必須注意,在處理有符號數(shù)時,如果遇到了負數(shù),應(yīng)該避免發(fā)生溢出。仍然利用此前的平均器實例,計算10 個有符號長度為16 位的數(shù)的平均值,返回一個16 位的結(jié)果。
因為很方便地將結(jié)果與除數(shù)倒數(shù)的擴展值相乘,您將這個數(shù)與1/10 ? 65536 = 6554 相乘來確定平均值。
這個數(shù)除以216 等于-32770, 但16 位的輸出結(jié)果無法正確地表示這個數(shù)。因此,模塊的設(shè)計過程必須考慮溢出,必須檢測溢出,以確保不會輸出不正確的結(jié)果。
現(xiàn)實世界的實現(xiàn)方式
假設(shè)您正在設(shè)計一個模塊,用于實現(xiàn)一個轉(zhuǎn)換氣壓的轉(zhuǎn)移函數(shù),其中氣壓的單位是毫巴,海拔的單位是米。
輸入值的范圍是0 ~ 10 毫巴,分辨率是0.1 毫巴。模塊輸出要求精確到+/-0.01 米。因為模塊規(guī)范沒有確定輸入刻度,您可以通過下列等式來計算。
因此,為了實現(xiàn)最高的精度,您應(yīng)將輸入數(shù)據(jù)的格式設(shè)置為4 個整數(shù)位,12 個小數(shù)位。開發(fā)這個模塊的下一步任務(wù)就是利用未擴展值并通過電子數(shù)據(jù)表計算出整個輸入范圍內(nèi)轉(zhuǎn)換函數(shù)的預(yù)期結(jié)果。如果輸入范圍過大而無法獲得合理的結(jié)果,則計算可接受的點數(shù)量。例如, 您使用100 個條目來確定整個輸入范圍的預(yù)期結(jié)果。在您計算出最初的非擴展預(yù)期值之后,下一步是確定正確的常數(shù)比例因子,利用擴展值來計算預(yù)期的輸出結(jié)果。為了實現(xiàn)最高的精度,您應(yīng)利用不同的因子來放大該式中每個常數(shù)。
多項式中第一個常數(shù)(A)的比例因子為:
多項式中第二個常數(shù)(B)的比例因子為:
因為最后的多項式常數(shù)(C)是一個純小數(shù),所以利用比例因子216 來放大它。
通過這些比例因子用戶可以計算出擴展的電子數(shù)據(jù)表,如表1 所示。每一階段的計算結(jié)果將得出超過16 位的結(jié)果。
Cx2 的計算得出32 位、格式為4,12 + 4,12 = 8,24 的結(jié)果。然后與常數(shù)C 相乘,得出了48 位、格式為8,24 + 0,16 = 8,40 的結(jié)果。對于這個實例所要求的精度來說,利用40 位來表示小數(shù)有點多。因此,將這個計算結(jié)果除以232,以得出16 位、格式為8,8 的結(jié)果。在計算Bx 過程中,也將結(jié)果減小至16 位,以得出格式為5,11 的結(jié)果。
計算結(jié)果是Cx2,Bx 與A 列中對應(yīng)數(shù)之和。但是,為了獲得正確的結(jié)果,您首先必須擴大A 和Cx2 ,并按x,11 格式對齊小數(shù)點,或者縮小Bx 的計算結(jié)果并按8,8格式對齊小數(shù)點,最終將小數(shù)點與A 和Cx2 的計算值的小數(shù)點對齊。
在這個例子中,我們將計算結(jié)果縮小23 倍,按8,8格式來對齊小數(shù)點。這種方法簡化了需要移位的數(shù)量,因此減小了實現(xiàn)這個實例所需邏輯單元的數(shù)量。注意如果您通過縮小來對齊小數(shù)點的方式而沒有實現(xiàn)要求的精度時,則必須擴大A 和Cx2 的計算結(jié)果來對齊小數(shù)點。在這個實例中,計算結(jié)果擴大了28。然后,您可以縮小這個結(jié)果,將其與從未擴展值中獲取的結(jié)果比較。實際計算結(jié)果和預(yù)期結(jié)果之間的差值表示精度,利用電子數(shù)據(jù)表中MAX() 和MIN() 命令來獲得計算結(jié)果的最大誤差和最小誤差,而您在電子數(shù)據(jù)表條目的整個范圍內(nèi)都可以獲取計算結(jié)果的這兩個誤差。
當基于電子數(shù)據(jù)表的計算結(jié)果確認了您已經(jīng)實現(xiàn)了所要求的精度,則可以編寫并仿真RTL 代碼。如果需要,您可以設(shè)計一個測試平臺,例如輸入值與電子數(shù)據(jù)表中的數(shù)據(jù)相同。這允許您將仿真輸出結(jié)果與基于電子數(shù)據(jù)表的計算結(jié)果進行比較,以確保采用了正確的RTL 實現(xiàn)方案。
RTL 實現(xiàn)方案
RTL 實例利用有符號并行數(shù)學(xué)運算在4 個時鐘周期之內(nèi)即可計算出結(jié)果。因為采用了有符號的并行乘法,所以應(yīng)該注意到必須正確地處理由乘法產(chǎn)生的額外符號位。
ENTITY transfer_function IS PORT(
sys_clk : IN std_logic;
reset : IN std_logic;
data : IN std_logic_vector(15 DOWNTO 0);
new_data : IN std_logic;
result : OUT std_logic_vector(15 DOWNTO
0);
new_res : OUT std_logic);
END ENTITY transfer_function;
ARCHITECTURE rtl OF transfer_function IS
-- this module performs the following
transfer function -0.0088x2 + 1.7673x +
131.29
-- input data is scaled 8,8, while the
output data will be scaled 8,8.
-- this module utilizes signed parallel
mathematics
TYPE control_state IS (idle, multiply,
add, result_op);
CONSTANT c : signed(16 DOWNTO 0) := to_
signed(-577,17);
CONSTANT b : signed(16 DOWNTO 0) := to_
signed(57910,17);
CONSTANT a : signed(16 DOWNTO 0) := to_
signed(33610,17);
SIGNAL current_state : control_state;
SIGNAL buf_data : std_logic; --used to
detect rising edge upon the new_data
SIGNAL squared : signed(33 DOWNTO 0); --
register holds input squared.
SIGNAL cx2 : signed(50 DOWNTO 0);
--register used to hold Cx2
SIGNAL bx : signed(33 DOWNTO 0); --
register used to hold bx
SIGNAL res_int : signed(16 DOWNTO 0);
--register holding the temporary result
BEGIN
fsm : PROCESS(reset, sys_clk)
BEGIN
IF reset = ‘1’ THEN
buf_data 《= ‘0’;
squared 《= (OTHERS =》 ‘0’);
cx2 《= (OTHERS =》 ‘0’);
bx 《= (OTHERS =》 ‘0’);
result 《= (OTHERS =》 ‘0’);
res_int 《= (OTHERS =》 ‘0’);
new_res 《= ‘0’;
current_state 《= idle;
ELSIF rising_edge(sys_clk) THEN
buf_data 《= new_data;
CASE current_state IS
WHEN idle =》
new_res 《= ‘0’;
IF (new_data = ‘1’) AND (buf_data
= ‘0’) THEN --detect rising edge
new data
squared 《= signed( ‘0’& data)
* signed(‘0’& data);
current_state 《= multiply;
ELSE
squared 《= (OTHERS =》‘0’);
current_state 《= idle;
END IF;
WHEN multiply =》
new_res 《= ‘0’;
cx2 《= (squared * c);
bx 《= (signed(‘0’& data)* b);
current_state 《= add;
WHEN add =》
new_res 《= ‘0’;
res_int 《= a + cx2(48 DOWNTO 32)
+
(“000”& bx(32 DOWNTO 19));
current_state 《= result_op;
WHEN result_op =》
result 《= std_logic_vector(res_
int (res_int‘high -1 DOWNTO 0));
new_res 《= ’0‘;
current_state 《= idle;
END CASE;
END IF;
END PROCESS;
END ARCHITECTURE rtl;
FPGA 架構(gòu)成為了實現(xiàn)數(shù)學(xué)函數(shù)的理想工具,盡管實現(xiàn)算法需要具有更多的最初想法以及利用MATLAB? 或Excel 等系統(tǒng)級仿真工具來建模。一旦掌握了FPGA數(shù)學(xué)運算的一些基本知識,用戶就可以快速地實現(xiàn)數(shù)學(xué)算法。
評論