1. 程式人生 > >ARM PL330 DMA控制器開發(二)

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可以由程式主動發起。


  1. 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 執行指令。

  2. DMALP/DMALPEND 還未成功,仍在除錯