C64X DSP EDMA小結
(1)EDMA概要
①EDMA數(shù)據(jù)傳輸有兩種發(fā)起方式:
CPU發(fā)起的EMDA數(shù)據(jù)傳輸(非同步方式):需要傳輸時,CPU設置ESR寄存器的相應位為1,從而觸發(fā)一個EDMA事件的產(chǎn)生,事件對應的通道參數(shù)被送往地址硬件并且完成相應的處理,這種非同步方式的實時數(shù)據(jù)傳輸無需設定EER寄存器;
事件觸發(fā)方式EDMA數(shù)據(jù)傳輸(同步方式):ER寄存器保存外設發(fā)送過來的事件,一旦CPU設置EER寄存器的相應位為1后,ER中的事件才會提交給事 件編碼器(Event Encoder),并且進一步引起相關的傳輸參數(shù)的發(fā)送給地址產(chǎn)生硬件;如果EER中對應于某事件的位沒有置1,則ER寄存器中的事件將保留,一旦置1則 觸發(fā)EDMA的傳輸,這種特性可以應用到EDMA Chain傳輸,需要EER和CCER結合使用;
(2)EDMA數(shù)據(jù)傳輸類型:
EDMA有兩種類型的數(shù)據(jù)傳輸:1D和2D的(OPT.2DS和OPT.DDS標示源地址和目的地址的數(shù)據(jù)傳輸類型,即有4種組合方式);數(shù)據(jù)的維數(shù)表明了數(shù)據(jù)的組成方式:
①1D數(shù)據(jù)
數(shù) 據(jù)組成是“塊->幀->元素”;一個塊中的每幀數(shù)據(jù)是獨立處理(即可以理解亦為2D數(shù)據(jù),但是第二維永遠是1),每次處理是 一個元素,因此一幀中的數(shù)據(jù)元素可以是在同一個內(nèi)存地址、連續(xù)的地址或者是與同一幀中的前面的數(shù)據(jù)元素地址具有一定偏移(Offset,由ELEIDX通 道參數(shù)指定)的某地址;不同幀之間的內(nèi)存地址偏移由FRMIDX通道參數(shù)指定(兩幀的第一個元素之間的偏移或者后一幀的第一個元素的地址與前一幀的最后一 個元素地址的偏移,具體依賴于通道參數(shù)FS的設定);每幀的數(shù)據(jù)元素個數(shù)可以不同,由通道參數(shù)ELECNT指定,傳完一幀數(shù)據(jù)后由ELERLD重新載入塊 中的下一幀的數(shù)據(jù)元素個數(shù)ELECNT;塊中的幀的個數(shù)由通道參數(shù)FRMCNT指定;
1D數(shù)據(jù)傳輸有兩種同步方式:OPT.FS=0,元素同步方式;OPT.FS=1,幀同步方式;
元 素同步時,一次同步事件引起一幀中的一個元素的傳輸,每傳輸一次ELECNT遞減1;當同步事件觸發(fā)時,ELECNT=1表明是一幀的最后一個數(shù)據(jù)元素, 此時EDMA控制器除了完成最后這個元素的傳輸外,還需要重新載入ELECNT(通過ELERLD)并且FRMCNT遞減1;ELEIDX表示元素之間的 偏移,F(xiàn)RMIDX表示一幀的最后一個元素和下一幀的第一個元素之間的偏移;如果OPT.LINK=1時,傳輸完成中斷產(chǎn)生(FRMCNT=0)就重新從 PRAM中載入當前通道的其他參數(shù);
幀同步時,一次同步事件引起一幀數(shù)據(jù)的傳輸,F(xiàn)RMIDX表示兩幀的第一個元素之間的偏移;
② 2D數(shù)據(jù)
數(shù)據(jù)組成為“塊->數(shù)組->元素”,同一數(shù)組中的元素是連續(xù)存放的,因此ELEIDX無意義;數(shù)組中的元素素引表示2D的第一維,塊中的數(shù)組索引表示2D的第二維;FRMIDX的值依賴于OPT.FS的設定;
OPT.FS=0:表示一次同步事件傳輸一個數(shù)組,此時FRMIDX是數(shù)組首地址之間的偏移;每傳完一個數(shù)組,F(xiàn)RMCNT遞減1;當OPT.LINK=1并且FRMCNT遞減至0時,從PRAM的中重新載入當前通道的其他參數(shù);
OPT.FS=1:表示一次同步事件傳輸一個塊;FRMIDX表示前一個數(shù)組的最后一個元素的地址與后一個數(shù)組的第一個元素的地址之間的偏移;如果OPT.LINK等于1,則當整塊數(shù)據(jù)傳完時,重新從PRAM中為當前通道載入新的參數(shù);
(3)EDMA傳輸過程的源/目的地址的修改
在每次同步事件觸發(fā)EDMA數(shù)據(jù)傳輸,并且傳輸完成后,需要對源/目的地址進行更新;地址的更新方式由SUM/DUM進行設定,并且和2DS、2DD以及FS是密切相關的;
(4)數(shù)據(jù)元素大小和對齊方式
源/目的地址是在元素大小的邊界對齊的,因此要注意指向源/目的地址的指針的類型需要和OPT.ESIZE匹配;
(5)FRMCNT和ELEMCNT的更新
QUESTION:每次進行計數(shù)更新時,ELERLD的值哪里來的?
(6)EDMA Linking Transfer
當傳輸完成時(根據(jù)當前通道參數(shù)設定已經(jīng)傳完所有數(shù)據(jù)了,具體條件如下表所示),并且OPT.LINK=1,EDMA控制器會根據(jù)通道參數(shù) LINK(非OPT.LINK,16bits)從PaRAM中的其他位置(以24個字節(jié)對齊,因為通道參數(shù)為6WORD)重新載入當前傳輸通道的參數(shù);可 以鏈接到一個空的通道參數(shù)集(NULL Parameter)來停止EDMA傳輸,也可以自鏈接(用于循環(huán)緩沖處理或者重復的數(shù)據(jù)傳輸);Linking過程中不對相關寄存器作判定;
(7)EDMA中斷
C64X DSP的EDMA控制器的所有64個通道只產(chǎn)生一種中斷:EDMA_INT。如果需要讓第n個EDMA通道(或者QDMA請求)可以在傳輸完成時可以產(chǎn)生中斷通知CPU的話,應該如下設定:
OPT.TCINT=1:表示啟用傳輸完成中斷
OPT.TCC=n:在傳輸完成時,CIPR[TCC]=1,用于標記對應通道的傳輸完成,即便對應的CIER位沒有啟動,傳輸完成事件還是會在CIPR記錄,即掛起的含義所在;
OPT.CIER[n]=1:表示立即允許掛起的第n個通道傳輸完成事件觸發(fā)EDMA_INT中斷發(fā)送給CPU;
其中,TCC用于表示的通道的位數(shù)不夠時,可以擴展使用TCCM(即TCCM:TCC),CIPR和CIER均由兩個寄存器組成:CIPRL+CIPRH以及CIERL+CIERH。
中 斷服務例程ISR讀取CIPR,確定哪一個通道完成了數(shù)據(jù)傳輸,進行相應的處理。ISR在進行處理之前需要清除CIPR中確定了通道的位(寫入1到相關位 清除,寫入0不起作用),目的是記錄以后的傳輸完成事件的發(fā)生。在中斷服務例程對某通道的傳輸完成中斷進行服務后,因為期間有可能有其他通道傳輸完成了, 也已經(jīng)設置了CIPR中的相應位,或者也有可能本來有好幾個中斷掛起了并且現(xiàn)在觸發(fā)了,因此中斷服務例程必須檢查所有的CIPR并全部完成中斷服務才行。 當CIPR[n]&CIER[n]=1時,則設置對應的IFR為1,防止在退出ISR時丟失中斷并且使得可以多次調(diào)用ISR。中斷服務例程的一個 任務是清除CIPR和CIER中的與通道對應的位。
C64X DSP除了傳輸完成中斷外,還有交替性傳輸完成中斷,即在傳輸過程中完成一個傳輸子過程(如傳完一個數(shù)據(jù)元素、傳完一個幀/數(shù)組數(shù)據(jù);2D幀同步傳輸沒有 交替性傳輸完成中斷)給CPU發(fā)送一個中斷,相應的設定由OPT.ATINT、OPT.ATCC設定,處理過程和傳輸完成中斷雷同,區(qū)別只是在傳輸還沒全 部完成的過程中進行中斷處理而已。
這是使用BIOS和CSL LIB 作EDMA簡單例子. 在CCS BIOS 中需要作 中斷--EDMA--edmaHwi 的設置. Spru234 和 spra636a 都是有用的參考
解釋見注釋.
void main()
{
initEdma(); //Edma 初始化
initHwi(); //中斷初始化
EDMA_setChannel(hEdma); //開始EDMA傳送
while (1) {}
}
void initEdma(void)
{
EDMA_Config gEdmaConfig; //EDMA設置表
hEdma = EDMA_open(EDMA_CHA_ANY, EDMA_OPEN_RESET); //
gXmtTCC = EDMA_intAlloc(-1); //分配一個可用的TCC
.... //根據(jù)實際需要填寫EDMA設置表
EDMA_config(hEdma, &gEdmaConfig); 設置EDMA通道
hEdmaReload = EDMA_allocTable(-1); //得到EDMA重載標實 EDMA_config(hEdmaReload, &gEdmaConfig); // 設置EDMA重載通道
EDMA_link(hEdma, hEdmaReload); //設置EDMA重載
EDMA_link(hEdmaReload, hEdmaReload); //設置EDMA重載
EDMA_intClear(gXmtTCC); // 清除可能的EDMA中斷
EDMA_intEnable(gXmtTCC); // 打開EDMA中斷
EDMA_intHook(gXmtTCC, edmaHwi); //通知系統(tǒng)edmaHwi是中斷服務者
}
void edmaHwi(int tcc) //中斷服務
{
。。。。//準備數(shù)據(jù)
EDMA_setChannel(hEdma); //再次開始EDMA傳送
}
DAT_open Opens the DAT module
●函數(shù) Uint32 DAT_open(
int chaNum,
int priority, 優(yōu)先級
Uint32 flags
);
●參數(shù) chaNum 指定分配那個DMA通道,必須是下面其中之一:
DAT_CHAANY
DAT_CHA0
DAT_CHA1
DAT_CHA2
DAT_CHA3
priority 指定DMA通道的優(yōu)先級必須是下面其中之一
DAT_PRI_LOW
DAT_PRI_HIGH
flags 各種各樣的打開標志
DAT_OPEN_2D
●返回值 success 如果失敗返回0,如果成功返回非零值,失敗的原因如下:
DAT模塊已經(jīng)打開,
需要的資源沒有被分派。
●描述 這個函數(shù)打開DAT模塊,而且必須在調(diào)用其它DAT API 函數(shù)之前被調(diào)用,ChaNum 參數(shù)指定了哪個DMA通道被DAT模塊單獨的打開,對于帶有EDMA的設備,ChaNum 參數(shù)被忽略,原因是快速使用的DMA并不具有一個和它匹配的通道,對具有DMA的設備,ChaNum指定了將要使用哪個DMA通道, DAT_PRI_LOW設置DMA通道的CPU優(yōu)先級 DAT_PRI_HIGH 設置DMA通道的DMA優(yōu)先級,對具有EDMA的設備, ChaNum 被忽略,DAT_PRI_LOW設置 LOW priority ,DAT_PRI_HIGH 設置HIGH priority,一旦 DAT 模塊被打開,任何被分配的資源,例如一個DMA通道,仍然被分配,你可以調(diào)用 DAT_close() 來釋放這些資源,如果,準備通過DAT_copy2d 來進行2Dde傳輸, DAT_OPEN_2D 標志必須被指定,對具有DMA接口的設備指定這個標志, 將要導致一個全局重載計數(shù)寄存器和一個全局索引寄存器的分配這些全局寄存器在調(diào)用DAT_close()時將被釋放,注意:對具有EDMA的設備,DAT 模塊使用EDMA寄存器來提交傳輸請求,而且將使用通道中斷掛起寄存器(CIPR),中斷并不被使能 ,但是CIPR中的中斷標志要用到, DAT模塊使用完成碼的1到4來計算一個在CIPR 寄存器中的掩碼0x00000001E ,在具有EDMA的設備上使用DAT模塊的用戶必須避免使用完成碼1到4。
使用任何有效的DMA通道來打開DAT模塊的例子,
DAT_open(DAT_CHAANY,DAT_PRI_LOW,0);
用高優(yōu)先級模式使用DMA通道2打開DAT模塊,使用:
DAT_open(DAT_CHA2,DAT_PRI_HIGH,0);
使用2D拷貝打開DAT模塊,使用:
DAT_open (DAT_CHAANY, DAT_PRI_HIGH, DAT_OPEN_2D);
DAT_copy Copies a linear block of data from Src to Dst using DMA or
EDMA hardware
Function Uint32 DAT_copy(
void *src,
void *dst,
Uint16 byteCnt
);
Arguments
void *src 是一個源地址指針,
void *dst 是目的地址指針,
Uint16 byteCnt 是一個無符號16位數(shù),用來計算你要copy的byte數(shù),這個值是以字節(jié)計算的。Return Value xfrId Transfer ID
函數(shù)的返回值是一個傳輸通道的標號(xfrId Transfer ID),一般用作句柄
描述:
用CSL的DAT之前好像也得先設置好DMA或EDMA的參數(shù),DAT可以自動選擇DMA或EDMA的方式和通道,但是具體傳輸時還是要用到DMA和EDMA的參數(shù),所以即使是用DAT來傳輸數(shù)據(jù),也不能回避掉DMA/EDMA的設置。
The DAT module must be opened before calling this function. See
DAT_open().
The return value is a transfer identifier that may be used later on to wait for
completion. See DAT_wait().
Example #define DATA_SIZE 256
Uint32 BuffA[DATA_SIZE/sizeof(Uint32)];
Uint32 BuffB[DATA_SIZE/sizeof(Uint32)];
…
DAT_open(DAT_CHAANY,DAT_PRI_LOW,0);
DAT_copy(BuffA,BuffB,DATA_SIZE);