搭建简单的superbench
在centOS中依靠makefile和VCS搭建了自己的平台之后,可以逐步将之前记录和解决的问题付诸实践,慢慢的归纳和分享。这次要来解决的是一个经典问题,就是验证环境中的静态模块(主要是module和interface)的随机问题;
内容较多,节约时间可直接至“结论”;
我需要做一个延时模块,目的是把输入信号延时一段时间,然后输出,主要的应用场景就是模拟跨异步传输延时误差和总线传输延时误差;
因为总线不止1bit,因此要例化多个模块,那么我的需求是这样的:
1.同一个延时模块进行多次延时,每次随机值应该不同;
2.不同延时模块进行同一次延时,延时随机值应该不同;
3.随机延时值必须可控,即相同种子的两次用例结果应该是一致的;
一般来说,我们做随机,常用的函数就是几个:$random、$urandom、$urandom_range(min, max)、std:randomize(),其中第一个大家一定都知道是要尽量避免使用的,因为其不受seed控制(顺便说一下,关于这里建议还是查询下sv标准,绿皮书里的说明也有些错误),这里就重点说明下$urandom与$random好了;
关于这两个系统函数呢,其实各路英雄也总结的很多了,核心就是一句话:$random不受seed控制,$urandom受seed控制,应该避免使用前者;
因为我这次是要在静态模块中使用随机,所以要进一步明确:
1.$random在module/interface等静态模块中使用,同一模块多次例化,那么单次run时不同例化实体中每一次例化的随机值是不同的,多次run的话宏观看跟第一次run没有区别;
2.$urandom在module/interface等静态模块中使用,同一模块多次例化,那么单次run时不同例化实体中每一次例化的随机值是相同的,多次run的话宏观看$urandom会受到seed的控制;
这么说实在是太饶了,不如我来做了实验吧!
实验代码如下:
module rand_test();
int a, b;
initial begin
for (int xx=0; xx<3; xx++)begin
a = $urandom;
b = $random;
$display("%m.a = %0d, b = %0d", a, b);
#100ns;
end
end
endmodule
module harness();
rand_test U_A();
rand_test U_B();
rand_test U_C();
initial #1000ns $finish;
endmodule
make run seed=100打印结果:
(Specify +UVM_NO_RELNOTES to turn off this notice)
harness.U_A.a = 1547241734, b = 303379748
harness.U_B.a = 1547241734, b = -1064739199
harness.U_C.a = 1547241734, b = -2071669239
harness.U_A.a = 5490726, b = -1309649309
harness.U_B.a = 5490726, b = 112818957
harness.U_C.a = 5490726, b = 1189058957
harness.U_A.a = 90939801, b = -1295874971
harness.U_B.a = 90939801, b = -1992863214
harness.U_C.a = 90939801, b = 15983361
$finish called from file "../top/harness.sv", line 20.
make run seed=200打印结果:
(Specify +UVM_NO_RELNOTES to turn off this notice)
harness.U_A.a = -1061171152, b = 303379748
harness.U_B.a = -1061171152, b = -1064739199
harness.U_C.a = -1061171152, b = -2071669239
harness.U_A.a = -1209238210, b = -1309649309
harness.U_B.a = -1209238210, b = 112818957
harness.U_C.a = -1209238210, b = 1189058957
harness.U_A.a = -183526848, b = -1295874971
harness.U_B.a = -183526848, b = -1992863214
harness.U_C.a = -183526848, b = 15983361
$finish called from file "../top/harness.sv", line 20.
观察两次的结果可以看出来:
如果使用$urandom(a使用的),同于一个例化模块而言,的确单次run中多次随机值不同,多次run时候也是不同的且能受seed控制,但是同个模块的多次例化同一次随机却是一样的,根本没能起到不同总线随机延时的功能;
如果使用$random(b使用的),能起到不同总线随机延时的功能,但是对于同一个例化,多次run时候随机值宏观看是一样的且不能受seed控制;
$urandom_range(min, max)、std:randomize()甚至randcase都和$urandom有相近的性质,也无法解决问题;
要解决这个问题,还是先把在$random上做文章的心思抛开了,这个系统函数本身就是个不受seed控制的随机数发生器,无论如何也进化不了了;
所以需要$urandom上做文章,寻找同一个例化模块的不同点;
我想了挺长时间,终于在吃香菇鸡米饭的一个中午豁然开朗!每个模块的例化路径不是一定不重样的么,这个不就可以作为我随机的基石么!!想明白了之后我又上网一查,发现显然不是我一个人想明白了,原来也有人探索过类似的做法了,不过没关系,前人的经验也可以为我所用嘛!
按照这个思路,我们来获取下每个inst的路径,并转为数值;获取路径的话很简单,有一个神奇的标识符——%m,说实话,自从我认识了%m后我就感觉人生都亮了,所以改一改module rand_test()的代码:
int a, b;
string path_str;
reg [2047:0] path_int;
initial begin
path_str = $psprintf(path_str, "%m");
path_int = path_str;
$display("path_str=%s, path_int=%0d", path_str, path_int);
end
$psprintf也可以考虑使用$sformat(path_str, "%m")替代,harness代码不变,打印结果如下:
(Specify +UVM_NO_RELNOTES to turn off this notice)
path_str=harness.U_A, path_int=126188465672475447999684417
path_str=harness.U_B, path_int=126188465672475447999684418
path_str=harness.U_C, path_int=126188465672475447999684419
$finish called from file "../top/harness.sv", line 29.
普大喜奔!确实能找到不一样的随机量了,但是有个问题啊,这个path_int只有后几位是不一样的,这区分度也太小了吧?
因此我引入了以下几个“扩大”随机的方法,先把代码放上来:
1 string path_str;
2 int path_int;
3 initial begin
4 path_str = $psprintf(path_str, "%m");
5 path_int = $urandom();
6 for(int i=path_str.len; i>=0; i=i-1)begin
7 path_int = path_int ^ path_str.getc(i);
8 path_int = $urandom(path_int);
9 end
10 path_int = $abs(path_int);
11 $display("path_str=%s, path_int=%0d", path_str, path_int);
12 end
1.line5,先把path_int通过$urandom给一个初始随机值,即使三个模块U_A、U_B、U_C是一样的也没关系,这个值结合后面路径的微小差异,就可以成就“蝴蝶效应”;
2.line6,从尾部-根部遍历路径字符,因为差异集中在尾部;
3.line7,str.getc(i)获取字符串对应位置字符的ASCII码,再跟path_int取异或,累计扩大差异性;
4.line8,进一步扩大差异性;
5.line10,实验过程中发现最后的结果可能是负数,这不是我所需要的,因此通过$abs函数全部取正;
好的,让我们来看看结果,seed = 100:
(Specify +UVM_NO_RELNOTES to turn off this notice)
path_str=harness.U_A, path_int=137603673
path_str=harness.U_B, path_int=976424504
path_str=harness.U_C, path_int=1319816804
$finish called from file "../top/harness.sv", line 36.
seed = 200:
(Specify +UVM_NO_RELNOTES to turn off this notice)
path_str=harness.U_A, path_int=1872717276
path_str=harness.U_B, path_int=548080505
path_str=harness.U_C, path_int=1711115269
$finish called from file "../top/harness.sv", line 36.
再来一次seed = 100:
(Specify +UVM_NO_RELNOTES to turn off this notice)
path_str=harness.U_A, path_int=137603673
path_str=harness.U_B, path_int=976424504
path_str=harness.U_C, path_int=1319816804
$finish called from file "../top/harness.sv", line 36.
稳定复现,还有比这更完美的事么!
可能有,后来我想了想,如果我是path_int = $urandom($urandom($urandom($urandom(path_int)))),是不是一样可以把微小的差异扩大啊?
进一步的,我把这个成果封成function+宏,以便之后取用:
`define module_urandom_define \
string path_str; \
initial path_str = $psprintf(path_str, "%m"); \
\
function integer urandom; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom = $abs(seed); \
end \
endfunction
module rand_test();
int a, b;
`module_urandom_define;
initial begin
for (int xx=0; xx<3; xx++)begin
a = urandom();
b = urandom();
$display("%m.a = %0d, b = %0d", a, b);
#100ns;
end
end
endmodule
module harness();
rand_test U_A();
rand_test U_B();
rand_test U_C();
initial #1000ns $finish;
endmodule
在module中直接`module_urandom_define后,就可一直接使用函数urandom(),seed = 100时打印结果如下:
(Specify +UVM_NO_RELNOTES to turn off this notice)
harness.U_A.a = 1471040728, b = 863921358
harness.U_B.a = 1338760535, b = 342532348
harness.U_C.a = 1904052621, b = 2077234749
harness.U_A.a = 1485973489, b = 1802195813
harness.U_B.a = 1046096403, b = 1198609932
harness.U_C.a = 968193349, b = 152370968
harness.U_A.a = 2088965477, b = 1393854800
harness.U_B.a = 1045163969, b = 1599575068
harness.U_C.a = 1987425378, b = 1015766576
$finish called from file "../top/harness.sv", line 42.
seed = 200时打印结果如下:
(Specify +UVM_NO_RELNOTES to turn off this notice)
harness.U_A.a = 786960723, b = 1012746144
harness.U_B.a = 993085640, b = 327905961
harness.U_C.a = 175655228, b = 341306910
harness.U_A.a = 1769729887, b = 1776605454
harness.U_B.a = 1756385201, b = 1608046143
harness.U_C.a = 601311374, b = 1036857959
harness.U_A.a = 1325663168, b = 248563413
harness.U_B.a = 1528493527, b = 399659340
harness.U_C.a = 2138557678, b = 1356991027
$finish called from file "../top/harness.sv", line 42.
目前看没有什么问题,因此我决定再多封装一个函数urandom_range():
`define module_urandom_define \
string path_str; \
initial path_str = $psprintf(path_str, "%m"); \
\
function integer urandom; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom = $abs(seed); \
end \
endfunction \
function integer urandom_range(); \
input integer min, max; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom_range = min + $abs(seed % (max - min)); \
end \
endfunction
module rand_test();
int a, b;
`module_urandom_define;
initial begin
for (int xx=0; xx<3; xx++)begin
a = urandom();
b = urandom_range(10,200);
$display("%m.a = %0d, b = %0d", a, b);
#100ns;
end
end
endmodule
打印结果如下:
seed = 100
(Specify +UVM_NO_RELNOTES to turn off this notice)
harness.U_A.a = 1471040728, b = 108
harness.U_B.a = 1338760535, b = 168
harness.U_C.a = 1904052621, b = 99
harness.U_A.a = 1485973489, b = 33
harness.U_B.a = 1046096403, b = 72
harness.U_C.a = 968193349, b = 98
harness.U_A.a = 2088965477, b = 180
harness.U_B.a = 1045163969, b = 38
harness.U_C.a = 1987425378, b = 176
$finish called from file "../top/harness.sv", line 51.
seed = 200
(Specify +UVM_NO_RELNOTES to turn off this notice)
harness.U_A.a = 786960723, b = 174
harness.U_B.a = 993085640, b = 171
harness.U_C.a = 175655228, b = 40
harness.U_A.a = 1769729887, b = 14
harness.U_B.a = 1756385201, b = 153
harness.U_C.a = 601311374, b = 39
harness.U_A.a = 1325663168, b = 103
harness.U_B.a = 1528493527, b = 50
harness.U_C.a = 2138557678, b = 17
$finish called from file "../top/harness.sv", line 51.
从结果来看,应该是满足之前提到的三个要求的:
1.同一个延时模块进行多次延时,每次随机值应该不同;
2.不同延时模块进行同一次延时,延时随机值应该不同;
3.随机延时值必须可控,即相同种子的两次用例结果应该是一致的;
最后我交出的代码就是这样的:
`define module_urandom_define \
string path_str; \
initial path_str = $psprintf(path_str, "%m"); \
\
function integer urandom; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom = $abs(seed); \
end \
endfunction \
function integer urandom_range(); \
input integer min, max; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom_range = min + $abs(seed % (max - min)); \
end \
endfunction
module rand_test();
int a, b;
`module_urandom_define;
initial begin
for (int xx=0; xx<3; xx++)begin
a = urandom();
b = urandom_range(10,200);
$display("%m.a = %0d, b = %0d", a, b);
#100ns;
end
end
endmodule