本文档中的示例实验的系统设计框图如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2FaM6NWy-1582858270651)(media/aab71e0ee5f6d827823f26628900ce6d.png)]
ZYNQ芯片的PL部分也就是FPGA部分,定义了一个用户逻辑的IP,实现将两个输入的32bit的数据相加。自定义的用户逻辑IP中设计了4个寄存器,其中3个配置寄存器(可读、可写)和一个状态寄存器(只读)。
ARM处理器通过写配置寄存器slv_reg0和slv_reg1分别写入两个输入数据,用户逻辑做加法运算,计算相加的结果放入slv_reg3寄存器中。slv_reg3寄存器作为自定义IP的状态寄存器使用,不能写,只能读。ARM处理器读取slv_reg3寄存器中的数据,并将结果显示在串口调试工具窗口中。
Xilinx官方提供了许多的IP核,在Vivado中我们通过IP
Catalog可以管理、添加和查看这些IP核。然后用户在构建自己的系统时,有时候需要使用自己的用户IP核。创建自定义IP核将使系统设计层次结构和模块化结构更加的清晰;增加功能模块的设计复用性,简化系统设计和缩短设计时间;可以在IP核中加入license有偿提供给别人使用。
在ZYNQ嵌入式开发中最常用的就是使用AXI总线将PS同PL
Fabric的IP核连接起来。本实验将为大家介绍如何在Vivado中创建一个AXI总线的自定义IP,并且创建该IP的驱动函数库,并在SDK应用程序中读写该自定义IP中的寄存器。
ALINX AX7020的使用的是Xilinx 公司的Zynq7000系列的SOC芯片。
SOC芯片的型号为XC7Z020-2CLG400I。
芯片上的PS系统集成了两个ARM Cortex-A9处理器。
FPGA硬件工程师需要在工程中添加和配置好PS端ARM内核,以及PS端的外设。
步骤如下:
新建一个Vivado工程,工程名“custom_ip”。
创建一个Block设计,添加一个ZYNQ7 Processing
System的IP核。根据开发板原理图和用户手册配置参数。
通过阅读原理图可以得知外设IO的MIO的BANK0的电平为3.3V,BANK1的电平为1.8V,因此将BANK0配置为LVCOMS3.3V,BANK1配置为LVCOMS1.8V。
串口的TX和RX分别连接到MIO48-MIO49,因此配置为UART1(MIO48-MIO49)。
QSPI FLASH的芯片型号为W25Q256,配置QSPI FLASH,选择Single SS 4bit IO。
开发板上有两个4Gbis的DDR3 SDRAM,配置为兼容“MT41J128M16
RE-125”,总线位宽为“32bit”。
千兆以太网的引脚接口如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYIrGCL7-1582858270654)(media/ccf7a897d8ca92aac9de379d49a72cc5.png)]
因此配置以太网接口为Ethernet 0
(MIO16-MIO27),此外还需要配置MDIO为以太网的配置寄存器的接口(MIO52-MIO53)。
SD卡槽的连接如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xj3nPFSc-1582858270660)(media/ad3e7dd294da586ea46d1abc30f57bc1.png)]
因此配置SD卡槽的配置为:SD 0 (MIO40-MIO45)。
PS端的输入时钟配置为33.333333MHz,CPU时钟配置为767MHz。
处理器IP的配置基本完成了。
创建自定义IP的步骤如下:
点击菜单栏“Tool”Create and Package New IP,弹出创建自定义IP的向导(Create
and Package New IP Wizard)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjpQkYZn-1582858270662)(media/df42d107ce191cfa246b65d544756395.png)]
点击Next,在任务类型选择时,选择创建一个新的AXI4外设。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bSJC1rmy-1582858270665)(media/097139309896a2e1159b46d9a0f32ae3.png)]
点击Next,在Peripheral
Detail的配置窗口中,设置设备IP的名称为my_app_0,点击Next。
在设备IP的AXI接口配置窗口对AXI接口进行配置,接口类型保持默认的AXI
Lite接口,接口模式为Slave模式,数据位宽为32bits,寄存器数量设置为4个。点击Finish,完成创建IP。
在Vivado的IP Catalog中可以查看刚才创建的自定义IP。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehYy6WQA-1582858270666)(media/00f7e469e3fbe1635531a72109c398b9.png)]
此时的自定义IP只有简单的寄存器读写功能,我们需要在其中添加用户逻辑。右键点击该IP,选择Edit
in IP Packager。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6wGKhUXq-1582858270669)(media/1ae8bd554f9722261714aa1c5fc06457.png)]
弹出对话框要求输入工程的名称和路径,这里保持默认。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EU5o1XRu-1582858270672)(media/99b873f1b189481dd2d525c7dbf73e26.png)]
点击OK,Vivado打开了一个新的工程,编辑IP。
添加用户逻辑代码。在目录custom_ip/下新建一个my_app0.v的源代码文件,内容如下:
`timescale 1ns / 1ps
//
module my_app0
#(
// register bit width
parameter N = 32
)
(
input clk,
input[N - 1:0] input_reg0,
input[N - 1:0] input_reg1,
input[N - 1:0] input_reg2,
output[N - 1:0] output_reg0
);
reg[N - 1:0] input_reg_reg0;
reg[N - 1:0] input_reg_reg1;
reg[N - 1:0] output_reg_reg0;
always@(posedge clk)
begin
input_reg_reg0 <= input_reg0;
input_reg_reg1 <= input_reg1;
end
always@(posedge clk)
begin
output_reg_reg0 <= input_reg_reg0 + input_reg_reg1;
end
assign output_reg0 = output_reg_reg0;
endmodule
这个用户逻辑的源码还是非常简单易懂的,就是将两个输入input_reg0和input_reg1分别放入input_reg_reg0和input_reg_reg1。并将它们的值相加,放入output_reg0中输入。
点击Add Sources,打开添加源码向导,选择Add or create design sourcesAdd
File,添加用户逻辑的源码文件,确保添加文件时选择Copy Sources into IP
Directory。添加的源文件会复制到IP目录中,之后在IP编辑窗口中,对用户逻辑文件的所有的修改都是对IP目录中的用户逻辑文件,而与之前的custom_ip/my_app0.v无关,且不会同步。注意这里并没有使用Create
File创建源代码文件的方式,这是因为软件源代码文件时会要求指定源代码的路径,而不是使用IP自动保存源码的路径,这可能造成后面源码修改的同步问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2hgG4UT3-1582858270675)(media/48deda5ed182536373c9afa610979d6b.png)]
通过源码管理器窗口中可以查看文件的属性。可以看到IP的源码位置在ip_repo/myapp_0_1.0/src。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rI8Zk3Ht-1582858270678)(media/89f608f73c0e1181424e2480b7fa60a2.png)]
自定义IP的这个工程,除了之前添加的用户逻辑源码文件以外,还有两个文件,其中一个为myapp_0_v1_0的IP顶层文件。在顶层文件中实现了一个“myapp_0_v1_0_S00_AXI_inst”模块的例化。其对应的源代码myapp_0_v1_0_S00_AXI.v文件中实现了AXI4-Lite
Slave接口的时序逻辑。
我们需要修改该myapp_0_v1_0_S00_AXI.v文件,在其中实现用户逻辑模块的例化,并实现用户逻辑寄存器与IP配置和状态读取寄存器的绑定。
在myapp_0_v1_0_S00_AXI.v文件的最下方,注释“Add user logic
here”的下方,添加如下的代码。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Xnd04IF-1582858270681)(media/98f3b7f7469a30bbb0282f6c5c0c7bd7.png)]
这里实现了用户逻辑模块的例化,其中三个输入端口分别连接到配置寄存器slv_reg0,slv_reg1和slv_reg2。输出引脚不能直接连接到slv_reg3,所以我们先添加了一个wire类型的变量,其位宽与slv_reg3的数据位宽相同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2cCshhB2-1582858270683)(media/eee06e129486377282a8afa89b73008d.png)]
修改如下的写寄存器的代码。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQUEPerQ-1582858270685)(media/0948822ccbdb67c6d89ac40d51e6706e.png)]
其中case语句中的写地址axi_awaddr为2’h0,2’h1和2’h2的情况下,分别写slv_reg0,slv_reg1和slv_reg2寄存器。我们要实现的是slv_reg0,slv_reg1寄存器保存并可设置输入的值,而slv_reg3寄存器保存slv_reg0和slv_reg1两个值相加的结果,因此我们将slv_reg0,slv_reg1和slv_reg2寄存器作为可读写的配置寄存器,而slv_reg3作为只读的状态寄存器。所以这里我们不允许写slv_reg3寄存器,因此删除如下的代码。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9EHHsJ2s-1582858270688)(media/48e4df97df904e097a90b5a8fd2f39ba.png)]
这一段也删除掉,因为不管地址是什么都不应该写slv_reg3寄存器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-agodUQNd-1582858270690)(media/a6b2755ed2e525bd2229641dbcebf5f0.png)]
然后在if(slv_reg_wren)语句包括的begin和end所有逻辑的后面,添加下面的语句。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ATTZZz0-1582858270693)(media/f301134204e73467dd7d10d86f084162.png)]
这段代码的意思:除了复位和AXI写寄存器以外的其它情况(如AXI读操作,非读、非写时,等情况)进行寄存器slv_reg3内数据的同步,将my_app0用户逻辑的输出(即两个配置寄存器内的值相加的结果)同步到slv_reg3寄存器。
保存文件。
双击Source窗口的IP-XACT下的component.xml文件。此时在Packaging Steps栏的File
Group选项卡中弹出“Merge changers from File Groups Wizard”。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6snEVXkL-1582858270694)(media/18870b4aa4d679f7f75fe9f8aff65194.emf)]
点击“Merge changers from File Groups Wizard”,更新合并修改的源代码。
点击“Review and Package”栏的“Re-Package IP”,完成IP的修改。
Vivado自动关闭了IP的工程,此时可以在custom_ip的Block
Design的Diagram设计窗口中点击添加自定义的IP,并点击“Run Connection
Automation”进行自动接线。设计框图如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l8GySmwq-1582858270697)(media/7739752a0fb006d3eb2f2e20d5fa1d90.png)]
保存设计,右键点击Source窗口下.db文件,选择Generate Output
Product,生成设计产品文件。
点击Generate Bitstream,运行综合和执行,并生成Bitstream文件。
点击菜单”File””Export””Export
Hardware”,导出硬件信息,在导出硬件信息时需要选上“Include Bitstream”。
点击菜单”File””Launch SDK”打开SDK。
下面进入到软件工程的开发。
在Vivado中运行Launch
SDK,自动打开SDK软件,可以看到一个design_1_wrapper_hw_platform_0的硬件信息。
展开可以看到里面的目录和文件的结构如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LlbG2jrc-1582858270698)(media/f0cec85caaa8b4830e9ba954c55ccd13.png)]
双击system.hdf文件,可以看到里面包含了硬件的总体信息,如目标FPGA芯片的型号,地址映射,以及IP模块的组成等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kN8iEfUU-1582858270700)(media/99a04a7fad63eeb986ede3f257a9882a.png)]
在dirvers目录下的myapp_0_v1_0目录中包含了自定义IP的驱动程序。双击myapp_0.h打开,里面的内容如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hpcaI9vD-1582858270701)(media/335668d01a3fa27d30bdc44cccadde26.png)]
主要定义了每个IP内部寄存器的偏移地址,也就是之前FPGA工程中定义的slv_reg0,slv_reg1,slv_reg2,slv_reg3,slv_reg4寄存器的偏移地址。
此外还定义了读写寄存器的宏定义函数,分别为:
MYAPP_0_mWriteReg和MYAPP_0_mReadReg。
最后还有一个自测试的函数:MYAPP_0_Reg_SelfTest。
双击打开myapp_0.c,可以看到里面只有一条包含头文件的代码。用户可以在这里定义自定义的驱动函数。因为示例中我们没有用到复杂的驱动函数,所有只使用头文件中定义的宏定义来实现寄存器的读写就可以了。
点击菜单FileNewApplication Project,新建一个ARM上的应用软件工程。
工程名称为“custom_ip_test”,模板为“Hello World”模板。
修改helloworld.c文件的代码如下。
#include
#include “platform.h”
#include “xil_printf.h”
#include “myapp_0.h”
#include “xparameters.h”
#include “xil_io.h”
int main()
{
u32 regValue;
init_platform();
print(“Hello World\n\r”);
MYAPP_0_mWriteReg(XPAR_MYAPP_0_0_S00_AXI_BASEADDR,MYAPP_0_S00_AXI_SLV_REG0_OFFSET,2);
MYAPP_0_mWriteReg(XPAR_MYAPP_0_0_S00_AXI_BASEADDR,MYAPP_0_S00_AXI_SLV_REG1_OFFSET,5);
regValue =
MYAPP_0_mReadReg(XPAR_MYAPP_0_0_S00_AXI_BASEADDR,MYAPP_0_S00_AXI_SLV_REG3_OFFSET);
printf(“result is %u \n\r”, (unsigned int)regValue);
cleanup_platform();
return 0;
}
其中第16行和第17行代码调用了自定义IP的写寄存器宏定义,分别在slv_reg0和slv_reg1中写入数值2和5。第19行代码调用自定义IP的读寄存器宏定义,读取slv_reg3中的数值,并放入regValue变量中。第20行代码,打印slv_reg0
打开PuTTY串口调试工具,设置串口号和波特率(笔者为:COM3、115200),打开串口监视端口。
右键点击custom_ip_test工程选择Run As Run
Configuration。弹出运行配置对话框,双击Xilinx C/C++ application (System
Debugger)新建一个运行配置,并选上Reset entire system和Program
FPGA选项框。点击运行。
串口调试窗口的显示结果如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qtt3fJTq-1582858270704)(media/3399e2ddae252ee4f9e82edd86886997.png)]
添加如下两行代码:
inputValue1 =
MYAPP_0_mReadReg(XPAR_MYAPP_0_0_S00_AXI_BASEADDR,MYAPP_0_S00_AXI_SLV_REG0_OFFSET);
inputValue2 =
MYAPP_0_mReadReg(XPAR_MYAPP_0_0_S00_AXI_BASEADDR,MYAPP_0_S00_AXI_SLV_REG1_OFFSET);
修改printf语句为:
printf("%u + %u = ?, result is %u \n\r",(unsigned
int)inputValue1,(unsigned int)inputValue2, (unsigned int)regValue);
重新运行代码。得到如下的结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L1fQylDi-1582858270707)(media/47c488e62ab7508314970af246d556c5.png)]
ReadReg(XPAR_MYAPP_0_0_S00_AXI_BASEADDR,MYAPP_0_S00_AXI_SLV_REG1_OFFSET);*
修改printf语句为:
printf("%u + %u = ?, result is %u \n\r",(unsigned
int)inputValue1,(unsigned int)inputValue2, (unsigned int)regValue);
重新运行代码。得到如下的结果。
[外链图片转存中…(img-L1fQylDi-1582858270707)]
实验结束。
声明:笔者通常是将word转成.md文件,但是在csdn导入时却每次不能导入图片,不是故意没图片的。可直接去下载笔者上传的word文档,另附上工程源码一份。如果暂时下载不了可能是正在审核中。
源码链接:https://download.csdn.net/download/weixin_43354598/12197619