Maggie • 来自相关话题

19天前

验证环境中经常使用的参数$test$plusargs和$value$plusargs

   $test$plusargs和$value$plusargs是Verilog和SystemVerilog仿真运行时调用的系统函数,可以在仿真命令行直接进行赋值,并且不局限于不同仿真器对于参数在仿真命令中定义格式不同的限制,也避免了调换 ...查看全部

   $test$plusargs和$value$plusargs是Verilog和SystemVerilog仿真运行时调用的系统函数,可以在仿真命令行直接进行赋值,并且不局限于不同仿真器对于参数在仿真命令中定义格式不同的限制,也避免了调换参数带来的频繁编译等问题。使用这两条函数对于搭建测试平台有一定的便利,同时对于理解Factory中用例是如何传递进Proxy Class有一定的帮助。

    本文将对$test$plusargs和$value$plusargs使用过程中遇到的问题进行小结。

    首先,在进行宏定义时,我们经常使用`ifdef等命令在代码中,参看下例:

     test.v

     initial begin

          `ifdef dumpon

                $dumpfile("results.vcd");

                 $dumpvars;

             `endif

         end

如果要能够成功调用$dump等函数,需要在编译(compile)时指定`define的宏定义,其使用方法如下:

 -define dumpon test.v

    但是,在仿真过程中不需要该部分定义时该如何处理呢?

当需要改变编译条件时,经常需要重新编译。并且一旦编译通过,在编译阶段指定的宏定义在整个仿真运行过程中一直有效,因此,如果需要修改宏定义,则需要重新进行编译,从而降低了仿真的效率。

   为此,可以使用$test$plusargs和$value$plusargs进行解决,该函数的调用发生在仿真运行(run)阶段。这样仅需要对设计进行一次编译即可,如果需要改变相应的条件,可以在run的时候动态指定,这样有利于脚本处理进行回归的验证,同时也有利于object的动态construct。


1.$test$plusargs

    在运行(run)仿真时指定要选择的条件,即只需要在仿真运行命令(run-options)中指定参数需要选择的条件即可,例如下例中,如果要将test01.dat、test02.dat、test03.dat分别load到各自的men中,仅需要如下命令在运行命令中加入“<+test01+test02+test03>”即可,当仿真运行时,$test$plusargs会在命令行中搜索指定的字符,若找到相应字符,在函数返回“1”,否则返回“0”。如果下次仿真时不需要test01时,仅需要将test01从运行命令中删除即可。

     test.v

      initial begin

           if($test$plusargs("test01"))

               $readmemh("test01.dat",mem01);

           if($test$plusargs("test02"))

                $readmemh("test02.dat",mem02);

           if($test$plusargs("test03"))

                $readmemh("test03.dat",mem03);

          end

+test01+test02+test03...


2.$value$plusargs

$value$plusargs可以讲运行命令(run-options)中的参数值传递给指定的信号或者字符,其语法格式如下:

Integer=$value$plusargs(“string”,signalname);

其中string=”plusarg_format”+”format_string”,”plusarg_format”指定了用户定义的要进行传递的值,”format_string”指定了要传递的值的格式(类似$display中定义的%s、%h、etc.),并且string中”plusarg_format”和”format_string”格式应该为”plusarg_format”(=/+)”format_string”。如果转换后的位宽和传递的值不一致,则按照如下规则转换:

plusarg位宽与sigalname的关系Signalname值
<
plusarg左补零
>plusarg截位
plusarg为负数按照正数处理
不匹配若为指定默认值,则reg类型为x

$value$plusargs使用示例如下:

        test.v

         if($value$plusargs("FINISH=%d",stop_clk)) begin

                  repeat(stop_clk) @(posedge);

                  $finish;

                end

             if($value$plusargs("TESTNAME=%s",testname))

                begin

                    $display("Running test %0s",testname);

                     ...

                 end

              if($vaue$plusargs("FREQ=",frequency)) begin

                   frequency=8.333333;

                   ...

          end


若使用的运行命令如下:

    +FINISH=10000+TESTNAME=this_test+FREQ=5.6666

则上例的运行结果为:

    stop_clk : 10000

    testname:this_test

    frequency:5.6666(如果run-options中没有增加“FREQ=5.6666”,那么frequency为8.333333)。

芯片家 • 来自相关话题

26天前

Soc debug经验<4>

  在soc芯片验证的时候,有时候需要我们验证一段数据通路,我们需要发送一个数据包,让这笔数据包从起始端,到达我们想要的目的端。这里有两个比较重要的点是需要注意的,第一个点是如何保证这笔数据包按照我们想要的数据通路走;第二个点是如果已经按照我们想要的 ...查看全部

  在soc芯片验证的时候,有时候需要我们验证一段数据通路,我们需要发送一个数据包,让这笔数据包从起始端,到达我们想要的目的端。这里有两个比较重要的点是需要注意的,第一个点是如何保证这笔数据包按照我们想要的数据通路走;第二个点是如果已经按照我们想要的数据通路走下去了,如何保证这笔数据能成功穿过每一个模块而不丢掉。

1.       如何保证这笔数据按照我们想要的数据通路走。

例如数据通路A<->B<->C<->D<->E。我们想让数据通过A->B->C->D->C->B->F。在Soc芯片中,如果我们想从A模块发送数据到F中,在编译的时候需要A模块是一个A模块的UVC,也就不是A模块的rtl代码,这样我们就可以利用A模块的UVC发送数据包 。从A发出来的数据包到达了D之后,可以有两路通路,一个是到E模块,另一个可以是从D返回去,也就是D->C->B->F,到达F模块。那这里如何保证数据包到达D模块之后,让数据包沿着D->C->B->F,到达F模块。

     在模块D中,会有各种各样的Decoder小模块,这些Decoder决定了到达模块D之后,数据包接下来的走向。这些Decoder是根据来源不同划分的,比如从模块A发出来的数据包可以叫A_Decoder,从其他模块A1发出来的,可以叫A1_DecoderAA1大概率属于不同类型的模块。这两个Decoder的逻辑很相似,里面都会有一个信号destination,该信号的值决定了要到达哪一个模块。

   举例:

   assign Destination= ~enable? 0:F_mem? `to_F : E_mem? `to_E: 0;

可以看到,我们要想让数据包成功到达F,必须让F_mem拉高。我们继续追踪F_mem

可能会发现这个F_mem的逻辑是对地址的范围判断。

   举例:

   assign F_mem=(addr >=F_mem_low)&&(addr<=F_mem_high);

这里就是说,我们从A模块发送到F模块的地址是有限制的,如果这个地址在F模块的memory区间里,接下来D模块就会发送数据到F,沿着D->C->B->F模块走。这里可能牵扯到在我们的case 里面,要先配置F_mem_low F_mem_high之后,才去从模块A发送数据包。如果你知道配置哪一个寄存器可以配置F_mem_low F_mem_high,那更好了,如果你不知道配置哪一个寄存器,这也没有关系,你可以读完接下来我要说的第二点去找到该寄存器。

 

2.       如果已经按照我们想要的数据通路走下去了,如何保证这笔数据能成功穿过每一个模块而不丢掉。

       假设我们想验证A->B->C->D->C->B->F这段数据通路。从模块A发送出来的地址到达模块C 的输入端口之后,这个时候发现数据包在CD的输出端口上没有了。我们去追踪该输出地址接口,可能会发现如下逻辑。

  Case (state)

  `idle:

       If(enable)

        begin

            C_D_addr<=C_D_addr_;

            state<=`send;

        end

   `send:

        …..

     我们可能发现C_D_addr_是有数据的,但是这个enable是等于0的,这个状态机只停留在idle状态,C_D_addr没有数据。

    这里enable没有起来,所以我们会继续追踪enable为什么没有被拉起来。(这个就像第一个点里面F_mem_lowF_mem_high没有数据是一样的)我们一直追踪enable,会发现如下decoder代码。

case(addr[7:0])

20:  enable_C_D_reg_control <=1;

30:  …….

…….

    我们如果追踪addr,能看到完整的地址,在波形中的这些地址都是我们已经配置的寄存器,这里显然没有我们想要的那个寄存器。实际也没有必要去追踪addr了。首先我们要明白,enable_C_D_reg_control是我们要配置寄存器的一个域,然后要配置寄存器地址的后面8位是20。所有寄存器地址define一般都会放在一个文件里。我们可以通过匹配一些关键词去找到该寄存器。

格式如下:

` enable_C_D_reg  10122120

 …….

    比如我们找到了该寄存器叫enable_C_D_reg,我们还需要确定域enable_C_D_reg_controlenable_C_D_reg是第几位。一般情况下,我们可以去查询寄存器的spec,去了解一下enable_C_D_reg寄存器,也可以通过查询UVM的寄存器模型去了解该寄存器。假设我们查到了该域enable_C_D_reg_control32位寄存器enable_C_D_reg的第二位。那在我们从A端发送数据包之前,需要在自己的case cpp里面配置该寄存器。每个验证环境配置具体函数不一样,但是大致思路都是一样的。配置思路代码如下:

data= api.read(enable_C_D_reg);

data=data|0x00000002;

Api.write(enable_C_D_reg,data);

芯片家 • 来自相关话题

29天前

IC验证--Systemverilog之assetionVCS/Verdi实战

    最近top level的同事提了验证需求,让我在RTL中加assert coverage,开始懵逼,后来查阅一些资料和代码,很快上手了。 这里用一个小例子记录一下,免得日后忘记了。先上代码://tb_top.sv的内容:module ...查看全部

    最近top level的同事提了验证需求,让我在RTL中加assert coverage,开始懵逼,后来查阅一些资料和代码,很快上手了。 这里用一个小例子记录一下,免得日后忘记了。先上代码:
//tb_top.sv的内容:
module tb_top();
reg clk, rst;
wire [3:0] out;
always #5 clk=~clk;
initial
begin
rst=1'b0;
clk=1'b0;
#30;
rst=1'b1;
#150;
$finish;
end
always@(out)
$display('counter is %d',out);
counter(.reset(rst),
.clk(clk),
.q(out));
endmodule
module counter(reset,clk,q);
input reset,clk;
output[3:0] q;
reg[3:0]q;
reg[3:0]count;
always@(posedge clk)
if(!reset) q<=4'b0;
else if(q==4'b1111)
q<=4'b0;
else
q<=q+1;
property p1;
@ (posedge clk) (q[0]|-> ##5 q[3]);
endproperty
a1: assert property(p1)
$display('succeed! time=%t',$time);
else
$display('failure! time=%t',$time);
;
c1: cover property(p1);
endmodule
这里是编译仿真以及查看覆盖率的命令们: r> vcs -sverilog -R -fsdb -ucli -do run_vcs.tcl -cm line+branch+assert tb_top.sv
urg -dir simv.vdb
verdi -workMode hardwareDebug -ssf counter.fsdb -f test_coverage.f
dve -cov -dir ./simv.vdb
verdi -cov -covdir ./simv.vdb
log 结果是:
ucli% run
counter is 0
counter is 1
counter is 2
counter is 3
counter is 4
counter is 5
counter is 6
counter is 7
'tb_top.sv', 41: tb_top.counter.a1: started at 45s failed at 95s
Offending 'q[3]'
failure! time= 95
counter is 8
counter is 9
succeed! time= 115
counter is 10
counter is 11
succeed! time= 135
counter is 12
counter is 13
succeed! time= 155
counter is 14
counter is 15
succeed! time= 175
$finish called from file 'tb_top.sv', line 14.
$finish at simulation time 180
'tb_top.sv', 41: tb_top.counter.a1: started at 165s notfinished
'tb_top.sv', 41: tb_top.counter.a1: started at 145s not finished
'tb_top.sv', 46: tb_top.counter.c1: started at 165s not finished
'tb_top.sv', 46: tb_top.counter.c1: started at 145s not finished
'tb_top.sv', 46: tb_top.counter.c1, 18 attempts, 4 match
---------------------------------------------------------------------------
  VCS Coverage Metrics: during simulation line, branch was monitored
先看波形,可以看到和log对应,95ns处fail, 115,135,155,175ns处成功触发了assertion的内容。后面天蓝色线表示145/165ns处开始但是还没有结束的监测。

亮.jpg

run_vcs.tcl 的内容是:
config timebase 1ns
scope tb_top
run 1ns
call \$fsdbDumpfile(\'counter.fsdb\');
call \$fsdbDumpvars(0,tb_top);
call \$fsdbDumpSVA;
# Run to completion
run
quit
注意,这里 “call \$fsdbDumpSVA;”表示控制dump SVA的东西到fsdb文件去,如果没有这一句,波形文件counter.fsdb里面就不会有a1/c1 .
所有的控制fsdb dump方式都有两种,这个也不例外,你还可以在TB顶层加如下语句去控制dump SVA的东西:
initial begin
$fsdbDumpSVA;
end
  但是个人不倾向于使用这种方式,因为这样就等于代码过多和EDA工具捆在一起了,看起来不干净,不易读。
  另外再看看coverage 报告:(dve -cov -dir ./simv.vdb),可以看到名字为c1的coverage有被cover到,说明这种序列在此次test中被cover了四次。特别要说明的是,通常assert property() 和 cover property()会成对出现,表示我有这样的信号时序行为要求,如果违规assert就要报错,同时还要求我这种时序行为一定要cover到,没有cover到表示我这种时序行为没有测到,coverage report里会显示没有cover到。所以 cover property()也是要写的,否则assert没有报错并不代表说这种“时序行为”测到了,可能根本没有触发这个序列的第一个条件。

亮2.jpg

  例如我把上面代码中counter更新的逻辑删除,那么counter reset成0 一直保持,那么assetion根本不会报错,因为根本没有触发p1里面的q[0]==1, 下面是log(同时log里能看出来c1根本没有cover到:18 attempts, 0 match):
ucli% run
counter is 0
$finish called from file 'tb_top.sv', line 14.
$finish at simulation time 180
'tb_top.sv', 42: tb_top.counter.c1, 18 attempts, 0 match

菜菜 • 来自相关话题

1月前

西安、北京--数字IC设计工程师、数字ic验证工程师、RFIC、FPGA、

西安、北京、数字IC设计工程师、数字IC验证工程师、RFIC、FPGA,欢迎自荐和推荐微信:861112547

西安、北京、数字IC设计工程师、数字IC验证工程师、RFIC、FPGA,欢迎自荐和推荐


微信:861112547

芯片家 • 来自相关话题

1月前

Soc芯片debug 经验<3>

在介绍bug之前,继续讲解一下在在wave中debug中经常用到的小技巧。1,标亮Wave中的信号选中Wave中的信号(左键点击一下),按下字母c,打开“Change Color”对话框。选择想要的颜色。2,Bus Operations   ...查看全部

在介绍bug之前,继续讲解一下在在wave中debug中经常用到的小技巧。

1,标亮Wave中的信号

选中Wave中的信号(左键点击一下),按下字母c,打开“Change Color”对话框。选择想要的颜色。

2,Bus Operations

   右键点击信号,选择Bus Operations,可以看到很多Bus操作。这里着重讲解一下Create Bus 操作。例,在dut中定义了一个信号addr [39:4],该信号在wave中只显示位宽[39:4],该地址只是case中写地址的一部分,我们要想确定该地址addr[39:4]是不是对应着case中的写地址,我们可以打开Create Bus对话框,使用Add Logic Low按钮,补全低四位。这样在Wave中的信号列表中显示的数据就是完整的case 中的地址。

3,Waveform->Signal value Radix

Wave中拉出来的信号默认显示是16进制,但dut中有时候会使用十进制的数据10’d等。使用Signal value Radix 可以改变该信号显示的进制。

接下来开始讲解Soc 验证中遇到的问题。

       1. 在soc系统中,集成了很多IP,有时候需要验证一个IP发送出去数据包的datapath是否正确。一般情况下,使用的是该IP的UVC去发送数据包,编译的是该IP的一个model,里面只有接口,没有实际的功能,把该IP的model 输入输出接口,连接到该IP UVC的interface上,这样如果启动验证环境,IP UVC的接口上有了数据包,就相当于该IP 发送了数据包。该IP数据包的验证通路要想成功的到达最终的地方,在发送该数据包之前,需要配置很多关键的寄存器。如果没有配置该寄存器,该数据可能在中途就会丢失。导致该case不能成功到达。

举例:A->B->C数据通路,ABC之间的数据传递使用的协议接口,A 的输出连接到B的输入,并且A的数据成功的传递到B的输入上,但是在B内部,经过复杂的逻辑之后,B没有了输出。我们在波形中一直去追踪B的输出,可能最终会发现如下代码,信号名字都是随手写的。

If (cntl)

   Valid<=iwdata[3];

我们可能会发现cntl没有起来,这个说明我们要配置一个寄存器的第3位,把第三位置为1,具体是哪一个寄存器,我们可以继续追踪cntl。我们可能会发现如下代码。

case (x_cntl_x)

    404: cntl=1;

    403:cntl_a=1;

default: cntl_b=1;

endcase

          这段代码,我们可以看到,寄存器的地址偏移是404,关键词是x_cntl_x。这个时候,我们就可以去寄存器地址文件表(一般情况下,所有的寄存器地址定义的define都在一个文件里)里去匹配关键词x_cntl_x,去找到后缀是404的寄存器。然后在自己的case里面,先去配置该寄存器,把该寄存器的域3,配置成1。我们也可以去看看对该寄存器的域的说明,去UVM寄存器模型里面搜索该寄存器,会找到该寄存器的所有域的说明以及总的位宽。假设该寄存器的位宽是32,我们可以通过读出该寄存器的值,然后把值和32‘h0008相或,把或之后的值写入该寄存器即可。代码如下。

         read_data=read_cntl_reg(xx_cntl_xx);

          read_data=read_data|32’b0008;

          write_cntl_reg(xx_cntl_xx, read_data);

   2. 在编写数据包的时候,数据包是要连接到IP uvc 的interface上的,这些interface上的接口都是协议接口,协议接口文件里面定义了很多assertion,也有部分assertion是单独编写的,单独例化的。这些assertion可能对于写数据的一些属性有固定要求或者有约束范围,如果我们没有按照要求给激励,在仿真环节会出现违背assertion 的error。

  例如错误现象:UVM_ERROR: $datapath/src/verif/assertion.sv #16.

       A==0

Hierarchy.a.b.c.A

我们通过Hierarchy.a.b.c.A 在波形Wave中拉出来信号A,发现他是0。然后我们打开文件assertion.sv,找到了assertion的第16行对A进行了assertion约束。具体写法略。

我们去追踪该信号,可能会发现如下例化代码。

.A(write_valid&&write_AWSIZE!=3)

我们就可以发现我们在写协议包属性的时候,把AWSIZE写成3了。最后我们要去查看该IP对应的技术文档,去确定技术文档里面,这个接口信号是不是标注了不能等于3,如果找不到,要去找对应的IP 部门去确认修改。

3. 在Soc系统中,有时候不止使用了一种协议,可能是多个协议。这里面就有了协议的转换。例如A->B->C这么一段datapath,A的输入接口是AXI协议,A的输出是其他类型协议,这样在A内部就有一个协议转换。如果发现A的输出valid没有起来,那我们一直追下去,可能会发现如下代码。

 Always @ (posedge clk)

      if(axi_valid)

         valid<=1;

我们把axi_valid和clk在波形中拉出来,发现axi_valid和clk都有,但是axi_valid翻转的脉冲很短,这个clk相对较慢,在clk的上升沿,axi_valid完美的错位过去了。这个现象是由于产生axi_valid的时钟是一个比较快的时钟,那这个axi_valid如果出现在clk上升沿之内,valid可能无法正确获得1。这个需要把问题发出来让其他部门的人一起来看看。

芯片家 • 来自相关话题

2月前

最近做的ECO和如何使用verdiload原理图辅助做ECO

        好几年没做过ECO了,再次做起来比较痛苦,有一种清零后重新捡起来的感觉。笨办法做ECO当然是对照网表和RTL,把网表相关寄存器的逻辑都画出来,然后化简分析,找打做ECO的点,然后再手工做。但是曾经在上一家 ...查看全部

        好几年没做过ECO了,再次做起来比较痛苦,有一种清零后重新捡起来的感觉。笨办法做ECO当然是对照网表和RTL,把网表相关寄存器的逻辑都画出来,然后化简分析,找打做ECO的点,然后再手工做。但是曾经在上一家公司(业内养老单位,已经倒闭)做ECO的时候好歹是用“先进”的verdi工具辅助做。不必说用nECO里面的tcl脚本做,起码能帮忙load fan-in/out-core,加速分析吧。
        但是我在使用verdi工具上摔了跟头,不知道怎么load那种带原理图的netlist,奈何问了几个老同事也都告诉我verdi load了网表自然就有那种与非门图形库了。现在我想告诉这几个老同事“编,使劲编”,根本就是大家作为大公司的designer,不管flow,人家都给做好了,你load进来看起来都是好的,实际上verdi是需要load netlist+symbol_lib才能展示如下可爱的模样:

1.jpg

不然只load netlist,就是这个鬼样子:

2.jpg

     不买关子了,怎样建立图形库呢? verdi提供了一个工具,叫做syn2SymDB,这个工具可以把S公司的库转换成symbol库给verdi使用。

在我的main tree目录/proj/usr/my_main 执行如下命令
syn2SymDB -o tsxx_xx *.tsxx.0pxv?00c.lib.gz *.tsxx_FTx.0pxv?00c.lib.gz
setenv NOVAS_LIBS tsxx_xx
setenv NOVAS_LIBPATHS /proj/usr/my_main
然后再用verdi load 网表,找到你关心的逻辑单 元,再去tools->new schemetic->fan-out-core之类的,漂亮的带与或非门的原理图就呈现出来了。
同时顺便总结下此次ECO的教训,对比RTL前后改动,找到需要修改的点,注意:
1. 改动不要影响其它寄存器,看看你的改动点所有扇出,改动完了之后再检查一遍(load到verdi之类的工具里trace一遍)
2. 如果报了falling point点,有可能是以下原因:
a. 理解错或者用错cell了,在tech/verilog/stdcell/目录里一般有(我这次就用错了,该是与非门ND2,用成了与门 AND2)
b. 你的改动影响到了别的寄存器(我这次n42578改动是给A寄存器的,结果影响到了另外一个寄存器B)
c. 少改了某一处,所以还要反复对照前后改动的RTL(这次我发给flow owner的信里列举的RTL改动点就没列举完全)

Maggie • 来自相关话题

2月前

关于芯片验证的感悟

 18年下半年参与了某款5G芯片验证的开发过程,空余时间总结一下: 由于现在芯片的规模越来越大, 所以导致芯片验证的工作也越来越重要。 传统的通过写TB 的方式来测试芯片设计功能的方式也越来越吃力,而通过UVM的方式来进行芯片验证已经成为业 ...查看全部

 18年下半年参与了某款5G芯片验证的开发过程,空余时间总结一下:

 由于现在芯片的规模越来越大, 所以导致芯片验证的工作也越来越重要。 传统的通过写TB 的方式来测试芯片设计功能的方式也越来越吃力,而通过UVM的方式来进行芯片验证已经成为业界主流的验证方式。

 使用UVM的方式可以编写任意多个test case 配合对应的 sequence ,加上shell 脚本的辅助,可以在线切换任意的phase场景进行测试,增加了验证的自动化和灵活性,功能覆盖性也大大增强。

1. UVM 验证平台的搭建。

 一个UVM 验证仿真平台的搭建 ,需要一个整体的tb顶层来进行DUT 的封装,时钟和复位的激励生成,以及数据的DUMP 操作,应用的 virtual interface (VIF) 和 总线接口(AXI)的构建和初始化,以及和DUT的接口连接。  

 接下来编写各个应用场景对应的sequence 和test case ,通过 UVM的机制来进行指定test case 的仿真运行和测试。

 sequence 主要负责 参数的随机化生成,寄存器值的初始化,以及仿真的运行时间控制;test case 主要负责 UVM ENV component的初始化,以及在 main phase 中 通过对应的 sequence 来手动开启(关闭) 整个UVM平台的运行(采用 raise objection的方式)。这里可以采用基类构建框架, 继承类 实现具体动作的方式来增加灵活性。 我们这里还在sequence body 里面,调用了refer-C DPI 的功能,来运行C 功能。

  env component 模块主要负责各个子component(agent , moniter ,scoreborad )的集成, 这里我们使用了AXI 总线,所以这里我们也集成了AXI的一些模块(register model , adapter , VIP )。agent component 集成了sequencer 和 driver 组件 ,sequencer模块功能比较简单,负责将sequence 里面的transcation形式的数据发送给 driver ;driver组件总要负责将transcation形式的数据解析给 virtual interface ,进而传输给了DUT 。

  moniter 组件负责将DUT 的输出打包成transaction的形式,传送给scoreboard 组件。

  scoreboard组件负责 将DUT 的数据存储到queue中,同时将refer-C 输出存储到queue中,完成数据的比对。

无标题.png

1.2 寄存器读写 

  这里 主要讨论 AXI的寄存器读写 。由 sequence来完成数据的随机化操作 ,然后写到 register model 中,通过 adapter 来完成 总线和寄存器的转换,最后传送到 VIP 中,输出以 axi_vif 的形式传送给DUT的 AXI 接口。

2.png

1.3 phase的理解

   对于UVM中的component , phase的理解 才能正确使用各个组件。

   build phase 主要 完成 组件内各个子组件的创建 ;

   connect phase 主要完成 该组件与外部组件的连接;

   无时钟消耗的phase:build phase ,connect phase ,before simulation 

   在 sequence raise_objection 某一个 sequencer 后,在各个组件的main_phase 产生时钟消耗。

   关于 main_phase 的流水线设计 :

   这里 需要知道 UVM各个组件是长久活动的,在各个组件main_phase 中也需要 设计成 RTL流水线形式:

   在main_phase 采用 fork  ··· join 来保证先进行具体活动的实行 ;采用 forever  @posedge(clk) 来进行流水线设计 (等同于 RTL 设计的 always@(posedge clk ))

   在 sequence drop objection 来关闭 。

1.4 refer-c 和DPI 接口设计  

   SV 方面:

   如果SV 和 C++ 交互为对象 :import “DPI-C" function chandle getWrapperInstance() ; 来获取对象 ;

   如果SV 和C++ 交互为函数:import "DPI-C" function void systemSetup (input chandle systemInst ,...) 来传送数据给refer-C

   C/C++ 方面:

   添加与SV交互的接口代码 :dpi_main.cpp

   修改C/C++ 代码,GDB 调试 ,最后集成到 UVM 中 

   Makefile :

   添加输入参数给 SV ,环境配置  

   shell:

   test case 的自动化配置,参数传递给 Makefile

左拉呀 #招聘 • 来自相关话题

2月前

北京智能可穿戴创新公司-Digital Verification Engineer  

岗位Digital Verification Engineer Design and Integration Engineer 要求:5年以上,有RISC-V经验者优先欢迎详细了解微信:1370621224 ...查看全部

岗位

Digital Verification Engineer

 

Design and Integration Engineer

 

要求:5年以上,有RISC-V经验者优先

欢迎详细了解

微信:1370621224

Maggie • 来自相关话题

2月前

Testbench/UsingFactoryOverrides

       UVM factory允许在构造时将类替换为派生类型的另一个类。这对于通过将一个类替换为另一个类而无需编辑或重新编译测试平台代码来更改测试平台的行为来说是非常有用的。为了使factory重载过程起作用,需要 ...查看全部

       UVM factory允许在构造时将类替换为派生类型的另一个类。这对于通过将一个类替换为另一个类而无需编辑或重新编译测试平台代码来更改测试平台的行为来说是非常有用的。为了使factory重载过程起作用,需要遵循许多编码约定先决条件,这些在UVM factory的文章中进行了解释。
       UVM factory可以被认为是查找表。当使用 :: type_id :: create(“”,)方法(approach)进行“普通”组件构造时,会发生的是type_id用于为类选择factory组件包装器, 构造其内容并再次传回结果句柄。factory重载更改了查找的发生方式,因此查找原始的type_id会导致使用不同的type_id。因此,返回不同类型的构造对象的句柄。此技术依赖于多态性,即能够使用基本类型句柄引用派生类型的能力。实际上,只有当父类被类扩展层次结构中的一个子类重载时,重载才会起作用。

       Component Overrides 

     UVM中有两种类型的组件重载 - type overrides 和 instance overrides。  

       Component Type Overrides  

       type override意味着每次在测试平台层次结构中创建组件类type时,都会在其位置创建替换type。这适用于该组件类型的所有实例。以下代码片段中说明了此类重载的方法调用:

       // 
       // Component type override example 
       // ---------------------------------------------

       // Colour parent class 
       class colour extends uvm_component;

       `uvm_component_utils(colour)

       //etc
       endclass: colour

       // Red child class 
       class red extends colour;

       `uvm_component_utils(red)

       //etc 
       endclass: red

       // 
       // Factory type override syntax is: 
       // 
       // ::type_id::set_type_override(::get_type(), replace);
       //
       // Where replace is a bit which when ==1 enables the override of an existing override, otherwise
       // the existing override is honoured.

       // To override all instances of colour with red:
       colour::type_id::set_type_override(red::get_type(), 1);

       // This means that the following creation line returns a red, rather than a colour
       pixel = colour::type_id::create("pixel", this);

       参数化组件类也可以被重载,但必须注意确保重载类具有与被重载的类相同的参数值,否则它们不被认为是相关types:

        // 
        // Type overrides for parameterised classes: 
        // ----------------------------------------------------------

        // Base class type 
        class bus_driver #(int BUS_WIDTH = 32) extends uvm_component;

        `uvm_component_param_utils(bus_driver #(BUS_WIDTH))
        // etc

        endclass: bus_driver

        // Derived class type 
        class bus_conductor #(int BUS_WIDTH = 32) extends bus_driver #(BUS_WIDTH);

        `uvm_component_param_utils(bus_conductor #(BUS_WIDTH)) 
        // etc

        endclass: bus_conductor

        // The parameterised type override needs to keep the parameterisation consistent【重点记录下】

        bus_driver #(64)::type_id::set_type_override(bus_conductor #(64)::get_type(), 1); // This will succeed

        // Creation of a #(64) bus_driver results in a #(64) bus_conductor handle being returned:

        bus_person = bus_driver#(64)::type_id::create("bus_person", this);

        // Whereas creating a #(16) bus_driver results in a #(16) bus_driver handle being returned because
        // the matching type override is not found:

        bus_person = bus_driver#(16)::type_id::create("bus_person", this);

        // Similarly if a type override has non-matching parameters, then it will fail and return the original type

        bus_driver #(64)::type_id::set_type_override(bus_conductor #(32)::get_type(), 1); // Returns bus_driver #(64)
       

        Component Instance Overrides

        可以通过在uvm组件层次结构中指定其位置来重载特定组件实例。同样,这种方法可以与参数化类一起使用,只要注意匹配重载中涉及的两个类类型的参数:

        // 
        // Component Instance Factory Override example 
        // --------------------------------------------

        // Using red --> colour example from type override example 
        // 
        // Syntax for the instance override:
        //
        // ::type_id::set_inst_override(::get_type(), );
        //

        colour::type_id::set_inst_override(red::get_type(), "top.env.raster.spot");

        // And again for a parameterised type, the parameter values must match

        bus_driver #(64)::type_id::set_inst_override(bus_conductor #(64)::get_type(), "top.env.bus_agent.m_driver");

        Object Overrides

      Objects or sequence 相关 objects通常仅与type重载一起使用,因为instance重载方法涉及UVM测试平台组件层次结构中哪些objects不参与的位置。然而,有一个编码技巧可用于重载object的特定“instances”,这在overriding sequences的文章中有解释。

       object 重载的代码遵循与组件重载相同的形式。

Maggie • 来自相关话题

2月前

Testbench/SystemVerilog Packages

包是SystemVerilog语言结构,它使相关的声明和定义能够在包名称空间中组合在一起。包可能包含类型定义,常量声明,函数和类模板。为了能在一个范围内使用包,必须先导入它,然后才能引用其内容。     &nb ...查看全部

包是SystemVerilog语言结构,它使相关的声明和定义能够在包名称空间中组合在一起。包可能包含类型定义,常量声明,函数和类模板。为了能在一个范围内使用包,必须先导入它,然后才能引用其内容。
       包是组织代码的一种有用方法,也是确保对类型、类等的引用是一致的有用方法。UVM基类库包含在一个名为“uvm_pkg”的包中。在开发UVM测试平台时,应该使用包来收集和组织各种类定义,这些类定义是为实现agent、envs、sequence libraries、 test libraries等而开发的。 

       UVM Package Coding Guidelines 

      Package naming and file naming conventions: 

       应使用_pkg 后缀命名包。包含该包的文件的名称应反映包的名称,并具有.sv扩展名。【如YnrrubysimTestTop.sv文件中包含一个包,名字是package  YnrrubysimTestTop】
      例如:文件spi_env_pkg.sv将包含spi_env_pkg包。
      Justification:.sv扩展名是一种约定,表示包文件是独立的编译单元。_pkg 后缀表示该文件包含一个包。这两个约定对人类和机器解析脚本都很有用。

      Classes contained within a package should be `included 

      在包范围内声明的类模板应该分成具有.svh扩展名的单个文件。这些文件应该按照需要编译的顺序包含在包中【使用`include】。包文件是唯一应该使用`includes的地方,被'include的文件中不应该包含其他`include语句。
      Justification:.将类声明在单独的文件中使它们更易于维护,并且还使包内容更清晰。

      Imports from other packages should be declared at the head of the package

      包的内容可能需要引用另一个包的内容。在这种情况下,外部包应该在包代码体的开头声明。单个文件(例如可能被`include的类模板)不应单独导入。
      Justification:.将所有imports分组到一个地方可以清楚地了解包依赖那些文件或外部包。将imports放置在包的其他部分或内部被include的文件中,这可能会导致排序和潜在的类型冲突。

      All the files used by a package should be collected together in one directory

      要包含在包中的所有文件应该一起收集在一个目录文件夹中。这对于agents来说,它的目录结构需要是完整的独立包,这点尤为重要。
      Justification:.这使编译更容易,因为只有一个include目录,它还有助于重用,因为包的所有文件可以很容易地收集在一起。
      下面是UVM env的包文件示例。这个env包含两个agent(spi和apb)和一个寄存器模型,这些作为子包导入。与env相关的类模板被`include。

       // Note that this code is contained in a file called spi_env_pkg.sv 
       // 
       // In Questa it would be compiled using:
       // vlog +incdir+$UVM_HOME/src+  /spi_env_pkg.sv
       //

       //
       // Package Description:
       //
       package spi_env_pkg;

       // Standard UVM import & include:
       import uvm_pkg::*; 
       `include "uvm_macros.svh"

       // Any further package imports:
       import apb_agent_pkg::*; 
       import spi_agent_pkg::*; 
       import spi_register_pkg::*;

       // Includes:
       `include "spi_env_config.svh"
       `include "spi_virtual_sequencer.svh"
       `include "spi_env.svh"

       endpackage: spi_env_pkg

       Package Scopes

      经常让用户感到困惑的是SystemVerilog package是一个范围。这意味着在package中声明的所有内容以及导入到package中的其他package的内容仅在本package的范围内可见。如果将包导入另一个范围(即另一个包或模块),则只能看到包的内容,而不是它导入的任何包的内容。如果新范围中需要这些其他包的内容,则需要单独导入它们。

       // 
       // Package Scope Example
       // ---------------------------------------------------- 
       //
       package spi_test_pkg;

       // The UVM package has to be imported, even though it is imported
       // in the spi_env package. This is because the import of the uvm_pkg 
       // is only visible within the current scope
       import uvm_pkg::*;
       // The same is true of the `include of the uvm_macros
       `include "uvm_macros.svh"

       // Import of uvm_pkg inside the spi_env package is not 
       // visible within the scope of the spi_test package
       import spi_env_pkg::*; 

       // Other imports 
       // Other `includes
       `include spi_test_base.svh

       endpackage: spi_test_pkg