本系列我想深入探寻 AXI4 总线。不过事情总是这样,不能我说想深入就深入。当前我对 AXI总线的理解尚谈不上深入。但我希望通过一系列文章,让读者能和我一起深入探寻 AXI4。
转载自:知乎
欢迎来到深入 AXI4 总线的实战篇,系列第二篇文章中,我们将首先了解调用 AXI VIP 产生激励与响应的方法,并完成一个小目标:实现三种情况下的握手信号。
本文配套文章:
ljgibbs:深入 AXI4 总线(一)握手机制zhuanlan.zhihu.com
关于平台移植
本文的实战在第一篇的示例工程上,新建 tb 来实现我们的功能。新建 tb 中的逻辑由 demo tb 的内容搬运简化而来。
本来计划新建一个工程,但是阅读 PG267 (IP 核的产品文档)发现,当前 Vivado 对于该 IP 的支持还比较弱,需要将 IP 的实例名以及层次路径硬编码至 tb 中,如果新建工程还比较麻烦。所以本文暂时还是在 example 的基础上展开 。
想要重新搭建工程的读者请注意阅读 demo tb 开头的说明,或者 PG267 第六章中的内容。
本文的场景为主机与从机之间通过 Pass-through (以后叫 ta 中间机?)进行通信。从机选用 mem 模式,有自己的存储模型,即使用 mem\_stimulus.sv 作为激励。关于存储模型,我们将在后续的文章中讨论。
我们的改动在于主机的激励部分,以原先的 mst\_stimulus.sv 为基础,构建我们自己的主机激励,改动后的 testbench 结构如下图所示。
是的,新的激励加上了 headbig 字段,这来自于 深入 AXI4 总线 系列文章的英文名: Headbig AXI4。
VIP API 基本调用方式
PG 文档中,Xilinx 表示 VIP 基于 SystemVerilog 语言开发,同时在 API 的设计上,命名与数据结构的设计均参考了 UVM 框架,便于 VIP 在验证系统中的集成。由于本文的重点不在于 UVM 或者 API 的设计,因此仅跟着 demo 以及 PG 中的 API 调用流程过一遍。
主机 master
首先来看主机,定义于 axi_vip_master_mst_stimulus.sv
中
- 为主机 master ip 创建一个 agent 对象,传入 master ip 的层级路径,后续通过该 agent 控制主机 ip
agent = new("master vip agent",DUT.ex_design.axi_vip_mst.inst.IF);
- 通过 agent 启动主机
agent.start_master();
- 在 fork ...join 并发块中同时发出主机的读写传输事务。
fork
begin
//调用写传输事务 API
end
begin
//调用读传输事务 API
end
join
- 产生两者的 API 结构相似,我们以写传输事务为例。例程中依次使用了 3 种 API ,分别产生
- 完全随机化的写传输事务
multiple_write_transaction_full_rand ("single write",1);
- 定制化的写传输事务
single_write_transaction_api("single write with api",
.id(mtestWID),
.addr(mtestWADDR),
.len(mtestWBurstLength),
.size(mtestWDataSize),
.burst(mtestWBurstType),
.wuser(mtestWUSER),
.awuser(mtestAWUSER),
.data(mtestWData)
);
- 部分随机化的写传输事务
multiple_write_transaction_partial_rand(相关参数);
我们常说,不想知道 API 函数之下发生了什么的程序员不是好程序员,IC 工程师同样如是。以较简单的定制化写传输事务函数为例,所谓函数实质上是一个 sv task,以下是 task 中的主要内容:
axi_transaction wr_trans;
wr_trans = agent.wr_driver.create_transaction(name);
wr_trans.set_write_cmd(addr,burst,id,len,size);
wr_trans.set_prot(prot);
//...
wr_trans.set_data_block(data);
agent.wr_driver.send(wr_trans);
首先声明一个 axi 传输事务对象,然后在主机 ip 的 agent 下建立传输事务。
通过 API 函数设定写命令信息,设定传输属性以及待传输的数据块。数据块的数据类型为
bit [4 * 1024 * 8 - 1:0]
从机 slave
接下来,我们看一下从机的相关流程,定义于 axi_vip_master_mem_stimulus.sv
中。
同样为从机创建并启动相应 agent,此处与主机相似不表。
在 demo 中构造了一个虚拟数据作为后续对主机读数据的回应,因为本文的主要工作是得到握手信息的波形,因此并不会实际存储主机写入的数据,而是在主机读取任意地址时,返回这个虚拟数据。
最后,从机调用 API 产生 wready 信号应答
task user_gen_wready();
axi_ready_gen wready_gen;
wready_gen = agent.wr_driver.create_ready("wready");
wready_gen.set_ready_policy(XIL_AXI_READY_GEN_OSC);
wready_gen.set_low_time(1);
wready_gen.set_high_time(2);
agent.wr_driver.send_wready(wready_gen);
endtask
此处表示 wready 信号在从机空闲时周期性生成,有效时间为 2/3,我们可以在后续的波形中看到。
握手波形
我们对主机的激励代码进行修改,仅保留单次定制化的读写传输事务,地址为 0x0,突发长度为 0。在波形中我们得到了三种情况下的握手信号。
(1)VALID 信号等待 READY 信号
在 tb 中主机并行地启动读写传输事务,AR/W VALID 同时置高,在等待从机给出 READY 信号后完成地址与控制信号的传输,此时地址为 0x0.
(2)READY 信号等待 VALID 信号
主机在发出读传输事务后,置高 RREADY 信号等待接收从机返回的读数据。在从机置高 RVALID 后,读传输事务完成。
(3)READY 与 VALID 信号同时置起
在设置从机的 READY 信号类型时,我们设置为周期性置高 READY,从下图中可以看到,READY 信号在送出 2 个周期高电平后置低 1 个周期。
在这个场景中,写数据通道中的 WREADY 信号正好与 WVALID 信号同时置起,解锁了最后三种握手姿势中的最后一种,OK 本文实战篇收工了。
结语
本文首先介绍了 AXI VIP 中产生传输事务的基本方法。基于 demo 修改了一个简单纯粹的例子,并基于这个例子观察到了握手信号,将实战篇与理论篇结合起来。修改的代码后续我会放到网络上某个地方进行分享(现在还没确定,可能是 github 吧)。
访问码云 HeadBig AXI 仓库,代码会陆续上传:
https://gitee.com/ljgibbs/HeadBig-AXIgitee.com
在后续的实战篇中,我们会使用一个真正的从机——BRAM,以供作为主机的 AXI VIP 消遣。此前的文章中,我们通过 DataMover IP 或者 tb 访问的 BRAM,多少有些局限或者可能不规范。
后续继续与理论结合,研究各个传输通道上的特性以及突发传输结构,一一上演窄传输、非对齐传输等例子。
再之后计划采用一个 ddr 控制器作为从机,来完整地验证 AXI 协议主机端的特性。先想这么多吧,另外理论篇也会继续更新。
推荐阅读
- 深入 AXI4 总线(E0)实战:平台搭建
- 可重构计算:基于FPGA可重构计算的理论与实践 1.器件架构 译文(三)
- 可重构计算:基于FPGA可重构计算的理论与实践 1.器件架构 译文(二)
- 可重构计算:基于FPGA可重构计算的理论与实践 1.器件架构 译文(一)
关注此系列,请关注专栏FPGA的逻辑