【正点原子FPGA连载】第二十一章AXI DMA环路测试 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html
第二十一章AXI DMA环路测试
DMA(Direct Memory Access,直接存储器访问)是计算机科学中的一种内存访问技术。它允许某些计算机内部的硬件子系统可以独立地直接读写系统内存,而不需中央处理器(CPU)介入处理。DMA是一种快速的数据传送方式,通常用来传送数据量较多的数据块,很多硬件系统会使用DMA,包括硬盘控制器、绘图显卡、网卡和声卡,在使用高速AD/DA时使用DMA也是不错的选择。
本章我们使用PL的AXI DMA IP核实现DMA环路功能,了解DMA的使用。本章包括以下几个部分:
2121.1简介
21.2实验任务
21.3硬件设计
21.4软件设计
21.5下载验证
21.1简介
DMA是所有现代计算机的重要特色,它允许不同速度的硬件设备进行沟通,而不需要依于中央处理器的大量中断负载。否则,中央处理器需要从来源把每一片段的数据复制到寄存器,然后把它们再次写回到新的地方。在这个时间里,中央处理器就无法执行其它的任务。
DMA是用硬件实现存储器与存储器之间或存储器与I/O设备之间直接进行高速数据传输。使用DMA时,CPU向DMA控制器发出一个存储传输请求,这样当DMA控制器在传输的时候,CPU执行其它操作,传输操作完成时DMA以中断的方式通知CPU。
为了发起传输事务,DMA控制器必须得到以下数据:
• 源地址 — 数据被读出的地址
• 目的地址 — 数据被写入的地址
• 传输长度 — 应被传输的字节数
图 21.1.1 DMA存储传输过程
DMA存储传输的过程如下:
- 为了配置用DMA传输数据到存储器,处理器发出一条DMA命令。
- DMA控制器把数据从外设传输到存储器或从存储器到存储器,而让CPU腾出手来做其它操作。
- 数据传输完成后,向CPU发出一个中断来通知它DMA传输可以关闭了。
MPSOC中有两个通用DMA控制器:LPD DMA和FPD DMA。除了一致性,命令缓冲区大小和数据宽度外,8通道LPD和FPD DMA控制器在体系结构上是相同的。
Zynq UltraScale+ MPSOC是一种用来存储到存储、存储到IO、IO到存储、IO到IO传输的通用DMA,其模块框图如下图所示:
图21.1.2 DMA模块框图
AXI Direct Memory Access(AXI DMA)IP内核在AXI4内存映射和AXI4-Stream IP接口之间提供高带宽直接储存访问。其可选的scatter gather功能还可以从基于处理器的系统中的中央处理单元(CPU)卸载数据移动任务。初始化、状态和管理寄存器通过AXI4-Lite从接口访问。核心的功能组成如下图所示:
图21.1.3 AXI DMA框图
AXI DMA用到了三种总线,AXI4-Lite用于对寄存器进行配置,AXI4 Memory Map用于与内存交互,又分为AXI4 Memory Map Read和AXI4 Memory Map Write两个接口,一个是读一个是写。AXI4 Stream 接口用于对外设的读写,其中AXI4 Stream Master(MM2S,Memory Map to Stream)用于对外设写,AXI4-Stream Slave(S2MM,Stream to Memory Map)用于对外设读。总之,在以后的使用中需要知道AXI_MM2S和AXI_S2MM是存储器端映射的AXI4总线,提供对存储器(DDR)的访问。AXIS_MM2S和AXIS_S2MM是AXI4-streaming总线,可以发送和接收连续的数据流,无需地址。
AXI DMA提供3种模式,分别是Direct Register模式、Scatter/Gather模式和Cyclic DMA模式,这里我们简单的介绍下常用的Direct Register模式和Scatter/Gather模式。
Direct Register DMA模式也就是Simple DMA。Direct Register模式提供了一种配置,用于在MM2S和S2MM通道上执行简单的DMA传输,这需要更少的FPGA资源。Simple DMA允许应用程序在DMA和Device之间定义单个事务。它有两个通道:一个从DMA到Device,另一个从Device到DMA。应用程序必须设置缓冲区地址和长度字段以启动相应通道中的传输。
Scatter/Gather DMA模式允许在单个DMA事务中将数据传输到多个存储区域或从多个存储区域传输数据。它相当于将多个Simple DMA请求链接在一起。SGDMA允许应用程序在内存中定义事务列表,硬件将在应用程序没有进一步干预的情况下处理这些事务。在此期间,应用程序可以继续添加更多工作以保持硬件工作。用户可以通过轮询或中断来检查事务是否完成。SGDMA处理整个数据包(被定义为表示消息的一系列数据字节)并允许将数据包分解为一个或多个事务。例如,采用以太网IP数据包,该数据包由14字节的报头后跟1个或多个字节的有效负载组成。使用SGDMA,应用程序可以将BD(Buffer Descriptor,用于描述事务的对象)指向报头,将另一个BD指向有效负载,然后将它们作为单个消息传输。这种策略可以使TCP / IP堆栈更有效,它允许将数据包标头和数据保存在不同的内存区域,而不是将数据包组装成连续的内存块。
在本设计中,不需要使用scatter gather DMA模式,因为可以使用DMA的更简单的寄存器直接模式充分实现系统,从而避免实现scatter gather功能带来的面积成本。在系统需要对DMA进行相对复杂的软件控制时,可以使用scatter gather模式。
21.2实验任务
本章的实验任务是在MPSOC开发板上使用PL的AXI DMA IP核从DDR中读取数据,并将数据写回到DDR中。
21.3硬件设计
在实际应用中,DMA一般与产生数据或需求数据的IP核相连接,该IP核可以是带有Stream接口的高速的AD(模拟转数字)或DA(数字转模拟) IP核。不失一般性,在本次实验中,我们使用AXI4 Stream Data FIFO IP核来充当这类IP进行DMA环回实验。大致的系统框图如下:
图 21.3.1 AXI DAM环路测试系统框图
PS开启Slave AXI HP和Master AXI HPM接口。AXI DMA和AXI4 Stream Data FIFO在PL中实现。处理器通过Master AXI HPM接口与AXI DMA通信,以设置、启动和监控数据传输。数据传输通过Slave AXI HP接口。AXI DMA通过Slave AXI HP接口从DDR中读取数据后发送给AXI4 Stream Data FIFO,这种情况下AXI4 Stream Data FIFO可以相当于带有Stream接口的高速DA。AXI DMA读取AXI4 Stream Data FIFO中的数据后通过Slave AXI HP接口写入DDR的情形,AXI4 Stream Data FIFO相当于带有Stream接口的高速AD。
step1:创建Vivado工程
打开Vivado,创建一个名为“axi_dma_loop”的空白工程,工程路径为F:\ZYNQ\Embedded_System\axi_dma_loop文件夹。注意,工程名和路径只能由英文字母、数字和下划线组成,不能包含中文、空格以及特殊字符!
step2:使用IP Integrator创建Processing System
2-1 在左侧导航栏(Flow Navigator)中,单击IP Integrator下的Create Block Design。然后在弹出的对话框中指定所创建的Block Design的名称,这里使用Design name栏中默认的“design_1”,然后点击“OK”,如下图所示:
图 21.3.2 创建 Block Design
2-2 添加Zynq UltraScale+ MPSOC模块。接下来按照《“Hello World”实验》中的步骤2-7、2-8分别配置PS的UART和DDR控制器。需要特别注意的是,我们在《“Hello World”实验》的步骤2-10中,移除了PS中与PL端交互的接口信号,这些接口在我们本次实验中需要予以保留。
2-3 配置时钟。
点击左侧的Clock Configuration页面,然后在右侧界面中打开Output Clocks标签页,依次展开Low Power Domain Clocks->PL Fabric Clocks,可以看到默认勾选PL0,且时钟频率为100MHz,这里我们使用默认的100MHz,如下图所示:
图 21.3.3 配置pl_clk0
2-4 开启HP接口。
点击左侧的PS-PL Configuration页面,然后依次展开PS-PL Interfaces->Slave Interface->AXI HP,勾选AXI HP0 FPD,数据位宽使用默认的128bit位宽,如下图所示:
图 21.3.4 启用HP0接口
2-5 因为DMA在传输完成后通过发送中断通知CPU,所以我们需要开启PL到PS的中断。
还是在PS-PL Configuration界面中,依次展开General->Interrupts->PL to PS,设置IRQ0[0:7]为1,如下图所示:
图 21.3.5 开启PL到PS的中断
2-6 配置Zynq UltraScale+ MPSOC模块完成,点击“OK”,配置完成后的Zynq UltraScale+ MPSOC模块如下图所示:
图 21.3.6 配置完成后的Zynq UltraScale+ MPSOC IP
2-7 添加DMA IP。
添加DMA IP,如同添加Zynq UltraScale+ MPSOC IP,只不过搜索词由MPSOC变为dma,如下图所示:
图 21.3.7 添加DMA模块
图 21.3.8 配置DMA模块
我们双击axi_dma_0,打开配置界面,如图 21.3.8所示,此处我们只需要取消勾选Enable Scatter Gather Engine即可。不过还是介绍下与配置相关的选项。
Enable Scatter Gather Engine
选中此选项可启用Scatter Gather模式操作,并在AXI DMA中包含Scatter Gather Engine。取消选中此选项可启用Direct Register模式操作,但不包括AXI DMA中的Scatter Gather Engine。禁用Scatter Gather Engine会使Scatter/Gather Engine的所有输出端口都绑定为零,并且所有输入端口都将保持打开状态。此处我们取消勾选Enable Scatter Gather Engine。
Enable Micro DMA
选中此选项会生成高度优化的DMA,资源数量较少。此设置可用于传输极少量数据的应用程序。此处我们不勾选。
Width of Buffer Length Register
此整数值指定用于控制字段缓冲区长度的有效位数和在Scatter/Gather描述符中传输的Status字段的字节数。字节数等于。因此,长度为26时,字节数为字节。对于多通道模式,此值应设置为23。此处我们保持默认设置14。
Address Width (32 - 64)
指定地址空间的宽度,可以是32到64之间的任何值。此处保持默认值32。
Enable Read Channel
开启AXI DMA的读通道MM2S,相关选项如下:
Number of Channels:指定通道数。保持默认值1。
Memory Map Data Width:AXI MM2S存储映射读取数据总线的数据位宽。有效值为32、64、128、256、512和1024。此处保持默认值32。
Stream Data Width:AXI MM2S AXI4-Stream数据总线的数据位宽。该值必须小于或等于Memory Map Data Width。有效值为8、16、32、64、128、512和1024。此处保持默认值32。
Max Burst Size:突发分区粒度设置。此设置指定MM2S的AXI4-Memory Map侧的突发周期的最大大小。有效值为2、4、8、16、32、64、128和256。此处保持默认值16。
Allow Unaligned Transfers:启用或禁用MM2S数据重新排列引擎(Data Realignment Engine,DRE)。选中时,DRE被使能并允许在MM2S存储映射数据路径上数据重新对齐到8位的字节水平。对于MM2S通道,从内存中读取数据。如果DRE被使能,则数据读取可以从任何缓冲区地址字节偏移开始,并且读取数据被对齐,使得第一个字节读取是AXI4-Stream上的第一个有效字节输出。
注意:如果为相应通道禁用DRE,则不支持未对齐的缓冲区、源或目标地址。在禁用DRE的情况下使用未对齐的地址会产生未定义的结果。DRE支持仅适用于512位及以下的AXI4-Stream数据宽度设置。
Enable Write Channel
开启AXI DMA的写通道S2MM,相关选项可参考读通道。
2-8 添加axis_data_fifo IP
图 21.3.9 添加axis_data_fifo IP
该IP保持默认设置即可。添加该IP核的重点不是了解该IP核如何使用,而是知道该IP核带有AXI4 Stream接口。在以后的实际使用中,需要封装自定义IP核时要注意这一点。
2-9 添加Concat IP。
Concate IP 实现了单个分散的信号,整合成总线信号。这里2个独立的中断信号,可以合并在一起接入到Zynq UltraScale+ MPSOC IP的中断信号上。
图 21.3.10 添加Concat IP
2-10 模块连接。
在Diagram窗口中,点击“Run connection Automation”,进行自动连接,在弹出的界面中勾选“All Automation”,然后点击OK,如下图所示:
图 21.3.11 Run connection Automation
2-11 自动连接完成后,发现Concat IP未连接,我们手动进行连接,如下图所示:
图21.3.12 手动连接Concat IP
另外添加的axis_data_fifo也未连接,我们同样手动连接。首先将DMA的M_AXIS_MM2S端口与axis_data_fifo的S_AXIS进行连接,如下图所示:
图21.3.13 连接axis_data_fifo的S_AXIS
然后将axis_data_fifo上的M_AXIS端口连接到DMA的S_AXIS_S2MM端口,如下图所示:
图21.3.14 连接axis_data_fifo上的M_AXIS
现在我们连接axis_data_fifo的时钟和复位。单击axis_data_fifo的s_axis_aresetn端口并将其连接到DMA的axi_resetn端口,单击axis_data_fifo的s_axis_aclk端口并将其连接到DMA的m_axi_mm2s_aclk端口,如下图所示:
图21.3.15 axis_data_fifo的时钟和复位
2-12 再次点击Run Connection Automation,然后右击空白处选择Regenerate Layout重新布局,最终的IP模块连接图如下图所示:
图21.3.16 模块连接图
2-13 到这里我们的Block Design就设计完成了,按“F6”键进行“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
step3:生成顶层HDL模块
3-1 在Sources窗口中,选中Design Sources下的design_1.bd, 这就是我们刚刚完成的Block Design设计。右键点击design_1.bd,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。注意在执行“Generate Output Products”步骤时,受电脑配置的影响,软件运行时间会较长,要等待一会。
step4:生成Bitstream文件并导出Hardware
4-1 在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”。在连续弹出的对话框中依次点击“YES”、“OK”。然后 Vivado 工具开始依次对设计进行综合、实现、并生成 Bitstream 文件。
4-2 导出硬件。
在生成Bitstream之后,在菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中,勾选“Include bitstream”。将导出的“design_1_wrapper.xsa”文件放到vitis文件夹,然后在菜单栏选择Tools > Launch Vitis,启动VITIS软件。
21.4软件设计
step5:在VITIS中创建应用工程
5-1 在VITIS软件中新建一个名为“axi_dma_loop”的空白应用工程。
5-2 为应用工程新建一个名为“main.c”源文件,我们在新建的main.c文件中输入本次实验的代码。代码的主体部分如下所示:
1 /***************************** Include Files *********************************/
2
3 #include "xaxidma.h"
4 #include "xparameters.h"
5 #include "xil_exception.h"
6 #include "xscugic.h"
7
8 /************************** Constant Definitions *****************************/
9
10 #define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
11 #define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
12 #define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
13 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
14 #define DDR_BASE_ADDR XPAR_PSU_DDR_0_S_AXI_BASEADDR //0x00000000
15 #define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1100000) //0x01100000
16 #define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) //0x01200000
17 #define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) //0x01400000
18 #define RESET_TIMEOUT_COUNTER 10000 //复位时间
19 #define TEST_START_VALUE 0x0 //测试起始值
20 #define MAX_PKT_LEN 0x100 //发送包长度
21
22 /************************** Function Prototypes ******************************/
23
24 static int check_data(int length, u8 start_value);
25 static void tx_intr_handler(void *callback);
26 static void rx_intr_handler(void *callback);
27 static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
28 u16 tx_intr_id, u16 rx_intr_id);
29 static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
30 u16 rx_intr_id);
31
32 /************************** Variable Definitions *****************************/
33
34 static XAxiDma axidma; //XAxiDma实例
35 static XScuGic intc; //中断控制器的实例
36 volatile int tx_done; //发送完成标志
37 volatile int rx_done; //接收完成标志
38 volatile int error; //传输出错标志
39
40 /************************** Function Definitions *****************************/
41
42 int main(void)
43 {
44 int i;
45 int status;
46 u8 value;
47 u8 *tx_buffer_ptr;
48 u8 *rx_buffer_ptr;
49 XAxiDma_Config *config;
50
51 tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
52 rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;
53
54 xil_printf("\r\n--- Entering main() --- \r\n");
55
56 config = XAxiDma_LookupConfig(DMA_DEV_ID);
57 if (!config) {
58 xil_printf("No config found for %d\r\n", DMA_DEV_ID);
59 return XST_FAILURE;
60 }
61
62 //初始化DMA引擎
63 status = XAxiDma_CfgInitialize(&axidma, config);
64 if (status != XST_SUCCESS) {
65 xil_printf("Initialization failed %d\r\n", status);
66 return XST_FAILURE;
67 }
68
69 if (XAxiDma_HasSg(&axidma)) {
70 xil_printf("Device configured as SG mode \r\n");
71 return XST_FAILURE;
72 }
73
74 //建立中断系统
75 status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
76 if (status != XST_SUCCESS) {
77 xil_printf("Failed intr setup\r\n");
78 return XST_FAILURE;
79 }
80
81 //初始化标志信号
82 tx_done = 0;
83 rx_done = 0;
84 error = 0;
85
86 value = TEST_START_VALUE;
87 for (i = 0; i < MAX_PKT_LEN; i++) {
88 tx_buffer_ptr[i] = value;
89 value = (value + 1) & 0xFF;
90 }
91
92 Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, MAX_PKT_LEN); //刷新Data Cache
93
94 status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) tx_buffer_ptr,
95 MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
96 if (status != XST_SUCCESS) {
97 return XST_FAILURE;
98 }
99
100 status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) rx_buffer_ptr,
101 MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
102 if (status != XST_SUCCESS) {
103 return XST_FAILURE;
104 }
105
106 Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, MAX_PKT_LEN); //刷新Data Cache
107 while (!tx_done && !rx_done && !error)
108 ;
109 //传输出错
110 if (error) {
111 xil_printf("Failed test transmit%s done, "
112 "receive%s done\r\n", tx_done ? "" : " not",
113 rx_done ? "" : " not");
114 goto Done;
115 }
116
117 //传输完成,检查数据是否正确
118 status = check_data(MAX_PKT_LEN, TEST_START_VALUE);
119 if (status != XST_SUCCESS) {
120 xil_printf("Data check failed\r\n");
121 goto Done;
122 }
123
124 xil_printf("Successfully ran AXI DMA Loop\r\n");
125 disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);
126
127 Done: xil_printf("--- Exiting main() --- \r\n");
128 return XST_SUCCESS;
129 }
在代码的第14行,我们重新宏定义了XPAR_PSU_DDR_0_S_AXI_BASEADDR,即DDR的基址,打开XPAR_PSU_DDR_0_S_AXI_BASEADDR的定义处,我们可以看到DDR的基址为0x00000000,如下图所示:
图 21.4.1 DDR的地址映射
从而DMA读取数据的起始地址TX_BUFFER_BASE为0x01200000,写入到DDR中的起始地址RX_BUFFER_BASE为0x01400000。代码第19行TEST_START_VALUE为测试起始值,此处我们将其设为0x0,也可以改为其它任意值。第20行的MAX_PKT_LEN是DMA传输的数据包的长度,此处为0x100,即256。
代码第42行的main函数是程序的主体。第63行的XAxiDma_CfgInitialize函数初始化DMA引擎,第75行的setup_intr_system函数建立DMA中断系统。第87~90行向DDR的指定地址写入数据,写入的第一个地址为TX_BUFFER_BASE即0x01200000,值为TEST_START_VALUE即0x0,写入的地址长度为MAX_PKT_LEN,即0x100。DMA从TX_BUFFER_BASE读取数据长度为MAX_PKT_LE的数据,然后写入到地址RX_BUFFER_BAS处。第92行的Xil_DcacheFlushRange函数刷新Data Cache,以防Data Cache缓存数据。从第95行到第105行配置并开启DMA传输数据。第107行再次刷新Data Cache,由于DDR中的数据已经更新,但Cache中的数据并没有更新,CPU如果想从DDR中读取数据需要刷新Data Cache。此处使用Xil_DcacheFlushRange函数,也可以使用Xil_DcacheInvalidateRange函数,使Data Cache指定范围的数据无效,函数调用方法相同。第118行的check_data函数检查当DMA传输完成后,写入的数据是否正确。第125行的disable_intr_system函数取消DMA中断。
主函数main中调用的自定义函数实现如下:
131 //检查数据缓冲区
132 static int check_data(int length, u8 start_value)
133 {
134 u8 value;
135 u8 *rx_packet;
136 int i = 0;
137
138 value = start_value;
139 rx_packet = (u8 *) RX_BUFFER_BASE;
140 for (i = 0; i < length; i++) {
141 if (rx_packet[i] != value) {
142 xil_printf("Data error %d: %x/%x\r\n", i, rx_packet[i], value);
143 return XST_FAILURE;
144 }
145 value = (value + 1) & 0xFF;
146 }
147
148 return XST_SUCCESS;
149 }
150
151 //DMA TX中断处理函数
152 static void tx_intr_handler(void *callback)
153 {
154 int timeout;
155 u32 irq_status;
156 XAxiDma *axidma_inst = (XAxiDma *) callback;
157
158 //读取待处理的中断
159 irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
160 //确认待处理的中断
161 XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);
162
163 //Tx出错
164 if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
165 error = 1;
166 XAxiDma_Reset(axidma_inst);
167 timeout = RESET_TIMEOUT_COUNTER;
168 while (timeout) {
169 if (XAxiDma_ResetIsDone(axidma_inst))
170 break;
171 timeout -= 1;
172 }
173 return;
174 }
175
176 //Tx完成
177 if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
178 tx_done = 1;
179 }
180
181 //DMA RX中断处理函数
182 static void rx_intr_handler(void *callback)
183 {
184 u32 irq_status;
185 int timeout;
186 XAxiDma *axidma_inst = (XAxiDma *) callback;
187
188 irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
189 XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);
190
191 //Rx出错
192 if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
193 error = 1;
194 XAxiDma_Reset(axidma_inst);
195 timeout = RESET_TIMEOUT_COUNTER;
196 while (timeout) {
197 if (XAxiDma_ResetIsDone(axidma_inst))
198 break;
199 timeout -= 1;
200 }
201 return;
202 }
203
204 //Rx完成
205 if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
206 rx_done = 1;
207 }
208
209 //建立DMA中断系统
210 // @param int_ins_ptr是指向XScuGic实例的指针
211 // @param AxiDmaPtr是指向DMA引擎实例的指针
212 // @param tx_intr_id是TX通道中断ID
213 // @param rx_intr_id是RX通道中断ID
214 // @return:成功返回XST_SUCCESS,否则返回XST_FAILURE
215 static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
216 u16 tx_intr_id, u16 rx_intr_id)
217 {
218 int status;
219 XScuGic_Config *intc_config;
220
221 //初始化中断控制器驱动
222 intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
223 if (NULL == intc_config) {
224 return XST_FAILURE;
225 }
226 status = XScuGic_CfgInitialize(int_ins_ptr, intc_config,
227 intc_config->CpuBaseAddress);
228 if (status != XST_SUCCESS) {
229 return XST_FAILURE;
230 }
231
232 //设置优先级和触发类型
233 XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
234 XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);
235
236 //为中断设置中断处理函数
237 status = XScuGic_Connect(int_ins_ptr, tx_intr_id,
238 (Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
239 if (status != XST_SUCCESS) {
240 return status;
241 }
242
243 status = XScuGic_Connect(int_ins_ptr, rx_intr_id,
244 (Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
245 if (status != XST_SUCCESS) {
246 return status;
247 }
248
249 XScuGic_Enable(int_ins_ptr, tx_intr_id);
250 XScuGic_Enable(int_ins_ptr, rx_intr_id);
251
252 //启用来自硬件的中断
253 Xil_ExceptionInit();
254 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
255 (Xil_ExceptionHandler) XScuGic_InterruptHandler,
256 (void *) int_ins_ptr);
257 Xil_ExceptionEnable();
258
259 //使能DMA中断
260 XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
261 XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
262
263 return XST_SUCCESS;
264 }
265
266 //此函数禁用DMA引擎的中断
267 static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
268 u16 rx_intr_id)
269 {
270 XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
271 XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
272 }
代码第132行起的check_data函数用于检查写入到DDR中的数据是否正确。
代码第152行起的DMA TX中断处理函数tx_intr_handler用于处理DMA发送中断。首先通过XAxiDma_IntrGetIrq函数读取待处理的中断,然后使用XAxiDma_IntrAckIrq函数确认待处理的中断。如果发现是接收出现错误的中断,则使用XAxiDma_Reset函数复位DMA,并使用XAxiDma_ResetIsDone函数判断是否复位完成。如果是发送完成的中断,则置位发送完成标志tx_done。代码第182行起的DMA RX中断处理函数与此类似。
代码第215行起的建立DMA中断系统函数setup_intr_system首先初始化中断控制器驱动,然后使用XScuGic_SetPriorityTriggerType函数设置DMA的优先级和触发类型。XScuGic_Connect函数为中断设置中断处理函数,因为有发送中断和接收中断,所以需要分别设置。XScuGic_Enable函数用于使能DMA发送中断和DMA接收中断源。最后启用来自硬件的中断和使用XAxiDma_IntrEnable函数使能DMA中断。
5-3 我们按快捷键Ctrl+S保存 main.c 文件,右击应用工程名,选择Build Project,编译工程。编译完成后 Console 中会出现提示信息“Build Finished”,同时生成elf文件。
21.5下载验证
首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将USB_UART(开发板PS PORT)接口与电脑连接,用于串口通信。最后连接开发板的电源,给开发板上电。
step6:板级验证
6-1 进入Debug模式。在前面的实验中,我们右击应用工程名,Run As选择第一项“Launch on Hardware”下载程序。本实验,我们换一种方法。右击应用工程名,Debug As选择第一项“Launch on Hardware”,进入调试界面,如下图所示:
图21.5.1 进入Debug模式
然后在串口终端(Vitis Serial Terminal)中,点击箭头处的加号设置端口号,端口号可以通过自己电脑设备管理器查看,这里选择COM9。
6-2 设置Memory Monitors。
在调试界面Memory窗口中,添加需要监视的储存器地址。首先我们添加地址0x1200000,也就是DMA从DDR中读取数据的起始地址TX_BUFFER_BASE,添加方式如下图所示:
图21.5.2 添加监视存储地址0x1200000
在新添加的Memory监视界面的空白处右击,在弹出的菜单栏中选择“Format…”,设置“Column Size”为1,分别如图21.5.3和图21.5.4所示:
图21.5.3 设置数据显示格式
图21.5.4 设置Column Size大小
设置完成后如下图所示,这样可以更方便观察数据。可以看到,刚下载完程序后,DDR中地址0x1200000处的值为0x71。
图21.5.5 0x1200000地址处的值
现在我们按照同样的方式添加并设置地址0x1400000,也就是DMA将数据写回DDR中的起始地址RX_BUFFER_BASE。从下图可以看到,刚下载完程序后,DDR的0x1400000地址处的值为0x71。
图21.5.6 0x1400000地址处的值
6-3 设置运行断点。
我们在程序的以下几个地方设置断点。
图21.5.7 设置断点
6-4 按F8先将程序运行到断点1处(113行),此时从Memory Monitors中可以看到0x1200000地址处的值变为00,紧随其后的地址的数据值逐次递增1。由于CPU与DDR之间是通过Cache 交互的,数据暂存在Cache中,没有刷新Data Cache数据到DDR,显示的数据是Data Cache中的。
图21.5.8 0x1200000地址数据变化
接着运行到断点2处(115行), Data Cache中的数据已经刷新到DDR4中。此时我们将Memory Monitors窗口中的监视内容切换到0x1400000,看看地址0x1400000处的数据是什么时候更新的,点击左侧的0x1400000即可切换。运行到断点3处(121行),执行完第115行的DMA发送函数,完成从内存中读取数据传输给外设,即DMA从地址0x1200000处读取数据传输给外设,此时地址0x1400000处的数据未更新。运行到断点4处(127行),执行完第121行的DMA接收函数,完成从外设读取数据写入到内存,即将刚才写入到外设的数据读取出来并从DDR的地址0x1400000处开始写入,不过此时我们从下图发现地址0x1400000处的数据还是未更新。其实此时DDR中的数据已经更新,只不过我们Data Cache中的数据未更新,而Memory Monitors窗口显示的正是Data Cache中的数据,所以需要刷新Data Cache。
图21.5.9 0x1400000地址数据未变化
接着运行到断点5处(128行),刷新Data Cache后,此时我们发现地址0x1400000处的值变为00,紧随其后的地址处的数据都变成预期的值。
图21.5.10 刷新Data Cache后0x1400000地址数据变化
到此,使用DMA从DDR中读取数据,并将数据写回到DDR中的实验任务就完成了。按F6继续往下执行,到程序结束,在下方的 VITIS Terminal 中可以看到应用程序打印的信息,如下图所示:
图21.5.11 串口终端中打印的信息
相关文章:
【正点原子FPGA连载】第二十一章AXI DMA环路测试 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第二十一章AXI D…...
手把手搭建springboot项目06-springboot整合RabbitMQ及其原理和应用场景
目录前言工作流程-灵魂画手名词解释交换机类型一、安装1.1 [RabbitMQ官网安装](https://www.rabbitmq.com/download.html)1.2 Docker安装并启动二、食用教程2.1.导入依赖2.2 添加配置2.3 代码实现2.3.1 直连(Direct)类型2.3.2 引入消息手动确认机制2.3.2…...
如何根据IP地址判断是IPv4还是IPv6
IPv4地址的书写形式为:“192.168.0.1” IPv6地址的书写形式为:“2001:DB8:85A3:8D3:1319:8A2E:370:7344” 给你一个IP地址,它有三种可能:IPv4、IPv6、既不是IPv4也不是IPv6的无效地址。所以,如果用函数ipGetAddressAsNumber,只能判断是不是ipv4,编写如下函数: int R…...
山地车和公路车怎么选
公路车: 只能适应平坦的路面,骑行阻力小,速度快比较适合新手 山地车: 能适应所有路面,更注重操控性和舒适性 怎么选? 1、先决定用途 旅游:旅行车、山地车、 通勤:公路车 2、预…...
Zotero设置毕业论文/中文期刊参考文献格式
大家在使用zotero时很容易遇到的问题: 英文参考文献中有多个作者时出现“等”,而不是用"et al"引文最后面有不需要的DOI号,或者论文链接对于一些期刊分类上会出现OL字样,即[J/OL]作者名为全大写 本文主要解决以上几个…...
【人工智能与深度学习】自动编码器的简介
【人工智能与深度学习】自动编码器的简介 自动编码器的应用图片生成像素空间和潜在空间插值的差异图像超级分辨率图像修补由文字说明转成图片什么是自动编码器?为什么我们用自动编码器?重建损失完成过度降噪自动编码器:Denoising autoencoder压缩式自动编码器定义自动编码器…...
Isaac-gym(9):项目更新、benchmarks框架梳理
一、项目更新 近期重新git clone isaac gym的强化部分(具体见系列第5篇)时发现官方的github库有跟新,git clone下来后发现多了若干个task,在环境配置上也有一定区别。 例如新旧两版工程项目的setup.py区别如下: git …...
Linux 学习笔记(一):终端 和 Shell 的区别和联系
一、Linux 介绍 1、什么是 Linux Linux 就是一个操作系统,全称 GNU/Linux,是一种类 Unix 操作系统Linux 一开始是没有图形界面的,所有操作都靠 命令 完成。如 磁盘操作、文件存取、目录操作、进程管理、文件权限 等等,可以说 Li…...
cycleGAN算法解读
本文参考:https://blog.csdn.net/Mr_health/article/details/112545671 1 CycleGAN概述 CycleGAN:循环生成对抗神经网络,是一种非监督学习模型。 Pix2pix方法适用于成对数据的风格迁移,而大多数情况下对于A风格的图像…...
解读“方差”
其实,从这个标题就可以看出来,方差,这个问题不简单, 先给出定义: 方差其实应该叫,差方差,(差方)差,差的平方的差,与差的平方之间的误差࿰…...
记录面试问题
以下问题不分先后,按照印象深浅排序,可能一次记录不完成,后面想起来会及时补充,如有不对,恳请各位围观大佬多多指教🙏 印象最深的是一道很简单很简单的题目,我结束面试之后赶紧代码敲敲发现答错…...
(六十四)设计索引的时候,我们一般要考虑哪些因素呢?(上)
本周我们将要讲解一下设计索引的时候,我们通常应该考虑哪些因素,给哪些字段建立索引,如何建立索引,建立好索引之后应该如何使用才是最合适的。 可能有的朋友会希望尽快更新后面的内容,但是因为工作的原因的确非常忙&a…...
【蓝桥杯嵌入式】LCD屏的原理图解析与代码实现(第十三届省赛为例)——STM32
🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 - 蓝…...
论文学习——Reproducing Activation Function for Deep Learning
论文学习——Reproducing Activation Function Abstract RAFs将集中基础激活函数进行线性组合,构建出神经元级的、数据驱动的激活函数。使用RAFs为激活函数的神经网络可以重现传统的近似工具,也能相对于传统网络以更少的参数量拟合目标函数。训练过程中,RAFs可以以更好的条…...
【趣味学Python】Python基础语法讲解
目录 编码 标识符 python保留字 注释 实例(Python 3.0) 实例(Python 3.0) 行与缩进 实例(Python 3.0) 实例 多行语句 数字(Number)类型 字符串(String) 实例(Python 3.0) 空行 等待用户输入 实例(Python 3.0) 同一行显示多条语句 实例(Python 3.0) 多个语句构…...
虚拟局域网VLAN的实现机制
虚拟局域网VLAN的实现机制1.IEEE 802.1Q帧2.交换的端口类型AccessTrunkHybrid(华为特有)1.IEEE 802.1Q帧 IEEE802.1Q帧(也称Dot One Q帧)对以太网的MAC帧格式进行了扩展,插入了4字节的VLAN标记。 2.交换的端口类型 A…...
Mask R-CNN 算法学习总结
Mask R-CNN 相关知识点整体框架1.Resnet 深度残差学习1.1 目的1.2 深度学习深度增加带来的问题1.3 Resnet实现思想【添加恒等映射】2.线性插值2.1 目的2.2 线性插值原理2.3 为什么使用线性插值?3.FPN 特征金字塔3.1 FPN介绍3.2 为什么使用FPN?3.3 自下而上层【提取特征】3.4 …...
Gorm -- 添加记录
文章目录添加单条记录直接添加模型对象赋予默认值方法一: gorm 标签赋予默认值方法二: 设置钩子方法(Hooks)指定字段插入插入时忽略某些字段插入时禁止使用钩子方法添加多条记录通过对象列表插入通过字典列表插入在字典中使用SQL内…...
go提高升阶(四) I/O流学习
I/O 官网课程 购买课程找博主推荐 文章目录I/O文件信息创建文件、目录IO读IO写(权限)文件复制Seeker接口断点续传遍历文件夹bufio电脑中一切,都是以 二进制流的形式存在的。jpg:010100000010010101001010101010010101010 编码格式,还原为一个…...
【代码随想录训练营】【Day28】第七章|回溯算法|93.复原IP地址|78.子集|90.子集II
复原IP地址 题目详细:LeetCode.93 这道题与上一道练习题分割回文字符串十分详细,一样是涉及到分割字符串、判断字符串、递归与回溯的问题,所以这道题要解决的难点在于: 如何分割IP地址字符串如何判断分割的IP地址是否合法递归的…...
Get请求和Post请求区别
前后端交互请求数据的方式有很多种。 例如:Get Post Put Patch Delete Copy 等等很多请求方式 但是用的最多的就是Get和Post Get请求方式 1. get多用于从服务器请求获取数据 2.get传送的数据量较小,不能大于2KB 3.get安全性非常低 Post请求方式 1.…...
static关键字
static的基本基本用法可以分为下面几种: (1)static修饰全局变量 (2) 修饰局部变量 (3)修饰普通函数 (4)修饰类的成员变量 一、static修饰全局变量 当同时编译多个文件时…...
A Comprehensive Tool for Modeling CMOS Image-Sensor-Noise Performance论文总结及翻译
A Comprehensive Tool for Modeling CMOS Image-Sensor-Noise Performance Author: Ryan D. Gow Link: https://ieeexplore.ieee.org/document/4215175/metrics#metrics Select: ⭐️⭐️⭐️⭐️ Type: Academic Journal 备注: CMOS图像传感器噪声性能建模的综合工具 总结 …...
嘀嗒出行再闯IPO:千军万马我无懈
羽扇纶巾笑谈间,千军万马我无懈。 在激烈竞争中再度冲刺港交所IPO的嘀嗒出行,闪露出一丝歌词里的气魄。交通运输部下属网约车监管信息交互系统的数据显示,截至2023年1月31日,全国共有300家网约车平台公司取得网约车平台经营许可。…...
MATLAB算法实战应用案例精讲-【优化算法】增强型鲸鱼优化算法(EWOA)(附matlab代码实现)
前言 增强型鲸鱼优化算法(Enhanced Whale Optimization Algorithm,EWOA)是Mohammad H. Nadimi-Shahraki等人于2022年提出的一种改进算法。由于标准的鲸鱼优化算法及其它的改进算法都存在种群多样性低和搜索策略差的问题,因此引入有效的策略来缓解鲸鱼优化算法的这些核心缺点…...
登录Oracle数据库遇到ORA-01017密码错误的解决办法
文章目录症状分析解决办法欢迎加下方我的微信👇,拉你入学习群我们在登录Oracle数据库时可能会遇到ORA-01017错误,这里分析原因并提供解决办法。点击试看博主的专著《MySQL 8.0运维与优化》(清华大学出版社) 症状 图像…...
10个黑客基础教程!简单有效
如果你的电脑运行缓慢,请使用下面介绍的方法来帮助加速、优化和提高电脑的性能。 1.关闭启动时自动运行的应用程序 计算机上安装的许多应用程序都可以将自己配置为在启动期间自动启动并继续在后台运行,但是,如果不是每天都使用这些应用程序…...
JPA之实体之间的关系
JPA之实体之间的关系 10.1.1实体类创建 注解的应用 Table,Entity IdGeneratedValue指定主键,Column P174 实体类编写规范 Table(name "t_user") Entity(name "User") public class User implements Serializable {IdGeneratedVa…...
如何在 C++ 中调用 python 解析器来执行 python 代码(三)?
本文在 C 中调用 multi.py 脚本,并向它传入参数并执行,然后获得返回值并在 C 中打印结果。 目录 如何在 C 中调用 python 解析器来执行 python 代码(一)?如何在 C 中调用 python 解析器来执行 python 代码࿰…...
【Linux】gcc/g++/gdb的使用
🔥🔥 欢迎来到小林的博客!! 🛰️博客主页:✈️小林爱敲代码 🛰️社区 : 进步学堂 🛰️欢迎关注:👍点赞🙌收…...
做宠物服务的相关网站/网站制作公司高端
我们阅读博客的时候经常会用到这样功能,当然有时候也会想把自己的网站上也加入类似的分享功能,各大厂商已经给出了相应的API,点击一个按钮即可弹出窗口进入分享,我们事先可以设置一些参数,一般常用的就是 网站的网址&a…...
网站空间期限查询/友情链接qq群
文章目录一、配置数据源-mysql二、下载安装1.1 下载包安装(已验证)1.2 Github 上下载源码(未验证)1.3 docker安装(已验证)三、springCloud pom主要配置3.1 pom3.2 bootstrap.yaml部分配置3.3 nacos设置对应一、配置数据源-mysql a:配置数据库 /*Navicat Premium Data Transfe…...
怎么在网站标头做图标/艺术培训学校招生方案
Linux操作系统性能评测与测试指标浅析性能测试是对一个操作系统运行效率进行评价的关键环节。我们采用适当的性能测试工具集,在保证工具正确运行和基准软硬件测试环境一致的前提下,运行性能测试工具,对测试数据进行收集和处理分析,…...
门户网站营销特点/杭州seo公司服务
http://www.jianshu.com/p/25e678fa43d3 转载于:https://www.cnblogs.com/mafeng/p/7146306.html...
最靠谱的网站建设公司/广告传媒公司主要做什么
参加认证考试是相当多的人寻求职业发展的必经之路。对于绝大多数人来说,没有证书,是"万万不能"的。在国外,每增加一个认证证书都会带来薪水的提高。国内的薪资水平虽然没有国外那么高,但是相比较国内其他行业࿰…...
在线建设房屋设计网站/360搜索引擎首页
2019独角兽企业重金招聘Python工程师标准>>> 如果说用“永存、曲折、已死、重生”来形容Java,笔者以为一点也不为过。 1991年,James Gosling带领着名为“Green Team”的团队着手研发一种新的语言以及专为下一代数字设备和计算机使用的网络系统…...