ARM PL330 DMA控制器開發(二)
四、S5PC100 PL330測試例子
由於PL330學習起來略顯煩瑣,因此建議在理解程式碼的基礎上做實驗,這樣才能對DMA的學習有一定的深入。篇幅有限這裡只給出核心的程式碼,若讀者想參考完整原始碼,請去華清遠見官方論壇上下載。
下面的程式碼目標要實現記憶體間的資料拷貝。對於S5PC100,有3個DMA控制器。要實現記憶體間的DMA訪問,需要使用DMA_mem。
如下圖所示為DMAC控制流程。
圖DMAC控制流程
配合上面的流程圖,可以編寫程式碼如下。
(1)相關的巨集定義。
#define MAX 100
#define Inp(addr) (*(volatileunsigned int *)(addr))
#define Outp(addr, data) (*(volatileunsigned int *)(addr) = (data))
extern void printf(const char *fmt, ...);
void int_dma();
volatile char sour[32] = "012345678901234567890123456789\n";
volatile char dest[32] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n";
//最終實現將32個位元組從sour的資料傳輸到dest
(2)設定SAR、CCR、DAR暫存器。
//main函式開始
uart0_init();
volatile char instr_seq[MAX];
int size = 0, x;
int loopstart, loopnum = 2; //每個迴圈傳輸16個位元組,傳輸2次
unsigned int source, destination,start, temp;
source = (unsigned int)sour;
destination = (unsigned int)dest;
start = (unsigned int)instr_seq; //記錄DMA指令的首地址
/*DMAMOV SAR0*/
instr_seq[size + 0] =(char)(0xbc);
instr_seq[size + 1] = (char)(0x0);
instr_seq[size + 2] =(char)((source>>0) & 0xff); //設定資料來源地址
instr_seq[size + 3] =(char)((source>>8) & 0xff);
instr_seq[size + 4] =(char)((source>>16) & 0xff);
instr_seq[size + 5] =(char)((source>>24) & 0xff);
size = 6;
/*DMAMOV DAR0*/
instr_seq[size + 0] = (char)(0xbc);
instr_seq[size + 1] = (char)(0x2);
instr_seq[size + 2] =(char)((destination>>0) & 0xff); //設定資料目標地址
instr_seq[size + 3] =(char)((destination>>8) & 0xff);
instr_seq[size + 4] =(char)((destination>>16) & 0xff);
instr_seq[size + 5] = (char)((destination>>24)& 0xff);
size += 6;
/*DMAMOV CC0. burst_size 8byte,burst_len 2*/
instr_seq[size + 0] =(char)(0xbc);
instr_seq[size + 1] = (char)(0x1);
//設定資料傳輸規則,每個迴圈傳輸burst_size* burst_len、原和目標地址變化規則、burst操作等
instr_seq[size + 2] = (char)(0x17);
instr_seq[size + 3] =(char)(0xc0);
instr_seq[size + 4] = (char)(0x5);
instr_seq[size + 5] = (char)(0x0);
size += 6;
(3)設定指令段的起始地址及執行第一次資料裝載並輸出FIFO。
/*DMALP LC0*/
instr_seq[size + 0] =(char)(0x20);
instr_seq[size + 1] =(char)(loopnum - 1);// 記錄迴圈的次數
size += 2;
loopstart = size;
/*DMALD*/
instr_seq[size + 0] =(char)(0x04); //從源讀資料
size += 1;
/*DMARMB*/
instr_seq[size + 0] =(char)(0x12);
size += 1;
/*DMAST*/
instr_seq[size + 0] =(char)(0x08); //寫資料到目標地址
size += 1;
/*DMAWMB*/
instr_seq[size + 0] =(char)(0x13);
size += 1;
(4)產生中斷,並延時一段時間。
/*可以在DMA指令執行過程中做延時。此處可以利用延時保證DMA傳輸完成後再停止DMA*/
/*DMALP LC0*/
instr_seq[size + 0] = (char)(0x20);
instr_seq[size + 1] = (char)(250); //迴圈次數
size += 2;
loopstart = size;
/*DMANOP*/
instr_seq[size + 0] = (char)(0x18); //DMA的NOP空指令,可以實現延時
size += 1;
/*DMALPEND 0*/
instr_seq[size + 0] = (char)(0x38);
instr_seq[size + 1] = (char)(size - loopstart);
size += 2;
/*DMASEV*/
instr_seq[size + 0] = (char)(0x34);
instr_seq[size + 1] = (char)(1<<3); //通過DMA通道1發出中斷申請,也可以選擇其它的通道
size += 2;
#endif
(5)結束DMAC控制。
/*DMAEND*/
instr_seq[size + 0] = (char)(0x0);
size += 1;
(6)開始DMAC控制,設定相應的中斷處理,並進行測試結果。
VIC0VECADDR18 = (unsigned int)int_dma;//DMA_mem的處理函式
INTERRUPT.VIC0INTENABLE |= 1<<18; //使能中斷控制器對應的中斷位
Outp(0xE8100000+0x20, 0x2); //使能控制器的1中斷通道,此處可以選擇其它的通道,要和DMASEV對應
/*DMAGO*/
do{
x = Inp(0xE8100D00);//檢測DMA狀態,確認可以操作
} while ((x&0x1)==0x1);
Outp(0xE8100D00+0x8,(0<<24)|(0xa0<<16)|(0<<8)|(0<<0));//DBGINST0 通道1
Outp(0xE8100D00+0xC, start);//DBGINST1
Outp(0xE8100D00+0x4, 0);//DBGCMD 執行DBGINST0、1中的DMAGO指令,start為開始地址
while(1);
//main函式結束
(7)ISR函式的實現如下。
void do_irq()
{
printf("in do_irq\n");
((void (*)(void))VIC0ADDRESS)();
}
/*ISR*/
void int_dma()
{
VIC0ADDRESS = 0;
Outp(0xE8100000+0x2C, 0x2);//清處DMA中斷掛起位,因為上面選擇的是通道1,所以清楚對應的位
printf("DMA Ending!\n");
printf("sour = %s", sour);
printf("dest = %s", dest);
}
實驗除錯過程與結果:
(1)將程式編譯後生成.bin檔案,開啟終端使用uboot的dnw命令通過USB線將.bin檔案下載到0x20008000這個地址,接著使用go命令去執行測試程式。
(2)可以看到下圖所示的測試結果,如圖所示。
五、小節
本文從PL330出發,介紹了基本的S5PC100下DMA控制器的操作方式和程式設計模型,旨在將最新的DMA控制技術以最簡單的方式呈現。筆者還實現了串列埠控制器的收發之間使用DMA傳輸的例程。讀者如果感興趣可以在華清遠見研發中心關於FS_S5PC100平臺的論壇上下載。硬體控制器關聯的DMA和記憶體間的DMA控制差別比較大的是DMA每次傳輸依賴控制器發出申請,DMA需要等待到一次申請後才能完成一次傳輸。而記憶體間的DMA可以由程式主動發起。
-
DMA memtomem 單次傳輸成功。
PL330 的DMA驅動和以往驅動的不同之處:
s3c2440/s3c6410 處理器內部的 DMA 都是提供特殊功能暫存器 SFR 對 DMA 進行程式設計,但 PL330 不是這樣。
PL330 DMA 的基本工作原理是:
a) DMA 有自己的特殊指令集,基本是1-6個位元組不定長,主要有 DMAMOV/DMALD/DMAST/DMAGO
b) DMA 的驅動主要包含兩個工作:
### 1) 開闢一個buf,在buf裡面存放DMA的二進位制指令,可以完成資料傳輸操作。
### 2) 通過執行 DMAGO 指令,使得 DMA 內部的取指部件 pc 跳轉到 buf,從 buf 取出並執行相應的指令。
DMAGO 指令的本身的執行,是通過寫 S5PV210 晶片的 DBGCMD/DBGINST0/DBGINST1 這3個暫存器實現的。
c) linux 下的 pl330.c 是可以參考使用的驅動,主要包含了一系列 _emit_MOV/_emit_LD/_emit_ST 等類似指令的介面。
這些介面完成類似編譯器的工作,把一個DMA操作函式,轉換為存放在 buf 數組裡面的二進位制指令。
d) 驅動程式設計的關鍵在於形成 buf 內部的正確機器指令,然後通過對 DBGCMD/DBGINST0/DBGINST1 這3個暫存器進行程式設計,完成跳轉到 buf 執行指令。 -
DMALP/DMALPEND 還未成功,仍在除錯