本文首发于微信公众号“芯片学堂”,作者JKZHAN
接口(interface)是SV引入的很重要的特性,目前在绝大多数验证环境或者设计中都会出现。接口最直接的作用就是将一组相关的信号封装到一起,特别是一些标准协议的接口信号,比如常见的AMBA AXI/AHB/APB等等。接口的定义不仅可以方便信号在验证环境组件中的连接,还可以将其复用至其他芯片设计中,降低连接出错的概率。
接口并不只是提供一组信号这么简单,它还是可综合的,它还可以包含modport、clocking block、parameter、过程语句块、断言、覆盖组(covergroup)、函数和任务等。其中modport、clocking block以及集成方式的讨论在下面会展开介绍。
这里先来一个简单的接口实例,后面根据介绍到的特性将在此基础上逐步扩展。这个例子只是用来展示接口的语法和使用方法。
01 Modport
Modport可以用来在接口中定义信号的方向,这种方向的定义相当于是对信号施加了连接约束,避免在模块或者环境组件集成的过程中连接出错。
这里的连接出错指的是,一般在接口中声明的信号是没有方向限制的,也就是说在模块集成的时候,语法上是允许两个模块同时对同一接口信号进行驱动,这种情况的出现一方面不符合设计规范,另一方面则可能会产生X态,最终导致仿真失败。
现在给上面的示例接口添加modport列表。在这个例子中,给接口添加了两个modport,分别定义两组信号方向,从modport命名可以看得出来它们分别是要给master和slave模块用的。
02 Clocking block
在默认情况下,接口中的信号直接是没有时序关系的,也就是说,仿真的时候所有信号都是理想对齐的。现在引入一个叫时钟块(clocking block)的东西。在这个时钟块中,可以清楚的定义各个信号相对于时钟边沿的时序关系,确定信号被驱动或者采样的时刻。专业点应该叫时钟偏斜(skew)。
Clocking block跟时序分析时的时序约束(timing constraint)的概念是不同的。Clocking block一般用来限定相对Testbench而言的时序关系,所以在clocking block中指定信号方向时,通常是站在testbench的立场上去考虑的。对于RTL来说,在做时序分析或后仿真的时候,会有单独一套时序约束,比如IO的input delay, output delay等。
继续在前面的例子上添加时钟块,下面的例子涵盖了大部分通常会用到的语法。之后在testbench中引用接口信号时,需要加上clocking block的名字这些时序关系才会起作用,比如demo_if_inst.cb.valid <= 1,不加clocking block的名字是不会起作用的!
03 Integration
Integration这个主题主要是讲模块的集成方式。最原始且最常用的模块集成方式可以叫Static Pin to Pin,在顶层模块中声明所有信号或者接口,然后分别在例化子模块的时候完成连接。在testbench或者顶层设计中,interface跟module一样需要先声明之后才能使用,其使用方法也没有很多花头。在有些代码中会用.*来隐性绑定信号,除非可以保证信号名都能匹配上,否则不建议这么做。
除了上面这种常见的集成方式,SV还提供了另一种集成方式:bind。其实bind要解决的是做白盒验证(whitebox verification)的驱动问题,也就是如何在testbench中完成对DUT内部模块的驱动。Bind特性,可以在不修改源代码的情况下,为目标模块构建模块或者接口,所以bind通常被用在添加断言模块或者内部接口的驱动和采样上。
04 Virtual Interface
Virtual interface(虚拟接口)这个概念很重要,在面向对象的验证环境架构中,虚拟接口为我们提供了接口动态绑定的功能。
在整个仿真环境里面,可以简单地将各种组件根据属性划分到硬件和软件两个空间中。接口本身的属性跟模块是一样的,属于硬件范畴,有些地方叫static component,它们在仿真的一开始就需要实现(elaboration)好。而基于OOP的验证环境中,很多环境组件,比如driver、monitor、scoreboard等组件则属于软件的范畴,它们是在仿真程序运行的过程中动态例化的。
这个时候矛盾就产生了。因为我们需要在driver中对某一个接口进行驱动,需要在monitor中对某一个接口进行采样,那么接口这个东西应该在哪个地方以及哪个时候实例化才合适。
虚拟接口的引入可以解决这个问题,且虚拟接口这种方法也是比较易用的,但不代表虚拟接口是解决该问题的唯一方法,比如上面Integration小节的第二个代码例子中,使用抽象类也可以解决。虚拟接口可以理解成是interface的句柄或者指针。在elaboration的时候,接口本身就已经实现好了,但还没有跟环境组件关联起来。我们可以在driver或者monitor这些需要用到该接口的类中创建虚拟接口成员,等driver或monitor在实例化的时候,再在构造函数中将虚拟接口实际指向某一个接口对象。可以看看下面这个代码片段。
参考文献
[1] IEEE Standard Association. "IEEE Standard for SystemVerilog-Unified Hardware Design, Specification, and Verification Language." (2013).