在上一篇中着重讲解了DMA的含义和AXI_DMA_IP,本次的重点就是搭建一个AXI_DMA环路工程,并从C语言角度分析其SDK代码
一、AXI_DMA工程设计
在工程设计中,DMA一般与产生数据或需求数据的IP相连,该IP core可以是带有AXI_Stream接口的高速AD或DA IP核,实验中使用AXI-Stream Data Fifo IP核作为该类IP进行DMA环回实验:
核心部分为:
在处理器系统中,PL侧的DMA通过HP接口从DDR中读取数据,AXI DMA核作为AXIS Data FIFO和AXI4内存映射之间提供高宽带直接存储访问。
二、SDK代码分析
在工程设计中,PL侧配置好IP core之后生成含有配置参数的比特流文件,将其导出到SDK中,PS侧通过对PL侧配置参数的查询,执行IP核的配置。
注意:位于PL侧的属于PS的可配置模块的配置是由PL完成的,但是执行是由PS实现的!
代码分析:
#include “xaxidma.h”
#include “xparameters.h”
#include “xil_exception.h”
#include “xscugic.h”
/************************** Constant Definitions *****************************/
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID //位于PL侧的DMA,xparameters.h
// DMA接收与发送通道的中断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
//定义DDR的基地址
#define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR //0x00100000
//定义内存的地址
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) //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 status;
u8 value;
/* 等价于
u8 tx_buffer_ptr[MAX_PKT_LEN]; //发送缓冲区指针,指针指向的数据为8bit无符 数
u8 rx_buffer_ptr[MAX_PKT_LEN]; //接受缓冲区指针,指针指向的数据为8bit无符 数
*/
u8* tx_buffer_ptr; //发送缓冲区指针,指针指向的数据为8bit无符 数
u8* rx_buffer_ptr; //接受缓冲区指针,指针指向的数据为8bit无符 数
/*
XAxiDma_Config是一个AXI_DMA配置的信息结构体,它里面包含需要配置的各种信息,
类似于一个空表,表里面有各种需要填的事项,
填表的方式是将AXI_DMA的设备 作为传入参数传递到XAxiDma_LookupConfig查找函数中,
如果传输的设备 和函数内部的设备 一样的话,就将根据PL侧的设计参数传递给查找表
*/
XAxiDma_Config *config;
/*
从C语言的角度看指针
TX_BUFFER_BASE与RX_BUFFER_BASE都是一个地址,在地址前面加上(u8 *)修饰符,
这样理解:
a=8’b1;
u8* p;
*p=(u8*)(&a);
p=就是a的地址
此处解释:
(u8 *) TX_BUFFER_BASE;
将TX_BUFFER_BASE转换成指向8位无符 数指针的内容
然后这个地址传递给tx_buffer_ptr
(u8*)的作用是指针该地址指向的数据为8bit无符 数,不可以多操作或者少操作
*/
tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;
xil_printf(“rn— Entering main() — rn”);
/*
进行DMA配置参数传递
通过调用DMA查找配置函数,传入设备ID,获取设备参数
需要注意的是,其中的参数是根据PL端的IP core的配置选项生成的参数
*/
config = XAxiDma_LookupConfig(DMA_DEV_ID);
if (!config) {
xil_printf(“No config found for %drn”, DMA_DEV_ID);
return XST_FAILURE;
}
/*
//初始化DMA引擎
根据PL端对DMA core的配置参数,PS对DMA进行真正的配置初始化过程,
axidma还存储在PS端的AXI——DMA配置表,根据对PL参数的读取,
PS运行对PL侧的DMA配置,这个配置过程是通过GP0接口对AXI_Lite4总线的控制完成的
*/
status = XAxiDma_CfgInitialize(&axidma, config);
if (status != XST_SUCCESS) {
xil_printf(“Initialization failed %drn”, status);
return XST_FAILURE;
}
/*
我们配置的是使用PL侧DMA的直接寄存器访问模式,所以数据传递也是通过该方式运行的,
为了以防万一,在这里运行一下SG查询函数看看是不是配置成了SG模式
*/
if (XAxiDma_HasSg(&axidma)) {
xil_printf(“Device configured as SG mode rn”);
return XST_FAILURE;
}
/*
//建立中断系统,详见函数定义
CallBackRef is the callback reference, usually the instance pointer of the connecting driver.
CallBackRef是回调引用,通常是连接驱动程序的实例指针。
axidma是这些传入参数里面最没用的东西,不过还是保留的
对于中断系统,分为PPI(私有外设中断)、SGI(软件生成中断)、SPI(共享外设中断)
我们在中断系统中根据AXI_DMA的接收发送中断 注册两种中断
axidma作用不大,起码没有直接感觉出来,解释中只是说axidma是回调引用,通常连接到驱动程序的实例指针,所以前面有一个取地址&
*/
status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
if (status != XST_SUCCESS) {
xil_printf(“Failed intr setuprn”);
return XST_FAILURE;
}
//建立好中断系统后,初始化标志信
tx_done = 0;
rx_done = 0;
error = 0;
//对要写入的数据赋值
value = TEST_START_VALUE;
for (int i = 0; i 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);
}
下一期预告:ZYNQ Cache一致性问题分析
文章知识点与官方知识档案匹配,可进一步学习相关知识C技能树首页概览114462 人正在系统学习中 相关资源:2012年下半年软件设计师上午试题及答案-软考等考文档类资源-CSDN…
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!