SystemVerilog学习笔记3——接口、采样、测试、调试

目录


接口

  • 接口可用作设计,也可用作验证
  • 在验证环境中,接口可以使连接变简洁而不易出错
  • interface和module使用性质很像,它可以定义端口和双向信号,它可使用initial和always,也可定义function和task
  • interface可在硬件环境和软件环境中传递,例如作为module中的端口列表和软件方法中的形参;
  • 可以把interface看作一个“插排”,而DUT与TB之间的数据驱动关系都可以使用interface这个“插排”来完成。

接口的优势:

  • 将有关信号封装在同个接口中,对于设计和验证环境都便于维护和使用。添加新信号只需在接口中定义该信号,在使用该接口的模块或验证环境中做相应修改;
  • interface是SV中唯一的硬件和软件环境的媒介交互
  • 接口由于可以例化,使得对于多组相同的总线,在例化和使用时变得更加灵活。

接口的定义和使用:

  • interface的端口列表中只需要定义时钟、复位等公共信号,或不定义任何端口信号,转而在变量列表中定义各个需要跟DUT和TB连接的logic变量
  • interface也可依靠函数参数化方式提供复用性
  • interface在例化时,同module例化方式一样;
  • 模块里面可以例化模块和interface,interface里面可例化interface但不能例化module

采样和数据驱动

竞争问题

  • 为避免在RTL仿真行为发生的信号竞争问题,建议通过非阻塞赋值或特定的信号延迟解决同步的问题。为尽量避免时序电路中时钟和驱动信号的时序竞争问题,需给出尽量明确的驱动时序和采样时序
  • 默认情况下,时钟对于组合电路的驱动会添加一个无限最小时间(delta-cycle)的延迟,而该延迟无法用绝对时间单位衡量,它要比最小时间单位精度还小;
  • 在一个时间片(time-slot)中可以发生很多事情,例如在仿真器中敲命令“run 0”,即让仿真器运行一个无限最小时间,一个时间片包含无限多个delta-cycle。 s > ms > ns > ps > fs > delta-cycle。

如何避免采样的竞争问题?

  • 在驱动时添加相应的人为延迟,模拟真实的延迟行为,同时加大clk与变量之间的延迟,以此提高DUT使用信号时的准确性和TB采样信号时的可靠性;
  • 对于一些采样时依然存在的delta-cycle延迟的信号,还可以依靠在采样事件前的某段时刻进行采样,来模拟建立时间的采样要求,确保采样的可靠性。

接口中的clocking

  • interface可通过modport进一步限定信号传输的方向,避免端口的连接错误
  • 可在接口中声明clocking(时序块)和采样的时钟信号,实现信号的同步和采样
  • clocking块基于时钟周期对信号进行驱动或采样的方式,使testbench不再苦恼于如何准确及时地对信号驱动或者采样,消除信号竞争的问题
  • clocking块也可定义在module和program中,其列举的信号不是自己定义的,而是应该由interface或其它声明clocking的模块定义的;
  • 声明完名字后应伴随着定义默认的采样事件,即“default input/output event”。没有定义则默认在clocking采样事件前的1step对输入进行采样,在采样事件后的#0对输出进行驱动
  • interface中可定义多个clocking,同个变量在不同clocking中可声明不同方向
    SystemVerilog学习笔记3——接口、采样、测试、调试
    注:“input clocking_skew”中clocking_skew(时钟偏移量)采取的是负值,而输出驱动采用的是正值
  • 采样偏移量抽象为物理中的建立时间,则其要求时钟上升沿之前的某段时间内数据不能有变化,否则数据采样会有不一样的结果;
  • 利用clocking块通过一种类似“物理保持时间”的驱动方式,可实现时钟沿叠加偏移量的延迟驱动效果;
  • 为避免可能的采样竞争问题,应在验证环境的驱动环节就添加固定延迟,使得在仿真波形中更容易体现时钟与被驱动信号之间的时序前后关系,同时便于DUT的准确处理和TB的准确采样;
  • 如果TB在采样从DUT送出的数据,时钟与被驱动信号之间存在delta-cycle时,应考虑时钟采样沿的更早时间段去模拟建立时间要求采样,这种方法也可以避免由于delta-cycle问题带来的采样竞争问题;
  • 当把clocking运用到interface中,用来声明各接口与时钟的采样和驱动关系后,可大大提高数据驱动和采样的准确性,从根本上消除采样竞争的可能性

测试的开始和结束

  • 各设计自身可作为一个大线程,内部又包含多个并行的线程,而模块之间即线程的通信主要依靠信号的变化
  • 对于一个设计,如果在仿真开始没有任何激励(如时钟和复位信号),那么仿真并未开始,也可认为已结束,因为对于设计内部并没有产生任何新事件,也不会由这些事件进一步触发组合逻辑和时序逻辑;
  • 如果在仿真开始后提供时钟和复位信号,对验证而言是必要步骤,但它本身不会对设计的功能产生实质的功能影响。从设计的角度来看复位信号只是让设计进入确定的状态,而时钟信号如同血管的供血功能一般保证设计可以正常地“跳动”;
  • 在Verilog的测试方式中,即便只给设计提供复位和时钟信号,整个仿真也会一直持续下去,并不会主动结束。即使DUT的输入激励已执行完毕,仿真也会一直进行下去,这就需要通过Verilog系统函数$finish()主动结束仿真(结束方式一),也可考虑使用$stop()暂停仿真;两者区别在于$finish会使仿真退出,将控制权交回给操作系统,仿真无法再次继续;$stop会使得仿真暂停,用户还有机会让仿真继续运行。

若仿真开始时在命令窗口敲入命令“run 0”,assign赋值语句和initial语句会开始执行,always时序/组合语句块不会执行:因为assign和intial在0时刻会执行一次,always必须等到信号变化才执行

结束方式二——program隐式结束:SV推出program将验证与设计进行有效隔离后,SV也会将每个program块作为一个独立的测试,如果testbench中只有一个program,则会在执行完该program最后一个initial后自动结束仿真。如果testbench中有多个program,那么需要等待所有program中最后一个initial才能结束仿真;要求仿真自动结束的前提是所有program的initial块都应在一定时间内完成

结束方式三——program显示结束:在目标program内置入系统函数$exit()要求该program强行结束,仿真器仍会等待其它program执行完毕再结束仿真

  • 建议将设计部分放置在module中,而将测试采样部分放置在program中;
  • program视为软件域不可出现和硬件行为相关的过程语句和实例,如always、module、interface(软硬媒介),也不应出现其它program例化语句
  • 为使program进行类软件方式的顺序执行方式,可在program内部定义变量,以及发起多个initial块
  • program内部定义的变量赋值方式应采用阻塞赋值(软件方式);
  • program内部在驱动外部硬件信号时应使用非阻塞赋值(硬件方式)。

为验证环境建立独立的测试盒子

  • 考虑采用program帮助消除采样竞争问题以及自动结束测试用例;
  • 也可采用module硬盒子的方式,使用interface clocking消除采样竞争问题,使用$stop()$finish()系统方法显示结束测试用例。

调试方法

库窗口

库(library)是编译的产物,在没有介绍软件之前,硬件(module,interface,program)都会编译到库中,如果不指定编译库则会被编译进入默认库中。从容纳的类型来看,库既可容纳硬件类型,也可容纳软件类型,例如类和方法,也包括包(package);

  • 对于编译的module,interface和package这些硬/软件,会进入哪个library呢?若无额外指定,它们都会被编译到默认库(work)中。在默认库中各个module是互相识别的,module也识别同个library中的package,如果要使用其他library中的module或package,那么config文件是一项好的选择;
  • 放入库中是仿真的必要步骤,但不代表后续的链接(elaboration)就不会出错。编译(compilation)会检查语法,而链接会进一步检查硬件和软件之间的兼容性、内存安全、结构可靠性等问题.

仿真窗口

仿真窗口(sim window)代表目前正在进行的仿真结构。一般在验证环境中,应该包含硬件测试的结构组件和待测设计(DUT)

  • 可以观察到TB的展开结构和DUT的结构,这种层次化的结构演示是所有仿真器的共性。而通过这种将硬件层次可视化展示的方式方便verifier查找任一层的信号。同时,被引入的package和其组件(utilities)、方法(methods)也会被显式出来;
  • 仿真窗口只能提供静态的层次结构,即由module,program,interface和package的内容,但对于class的例化实例这种在验证环境中的软件动态内容,则需要在类窗口(class window)和对象窗口(object window)中查看。

过程窗口

过程窗口(process window)代表整个仿真在某一时间点上所有过程语句块(initial, always, assign)的状态

  • 如果状态为active,代表该进程块正在执行ready状态代表该进程块是非活跃状态(但不一定代表它已执行完毕或不会再执行);
  • 过程窗口对调试仿真挂起(hang-on),时间无法继续前进的仿真难题特别有效。

查看信号和波形

打印消息

  • 打印消息是调试循环语句、顺序执行语句等查看执行路径和当前变量值的简便方式;
  • 打印消息的基本要点:在$display()系统方法中通过传递$time显示消息发生的时间点。在$display()中通过不同数据格式显式想要的数据格式,例如%x(十六进制)、%d(十进制)、%b(二进制)、%s(字符串)、%t(时间)等;
  • 掌握$display()(消息级别)、$warning()(警告级别)、$error()(错误级别)和$fatal()(严重级别)的使用场景;
  • 在打印数据消息时尽量采取格式美观、可读性高、便于维护的字符串显示格式;
  • 要实现对字符串string赋值,可采用$sformatf()方式对字符串变量格式化。

设置断点和查看变量

设置断点是软件的方法,在软件执行中,通过设置断点(breakpoint)可查看在程序在执行到断点处(程序暂时停止)时的变量数值,而设置断点就要求verifier对程序的执行顺序足够了解;

  • 设置断点便于查看软件程序(function,task,object)中局部变量的数值。要注意的是软件部分的变量(即动态变量,与硬件变量即静态变量相对)无法添加到波形窗口,原因之一是软件变量会在0时刻中完成多次运算,而波形窗口无法反映出在0时刻中的若干变化,且这么做对于波形存储的压力也很大。因此软件变量只可以通过设置断点来查看当前程序中的变量数值;
  • 设置断点的另一个作用在于可以用来调试程序执行的顺序,例如在顺序执行语句执行的多个位置设置断点,通过让仿真执行,查看是否程序在断点处停下,如果没有某个断点没有停留,那么程序的挂起(hang-on)原因就应该在上一个停留的断点和下一个未停留的断点之间。通过这种方式可以缩小调试可疑程序的范围
  • 如果要查看程序执行处的局部变量,需要使用的是局部变量窗口(locals window),可以从菜单栏View-> Locals选中,继而通过断点查看变量,不应使用信号窗口(objects window)查看变量,因为信号窗口是针对静态变量,而局部变量窗口是针对动态变量的。
上一篇:2021大场面试题(十)


下一篇:ARM GIC(三) GIC V3 Handling Interrupts学习笔记。