EBAZ4205 第九个工程 ZYNQ端PS 访问 PL端的reg 寄存器,实现PS与PL数据交互

PS端与PL端在硬件上是相互独立的,之前可以通过EMIO 和 AXI GPIO等方式 让ZYNQ PS 端的GPIO 口映射到PL端上,但是仅仅只能控制GPIO。 本文介绍一种新的方法,通过让PS端访问PL端寄存器的方式,来让ARM 和FPGA实现简单的数据交互(小数据量)

一 创建一个带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,如下所示输入相关信息,也可以不修改

在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 分配管脚 ,综合

编译 RUN
RTL ANALYSIS->SCHEMATIC->I/O Ports
为LED 分配管脚, H18是 扩展板上的LED 管脚
综合并生成 bit文件

三.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灯在闪烁

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 的通讯也是个大课题,有时间也好好整理一下

本文的完整工程如下:

 

“EBAZ4205 第九个工程 ZYNQ端PS 访问 PL端的reg 寄存器,实现PS与PL数据交互”的5个回复

  1. 有个问题请教您:

    1、上面这个工程只是用PS部分跑了个裸程序,并不是基于linux系统的工程吧?
    2、vivado里面新建的PS工程,与网上说的搭建PS部分的linux开发环境,区别在哪里?

    1. 是的 ,上面这个工程是用来实现 PS部分和PL部分,数据共享用的,程序确实是裸奔的,没有跑任何操作系统 我对linux 这块不是特别熟, 不过据我所知 创建linux开发环境 好像需要在 linux下进行kernal的编译, 当然 如果用现成的 linux 镜像直接跑应该也是没问题的, vivado创建的PS 工程比较底层, linux 的工程比较上层,也就是你在zynq上跑linux ,剩下的程序就都相当于是在linux下开发驱动和APP了

  2. always@(posedge S_AXI_ACLK)begin
    if(slv_reg0==32’d0)LED<=0;
    else if(slv_reg0<=32'd1)LED<=1;
    end
    是不是拼写错误?错误在IP中如何调试发现?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注