路科验证的个人空间 https://blog.eetop.cn/1561828 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

SV的环境构建篇之三:接口

已有 4111 次阅读| 2016-12-4 21:52 |个人分类:验证系统思想|系统分类:芯片设计

在认识了DUT ctrl_regs的接口定义以及如何进行例化以后,我们接下来就需要考虑如何在testbench中给ctrl_regs添加需要的激励。从下面这张图可以看到,ctrl_regs在MCDF的集成中需要与多个模块进行连接,这些模块包括有:
  • 外部的时钟复位模块(Clock & Reset)
  • 外部的控制模块(Controller)
  • 内部的Slaves、FIFOs、Arbiter以及Formatter
如果要完成充分的验证,我们就需要对上述的连接关系给予激励。在发生激励之前,我们采用接口(interface)进行stimulator与DUT的连接。

interface的基本作用是对各个模块做清晰有序地连接,因此interface可以看做一捆智能的集束线(collector)。如果将interface作为一个路由器(router),那么意味着只要同interface的接口类型一致,多个模块都可以使用这个interface,将其与interface进行连接也不需要担心信号的驱动方向、连接性问题。可见,interface不但可以方便多个模块之间的连线,也能将DUT同testbench的连接隔离开来。对于DUT同testbench隔离的好处,我们将会在后续部分关于stimulator、monitor的实现中进一步介绍。

那么有了interface,我们应该如何声明这样的接口,而且应该需要几个接口呢?下面是常见的两种实现方式:

interface连接方式1
该连接方式只定义了一个interface: regs_if,而且将所有需要同ctrl_regs相连接的信号均定义在regs_if中且与ctrl_regs在testbench中进行连接。

interface连接方式2
该方式则定义了三个interface: regs_cf_if、regs_ini_if和regs_rsp_if。该三个interface的功能划分更加明确:
  • regs_cr_if:时钟和复位的接口,用来供给ctrl_regs和其它两个接口。
  • regs_ini_if:ctrl_regs读写接口,也由于该接口模拟了集成以后外部控制器的作用,我们将其称之为initiator(发起端)接口,即该接口主动发起读写请求。
  • regs_rsp_if:ctrl_regs配置和状态反馈接口,该接口时为了能够及时响应ctrl_regs在相应寄存器经过读写以后对所对应模块的功能配置或者状态返回。我们也将其称之为responsor(响应端)接口。

那么这两种方式从实现方式来看,方式2看起来需要做更多接口的定义,而且似乎将一件连接环境的事情变得更复杂了,是这样的吗?从实现的工作量来看,方式2确实会引入更多的工作,但从日后ctrl_regs的testbench在更高层级的集成复用来看,现在按照ctrl_regs边界信号进行有效的划分会对日后的工作提供更多的方便,这一点也会在以后的篇章关于《SV的系统集成篇》中得到体现。

那么,下面是关于上述三个interface的定义:

interface regs_cr_if;

logic clk;
logic rstn;

endinterface: regs_cr_if


interface regs_ini_if
#(parameter int addr_width = 8,
parameter int data_width = 32);

logic clk;
logic rstn;
logic [ 1:0] cmd;
logic [addr_width-1:0] cmd_addr;
logic [data_width-1:0] cmd_data_w;
logic [data_width-1:0] cmd_data_r;

endinterface: regs_ini_if


interface regs_rsp_if;

logic clk;
logic rstn;
logic [ 7:0] slv0_avail;
logic [ 7:0] slv1_avail;
logic [ 7:0] slv2_avail;
logic [ 2:0] slv0_len;
logic [ 2:0] slv1_len;
logic [ 2:0] slv2_len;
logic [ 1:0] slv0_prio;
logic [ 1:0] slv1_prio;
logic [ 1:0] slv2_prio;
logic slv0_en;
logic slv1_en;
logic slv2_en;

endinterface: regs_rsp_if

在上述interface定义中我们需要注意以下几点:
  • interface可以定义input/output/inout端口,或者如果这些接口对不同连接模块的方向不相同时,可以将这些端口定义在interface实体中,这样的信号定义本身没有方向。
  • 我们建议将interface上信号数据类型定义为logic,而非wire或者reg。这么做是因为SV的logic类型本身扩展了传统的reg类型,使得其也可以像wire进行连线。值得注意的一点是,唯一不能使用logic变量的地方是含有多驱动(multi-drive)的场景,这时候你必须使用连线类型,例如wire。
  • 此外,我们需要再次强调,interface中的信号如果用来同DUT相连接,那么应该必须是四值逻辑即logic/reg/wire等,而不应该是二值逻辑bit/int/byte等。这么做的考虑是确保在今后的stimulator到DUT的驱动场景或者DUT到monitor的数据采集场景中,硬件部分的'X'或者'Z'信号不会被缺省转换以致丢失。
  • interface也可以定义parameter来方便扩展复用。

在实现了interface的端口定义之后,我们进一步深入interface的其它应用。首先,我们提到了regs_cr_if的功能是提供时钟和复位信号的,这里我们可以在regs_cr_if中产生时钟和复位信号。

interface regs_cr_if;

logic clk;
logic rstn;

initial begin
clk <= 0;
forever begin
#5ns clk <= !clk;
end
end

initial begin
#20ns;
rstn <= 1;
#40ns;
rstn <= 0;
#40ns;
rstn <= 1;
end

endinterface: regs_cr_if

从上面的例子可以看到,interface也可以包含过程语句(always和initial)以及连续赋值语句,这对更高层级的建模和testbench应用都有好处。从上面的regs_cr_if中可以看到通过两个简单的initial过程语句可以在interface内部产生出时钟和复位信号。

接下来我们看三个interface与DUT的实际连接关系:

// interface 例化
regs_cr_if cr_if();
regs_ini_if ini_if();
regs_rsp_if rsp_if();

// 时钟和复位信号
assign ini_if.clk = cr_if.clk;
assign ini_if.rstn = cr_if.rstn;
assign rsp_if.clk = cr_if.clk;
assign rsp_if.rstn = cr_if.rstn;

// ctrl_regs例化及同interface连接
ctrl_regs regs(
.clk_i (cr_if.clk ),
.rstn_i (cr_if.rstn ),
.cmd_i (ini_if.cmd ),
.cmd_addr_i (ini_if.cmd_addr ),
.cmd_data_i (ini_if.cmd_data_w ),
.cmd_data_o (ini_if.cmd_data_r ),
.slv0_avail_i (rsp_if.slv0_avail ),
.slv1_avail_i (rsp_if.slv1_avail ),
.slv2_avail_i (rsp_if.slv2_avail ),
.slv0_len_o (rsp_if.slv0_len ),
.slv1_len_o (rsp_if.slv1_len ),
.slv2_len_o (rsp_if.slv2_len ),
.slv0_prio_o (rsp_if.slv0_prio ),
.slv1_prio_o (rsp_if.slv1_prio ),
.slv2_prio_o (rsp_if.slv2_prio ),
.slv0_en_o (rsp_if.slv0_en ),
.slv1_en_o (rsp_if.slv1_en ),
.slv2_en_o (rsp_if.slv2_en )
);

interface本身既需要同DUT连接, 也需要同stimulator和monitor连接,那么对于不同对象来讲,连接的信号方向也是不相同的。考虑到interface中声明了的信号自身没有方向,这就使得同一个信号可能会连错或者反向驱动的问题。

interface为了限制不同对象对其信号的访问权限和方向,通过modport来做进一步的声明来确立信号应该连接的方向。这里,我们以regs_ini_if为例:

interface regs_ini_if;
... // 省略了接口信号声明
modport dut(
input cmd, cmd_addr, cmd_data_w,
output cmd_data_r
);

modport stim(
input cmd_data_r,
output cmd, cmd_addr, cmd_data_w
);

modport mon(
input cmd, cmd_addr, cmd_data_w, cmd_data_r
);

endinterface: regs_ini_if

在regs_ini_if实体中,进一步定义了3个modport:dut,stim和mon。它们分别用来作为DUT,stimulator和mon的“插座”,这样一方面澄清了各个对象可以连接的interface信号,也通过限制的方向避免了反向驱动的问题。如果形象地来理解modport的作用,可以将interface作为一个插排,而各个modport作为针对不同对象的插槽,这使得针对更复杂的连线问题可以得到简化。
如果regs_ini_if通过modport来进一步约束信号的连接,那么上面的代码实例对于DUT的连接就转换为下面的部分:

ctrl_regs2 regs(
.clk_i (cr_if.clk ),
.rstn_i (cr_if.rstn ),
.cmd_i (ini_if.dut.cmd ),
.cmd_addr_i (ini_if.dut.cmd_addr ),
.cmd_data_i (ini_if.dut.cmd_data_w ),
.cmd_data_o (ini_if.dut.cmd_data_r ),
.slv0_avail_i (rsp_if.dut.slv0_avail ),
.slv1_avail_i (rsp_if.dut.slv1_avail ),
.slv2_avail_i (rsp_if.dut.slv2_avail ),
.slv0_len_o (rsp_if.dut.slv0_len ),
.slv1_len_o (rsp_if.dut.slv1_len ),
.slv2_len_o (rsp_if.dut.slv2_len ),
.slv0_prio_o (rsp_if.dut.slv0_prio ),
.slv1_prio_o (rsp_if.dut.slv1_prio ),
.slv2_prio_o (rsp_if.dut.slv2_prio ),
.slv0_en_o (rsp_if.dut.slv0_en ),
.slv1_en_o (rsp_if.dut.slv1_en ),
.slv2_en_o (rsp_if.dut.slv2_en )
);

可以看到,除了时钟和复位信号意外,其它信号与ini_if和rsp_if内部信号连接时需要再进一步通过modport dut来连接。与之前的点对点连接方式比较,modport可以使所有相关的信号集中在同一个地方描述,减少出错的概率,但也需要额外地对这些不同的信号进行分类集合。

在介绍完interface主要的使用情况以后,我们下一节将介绍testbench的“外包装”——program(程序块),来看一看program与module的比较,以及它们之间可能存在的竞争问题。

谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。



点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 254

    粉丝
  • 25

    好友
  • 33

    获赞
  • 45

    评论
  • 访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-5-12 15:24 , Processed in 0.017736 second(s), 12 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部