PS端与PL端在硬件上是相互独立的,之前可以通过EMIO 和 AXI GPIO等方式 让ZYNQ PS 端的GPIO 口映射到PL端上,但是仅仅只能控制GPIO。 本文介绍一种新的方法,通过让PS端访问PL端寄存器的方式,来让ARM 和FPGA实现简单的数据交互(小数据量)
- 此章节内容适用于Lemon ZYNQ主板,如是其他板子请看对应板子目录
- 本文在 vivado2018.3版本上演示
一、工程创建
工程创建的过程可以参考试验一中的内容,这里不再详细描述了。基于Lemon ZYNQ的PS实验一 GPIO之用EMIO方式点亮LED(完整图文)(芯片型号选XC7Z020CLG400-1)
二、创建一个带AXI 接口的IP
1)创建并封装一个带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,如下所示输入相关信息,也可以不修改
11)在file Groups项中点击Merge changs from File Groups Wizard
12)根据上面的操作,也将软件界面中没有打勾的 选项全部都merge了
13)当除了最后一项,所有的选项都勾上更新后, 在最后一栏里点重新打包封装IP,完成IP的打包
IP打包后,修改IP的工程会被系统自动关闭
三、在vivado工程中增加ZYNQ模块
1)增加ZYNQ 模块
在Block Design 中 增加ZYNQ模块,并且在设置界面 将DDR选项设置成 MT41K256M16RE-125 位宽选择16bit
2)因为Lemon ZYNQ主板的PS时钟是50M的晶振输入的,所以这里需要把默认的PS输入时钟33.33M改成50M
3)选择 Run Block Automation 完成端口自动布线
4)手动连接 FCLK_CLK0到 M_AXI_GP0_ACLK
5)在block design 中添加我们刚才创建的PS_TO_PL_REG IP模块
6) 如下图所示 我们添加的PS_PL_REG模块已经添加到设计中,再次点选run connection automation尝试自动连线
如下图所示,系统为我们自动添加需要增加的模块,并自动连接好了各个模块
7) 因为我们的演示 是需要通过LED 来观察结果的,所以这里右键LED 将LED窗口印出来
这样我们的整个硬件系统就算布局完成了。
8) 在design Sources中 右键我们刚刚设计的ZYNQ模块,然后点选 Create HDL Wrapper
9)编译 RUN
10)为LED 分配管脚
11)综合并生成 bit文件
三、PS部分工程创建
1)File→Export→Export hardware…,在弹出的对话框中勾选“include bitstream”,点击“OK”确认,如下图所示。
2)File→Lauch SDK,在弹出的对话框中,保存默认,点击“OK”,如下图所示。
系统将自动打开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; }
五、下载与验证
1)新创建的工程最好先对Run as 进行配置:右键工程,并点选Run As -> Run Configurations
2) 在弹出的窗口中对Reset entire system 和 Program FPGA两个选项进行勾选操作,这样就不会出现下载程序debug的时候概率性不工作的问题了。(这样操作后系统会自动对FPGA进行配置,不需要按之前工程手动对FPGA进行编程了)
PS如果没有出现下图对话框,可以直接双击左侧的applicationo (System Debugger)。
3)设置好之后,选择Run As
-> Launch on Hardware (System Debugger)
进行调试。
可以看到板子上的LED1灯在闪烁
六、代码解读
#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 的通讯也是个大课题,有时间会继续整理相关内容。
- 本文的完整工程下载:13_PS_TO_PL_REG
- VIVADO的版本:2018.3
- 工程创建目录: E:\Lemon_ZYNQ\SDK\13_PS_TO_PL_REG