有些情况下,constraint不能简单用一行来表达,而是需要复杂的计算,如果都写到constraint block内部就比较复杂,而且很乱,这时候可以调用functions来约束随机变量。在constraint内调用function就称为”function in constraints”。它的格式如下:
constraint constraint_name { rand_var == function_call(arguments...); }
- function的定义写在constraint block之外,它内部包含了对arguments的处理。
- 在调用randomize()的时候,function会先被求解,function的返回值将会作为state variables去参与接下来的求解。
不过在使用function in constraints有以下几点需要注意:
- 在constraint内部调用的function不能包含output或ref类型的arguments,但是const ref是允许的;
- 在constraint内部调用的function应该是automatic类型的;
- 在constraint内部调用的function不能修改constraints,例如调用rand_mode或constraint_mode方法;
- Function会先被求解,也就是它的返回值会被当作state variables。因此,function的arguments和其它rand variables会隐含建立求解order关系(有一点点类似solve…before…),arguments会先求解,解完之后作为传入function得到返回值作为state variables,最后再求解其它rand variables。另外,如果隐含约束关系会造成求解循环依赖,那么仿真器将会报错;
顺便提一下state variables的概念,它是constraint guards的一种,我们可以把它理解成常数(constants),也就是它的值是固定的了,不会发生变化,不会创建constraint。
接下来看个例子来加深印象。
代码1如下:
class packet;
rand int length, size, add;
constraint const_c { /*solve length before size;*/ length == calc(size, add); }
constraint const_d { size inside {1, 2, 3, 4, 5, 6, 7, 8}; }
constraint const_e { add inside {1, 0}; }
function int calc(int _s, int _m);
if ( _m )
return ( 100 + 2**_s + _s);
else
return ( 100 - 2**_s - _s);
endfunction: calc
endclass
module top;
initial begin
packet pkt;
pkt = new();
repeat(3) begin
pkt.randomize();
$display("length = %0d, size = %0d, add=%0d",pkt.length, pkt.size, pkt.add);
end
end
endmodule
使用Cadence Xcelium 20.09运行结果如下:
xcelium> run
length = 120, size = 4, add=1
length = -35, size = 7, add=0
length = 80, size = 4, add=0
xmsim: *W,RNQUIE: Simulation is complete.
结果分析:
上面例子在const_c constraint block里使用了函数calc,block给calc传递的实参是size和add,因此systemverilog会先求解size和add变量的值,然后再去计算calc的返回结果,这时size, add以及calc()函数的返回值都当作state variables,最终再去求解length变量的值。
但如果将代码1里第4行的/*solve length before size;*/注释打开,也就是如下代码2:
class packet;
rand int length, size, add;
constraint const_c { solve length before size; length == calc(size, add); }
constraint const_d { size inside {1, 2, 3, 4, 5, 6, 7, 8}; }
constraint const_e { add inside {1, 0}; }
function int calc(int _s, int _m);
if ( _m )
return ( 100 + 2**_s + _s);
else
return ( 100 - 2**_s - _s);
endfunction: calc
endclass
module top;
initial begin
packet pkt;
pkt = new();
repeat(3) begin
pkt.randomize();
$display("length = %0d, size = %0d, add=%0d",pkt.length, pkt.size, pkt.add);
end
end
endmodule
那么使用Cadence Xcelium 20.09运行结果变成如下所示:
xcelium> run
xmsim: *W,RNDSVB: These solve/before constraints are circular:
0. length -> size
constraint const_c { solve length before size; length == calc(size, add); } (./testbench.sv,4)
1. size -> length
( because of an implicit solve..before for random function-call arguments ): constraint const_c { solve length before size; length == calc(size, add); } (./testbench.sv,4)
pkt.randomize();
|
xmsim: *W,SVRNDF (./testbench.sv,22|18): The randomize method call failed. The unique id of the failed randomize call is 0.
Observed simulation time : 0 FS + 0
结果分析:
这是因为solve length before size与cacl()函数创造的隐含求解order约束冲突了。(Circular dependencies)