简介
AXI DMA操作需要先提供一个在内存中驻留的不变空间,用于存储需要进行的DMA操作。形容这“每一次操作”的东西叫做Buffer Descriptor,缩写叫BD,这些BD是连接成链表的形式的,因为BD会动态增加,而预先分配存储BD的空间是恒定的,因此BD被连成一个环(BD Ring),其实就是一个循环链表。
Scatter/Gather 允许一个数据包(Packet)由多个描述符(BD)来描述。官方文档指出的一个典型应用是在传输网络包时,Header和数据往往是分开存储的,利用SG模式可以较好的处理向多个目标读写的操作,提高应用吞吐量。 DB Ring中DB成链存放,为了解决环形结构带来的不知道Packet的起点和终点带来的问题,DMA使用了帧开始位 (TXSOF,TX Start of Frame bit) 和帧结束位 (TXEOF,TX End of Frame Bit)来分辨一段Packet。 当 DMA 获取设置了 TXSOF 位的描述符时,将触发Packet的开始。 Packet继续获取后续描述符,直到它获取一个设置了 TXEOF 位的描述符。
在接收 (S2MM) 通道上,当开始接收数据包时,AXI DMA会自动使用 RXSOF 标记描述符,告诉软件部分这个描述符对应的buffer是一个数据包的开头。 如果正在接收的数据包的总字节数比描述符中指定的长,则用下一个描述符接着传。 这种获取和存储过程一直持续到整个接收数据包被传输完毕。 接收到数据包结尾时正在处理的描述符由AXI DMA自动标记为RXEOF=1。表明与该描述符关联的缓冲区包含数据包的结尾。
每个描述符内部指明了该特定描述符实际传输的字节数。 软件可以通过从 RXSOF 描述符通过描述符链到 RXEOF 描述符来确定为接收数据包传输的总字节数。
Scatter Gather 操作从设置控制寄存器和描述符指针开始。
设置和启动 MM2S 通道的 DMA 具体操作如下:
- 将起始描述符的位置写入Current Descriptor寄存器中
- 设置运行/停止位为1(MM2S_DMACR.RS=1)启动MM2S运行
- (可选)启用中断(MM2S_DMACR.IOC_IrqEn 和 MM2S_DMACR.Err_IrqEn)
- 将末尾描述符位置写入Tail Descriptor寄存器中,写入后会立刻触发DMA获取描述符,如果是多通道,这一步骤会在数据包到达S2MM时开始
- 处理当前描述符,从内存中读取数据并转化成Stream输出
S2MM通道的配置类似:
- 将起始描述符的位置写入Current Descriptor寄存器中
- 通过将运行/停止位设置为 1 (S2MM_DMACR.RS =1) 来启动 S2MM 通道运行,并且暂停位 (DMASR.Halted) 置低,指示 S2MM 通道正在运行
- (可选)启用中断(MM2S_DMACR.IOC_IrqEn 和 MM2S_DMACR.Err_IrqEn)
- 将有效地址写入尾部描述符寄存器,自动触发 DMA 从内存获取描述符
- 处理获取的描述符,并将从 S2MM 流式通道接收到的任何数据写入内存
不说了理论知识去看其他up分享我们直接上配置教程
AXI DMA 配置
AXI FIFO配置
ZYNQ配置
配置完成我们进行AXI Stream流接口ip核的设计
上源码 (封装IP核的技能自己学一下)
module pl_write(
input m_axis_aclk,
input m_axis_aresetn,
input [63:0] pl_data ,
input data_en ,
input m_axis_tready,
output reg [31:0] m_axis_tdata,
output [3:0] m_axis_tkeep,
output m_axis_tlast,v
output reg m_axis_tvalid
);
parameter DATA_0 = 4'b0000,
DATA_1 = 4'b0001,
DATA_2 = 4'b0011,
DATA_3 = 4'b0010,
DATA_4 = 4'b0110,
DATA_5 = 4'b0111,
DATA_6 = 4'b0101,
DATA_7 = 4'b0100,
DATA_8 = 4'b1100,
DATA_9 = 4'b1101;
reg data_en_1;
reg [63:0]pl_data_1;
reg [3:0] next_flag;
assign m_axis_tkeep = 4'b1111;
assign m_axis_tlast = (next_flag==DATA_9)&&m_axis_tready&&m_axis_tvalid;
always @(posedge m_axis_aclk) begin
if(m_axis_aresetn == 1'b0)begin
data_en_1<=1'b0;
pl_data_1<=64'd0;
end
else if(m_axis_tready) begin
data_en_1<=data_en;
pl_data_1<=pl_data;
end
end
always@(posedge m_axis_aclk)begin
if(m_axis_aresetn == 1'b0)begin
next_flag<=DATA_0;
m_axis_tdata <= 32'd0;
m_axis_tvalid<=1'b0;
end
else if(data_en_1) begin
m_axis_tvalid<=1'b1;
case (next_flag)
DATA_0 : begin
if(m_axis_tready)begin
next_flag<=DATA_1;
m_axis_tdata<={32'h22111111};
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_0;
end
end
DATA_1 :begin
if(m_axis_tready)begin
m_axis_tdata<={32'h22111111};
next_flag<=DATA_2;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_1;
end
end
DATA_2 :begin
if(m_axis_tready)begin
m_axis_tdata<={32'h22111111};
next_flag<=DATA_3;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_2;
end
end
DATA_3 :begin
if(m_axis_tready)begin
m_axis_tdata<={32'h22111111};
next_flag<=DATA_4;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_3;
end
end
DATA_4 : begin
if(m_axis_tready)begin
m_axis_tdata<={8'd01,8'd12,pl_data_1[15:0]};
next_flag<=DATA_5;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_4;
end
end
DATA_5 :begin
if(m_axis_tready)begin
m_axis_tdata<={8'd02,8'd12,pl_data_1[31:16]};
next_flag<=DATA_6;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_5;
end
end
DATA_6 :begin
if(m_axis_tready)begin
m_axis_tdata<={8'd03,8'd12,pl_data_1[47:32]};
next_flag<=DATA_7;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_6;
end
end
DATA_7 :begin
if(m_axis_tready)begin
m_axis_tdata<={8'd04,8'd12,8'd0,pl_data_1[55:48]};
next_flag<=DATA_8;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_7;
end
end
DATA_8 :begin
if(m_axis_tready)begin
m_axis_tdata<={8'd05,8'd12,8'd0,pl_data_1[63:56]};
next_flag<=DATA_9;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_8;
end
end
DATA_9 :begin
if(m_axis_tready)begin
m_axis_tdata<={32'hffffffff};
next_flag<=DATA_0;
end else begin
m_axis_tdata <= m_axis_tdata;
next_flag<=DATA_9;
end
end
default:begin
next_flag<=DATA_0;
m_axis_tdata <= m_axis_tdata;
end
endcase
end
else begin
m_axis_tvalid<=1'b0;
end
end
endmodule
数据发送IP核根据自己需求创建了
上ps端代码
注意:每一个bd可存储4字节的数据 bd串联为一个环形成一个数据包packet
// 要传输的每个packet大小
#define MAX_PKT_LEN 0x4 //缓冲个数 字节
#define MARK_UNCACHEABLE 0x701
// 每个packet对应的BD数量
#define NUMBER_OF_BDS_PER_PKT 11//一个bd 4字节
// 一共要传输的packet个数
#define NUMBER_OF_PKTS_TO_TRANSFER 1文章来源:https://www.toymoban.com/news/detail-700119.html
此代码为接收代码
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"
#include "xaxidma_hw.h"
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
#define MEM_BASE_ADDR 0x01100000 //ddr地址
#define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define RX_BD_SPACE_BASE (MEM_BASE_ADDR)
#define RX_BD_SPACE_HIGH (MEM_BASE_ADDR + 0x0000FFFF)
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH (MEM_BASE_ADDR + 0x00300FFF)
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
//超时计数
#define RESET_TIMEOUT_COUNTER 10000
// 要传输的每个packet大小
#define MAX_PKT_LEN 0x4 //缓冲个数 字节
#define MARK_UNCACHEABLE 0x701
// 每个packet对应的BD数量
#define NUMBER_OF_BDS_PER_PKT 11//一个bd 4字节
// 一共要传输的packet个数
#define NUMBER_OF_PKTS_TO_TRANSFER 1
// 总共需要的BD总数
#define NUMBER_OF_BDS_TO_TRANSFER (NUMBER_OF_PKTS_TO_TRANSFER * \
NUMBER_OF_BDS_PER_PKT)
//中断合并阈值和延迟定时器阈值我们将合并阈值设置为包的总数。在这个例子中,接收端只会得到一个完成中断。
#define COALESCING_COUNT NUMBER_OF_PKTS_TO_TRANSFER
#define DELAY_TIMER_COUNT 100
#define INTC XScuGic
#define INTC_HANDLER XScuGic_InterruptHandler
//函数申明
int CheckData(int Length);
void RxCallBack(XAxiDma_BdRing * RxRingPtr);
void RxIntrHandler(void *Callback);
int SetupIntrSystem(INTC * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 RxIntrId);
void DisableIntrSystem(INTC * IntcInstancePtr, u16 RxIntrId);
int RxSetup(XAxiDma * AxiDmaInstPtr);
//设备实例
XAxiDma AxiDma;
//中断控制器实例
static INTC Intc;
//中断标志
volatile int RxDone=0;
volatile int Error=0;
int main(void)
{
int Status;
XAxiDma_Config *Config;
xil_printf("\r\n--- Entering strart --- \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引擎
XAxiDma_CfgInitialize(&AxiDma, Config);
if(!XAxiDma_HasSg(&AxiDma)) {
xil_printf("Device configured as Simple mode \r\n");
return XST_FAILURE;
}
//设置接收通道,以使数据准备好接收
Status = RxSetup(&AxiDma);
if (Status != XST_SUCCESS) {
xil_printf("Failed RX setup\r\n");
return XST_FAILURE;
}
//设置中断
Status = SetupIntrSystem(&Intc, &AxiDma, RX_INTR_ID);
if (Status != XST_SUCCESS) {
xil_printf("Failed intr setup\r\n");
return XST_FAILURE;
}
return XST_SUCCESS;
}
//数据比对
int CheckData(int Length)
{
u8 *RxPacket;
int Index = 0;
Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, MAX_PKT_LEN *NUMBER_OF_BDS_TO_TRANSFER);
RxPacket = (u8 *) RX_BUFFER_BASE;
//使指定地址缓存区失效
Xil_DCacheInvalidateRange((UINTPTR)RxPacket, Length);
for(Index = 0; Index < Length; Index+=4) {
xil_printf("Data %d: %x %x %x %x\r\n", Index / 4, RxPacket[Index + 3], RxPacket[Index + 2], RxPacket[Index + 1], RxPacket[Index + 0]);
}
RxDone=0;
RxSetup(&AxiDma);
XAxiDma_IntrAckIrq(&AxiDma,XAXIDMA_IRQ_IOC_MASK,XAXIDMA_DEVICE_TO_DMA);
return XST_SUCCESS;
}
void RxCallBack(XAxiDma_BdRing * RxRingPtr)
{
int BdCount;
XAxiDma_Bd *BdPtr;
XAxiDma_Bd *BdCurPtr;
u32 BdSts;
int Index;
xil_printf("enter RxCallBack\r\n");
//获取所有已处理bd
BdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
//处理bd
BdCurPtr = BdPtr;
for (Index = 0; Index < BdCount; Index++) {
//检查bd状态并判断是否有错误
BdSts = XAxiDma_BdGetSts(BdCurPtr);
if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
(!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
Error = 1;
break;
}
//查找下一个处理的bd
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
RxDone += 1;
}
CheckData(RxDone);
}
void RxIntrHandler(void *Callback)
{
XAxiDma_BdRing *RxRingPtr = (XAxiDma_BdRing *) Callback;
u32 IrqStatus;
int TimeOut;
/* Read pending interrupts */
IrqStatus = XAxiDma_BdRingGetIrq(RxRingPtr);
/* Acknowledge pending interrupts */
XAxiDma_BdRingAckIrq(RxRingPtr, IrqStatus);
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {return;}
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
XAxiDma_BdRingDumpRegs(RxRingPtr);
Error = 1;
XAxiDma_Reset(&AxiDma);
TimeOut = RESET_TIMEOUT_COUNTER;
while (TimeOut) {
if(XAxiDma_ResetIsDone(&AxiDma)) {break;}
TimeOut -= 1;
}
return;
}
//调用回调
if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
xil_printf("Generate rx interrupt\r\n");
RxCallBack(RxRingPtr);
}
}
//初始化中断
int SetupIntrSystem(INTC * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 RxIntrId)
{
//获取发送/接收 环
XAxiDma_BdRing *RxRingPtr = XAxiDma_GetRxRing(AxiDmaPtr);
int Status;
XScuGic_Config *IntcConfig;
//查找中断控制器信息
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
//初始化中断控制器
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//设置中断优先级
XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
//绑定中断处理函数
Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
(Xil_InterruptHandler)RxIntrHandler,
RxRingPtr);
if (Status != XST_SUCCESS) {
return Status;
}
//启动中断源
XScuGic_Enable(IntcInstancePtr, RxIntrId);
//启动硬件中断
Xil_ExceptionInit();
//绑定中断异常处理函数
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)INTC_HANDLER,
(void *)IntcInstancePtr);
//启用IRQ异常
Xil_ExceptionEnable();
return XST_SUCCESS;
}
//禁用dma中断
void DisableIntrSystem(INTC * IntcInstancePtr, u16 RxIntrId)
{
XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
}
//设置读取通道
int RxSetup(XAxiDma * AxiDmaInstPtr)
{
XAxiDma_BdRing *RxRingPtr;
int Status;
XAxiDma_Bd BdTemplate;
XAxiDma_Bd *BdPtr;
XAxiDma_Bd *BdCurPtr;
int BdCount;
int FreeBdCount;
UINTPTR RxBufferPtr;
int Index;
//获取接收环
RxRingPtr = XAxiDma_GetRxRing(&AxiDma);
//设置空间之前禁用所有读取中断
XAxiDma_BdRingIntDisable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
//设置bd空间
BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
RX_BD_SPACE_HIGH - RX_BD_SPACE_BASE + 1);
//创建bd环
Status = XAxiDma_BdRingCreate(RxRingPtr, RX_BD_SPACE_BASE,RX_BD_SPACE_BASE,
XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
if (Status != XST_SUCCESS) {
xil_printf("Rx bd create failed with %d\r\n", Status);
return XST_FAILURE;
}
//为Rx通道设置BD模板。然后复制到每个RX BD。
//bd归零
XAxiDma_BdClear(&BdTemplate);
//复制模板到创建的bd 模板为16个4字节数据uint32_t类型
Status = XAxiDma_BdRingClone(RxRingPtr, &BdTemplate);
if (Status != XST_SUCCESS) {
xil_printf("Rx bd clone failed with %d\r\n", Status);
return XST_FAILURE;
}
//在读取bd环上加上缓冲区以便读取数据
FreeBdCount = XAxiDma_BdRingGetFreeCnt(RxRingPtr);
Status = XAxiDma_BdRingAlloc(RxRingPtr, FreeBdCount, &BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx bd alloc failed with %d\r\n", Status);
return XST_FAILURE;
}
BdCurPtr = BdPtr;
RxBufferPtr = RX_BUFFER_BASE;
for (Index = 0; Index < FreeBdCount; Index++) {
//设置bd缓冲地址
Status = XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx set buffer addr %x on BD %x failed %d\r\n",
(unsigned int)RxBufferPtr,
(UINTPTR)BdCurPtr, Status);
return XST_FAILURE;
}
//为给定的bd设置子段长度
Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
RxRingPtr->MaxTransferLen);
if (Status != XST_SUCCESS) {
xil_printf("Rx set length %d on BD %x failed %d\r\n",
MAX_PKT_LEN, (UINTPTR)BdCurPtr, Status);
return XST_FAILURE;
}
//接收BDs不需要设置任何控件硬件会设置每个流的SOF/EOF位
//设置bd控制位
XAxiDma_BdSetCtrl(BdCurPtr, 0);
//设置bd的id
XAxiDma_BdSetId(BdCurPtr, RxBufferPtr);
RxBufferPtr += MAX_PKT_LEN;
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
}
//设置合并阈值,因此只有一个接收中断在本例中出现如果你想有多个中断发生,改变 COALESCING_COUNT是一个较小的值
//为给定的描述符环形通道设置中断合并参数。
Status = XAxiDma_BdRingSetCoalesce(RxRingPtr, COALESCING_COUNT,DELAY_TIMER_COUNT);
if (Status != XST_SUCCESS) {
xil_printf("Rx set coalesce failed with %d\r\n", Status);
return XST_FAILURE;
}
//将一组bd加入到分配的硬件中
Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx ToHw failed with %d\r\n", Status);
return XST_FAILURE;
}
//使能所有读取中断
XAxiDma_BdRingIntEnable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
//启动读取dma通道
Status = XAxiDma_BdRingStart(RxRingPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx start BD ring failed with %d\r\n", Status);
return XST_FAILURE;
}
return XST_SUCCESS;
}
此代码为回环 DMA需要勾选发送和接收端口并直接将zynq接收和发送端连接起来形成回环就可以了
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
#define MEM_BASE_ADDR 0x01100000 //ddr地址
#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 RX_BD_SPACE_BASE (MEM_BASE_ADDR)
#define RX_BD_SPACE_HIGH (MEM_BASE_ADDR + 0x0000FFFF)
#define TX_BD_SPACE_BASE (MEM_BASE_ADDR + 0x00010000)
#define TX_BD_SPACE_HIGH (MEM_BASE_ADDR + 0x0001FFFF)
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000)
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH (MEM_BASE_ADDR + 0x004FFFFF)
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
//超时计数
#define RESET_TIMEOUT_COUNTER 10000
// 要传输的每个packet大小
#define MAX_PKT_LEN 0x100 //缓冲个数
#define MARK_UNCACHEABLE 0x701
// 每个packet对应的BD数量
#define NUMBER_OF_BDS_PER_PKT 12
// 一共要传输的packet个数
#define NUMBER_OF_PKTS_TO_TRANSFER 11
// 总共需要的BD总数
#define NUMBER_OF_BDS_TO_TRANSFER (NUMBER_OF_PKTS_TO_TRANSFER * \
NUMBER_OF_BDS_PER_PKT)
/*中断合并阈值和延迟定时器阈值
*有效范围为1到255
*我们将合并阈值设置为包的总数。在这个例子中,接收端只会得到一个完成中断。
*/
#define COALESCING_COUNT NUMBER_OF_PKTS_TO_TRANSFER
#define DELAY_TIMER_COUNT 100
#define INTC XScuGic
#define INTC_HANDLER XScuGic_InterruptHandler
//函数申明
static int CheckData(int Length, u8 StartValue);
static void TxCallBack(XAxiDma_BdRing * TxRingPtr);
static void TxIntrHandler(void *Callback);
static void RxCallBack(XAxiDma_BdRing * RxRingPtr);
static void RxIntrHandler(void *Callback);
static int SetupIntrSystem(INTC * IntcInstancePtr,
XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);
static void DisableIntrSystem(INTC * IntcInstancePtr,
u16 TxIntrId, u16 RxIntrId);
static int RxSetup(XAxiDma * AxiDmaInstPtr);
static int TxSetup(XAxiDma * AxiDmaInstPtr);
static int SendPacket(XAxiDma * AxiDmaInstPtr);
//设备实例
XAxiDma AxiDma;
//中断控制器实例
static INTC Intc;
//中断标志
volatile int TxDone;
volatile int RxDone;
volatile int Error;
//发送数据包缓冲区,必须32位对齐使用
u32 *Packet = (u32 *) TX_BUFFER_BASE;
int main(void)
{
int Status;
XAxiDma_Config *Config;
xil_printf("\r\n--- Entering strart --- \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引擎
XAxiDma_CfgInitialize(&AxiDma, Config);
if(!XAxiDma_HasSg(&AxiDma)) {
xil_printf("Device configured as Simple mode \r\n");
return XST_FAILURE;
}
//设置发送通道,以使数据准备好发送
Status = TxSetup(&AxiDma);
if (Status != XST_SUCCESS) {
xil_printf("Failed TX setup\r\n");
return XST_FAILURE;
}
//设置接收通道,以使数据准备好接收
Status = RxSetup(&AxiDma);
if (Status != XST_SUCCESS) {
xil_printf("Failed RX setup\r\n");
return XST_FAILURE;
}
//设置中断
Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
if (Status != XST_SUCCESS) {
xil_printf("Failed intr setup\r\n");
return XST_FAILURE;
}
//初始化标志信号
TxDone = 0;
RxDone = 0;
Error = 0;
//发送数据
Status = SendPacket(&AxiDma);
if (Status != XST_SUCCESS) {
xil_printf("Failed send packet\r\n");
return XST_FAILURE;
}
//检查发送接收是否完成
while (((TxDone < NUMBER_OF_BDS_TO_TRANSFER) ||
(RxDone < NUMBER_OF_BDS_TO_TRANSFER)) && !Error) {}
if (Error) {
xil_printf("Failed test transmit%s done, "
"receive%s done\r\n", TxDone? "":" not",
RxDone? "":" not");
goto Done;
}else {
//数据比对
Status = CheckData(MAX_PKT_LEN * NUMBER_OF_BDS_TO_TRANSFER,0xC);
if (Status != XST_SUCCESS) {
xil_printf("Data check failed\r\n");
goto Done;
}
xil_printf("Successfully ran AXI DMA SG interrupt Example\r\n");
}
//关闭发送、接收中断
DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);
Done:
xil_printf("--- Exiting end --- \r\n");
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
//数据比对
static int CheckData(int Length, u8 StartValue)
{
u8 *RxPacket;
int Index = 0;
u8 Value;
RxPacket = (u8 *) RX_BUFFER_BASE;
Value = StartValue;
//使指定地址缓存区失效
Xil_DCacheInvalidateRange((UINTPTR)RxPacket, Length);
for(Index = 0; Index < Length; Index++) {
if (RxPacket[Index] != Value) {
xil_printf("Data error %d: %x/%x\r\n",
Index, RxPacket[Index], Value);
return XST_FAILURE;
}
Value = (Value + 1) & 0xFF;
}
return XST_SUCCESS;
}
//发送回调函数
static void TxCallBack(XAxiDma_BdRing * TxRingPtr)
{
int BdCount;
u32 BdSts;
XAxiDma_Bd *BdPtr;
XAxiDma_Bd *BdCurPtr;
int Status;
int Index;
xil_printf("enter TxCallBack\r\n");
//获取所有已处理bd
BdCount = XAxiDma_BdRingFromHw(TxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
//处理bd
BdCurPtr = BdPtr;
for (Index = 0; Index < BdCount; Index++) {
//检查bd状态并判断是否有错误
BdSts = XAxiDma_BdGetSts(BdCurPtr);
if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
(!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
Error = 1;
break;
}
//这里我们什么都不需要做。但是如果RTOS是使用时,我们可能需要释放附加的数据包缓冲区处理过的BD
/* NOP */
//查找下一个处理的bd
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(TxRingPtr, BdCurPtr);
}
//释放所有bd内存以便下一次使用
Status = XAxiDma_BdRingFree(TxRingPtr, BdCount, BdPtr);
if (Status != XST_SUCCESS) {
Error = 1;
}
if(!Error) {
//没有错误返回已处理bd个数
TxDone += BdCount;
}
}
//发送中断函数
static void TxIntrHandler(void *Callback)
{
XAxiDma_BdRing *TxRingPtr = (XAxiDma_BdRing *) Callback;
u32 IrqStatus;
int TimeOut;
//读取挂起中断
IrqStatus = XAxiDma_BdRingGetIrq(TxRingPtr);
//确认挂起中断
XAxiDma_BdRingAckIrq(TxRingPtr, IrqStatus);
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
return;
}
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
XAxiDma_BdRingDumpRegs(TxRingPtr);
Error = 1;
//复位
XAxiDma_Reset(&AxiDma);
TimeOut = RESET_TIMEOUT_COUNTER;
while (TimeOut) {
if (XAxiDma_ResetIsDone(&AxiDma)) {break;}
TimeOut -= 1;
}
return;
}
//无错误调用发送回调函数
if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
xil_printf("Generate tx interrupt\r\n");
TxCallBack(TxRingPtr);
}
}
static void RxCallBack(XAxiDma_BdRing * RxRingPtr)
{
int BdCount;
XAxiDma_Bd *BdPtr;
XAxiDma_Bd *BdCurPtr;
u32 BdSts;
int Index;
xil_printf("enter RxCallBack\r\n");
//获取所有已处理bd
BdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
//处理bd
BdCurPtr = BdPtr;
for (Index = 0; Index < BdCount; Index++) {
//检查bd状态并判断是否有错误
BdSts = XAxiDma_BdGetSts(BdCurPtr);
if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
(!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
Error = 1;
break;
}
//查找下一个处理的bd
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
RxDone += 1;
}
}
static void RxIntrHandler(void *Callback)
{
XAxiDma_BdRing *RxRingPtr = (XAxiDma_BdRing *) Callback;
u32 IrqStatus;
int TimeOut;
/* Read pending interrupts */
IrqStatus = XAxiDma_BdRingGetIrq(RxRingPtr);
/* Acknowledge pending interrupts */
XAxiDma_BdRingAckIrq(RxRingPtr, IrqStatus);
if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {return;}
if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
XAxiDma_BdRingDumpRegs(RxRingPtr);
Error = 1;
XAxiDma_Reset(&AxiDma);
TimeOut = RESET_TIMEOUT_COUNTER;
while (TimeOut) {
if(XAxiDma_ResetIsDone(&AxiDma)) {break;}
TimeOut -= 1;
}
return;
}
//调用回调
if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
xil_printf("Generate rx interrupt\r\n");
RxCallBack(RxRingPtr);
}
}
//初始化中断
static int SetupIntrSystem(INTC * IntcInstancePtr,
XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
//获取发送/接收 环
XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(AxiDmaPtr);
XAxiDma_BdRing *RxRingPtr = XAxiDma_GetRxRing(AxiDmaPtr);
int Status;
XScuGic_Config *IntcConfig;
//查找中断控制器信息
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
//初始化中断控制器
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//设置中断优先级
XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
//绑定中断处理函数
Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
(Xil_InterruptHandler)TxIntrHandler,
TxRingPtr);
if (Status != XST_SUCCESS) {
return Status;
}
Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
(Xil_InterruptHandler)RxIntrHandler,
RxRingPtr);
if (Status != XST_SUCCESS) {
return Status;
}
//启动中断源
XScuGic_Enable(IntcInstancePtr, TxIntrId);
XScuGic_Enable(IntcInstancePtr, RxIntrId);
//启动硬件中断
Xil_ExceptionInit();
//绑定中断异常处理函数
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)INTC_HANDLER,
(void *)IntcInstancePtr);
//启用IRQ异常
Xil_ExceptionEnable();
return XST_SUCCESS;
}
//禁用dma中断
static void DisableIntrSystem(INTC * IntcInstancePtr,
u16 TxIntrId, u16 RxIntrId)
{
XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
}
//设置读取通道
static int RxSetup(XAxiDma * AxiDmaInstPtr)
{
XAxiDma_BdRing *RxRingPtr;
int Status;
XAxiDma_Bd BdTemplate;
XAxiDma_Bd *BdPtr;
XAxiDma_Bd *BdCurPtr;
int BdCount;
int FreeBdCount;
UINTPTR RxBufferPtr;
int Index;
//获取接收环
RxRingPtr = XAxiDma_GetRxRing(&AxiDma);
//设置空间之前禁用所有读取中断
XAxiDma_BdRingIntDisable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
//设置bd空间
BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
RX_BD_SPACE_HIGH - RX_BD_SPACE_BASE + 1);
//创建bd环
Status = XAxiDma_BdRingCreate(RxRingPtr, RX_BD_SPACE_BASE,RX_BD_SPACE_BASE,
XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
if (Status != XST_SUCCESS) {
xil_printf("Rx bd create failed with %d\r\n", Status);
return XST_FAILURE;
}
//为Rx通道设置BD模板。然后复制到每个RX BD。
//bd归零
XAxiDma_BdClear(&BdTemplate);
//复制模板到创建的bd 模板为16个4字节数据uint32_t类型
Status = XAxiDma_BdRingClone(RxRingPtr, &BdTemplate);
if (Status != XST_SUCCESS) {
xil_printf("Rx bd clone failed with %d\r\n", Status);
return XST_FAILURE;
}
//在读取bd环上加上缓冲区以便读取数据
FreeBdCount = XAxiDma_BdRingGetFreeCnt(RxRingPtr);
Status = XAxiDma_BdRingAlloc(RxRingPtr, FreeBdCount, &BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx bd alloc failed with %d\r\n", Status);
return XST_FAILURE;
}
BdCurPtr = BdPtr;
RxBufferPtr = RX_BUFFER_BASE;
for (Index = 0; Index < FreeBdCount; Index++) {
//设置bd缓冲地址
Status = XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx set buffer addr %x on BD %x failed %d\r\n",
(unsigned int)RxBufferPtr,
(UINTPTR)BdCurPtr, Status);
return XST_FAILURE;
}
//为给定的bd设置子段长度
Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
RxRingPtr->MaxTransferLen);
if (Status != XST_SUCCESS) {
xil_printf("Rx set length %d on BD %x failed %d\r\n",
MAX_PKT_LEN, (UINTPTR)BdCurPtr, Status);
return XST_FAILURE;
}
//接收BDs不需要设置任何控件硬件会设置每个流的SOF/EOF位
//设置bd控制位
XAxiDma_BdSetCtrl(BdCurPtr, 0);
//设置bd的id
XAxiDma_BdSetId(BdCurPtr, RxBufferPtr);
RxBufferPtr += MAX_PKT_LEN;
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
}
//设置合并阈值,因此只有一个接收中断在本例中出现如果你想有多个中断发生,改变 COALESCING_COUNT是一个较小的值
//为给定的描述符环形通道设置中断合并参数。
Status = XAxiDma_BdRingSetCoalesce(RxRingPtr, COALESCING_COUNT,DELAY_TIMER_COUNT);
if (Status != XST_SUCCESS) {
xil_printf("Rx set coalesce failed with %d\r\n", Status);
return XST_FAILURE;
}
//将一组bd加入到分配的硬件中
Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx ToHw failed with %d\r\n", Status);
return XST_FAILURE;
}
//使能所有读取中断
XAxiDma_BdRingIntEnable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
//启动读取dma通道
Status = XAxiDma_BdRingStart(RxRingPtr);
if (Status != XST_SUCCESS) {
xil_printf("Rx start BD ring failed with %d\r\n", Status);
return XST_FAILURE;
}
return XST_SUCCESS;
}
//设置发送
static int TxSetup(XAxiDma * AxiDmaInstPtr)
{
XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(&AxiDma);
XAxiDma_Bd BdTemplate;
int Status;
u32 BdCount;
XAxiDma_BdRingIntDisable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);
BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
(UINTPTR)TX_BD_SPACE_HIGH - (UINTPTR)TX_BD_SPACE_BASE + 1);
Status = XAxiDma_BdRingCreate(TxRingPtr, TX_BD_SPACE_BASE,
TX_BD_SPACE_BASE,
XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
if (Status != XST_SUCCESS) {
xil_printf("Failed create BD ring\r\n");
return XST_FAILURE;
}
XAxiDma_BdClear(&BdTemplate);
Status = XAxiDma_BdRingClone(TxRingPtr, &BdTemplate);
if (Status != XST_SUCCESS) {
xil_printf("Failed clone BDs\r\n");
return XST_FAILURE;
}
Status = XAxiDma_BdRingSetCoalesce(TxRingPtr, COALESCING_COUNT,
DELAY_TIMER_COUNT);
if (Status != XST_SUCCESS) {
xil_printf("Failed set coalescing"
" %d/%d\r\n",COALESCING_COUNT, DELAY_TIMER_COUNT);
return XST_FAILURE;
}
XAxiDma_BdRingIntEnable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);
Status = XAxiDma_BdRingStart(TxRingPtr);
if (Status != XST_SUCCESS) {
xil_printf("Failed bd start\r\n");
return XST_FAILURE;
}
return XST_SUCCESS;
}
static int SendPacket(XAxiDma * AxiDmaInstPtr)
{
XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(AxiDmaInstPtr);
u8 *TxPacket;
u8 Value;
XAxiDma_Bd *BdPtr, *BdCurPtr;
int Status;
int Index, Pkts;
UINTPTR BufferAddr;
//限制报文长度
if (MAX_PKT_LEN * NUMBER_OF_BDS_PER_PKT >
TxRingPtr->MaxTransferLen) {
xil_printf("Invalid total per packet transfer length for the "
"packet %d/%d\r\n",
MAX_PKT_LEN * NUMBER_OF_BDS_PER_PKT,
TxRingPtr->MaxTransferLen);
return XST_INVALID_PARAM;
}
TxPacket = (u8 *) Packet;
//发送数据
Value = 0xC;
for(Index = 0; Index < MAX_PKT_LEN * NUMBER_OF_BDS_TO_TRANSFER;
Index ++) {
TxPacket[Index] = Value;
Value = (Value + 1) & 0xFF;
}
//发送之前刷新缓冲区
Xil_DCacheFlushRange((UINTPTR)TxPacket, MAX_PKT_LEN *
NUMBER_OF_BDS_TO_TRANSFER);
Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, MAX_PKT_LEN *
NUMBER_OF_BDS_TO_TRANSFER);
Status = XAxiDma_BdRingAlloc(TxRingPtr, NUMBER_OF_BDS_TO_TRANSFER,
&BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Failed bd alloc\r\n");
return XST_FAILURE;
}
BufferAddr = (UINTPTR)Packet;
BdCurPtr = BdPtr;
/*
* Set up the BD using the information of the packet to transmit
* Each transfer has NUMBER_OF_BDS_PER_PKT BDs
*/
for(Index = 0; Index < NUMBER_OF_PKTS_TO_TRANSFER; Index++) {
for(Pkts = 0; Pkts < NUMBER_OF_BDS_PER_PKT; Pkts++) {
u32 CrBits = 0;
//设置缓冲区地址
Status = XAxiDma_BdSetBufAddr(BdCurPtr, BufferAddr);
if (Status != XST_SUCCESS) {
xil_printf("Tx set buffer addr %x on BD %x failed %d\r\n",
(unsigned int)BufferAddr,
(UINTPTR)BdCurPtr, Status);
return XST_FAILURE;
}
//设置长度字段
Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
TxRingPtr->MaxTransferLen);
if (Status != XST_SUCCESS) {
xil_printf("Tx set length %d on BD %x failed %d\r\n",
MAX_PKT_LEN, (UINTPTR)BdCurPtr, Status);
return XST_FAILURE;
}
//起始bd设置sof
if (Pkts == 0) {
CrBits |= XAXIDMA_BD_CTRL_TXSOF_MASK;
}
//最后一个bd设置eof和ioc
if(Pkts == (NUMBER_OF_BDS_PER_PKT - 1)) {
CrBits |= XAXIDMA_BD_CTRL_TXEOF_MASK;
}
XAxiDma_BdSetCtrl(BdCurPtr, CrBits);
XAxiDma_BdSetId(BdCurPtr, BufferAddr);
BufferAddr += MAX_PKT_LEN;
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(TxRingPtr, BdCurPtr);
}
}
//将bd交给硬件
Status = XAxiDma_BdRingToHw(TxRingPtr, NUMBER_OF_BDS_TO_TRANSFER,
BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Failed to hw, length %d\r\n",
(int)XAxiDma_BdGetLength(BdPtr,
TxRingPtr->MaxTransferLen));
return XST_FAILURE;
}
return XST_SUCCESS;
}
笔者使用它进行传感器数据搬运到ddr ps端移植freertos进行oled显示与控制
文章来源地址https://www.toymoban.com/news/detail-700119.html
附上freertos的代码吧官方只是移植了一个框架使用重点在任务间的数据传递以及调度器的使用
//
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
/* Xilinx includes. */
#include "xil_printf.h"
#include "xparameters.h"
#include "sleep.h"
#include "oled_driver.h"
/* user includes. */
#include "sg_dma.h"
/*-----------------------------------------------------------*/
static void dma_rx_Task( void *pvParameters );
static void oled_Task( void *pvParameters );
static void led_Task(void *pvParameters);
static TaskHandle_t dma_RxTask;
static TaskHandle_t oled;
static TaskHandle_t led;
static QueueHandle_t xQueue = NULL;
static QueueHandle_t led_flag = NULL;
extern int rx_data_buf[4];
int main( void )
{
xil_printf( "Hello from Intelligent home system\r\n" );
sg_dma_init();
xTaskCreate( dma_rx_Task,
( const char * ) "DMA_RX",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY,
&dma_RxTask );
xTaskCreate( oled_Task,
( const char * ) "oled",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY+1 ,
&oled );
xTaskCreate( led_Task,
( const char * ) "led",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY+2,
&led );
//创建队列
xQueue = xQueueCreate( 1,sizeof( rx_data_buf ) );
led_flag = xQueueCreate( 1,3);
//检查队列是否创建
configASSERT( xQueue );
vTaskStartScheduler();
for( ;; );
}
static void dma_rx_Task( void *pvParameters )
{
for( ;; )
{
rx_data();
xil_printf("task1\r\n");
xQueueSend( xQueue,
rx_data_buf,
0UL );
}
}
static void oled_Task( void *pvParameters )
{
int Recdstring[4] = {0};
u8 key_val;
u8 led_data[3]={0};
for( ;; )
{
xil_printf("task2\r\n");
xQueueReceive( xQueue,
Recdstring,
portMAX_DELAY );
OLED_ShowNum(34,0,Recdstring[0],2,16);
OLED_ShowNum(34,2,Recdstring[1],2,16);
OLED_ShowNum(98,2,Recdstring[2],1,16);
OLED_ShowNum(98,0,Recdstring[3],3,16);
if (READ == 0){
usleep(20000);
if (READ == 0)
key_val = ~key_val;}while(READ == 0){}
if(key_val){
if(Recdstring[3]<200)
{
led_data[0]=1;
}else{
led_data[0]=0;
}
if(Recdstring[0]>28)
{
led_data[1]=1;
}else{
led_data[1]=0;
}
if(Recdstring[2]>50)
{
led_data[2]=1;
}else{
led_data[2]=0;
}
xQueueSend( led_flag,
led_data,
0UL );
}
}
}
static void led_Task(void *pvParameters)
{
u8 led[3];
for( ;; )
{
xil_printf("task3\r\n");
xQueueReceive( led_flag,
led,
portMAX_DELAY );
if(led[0]){
LIGHT_HGIH;
}else{
LIGHET_LOW;
}
if(led[1]){
AIR_HIGH;
}else{
AIR_LOW;
}
if(led[2]){
flag_HIGH;
}else{
flag_LOW;
}
}
}
到了这里,关于ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!