该随笔参考了正点原子P15开发板 MPSoC-P15之嵌入式VITIS开发指南V1.0第二十一章 AXI DMA 环路测试的教程。
这里贴出网址http://www.openedv.com/docs/boards/fpga/MPSoC_P15.html。
硬件的配置过程这里不再展示,大家板卡情况不同,需要自行了解并尝试配置。
下面贴出正点原子的代码(全)(已下载尝试过)。
具体代码分析不在展示,请大家自行询问Deepseek。

点击查看代码
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xscugic.h"

/************************** Constant Definitions *****************************/

#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
#define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define DDR_BASE_ADDR XPAR_PSU_DDR_0_S_AXI_BASEADDR //0x00000000    DDR基地址
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x01100000) //0x01100000
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) //0x01200000
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) //0x01400000
#define RESET_TIMEOUT_COUNTER 10000 //复位时间
#define TEST_START_VALUE 0x0 //测试起始值
#define MAX_PKT_LEN 0x100 //发送包长度

/************************** Function Prototypes ******************************/
static int check_data(int length, u8 start_value);
static void tx_intr_handler(void *callback);
static void rx_intr_handler(void *callback);
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr, u16 tx_intr_id, u16 rx_intr_id);
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id, u16 rx_intr_id);
/************************** Variable Definitions *****************************/

static XAxiDma axidma; //XAxiDma 实例
static XScuGic intc; //中断控制器的实例
volatile int tx_done; //发送完成标志
volatile int rx_done; //接收完成标志
volatile int error; //传输出错标志

/************************** Function Definitions *****************************/

int main(void)
{
    int i;
    int status;
    u8 value;
    u8 *tx_buffer_ptr;
    u8 *rx_buffer_ptr;
    XAxiDma_Config *config;

    tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
    rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;

xil_printf("\r\n--- Entering main() --- \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;
    }

//初始化 DMA 引擎
    status = XAxiDma_CfgInitialize(&axidma, config);
    if (status != XST_SUCCESS) {xil_printf("Initialization failed %d\r\n", status);
    return XST_FAILURE;
    }

    if (XAxiDma_HasSg(&axidma)) {xil_printf("Device configured as SG mode \r\n");
    return XST_FAILURE;
    }

//建立中断系统
    status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
    if (status != XST_SUCCESS) {xil_printf("Failed intr setup\r\n");
    return XST_FAILURE;
    }

//初始化标志信号
    tx_done = 0;
    rx_done = 0;
    error = 0;

    value = TEST_START_VALUE;
    for (i = 0; i < MAX_PKT_LEN; i++) {
        tx_buffer_ptr[i] = value;
        value = (value + 1) & 0xFF;
    }

    Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, MAX_PKT_LEN); //刷新 Data Cache

    status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) tx_buffer_ptr, MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
    if (status != XST_SUCCESS) {return XST_FAILURE;}

    status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) rx_buffer_ptr, MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
    if (status != XST_SUCCESS) {return XST_FAILURE; }

    Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, MAX_PKT_LEN); //刷新 Data Cache
    while (!tx_done && !rx_done && !error)
//传输出错
    if (error) {xil_printf("Failed test transmit%s done, " "receive%s done\r\n", tx_done ? "" : " not", rx_done ? "" : " not");
    goto Done;
    }

//传输完成,检查数据是否正确
    status = check_data(MAX_PKT_LEN, TEST_START_VALUE);
    if (status != XST_SUCCESS) {xil_printf("Data check failed\r\n");goto Done;}
    xil_printf("Successfully ran AXI DMA Loop\r\n");
    disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);
    Done: xil_printf("--- Exiting main() --- \r\n");
    return XST_SUCCESS;
}

//检查数据缓冲区
static int check_data(int length, u8 start_value)
{
    u8 value;
    u8 *rx_packet;
    int i = 0;

    value = start_value;
    rx_packet = (u8 *) RX_BUFFER_BASE;
    for (i = 0; i < length; i++) {
        if (rx_packet[i] != value) {xil_printf("Data error %d: %x/%x\r\n", i, rx_packet[i], value);
    return XST_FAILURE;}
    value = (value + 1) & 0xFF;
    }

    return XST_SUCCESS;
}

//DMA TX 中断处理函数
static void tx_intr_handler(void *callback)
{
    int timeout;
    u32 irq_status;
    XAxiDma *axidma_inst = (XAxiDma *) callback;

//读取待处理的中断
    irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
//确认待处理的中断
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);
//Tx 出错
    if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
    error = 1;
    XAxiDma_Reset(axidma_inst);
    timeout = RESET_TIMEOUT_COUNTER;
    while (timeout) {
    if (XAxiDma_ResetIsDone(axidma_inst))
    break;
    timeout -= 1;
    }
    return;
    }
 //Tx 完成
    if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
    tx_done = 1;
}

//DMA RX 中断处理函数
static void rx_intr_handler(void *callback)
{
    u32 irq_status;
    int timeout;
    XAxiDma *axidma_inst = (XAxiDma *) callback;
    irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);
//Rx 出错
    if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
    error = 1;
    XAxiDma_Reset(axidma_inst);
    timeout = RESET_TIMEOUT_COUNTER;
    while (timeout) {
    if (XAxiDma_ResetIsDone(axidma_inst))
    break;
    timeout -= 1;
    }
    return;
    }
 //Rx 完成
    if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
    rx_done = 1;
}

 //建立 DMA 中断系统
 // @param int_ins_ptr 是指向 XScuGic 实例的指针
 // @param AxiDmaPtr 是指向 DMA 引擎实例的指针
 // @param tx_intr_id 是 TX 通道中断 ID
 // @param rx_intr_id 是 RX 通道中断 ID
 // @return:成功返回 XST_SUCCESS,否则返回 XST_FAILURE
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr, u16 tx_intr_id, u16 rx_intr_id)
{
    int status;
    XScuGic_Config *intc_config;
//初始化中断控制器驱动
    intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (NULL == intc_config) {
    return XST_FAILURE;
    }
    status = XScuGic_CfgInitialize(int_ins_ptr, intc_config, intc_config->CpuBaseAddress);
    if (status != XST_SUCCESS) {
    return XST_FAILURE;
}
//设置优先级和触发类型
    XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);
//为中断设置中断处理函数
    status = XScuGic_Connect(int_ins_ptr, tx_intr_id, (Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
    if (status != XST_SUCCESS) {
    return status;
    }
    status = XScuGic_Connect(int_ins_ptr, rx_intr_id, (Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
    if (status != XST_SUCCESS) {
    return status;
}
    XScuGic_Enable(int_ins_ptr, tx_intr_id);
    XScuGic_Enable(int_ins_ptr, rx_intr_id);
//启用来自硬件的中断
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    (Xil_ExceptionHandler) XScuGic_InterruptHandler, (void *) int_ins_ptr);
    Xil_ExceptionEnable();
//使能 DMA 中断
    XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
    return XST_SUCCESS;
}
//此函数禁用 DMA 引擎的中断
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id, u16 rx_intr_id)
{
    XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
    XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
}

这是下载后的调试界面,我圈出了两个方框。按下F8之后。变为第二张图,整个过程,简单来讲就是往0x1200000写入256个累加数(u8 0-255),然后通过DMA扔到PL端的stream fifo,然后再通过DMA传到0x1400000。



在Deepseek的帮助下,我们做一点代码上的修改,其实就是将main.c主函数拆分成多模块,方便后续添加其他模块,修改并进行维护。
下面是初始化头文件和源文件。

点击查看代码
#ifndef MY_INIT_H
#define MY_INIT_H

#include "xaxidma.h"
#include "xparameters.h"

#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID

/************************** Function Prototypes ******************************/
int dma_init(XAxiDma *axidma);

#endif // DMA_INIT_H
点击查看代码
#include "my_init.h"
#include "xil_printf.h"

int dma_init(XAxiDma *axidma) {
    XAxiDma_Config *config;
    config = XAxiDma_LookupConfig(DMA_DEV_ID);
    if (!config) {
        xil_printf("No config found for %d\r\n", DMA_DEV_ID);
        return XST_FAILURE;
    }

    int status = XAxiDma_CfgInitialize(axidma, config);
    if (status != XST_SUCCESS) {
        xil_printf("Initialization failed %d\r\n", status);
        return XST_FAILURE;
    }

    if (XAxiDma_HasSg(axidma)) {
        xil_printf("Device configured as SG mode\r\n");
        return XST_FAILURE;
    }

    return XST_SUCCESS;
}

下面是中断头文件文件和源文件。

点击查看代码
#ifndef MY_INTR_H
#define MY_INTR_H

#include "xaxidma.h"
#include "xscugic.h"
#include "xparameters.h"

#define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#define RESET_TIMEOUT_COUNTER 10000 //复位时间
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define INTR_PRIORITY     0xA0
#define TRIGGER_TYPE      0x3
#define RESET_TIMEOUT_MSG() xil_printf("DMA reset timeout!\r\n")


/************************** Function Prototypes ******************************/
int setup_interrupts(XScuGic *intc, XAxiDma *axidma);
void handle_dma_error(XAxiDma *axidma_inst);
void disable_interrupts(XScuGic *intc, XAxiDma *axidma);

#endif // MY_INTR_H
点击查看代码
#include "my_intr.h"
#include "xil_printf.h"
#include "xil_exception.h"
#include "globals.h"

// 中断处理函数
static void tx_intr_handler(void *callback) {
    XAxiDma *axidma_inst = (XAxiDma *)callback;
    u32 irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);

    if (irq_status & XAXIDMA_IRQ_ERROR_MASK) {
        handle_dma_error(axidma_inst);
    }

    if (irq_status & XAXIDMA_IRQ_IOC_MASK) {
        tx_done = 1;
    }
}

static void rx_intr_handler(void *callback) {
    XAxiDma *axidma_inst = (XAxiDma *)callback;
    u32 irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);

    if (irq_status & XAXIDMA_IRQ_ERROR_MASK) {
        handle_dma_error(axidma_inst);
    }

    if (irq_status & XAXIDMA_IRQ_IOC_MASK) {
        rx_done = 1;
    }
}
int setup_interrupts(XScuGic *intc, XAxiDma *axidma) {
    XScuGic_Config *intc_config;
    intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (!intc_config) {
        xil_printf("Interrupt controller config not found\r\n");
        return XST_FAILURE;
    }

    int status = XScuGic_CfgInitialize(intc, intc_config, intc_config->CpuBaseAddress);
    if (status != XST_SUCCESS) {
        xil_printf("GIC initialization failed: %d\r\n", status);
        return XST_FAILURE;
    }

    XScuGic_SetPriorityTriggerType(intc, TX_INTR_ID, INTR_PRIORITY, TRIGGER_TYPE);
    XScuGic_SetPriorityTriggerType(intc, RX_INTR_ID, INTR_PRIORITY, TRIGGER_TYPE);

    status = XScuGic_Connect(intc, TX_INTR_ID, (Xil_InterruptHandler)tx_intr_handler, axidma);
    if (status != XST_SUCCESS) {
        xil_printf("Failed to connect TX interrupt: %d\r\n", status);
        return status;
    }

    status = XScuGic_Connect(intc, RX_INTR_ID, (Xil_InterruptHandler)rx_intr_handler, axidma);
    if (status != XST_SUCCESS) {
        xil_printf("Failed to connect RX interrupt: %d\r\n", status);
        return status;
    }

    XScuGic_Enable(intc, TX_INTR_ID);
    XScuGic_Enable(intc, RX_INTR_ID);

    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, intc);
    Xil_ExceptionEnable();
    XAxiDma_IntrDisable(axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrDisable(axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrEnable(axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);

    return XST_SUCCESS;
}

void handle_dma_error(XAxiDma *axidma_inst) {
    error = 1;
    XAxiDma_Reset(axidma_inst);
    
    int timeout = RESET_TIMEOUT_COUNTER;
    while (timeout && !XAxiDma_ResetIsDone(axidma_inst)) {
        timeout--;
    }
    
    if (!timeout) {
        RESET_TIMEOUT_MSG();
    }
}

void disable_interrupts(XScuGic *intc, XAxiDma *axidma) {
  // 先禁用DMA中断
    XAxiDma_IntrDisable(axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrDisable(axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
    XScuGic_Disconnect(intc, TX_INTR_ID);
    XScuGic_Disconnect(intc, RX_INTR_ID);
    XScuGic_Disable(intc, TX_INTR_ID);
    XScuGic_Disable(intc, RX_INTR_ID);
}

下面是个人根据需求编写的代码块头文件和源文件。

点击查看代码
#ifndef MY_CODE_H
#define MY_CODE_H

#include "xaxidma.h"
#include "xparameters.h"

#define DDR_BASE_ADDR XPAR_PSU_DDR_0_S_AXI_BASEADDR //0x00000000    DDR基地址
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x01100000) //0x01100000
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) //0x01200000
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) //0x01400000
#define TEST_START_VALUE 0x0 //测试起始值
#define MAX_PKT_LEN 0x100 //发送包长度

/************************** Function Prototypes ******************************/
int dma_transfer(XAxiDma *axidma, u8 *tx_buffer, int length);
int dma_receive(XAxiDma *axidma, u8 *rx_buffer, int length);


#endif // MY_CODE_H
点击查看代码
#include "my_code.h"
#include "xil_printf.h"
#include "xil_cache.h"

int dma_transfer(XAxiDma *axidma, u8 *tx_buffer, int length) {
    Xil_DCacheFlushRange((UINTPTR)tx_buffer, length);
    int status = XAxiDma_SimpleTransfer(axidma, (UINTPTR)tx_buffer, length, XAXIDMA_DMA_TO_DEVICE);
    if (status != XST_SUCCESS) {
        return XST_FAILURE;
    }
    return XST_SUCCESS;
}


int dma_receive(XAxiDma *axidma, u8 *rx_buffer, int length) {
    int status = XAxiDma_SimpleTransfer(axidma, (UINTPTR)rx_buffer, length, XAXIDMA_DEVICE_TO_DMA);
    if (status != XST_SUCCESS) {
        return XST_FAILURE;
    }
    Xil_DCacheFlushRange((UINTPTR)rx_buffer, length);
    return XST_SUCCESS;
}

最后是全局变量头文件和主函数main文件。

点击查看代码
#ifndef GLOBALS_H
#define GLOBALS_H

volatile int tx_done; //发送完成标志
volatile int rx_done; //接收完成标志
volatile int error  ; //传输出错标志

#endif // GLOBALS_H
点击查看代码
#include "my_init.h"
#include "my_intr.h"
#include "my_code.h"
#include "globals.h"

int main(void){
    u8 *tx_buffer = (u8 *)TX_BUFFER_BASE;
    u8 *rx_buffer = (u8 *)RX_BUFFER_BASE;
    XAxiDma axidma;
    XScuGic intc;

xil_printf("\r\n--- Entering main() --- \r\n");
    tx_done = 0;
    rx_done = 0;
    error   = 0;
    // 初始化 DMA
    if (dma_init(&axidma)) {
        xil_printf("DMA initialization failed\r\n");
        return XST_FAILURE;
    }

    // 设置中断
    if (setup_interrupts(&intc, &axidma)) {
        xil_printf("Interrupt setup failed\r\n");
        return XST_FAILURE;
    }

    // 准备发送数据
    for (int i = 0; i < MAX_PKT_LEN; i++) {
        tx_buffer[i] = (TEST_START_VALUE + i) & 0xFF;
    }

    // 启动 DMA 传输
    if (dma_transfer(&axidma, tx_buffer, MAX_PKT_LEN)) {
        xil_printf("DMA transfer failed\r\n");
        goto done;
    }

    while (!tx_done)
    {
    }
    tx_done = 0;
    
    // 启动 DMA 接收
    if (dma_receive(&axidma, rx_buffer, MAX_PKT_LEN)) {
        xil_printf("DMA receive failed\r\n");
        goto done;
    }
    while (!rx_done)
    {
    }
    rx_done = 0;

xil_printf(" AXI DMA Loop END\r\n");
    
done:
    disable_interrupts(&intc, &axidma);
    xil_printf("--- Exiting main() --- \r\n");
    return XST_SUCCESS;
}

最后是修改之后再次调试下载后的界面,也是通过的。