ZYNQ 系列 SOC 的 AXI_EMC IP 核是一个可以支持各种内存型号的控制器,利用这个 IP 核可以非常方便地模拟各种类型的内存或者 FLASH 接口实现数据的交互和通信。数据交互可以发生在 ZYNQ 处理器和外部存储器之间,也可以用于 ZYNQ 处理器内部 PS 和 PL 之间的数据交互。本文主要介绍利用该 IP 核实现 PS 和 PL 的数据交互。
注:EMC, External Memory Communication,有时候也叫做 EMIF, External Memory Interface。
关于 AXI_EMC IP 核的功能特性可以参考 Xilinx 官方文档pg100,下面是列举的部分特性:
- 支持 AXI4 Slave Memory Map 接口,数据宽度为 32 位和 64 位
- 支持写入/读取寄存器的可选 AXI4-Lite Slave 数据宽度为 32 位
- 支持 AXI4 增量和包传输
- 支持 AXI4 窄带和非对齐传输
- 最多支持四个外部存储器组
- 支持具有可配置字节奇偶校验和流水线级的同步 SRAM
- 支持的内存类型:同步 SRAM、异步 SRAM、线性闪存(或并行 NOR 闪存)、PSRAM(或蜂窝 RAM)
- 提供配置寄存器,动态更改 PSRAM 和 Micron®闪存的访问机制
- 为同步 SRAM 存储器提供奇偶校验错误状态寄存器
本文以 AXI_EMC 实现异步 SRAM 读写为例,实现 PS 和 PL 之间的数据交互。
一、搭建工程及异步 SRAM 设置
过程略,下面是框图结构。
本文主要用到了 AXI_EMC 这个 IP 核,下面我们看下这个 IP 的设置,双击这个 IP。
3.1 AXI EMC 标签页
下图中的的位宽设置为 32bit,其他参数默认,其中 Base Address 和 High Address 是 AXI_EMC IP 在 ZYNQ 4GB 地址空间中的分配的地址。
3.2 Memory Bank 1 标签页
下图中,设置 Memory Type 为 Async SRAM,也就是异步 SRAM,这种 SRAM 读写起来比较方便。位宽设置为 32bit。Timing Parameters 是 AXI 系统时钟为 100M 的情况下的时间参数,如果采用其他时钟可能要修改时间参数,时间参数的含义可以在 pg100 文档中查看,读不懂的可以参考这篇文章:AXI EMC 使用总结。
异步 SRAM 读写相对比较简单,只用到了接口侧的 mem_a, mem_cen, meme_dq_i, mem_dq_o, mem_oen, mem_wen 等 6 种信号线。异步 SRAM 的读写波形可以在 pg100 文档中查看,下面分别是截取的读写时序图。
最后,Advanced Configuration 保持默认,Summary 页查看汇总信息。这样,就设置好 AXI_EMC IP 核了。
然后,回到 Block Deign 中,给 AXI_EMC IP 核分配地址控制,以下是 AXI_EMC IP 核在 ZYNQ 4GB 地址空间中的分配的地址:
二、设计 FPGA 代码
可以直接修改 BD 的顶层文件,也可以单独写成一个文件,在 BD 中调用。下面是直接修改 BD 的顶层文件:
module system_top( inout [14:0]DDR_addr, inout [2:0]DDR_ba, inout DDR_cas_n, inout DDR_ck_n, inout DDR_ck_p, inout DDR_cke, inout DDR_cs_n, inout [3:0]DDR_dm, inout [31:0]DDR_dq, inout [3:0]DDR_dqs_n, inout [3:0]DDR_dqs_p, inout DDR_odt, inout DDR_ras_n, inout DDR_reset_n, inout DDR_we_n, inout FIXED_IO_ddr_vrn, inout FIXED_IO_ddr_vrp, inout [53:0]FIXED_IO_mio, inout FIXED_IO_ps_clk, inout FIXED_IO_ps_porb, inout FIXED_IO_ps_srstb ); wire clk_100m; wire [31:0]mem_a; wire [0 :0]mem_cen; reg [31:0]mem_dq_i; wire [31:0]mem_dq_o; wire [0 :0]mem_oen; wire mem_wen; //************************************************************************* reg [31:0] data_reg1; reg [31:0] data_reg2; reg [31:0] data_reg3; reg [31:0] data_reg4; always @ (posedge clk_100m) if(mem_wen==1'b0)begin case(mem_a) 32'h6000_0000:begin data_reg1<=mem_dq_o; end 32'h6000_0004:begin data_reg2<=mem_dq_o; end 32'h6000_0008:begin data_reg3<=mem_dq_o; end 32'h6000_000C:begin data_reg4<=mem_dq_o; end default : begin end endcase end always @ (posedge clk_100m) if(mem_oen==1'b0)begin case(mem_a) 32'h6000_0000:begin mem_dq_i<=data_reg1; end 32'h6000_0004:begin mem_dq_i<=data_reg2; end 32'h6000_0008:begin mem_dq_i<=data_reg3; end 32'h6000_000C:begin mem_dq_i<=data_reg4; end default : begin end endcase end //************************************************************************* wire [131:0] probe0; ila_core ila_core_uut ( .clk(clk_100m), // input wire clk .probe0(probe0) // input wire [99:0] probe0 ); assign probe0[31:0]=mem_a; assign probe0[63:32]=mem_dq_o; assign probe0[95:64]=mem_dq_i; assign probe0[96]=mem_cen; assign probe0[97]=mem_oen; assign probe0[98]=mem_wen; system system_i( .DDR_addr(DDR_addr), .DDR_ba(DDR_ba), .DDR_cas_n(DDR_cas_n), .DDR_ck_n(DDR_ck_n), .DDR_ck_p(DDR_ck_p), .DDR_cke(DDR_cke), .DDR_cs_n(DDR_cs_n), .DDR_dm(DDR_dm), .DDR_dq(DDR_dq), .DDR_dqs_n(DDR_dqs_n), .DDR_dqs_p(DDR_dqs_p), .DDR_odt(DDR_odt), .DDR_ras_n(DDR_ras_n), .DDR_reset_n(DDR_reset_n), .DDR_we_n(DDR_we_n), .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn), .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp), .FIXED_IO_mio(FIXED_IO_mio), .FIXED_IO_ps_clk(FIXED_IO_ps_clk), .FIXED_IO_ps_porb(FIXED_IO_ps_porb), .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb), .clk_100m(clk_100m), .mem_a(mem_a), .mem_cen(mem_cen), .mem_dq_i(mem_dq_i), .mem_dq_o(mem_dq_o), .mem_oen(mem_oen), .mem_wen(mem_wen));
其中,ila_core 为例化的在线调试 IP 核。
接下来就是走流程,编译,生成 bitsream,导出硬件配置。
三、编写 SDK 代码
软件流程:代码中先写入 4 个数据,之后在读出来,这里需要注意,ZYNQ 是 32bit 的数据总线所以地址每次要增加 4。
/* * main.c * * Created on: 2022 年 7 月 25 日 * Author: Administrator */ #include <stdio.h> #include "sleep.h" u32 data[4]; u32 i; int main() { for(i=0;i<4;i++) { Xil_Out32(XPAR_EMC_0_S_AXI_MEM0_BASEADDR+i*4,i); } sleep(1); for(i=0;i<4;i++) { data[i]=Xil_In32(XPAR_EMC_0_S_AXI_MEM0_BASEADDR+i*4); } for(i=0;i<4;i++) xil_printf("read %d = %d\n\r", i,data[i]); return 0; }
四、测试结果
写波形
读波形
串口打印输出
扫码关注尚为网微信公众号
原创文章,作者:sunev,如若转载,请注明出处:https://www.sunev.cn/embedded/1252.html