本实验教程的目标是展示如何使用Vivado来创建一个AXI-Lite Slave接口模板文件,然后修改模板文件以封装自己的简单加法器的RTL设计,最后通过PYNQ来调用该IP进行加法功能的测试。本教程将介绍:
- 如何使用Vivado的“Create and Package New IP”功能
- 如何理解AXI-Lite Slave接口模板文件、对其进行修改以封装自己的RTL设计
- 如何将AXI-Lite Slave的IP集成到硬件系统中
- 如何在PYNQ中对AXI-Lite Slave接口的IP进行读写
- PYNQ-Z2远程实验室服务或物理板卡
- Vivado
-
打开Vivado软件,打开任意一个项目,然后点击左上角工具栏中的tools,选择Create and Package New IP以开始创建新的IP核。
-
在新弹出的Create and Package New IP窗口中选择Next以继续
-
选择Create a new AXI4 peripheral的选项,并单击Next
-
在Name栏输入IP名称axilite_adder,在IP location栏选择自己的工作目录,然后点击Next
-
进入到Add Interfaces界面,进行AXI接口的选择与添加。按照如下界面,添加一个AXI Lite的Slave接口,Data Width设置为32,Number of Registers设置为4,然后点击Next继续。
-
选择Edit IP,点击Finish,这将打开一个新的Vivado开发界面。
-
在新弹出的edit_axilite_adder_v1_0界面中,双击打开axilite_adder_v1_0_S00_AXI_inst文件。在Design Source中的外层文件axilite_adder_v1_0.v是顶层封装,会根据上一步中添加的AXI接口类型实例化对应AXI Interface; axilite_adder_v1_0_S00_AXI_inst.v则包含具体的AXI协议握手内容,也是我们需要修改与关注的部分。
-
虽然该教程剩余部分具体介绍了需要对哪些代码进行修改,但如果读者是第一次接触该类文件,我们仍然建议读者在此花一些时间阅读下axilite_adder_v1_0_S00_AXI_inst.v文件的具体内容。
-
axilite_adder_v1_0_S00_AXI_inst.v的第219到269行包含了由PS向PL AXI Lite 写寄存器的过程,其中
slv_regN[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
语句实现了从S_AXI_WDATA上读取数据写入到寄存器slv_regN的过程。由于我们在上一步选择了生成4个寄存器,因此这里生成了slv_reg0-slv_reg3四个寄存器,在PS侧进行写入后结果将保存在这4个寄存器中。
always @( posedge S_AXI_ACLK ) begin if ( S_AXI_ARESETN == 1'b0 ) begin slv_reg0 <= 0; slv_reg1 <= 0; slv_reg2 <= 0; slv_reg3 <= 0; end else begin if (slv_reg_wren) begin case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 0 slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end 2'h1: for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 ) if ( S_AXI_WSTRB[byte_index] == 1 ) begin // Respective byte enables are asserted as per write strobes // Slave register 1 slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8]; end ...... default : begin slv_reg0 <= slv_reg0; slv_reg1 <= slv_reg1; slv_reg2 <= slv_reg2; slv_reg3 <= slv_reg3; end endcase end end end
-
第365到379行包含了由PS向PL AXI Lite读寄存器的过程,
reg_data_out <= slv_regN
语句实现了从寄存器读取数据到S_AXI_RDATA的功能,这一部分是我们经常需要修改的。assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid; always @(*) begin // Address decoding for reading registers case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] ) 2'h0 : reg_data_out <= slv_reg0; 2'h1 : reg_data_out <= slv_reg1; 2'h2 : reg_data_out <= slv_reg2; 2'h3 : reg_data_out <= slv_reg3; default : reg_data_out <= 0; endcase end
-
点击左上角Sources栏中的加号键,选择Add or create design sources并选择Next,在弹出界面中添加需要测试的文件dut.v并选择Finish。
-
添加的dut.v就是我们要测试的简单IP,其实现了一个最简单的组合逻辑加法器,输入和输出都是32位无符号整型:
module dut( input wire [31:0] input1, input wire [31:0] input2, output wire [31:0] sum ); assign sum = input1 + input2; endmodule
-
在axilite_adder_v1_0_S00_AXI_inst.v第116行左右添加sum信号用于连接dut的输出结果:
-
在axilite_adder_v1_0_S00_AXI_inst.v第377行左右修改赋值语句为
2'h3 : reg_data_out <= sum
;用于将dut模块的输出结果赋值给S_AXI_RDATA: -
在axilite_adder_v1_0_S00_AXI_inst.v第402行左右添加dut模块的实例化语句,输入为slv_reg0和slv_reg1,输出为sum:
-
点击最左侧列导航栏的Edit Packaged IP选项,弹出Package IP窗口:
-
在Package IP窗口中点击File Groups,再点击蓝色的Merge changes from File Groups Wizard选项
-
在Package IP窗口中点击Review and Package,再点击蓝色的IP has been modified选项,之后点击最下方的Re-Package IP选项。
-
提示完成Package操作,可以选择No先不关闭该窗口。
该部分内容与该教程第二部分“在Vivado中进行IP集成”完全一致,在此我们略去重复图片,读者如有疑问或对该部分GUI不熟悉,可参考原教程对应部分、有详细的逐步图片展示。
-
打开Vivado软件,点击Create Project,创建一个新的项目,点击Next
-
在Project name输入项目名axilite_adder_system,点击右侧的 ... 按键选择一个合适的目录位置,点击Next
-
进入Project Type界面,勾选上Do not specify sources at this time,再点击Next
-
进入Default Part界面,在Search栏中搜索xc7z020clg484-1,将其选中,再点击Next
-
点击Finish完成项目创建
-
我们需要首先将刚封装完的AXI-Lite IP导入到Vivado中,点击左侧窗口Flow Navigator中的Settings 选项,弹出Settings窗口
-
将左侧的Project Settings中展开IP栏目,选中Repository项,点击右侧面板中的 + 按键,在弹出窗口中选择我们刚才封装的IP axilite_adder,再点击Select
-
可以看到对应的IP已经被成功添加到了工程中,在两个窗口中依次单击OK来关闭这些窗口
-
下面我们创建一个Block Design,利用Vivado的IP集成功能来构建完整系统。在左侧的Flow Navigator中点击IP INTEGRATOR > Create Block Design,在弹出的Create Block Design 窗口中保持各选项不变,设计名称使用默认的design_1,点击OK创建Block Design
-
在出现的Diagram窗口中点击上方的 + 按钮,会弹出一个搜索框,在输入栏中键入zynq,双击备选项中出现的ZYNQ7 Processing System,即可将该IP添加到设计中
-
在窗口上方会出现蓝色下划线提示Run Block Automation, 单击该区域弹出对应窗口,我们保持默认设置不变,直接点击OK
-
点击Diagam窗口上方的 + 按钮,搜索axilite_adder,可以看到我们刚才导入的IP已经可以使用了,双击axilite_adder以将其添加到设计中
-
下面我们对设计进行自动连线。点击窗口上方的蓝色下划线提示Run Connection Automation,弹出对应窗口,将左侧All Automation 选项勾选上,再点击OK
-
系统将根据对应接口自动进行连线,我们可以得到如下图的设计
-
在Diagram上侧的工具栏中点击勾形图标Validate Design,对设计进行验证
-
在左侧的Source > Design Sources > design_1选项上右键,选择Generate Output Products
-
在弹出窗口中保持各配置不变,点击Generate,这一过程将耗费约1分钟的时间
-
在左侧的Source > Design Sources > design_1选项上右键,选择Create HDL Wrapper,在弹出窗口中保持选项不变并点击OK,完成后可以看到在design_1.bd上层嵌套了一层design_1_wrapper.v文件
-
在左侧的Flow Navigator中选择Run Synthesis,在弹出窗口中保持选择不变并选择OK
-
综合完成后,会弹出Synthesis Completed窗口,在Next栏中保持默认的Run Implementation选项,并点击OK,如果出现新弹窗,同样保持默认选项并点击OK即可
-
Implementation结束后,会弹出Implementation Completed窗口,在Next栏中选择Generate Bitstream选项,并点击OK,如果出现新弹窗,同样保持默认选项并点击OK即可
-
比特流生成后,会弹出Bitstream Genreation Completed窗口,我们直接点击Cancel即可
-
至此,我们已经完成了硬件部分的设计与导出
- 在连接完上述设计后,我们可以点开Diagram窗口旁边的Address Editor窗口来查看Vivado为我们的IP分配的地址,可以看到Offset为0x43C0_0000,如果我们后续在PYNQ中使用MMIO进行寄存器读写的话就会需要知道这些值。
-
在文件管理器中访问 \axilite_adder_system\axilite_adder_system.runs\impl_1 目录,该目录下的design_1_wrapper.bit文件即为生成的比特流文件,将其复制到自己的文件夹中保存,并重命名为adder.bit
-
在文件管理器中访问 \axilite_adder_system\axilite_adder_system.gen\sources_1\bd\design_1\hw_handoff 目录,其中的design_1.hwh即为我们需要的hardware handoff文件,将其复制到自己的文件夹中保存,并重命名为adder.hwh
-
请先完成PYNQ远程实验室的账号注册与Jupyter访问
-
登录Jupyter界面,点击界面右上方的upload按钮,将以下文件上传到开发板上
-
/jupyter 目录下的adder.ipynb
-
上一步中得到的adder.bit与adder.hwh文件
- 如果你在前面操作中导出失败了,你也可以先使用 /overlay 目录下的adder.bit与adder.hwh文件上传,以完成余下实验
-
-
在Jupyter中进入到adder.ipynb页面,Kernel自动加载完成显示为Python3字样
-
点击窗口上侧的Run按钮,Jupyter Notebook会执行当前Cell,同时自动切换到下一个Cell
-
完成按照顺序依次点击Run至结束即可,各代码块的含义在Jupyter Notebook中已经标注,请阅读Jupyter Notebook中的信息继续完成实验。