【转】Altera Scatter-Gather DMA (SG-DMA)的简单使用

本文介绍SG-DMA在Quartus7.2及以上版本中的使用方法,包括其三种工作模式:Memory-to-Stream、Stream-to-Memory及Memory-to-Memory,并通过实例演示了如何在SOPC中配置SG-DMA进行数据传输。

文章来源:http://www.cnblogs.com/scnutiger/archive/2010/02/06/1664980.html

在Quartus7.2之后的版本中,除了原有的基于avalon-mm总线的DMA之外,还增加了Scatter-Gather DMA这种基于avalon-ST流总线的DMA IP核,它更适合与大量数据流传输的场合,使用起来比较灵活,增加了与外设流器件配合的能力。由于网上关于SG-DMA介绍的资料比较少,因此这里简单介绍一下SG-DMA的使用,利用它可以搭配Altera的千兆网MAC核来实现千兆网方面的应用。

SG-DMA的数据手册已经介绍得非常详细,具体的相关寄存器和功能可能查阅相关手册。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流接口数据手册,依照需要设置接口。由于在本例中只有一个通道,也不校验错误,所以我们都设置为零。

图4. SOPC连接示意图

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 *)DISCRIPTOR_MEMORY_BASE;

    //打开sgdma_tx和sgdma_rx
    sgdma_tx_dev = alt_avalon_sgdma_open(SGDMA_TX_NAME);  //SGDMA_TX_NAME定义为"/dev/sgdma_tx"
    if(!sgdma_tx_dev) 
    {
        printf("[triple_speed_ethernet_init] Error opening TX SGDMA\n");
        return -1;
    }
    sgdma_rx_dev = alt_avalon_sgdma_open(SGDMA_RX_NAME);
    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(SGDMA_RX_BASE, ALTERA_AVALON_SGDMA_CONTROL_SOFTWARERESET_MSK);
    IOWR_ALTERA_AVALON_SGDMA_CONTROL(SGDMA_RX_BASE, 0x0);
    /* Reset TX-side SGDMA */
    IOWR_ALTERA_AVALON_SGDMA_CONTROL(SGDMA_TX_BASE, 0);
    IOWR_ALTERA_AVALON_SGDMA_STATUS(SGDMA_TX_BASE, 0xFF);

    //注册sgdma_rx回调函数
    alt_avalon_sgdma_register_callback(
             sgdma_rx_dev,
             (alt_avalon_sgdma_callback) &sgdma_rx_isr,
             (alt_u16)ALTERA_AVALON_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK | \
                            ALTERA_AVALON_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK | \ 
                            ALTERA_AVALON_SGDMA_CONTROL_IE_GLOBAL_MSK,
);

    //填充发送decriptor memory 并不需要自己填充,调用函数就好了。
    alt_avalon_sgdma_construct_stream_to_mem_desc(
                        &desc[0],             //主描述字
                        &desc[1],             //次描述字
                        rx_payload,
,
);
    //填充接收decriptor memory 
    alt_avalon_sgdma_construct_mem_to_stream_desc(
                        &desc[2],                 //主描述字
              &desc[3],                 //次描述字
              (unsigned int*)buf,  //发送指针
                        (256),                       //发送字数
,
, 
,
);

    //启动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]);
}

//回调函数,负责处理接收后数据,并重置sgdma_rx,本例中并未对数据进行处理
void sgdma_rx_isr(void * context, u_long intnum);
{
    int sgdma_status = IORD_ALTERA_AVALON_SGDMA_STATUS(SGDMA_RX_BASE);
    alt_sgdma_descriptor  *currdescriptor_ptr = &desc[0];
    
    if(sgdma_status & (ALTERA_AVALON_SGDMA_STATUS_CHAIN_COMPLETED_MSK | 
                     ALTERA_AVALON_SGDMA_STATUS_DESC_COMPLETED_MSK) ) 
    {
        desc_status = IORD_ALTERA_TSE_SGDMA_DESC_STATUS(currdescriptor_ptr);
        if( (desc_status & 
              (ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_CRC_MSK | 
               ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_PARITY_MSK | 
               ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_OVERFLOW_MSK |
               ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_SYNC_MSK | 
               ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_UEOP_MSK | 
               ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_MEOP_MSK | 
               ALTERA_AVALON_SGDMA_DESCRIPTOR_STATUS_E_MSOP_MSK ) ) == 0)
        {
            printf("RX descriptor reported OK\n");
        }        
        else 
        {
            printf("RX descriptor reported error\n");
        }
        IOWR_32DIRECT(&(currdescriptor_ptr->write_addr), 0, 
                (alt_u32)(rx_payload));             

        IOWR_32DIRECT(&(currdescriptor_ptr->actual_bytes_transferred), 0, 
                (alt_u32) ((ALTERA_AVALON_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK |
                      ALTERA_AVALON_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK) << 24) );
        
        // Re-start SGDMA (always, if we have a single descriptor)
        alt_avalon_sgdma_do_async_transfer(sgdma_rx_dev, &desc[0]);
    }
}

 

### Scatter-Gather DMA 的工作原理 Scatter-Gather DMA 是一种高级的直接内存访问技术,允许设备控制器一次性传输多个不连续的数据块到不同的内存位置或从不同内存位置读取数据[^1]。这种机制通过减少 CPU 和设备之间的交互次数来提高性能。 #### 基本概念 DMA 控制器通常会维护一个描述符表(Descriptor Table),其中包含了每个数据块的相关信息,例如起始地址和长度。这些描述符定义了哪些内存区域需要被访问以及如何处理它们。当启用 scatter-gather 功能时,DMA 控制器能够按照指定顺序依次完成对各个分散内存块的操作而无需中断主机处理器[^2]。 #### 实现细节 为了支持 scatter-gather 操作,在硬件层面需具备以下特性: - **描述符结构设计**: 描述符应至少包含目标缓冲区的基址(Base Address),传输字节数(Transfer Length)以及其他控制标志位如链式模式(Link Flag)[^1]。 - **链表管理**: 如果存在大量非连续的小型数据片段,则可以采用链接列表形式组织各条目;每项记录除了常规字段外还可能增加指向下一个节点指针或者偏移量的信息以便于继续执行后续任务直到整个过程结束为止[^2]。 以下是简化版伪代码展示如何初始化并启动基于scatter gather list(DMA SG List)架构下的事务流程: ```c struct dma_descriptor { uint32_t src_addr; /* Source Memory Address */ uint32_t dst_addr; /* Destination Memory Address */ uint16_t length; /* Transfer Byte Count */ uint8_t flags; /* Control Flags e.g., End Of Chain etc.*/ }; void setup_dma_transfer(struct dma_controller *controller, struct dma_descriptor descriptors[], int count){ controller->base_register = (uintptr_t)&descriptors[0]; // Set Base Pointer To First Descriptor for(int i=0;i<count;i++){ if(i != count -1 ){ descriptors[i].flags |= DESCRIPTOR_LINKED; } write_to_hardware(&descriptors[i]); // Write Each Entry Into Hardware Registers Or Memory Mapped Area. } trigger_dma(controller); // Start The Actual Data Movement Operation After All Setup Is Complete. } ``` 此函数接受一组 `dma_descriptor` 数组作为参数,并将其配置给特定的 DMA 控制器实例。它还会设置必要的寄存器以指示第一个描述符的位置,并逐个写入所有描述符至相应的硬件资源中去。最后调用触发方法正式启动实际的数据传送动作。 #### 散列收集的优势 相比传统单次大块拷贝的方式而言,利用scatter-gather 技术有诸多好处: - 减少了上下文中断开销因为不需要频繁切换当前活动进程状态; - 提高吞吐率由于允许多路并发操作从而充分利用带宽资源; - 更灵活适应复杂应用场景比如网络协议栈分片重组等问题解决起来更加简便高效[^1]. 然而值得注意的是实现该功能往往伴随着额外成本包括但不限于更复杂的驱动程序逻辑编写难度提升以及潜在的安全隐患如果未能妥善保护好敏感数据的话则可能导致泄露风险加剧等情况发生因此开发者应当权衡利弊合理选用合适的技术方案满足具体需求场景的要求.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值