作者:David Katz,Tomasz Lukasiak,Rick Gentile
數(shù)字信號(hào)處理器(DSP)在性能、外設(shè)、功耗和價(jià)格上已經(jīng)結(jié)合得非常好了,許多系統(tǒng)工程師希望利用DSP的優(yōu)勢(shì),取代傳統(tǒng)設(shè)計(jì)方案中使用的處理器,但一個(gè)潛在難題是設(shè)計(jì)工程師已經(jīng)為他們的應(yīng)用開發(fā)了大量C和C++代碼。很明顯,工程師都愿意在DSP平臺(tái)上利用現(xiàn)有的高級(jí)代碼,同時(shí)利用DSP的體系結(jié)構(gòu)特性以獲得更好的性能。
HLL與匯編語言
當(dāng)開發(fā)基于DSP的軟件時(shí),必須要做的一項(xiàng)工作就是確定使用哪一種程序設(shè)計(jì)方法學(xué),通常是在匯編語言和高級(jí)語言(HLL)(例如C或C++語言)之間進(jìn)行選擇。
C和C++的優(yōu)點(diǎn)包括模塊性、便攜性以及可重復(fù)利用性。
傳統(tǒng)的匯編語言由于其語法和縮寫難以理解和使用,長期以來不被人們所看好。目前的“代數(shù)語法”體系結(jié)構(gòu)有了很大改進(jìn)。表1是使用傳統(tǒng)風(fēng)格和使用代數(shù)格式的典型DSP指令實(shí)例,很顯然后者的結(jié)構(gòu)更加直觀。在所提供的實(shí)例中,r寄存器是數(shù)據(jù)寄存器,p寄存器是指針寄存器。
用匯編語言編程困難的一個(gè)原因是由于其數(shù)據(jù)流集中在DSP實(shí)際寄存器組、計(jì)算單元和存儲(chǔ)器之間。用C或C++語言編程,這種操作通常通過使用變量和函數(shù),或過程調(diào)用出現(xiàn)在更加抽象的處理等級(jí)之間,從而使得代碼更容易跟隨。
為提高DSP執(zhí)行效率,工具開發(fā)商會(huì)使用匯編語言對(duì)重要的數(shù)據(jù)密集代碼模塊進(jìn)行優(yōu)化。HLL編譯優(yōu)化轉(zhuǎn)換可以很好地完成該項(xiàng)工作,而且對(duì)DSP數(shù)據(jù)流和計(jì)算的直接控制能力是極為出色的。這就是為什么設(shè)計(jì)工程師通常結(jié)合使用C/C++和匯編語言。HLL適合于控制和基本的數(shù)據(jù)操作,而匯編語言適合于高效的數(shù)字計(jì)算。
適合高效編程的體系結(jié)構(gòu)的特性
為了使匯編語言程序員能夠有效地完成工作,需要了解處理器的結(jié)構(gòu)類型,以便能夠區(qū)分不適合高速數(shù)字計(jì)算的那些DSP處理器。這些適合高效編程的體系結(jié)構(gòu)結(jié)構(gòu)特性包括:
● 專用尋址模式
● 硬件環(huán)路結(jié)構(gòu)
● 高速緩沖存儲(chǔ)器
● 每周期多次操作
● 互鎖流水線
● 靈活的數(shù)據(jù)寄存器文擋
專用尋址模式
允許處理器在單周期內(nèi)訪問多個(gè)數(shù)據(jù)字需要靈活的地址產(chǎn)生方式。除了需要更大的以DSP為中心的16bit和32bit邊界的訪問尺寸外,還需要字節(jié)尋址以達(dá)到最有效的處理。這是非常重要的,因?yàn)橐恍┢胀☉?yīng)用(例如許多基于視頻的系統(tǒng))都是按照8bit數(shù)據(jù)操作。當(dāng)處理器訪問限制在單一邊界內(nèi)時(shí),處理器可能需要額外的周期來掩蔽掉相關(guān)的位。
另外一種有利的尋址能力是“循環(huán)緩沖”。該特性必須直接由處理器支持,無須專門的軟件管理開銷。循環(huán)緩沖允許程序員定義存儲(chǔ)器中的緩沖器,并且自動(dòng)跨越它們。一旦緩沖器設(shè)置好,則無須專門的軟件交互操作數(shù)據(jù)。地址發(fā)生器處理非同式跨幅,更重要的是可以處理圖1所示的“環(huán)繞式”特性。如果沒有這種自動(dòng)地址產(chǎn)生功能,程序員必須手動(dòng)跟蹤緩沖器,從而浪費(fèi)了寶貴的處理周期。
圖1 環(huán)繞式緩沖的例子
在此例中,基地址和起始索引地址=0x0;索引地址寄存器I0指向地址0x0;緩沖器長度L=44(11個(gè)數(shù)據(jù)元素×4字節(jié)/元素);修改寄存器M0=16(4個(gè)數(shù)據(jù)元素×4字節(jié)/元素)
實(shí)例代碼:
R0 = [I0++M0]; //R0 = 1,I0在代碼執(zhí)行后指向0x10
R1 = [I0++M0]; //R1 = 5,I0在代碼執(zhí)行后指向0x20
R2 = [I0++M0]; //R2 = 9,I0在代碼執(zhí)行后指向0x04
R3 = [I0++M0]; //R3 = 2,I0在代碼執(zhí)行后指向0x14
R4 = [I0++M0]; //R4 = 6,I0在代碼執(zhí)行后指向0x24
用于高效信號(hào)處理運(yùn)算(例如快速傅立葉變換(FFT)和離散余弦變換(DCT))的一種重要尋址模式是比特翻轉(zhuǎn)。顧名思義,“比特翻轉(zhuǎn)”就是將二進(jìn)制地址中的比特翻轉(zhuǎn),也就是說將權(quán)值最小的比特與權(quán)值最大的比特交換位置。由基為2的蝶形所要求的數(shù)據(jù)順序是“已翻轉(zhuǎn)比特”的順序,因此比特翻轉(zhuǎn)索引用來組合FFT級(jí)??梢杂?jì)算軟件中的這些比特翻轉(zhuǎn)索引,但是這樣做的效率非常低。圖2所示是比特翻轉(zhuǎn)地址流實(shí)例。
圖2 硬件比特翻轉(zhuǎn)機(jī)理
實(shí)例代碼:
LSETUP(起始, 終止)LC0=P0; //循環(huán)數(shù)P0=8
起始:R0 = [I0] || I0 += M0(BREV); //I0指向輸入緩沖器,在比特翻轉(zhuǎn)過程中自動(dòng)增加
終止:[I2++] = R0; ; //I2指向比特翻轉(zhuǎn)緩沖器
硬件循環(huán)結(jié)構(gòu)
循環(huán)在通信處理算法中是一項(xiàng)很重要的特性。有兩個(gè)與循環(huán)有關(guān)的關(guān)鍵特性能夠改進(jìn)多種算法的性能。第一個(gè)特性稱作“零開銷硬件循環(huán)”。隨著尋址能力的提高,循環(huán)結(jié)構(gòu)可以在硬件中實(shí)現(xiàn)。此外,當(dāng)該項(xiàng)功能用軟件實(shí)現(xiàn)時(shí),相關(guān)的開銷可減小到實(shí)時(shí)處理預(yù)算。零開銷循環(huán)允許程序員通過設(shè)置計(jì)數(shù)值并且定義循環(huán)邊界初始化循環(huán)。處理器將繼續(xù)執(zhí)行循環(huán)直到計(jì)數(shù)結(jié)束。
零開銷循環(huán)是大多數(shù)處理器必不可少的一部分,但“硬件循環(huán)緩沖器”實(shí)際上可以提高循環(huán)結(jié)構(gòu)的性能。它們用作循環(huán)中所執(zhí)行指令的一種高速緩沖存儲(chǔ)器。例如,在第一次執(zhí)行完一個(gè)循環(huán)之后,可將該指令保存在循環(huán)緩沖器中,從而無須在每一次循環(huán)都反復(fù)重取相同指令。通過將循環(huán)指令保存在能夠在單周期內(nèi)訪問的緩沖器中,能夠節(jié)省大量的周期數(shù)。該特性不需要程序員進(jìn)行額外的設(shè)置,但是需要知道循環(huán)緩沖器的尺寸以合理地選擇循環(huán)大小。
高速緩沖存儲(chǔ)器
通常典型的DSP具有少量的快速、內(nèi)置存儲(chǔ)器。MCU通??梢栽L問大量的外部存儲(chǔ)器。分層存儲(chǔ)器體系結(jié)構(gòu)將這兩種方案的優(yōu)點(diǎn)結(jié)合在一起,從而可提供幾種等級(jí)具有不同性能的存儲(chǔ)器。對(duì)于需要最高性能應(yīng)用,可以在單個(gè)核心時(shí)鐘周期內(nèi)訪問內(nèi)置SRAM。對(duì)于具有大代碼尺寸的系統(tǒng),可提供大量、較長等待延遲的片內(nèi)和片外存儲(chǔ)器。
這種分層存儲(chǔ)器體系結(jié)構(gòu)本身僅僅是發(fā)揮一定的作用,由于現(xiàn)在的高性能處理器常常運(yùn)行在較低的時(shí)鐘頻率下,因此大的應(yīng)用程序只能裝在較慢的外部存儲(chǔ)器中。此外,程序員被迫手動(dòng)將關(guān)鍵代碼移入和移出內(nèi)部SRAM。但是,通過將數(shù)據(jù)和指令高速緩沖存儲(chǔ)器加到該體系結(jié)構(gòu)中后,外部存儲(chǔ)器更易于管理,高速緩沖存儲(chǔ)器可以減少將指令和數(shù)據(jù)移入處理器內(nèi)核的手工操作。由于不需要考慮進(jìn)入內(nèi)核的數(shù)據(jù)和指令流管理,可以大大簡化編程模式。
指令高速緩存通常采用了某種類型的最近最少使用(LRU)算法,從而確保更常用的指令代替較少使用的指令。將一些內(nèi)置數(shù)據(jù)存儲(chǔ)器配置為高速緩存和部分SRAM的能力可以優(yōu)化性能。直接存儲(chǔ)器訪問(DMA)控制器能夠直接控制內(nèi)核,同時(shí)當(dāng)需要表中數(shù)據(jù)時(shí)將其讀入數(shù)據(jù)高速緩存。一旦高速緩存允許并且DMA控制器配置完畢,則程序員即可集中精力于內(nèi)核算法的開發(fā)。
每周期多次操作
通常以每秒執(zhí)行多少百萬條指令(MIPS)來衡量處理器。曾經(jīng)被保留用在高成本并行處理器中的多發(fā)布指令目前也可用在低成本、定點(diǎn)處理器中。除了在每個(gè)核心處理器周期內(nèi)完成多條ALU或MAC操作外,在相同周期內(nèi)還可完成額外的數(shù)據(jù)處理和數(shù)據(jù)存儲(chǔ)。存儲(chǔ)器通常劃分為子若干個(gè)存儲(chǔ)器組,這些存儲(chǔ)器組可以被內(nèi)核雙向訪問并且被DMA控制器隨機(jī)訪問。鑒于上述的基于硬件的尋址算法的分解方法,很明顯其可以在單個(gè)周期內(nèi)完成許多操作。
圖3示出多操作指令的一個(gè)實(shí)例。如圖3所示,在一個(gè)周期內(nèi)除了完成兩條獨(dú)立的MAC操作外,在相同的處理器時(shí)鐘周期內(nèi)還完成了數(shù)據(jù)讀取和數(shù)據(jù)存儲(chǔ)。
圖3 Blackfin多發(fā)布指令在單周期內(nèi)完成幾次操作
互鎖流水線
隨著處理器速度的增加,從整個(gè)電路級(jí)來看處理流水線必定會(huì)變得更深。然而,有些處理器具有“互鎖”流水線。這意味著當(dāng)完成匯編語言編程時(shí),程序員不必手動(dòng)調(diào)度或跟蹤通過流水線的數(shù)據(jù)和指令。處理器會(huì)自動(dòng)處理這些時(shí)序的事情。
靈活的數(shù)據(jù)寄存器文檔
最后,另外一個(gè)補(bǔ)充特性是通用數(shù)據(jù)寄存器集。在傳統(tǒng)的定點(diǎn)DSP中,字長通常是固定的。然而,具有能夠用作一個(gè)32bit(例如R0)或兩個(gè)16bit(例如分別用于低8bit和高8bit的R0.L和R0.H)的數(shù)據(jù)寄存器是非常有利的。在雙MAC系統(tǒng)中,這允許在單周期內(nèi)操作四個(gè)16bit的數(shù)據(jù)。
內(nèi)積
內(nèi)積或者標(biāo)量積在度量兩個(gè)向量的正交性時(shí)是很有效的操作。大多數(shù)C語言程序員應(yīng)該熟悉以下的內(nèi)積操作: short dot(short a[], short b[], int size) {
int i;
int output = 0;
for(i=0; i }
return output;
下面是Blackfin處理器匯編代碼的主要部分:
//P0=loop count, I0 & P1 are address registers
A1 = A0 = 0; // A0 & A1 are accumulators
LSETUP (loop1,loop1) LC0 = P0 ;// Setup hardware loop starting at label loop1:
loop1: A1 +=R1.H*R0.H, A0+=R1.L*R0.L||R1=[P1++]||R0=[I0++];
下面幾點(diǎn)說明了簡化這種緊湊編碼的DSP體系結(jié)構(gòu)特性。
硬件循環(huán)緩沖器和循環(huán)計(jì)數(shù)器在每次迭代的末端不需要跳轉(zhuǎn)指令。由于內(nèi)積是乘積總和,因此可在一次循環(huán)中完成。該匯編程序所示是LSETUP指令,它是執(zhí)行循環(huán)所需的唯一指令。
多發(fā)布指令允許在相同的周期內(nèi)執(zhí)行多條指令和兩次數(shù)據(jù)訪問。在每一次迭代時(shí),必須先讀取a和b值,然后相乘,最后寫回到可變輸出的運(yùn)行總和中。在許多MCU平臺(tái)上,這實(shí)際上等于四條指令。匯編代碼的最后一行示出在一個(gè)周期內(nèi)可執(zhí)行的所有操作。
并行ALU操作允許同時(shí)執(zhí)行兩條16bit指令。匯編代碼示出在每次迭代中使用的兩個(gè)累加器單元(A0和A1),這將迭代次數(shù)減少了50%,從而將原來的執(zhí)行時(shí)間減少了一半。
FIR算法
有限脈沖響應(yīng)(FIR)濾波器是一種等價(jià)于卷積操作很常見的濾波器結(jié)構(gòu)。A通過C操作看起來非常類似于內(nèi)積。
//將信號(hào)取樣到循環(huán)緩沖器中
x[cur] = sampling_function();
cur = (cur+1)%TAPS; // 在循環(huán)中增加cur指針
//完成乘加
y = 0;
for (k=0; k }
使用匯編語言編寫的FIR內(nèi)核格式有些類似于內(nèi)積。在這個(gè)特定的例子中,取樣值存儲(chǔ)在R0寄存器中,同時(shí)系數(shù)存儲(chǔ)在R1寄存器中。
// P0 存有濾波器抽頭
R0=[I0++] || R1=[I1++]; // 設(shè)置R0和R1的初始值
A1=A0=0; // 將累加器置零
LSETUP (loop1, loop1) LC0 = P0;//設(shè)置內(nèi)部循環(huán)
loop1: A1+=R0.L*R1.L, A0+=R0.H*R1.H||R0=[I0++]||R1=[I1++]; //計(jì)算
除了具有所描述的用于內(nèi)積的特性外,上面所示的FIR算法也可使用循環(huán)緩沖。
循環(huán)緩沖器不需要直接取余運(yùn)算。在C代碼片段中,%(取余)運(yùn)算符可提供一種用于循環(huán)緩沖的機(jī)理。正如匯編內(nèi)核中所示,這些取余運(yùn)算符不能被編譯為內(nèi)循環(huán)中的其他指令。相反,數(shù)據(jù)地址發(fā)生器寄存器I0和I1可在外循環(huán)中配置,以便在循環(huán)到達(dá)系數(shù)緩沖器邊界時(shí)能夠自動(dòng)返回起始處。
FFT算法
快速傅立葉變換(FFT)是許多信號(hào)處理算法不可缺少的一部分,其特點(diǎn)之一是輸入向量按照連續(xù)時(shí)間順序排列,但輸出按比特翻轉(zhuǎn)順序排列。大多數(shù)傳統(tǒng)的通用處理器要求程序員用單獨(dú)的程序整理比特翻轉(zhuǎn)輸出。在DSP平臺(tái)上,比特翻轉(zhuǎn)可用于尋址引擎。
比特翻轉(zhuǎn)尋址在實(shí)現(xiàn)FFT時(shí)不需要單獨(dú)的比特翻轉(zhuǎn)程序,允許硬件自動(dòng)將FFT算法的輸出進(jìn)行比特翻轉(zhuǎn),無須程序員編寫額外的程序,從而改進(jìn)了性能。
除了上面所提到的指令外,有些處理器也包括額外一套專用指令以支持多種應(yīng)用。提供這些指令的目的就是進(jìn)一步提高對(duì)算法的處理能力,例如維特比算法、Huffman編碼以及許多其他比特操作程序。
zrbj:gt
評(píng)論
查看更多