1、对于一个定宽数组的注册采用下面这种即可:

int      mai_port[9] ;
int      mai_XX[] ;

`uvm_field_sarray_int(mai_port, UVM_ALL_ON) // sarray for array.size is fixed
`uvm_field_array_int(mai_port, UVM_ALL_ON) //

2、针对多维度队列和数组的注册:

class   dut_entry_cfg extend uvm_object;
int unsigned mqui_XX[4][$] ;
int          mai_XX[5][6];

`uvm_object_utils_begin(dut_entry_cfg)
foreach(mqui_XX[i])  begin
   `uvm_field_queue_int(mqui_XX[i], UVM_ALL_ON)
end
foreach(mai_XX[i])   `uvm_field_sarray_int(mai_XX[i], UVM_ALL_ON)
`uvm_object_utils_end

endclass

3 ,struct类型的注册可以直接使用:

`uvm_field_int(your_struct,UVM_ALL_ON)


3、设置超时机制结束异常仿真:

超时机制有助于结束异常仿真,在UVM中可以通过set_global_timeout(timeout)来设置超时的时间。可以在run_test();那一层加入。

module test;

  initial begin
        set_global_timeout(9999ns);
  end

  initial begin
        run_test();
   end

endmodule

4、pack的使用:

pack是uvm_object里面的内建函数,可以把注册到工厂的字段打成bit流(就是filed_automation机制)使用`uvm_field_xxx()宏注册所有类的变量成员;

5、uvm_object的内建函数compare:



config db的set和get:

甚至可以在top_tb的initial的块里面去set:

uvm_config_db #(virtual xxxx_global_interface)::set(null, "*","xxx_glb",v_xxx_intf);(第二个参数用通配符:因为有些sequence在实例化的名字是不固定的,甚至是未知的--参考白皮书P201)

因为set的第一个参数不是uvm_component所以填null(或者uvm_root::get());

同样的,在uvm_object里面get的第一个参数也得是null,(不能是this,参考白皮书P201);

uvm_config_db#(virtual xxxx_global_interface)::get(null, get_full_name(), "xxx_glb", this.v_xxx_intf);

或者:uvm_config_db#(virtual xxxx_global_interface)::get(null, “*“, "xxx_glb", this.v_xxx_intf);

如果是在uvm_component里面去get:

uvm_config_db#(virtual xxxx_global_interface)::get(this, " ", "xxx_glb", this.v_xxx_intf)

uvm_config_db#(virtual xxxx_global_interface)::get(this, get_full_name(), "xxx_glb", this.v_xxx_intf) 


memory表项:

间接读写和直接读写:

间接读写:是通过一组读写寄存器来实现对memory的读写,一般这组寄存器包含读写地址,读写数据,读写状态(是否成功)等信息;

直接读写:就是直接通过地址去访问memory端口去读写memory;

验证实现方式:memory模型和非memory模型;

memory模型:要基于UVM的方式实现前后门的方式去读写表项,优点:不需要感知到DUT对mem的具体的切片;具体的读写由RTL内部的task来实现;验证只需要通过.write、.read形式去调用该task;后门路径的感知交给DUT的wrapper去实现,验证不感知,只需要提供每一个表项的起始地址,每个entry的地址=根据偏移地址+表项的起始地址

非memory模型:之前遇到的有,通过前门接口直接读写表项和后门路径读写表项,后门实现中,需要感知到DUT对mem的具体的切片(不太友好);还需要感知具体的后门路径;

另外在IT和ST验证层次时,会有大量的表项配置,如果此时走前门配置,会花费大量的仿真时间;


仿真结束机制:

1、可以放在virtual sequence里面的post_body里面进行;

2、结束条件:如可以是达到预期的发包数等;

仿真结束检查:

1、放在report_phase里面进行?

2、检查什么:常见的有:

  • 检查DUT与TB的空闲链是否一致;
  • 检查TB的空闲链地址是否有重复,超出范围,缺失等;
  • 比对DUT与TB的队列空非状态是否一致,队列是否为空;(非空场景是否合理)
  • 检查DUT的计数器与TB的计数器是否一致,计数器是否为0;
  • 接口信号检查:反压和丢弃信号是否为0;(非0场景是否合理)
  • 所有从DUT拉出的数据传输信号应为0;(有必要没?)
  • 检查DUT是否有致命中断拉起;
  • FIFO检查

     


基于UVM内建的sequence去检查寄存器:

     ral_block_xxx        ralf;//寄存器模型


     uvm_reg_mem_hdl_paths_seq         ck_hdl_seq; // cheack hdl path
     uvm_reg_access_seq                ck_rw_reg ; // cheack RW reg write/read function
     uvm_reg_hw_reset_seq              ck_reset_value_seq ; // cheack

     ck_hdl_seq = new("ck_hdl_seq");
     ck_rw_reg  = new("ck_rw_reg" );
     ck_reset_value_seq  = new("ck_reset_value_seq" );

     ck_hdl_seq.model = ralf; //// cheack hdl path
     ck_rw_reg.model  = ralf; //// cheack 
     ck_reset_value_seq.model  = ralf; //

     ck_hdl_seq.start(null);// cheack hdl path
     ck_reset_value_seq.start(null);// cheack 
     ck_rw_reg.start(null);// cheack

set机制与urandom_range结合:

uvm_config_db #(int)::set(null, "*","key_word",$urandom_range(0,100));


begin_tr、accept_tr和end_tr是属于uvm_componet组件的recording interface的内建方法,这些方法组成了基于组件的事务记录接口。此方法可以用来帮助组件记录事务的产生或消耗。

参考:

UVM中accept_tr, begin_tr, end_tr的理解_浩字不好雨的博客-CSDN博客


1 、sequence中的get前提是对应的vsqr.xxx_sequencer在env中与xxx_agent.seqr已经连接(等号赋值),只有这样顶层的set,才能把“信”送给sequence;

2,有时候不想在body里面去get,在其他方法里面去get;需要提前制定seqr: set_sequencer()函数

set_sequencer()函数会调用m_set_p_sequencer(),这个函数是一个空的虚函数。set_sequencer()函数完成挂载,给m_sequencer赋值。

使用实例: 

////tc_name: connect_phase

user_seq.set_sequencer(env.xx_agt.seqr)

设置报错多少个UVM_ERROR就停止仿真。

function void `tc_name::connect_phase (uvm_phase phase);
    super.connect_phase(phase);
    server.set_max_quit_count(500);
endfunction:connect_phase

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐