本文构建一个AXI4-Lite Master IP来实现PL读写DDR3。最后用sdk 程序验证pl 读写过程中写入的数据。
本文参考 https://www.eefocus.com/antaur/blog/17-08/423773_0818c.html 学习而来,但我添加了数据验证部分。
我的另篇博文是:zynq 7000 自定义IP 实验https://blog.csdn.net/leon_zeng0/article/details/78674832
对于理解本文也有很多帮助。那篇是 构建一个AXI4-Lite Slave IP,本文是AXI4-Lite Master IP。
对于代码的解说,请见:PL读写DDR3 实现PS和PL间的数据交互 代码分析
这里只是简单介绍一下,详细的设置,可以查看我的上文:自定义IP 实验。
打开Vivado, 点击新建工程,工程取名 RWddr。
一路Next , 在硬件设置界面设置好你所用的硬件。
点击Create Block Design,取名system。
在原理图里右键添加ip , 添加ZYNQ Processing System. 添加完成后点击 Run Block Automation。
然后 点击图标, recustom ip, 设置处理器。首先设置好DDR,设置信息监控端口, UART1,
时钟设置比较重要,选择Clock Configuration,点开 IO Peripheral Clocks, PL Fabric Clocks, 检查FCLK_CLK0 是否已勾上,并且频率设置为100 MHZ。
PS-PL Configuration 这里是本文与上文自定义IP 实验的差异。上面红箭是上文中选择了的,使用 AXI4-Lite Slave IP 就要勾上一个。
下面的红箭则是因为我们要用到AXI4-Lite Master IP
菜单选择 Tools -> Create and Package IP... 。这里前面操作与上文基本一致。
这里说一下不同点:就是在下面这个界面里,Interface Mode不是选择 Slave, 而是Master
在上文中,我介绍的是直接Edit IP, 这里介绍另个方法, 先 Add IP, 然后再编辑ip,在Add IP 后,选择左边Flow Navigator的 IP Catalog, 在右边原理图这边, IP Catalog , 选择User Repository, 可以看到我们加的 IP, 图中名字为 AzIP_AXI_Master-v1.0,在这个ip 上右键,选择Edit in Packager
我们先来看看主界面。在 Design Source 下面有2个.v 文件。上面的AzIP_AXI_Master_v1_0.v 是顶层文件, top level,但他只是实现接口界面,并调用实例函数。所以真正实现是在 下面的文件, AzIP_AXI_Master_v1_0_M00_AXI.v, 当然这个文件主要是实现AXI接口。重要的逻辑实现需要我们另外添加自己的函数。但在这个例子,我们把这部分最简单化,只是看看他的代码,主要是学习他的读写流程。
我们再查看这个ip 的逻辑连线。这个操作是左边FLOW->RTL ANALYSIS->Schematic 得到。看这个图可以帮助我们了解输入输出的信息。总共有157 个io
如果要看代码的中文讲解,可以查看 https://www.eefocus.com/antaur/blog/17-08/423751_6cc0d.html
这里不做过多介绍了。
除了读写的基本逻辑线外,程序里多了INIT_AXI_TXT, TXT_DONE, ERROR, 这3根应该算测试线吧。
本以为只有读写逻辑模块的,可能master 的特别性, 包含了一个测试例子。INIT_AXI_TXT发起,TXT_DONE结束, ERROR作为错误指示。
例子里的逻辑是写入DDR, 读取 DDR, 比较2个数据是否一致,不一致就ERROR指示,这样循环4个地址。
基地址由参数 parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000 这个确定。偏置地址是0,4,8,12
如果要做出自己的ip ,估计要整理这个例子。也不是那么容易,这里就不动了。
ip add 选择添加刚才的工程, 添加完后点击 自动连线,然后 在上面3个端口 Make External, 用Validate Design(F6)检查原理图错误, 用原理图上方图标Regenerate Layout整理图形,最后的原理图如下:
Generate Output Products, Create HDL Wrapper, 然后添加约束文件,内容如下:
这个文件的端口号要与你硬件对应, m00_axi_error_0,m00_axi_txn_done_0 是输出,比如led, m00_axi_init_axi_txn_0 则是输入,比如按钮。
set_property IOSTANDARD LVCMOS33 [get_ports {m00_axi_error_0}]
set_property IOSTANDARD LVCMOS33 [get_ports {m00_axi_txn_done_0}]
set_property PACKAGE_PIN G15 [get_ports {m00_axi_error_0}]
set_property PACKAGE_PIN K16 [get_ports {m00_axi_txn_done_0}]
set_property IOSTANDARD LVCMOS33 [get_ports m00_axi_init_axi_txn_0]
set_property PACKAGE_PIN T19 [get_ports m00_axi_init_axi_txn_0]
产生 比特流, Export Hardware, launch SDK,
建立一个 hello world 工程,Program fpga, debug 或 Run as
可以看到 helloworld ,但这不是重要的。按按钮,激活axi_init_axi_txn, 然后观察报错,和完成指示灯。
我在读写完后done 亮, 错误不亮。这是最后结果的情况,开始没怎么注意。
在这里我们要注意一个对应关系,操作的ddr 地址是 C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000开始的4个4字节。这里对吗?
如果写的范围不是有效的ddr 范围,估计会报错吧, 还有要避免写代码区和栈区。
在上面这个简单helloworld 的程序,感觉没什么内容,我把以前的ddr 读写代码放这里,再来看看ddr 的ps 读写过程。
#include
#include "platform.h"
#include "xil_printf.h"
#define MAXLENTH 512000000
unsigned char lasercmd[MAXLENTH];
//int ip_data;
void test_ram(void)
{
int i,j;
int* ip=(int*)lasercmd;
printf("ip=%x\n",(int)ip);
for(i=0;i<512000000/4;i++)
{
ip[i]=i*2;
}
for(i=0;i<51;i++)
{
printf("%d=%d\n",i,ip[i*1000000]);
}
}
int main()
{
init_platform();
print("Hello World\n\r");
test_ram();
cleanup_platform();
return 0;
}
程序运行的结果是:
Hello World
ip=114320
0=0
1=2000000
2=4000000
这个程序与我们实验有什么相关呢?ip=114320 (hex)
我们开始设置的地址根本就不在ddr 区,回到vivado 再重新设置
C_M_TARGET_SLAVE_BASE_ADDR = 32'h00400000
还有起始数据 CM00 AXI START DATA VALUE = 0xA1002200
设置的界面如下,具体数据,你也可以不一样,但要保证读取范围在DDR里:.
现在把测试代码修改一下,读取pl-ip 读写的区间,我在这里设置了断点的。显示完pl 读写区间,赋初值,断点,按一下按钮,显示pl 读写区间。
void test_ram(void)
{
int i=0;
int* ip=(int*)lasercmd;
int* obj=(int*)0x400000;
printf("ip=%x,obj=%x\n",(int)ip,(int)obj);
printf("pl ddr=%x, %x %x %x\n",obj[0],obj[1],obj[2],obj[3]);
for(i=0;i<512000000/4;i++)
{
ip[i]=i*2;
}
printf("2pl ddr=%x, %x %x %x\n",obj[0],obj[1],obj[2],obj[3]);
for(i=0;i<512000000/4;i++)
{
if(ip[i]!=i*2)printf("i=%x, ip=%x\n",i,ip[i]);
}
for(i=0;i<51;i++)
{
printf("%d=%x\n",i,ip[i*1000000]);
}
}
程序运行的显示结果部分如下:
ip=114320,obj=400000
pl ddr=175e70, 175e72 175e74 175e76
2pl ddr=a1002200, a1002201 a1002202 a1002203
i=baf38, ip=a1002200
i=baf39, ip=a1002201
i=baf3a, ip=a1002202
i=baf3b, ip=a1002203
0=0
1=1e8480
主程序里,对这个程序循环了几次,方便测试。可以看到
2pl ddr=a1002200, a1002201 a1002202 a1002203
这个是pl 写入的数据的四个数据。
开始好像读取不到,所以我有段查找的代码,看这段空间里哪个数据与ps 写入的不一致,这里找到4个数据。
i=baf38, ip=a1002200
i=baf39, ip=a1002201
i=baf3a, ip=a1002202
i=baf3b, ip=a1002203
我们进行一下计算 (hex 运算):baf38*4=2ebce0
2ebce0+114320=400000
我觉得看灯,好像不能说明问题,既然是读写,至少看看写入的数据是不是预期的。