前面已经详细介绍了AXI4和AXI4-Lite协议,光说不练假把式,要用起来才能知道理解对不对,今天就用AXI4-Lite协议来读写BRAM,看一下协议的读写过程与时序关系。
一. 建立工程
使用Vivado 2018.3,Create Project -> 一路next -> 选择芯片型号xc7z020clg484-2 -> next -> finish,等待工程创建完成。
二. 创建块设计
Flow/Flow Navigator -> Create Block Design -> Design name: bram -> OK
2.1 搭建原理图
2.1.1 添加BRAM并设置
在Diagram中,点击加号+来添加IP,在搜索框输入block,双击选择Block Memory Generator 。
双击Block Menory Generator块设置此IP,在Other Options选项卡最下方取消Enable Safety Circuit的勾选,OK,如下图:发现rsta_busy信号消失了,此信号是用来指示BRAM的工作状态的,我们这个试验不需要关注它。
2.1.2 添加BRAM控制器并设置
添加AXI BRAM Controller,如下图:
双击此IP块进行设置,将AXI Protocol设置为AXILITE,Number of BRAM interfaces设置为1,OK,如下图:
2.1.3 连接IP,引出端口
连线并Make External,如下图所示:
2.1.4 映射存储地址 Assign Address
在Address Editor窗口点击Auto Assign Address按钮,如下图:
2.1.5 生成输出产品 Generate Output Products
在Sources窗口,选中bram后右击选择Generate Output Products,如下图:
2.1.6 添加AXI SmartConnect并设置
添加AXI SmartConnect,将Number of Slave Interfaces设为1,如下图:
2.1.7 连线并验证原理图
全部设置完后连线,最终的原理框图如下图所示:
再映射一遍地址,按F6来验证设计(Validate Design),无报错,按Ctrl+S保存设计,此时一个AXI4-Lite的从机就搭建完成了。后续我们需要对它进行封装以便于测试程序的调用。
2.3 封装原理图
封装需要进行两个步骤:
第一步:生成输出文件 Generate Output Products
在Sources选项卡下,选择bram后右击选择Generate Output Products
综合选项Synthesis Options选择Out of context per IP,点击Generate,等待生成输出文件完成。
第二步: 创建HDL封装
在Sources选项卡下,选择bram后右击选择Create HDL Wrapper。
选择Let Vivado manage wrapper and auto-update,让Vivado管理封装并自动更新,OK,等待封装完成。
细心的朋友应该发现了,我们生成输出产品操作了两次,原因是如果先直接把三个IP添加进去连线的话,生成了M_AXI端口协议是AXI4而非AXI4-Lite,所以我们先将添加了BARM和BRAM控制器生成了输出,来确保M_AXI端口是AXI4-LITE协议,然后再添加AXI SmartConnect,最终封装。不添加AXI SmartConnect时从机不能输出rwready,即无法对BRAM进行写入。
三. 编写testbench
在Sources选项卡中选中sim_1 -> 点击+ -> 选择Add or Create Simulation Sources -> Create File -> File type选择SystemVerilog,File name为bram_tb,OK -> finish -> Yes。
完成后,会在sim_1下生成vivado的仿真文件bram_tb.sv,此文件只是一个框架,内容需要自行编辑。
testbench代码如下:
/*
* @Author: Xu XiaoKang
* @Email: xuxiaokang_up@qq.com
* @Date: 2020-08-01 17:24:27
* @LastEditors: xu XiaoKang
* @LastEditTime: 2020-08-03 10:52:08
* @Filename:
* @Description: bram AXI Master仿真
*/
module bram_tb ();
timeunit 1ns;
timeprecision 1ps;
logic [12:0] M_AXI_awaddr;
logic [2 :0] M_AXI_awprot;
logic M_AXI_awvalid;
logic M_AXI_awready;
logic [31:0] M_AXI_wdata;
logic [3 :0] M_AXI_wstrb;
logic M_AXI_wvalid;
logic M_AXI_wready;
logic [1:0] M_AXI_bresp;
logic M_AXI_bvalid;
logic M_AXI_bready;
logic [12:0] M_AXI_araddr;
logic [2 :0] M_AXI_arprot;
logic M_AXI_arvalid;
logic M_AXI_arready;
logic [31:0] M_AXI_rdata;
logic [1 :0] M_AXI_rresp;
logic M_AXI_rvalid;
logic M_AXI_rready;
logic clk;
logic rstn;
// 生成时钟
localparam CLKT = 2;
initial begin
clk = 0;
forever #(CLKT / 2) clk = ~clk;
end
initial begin
rstn = 0;
#(CLKT * 5)
rstn = 1;
M_AXI_awaddr = 13'b0_0000_0000_1000;
M_AXI_awprot = '0;
M_AXI_awvalid = 1'b1;
wait(M_AXI_awready == 1'b1) #(CLKT * 1) M_AXI_awvalid = 1'b0;
M_AXI_wdata = 32'h0104_15bc;
M_AXI_wstrb = 4'b1001;
M_AXI_wvalid = 1'b1;
wait(M_AXI_wready == 1'b1) #(CLKT * 1) M_AXI_wvalid = 1'b0;
M_AXI_bready = 1'b1;
wait(M_AXI_bvalid == 1'b1) #(CLKT * 1) M_AXI_bready = 1'b0;
M_AXI_araddr = 13'b0_0000_0000_1000;
M_AXI_arprot = '0;
M_AXI_arvalid = 1'b1;
wait(M_AXI_arready == 1'b1) #(CLKT * 1) M_AXI_arvalid = 1'b0;
M_AXI_rready = 1'b1;
wait(M_AXI_rvalid == 1'b1) #(CLKT * 1) M_AXI_rready = 1'b0;
#(CLKT * 10) $stop;
end
bram_wrapper bram_wrapper_inst_0 (.*);
endmodule
四. 运行仿真
使用vivado 2018.3 和 modelsim 10.6e SE-64 联合仿真,关于如何在vivado中使用modelsim仿真我后续会更新相关博客。
可以看到,写入的数据为32’h0104_15bc,此时写选通wstrb = 4‘b1001,表示最高字节位和最低字节位的数据有效,从同一地址读出的数据为32’h0100_00bc,可见写入与读出是一致的。
五. 总结与分享
实验表明AXI4-Lite如前文介绍的一样工作。AXI4-Lite每次只能写入一个数据,且必须按照先地址后数据的方式,它不支持乱序传输,唯一需要注意的是写选通信号wstrb,通过此信号可控制数据总线上的哪些字节会被写入到指定地址。
完整工程文件:axiLiteRwRam.7z
欢迎大家关注我的公众号:徐晓康的博客,回复以下四位数字获取。
4569
建议复制过去不会码错字!
如果本文对你有所帮助,欢迎点赞、转发、收藏、评论让更多人看到,赞赏支持就更好了。
如果对文章内容有疑问,请务必清楚描述问题,留言评论或私信告知我,我看到会回复。
徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。