在
Quartus7.2之后的版本中,除了原有的基于avalon-mm总线的DMA之外,还增加了Scatter-Gather
DMA这种基于avalon-ST流总线的DMA
IP核,它更适合与大量数据流传输的场合,使用起来比较灵活,增加了与外设流器件配合的能力。由于网上关于SG-DMA介绍的资料比较少,因此这里简单介
绍一下SG-DMA的使用,利用它可以搭配Altera的千兆网MAC核来实现千兆网方面的应用。
SG-DMA的
数据手册已经介绍得非常详细(见Scatter-Gather DMA Controller Core
),具体的相关寄存器和功能可能查阅相关手册。Altera为了开发的便利,已经为各个IP核设计好了HAL软件层的代
码,SG-DMA也不例外,因此使用的时候我们没有必要逐个配置相关寄存器,直接调用HAL层代码即可。这也是使用这类IP核简便的地方,只是需要清楚这
类代码如何调用。
1. 首先我们简单看看SG-DMA的应用环境,从数据手册中截下几张图片简单介绍。
SG-DMA有
三种工作方式,可以工作在Memory-to-Stream即存储接口到流接口,或者Stream-to-Memory即流接口到存储接口,以及
Memory-to-Memory的存储器到存储器工作方式。工作在存储器到存储器的工作方式与普通DMA并无差别,没有数据流处理的优势。另外SG-
DMA增加了Descriptor
Processor,可以实现批量工作,从而进一步减轻Nios处理器的工作。只需要将Descriptor命令字写入到相应的Descriptor
memory中。我们简单看看以上的工作方式。
图1. Memory-to-Stream
图2. Stream-to-Memory
图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流接口数据手册,依照需要设置接
口。由于在本例中只有一个通道,也不校验错误,所以我们都设置为零。
3. SG-DMA HAL代码调用。
要使得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]);
-
-
}
阅读(9743) | 评论(1) | 转发(1) |