在 Quartus7.2以後的版本中,除了原有的基於avalon-mm總線的DMA以外,還增長了Scatter-Gather DMA這種基於avalon-ST流總線的DMA IP核,它更適合與大量數據流傳輸的場合,使用起來比較靈活,增長了與外設流器件配合的能力。因爲網上關於SG-DMA介紹的資料比較少,所以這裏簡單介 紹一下SG-DMA的使用,利用它能夠搭配Altera的千兆網MAC核來實現千兆網方面的應用。css
SG-DMA的 數據手冊已經介紹得很是詳細(見Scatter-Gather DMA Controller Core
),具體的相關寄存器和功能可能查閱相關手冊。Altera爲了開發的便利,已經爲各個IP核設計好了HAL軟件層的代 碼,SG-DMA也不例外,所以使用的時候咱們沒有必要逐個配置相關寄存器,直接調用HAL層代碼便可。這也是使用這類IP核簡便的地方,只是須要清楚這 類代碼如何調用。ios
1. 首先咱們簡單看看SG-DMA的應用環境,從數據手冊中截下幾張圖片簡單介紹。web
SG-DMA有 三種工做方式,能夠工做在Memory-to-Stream即存儲接口到流接口,或者Stream-to-Memory即流接口到存儲接口,以及 Memory-to-Memory的存儲器到存儲器工做方式。工做在存儲器到存儲器的工做方式與普通DMA並沒有差異,沒有數據流處理的優點。另外SG- DMA增長了Descriptor Processor,能夠實現批量工做,從而進一步減輕Nios處理器的工做。只須要將Descriptor命令字寫入到相應的Descriptor memory中。咱們簡單看看以上的工做方式。緩存

圖1. Memory-to-Streamasync

圖2. Stream-to-Memoryide

圖3. Memory-to-Memory函數
2. 而後咱們直接進入主題,看在Altera的SOPC中如何鏈接使用SG-DMA器件。測試
M-to-M模 式就不作介紹了,這裏主要介紹M-to-S和S-to-M這兩種方式。咱們添加兩個SG-DMA器件,讓它們分別工做在這兩個工做方式下。鏈接示意以下所 示。注意到其中的descriptor memory的設置,原則上只要帶有avalon-mm接口的存儲器均可以用來作descriptor memroy,所以咱們能夠將decriptor memory與主存分離,亦能夠直接使用主存的一部分做爲descriptor memroy。但爲了避免影響主存的使用,最好將descriptor memroy分離。另外SG-DMA的有關設置,例如channel和error的位數控制能夠參考avalon-st流接口數據手冊,依照須要設置接 口。因爲在本例中只有一個通道,也不校驗錯誤,因此咱們都設置爲零。spa

3. SG-DMA HAL代碼調用。.net
要使得SG-DMA正式工做起來,咱們能夠直接調用HAL層代碼,省去不少開發時間。下面直接使用一段程序,添加部分註釋,相信SG-DMA的基本使用便可完成了,並無相信中的這麼複雜。
- #include <stdio.h>
- #include "altera_avalon_sgdma_descriptor.h"
- #include "altera_avalon_sgdma_regs.h"
- #include "altera_avalon_sgdma.h"
- #include "system.h"
- #include "alt_types.h"
- //注意包含這幾個頭文件
- alt_sgdma_dev *sgdma_tx_dev; //sgdma_tx設備文件
- alt_sgdma_dev *sgdma_rx_dev; //sgdma_rx設備文件
- alt_sgdma_descriptor *desc; //descriptor memory指針
- char buf[1000]; //SG-DMA傳送緩存,暫定1000字節作測試
- alt_u32 rx_payload[256]; //SG-DMA接收緩存
- void sgdma_rx_isr(void * context, u_long intnum);
- //咱們的基本思路就是,先配置好sgdma_rx和sgdma_tx的基本配置,而後設置好sgdma_rx的回調函數。
- //即接收數據完成以後調用的函數,最後啓動sgdma_tx完成dma發送。在這個過程當中涵蓋了sgdma_tx和sgdma_rx的基本使用
- int main()
- {
- int i;
- int timeout = 0;
- for(i=0; i<1000; i++) //填充緩存數據
- buf[i] = i%256;
- //重定義desc DISCRIPTOR_MEMORY_BASE定義在system.h中,即descriptor_memory的基地址
- desc = (alt_sgdma_descriptor *)DESC_MEM_BASE;
- //打開sgdma_tx和sgdma_rx
- sgdma_tx_dev = alt_avalon_sgdma_open("/dev/tx_sgdma");
- if(!sgdma_tx_dev)
- {
- printf("[triple_speed_ethernet_init] Error opening TX SGDMA\n");
- return -1;
- }
- sgdma_rx_dev = alt_avalon_sgdma_open("/dev/rx_sgdma");
- if(!sgdma_rx_dev)
- {
- printf("[triple_speed_ethernet_init] Error opening RX SGDMA\n");
- return -1;
- }
-
- /* Reset RX-side SGDMA */
- IOWR_ALTERA_AVALON_SGDMA_CONTROL(RX_SGDMA_BASE, ALTERA_AVALON_SGDMA_CONTROL_SOFTWARERESET_MSK);
- IOWR_ALTERA_AVALON_SGDMA_CONTROL(RX_SGDMA_BASE, 0x0);
- /* Reset TX-side SGDMA */
- IOWR_ALTERA_AVALON_SGDMA_CONTROL(TX_SGDMA_BASE, 0);
- IOWR_ALTERA_AVALON_SGDMA_STATUS(TX_SGDMA_BASE, 0xFF);
- //註冊sgdma_rx回調函數
- alt_avalon_sgdma_register_callback(
- sgdma_rx_dev,
- (alt_avalon_sgdma_callback) &sgdma_rx_isr,
- //ALTERA_AVALON_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK | //每一個描述符數據傳輸完產生中斷
- ALTERA_AVALON_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK | //全部描述符數據傳輸完時產生中斷
- ALTERA_AVALON_SGDMA_CONTROL_IE_GLOBAL_MSK //開中斷
- ,
- 0);
- //填充接收decriptor memory 並不須要本身填充,調用函數就行了。
- alt_avalon_sgdma_construct_stream_to_mem_desc(
- &desc[0], //主描述字
- &desc[1], //次描述字
- rx_payload, //接收地址
- 0, //length,爲0時當收到EOP時結束
- 0); //write_fixed
-
- //填充發送decriptor memory
- alt_avalon_sgdma_construct_mem_to_stream_desc(
- &desc[2], //主描述字
- &desc[3], //次描述字
- (unsigned int*)buf, //發送指針
- (256), //發送字數
- 0, //read_fixed
- 0, //不發送SOP
- 0, //不發送EOP
- 0); //暫不支持
-
- alt_avalon_sgdma_construct_mem_to_stream_desc(
- &desc[3], //主描述字
- &desc[4], //次描述字
- (unsigned int*)(buf+256), //發送指針
- (256), //發送字數
- 0,
- 1, //發送SOP
- 1, //發送EOP
- 0);
- //啓動sgdma_rx和sgdma_tx
- alt_avalon_sgdma_do_async_transfer(sgdma_rx_dev, &desc[0]);
- alt_avalon_sgdma_do_sync_transfer(sgdma_tx_dev, &desc[2]);
-
- for(i=0; i<256; i++)
- printf("%x ",rx_payload[i]);
- printf("\n\n\n\n");
- }
- //回調函數,負責處理接收後數據,並重置sgdma_rx,本例中並未對數據進行處理
- void sgdma_rx_isr(void * context, u_long intnum)
- {
- int i;
- alt_sgdma_descriptor *currdescriptor_ptr = &desc[0];
- if(alt_avalon_sgdma_check_descriptor_status(&desc[0])==0)
- {
- printf("RX descriptor reported OK\n");
- }
- else
- {
- printf("RX descriptor reported error\n");
- }
-
- }
或者也能夠這樣
- #include <stdio.h>
- #include "altera_avalon_sgdma_descriptor.h"
- #include "altera_avalon_sgdma_regs.h"
- #include "altera_avalon_sgdma.h"
- #include "system.h"
- #include "alt_types.h"
- //注意包含這幾個頭文件
- alt_sgdma_dev *sgdma_tx_dev; //sgdma_tx設備文件
- alt_sgdma_dev *sgdma_rx_dev; //sgdma_rx設備文件
- alt_sgdma_descriptor *desc; //descriptor memory指針
- char buf[1000]; //SG-DMA傳送緩存,暫定1000字節作測試
- alt_u32 rx_payload[256]; //SG-DMA接收緩存
- void sgdma_rx_isr(void * context, u_long intnum);
- //咱們的基本思路就是,先配置好sgdma_rx和sgdma_tx的基本配置,而後設置好sgdma_rx的回調函數。
- //即接收數據完成以後調用的函數,最後啓動sgdma_tx完成dma發送。在這個過程當中涵蓋了sgdma_tx和sgdma_rx的基本使用
- int main()
- {
- int i;
- int timeout = 0;
- for(i=0; i<1000; i++) //填充緩存數據
- buf[i] = i%256;
- //重定義desc DISCRIPTOR_MEMORY_BASE定義在system.h中,即descriptor_memory的基地址
- desc = (alt_sgdma_descriptor *)DESC_MEM_BASE;
- //打開sgdma_tx和sgdma_rx
- sgdma_tx_dev = alt_avalon_sgdma_open("/dev/tx_sgdma");
- if(!sgdma_tx_dev)
- {
- printf("[triple_speed_ethernet_init] Error opening TX SGDMA\n");
- return -1;
- }
- sgdma_rx_dev = alt_avalon_sgdma_open("/dev/rx_sgdma");
- if(!sgdma_rx_dev)
- {
- printf("[triple_speed_ethernet_init] Error opening RX SGDMA\n");
- return -1;
- }
-
- /* Reset RX-side SGDMA */
- IOWR_ALTERA_AVALON_SGDMA_CONTROL(RX_SGDMA_BASE, ALTERA_AVALON_SGDMA_CONTROL_SOFTWARERESET_MSK);
- IOWR_ALTERA_AVALON_SGDMA_CONTROL(RX_SGDMA_BASE, 0x0);
- /* Reset TX-side SGDMA */
- IOWR_ALTERA_AVALON_SGDMA_CONTROL(TX_SGDMA_BASE, 0);
- IOWR_ALTERA_AVALON_SGDMA_STATUS(TX_SGDMA_BASE, 0xFF);
- //註冊sgdma_rx回調函數
- alt_avalon_sgdma_register_callback(
- sgdma_rx_dev,
- (alt_avalon_sgdma_callback) &sgdma_rx_isr,
- //ALTERA_AVALON_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK | //每一個描述符數據傳輸完產生中斷
- ALTERA_AVALON_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK | //全部描述符數據傳輸完時產生中斷
- ALTERA_AVALON_SGDMA_CONTROL_IE_GLOBAL_MSK //開中斷
- ,
- 0);
- //填充接收decriptor memory 並不須要本身填充,調用函數就行了。
- alt_avalon_sgdma_construct_stream_to_mem_desc(
- &desc[0], //主描述字
- &desc[1], //次描述字
- rx_payload, //接收地址
- 0, //length,爲0時當收到EOP時結束
- 0); //write_fixed
-
- //填充發送decriptor memory
- alt_avalon_sgdma_construct_mem_to_stream_desc(
- &desc[2], //主描述字
- &desc[3], //次描述字
- (unsigned int*)buf, //發送指針
- (256), //發送字數
- 0, //read_fixed
- 0, //不發送SOP
- 1, //發送EOP
- 0); //暫不支持
-
- alt_avalon_sgdma_construct_mem_to_stream_desc(
- &desc[3], //主描述字
- &desc[4], //次描述字
- (unsigned int*)(buf+256), //發送指針
- (256), //發送字數
- 0,
- 1, //發送SOP
- 1, //發送EOP
- 0);
- //啓動sgdma_rx和sgdma_tx
- alt_avalon_sgdma_do_async_transfer(sgdma_rx_dev, &desc[0]);
- alt_avalon_sgdma_do_sync_transfer(sgdma_tx_dev, &desc[2]);
-
- for(i=0; i<256; i++)
- printf("%x ",rx_payload[i]);
- printf("\n\n\n\n");
- }
- //回調函數,負責處理接收後數據,並重置sgdma_rx,本例中並未對數據進行處理
- void sgdma_rx_isr(void * context, u_long intnum)
- {
- int i;
- alt_sgdma_descriptor *currdescriptor_ptr = &desc[0];
- if(alt_avalon_sgdma_check_descriptor_status(&desc[0])==0)
- {
- printf("RX descriptor reported OK\n");
- }
- else
- {
- printf("RX descriptor reported error\n");
- }
-
- alt_avalon_sgdma_construct_stream_to_mem_desc(
- &desc[0], //主描述字
- &desc[1], //次描述字
- rx_payload+64, //接收地址
- 0, //length,爲0時當收到EOP時結束
- 0); //write_fixed
- // Re-start SGDMA (always, if we have a single descriptor)
- alt_avalon_sgdma_do_async_transfer(sgdma_rx_dev, &desc[0]);
-
- }