DMA测试(Direct Register Mode)(PS + PL)

62 篇文章 11 订阅
9 篇文章 3 订阅

ZedBorad–(5)嵌入式Linux下的DMA测试(PS+PL)

本文将详细介绍如何在ZedBorad上使用AXI接口的DMA IP核。(文末会给出测试代码的下载链接)

DDR控制器、AXI DMA控制器以及PS之间的互连关系如下图所示。
这里写图片描述
DDR控制器已经包含在PS中,而AXI DMA和数据FIFO是需要我们自己在PL中实现的。由图可知,处理器使用AXI-Lite总线和DMA、DDR控制器进行交互。AXI_MM2S和AXI_S2MM为DDR和DMA之间的交互提供了通道。

  • MM2S即Memory-Mapped to Streaming
  • 若DMA使用Scatter-Gather模式,DMA和DDR内存控制器之间会有多一条AXI总线(简洁起见,图中省略)

【注意】本文默认读者已经熟悉Vivado、Xilinx SDK的基本使用方法。


硬件部分

下面开始介绍如何在ZedBorad上利用PS和PL实现DMA。

(1)新建Vivado工程,选择Create Block Design,从IP Catalog添加ZYNQ7 Processing System,然后点击Run Block Automation,确认Apply Board Perset前面的勾是勾上的。如下俩图所示。
这里写图片描述
这里写图片描述

(2)双击刚刚添加的ZNYQ7 PS,出现Re-Customize的窗口。左侧点击Clock Configuration,右侧展开PL Fabric Clocks,按下图设置。
这里写图片描述

(3)左侧继续点击PS-PL Configuration,右侧打开GP Master AXI Interface,按下图设置。
这里写图片描述

(4)然后点击OK,完成后PS变成这样:
这里写图片描述

(5)我们需要在此基础上连一根线,如下图所示。
这里写图片描述

(6)从IP Catalog添加AXI Direct Memory Access,然后点击Run Connection Automation,选择/axi_dma_0/S_AXI_LITE,如下图所示。弹出的窗口直接点OK。
这里写图片描述

这时整个系统的模块连接图应该是这样的:
这里写图片描述
双击DMA模块,打开配置窗口,去掉Enable Scatter Gather Engine前面的勾。

(7)然后我们需要将DMA的M_AXI_SG、M_AXI_MM2S和M_AXI_S2MM总线与PS的AXI slave接口相连。因为目前PS没有这些接口,因此双击PS进行Re-Customize,左侧点击PS-PL Configuration,右侧打开HP Slave AXI Interface,按下图配置后点击OK。
这里写图片描述

(8)回到Block Design后,点击Run Connection Automation,选择/processing_system7_0/S_AXI_HP0,如下图所示。
这里写图片描述

在弹出的对话框中,确保接连接的是DMA,如下图所示。
这里写图片描述

(9)继续点击Run Connection Automation,选择/axi_dma/M_AXI_SG;完了之后再点击Run Connection Automation,选择/axi_dma/M_AXI_S2MM。

(10)接下来,从IP Catalog中添加AXI4-Stream Data FIFO,按下面4张图进行连接。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

(11)由于本设计不需要AXI-Streaming的状态和控制信息,因此需要把它们去掉。双击AXI DMA模块,将Enable Control / Status Stream前面的勾去掉。

(12)接下来需要将DMA的中断信号连接到PS。双击PS,左侧点击Interrupts,按下图配置。
这里写图片描述

然后从IP Catalog中添加Concat模块,并按照下面3张图片进行连接。
这里写图片描述
这里写图片描述
这里写图片描述

(13)至此,硬件连接完毕。在菜单Tools中点击Validate Design。
这里写图片描述

如果前面操作正确,将弹出对话框提示成功,如下图所示。
这里写图片描述
(14)可以点击Regenerate Layout按钮,重新布局一下连接,如下图所示。
这里写图片描述

完了整个电路图长这样:
这里写图片描述

(15)调整过走线之后,保存Block Design,并在其上右击选择Create HDL Wrapper,如下俩图所示。
这里写图片描述
这里写图片描述

之后就可以生成比特流了,完了之后将工程Export到SDK。菜单File选择Export,如下面几张图所示。
这里写图片描述
这里写图片描述

菜单File中选择Launch SDK,如下图所示。
这里写图片描述


软件部分

经过一长串步骤之后终于转到了SDK。从Vivado转到SDK之后,左边Project Explorer下的design_x_wrapper_hw_platform就是上面Export的硬件。

由于本历程需要在嵌入式Linux下运行,因此首先应该生成BOOT.BIN文件。生成BOOT.BIN的方法在上一篇博客中有介绍:ZedBoard–(4)嵌入式Linux下的LED实验(PS + PL)

然后从File菜单中新建Application Project,如下图所示。
这里写图片描述

输入工程名字,Hardware Platform选择从Vivado导出的硬件平台;OS Platform选择standalone;BSP选择Create New。点击Next,如下图所示。
这里写图片描述

选择Hello World,如下图所示。
这里写图片描述

展开C/C++工程底下的src文件夹,在helloworld.c中输入测试代码:

/**
 * Using AXI Direct Memory Access v7.1
 * Direct Register Mode
 *
 * Author:  ZMJ@CSE, SEU
 * Date:    2016-07-12
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/mman.h>

#define DMA_BASE_ADDR   0x40400000      // Phy. address of axi dma
#define DMA_TX_ADDR     0x0F000000      // Phy. address of TX buffer
#define DMA_RX_ADDR     0x0F800000      // Phy. address of RX buffer

// Direct Register Mode Register Address
#define MM2S_DMACR      0x00        // MM2S DMA Control Register
#define MM2S_DMASR      0x04        // MM2S DMA Status Register
#define MM2S_SA         0x18        // MM2S DMA Source Address Register
#define MM2S_LENGTH     0x28        // MM2S DMA Transfer Length Register
#define S2MM_DMACR      0x30        // S2MM DMA Control Register
#define S2MM_DMASR      0x34        // S2MM DMA Status Register
#define S2MM_DA         0x48        // S2MM DMA Destination Address Register
#define S2MM_LENGTH     0x58        // S2MM DMA Buffer Length Register

// Shared Memory Settings
#define TX_PKT_SIZE     0x7FFFFF    // Size of TX Packet(0~0x7FFFFF)
#define RX_PKT_SIZE     0x7FFFFF    // Size of RX Packet(0~0x7FFFFF)

typedef unsigned int u32;

u32* axidma_base;           // A pointer to axidma base address.
u32* tx_packet;             // A pointer to tx buffer area.
u32* rx_packet;             // A pointer to rx buffer area.

void dma_set(u32* addr, int offset, u32 value);
u32 dma_get(u32* addr, int offset);
void memoryMap(int fd);     // Memory map from physical address to logical address
void initDMAReg();          // Initialize AXI DMA's register.
void startTransfer();       // Start DMA transfer.
void waitDMA();             // Wait until current transfer ends.
void dma_status();          // Print status of MM2S and S2MM channels.
void check();               // Check DMA result.

int main()
{
    // Open /dev/mem which represents the whole physical memory.
    int fd = open("/dev/mem", O_RDWR | O_SYNC);

    memoryMap(fd);      // Memory map from physical address to logical address
    initDMAReg();       // Initialize AXI DMA's register.

    // Test 1
    printf("\nSimple DMA Test 1:\n");
    int i;
    for (i = 0; i < TX_PKT_SIZE / 4; i++) tx_packet[i]= i;
    startTransfer();    // Start DMA transfer.
    waitDMA();          // Wait until current transfer ends.
    dma_status();       // Print status of MM2S and S2MM channels.
    check();            // Check DMA result.

    // Test 2
    printf("\nSimple DMA Test 2:\n");
    memset(tx_packet, 0, TX_PKT_SIZE);
    memset(rx_packet, 0, RX_PKT_SIZE);
    for (i = 0; i < TX_PKT_SIZE / 4; i++) tx_packet[i]= i;
    startTransfer();    // Start DMA transfer.
    waitDMA();          // Wait until current transfer ends.
    dma_status();       // Print status of MM2S and S2MM channels.
    check();            // Check DMA result.

    // Test 3
    printf("\nSimple DMA Test 3:\n");
    memset(tx_packet, 0, TX_PKT_SIZE);
    memset(rx_packet, 0, RX_PKT_SIZE);
    for (i = 0; i < TX_PKT_SIZE / 4; i++) tx_packet[i]= i;
    startTransfer();    // Start DMA transfer.
    waitDMA();          // Wait until current transfer ends.
    dma_status();       // Print status of MM2S and S2MM channels.
    check();            // Check DMA result.

    close(fd);

    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

【注意】完整的测试代码将在文末给出。

然后有几个地方需要设置一下:
(1)选中刚刚新建的C/C++工程,右键Property,展开C/C++ Build下的Tool Chain Editior,右边的Current toolchain选择Xilinx ARM GNU/Linux Toolchain,并且确保Used Tools和下图一致(如果缺少工具,需要点击Select Tools按钮添加)。设置完之后,将src文件夹下除测试的.c/.cpp文件以外的其他源文件删除。
这里写图片描述

(2)展开C/C++ Build,点Settings,在Binary Parsers里面,只勾上Xilinx ARM Linux ELF,如下图所示:
这里写图片描述

(3)检查一下C/C++ General下的Paths and Symbols右边的Libraries和Library Paths。对于本文提供的测试程序,这两个标签页下面应该为空(若不为空,请将原有的内容删除)。读者需要根据自身项目的实际情况添加Library并设置其路径,例如如果需要使用pthread.h的头文件,直接在Libraries下添加pthread的库即可(无需手动添加路径),如下图所示。
这里写图片描述

设置无误后,Ctrl + B将工程Build一下,如果没有错误将生成.elf的可执行文件。将.elf文件拷贝到SD卡中,在Linux下执行。程序执行时,读者可以在串口看到程序输出的信息。

SDK默认控制台不接收串口信息,所以需要设置一下(如果不想设置,可以打开第三方串口调试助手查看程序输出)。

设置方法如下:
(1)选中新建的Application Project,右键点击Properties。点击Run/Debug Settings,选中右边的.elf,点击Edit按钮,如下图所示:
这里写图片描述

(2)点击STDIO Connection选项卡,点击Connect STDIO to Console的复选框,设置好开发板所在的串口以及波特率,点击OK即可。如下图所示:
这里写图片描述

设置完成之后,读者就可以在控制台看到来自开发板的串口信息了。


参考资料

Using the AXI DMA in Vivado

Xilinx PG021


资源下载

AXI DMA测试Vivado工程,含XSDK FSBL Project和Application Project

基于ZedBoard的AXI DMA测试代码

Xilinx官方AXI DMA技术文档(PG021)

### 回答1: Zynq是一款Xilinx公司的片上系统(SoC),它将传统的处理系统(PS,即Processing System)和可编程逻辑(PL,即Programmable Logic)集成在一起。PSPL之间的通信是通过DMADirect Memory Access)实现的。 DMA是一种高效的数据传输方式,它可以在不经过处理器的情况下直接将数据从一个设备传输到另一个设备的内存中。在Zynq中,DMA控制器可以在PSPL之间进行数据传输,以实现高速的数据交换。 在使用Zynq的PSPL之间进行通信时,首先需要在PL中实例化一个DMA控制器,并将其配置为与PS内存进行交互。然后,在PS中通过相应的软件驱动程序或API接口配置和控制DMA控制器。通过设置合适的寄存器和缓冲区,可以实现从PSPL的数据传输或从PLPS的数据传输,以及在传输过程中的中断处理。 PSPL之间的DMA通信可以实现快速的数据交换,因为数据可以直接在PL中进行处理,无需经过PS的干预。这对于需要高速数据处理的应用非常有用,比如图像处理、信号处理等。 需要注意的是,使用DMA进行PSPL之间的通信需要合理地配置和管理DMA控制器的缓冲区和寄存器,以及在PSPL之间的数据传输过程中进行正确的同步和互斥操作,以避免数据冲突和错误。 总之,Zynq PSPL之间的通信使用DMA可以实现高速的数据传输和处理,为嵌入式应用带来了更大的灵活性和高效性。 ### 回答2: Zynq系统中的PS(Processing System)与PL(Programmable Logic)之间的通信可以通过DMADirect Memory Access)实现。 DMA是一种特殊的数据传输机制,它可以在不需要CPU的干预下,在内存和外设之间直接传输数据。在Zynq系统中,PSPL之间的DMA通信可以通过AXI(Advanced eXtensible Interface)总线实现。 首先,在Zynq系统中,PS可以使用AXI DMA控制器来设置数据传输的源地址、目的地址、传输长度等参数。这些参数可以通过PS的程序来配置。 然后,在PL中,我们可以使用AXI接口来设计自定义的IP(Intellectual Property)核,该IP核可以与AXI DMA进行通信。这个IP核可以通过PL的开发工具进行开发和配置。 接下来,通过适当的设计和配置,我们可以在PL中连接AXI DMA核和其他IP核,以实现PSPL之间的数据传输。例如,我们可以将一个读取数据的IP核连接到AXI DMA的输入端口,将一个写入数据的IP核连接到AXI DMA的输出端口。这样,当AXI DMA启动数据传输时,读取数据的IP核将从PL的某个存储器中读取数据,然后通过DMA传输到PS的某个存储器中,而写入数据的IP核将从PS的某个存储器中读取数据,然后通过DMA传输到PL的某个存储器中。 总之,通过使用AXI DMA和适当的设计和配置,Zynq系统中的PSPL之间可以进行高效的数据传输和通信。这种方式可以提高系统的性能和可扩展性,并且降低了CPU的负载,提供了更好的系统整合能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值