当前位置:   article > 正文

【数字设计验证】System Verilog(sv)稍微进阶的笔记(一)_system verilog中改变波形颜色

system verilog中改变波形颜色

接触硬件描述语言(HDL)也有几个年头了,由于之后research会偏向Architecture,做偏软件的活,算是走入一个新的阶段,因此想写一篇关于SV的笔记进行总结复习。选择SV的原因在于它目前是业界主流。SV是Verilog的继承扩展版本,类似于Cpp和C的关系,扩展内容可以分为Declaration Enhancement(多了变量类型),和Programming Enhancement(一些写法的shortcut,硬件行为描述的支持,运算符,directive等等),具体参见下图,Coding会在Part 2详细说明。总之sv对design和verification的支持都很显著,本文会挑个人觉得比较有用的点去记,所以这不是一个入门的指导,我会跳过蛮多基础的东西,Bear in mind!!!
SV Extension

1. EDA工具对代码的处理与输出

在讲Coding前,还是稍微skim一下它的工作步骤,只想看sv的Coding Guide的话直接跳到Part 2,这里不细说底层的运行,因为这与不同EDA工具和FAB的lib有关系 (例如说Synopsy和Cadence的综合程序算法就不大一样),目前还没有能力和兴趣去探究,主要还是说一些已经standardized的东西。我们使用HDL的最终目的在于生成可靠可知的IC layout,我们将layout及其设计步骤抽象为代码,人写完代码后借由一系列Computer Aided Design(CAD)工具(在EE领域我们特指它为EDA)再将代码转变为layout的输出文件。因为我们需要其可靠可知,所以设计流程中的一些中间产物也是很重要的。因此这里将对HDL代码的处理分步为:Compile, Simulation和Synthesis。对应着不同类的EDA工具对应的功能目的和输出结果。

PS: HDL可服务于不同类型的DIC实现:Full Custom, Semi-Custom和Programmable。这更分化了不同的EDA功用,但是大道至简,殊途同归,后文主要还是讲general的部分和一些经验tips方便回忆
HW impl

1.1 Compile

第一步是编译,实际上对于这一名词不同工具我们也能看到不同的结果。比如说对于vivado或者dc shell这类需要后续制作netlist等的软件,当我们输入filelist进行编译后除了进行syntax analysis外,还会附带生成对应的RTL Schematic,也就是说我们可以看到电路的hierarchy,甚至有些情况Compile就直接是Synthesis操作本身。而对于仿真器而言,Compile的含义很多只代指对与各个文件的代码分析,当要去执行Simulation的时候才会load各个模块生成Schematic和对应的Hierarchy。

这里仿真软件举的例子为Modelsim(vsim这个软件的组分也是比较多的,包括compiler, linter, simulator, waveform visioner等,支持GUI或者TCL对于各个模块的调用)。它在compile worklib后点击View–Schematic依旧是空的,在Run Simulation过后可以在Sim窗口中看到目标top module 的Hierarchy结构还有schematic。而Dc shell和vivado等可以直接set top module并查看这些东西。Anyway,在这里我还是想将对RTL代码Compile的概念统一描述,就当是Syntax Analyze 和 Linter,进行纠错并整合信息方便后面的程序进行处理。Compile是可以分辨一些directives(主要应用于Syn),挑出基本的组分。此外,关于Tb层面的compile,默认的timescale为1ns/xx(时间单位/仿真精度)。还有package和#include之于compile的区别,会在Part 2提及,Makefile的特性,我们重复compile不需要update没有更改的成员。在编写code的时候利用文本插件(推荐Vim或者VSC等,能装插件就行)的辅助可以很快通过第一次compile(减少很多syntax层面的typo)。

1.2 Simulation

当然在通过1.1的Compile后我们只是独自对各个file进行检查,想要把DUT作为一个整体,还需要各个模块进行完整的interconnection。因此当我们在Modelsim跑某个testbench时,即便Compile全部PASS,也会出现例如组件名字mismatch,端口连接mismatch或不能识别某个名字等的情况。总之,针对DUT部分,我们需要将它作为电路进行编写并进行严格的连接,这些组件都能在Simulation后的hierarchy中单独显示,合理地划分组件和功能能更方便Debug,SV对于Simulation有非常多额外的支持。

这里标记一些用Simulation Tool的小tips。

  • 首先跑Sim后,代码和各组件建立了映射关系,因此我们可以通过看代码添加所需的信号,也可以邮件代码中的一些信号trace driver或reference,这个对于debug一些陌生的代码很有好处,可以快速地了解信号,寄存器之间的关系,反之亦然-go to source。
  • 在写代码的时候,除了一些需要用parameters customize Hardware,尽量添加信号位宽的标注可以方便Debug发现忘记添加变量声明的情况。或者说直接添加以下代码取消掉SV对default type的设置。
`default_nettype none
  • 1

这样的话如果我们不小心用了未声明的变量就会直接报错,而非默认为logic[0:0],在一些现有的代码中,有人习惯直接用未声明的变量做interconnection,个人认为这不是个好习惯,尽管说这些变量不具备其他的功能,但标注出来对debug而言会更方便。

  • 添加Divider在waveform中可以方便区分不同module的信号,也可以在preference设置代码名称的path深度来简化信号来源的描述。信号的默认名字一般就是main/sub/signal name。我一般习惯设置max path长度为2~3,一般来讲记性越差,工程越大,所需要的path长度更大吧。一次性拉了过多的信号的话,可以通过set property改变wave的颜色使重要成员显著
    signal path

  • 做self check的时候可以多使用 $stop or $fatal(“log”)并fork timeout thread来做debug,step by step的debug是我们所喜欢的方式

  fork
	//Error occurs when pwr_up is never asserted
	begin: timeout
		repeat(30000) @(posedge clk);
		$fatal("Timeout for waiting pwr_up");
	end: timeout1
	//If pwr_up is asserted, disable timeout
	begin
		@(posedge iDUT.iAuth.pwr_up);
		disable timeout;	//Once pwr_up is asserted, disable timeout and keep testing
	end
  join
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • $strobe $display $monitor 在event-driven simulation下发生在哪个queue呢(时刻),参见Stratified Event Queue,它详细描述了Simulation的运行规范,这里就记住strobe和monitor能发生在非阻塞赋值后就行。
  • $stop $finish stop类似于断点,可以continue。finish类似于terminate当前thread,在GUI中它通常会问我们finish后要不要exit。Debug还是stop用的比较多
  • class 中的变量无法生成add到wave,我们需要将它转为interface才可以拉出来看到波形。这不太方便,因此要注意信号尽量例化或连接到外面。
  • 后仿真需要注意精度的问题,会对结果产生出入。而且对Netlist仿真有时候就很玄学,应该和Lib有关系,个人看法还是图个乐呵。除此之外尽量也不要依靠内部信号,很容易出现X value,内部信号名很多也会被优化或者处理掉,尤其是synthesize flatten的情况,DUT变成了一层。如果一定要refer一个内部信号,可以在综合时加入一下代码,或者一些软件可以识别RTL代码中的dont touch,下面一些代码是dc shell一些代码是vivado的,使用的时候参见其TCL文档即可。
# in scripts
set_dont_touch [find pin main/sub/sig_name]
set_property DONT_TOUCH TRUE [~]
set_property MARK_DEBUG TRUE [[get_nets –of [get_pins hier1/hier2/<flop_name>/Q]]]
# or in the RTL codes
(*DONT_TOUCH = "TRUE"*) wire xxx...
(* MARK_DEBUG = "{TRUE|FALSE}" *) logic yyy...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.3 Synthesis

这里只讲Code->Netlist这一步也就是,后续Netlist->layout的APR部分不在此列,以DC shell为例子。在Compile并进行完成前仿后,我们将RTL代码mapping到对应库的门级网标中,这一步我们实际上无论是Semi-Custom,Full Custom抑或是FPGA,都是必要的,因为从Code到Gate List是比较外层的由抽象到具象的程序,到了具体的implementation再分化。但无论如何,Netlist所依赖的Lib各不相同,对于Full Custom而言,那就不是单纯依赖Fab提供的Lib了,而是需要更多人工的设计。以DC shell为例子的话,Synthesis在以下方面可以额外注意下,基础共通部分我不赘述,随便看一个完整的脚本代码再参照资料就可以理解:

  1. Cost Function = -Slack + Area + power 此为综合trade off的标准,往往不可兼得
  2. Timing Constraints: 就是数电的基础时序约束,Setup和Hold。Tsu+Td+Tco+Tuncertainty < Tclk; Tco + Td-Tuncertainty > Thold。Tuncertainty来源于Skew也就是Clock Tree的不均衡
  3. 一般在DC shell中我们不会touch clk。会在CTS(Clock Tree Syn)中单独去解决它。
  4. Input delay 和 Output delay一般默认是同步的环境,可当作Td的一部分,针对异步的模块我们一般手动去设置,不对它纳入时序分析的整体和优化。
set_multicycle_path 2 -setup -from [find pin Main/Sub/ptch_*_reg*/CLK]
set_multicycle_path 2 -setup -from [find pin Main/Sub/AZ*_reg*/CLK]

set_clock_groups -name async_clk0_clk1 -asynchronous -group {clk0 usrclk itfclk} \
-group {clk1 gtclkrx gtclktx} ...
  • 1
  • 2
  • 3
  • 4
  • 5
  1. drive以及Capacitive load决定了门的延时,可以参照那个Ids的方程,信号的传输就是经历一个又一个transition,对应的Slope自然就一定上决定了速度。举个例子,Fanout过大drive弱,因此消耗的delay也就多,我们一般也会加以约束,添加buffer,只不过说这又要增加Area了,正好也对应了1的tradeoff
  2. 多次综合的优势:我们之前说过compile定义的区分,对于综合工具而言,Compile有时可以定义为综合操作本身,这一点可以在对应脚本命令中看出,map effort越高,所用以fix timing的area消耗越大。这里我们统一描述为综合Syn,但是tcl还是照常。多次综合可以把Netlist做Flatten,取缔掉原版设计的hierarchy,尽力去服用Gate本身。也能添加一些额外的约束到Netlist以便于重新组织,例如说之前提到的asynchronous和hold time fix。示例代码如下:
compile -map_effort high 
set_multicycle_path 2 ...
ungroup -all -flatten
set_fix_hold clk
compile -map_effort medium
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 还有一个小tip,有些人会
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号