LJgibbs · 2020年06月03日

Zynq SDK 驱动探求(四)数据从软件来到逻辑

对于一个希望能够达到软件定义,硬件加速的协议栈来说,打通软硬之间的任督二脉是最为重要的一环。本文通过搭建一个 AXI DMA 子系统,将 PS 中网卡驱动软件接收到的数据包发送给逻辑部分,建立起 PS 到 PL 的数据流。
作者:李凡
来源: https://zhuanlan.zhihu.com/p/61356797

搭建逻辑 DMA 子系统

在 Vivado 的 Block Design 功能中通过拖动 IP 的方式建立一个 DMA传输子系统。DMA 子系统包括一个 AXI-DMA 和一个 AXI-FIFO。本文中的实验因为是把数据从 PS 端传输到 PL 端,所以只会用到接收 FIFO(但为了回环测试,所以又将接收 FIFO 的输出端连接到了 DMA 发送端(S2MM 对应于逻辑部分的发送端))。DMA IP 有多条总线通道,分别用于和 FIFO 以及 PS 连接,下图表示了 DMA 系统各部分的连接情况。

在 IP 连接图上会看到很多的总线矩阵( interconnect ),DMA 和 PS 之间通过两个总线矩阵连接,不考虑这些总线矩阵,本质上的总线连接就如上图,蓝色的是逻辑部分,红色的是 PS 部分。红蓝之间共有 3 条总线与一条信号线连接。系统中真正使用的为接收 FIFO,发送 FIFO 暂未使用,用虚线表示。

信号线是中断控制线,DMA 产生的中断会由 PS 进行中断响应。AXI-Lite 是一条配置总线,PS 端对 DMA 的配置控制都会通过这条总线进行。剩下的是一对 AXI 总线,这对总线分别对应着发送接收的数据流通道。图中的发送是指 DMA 到 PS 的数据流向,在本实验中没有用到,使用的是接收是 PS 到 DMA 的数据流向。

DMA 的工作可以理解为将数据从 AXI 总线的形式转为 AXIS 的形式。AXI 是指 Memory Mapped 的数据,即数据有对应的内存地址,数据存储于存储介质(如 DDR,BRAM)或者寄存器(比如网络 MAC 的数据缓冲寄存器)中。在本实验中 MM 指的是从 PS 的 MAC 数据缓冲寄存器中搬用的网络数据包。

AXIS 总线对应的一般是对数据进行处理的逻辑部分,AXIS 总线在本实验中指的是接收 FIFO,此时的数据没有地址的概念,以数据流的形式出现,简单来说当 valid 信号线为 1 时, data 上的数据有效。FIFO 作为从机 (slave)接收数据,所以整个从 PS 到 PL 的传输过程称为 MM2S (Memory Mapped to Slave),PL 到 PS 的数据流则称为 S2MM 。

本实验中的 DMA 配置为简单模式,数据传输的 AXI 总线连接到 PS 的 HP\_AXI 接口,控制用 AXI-Lite 连接到 PS 的 GP\_AXI 接口。我们将在后续的文章中讨论 DMA 以及 PS 上的多种 AXI 接口。

AXI-DMA 外设驱动

AXI-DMA 属于我们在前文中说的,由 Xilinx 官方提供的逻辑 IP 外设。区别于 PS 系统中包括的 ps7\_dma,DMA 在 Vivado 中是一个标准的 IP 核,所以官方有提供标准的外设驱动以及使用示例。
63.jpg
这里我们同样化用 example 中的代码,封装成我们自己的功能函数。

我们使用的是简单模式,使用中断的例程 axi\_dma\_loop\_test\_bsp\_xaxidma\_example\_simple\_intr,首先看下例程的结构。例程中主要的函数分别是

main(void)

主函数中完成了对 DMA 的初始化,准备一份发送数据,放在数组缓冲区中,调用 DMA 的 XAxiDma\_SimpleTransfer 函数发送,同时接收一份数据放到接收缓冲区中。因为硬件结构上为回环连接,发送的数据会被 DMA 接收,主函数会对发送接收的数据进行比较。

void TxIntrHandler(void *Callback)/void RxIntrHandler(void *Callback)

发送接收中断函数在 DMA 发送/接收后被调用,对当前置起的中断进行处理,如果没有错误发生,那么置起发送完成标志。这是一个全局变量,主程序在发送/接收中断完成后,对发送接收的数据进行比较。在正常的使用中,发送接收中断完成标志表示程序可以进行下一次发送/接收操作。

封装应用驱动函数

为了方便使用,我们以 example 的代码为基础,封装成自己的应用驱动

初始化 DMA 函数,在主函数初始化外设时调用。

int axidma_init(XAxiDma * dma_inst,XScuGic * intc_inst)
{
    int Status;
    XAxiDma_Config *Config;
    xil_printf("\r\n--- Init AXI_DMA Begin--- \r\n");
    Config = XAxiDma_LookupConfig(DMA_DEV_ID);
    if (!Config) {
        xil_printf("No config found for %d\r\n", DMA_DEV_ID);
        return XST_FAILURE;
    }
    Status = XAxiDma_CfgInitialize(dma_inst, Config);
    if (Status != XST_SUCCESS) {
        xil_printf("Initialization failed %d\r\n", Status);
        return XST_FAILURE;
    }
    if(XAxiDma_HasSg(dma_inst)){
        xil_printf("Device configured as SG mode \r\n");
        return XST_FAILURE;
    }
    /* Set up Interrupt system  */
    Status = SetupIntrSystem(intc_inst, dma_inst, TX_INTR_ID, RX_INTR_ID);
    if (Status != XST_SUCCESS) {
        xil_printf("Failed intr setup\r\n");
        return XST_FAILURE;
    }
    /* Disable all interrupts before setup */
    XAxiDma_IntrDisable(dma_inst, XAXIDMA_IRQ_ALL_MASK,
            XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrDisable(dma_inst, XAXIDMA_IRQ_ALL_MASK,
            XAXIDMA_DEVICE_TO_DMA);
    /* Enable all interrupts */
    XAxiDma_IntrEnable(dma_inst, XAXIDMA_IRQ_ALL_MASK,
            XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(dma_inst, XAXIDMA_IRQ_ALL_MASK,
            XAXIDMA_DEVICE_TO_DMA);
    xil_printf("\r\n--- Init AXI_DMA Scuess--- \r\n");
    return XST_SUCCESS;
}

DMA 发送函数,封装了 XAxiDma\_SimpleTransfer 函数。

int axidma_send_data(XAxiDma * dma_inst,u32 * send_buff,u32 data_len)
{
    int Status;
    Xil_DCacheFlushRange((UINTPTR)send_buff, data_len);
    Status = XAxiDma_SimpleTransfer(dma_inst,(UINTPTR) send_buff,
            data_len, XAXIDMA_DMA_TO_DEVICE);

    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }
    return XST_SUCCESS;
}

在 LwIP 中使用 DMA 驱动

在本文实验中,将 LwIP 和 DMA 子系统结合起来,将网卡驱动接收到的数据传输给逻辑部分。在 xilinx sdk 的 LwIP 例程的基础上,添加对于 AXI DMA 驱动的支持。

首先在主函数 main 中初始化平台时,初始化 DMA 。

接下来修改网卡接收中断函数 emacps\_recv\_handler ,加入 DMA 发送函数 axidma\_send\_data 。在将数据包 pbuf 交付上层软件之前,先启动 DMA 传输,传输长度为数据包的长度。我们这里只传输数据包的 payload 部分,所以传入指向 payload 的指针。

数据来了

DMA 开始传输后,数据就开始从 axis 接口进入接收 FIFO。可以使用 vivado 的 debug 功能设置触发电,观察到数据开始到来。我们可以把逻辑收到的数据和软件中 payload 指针指向的地址上的数据相比较,可以发现数据显然是一致的。但注意到此时传输给逻辑部分的数据也是按照主机字节序组织的。

对数据做些什么

目前初步的计划是在逻辑中进行计算数据的校验和,解析协议等操作,在 PS 需要这些操作的结果时,不需要在 PS 端再进行计算,只需要从寄存器中读取由逻辑部分计算完成的信息即可,以此实现减少 PS 端软件运算需求的目的。

结语

本文中简单介绍了 DMA 子系统的硬件搭建,DMA example 的介绍以及在 LwIP 网卡接收中断函数中添加 DMA 传输函数。介绍地比较简单,有兴趣的读者可以来进一步讨论。

推荐阅读

关注此系列,请关注专栏FPGA的逻辑
推荐阅读
关注数
10510
内容数
512
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息