PS端与PL端在硬件上是相互独立的,之前可以通过EMIO 和 AXI GPIO等方式 让ZYNQ PS 端的GPIO 口映射到PL端上,但是仅仅只能控制GPIO。 本文介绍一种新的方法,通过让PS端访问PL端寄存器的方式,来让ARM 和FPGA实现简单的数据交互(小数据量)
本文在 vivado2018.3版本上 演示, 其他版本请自行研究
一 创建一个带AXI 接口的IP
- 重新创建并封装一个带AXI 接口的IP 具体过程如下 ,TOOLS->Create and Package New IP
2.选择封装带AXI4总线的
3、next,填写名称等信息,注意IP保存路径。这里给IP取名 PS_PL_REG 即 PS和PL的REG互相访问
4、next,选择总线相关信息,这里AXI的 协议选择 LITE 轻型, 然后number of registers 根据需求选择,4即代表有四个32bit的寄存器 最小选择是4个
5.选择 下一步 EDIT IP 并点FINISH完成(这里一定要选择“Edit IP”,否则,软件只是生成了框架文件,而不会创建一个关于IP 核的工程)
6.之后系统会自动生成一个该IP的工程。 这里我们双击打开系统默认生成的IP代码
7.研究代码,可以看到 我们设置IP的时候 PL端的4个用于PS访问的寄存器分别是 slv_reg0,slv_reg1,slv_reg2,svl_reg3 ,这4个寄存器 通过AXI 接口,可供ARM PS端进行读和写的操作。
8.为了让交互的过程更为直观,我们在原有的IP代码中添加一个LED指示灯用来观察交互的结果
output reg LED;
并在代码中增加以下代码用来将灯和数据进行对应
always@(posedge S_AXI_ACLK)begin
if(slv_reg0==32'd0)LED<=0;
else if(slv_reg0<=32'd1)LED<=1;
end
代码解释 ,当slv_reg0等于32位的0时 LED 熄灭, 当slv_reg0等于32‘d1时,即十进制1时,LED点亮(slv_reg0后面可由ARM端的程序直接读写)
9.对系统生成的IP的上层模块,也相应的增加LED 接口,并与内部的模块相连接
10.编辑完后,保存修改,接下来是打包 修改完的IP(封装之前,先编译综合看看代码是否有报报错),回到Package IP页面设置IP,如下所示输入相关信息,也可以不修改
在file Groups项中点击Merge changs from File Groups Wizard
根据上面的操作,也将软件界面中没有打勾的 选项全部都merge了
当除了最后一项,所有的选项都勾上更新后, 在最后一栏里点重新打包封装IP,完成IP的打包
IP打包后,修改IP的工程会被系统自动关闭
二 在vivado工程中增加ZYNQ模块
1.增加ZYNQ 模块
1)在Block Design 中 增加ZYNQ模块,并且在设置界面 将DDR选项设置成 MT41K128M16JT 位宽选择16bit
2)选择 Run Block Automation 完成端口自动布线
3)手动连接 FCLK_CLK0到 M_AXI_GP0_ACLK
4)在block design 中添加我们刚才创建的PS_TO_PL_REG IP模块
5) 如下图所示 我们添加的PS_PL_REG模块已经添加到设计中,再次点选run connection automation尝试自动连线
如下图所示,系统为我们自动添加需要增加的模块,并自动连接好了各个模块
6) 因为我们的演示 是需要通过LED 来观察结果的,所以这里右键LED 将LED窗口印出来
这样我们的整个硬件系统就算布局完成了。
7) 在design Sources中 右键我们刚刚设计的ZYNQ模块,然后点选 Create HDL Wrapper
8)编译 , 为LED 分配管脚 ,综合
三.PS部分工程创建
1)File→Export→Export hardware…,在弹出的对话框中勾选“include bitstream”,点击“OK”确认,如下图所示。
2)File→Lauch SDK,在弹出的对话框中,保存默认,点击“OK”,如下图所示。
系统将自动打开SDK开发环境
系统将自动打开SDK开发环境
3)再创建一个LED 工程,负责LED 指示灯的闪烁
a. 创建一个新的空工程,可以取名叫PS_PL_REG_TEST
b.右键工程的SRC目录,然后新建一个SOURCE FILE
c.取名 main.c
四 PS端代码编写
1.点开platform中的 system.hdf 这里所有的资源都会显示在这里, 找到我们之前创建的PS_PL_REG ,可以看到 这个外设的基地址是 0X43C00000 ,后面我们会用到这个地址
2.在main .c中添加如下代码
#include "xparameters.h" #include "xil_io.h" #define LED_BASE_ADDR 0x43c00000 int main() { volatile int Delay; while(1) { Xil_Out32(LED_BASE_ADDR, 1); for (Delay = 0; Delay < 10000000; Delay++); Xil_Out32(LED_BASE_ADDR, 0); for (Delay = 0; Delay < 10000000; Delay++); } return 0; }
3.下载程序到板子上验证
a)选中工程中的硬件平台,并点击右键→Program FPGA,在弹出的对话框中选择默认,点击“program”,完成FPGA PL部分的Program工作
b)右键我们创建的工程,选择Run As→1 Launch on Hardware(System Debugger)
可以看到板子上的LED1灯在闪烁
如果想在DEBUG的时候自动 重载FPGA的bit ,则看按下列操作进行设置 右键工程–>RUN AS–>Run Configurations
勾上 program fpga 和 reset entire system 两个选项
经过上两步的设置,每次点绿色箭头DEBUG时,系统都会重置FPGA并且重新对PL部分进行Program
d)代码解读
#define LED_BASE_ADDR 0x43c00000
这里的0X43C00000就是我们之前看到的 模块地址
Xil_Out32(LED_BASE_ADDR, 1); 相当于向上面的模块地址写入1
Xil_Out32(LED_BASE_ADDR,0) ; 相当于向模块的基地址写入0
因为我们在PL端创建的模块中的4个寄存器,只有寄存器0对应了LED ,所以我们只要向基地址写入信息就可以控制LED 指示灯了。
如果要控制寄存器1,寄存器2 ,寄存器3 只需要在基地址的基础上+4就可以了 (+4是因为数据都是32bit的)
另外我们也可以反向用PS 去读取PL端的寄存器值,具体过程不展开描述了, 实际PS访问 可以通过指令Xil_In32(LED_BASE_ADDR); (LED_BASE_ADDR 为我们定义的基地址 )来进行读取。
通过上述方式 我们成功的实现了 PS和PL 通过寄存器方式的交互,这种方式相对比较简单 容易入手,当然交互的方式还有很多,后面 有时间再继续整理。
AXI 的通讯也是个大课题,有时间也好好整理一下
本文的完整工程如下: