?1. 仿真时间控制方法
SystemVerilog中,仿真时间的推进可以用以下三种方法之一:
Delay control:#
Event control:@
Wait statement,它像是event control和while loop的结合。
Delay control有两种方式,一种是#在执行语句之前,一种是#在执行语句的等号右边。如下所示:
#10 rega = regb; ? // 延迟10个单位后,执行rega = regb赋值语句;#((d+e)/2) rega = regb; ?// 延迟(d+e)/2个单位后,执行执行rega = regb赋值语句;a =?#5 b; ?// 等价于temp=b; #5 a=tempa = @(posedge clk) b; ?// 等价于temp=b; @(posedge clk) a = temp;a = repeat(3) @(posedge clk) b; ?// 等价于temp = b; @(posedge clk); @(posedge clk); @(posedge clk) a = temp;
也就是说,#在执行语句之前的话,先delay,然后计算等号两边的值;#在执行语句的等号右边,先计算右边的式子,然后等delay到了,赋值给左边的变量;
2. 控制fork线程
在用fork…join/join_any/join_none之后,需要一些方式对它们进行控制,可以使用wait fork、disable或disable fork。
wait fork会等待当前进程创建的子进程都结束后,才会继续执行,需要注意的是,它不会去等这些子进程创建的子进程。
disable会停掉一个命名块或task中的所有活动,不管父-子进程的关系,子进程可以停掉父线程。而且一个进程也可以被任何不相关的进程使用disable停掉。
disable fork也是用于停掉进程的执行,但会考虑父-子进程的关系,另外它是会停掉所有活跃的子进程,包括衍生的所有子进程。
3. 进程(process)精细控制
process是SV内建的class类,在process启动后,允许其它process去访问和控制它。这个类的定义如下:
class?process;? typedef?enum?{ FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state;??static?function process?self();??function state?status();??function?void?kill();??task?await();??function?void?suspend();??function?void?resume();??function?void?srandom(?int?seed?);??function?string?get_randstate();??function?void?set_randstate(?string?state?);endclass
当产生一个process时,process类型的对象会在该线程内部创建,用户是不能自己创建process类型的对象,如果尝试用new构造函数去创建一个新的process对象,那么编译器会报错的。而且process这个class也不能被继承,尝试去继承也会导致编译错误的。
用户如果想要操控一个process,那么他可以通过定义一个process类型的变量,然后把它指向该process的对象就可以了。self()函数可以用于返回当前process的句柄。如下所示:
process?job;fork??begin: new_blk? ? job =?process::self();? ? ... ;??endjoin_none
通过job句柄就可以操控new_blk的进程了。
在process类的内建函数中:
status()用于返回当前process对象的状态,有以下这些状态:
FINISHED:表示process正常结束;
RUNNING:表示着当前process正在运行,没有被block语句挡住;
WAITING:表示当前process正在等待block语句执行完;
SUSPENDED:表示当前process被暂停掉,在等待恢复执行状态;
KILLED:表示当前process被强制kill或disable掉;
kill()函数会终止给定process和它衍生子process,这些子process是在被killed process中使用fork语句创建的;
await() task允许一个process等待另一个process完成,process自身不能调用自己的await();
suspend()函数允许一个process暂停掉自己的执行或其它process的执行;
resume()函数可以重新恢复之前被暂停执行的process;
4. 相等运算符
相等运算符有三种:
Logical equality:==, !=,该运算符中如果运算数包含有x/z态,那么结果就是x态。只有在两边的数都不包含x/z态,最终结果才会为0(False)或1(True);
Case equality:===, !==,该运算符中会把两边运算数的x/z态都考虑进去,最终结果肯定是0或1;
Wildcard equality:==?, !=?,该运算符的右边是wildcard匹配,右边操作数的x或z bit对应于左边的对应位可以为任何数字,但左边的x或z不能被认为是wildcard。因此该运算符的结果可能有x/0/1三种结果,如果左边的x/z态不能被右边的wildcard匹配,那么结果就是x态,如果左边的x/z态会被右边的wildcard匹配,那么结果就为0/1;
5. 短路求值
只有&&, ||, ->, ?: 支持短路求值(short circuit evaluation),该方法如下:
第一个operand表达式肯定会被执行,对于&&,如果第一个operand结果为false,那么后续的operands就不会执行;对于||,如果第一个operand结果是true,那么后续的operands不会被执行。
对于条件运算符(cond_predicate ? expr1 : expr2),如果cond_predicate为True,那么第一个expr1会被执行;如果cond_predicate为False,那么第二个expr2会被执行。如果cond_predicate为x或z态,那么将具体情况具体分析,根据expr1和expr2的值来决定,如下表所示,第一列为expr1,第一行为expr2。
比如以下运算:
wire?[15:0] busa = drive_busa ? data :?16'bz;
如果drive_busa为1,那么busa等于data;如果drive_busa为不确定态,那么busa也是不确定的。因为expr2为z态,所以不管data为任何值,busa就是x态。
6. 集合操作符
集合操作符的定义如下:
inside_expression?::= expression inside {?open_range_list?}
inside左边的expression是singular expression。inside的右边是用逗号分隔的expression或range列表,如果expression是unpacked array,那么它将自动会被展开直到为singular value。open_range_list的value可以重复,所以values和value ranges可以有重叠。
对于非整形expression,inside是采用logical equality(==)运算符来比较的。对于整形expression,inside是使用wildcard equality(==?)来比较的,所以range_list中expression含有的x/z 位不参与比较,但左边的x/z仍然是需要比较的。
inside没有匹配到相等的expression时,如果一些比较里结果为x,那么将整体返回1'bx。
inside中range的指定时采用[low_bound : high_bound]的方式,$可以可以用于代表左边expression中最小或最大值。如果[:]中左边的值大于右边的值,那么该range会被认为时无效的,没有包含任何value。
如下例子:
int?array?[$] =?'{3,4,5};if ( ex inside {1, 2, array} ) ... // same as { 1, 2, 3, 4, 5}assign r=3'bz11 inside {3'b1?1, 3'b011};?// r = 1'bx
965