DMA搬运数据实验

前言

阅读学习杰哥过去留下的 limfx 文档及相关资料后,我发现完整工程并不是一次性搭建完成的,而是由多个小实验逐步验证、组合而成。因此,本阶段我也计划先从几个关键实验入手,在实践中逐步理解 PS 端 DMA 搬运、DDR 缓存以及后续留盘功能的实现方式。

1. 实验目标

PL 端产生测试数据,通过 AXI DMA 搬运到 PS 端 DDR 中,再由 Vitis 裸机程序读取 DDR 数据,并通过 UART 串口打印出来,以验证 PL 到 PS DDR 的数据通路是否正确。

2. 实验设计与流程

总体流程如下所示:

测试数据产生模块(testdata_gen.v)    // 模拟产生 5 张子卡、共 160 通道的同步数据
    ↓
数据格式转换模块(ad_to_dma.v)       // 将 5 路 512 bit 数据整理为 AXI-Stream 数据,并通过FIFO完成缓存与跨时钟域,将数据送入 DMA
    ↓
AXI DMA                              // 通过 S2MM 通道将数据写入 PS DDR
    ↓
Vitis 裸机程序读取 DDR                // 从指定 DDR 地址读取 DMA 写入的数据
    ↓
UART 串口打印验证                     // 打印数据内容,判断搬运结果是否正确

测试数据产生模块

testdata_gen.v 用于在 PL 端产生测试数据,模拟真实 AD 采集数据。当前设计中模拟 5 张子卡,每张子卡以2M/s的采样率输出 512 bit 数据,对应 32 个 16 bit 通道。该模块的输入输出为:

input  wire         clk,            //系统时钟
input  wire         rst_n,          //复位信号
input  wire         enable,         //模块使能信号
output reg  [511:0] testdata0_o,    //子卡0测试数据
output reg  [511:0] testdata1_o,    //子卡1测试数据
output reg  [511:0] testdata2_o,    //子卡2测试数据
output reg  [511:0] testdata3_o,    //子卡3测试数据
output reg  [511:0] testdata4_o,    //子卡4测试数据
output reg          data_valid      //数据有效信号

测试数据格式为:

testdata0_o: sample_cnt + 通道号
testdata1_o: sample_cnt + 通道号 + 0x1000
testdata2_o: sample_cnt + 通道号 + 0x2000
...

数据格式转换模块

ad_to_dma.v 用于接收 5 张子卡的数据,并将其拆分、整理为 128 bit 宽度的 AXI-Stream 数据流,供后续 AXI DMA 接收。

由于每个采样点的数据大小为512*5 = 2560 bit,也就是需要拆分成 20 个 128 bit 的数据,考虑到为便于后续数据解析,每个采样点的边界应当清晰,所以在每个采样点前加一帧作为帧头,因此当前帧格式为:

1 个帧头 + 20 拍数据

数据转换模块的输入输出为:

input  wire         ad_clk,              //PL侧时钟信号        
input  wire         ad_rst_n,            //PL侧复位信号                                             
input  wire         dma_clk,             //PS侧时钟信号    
input  wire         dma_rst_n,           //PS侧复位信号                                     
input  wire         enable_i,            //模块使能信号 
                                     
input  wire [511:0] ad_data0_i,          //采集数据及有效信号输入
input  wire [511:0] ad_data1_i,          
input  wire [511:0] ad_data2_i,          
input  wire [511:0] ad_data3_i,          
input  wire [511:0] ad_data4_i,          
input  wire         data_valid_i,        
                                                                
output wire  [127:0] m_axis_tdata_o,     //AXI-Stream 数据总线,输出给 AXI DMA S_AXIS_S2MM
output wire  [15:0]  m_axis_tkeep_o,     //字节有效标志,每 1 bit 对应 tdata 中 1 byte,16'hFFFF 表示 128 bit 全部有效
output wire          m_axis_tvalid_o,    //数据有效信号,表示当前 tdata/tkeep/tlast 有效
input  wire          m_axis_tready_i,    //下游就绪信号,由 AXI DMA/FIFO 返回,表示可以接收当前数据
output wire          m_axis_tlast_o      //帧结束信号,拉高表示当前拍是一帧数据的最后一拍

DMA配置及Block Design设计

Block Design 中需要加入 Zynq PS、AXI DMA 以及自定义 RTL 模块。 加入后首先配置Zynq UItraScale+ MPSoC: alt text

配置好Zynq UItraScale+ MPSoC后需要进行DMA配置。DMA配置为:

SG:开启
Control/Status Stream:关闭
MM2S:关闭
S2MM:开启
S2MM Stream Width:128 bit
S2MM Memory Map Width:128 bit
SG Length Width:26

然后进行Block Design连线。Block Design设计如下所示: alt text

3. 仿真与测试

PL端测试

通过顶层模块将测试数据产生模块数据格式转换模块实例化,使能信号设置为复位后拉高1s,产生640MB的测试数据。

always @(posedge ad_clk or negedge ad_rst_n) begin
    if (!ad_rst_n) begin                          
        enable <= 1'b0;                           
        enable_cnt <= 32'd0;                      
    end else begin                                
        if(enable_cnt == 32'd100_000_000)begin    
            enable <= 1'b0;                       
        end else begin                            
            enable <= 1'b1;                       
            enable_cnt <= enable_cnt+32'd1;       
        end                                       
    end                                           
end                                               

然后搭建仿真平台,通过仿真波形观察数据及其时序是否正确。当前仿真结果显示,每帧输出稳定为 21 拍,帧头固定为55AA55AA55AA55AA55AA55AA55AA55AA,符合当前帧格式设计。

alt text

DDR写入测试

4. 下一步计划

  1. 检查并完善Block Design的设计。
  2. 生成 bitstream,并导出包含 bitstream 的 .xsa 文件。
  3. 在 Vitis 中创建裸机工程,编写 DMA 单次搬运测试程序。
  4. 通过 UART 打印 DDR 中的数据,验证 DMA 是否成功写入。

本文章使用limfx的vscode插件快速发布