手把手教你 Verilog 实现 AXI 总线协议读写:PL 与 PS 交互访问 DDR 全流程_pspl写ddr的三个步骤详解
根据之前设计的AXI读写模块,今天来实际应用一下,进行PL 与 PS 交互操作 DDR 全流程
制作不易,记得三连哦,给我动力,持续更新!!!
完整工程文件下载:PS与PL交互访问DDR工程源码 (点击蓝色字体获取)
📌 项目背景
-
平台:Xilinx Zynq-7000 ( ZedBoard )
-
目的:通过 PL 端 Verilog 逻辑发起 AXI Master 读写操作,访问 PS 端的 DDR 内存,实现数据交互。
-
工具:Vivado2018.3 + Xilinx SDK
✅功能展示
在文章开始之前,首先来看看本篇文章具体做了什么
❶ 通过PS端写1KB数据到DDR
❷ 通过EMIO控制PL端读取DDR
❸ PL端将读取到的数据写到DDR另外一个地址
一、设计思路
根据咱们之前的文章,相信大家已经完全掌握了AXI总线的读写功能,这篇文章将在上次的基础上,进行实际项目的使用,来设计一套基于ZYNQ芯片的PS与PL之间的数据交互demo。以后进行大规模数据处理的时候,就可以根据此设计进行改进,也可以用此项目进行学习AXI总线的使用。
废话不多说,直接上干货!!!
二、整体设计框图
本次设计的总体框图,如下所示
下面将给大家进行简单的讲解
框图展示了FPGA中的PL(Programmable Logic,可编程逻辑)和PS(Processing System,处理系统)之间的数据流和连接关系
- PL部分:
- 包含AXI read module(AXI读模块)和AXI write module(AXI写模块),它们通过AXI4接口与SmartConnect模块连接。
- SmartConnect是一个AXI互联模块,负责管理PL中的数据路由和传输。
- PL通过“AXI HP”接口与PS通信。
- PS部分:
- 包含ARM Cortex-A9处理器,负责运行用户应用程序(user APP)。
- 配备EMIO(Extended Multiplexed I/O)和S_AXI_HP0接口,用于与PL通信。
- DDR3内存通过PS与ARM Cortex-A9连接,用于数据存储。
- 数据流:
- AXI read module和AXI write module通过AXI4接口与SmartConnect交换数据。
- SmartConnect通过AXI4接口与PS的S_AXI_HP0接口连接,实现高性能数据传输。
- PL与PS之间的控制和数据传输通过EMIO和AXI4接口完成。
- 用户APP可以访问DDR3内存,并通过ARM Cortex-A9处理数据。
三、PL部分设计
PL端主要设计有对ZYNQ7 处理系统的配置,已经PL端读写PS端DDR的逻辑模块
3.1 zynq处理系统配置
通过vivado创建一个工程,然后创建一个block design设计,搜索ZYNQ7 Processing System并添加,然后双击进行一些主要功能的配置
配置MIO
配置HP接口
配置一个时钟
主要配置的就是这三个部分,其他的没用到的,就先不配置了
3.2 PL端设计
整体采用block design进行设计,
其中的AXI读写代码,上一篇文章已经讲解过,不懂得可以去看上一篇文章
手把手教你verilog实现AXI总线协议读写(一)---- AXI读写模块设计
其介绍和整体设计框图的完全一致,大家可以按照那个介绍进行理解和学习。
3.3 PS端设计
PS端主要通过SDK来进行设计,其主要的设计流程图如下所示
通过用户输入指令,来控制是否进行DDR的读写和是否触发PL端DDR的读写操作,其部分代码如下所示
while (1){xil_printf(\"请输入触发的方式:\\n\");xil_printf(\"1:仅仅触发EMIO。\\n\");xil_printf(\"2:写DDR,并触发EMIO模式。\\n\");xil_printf(\"3:从两块地址读出数据模式。\\n\");xil_printf(\"4:从两块地址读出数据,并比较\\n\");xil_printf(\"请输入指令:1-4\\n\");scanf(\" %c\", &j);xil_printf(\"你输入的值是:%c\\n\",j);switch (j) {case \'1\':// 触发 EMIO 信号以启动 PL 端操作xil_printf(\"触发 EMIO 信号...\\n\");trigger_emio(&Gpio, EMIO_GPIO_PIN + 54);break;case \'2\':xil_printf(\"写DDR并触发EMIO模式 \\n\");xil_printf(\"向 DDR 地址 0x%X 写入 %d 个 32 位数据\\n\", DST_ADDR, DATA_SIZE);// 写入DDRfor (int i = 0; i < DATA_SIZE; i++) {Xil_Out32(DST_ADDR+i*4,i*2);xil_printf(\"addr = %X,data= 0x%X\\n\",DST_ADDR+i*4,i*2);}Xil_DCacheFlushRange(DST_ADDR, DATA_SIZE * sizeof(uint32_t)); // 刷新 Cache// 触发 EMIO 信号xil_printf(\"触发 EMIO 信号...\\n\");trigger_emio(&Gpio, EMIO_GPIO_PIN + 54);break;case \'3\':xil_printf(\"从两块地址读出数据模式 \\n\");Xil_DCacheInvalidateRange(SRC_ADDR, DATA_SIZE*4);Xil_DCacheInvalidateRange(DST_ADDR, DATA_SIZE*4);//读取0x10000000地址数据xil_printf(\"从 DDR 地址 0x%X 读取 %d 个 32 位数据\\n\", DST_ADDR, DATA_SIZE);for (int i = 0; i < DATA_SIZE; i++) {data_buffer0 = Xil_In32(DST_ADDR+i*4); // 读取 DDRxil_printf(\"addr = %X,data= %d\\n\",DST_ADDR+i*4,data_buffer0);}//读取0x11000000地址数据xil_printf(\"从 DDR 地址 0x%X 读取 %d 个 32 位数据\\n\", SRC_ADDR, DATA_SIZE);for (int i = 0; i < DATA_SIZE; i++) {data_buffer1 = Xil_In32(SRC_ADDR+i*4); // 读取 DDRxil_printf(\"addr = %X,data= %d\\n\",SRC_ADDR+i*4,data_buffer1);}break;case \'4\':xil_printf(\"从两块地址读出数据,并比较 \\n\");Xil_DCacheInvalidateRange(SRC_ADDR, DATA_SIZE*4);Xil_DCacheInvalidateRange(DST_ADDR, DATA_SIZE*4);xil_printf(\"从 DDR 地址 0x%X 读取 %d 个 32 位数据\\n\", DST_ADDR, DATA_SIZE);for (int i = 0; i < DATA_SIZE; i++) {data_buffer0 = Xil_In32(DST_ADDR+i*4); // 读取 DDRdata_buffer1 = Xil_In32(SRC_ADDR+i*4); // 读取 DDRif (data_buffer0 != data_buffer1)xil_printf(\"数据不匹配,索引 %d: 预期 0x%X, 实际 0x%X\\n\", i, data_buffer0, data_buffer1);elsexil_printf(\"数据匹配成功,索引 %d: 预期 0x%X, 实际 0x%X\\n\",i, data_buffer0, data_buffer1);}break;default:xil_printf(\"无效指令!\\n\");break;}xil_printf(\"\\n\");xil_printf(\"\\n\");xil_printf(\"\\n\"); }
整体代码虽然相对简单,但作为本次的演示已经完全足够,因为它涵盖了PS端的DDR读写、PL端的代码读写以及PS与PL之间的交互机制。这使得它在未来的项目中可以直接拿来进行简单的修改和适配。
四、功能演示
大部分功能演示在文章开头的功能展示部分已经展示过了,这里我将用一个视频的形式,给大家演示本设计的所以功能和介绍。
实现AXI总线协议读写:PL与PS交互访问DDR全流程
至此,我们的PL 与 PS 交互访问 DDR 全流程就彻底验证完成了!!
下一节讲解如何在实际工程中调用并使用这AXI两个模块!!,大家有想看实现什么功能的也可以发到评论区,我给大家更新!!!!
制作不易,记得三连哦,给我动力,持续更新!!!