第十一节,ZYNQ的AXI_DMA的使用

本文介绍了ZYNQ的AXI_DMA使用,包括DMA控制器架构原理,如工作模式、多通道模式、循环模式等,还提到了Cache一致性问题及解决办法。同时进行了AXI_DMA实验,设计了基本的DMA环路,给出了PL端和PS端的程序设计及各步骤的函数源码。

ZYNQAXI_DMA的使用

1 DMA控制器架构原理

AXIDMA:官方解释是为内存与AXI4-Stream外设之间提供高带宽的直接存储访问,其可选的scatter/gather功能可以将CPU从数据搬移任务中解放出来,在ZYNQ中,AXIDMA就是FPGA访问DDR3的桥梁,不过该过程受ARM的监控和管理。使用其他的IP(也是AXI4-Stream转AXI4-MM)可以不需要ARM管理。AXIDMA IP有6个接口,S_AXI_LITE是ARM配置DMA寄存器的接口,M_AXI_SG是从存储器加载buffer descriptor的接口,剩下4个接口构成两对,S2MM和MM2S表示数据的方向,AXI存储器一侧的接口,AXIS是FPGA一侧的接口。AXIDMA IP和ARM自带的DMA是很像的,只不过不具备从存储器到存储器的功能,当然也可以将S2MM和MM2S接口与AXIS接口直接相连,其结构如图1所示。

图1,AXI_DMA的IP结构

AXIDMA工作模式可以分为两种,Direct Register Mode和Scatter/Gather Mode。

Direct Register Mode具备DMA的基本功能,除了控制寄存器和状态寄存器之外,给出目的地址和传输长度之后就可以开启一次传输了。但是Direct Register Mode模式配置完一次寄存器之后只能完成存储器连续地址空间的读写,如果有需求往不同空间搬运数据的话,那就需要重新配置寄存器开启一次新的传输。

Scatter/Gather Mode配置灵活,其工作模式复杂很多。Scatter/Gather Mode把关于传输的基本参数(起始地址,传输长度,包信息等)存储在存储器中,一套参数称之为Buffer Descriptor(BD),在工作过程中通过上面提到的SG接口来加载BD且更新BD中的状态。Scatter/Gather Mode下的寄存器没有Address、Length相关寄存器了,取而代之的是CURDESC、TAILDESC。非多通道模式下的BD,主要有四部分内容:NXTDESC、BUFFER_ADDRESS、CONTROL、STATUS。NXTDESC指定下一个BD的地址,由此可以构成一个BD链条,AXIDMA可以顺着该链条依次fetch BD,BUFFER_ADDRESS指定传输的源地址或目的地址,CONTROL主要是length和包信息,STATUS反映该BD完成后的状态。AXIDMA启动后,首先从CURDESC指定的位置加载BD,完成当前BD的传输任务后根据BD链条找到下一个BD,依次完成BD指定的传输,知道遇到TALDESC指定的BD才停止。

Multichannel模式:在Scatter/Gather Mode下S2MM和MM2S都支持多个通道,Direct Register Mode不支持多通道,多通道相比非多通道,BD中增加了TID和TDEST,用来区分不同的通道。多通道支持2D-Transfer,从buffer address开始,读写HSIZE后跳过剩余的Stride – HSIZE个地址单元,下一次从buffer address + Stride位置开始,此过程迭代VSIZE此后结束该BD指定的传输。在Multichannel模式下S2MM有16个通道,每个通道都有独立的CURDESC和TAILDESC寄存器,而CR和SR则是共用的。而MM2S的多个通道共用一个CURDESC和TAILDESC寄存器,MM2S端只能等当前包传输完成才能开始下一次的传输,可能这与CPU不太容易同时操纵多个通道的数据包发送有关系。所以在实际使用时只能先执行一个通道的发送任务再执行另一个通道的发送任务。

Cyclic DMA:循环模式是在Scatter/Gather Mode模式下的一种独特工作方式,在Multichannel Mode下不可用。正常情况下的Scatter/Gather Mode模式在遇到Tail BD就应该结束当前的传输,但是如果使能了Cyclic模式的话,在遇到Tail BD时会忽略completed位,并且回到First BD,这一过程会一直持续直到遇到错误或者人为终止。Cyclic模式只需要在开启传输前设置好BD链条,工作之后就再不需要管了。

Data Cache:在zynq内部AMR CPU和DDR3之间存在两级缓存区,分别是L1 I/D Cache和L2 Cache,它们都是32-byte line size。Data Cache的使用带来了一个问题,DMA和CPU都与DDR3有数据往来,可CPU的Cache是不知道DMA对DDR3的数据读写过程的,也就是说CPU得到的数据很可能是“假的”,这就是Cache一致性问题。解决该问题的办法是在程序中使用flush函数(invalid函数)及时将Cache的数据写入到DDR3(从DDR3读取数据到Cache),也就是说要避免该问题就需要注意编码时加上flush函数。

 AXI_DMA实验

AXI_DMA实验,设计一个最基本的DMA环路,实现DMA的环路测试,在通过PS端发送数据到DMA然后DMA在把数据发回到DDR里面,PS读取内存地址里面的数据,对比接收的数据是否和发送出去的一致。DMA的接口部分使用了data_fifo的IP链接,其数据流如图2所示。

程序设计:

PL端设计:

建立好工程后,创建一个block,然后在其中添加zynq的IP,AXI4-Stream Data FIFO的IP,AXI Direct Memory Acceess的IP,以及Concat的IP。如图3所示。然后根据需求配置好各个IP的参数。

zynq的IP的参数配置如图4所示,在配置了ZYNQ的基本最小系统后,增加一些配置,由于这个系统需要PS提供时钟给AXI_DMA,所以在Clock Configuration选项卡中配置了PL Fabric clocks->FCLK_CLK0为100M,启动1路HP接口,HP接口是ZYNQ个高速数据接口,所以在PS-PL Configuration选项卡选项中配置了一路HP Slave AXI Interface->S AXI HP0 interface,需要用到PL的中断资源,所以在Interrupts选项卡中增加了PL中断,Fabric Interrupts->PL-PS Interrupt Port->IRQ_F2P[15:0]。

 

图3 添加IP

图4 ZYNQ的配置

AXI Direct Memory Access IP的配置如图5所示,同时勾选读通道和写通道,另外设置,Wideh of buffer length register 为23bit 这个含义是2的23次方8,388,607bytes8M大小,这里设置14bit 就够用了,长度越大需要的资源也就越多

      

图5 AXI Direct Memory Access的配置

AXI4-Stream Data FIFO的IP的配置如图6所示。

图6 AXI4-Stream Data FIFO的配置

Concat的IP的配置如图7所示。

图7 Concat的IP配置

连接好物理连线,最终生成硬件版图如图8所示。

图8 硬件配置

PS端设计:

由于是一个回环设计,所以在实现DMA传输数据时,需要用到接收中断和发送中断,所以外设初始化配置和测试流程如下:

  1. 初始化AXI_DMA的数据结构配置,配置其工作模式为Direct Register Mode;
  2. 将GIC中断映射到硬件中断上;
  3. 配置好GIC中断,并注册好发送完成中断和接收完成中断的服务函数,设置好对应中断的优先级,并且开启接收和发送中断;
  4. 完成中断服务函数的编写(清除中断,解析中断源,完成中断操作);
  5. 配置发送数据缓存和接收数据缓存,设置好DMA的源地址和目的地址以及发送数据长度,触发DMA发送传输和接收传输。

步骤1函数源码:

int Xaxi_dma_init(void)

{

    int Status = 0;

    XAxiDma_Config* XaxidmaConfigInit = NULL;

    //1,find device

    XaxidmaConfigInit = XAxiDma_LookupConfig(XPAR_AXIDMA_0_DEVICE_ID);

    //2,config Xaxidma

    Status = XAxiDma_CfgInitialize(&XAxi_Dma,XaxidmaConfigInit);

    if (Status != XST_SUCCESS)

    {

        xil_printf("Initialization failed %d\r\n", Status);

        return XST_FAILURE;

    }

    //3,axidma mode is sg

    if(XAxiDma_HasSg(&XAxi_Dma))

    {

        xil_printf("Device configured as SG mode \r\n");

        return XST_FAILURE;

    }

    return XST_SUCCESS;

}

步骤2函数源码:

void Exception_Init(XScuGic* Xscugic)

{

    //1,connect to hardware intc

    Xil_ExceptionInit();

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)Xscugic);

 

    //2,enable hardware intc

    Xil_ExceptionEnable();

    Xil_ExceptionDisableMask(XIL_EXCEPTION_ID_IRQ_INT);

 

}

步骤3函数源码:

void Axidma_Gic_Init(XScuGic* Xscugic,XAxiDma *AxiDmaPtr,u16 GicID,u16 TxIntrId,u16 RxIntrId)

{

    XScuGic_Config *GicConfigInit;

    //1,find device

    GicConfigInit = XScuGic_LookupConfig(GicID);

    //2,config gicconfig

  XScuGic_CfgInitialize(Xscugic,GicConfigInit,GicConfigInit->CpuBaseAddress);

    //3,gic selftest

    XScuGic_SelfTest(Xscugic);

 

    //4,gic connect to axidma

    XScuGic_Connect(Xscugic, TxIntrId,(Xil_InterruptHandler)Dma_TxHandler,(void *)AxiDmaPtr);

    XScuGic_Connect(Xscugic, RxIntrId,(Xil_InterruptHandler)Dma_RxHandler,(void *)AxiDmaPtr);

    //5,set gic interrupt Priority and type

    XScuGic_SetPriorityTriggerType(Xscugic,TxIntrId,0xA0, 0x03);

    XScuGic_SetPriorityTriggerType(Xscugic,RxIntrId,0xA0, 0x03);

    //6,enable gic interrupt

    XScuGic_Enable(Xscugic,TxIntrId);

    XScuGic_Enable(Xscugic,RxIntrId);

}

步骤4函数源码:

void Dma_RxHandler(void* CallBackRef)

{

    u32 irqstatus = 0;

    int TimeOut = RESET_TIMEOUT_COUNTER;

    XAxiDma *XaxidmaPter = (XAxiDma*)CallBackRef;

    //1,Read pending interrupts /XAXIDMA_DEVICE_TO_DMA

    irqstatus = XAxiDma_IntrGetIrq(XaxidmaPter,XAXIDMA_DEVICE_TO_DMA);

    //2,Acknowledge pending interrupts

    XAxiDma_IntrAckIrq(XaxidmaPter,irqstatus,XAXIDMA_DEVICE_TO_DMA);

    //3,If no interrupt is asserted, we do not do anything

    if (!(irqstatus & XAXIDMA_IRQ_ALL_MASK))

    {

        return;

    }

    //4,If error interrupt is asserted, raise error flag, reset the hardware to recover from the error, and return with no further processing.

    if(irqstatus & XAXIDMA_IRQ_ERROR_MASK)

    {

        //1,flag = 1

        Error = 1;

        //2,Reset should never fail for transmit channel

        XAxiDma_Reset(XaxidmaPter);

        //3,wait time out

        TimeOut = RESET_TIMEOUT_COUNTER;

        while(TimeOut)

        {

            if (XAxiDma_ResetIsDone(XaxidmaPter))

            {

                break;

            }

            TimeOut--;

        }

        return;

    }

    //5,If Completion interrupt is asserted, then set the TxDone flag

    if(irqstatus & XAXIDMA_IRQ_IOC_MASK)

    {

        RxDone = 1;

    }

}

void Dma_TxHandler(void* CallBackRef)

{

    u32 irqstatus = 0;

    int TimeOut = RESET_TIMEOUT_COUNTER;

    XAxiDma *XaxidmaPter = (XAxiDma*)CallBackRef;

    //1,Read pending interrupts /XAXIDMA_DMA_TO_DEVICE

    irqstatus = XAxiDma_IntrGetIrq(XaxidmaPter,XAXIDMA_DMA_TO_DEVICE);

    //2,Acknowledge pending interrupts

    XAxiDma_IntrAckIrq(XaxidmaPter,irqstatus,XAXIDMA_DMA_TO_DEVICE);

 

    //3,If no interrupt is asserted, we do not do anything

    if (!(irqstatus & XAXIDMA_IRQ_ALL_MASK))

    {

        return;

    }

    //4,If error interrupt is asserted, raise error flag, reset the hardware to recover from the error, and return with no further processing.

    if(irqstatus & XAXIDMA_IRQ_ERROR_MASK)

    {

        //1,flag = 1

        Error = 1;

        //2,Reset should never fail for transmit channel

        XAxiDma_Reset(XaxidmaPter);

        //3,wait time out

        TimeOut = RESET_TIMEOUT_COUNTER;

        while(TimeOut)

        {

            if (XAxiDma_ResetIsDone(XaxidmaPter))

            {

                break;

            }

            TimeOut--;

        }

        return;

    }

    //5,If Completion interrupt is asserted, then set the TxDone flag

    if(irqstatus & XAXIDMA_IRQ_IOC_MASK)

    {

        TxDone = 1;

    }

}

步骤5函数源码:

int axi_dma_test()

{

    int Status;

    TxDone = 0;

    RxDone = 0;

    Error = 0;

    xil_printf("\r\n----DMA Test----\r\n");

    xil_printf("PKT_LEN=%d\r\n",MAX_PKT_LEN);

 

    for(i = 0; i < Tries; i ++)

    {

        LED2_ON;

        Value = TEST_START_VALUE + (i & 0xFF);

        for(Index = 0; Index < MAX_PKT_LEN; Index ++)

        {

                TxBufferPtr[Index] = Value;

                Value = (Value + 1) & 0xFF;

        }

        /* Flush the SrcBuffer before the DMA transfer, in case the Data Cache

         * is enabled

         */

        Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);

 

        Status = XAxiDma_SimpleTransfer(&XAxi_Dma,(u32) RxBufferPtr,

                   MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

 

        if (Status != XST_SUCCESS) {

            return XST_FAILURE;

        }

        Status = XAxiDma_SimpleTransfer(&XAxi_Dma,(u32) TxBufferPtr,

                   MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

        if (Status != XST_SUCCESS) {

            return XST_FAILURE;

        }

        /*

         * Wait TX done and RX done

         */

        while (!TxDone || !RxDone) {

                /* NOP */

        }

 

        success++;

        TxDone = 0;

        RxDone = 0;

        if (Error) {

            xil_printf("Failed test transmit%s done, "

            "receive%s done\r\n", TxDone? "":" not",

                           RxDone? "":" not");

            goto Done;

        }

        /*

         * Test finished, check data

         */

        Status = DMA_CheckData(MAX_PKT_LEN, (TEST_START_VALUE + (i & 0xFF)));

        if (Status != XST_SUCCESS) {

            xil_printf("Data check failed\r\n");

            goto Done;

        }

        LED2_OFF;

    }

    xil_printf("AXI DMA interrupt example test passed\r\n");

    xil_printf("success=%d\r\n",success);

    LED1_ON;

    /* Disable TX and RX Ring interrupts and return success */

//  DMA_DisableIntrSystem(&Xgic, TX_INTC_ID, RX_INTC_ID);

Done:

    xil_printf("--- Exiting Test --- \r\n");

    return XST_SUCCESS;

}

当然可以!下面是 **在 Xilinx Vivado 中使用 AXI DMA 实现 AD9361 数据采集并存入 DDR 的完整详细操作步骤**,适用于 Zynq-7000(如 ZedBoard、Zybo Z7)或 Zynq UltraScale+ 系列开发板。 我们将从零开始,一步步带你完成: - Block Design 配置 - 添加 AXI DMA IP - 连接信号与接口 - 生成 HDL 封装 - 编写 Verilog 模块发送数据 - SDK/Vitis 软件读取 DDR 数据 --- ## ✅ 目标功能 > 使用 `AXI DMA` 将来自 AD9361 的 ADC 数据(`adc_data_d1`, `adc_data_q1`)通过 PL 写入 PS 控制的 DDR3,ARM CPU 可后续读取分析。 --- ## 🛠️ 工具环境要求 - Vivado 2018.3 ~ 2023.2(推荐 2020.2) - Xilinx Zynq 开发板(如 ZedBoard, PYNQ-Z1/Z2, Zybo Z7) - AD9361 已正确接入 FPGA(差分 LVDS 接口) --- # 🔧 第一步:创建工程 & 创建 Block Design ### 1. 打开 Vivado → Create Project - 选择 RTL Project - 勾选 “Do not specify sources at this time” - 选择你的 FPGA 器件(如 xc7z020clg400-1) ### 2. 创建 Block Design ```tcl # 在 Tcl Console 输入: create_bd_design "design_1" ``` 或者点击:**Create Block Design** --- # 🧩 第二步:添加 Zynq Processing System(PS) ```tcl # Tcl 命令(可选): create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0 ``` 或 GUI 操作: - Click "+" → Search `processing_system7` → Add ### 双击 `processing_system7_0` 进行配置: #### ✅ 1. **Clock Configuration** - External Port Clock Enable → ✔️ (输入时钟频率,例如 50MHz) #### ✅ 2. **Peripheral I/O Pins** - MIO Selection: - SDIO 0 → Enable if you need SD card - UART 0 → Enable for serial output - GPIO → Optional #### ✅ 3. **DDR Configuration** - 选择你的 DDR 类型(如 DDR3, DDR3L) - 设置电压、速率(如 800 MHz) - 确保参数与开发板一致(查原理图) #### ✅ 4. **PS-PL Configuration → AXI Interface** - 启用: - **S AXI HP0 interface** → ✔️ - Data Width: 64-bit 或 32-bit(根据性能需求) - 其他保持默认 ✅ 完成后点击 OK --- # ⚙️ 第三步:运行 Block Automation 点击画布上的 `Run Connection Automation` - 对 `processing_system7_0` 弹出窗口: - 勾选 `/clockingwizard_0/clk_out1`(如果有)或 `FCLK_CLK0` - 自动连接 FCLK_RESET_N0 到 reset 👉 Vivado 会自动添加 `proc_sys_reset` 并连接时钟和复位 --- # 💾 第四步:添加 AXI DMA IP ```tcl # Tcl 命令: create_bd_cell -type ip -vlnv xilinx.com:ip:axi_dma:7.1 axi_dma_0 ``` 或 GUI: - "+" → 搜索 `axi_dma` → 添加 ### 双击 `axi_dma_0` 配置如下: | 参数 | 设置值 | 说明 | |------|--------|------| | **General Options** | | | | Include MM2S | ✔️ Checked | FPGA → DDR(我们重点用这个) | | Include S2MM | ❌ Unchecked | 不需要 DDR → FPGA | | Include SG Engine | ❌ Unchecked | 禁用 Scatter Gather(简化) | | **MM2S Channel** | | | | Data Width | 32-bit | 匹配你的 ADC 数据宽度 | | Burst Size | 16~256 | 推荐 16 或 32 | | Max Transfer Size | 65535 bytes | 可改小测试 | > ✅ 注意:只启用 MM2S(Memory Map to Stream),用于把流数据写进内存 --- # 🔗 第五步:连接 AXI DMA 到 PS 和时钟 ### 1. 连接 AXI 接口 将 `axi_dma_0/M_AXI_MM2S` 连接到 `processing_system7_0/S_AXI_HP0_FPD` 👉 方法: - 点击 `axi_dma_0` 输出端口 `M_AXI_MM2S` - 拖动到 `processing_system7_0` 上的 `S_AXI_HP0_FPD` 输入口 - Vivado 会提示自动连接 → 点击 OK ### 2. 连接时钟和复位 - `axi_dma_0/s_axi_lite_aclk` ← `processing_system7_0/FCLK_CLK0` - `axi_dma_0/m_axi_mm2s_aclk` ← `processing_system7_0/FCLK_CLK0` - `axi_dma_0/mm2s_aresetn` ← `rst_ps7_0_aresetn`(即 proc_sys_reset 输出) 💡 提示:可用 `Run Connection Automation` 让 Vivado 自动连这些 --- # 📥 第六步:暴露关键信号到顶层 我们需要把以下信号导出给外部模块使用: - `axi_dma_0/MM2S_PRIM_RESET_OUT_N` → 复位流通道 - `axi_dma_0/S_AXIS_MM2S_*` → 流输入接口(接 ADC 数据) 右键这些信号 → **Make External** 重命名建议: - `S_AXIS_MM2S_TVALID` → `m_axis_tvalid` - `S_AXIS_MM2S_TDATA` → `m_axis_tdata` - `S_AXIS_MM2S_TREADY` → `m_axis_tready` - `S_AXIS_MM2S_TLAST` → `m_axis_tlast` --- # 🧱 第七步:生成设计输出产品 右键 `design_1` → **Generate Output Products** - 选择 “All” - 格式:Verilog(或其他你用的语言) 等待完成 --- # 🧱 第八步:封装整个 Block Design 为 HDL 模块 右键 `design_1` → **Create HDL Wrapper** - 选择 “Let Vivado manage wrapper” 生成文件:`design_1_wrapper.v` --- # 📥 第九步:编写 ADC 数据转 AXIS 模块(adc_to_axis.v) ```verilog // File: adc_to_axis.sv module adc_to_axis ( input clk, input rst_n, // 来自 AD9361 的数据 input adc_valid, input [11:0] adc_data_i, input [11:0] adc_data_q, // 输出给 AXI_DMAAXIS 接口 output m_axis_tvalid, output [31:0] m_axis_tdata, output m_axis_tlast, output m_axis_tready ); assign m_axis_tvalid = adc_valid; assign m_axis_tdata = {adc_data_q, adc_data_i, 8'b0}; // 32bit: Q[11:0], I[11:0], zero pad assign m_axis_tlast = adc_valid; // 每个 sample 视为一帧(也可多个拼成一帧) assign m_axis_tready = 1'b1; // 假设 DMA 一直准备好(实际中可反馈 backpressure) endmodule ``` 📌 说明: - `adc_valid` 对应 `adc_d1q1_valid` - `adc_data_i/q` 是 I/Q 路数据 - 组合成 32bit 数据送入 AXI DMA - `tready=1` 表示始终准备接收(适合高速连续传输) --- # 🧩 第十步:修改顶层模块 ad9361_top.v 假设你原来的 `design_1_wrapper` 已经例化好了,现在加上 `adc_to_axis` 模块: ```verilog // 实例化 adc_to_axis adc_to_axis u_adc_to_axis ( .clk(data_clk), // 来自 AD9361 的采样时钟(如 51.2MHz) .rst_n(MM2S_PRIM_RESET_OUT_N), // 来自 AXI_DMA 的复位反相信号 .adc_valid(adc_d1q1_valid), .adc_data_i(adc_data_d1), .adc_data_q(adc_data_q1), .m_axis_tvalid(m_axis_tvalid), .m_axis_tdata(m_axis_tdata), .m_axis_tlast(m_axis_tlast), .m_axis_tready(m_axis_tready) ); ``` 确保这些信号也连接到 `design_1_wrapper` 的对应端口。 --- # 💾 第十一步:分配地址 & 导出硬件 ### 1. 在 Block Design 中点击: **Validate Design** → 确保无错误 ### 2. Synthesis → Implement → Generate Bitstream ### 3. Export Hardware - File → Export → Export Hardware - 勾选 “Include bitstream” - 路径:`<your_project>.sdk` 或 `<your_project>.hardware` --- # 💻 第十二步:使用 Vitis(或 SDK)编写软件读取数据 打开 Vitis: ```bash File → New → Application Project ``` 选择 OS:standalone Processor:ps7_cortexa9_0 创建新应用工程(如 `dma_adc_reader`) ### 替换 `src/main.c` 内容: ```c #include <stdio.h> #include "platform.h" #include "xil_io.h" #include "xparameters.h" // 来自 xparameters.h #define DMA_BASEADDR XPAR_AXI_DMA_0_BASEADDR #define DDR_BUFFER_ADDR 0x10000000 // 存储起始地址(256MB处) #define SAMPLE_COUNT 1024 // 读取前1024个样本 // AXI DMA 寄存器偏移 #define XAXIDMA_CR_OFFSET 0x00 #define XAXIDMA_SR_OFFSET 0x04 #define XAXIDMA_MM2S_ADDR_OFFSET 0x18 int main() { init_platform(); printf("=== AXI DMA ADC Data Capture Demo ===\n"); // 1. 复位 DMA Xil_Out32(DMA_BASEADDR + XAXIDMA_CR_OFFSET, 0x04); usleep(1000); Xil_Out32(DMA_BASEADDR + XAXIDMA_CR_OFFSET, 0x00); // 2. 设置目标地址 Xil_Out32(DMA_BASEADDR + XAXIDMA_MM2S_ADDR_OFFSET, DDR_BUFFER_ADDR); // 3. 启动 MM2S 通道 Xil_Out32(DMA_BASEADDR + XAXIDMA_CR_OFFSET, 0x01); // Start printf("DMA started. Capturing data to 0x%x...\n", DDR_BUFFER_ADDR); sleep(1); // 等待采集 // 4. 读取前几个样本验证 printf("First %d samples:\n", SAMPLE_COUNT / 10); for (int i = 0; i < SAMPLE_COUNT && i < 100; i += 10) { u32 val = Xil_In32(DDR_BUFFER_ADDR + i * 4); u16 i_val = val & 0x0FFF; u16 q_val = (val >> 12) & 0x0FFF; printf("Sample[%d]: I=%d, Q=%d\n", i, i_val, q_val); } cleanup_platform(); return 0; } ``` --- # ✅ 第十三步:烧写与调试 1. Launch on Hardware 2. 下载 `.bit` 文件 + `.elf` 3. 打开串口终端(波特率 115200) 4. 查看输出是否显示 ADC 数据 --- # 🧪 可选:使用 ILA 调试 AXIS 信号 添加 ILA 核心监控: - `data_clk` - `adc_d1q1_valid` - `m_axis_tvalid`, `tdata`, `tlast`, `tready` 确认数据是否持续输出 --- # ✅ 总结:AXI DMA 使用全流程图 ```text AD9361 ↓ (LVDS) ADC Data → adc_to_axis → AXIS TDATA/TVALID ↓ AXI DMA (MM2S) ↓ DDR3 via S_AXI_HP0 ↓ ARM CPU (in Vitis) Read & Print ``` --- # ✅ 关键注意事项 | 项目 | 注意事项 | |------|----------| | 时钟域 | `data_clk` 必须稳定,建议来自 AD9361 的 `rx_frame_clk` | | 地址空间 | `0x10000000` 要避开 Linux 内核占用区(裸机可用) | | tready | 若 `tready` 被拉低,需处理背压(可用 FIFO 缓冲) | | 性能 | 51.2Msps × 4B = ~204.8 MB/s,在 AXI HP 带宽范围内 | | 初始化 | 确保 PS 成功初始化 DDR(否则 DMA 写失败) | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值