- 子类和父类的方法调用问题:(参考绿皮书P227)
B extend A;A和B都有方法 fun,且fun前面都有virtual修饰:此时根据句柄指向的对象类型来决定调度谁的fun;
如果fun前面没有virtual修饰:则会根据句柄类来决定调用谁的fun,而不是对象类型;
多个具有继承关系的类的方法,共用同一个名字的现象即为:“多态”
- vcs 只支持 bit [10:0] XXX;
- 不支持:byte [10:0] XXX; 这种编译会报错
- vcs编译某一个filelist文件:vcs -sverilog -f xxx.f -l xxx.log
上述编译完成后会生成:simv文件;再输入./ simv进行仿真;
1)GVIM:
1、:iab lw liu wei 编辑简写;.vimrc配置
2、gvim窗口:命令模式:vsp 说明:左右分屏
3、:E 打开目录页
4、:bd :w回退窗口
2)linux:
ssh 主机名 //切换主机 or 服务器
3)verdi波形中需要下载数组信号时:在仿真中加入 $fsdbDumpMDA()或者按下图加上“”“+all”
4)根据用例名字和随机种子生成波形文件名:
在top_tb里面加入:
reg [1000:0] casename;
initial begin // 12 27
if($value$plusargs("TESTNAME_FSDB=%s",casename)) begin
$display("TESTNAME_FSDB = %s ",casename);
$fsdbAutoSwitchDumpfile(1024,casename,10);
end
$fsdbDumpvars(0,top_tb,"+all"); // +all for strunct
$fsdbDumpMDA();
$fsdbDumpon();
end
在run/Makefile加入:
RUN_OPTS += +TESTNAME_FSDB=$(tc)_$(seed).fsdb #3-14
根据用例名字和随机种子生成波形文件名:
makefile里面:(export 把fsdb_name导出)
- -ucli 是启动UCLI命令行模式,-i 吃进去 tcl 脚本文件;
- +fsdb+autofulsh,一边仿真,一边dump 波形;()
tcl部分:(set env()获得变量fsdb_name(由makefile导出的))
取int数据中的某一些比特;
资料来源:公众号-芯片学堂
1.initialize RNG
(1)初始化RNG是产生随机数的开始,用来给RNG初始化随机种子。
(2)每一个模块实例(module instance)、接口实例(interface instance)、程序块(program)和包(package)实例都有属于自己的初始化RNG,在不指定随机种子的情况下,默认的随机种子根据不同编译器的实现决定的。
2.hierarchy seeding
(1)分层分配随机种子是随机稳定性的重要机制。
(2)在创建新的线程或者实例化对象的时候,父线程使用的RNG的下一个随机值会作为这个新线程或者新对象的RNG的随机状态,即作为新的种子传递下去。
3.thread & object stability
(1)SV中将程序(program)、模块(module)、接口(interface)、函数(function)、任务(task)等这些独立的块叫Process。
(2)每个Process都有自己的RNG。每个RNG都有自己的随机状态(random state)。
(3)可以通过process::self()这个静态方法获取当前Process的RNG句柄,再通过句柄调用get_randstate()方法来获得随机状态。不同的仿真工具返回来的随机状态的值的表现方式可能会不一样,但基本都是一段看起来没有规律的字符串,这个字符串表示下一个要产生的随机数的值。
注:在复现一个执行失败的测试用例的时候,不要改动之前布下的种子,也不要改变程序中线程和对象创建的顺序,避免更改了分层随机种子的顺序。
在sv/verilog中,我们常用的是整数(int, longint),小数可以用(real),对小数的处理大致分为三种:
1. 四舍五入(如果除数和被除数均为整数,可以通过乘以1.0来实现)
2. 向上取整:系统函数$ceil(164*8/28) = 47
3. 向下取整:通过系统函数$floor实现;$floor(164*8/28) = 46
$ceil和$floor只參數接受int 和real類型,不接受bit類型或int unsigned;
随机一个数组:
int ai_port[] ;
int port_tmp[8] = '{0,1,2,3,4,5,6,7,8};
ai_port = new[7]; //初始化
ai_port_tmp.shuffle(); //打乱数组顺序,再去取前面的一部分;
foreach (ai_port[i]) ai_port[i] = ai_port_tmp[i] ;
ai_port[$] = 9 ; //对数组的最后一个元素给特定值
void'(std::randomize(qid_port) with {qid_port inside {ai_port}); //qid_port将在ai_port里面的几个值平均随机;即使里面重复多次的数,也会是均匀随机,参考绿皮书P142 例子6.14
//上面数组ai_port,也可以是队列的形式
$display的打印选项:
语句 | 含义 |
\n | 换行符 |
\t | 制表符 |
\v | 垂直制表符 |
\f | 换页符 |
\\ | \字符 |
\" | " 字符 |
\a | %字符 |
%d | 十进制输出 |
%b | 二进制输出 |
%o | 八进制输出 |
%h | 十六进制输出 |
%e | 指数形式输出 |
%f | 十进制表示方法输出实数 |
%g | 十进制或者指数表示方法输出实数 |
%s | 字符串输出 |
%l or %L | Display library binding information,显示库关联关系 |
%c | ASCII码输出 |
%m | 输出层次名 |
%v | 输出网线类型变量的强度 |
%t | 输出当前时间 |
%p | 可以打印数组(还有unpacked structures, arrays, and unions.) |
IEEE Std 1800™-2012 :The %p format specifier may be used to print aggregate expressions such as unpacked structures, arrays, and unions.
常用形式:
小数计算场景:
int i_tmp=3;
real r_real_ab;
real r_real_a = 1;
real r_real_b = 2;
i_tmp = real'(1/3); // 结果为0
r_real_ab = real'(2)/real'(3); // 结果为:0.6667
i_tmp = 4/3;// 结果为: 1
r_real_ab = 1/3; // 结果为0
r_real_ab = r_real_a/r_real_b; // 结果为:0.5
int ai_test_array[10];
int a = 4;
int b = 9;
ai_test_array [1] = $ceil(real'(9)/real'(4));// result is 3
ai_test_array [2] = $ceil(real'(9)/4) ;// result is 3
ai_test_array [3] = $ceil(9.0/4.0); // result is 3
ai_test_array [4] = $ceil(9/4.0) ; // result is 3
ai_test_array [5] = $ceil(9/4*1.0); // result is 2
ai_test_array [6] = $ceil(1.0*9/4); // result is 3
ai_test_array [7] = $ceil(9/real'(4)); // result is 3
ai_test_array [8] = $ceil(9/real'(a)); // result is 3
ai_test_array [9] = $ceil(real'(b)/4); // result is 3
字符串类型:
SystemVerilog引入了一个字符串类型(string),它是一个大小可变、动态分配的字节数组。
字符串类型变量的声明语法如下:
string variable_name [=initial_value];
如果在声明中没有指定初始值,变量会被初始化成空字符串(“”)。字符串文本由引号(“”)包围并且拥有自己的数据类型。对字符串文本的长度没有预定义的限制,一个字符串文本必须存在一个单行中,除非新的行紧跟着一个反斜杠(\)。
SV文件写入:()
integer fit_dut,fid_refm;// 声明文件的句柄
fid_refm = $fopen("data_refm.txt","w");
fid_dut = $fopen("data_dut.txt","w");
$fwrite(fid_refm,"%0h",exp_tr_0);
$fwrite(fid_dut,"%0h",act_tr_0);
//......
$fclose(fid_refm); //已验证 可行
$fclose(fid_dut);
有时候,一个类的里面定义了一套写入txt的debug代码,但是这个类会被例化成多个,所以要确保文件名不能一样;在UVM中,可以利用不能同名的实例名来作为文件的句柄;
class base_scb extends uvm_scoreboard;
string ms_rep_id;
integer mi_file_name;
function new(string name="base_scb",uvm_component parent);
super.new(name,parent);
ms_rep_id = name;//获取这个类被实例化时注册的名字,以便作为句柄名;
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
mi_file_name = $fopen({ms_rep_id,"xx_debug.txt"},"w");
endfunction
endclass
静态变量和动态变量:
静态变量只初始化一次;动态变量每次调用都会初始化;
静态变量的初始化时不可综合的;动态变量的初始化是可综合的,前提是这些动态变量只是函数内部使用,不会传递到函数外面;
变量在声明时,可以直接初始化;(int mi_abc = 99;)(在SV中内嵌初始化在仿真时刻0之前执行)(initial块则是在仿真时刻0执行)
静态task/function内的变量,缺省时也是静态的;
模块一级的变量都是都是静态的,不能显示的声明为static或automatic;(参考黄皮书P44)
在verilog中:module、begin......end块、fork.......join块以及非自动的task和function中,缺省时都为静态存储;
SV向后兼容verilog:所以上述:module、begin......end块、fork.......join块以及非自动的task和function中,缺省时都为静态存储;(这就是为什么fork.....join_none开启多个线程时,需要用到动态变量来辅助的原因吧TBD)
函数调用自身:递归函数;
一般系统函数和系统任务是不可以综合的;
@(posedge bus.clk);(这个好像会多花一拍时间)(10368ns)
@bus.monitor_cb;(这个会少花一拍时间)(10367ns)
实际场景中:如果没有统一,采用混用的话,可能会出现输出数据的顺序不一致
这两种触发有什么区别吗?
第一:@(posedge bus.clk)采的信号仿真器认为是上升沿前的数据,@bus.monitor_cb仿真器认为是触发沿后的数据;(如果所有的采样信号都已加入clocking block,那么采样的数据均为时钟沿前的数据)
第二:后一种方式在时钟信号名发生改变后只需要修改interface文件即可
执行顺序顺序问题:
fork
begin // thred1:
repeat(1) @(posedge v_glb_intf.clk);
$display("it is not #0ns");
end
begin // thred2: #0ns
repeat(1) @(posedge v_glb_intf.clk);
#0ns;
$display("it is #0ns");
end
begin // thred3:
repeat(1) @(posedge v_glb_intf.clk);
repeat(2) #0ns;
$display("it is 2 #0ns");
end
join_none
//#0ns 可以用来调整代码块的执行顺序;上述伪代码:会先执行线程1,再执行线程2,最后再执行线程3
task和function的区别:
1、function不能执行耗时语句,可以用return返回值;
2、task能执行耗时语句,不可以用return返回值;
常见的耗时语句@event 、wait event 、 #delay
function和task的区别——SV,SystemVerilog_小小verifier的博客
对某一个信号打拍(就是refm的信号延迟多少拍后再与DUT的信号比对)
//beat_num 要打的拍数
int pipe_mem_bp[]; //pipe refm_bp //solve refe_mode syn DUT signal;
pipe_mem_bp = new[beat_num];
foreach(pipe_mem_bp[i]) pipe_mem_bp[i] = 0;// initial array[]
while(1) begin
@(posedge v_glb_intf.clk); //
for(int i = 0; i < (beat_num -1); i++) pipe_mem_bp[i+1] = pipe_mem_bp[i]; // to achieve pipe beat_num clk
pipe_mem_bp[0] = refm_bp;
if (pipe_mem_bp[beat_num-1] != dut_bp) begin
//do somethng
end
end
pre_randomize()和post_randomize;
当调用 obj . randomize () 的时候,它首先调用 obj 的 pre_randomize () 方法以及它的所有被使能的随机对象成员。接下 来 pre_randomize () 会调用 super . pre_randomize () 。在新的随机值被计算并赋值后, randomize () 会调用 obj 的 post_randomize () 方法以及它的所有被使能的随机对象成员。接下来 post_randomize () 会调用 super . post_randomize ();
用户可以在任何类中过载 pre_randomize () 方法以便执行对象被随机化之前的初始化和预处理。
用户可以在任何类中过载 post_randomize () 方法以便执行对象被随机化之后的清除、打印诊断、以及后处理。
如果这些方法被过载,那么它们必须调用对应的父类方法,否则它们在随机化之前和之后的处理步骤会被忽略。
pre_randomize()和 post_randomize()方法不是虚拟的。然而,因为它们会被虚拟的 randomize()方法自动调用,所 以它们看上去像是虚拟的。
给所有bit赋值:data = 'b1; data = {32{1'b1}};
1、在使用ref修饰函数参数时(在module/program中),需要将function/task修饰为automatic,否则xrun编译不通过,VCS可以。严谨起见,还是记得加automatic。
通过ref传递参数是一个唯一的参数传递限定符,将ref与任何其他方向限定符(input/output/inout)组合应该是非法的。
对于具有静态生命的子程序,通过引用的方式传递参数是非法的。
原文链接:
关联数组:可以通过foreach遍历里面的每一个有效存在的成员;
int mi_array[string];