基于FPGA 的解決方案具有眾多優(yōu)勢(shì),其中之一就是能夠針對(duì)眼前的問題采用最佳的方式來進(jìn)行數(shù)學(xué)算法。例如,如果響應(yīng)時(shí)間至關(guān)重要,我們就簡(jiǎn)化數(shù)學(xué)運(yùn)算步驟。如果注重運(yùn)算結(jié)果的精度,我們就使用更多的位來確保達(dá)到預(yù)期的精度。當(dāng)然,很多新型FPGA 還具有嵌入式乘法器和DSP slice 的優(yōu)勢(shì),可用于在目標(biāo)器件中獲得最佳的實(shí)現(xiàn)性能。
讓我們了解一下在FPGA 或其它可編程器件內(nèi)開發(fā)數(shù)學(xué)函數(shù)所使用的規(guī)則與方法。
數(shù)字的表示方式
在一種設(shè)計(jì)方案中可以使用兩種數(shù)字表示方式,即定點(diǎn)數(shù)與浮點(diǎn)數(shù)。定點(diǎn)表示法中小數(shù)點(diǎn)位置固定不變,可以直接進(jìn)行算數(shù)運(yùn)算。定點(diǎn)數(shù)的主要缺點(diǎn)是如果要表示一個(gè)較大的數(shù)或者得到一個(gè)更精確的小數(shù)值,就需要使用若干個(gè)位。定點(diǎn)數(shù)由兩部分構(gòu)成:整數(shù)和小數(shù)。
浮點(diǎn)表示法中小數(shù)點(diǎn)位置隨數(shù)值的大小在不同位置浮動(dòng)。浮點(diǎn)數(shù)同樣也可分為兩部分:指數(shù)和尾數(shù)。這種表示方法類似于科學(xué)計(jì)數(shù)法,科學(xué)技術(shù)法是將一個(gè)數(shù)表示為A 乘以10 的B 次冪,其中A 為尾數(shù)、B 為指數(shù)。但在浮點(diǎn)數(shù)中,指數(shù)部分的基數(shù)是2,即A 乘以2 的B次冪。IEEE/ANSI 754-1985 標(biāo)準(zhǔn)對(duì)浮點(diǎn)數(shù)表示法進(jìn)行了標(biāo)準(zhǔn)化?;綢EEE 浮點(diǎn)數(shù)使用8 位指數(shù)和24 位尾數(shù)。
由于浮點(diǎn)數(shù)的表示法存在一定的復(fù)雜性,我們作為設(shè)計(jì)人員應(yīng)盡可能多地采用定點(diǎn)表示法。上述浮點(diǎn)數(shù)采用補(bǔ)碼表示法, 其無符號(hào)數(shù)表示范圍介于0.0 ~255.9906375 之間,有符號(hào)數(shù)表示范圍介于-128.9906375~ 127.9906375 之間。您在一種設(shè)計(jì)方案中既可以使用無符號(hào)數(shù)也可以使用有符號(hào)數(shù),這通常取決于您所用的算法。無符號(hào)數(shù)的表示范圍為0 ~ 2n-1,始終表示正數(shù)。
相比之下,有符號(hào)數(shù)的表示范圍則取決于所采用的編碼方案,即符號(hào)數(shù)值表示法(即原碼)、1 的補(bǔ)碼(即反碼)或2 的補(bǔ)碼(即補(bǔ)碼)。
原碼中最左邊的位表示數(shù)的符號(hào)(0 為正,1 為負(fù))。其余的位表示數(shù)值的大小。在這種表示方法中,正數(shù)和負(fù)數(shù)的絕對(duì)值相同,但是符號(hào)位不同。因此,原碼方案中存在正零和負(fù)零。
正數(shù)的反碼與其原碼的無符號(hào)數(shù)相同。負(fù)數(shù)的反碼為正數(shù)按位取反。
補(bǔ)碼是使用最廣泛的有符號(hào)數(shù)編碼方案。這里與其它兩種編碼方案一樣,正數(shù)與無符號(hào)數(shù)的表示形式相同,而負(fù)數(shù)的二進(jìn)制表達(dá)式與絕對(duì)值相同的正數(shù)相加后等于0。計(jì)算負(fù)數(shù)補(bǔ)碼時(shí),首先將正數(shù)按位取反,然后再加1。補(bǔ)碼允許您將兩個(gè)數(shù)的減法按照加法來處理。補(bǔ)碼可以表示的范圍是:
將一個(gè)數(shù)轉(zhuǎn)換為補(bǔ)碼格式的方法是按從右至左的順序按位遍歷,從遇到的第一個(gè)“1”開始將二進(jìn)制位按位取反,而之前的二進(jìn)制位保持不變。
定點(diǎn)運(yùn)算
在定點(diǎn)數(shù)中,通常用x 和y 來區(qū)分整數(shù)位和小數(shù)位,其中x 表示整數(shù)位的數(shù)量,y 表示小數(shù)位的數(shù)量。例如,8,8 表示8 個(gè)整數(shù)位和8 個(gè)小數(shù)位;16,0 表示16 個(gè)整數(shù)位和0 個(gè)小數(shù)位。在很多情況下,您通常需要在設(shè)計(jì)階段根據(jù)浮點(diǎn)算法轉(zhuǎn)換來確定所需的整數(shù)和小數(shù)位數(shù)量。得益于FPGA 的靈活性,我們可以表達(dá)任意二進(jìn)制長(zhǎng)度的定點(diǎn)數(shù);整數(shù)位的數(shù)量取決于需要存儲(chǔ)的最大整數(shù)值,而小數(shù)位的數(shù)量取決于最終結(jié)果的精度。我們利用以下公式來確定整數(shù)位的數(shù)量:
例如,要表示0.0 ~ 423.0 范圍內(nèi)的數(shù)值,所需整數(shù)位的數(shù)量為:
這表示您需要9 個(gè)整數(shù)位,可以代表0 ~ 511 范圍內(nèi)的數(shù)。利用16 個(gè)位來表示這個(gè)數(shù)時(shí),可以有7 個(gè)位用于表示小數(shù)。利用下面的等式計(jì)算這種表達(dá)方式所能提供的精度:
您可以增加小數(shù)位的數(shù)量,進(jìn)而提高定點(diǎn)數(shù)的精度。在設(shè)計(jì)過程中,我們有時(shí)希望只存儲(chǔ)小數(shù)(0,16), 這主要取決于您希望將精度提高到多少。利用216 進(jìn)行擴(kuò)展可能依然無法達(dá)到足夠高的精度。這種情況下,您可以用2 的冪次方來放大這個(gè)數(shù),使這個(gè)數(shù)可以用16 個(gè)位來表示。然后,您可以在下一階段刪除這個(gè)比例因子。例如,為了用16 個(gè)位來表示1.45309806319x10-4,第一步需要將這個(gè)數(shù)與216 相乘。
只存儲(chǔ)結(jié)果的整數(shù)部分(9)將導(dǎo)致這個(gè)數(shù)的實(shí)際存儲(chǔ)值為1.37329101563x10-4(9 / 65536)。需要存儲(chǔ)的數(shù)值與實(shí)際存儲(chǔ)的數(shù)值之間差值較大,可能導(dǎo)致出現(xiàn)無法接受的錯(cuò)誤計(jì)算結(jié)果。您可以按照比例因子2 來放大這個(gè)數(shù),以獲取更精確的結(jié)果。結(jié)果介于32768-65535之間,因此仍然可以用一個(gè)16 位的數(shù)字來存儲(chǔ)。利用此前存儲(chǔ)1.45309806319x10-4 的實(shí)例,將這個(gè)數(shù)與比例因子228 相乘將產(chǎn)生一個(gè)可以用16 個(gè)位來存儲(chǔ)的數(shù),并使預(yù)期的數(shù)值具有更高的精度。
假定在接下來的計(jì)算過程中您可以解決用比例因子228進(jìn)行放大的問題, 那么結(jié)果的整數(shù)部分將給予您1.45308673382x10-4 的存儲(chǔ)結(jié)果,并使得計(jì)算結(jié)果具有更高精度。例如,將已擴(kuò)展的數(shù)與一個(gè)16 個(gè)位格式為4,12 的數(shù)相乘,產(chǎn)生了4,40(28 + 12)形式的結(jié)果。但是,這個(gè)結(jié)果將以32 位來存儲(chǔ)。
定點(diǎn)規(guī)則
在執(zhí)行加法、減法或除法時(shí),2 個(gè)數(shù)的小數(shù)點(diǎn)必須對(duì)齊。這就是說您只可以將一個(gè)表示格式為x,8 的數(shù)與另一個(gè)表示格式也為x,8 的數(shù)相加、相減或相除。對(duì)具有不同格式的x 和y 進(jìn)行算術(shù)運(yùn)算時(shí),您首先應(yīng)保證小數(shù)點(diǎn)對(duì)齊。為了對(duì)齊不同格式的數(shù)字,您有兩個(gè)選擇:將帶有更多整數(shù)位的數(shù)與2X 相乘,或者將具有最小整數(shù)位的數(shù)除以2X。但是,除法會(huì)降低結(jié)果的精度,還可能導(dǎo)致結(jié)果超出容許公差。由于所有的數(shù)都可以利用兩種形式來存儲(chǔ),這樣您在FPGA 中通過移位操作可以很方便地對(duì)數(shù)進(jìn)行放大或縮小,其中左移或右移1 位分別放大或縮小了1 倍,實(shí)現(xiàn)十進(jìn)制小數(shù)點(diǎn)的對(duì)齊。為了對(duì)兩個(gè)格式分別為8,8和9,7 的兩個(gè)數(shù)相加,如果可以接受最低有效位的丟失,則您可以利用比例因子21 來放大格式為9,7 的數(shù),也可以將格式為8,8 的數(shù)縮小至格式為9,7。
例如,您打算將234.58 和312.732 這兩個(gè)數(shù)相加,而它們分別以8,8 和9,7 的格式來存儲(chǔ)。第一步,確定實(shí)際相加的16 位數(shù)。
從上可以看出,兩個(gè)加數(shù)分別為60052 和40029。但是,在相加之前,您必須對(duì)齊小數(shù)點(diǎn)。通過放大帶有更多整數(shù)位的數(shù)來對(duì)齊十進(jìn)制小數(shù)點(diǎn),您必須利用因子21 來放大9,7 格式的數(shù)。
然后,您通過執(zhí)行加法來計(jì)算結(jié)果:
以10,8 格式(140110 / 28)表示,則為547.3046875。
當(dāng)兩個(gè)數(shù)相乘時(shí),您無需對(duì)齊小數(shù)點(diǎn),因?yàn)槌朔ㄌ峁┝朔秶荴1 + X2,Y1 + Y2 的結(jié)果。將格式分別為14,2 和10,6 的兩個(gè)數(shù)相乘將得出一個(gè)整數(shù)位為24,小數(shù)位為8 的結(jié)果。
通過與除數(shù)的倒數(shù)相乘這種方法,在一個(gè)式子中您可以采用與小數(shù)相乘來代替除法。這種途徑可以顯著降低設(shè)計(jì)的復(fù)雜性。例如,將212.732(以9,7(40029)格式來表示)除以15,第一步是計(jì)算除數(shù)的倒數(shù)。
這個(gè)倒數(shù)必須被放大,以16 位數(shù)的形式來表示。
將這兩個(gè)數(shù)相乘,得出格式為9,23 的結(jié)果。
相除結(jié)果為:
當(dāng)預(yù)期的結(jié)果是20.8488,如果結(jié)果的精度不夠高,則您可以利用一個(gè)更大的比例因子來放大這個(gè)倒數(shù),以得到更精確的結(jié)果。因此,當(dāng)可以與一個(gè)數(shù)的倒數(shù)相乘時(shí),永遠(yuǎn)不要除以這個(gè)數(shù)。
溢出問題
在實(shí)現(xiàn)算法時(shí),結(jié)果必須不大于結(jié)果寄存器可以存儲(chǔ)的最大值。否則,就會(huì)發(fā)生溢出。當(dāng)溢出發(fā)生時(shí),存儲(chǔ)結(jié)果就會(huì)有誤,最高幾位會(huì)丟失。溢出的最簡(jiǎn)單實(shí)例是將2個(gè)16 位的數(shù)相加,每個(gè)數(shù)的值都是65535,然后將結(jié)果存儲(chǔ)在16 位寄存器中。
上述計(jì)算將使得這個(gè)16 位結(jié)果寄存器中的值為65534,但這個(gè)結(jié)果不正確。防止溢出的最簡(jiǎn)單方式是確定數(shù)學(xué)運(yùn)算允許的最大值,利用這個(gè)方程來確定所需結(jié)果寄存器的大小。
如果您正在開發(fā)一個(gè)平均器,計(jì)算50 個(gè)16 位輸入值的平均值,則可以計(jì)算所需結(jié)果寄存器的大小。
仍然利用同一個(gè)方程,需要一個(gè)22 位結(jié)果寄存器來防止溢出的發(fā)生。您也必須注意,在處理有符號(hào)數(shù)時(shí),如果遇到了負(fù)數(shù),應(yīng)該避免發(fā)生溢出。仍然利用此前的平均器實(shí)例,計(jì)算10 個(gè)有符號(hào)長(zhǎng)度為16 位的數(shù)的平均值,返回一個(gè)16 位的結(jié)果。
因?yàn)楹芊奖愕貙⒔Y(jié)果與除數(shù)倒數(shù)的擴(kuò)展值相乘,您將這個(gè)數(shù)與1/10 ? 65536 = 6554 相乘來確定平均值。
這個(gè)數(shù)除以216 等于-32770, 但16 位的輸出結(jié)果無法正確地表示這個(gè)數(shù)。因此,模塊的設(shè)計(jì)過程必須考慮溢出,必須檢測(cè)溢出,以確保不會(huì)輸出不正確的結(jié)果。
現(xiàn)實(shí)世界的實(shí)現(xiàn)方式
假設(shè)您正在設(shè)計(jì)一個(gè)模塊,用于實(shí)現(xiàn)一個(gè)轉(zhuǎn)換氣壓的轉(zhuǎn)移函數(shù),其中氣壓的單位是毫巴,海拔的單位是米。
輸入值的范圍是0 ~ 10 毫巴,分辨率是0.1 毫巴。模塊輸出要求精確到+/-0.01 米。因?yàn)槟K規(guī)范沒有確定輸入刻度,您可以通過下列等式來計(jì)算。
因此,為了實(shí)現(xiàn)最高的精度,您應(yīng)將輸入數(shù)據(jù)的格式設(shè)置為4 個(gè)整數(shù)位,12 個(gè)小數(shù)位。開發(fā)這個(gè)模塊的下一步任務(wù)就是利用未擴(kuò)展值并通過電子數(shù)據(jù)表計(jì)算出整個(gè)輸入范圍內(nèi)轉(zhuǎn)換函數(shù)的預(yù)期結(jié)果。如果輸入范圍過大而無法獲得合理的結(jié)果,則計(jì)算可接受的點(diǎn)數(shù)量。例如, 您使用100 個(gè)條目來確定整個(gè)輸入范圍的預(yù)期結(jié)果。在您計(jì)算出最初的非擴(kuò)展預(yù)期值之后,下一步是確定正確的常數(shù)比例因子,利用擴(kuò)展值來計(jì)算預(yù)期的輸出結(jié)果。為了實(shí)現(xiàn)最高的精度,您應(yīng)利用不同的因子來放大該式中每個(gè)常數(shù)。
多項(xiàng)式中第一個(gè)常數(shù)(A)的比例因子為:
多項(xiàng)式中第二個(gè)常數(shù)(B)的比例因子為:
因?yàn)樽詈蟮亩囗?xiàng)式常數(shù)(C)是一個(gè)純小數(shù),所以利用比例因子216 來放大它。
通過這些比例因子用戶可以計(jì)算出擴(kuò)展的電子數(shù)據(jù)表,如表1 所示。每一階段的計(jì)算結(jié)果將得出超過16 位的結(jié)果。
Cx2 的計(jì)算得出32 位、格式為4,12 + 4,12 = 8,24 的結(jié)果。然后與常數(shù)C 相乘,得出了48 位、格式為8,24 + 0,16 = 8,40 的結(jié)果。對(duì)于這個(gè)實(shí)例所要求的精度來說,利用40 位來表示小數(shù)有點(diǎn)多。因此,將這個(gè)計(jì)算結(jié)果除以232,以得出16 位、格式為8,8 的結(jié)果。在計(jì)算Bx 過程中,也將結(jié)果減小至16 位,以得出格式為5,11 的結(jié)果。
計(jì)算結(jié)果是Cx2,Bx 與A 列中對(duì)應(yīng)數(shù)之和。但是,為了獲得正確的結(jié)果,您首先必須擴(kuò)大A 和Cx2 ,并按x,11 格式對(duì)齊小數(shù)點(diǎn),或者縮小Bx 的計(jì)算結(jié)果并按8,8格式對(duì)齊小數(shù)點(diǎn),最終將小數(shù)點(diǎn)與A 和Cx2 的計(jì)算值的小數(shù)點(diǎn)對(duì)齊。
在這個(gè)例子中,我們將計(jì)算結(jié)果縮小23 倍,按8,8格式來對(duì)齊小數(shù)點(diǎn)。這種方法簡(jiǎn)化了需要移位的數(shù)量,因此減小了實(shí)現(xiàn)這個(gè)實(shí)例所需邏輯單元的數(shù)量。注意如果您通過縮小來對(duì)齊小數(shù)點(diǎn)的方式而沒有實(shí)現(xiàn)要求的精度時(shí),則必須擴(kuò)大A 和Cx2 的計(jì)算結(jié)果來對(duì)齊小數(shù)點(diǎn)。在這個(gè)實(shí)例中,計(jì)算結(jié)果擴(kuò)大了28。然后,您可以縮小這個(gè)結(jié)果,將其與從未擴(kuò)展值中獲取的結(jié)果比較。實(shí)際計(jì)算結(jié)果和預(yù)期結(jié)果之間的差值表示精度,利用電子數(shù)據(jù)表中MAX() 和MIN() 命令來獲得計(jì)算結(jié)果的最大誤差和最小誤差,而您在電子數(shù)據(jù)表?xiàng)l目的整個(gè)范圍內(nèi)都可以獲取計(jì)算結(jié)果的這兩個(gè)誤差。
當(dāng)基于電子數(shù)據(jù)表的計(jì)算結(jié)果確認(rèn)了您已經(jīng)實(shí)現(xiàn)了所要求的精度,則可以編寫并仿真RTL 代碼。如果需要,您可以設(shè)計(jì)一個(gè)測(cè)試平臺(tái),例如輸入值與電子數(shù)據(jù)表中的數(shù)據(jù)相同。這允許您將仿真輸出結(jié)果與基于電子數(shù)據(jù)表的計(jì)算結(jié)果進(jìn)行比較,以確保采用了正確的RTL 實(shí)現(xiàn)方案。
RTL 實(shí)現(xiàn)方案
RTL 實(shí)例利用有符號(hào)并行數(shù)學(xué)運(yùn)算在4 個(gè)時(shí)鐘周期之內(nèi)即可計(jì)算出結(jié)果。因?yàn)椴捎昧擞蟹?hào)的并行乘法,所以應(yīng)該注意到必須正確地處理由乘法產(chǎn)生的額外符號(hào)位。
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)成為了實(shí)現(xiàn)數(shù)學(xué)函數(shù)的理想工具,盡管實(shí)現(xiàn)算法需要具有更多的最初想法以及利用MATLAB? 或Excel 等系統(tǒng)級(jí)仿真工具來建模。一旦掌握了FPGA數(shù)學(xué)運(yùn)算的一些基本知識(shí),用戶就可以快速地實(shí)現(xiàn)數(shù)學(xué)算法。
評(píng)論