• uvm简介


    只有driver的验证平台

    开始是直接对driver进行例化,同时调用main_phase,从而会将数据进行驱动到top中信号。当加入interface后,通过interface将数据驱动到top上。也可以通过interface读取相应的数据。比如monitor中引入接口,监测相应的transaction。

    monitor中的interface?

    ‘uvn_info取代display,可以打印更多东西

    uvm_macros.svh包含众多宏定义,需要import

    ’uvm_component_utils对新定义类应用时,相当于把类注册到uvm内部的一张表中,可以根据类目创建一个类的实例,也拥有实现factory功能的基础。

    ‘uvm_fatal出现表示出现了问题无法继续

    实例化

    run_test语句实例化了一个脱离top_tb层次结构的实例,建立了一个新的层次结构

    run_test创建的实例是树根,并且名字固定为uvm_test_top

    1. top中实例化
    2. initial begin
    3. my_driver drv;
    4. drv = new("drv",null);
    5. drv.main_phas(null);
    6. $finish();
    7. end
    8. //利用uvm_component_utils注册后可以实现和上面一样的功能
    9. //此处run_test实例化后的实例名字为uvm_test_top
    10. initial begin
    11. run_test("my_driver"); //创建实例,调用mian_phase 如果有多个task,会调用别的吗,还是只
    12. end 调用main_phase

    uvm父类

    • 每一个派生自uvm_component或其派生类的类在其new函数中要指明两个参数:name和parent,另外还要使用uvm_component_utils注册。(比如uvm_driver。)只有uvm_component才能作为数的节点。
    • 所有的transaction要派生自uvm_sequence_item,这样才可以使用sequence机制
    • uvm_object_utils有生命周期,不想uvm_component_utils一直存在。(比如transaction)

    phase

    • uvm验证平台的运行主要通过phase管理,以xxx_phase命名,都有一个类型为uvm_phase、名字为phase的参数。driver所做的事几乎都在main_phase中完成,实现一个driver等于实现一个main_phase
    • build_phase(仿真时间为0时执行)在new函数之后,mian_phase之前执行。先执行树根的build_phase之后再执行树叶的,当所有build_phase执行完才会执行后面的phase。uvm树最晚在build_phase时段完成,在main_phase中实例化会报错,而在new中实例化不会报错。
    • connect_phase在build_phase执行完成后马上执行,执行顺序是从树叶到树根。

    函数phase(不消耗仿真时间):build_phase

    任务phase:mian_phase

    1. class A;
    2. ……
    3. endclass
    4. A a_inst;
    5. a_inst=new(); //new()通知仿真器创建一个A的实例,之后会开辟空间,返回空间指针

    引入objection

    每个phase中,uvm会检查是否有objection被提起(raise_objection),若有,那么等待这个objection被撤销(drop_objection)后停止仿真,若无则马上结束当前phase。raise_objection必须在main_phase中第一个消耗仿真时间的语句前。

    加入virtual interface

    避免绝对路径方法

    • 使用宏
    • 使用interface (如top_tb.clk变为vif.ck)

    定义了接口后,在top中实例化后,通过dut实例化就可以将接口作为参数传递进去进行连接。

    1. //错误用法,在类中,不能这样声明句柄?
    2. class my_driver extends uvm_drive;
    3. my_if drv_if;
    4. endclass
    5. //正确用法
    6. class my_driver extends uvm_driver;
    7. virtual my_if vif;
    8. endclass

    config_db机制

    不在top层时,在build_phase中使用

    1. //set第二个参数表示路径索引
    2. //set和get第三个参数必须一致
    3. //set的第四个参数表示要将哪个interface进行传递,get的第四个参数表示要将得到的interface给哪个成员变量
    4. //使用双冒号是因为函数是静态函数
    5. //uvm_config_db#(virtual my_if)表示一个参数化的类,参数为寄信类型
    6. uvm_config_db#(virtual my_if)::get(this,"","vif",vif)
    7. uvm_config_db#(virtual my_if)::set(null,"uvm_test_top","vif",input_if)

    为验证平台加入各个组件

    加入transaction

    加入env

    在容器中例化driver、monitor、reference model、scoreboard,调用run_test时,传递的参数不再是my_driver,而是这个容器类,即让UVM自动创建这个容器类(uvm_env)的实例。

    1. class my_env extends uvm_env;
    2. my_driver drv; //在env中例化
    3. function new(string name = "my_env" ,uvm_component parent)
    4. ……
    5. endfuncrion
    6. virtual function void build_phase(uvm_phase phase);
    7. super.build_phase(phase);
    8. //验证平台组件实例化都应该按这种方式实例化,只有使用factory机制注册的类才可以这样实例化
    9. //只有这样实例化的才可以使用factory的重载机制
    10. //create传递的参数会影响set的第二个参数
    11. drv=my_driver::type_id::create("drv",this); //参数为名字和this指针
    12. endfunction
    13. 'uvm_component_utils(my_env)

    加入monitor

    driver把transaction级别数据转变为DUT端口级别,并驱动给DUT,monitor则用来收集DUT的端口数据,并将其转换为transaction交给后续组件如reference model、scoreboard等处理。

    monitor也需要virtual my_if

    封装成agent

    driver和monitor处理的是同一种协议,故将两者封装在一起,称为一个agent。不同agent代表不同协议。

    agent有一个属性is_active来选择是否实例化,默认情况为UVM_ACTIVE。

    在agent中声明driver、monitor,在其build_phase中进行例化

    在env中声明agent,在其build_phase中进行例化,对对相应的is_active模式进行声明

    (可以通过config_db机制传递这个参数)

    加入reference model

    同样声明端口,main_phase中例化数据

    数据来源于agent,如何将数据发送到module

    uvm_analysis_port :发送

    uvm_blocking_get_port :接收

    uvm_tlm_fifo :连接两个端口  (大多在env中)暂存作用

    (三种方式都是提前声明,在build_phase中利用new进行例化)

    1. uvm_analysis_port #(transaction) ap
    2. i_agt.ap.connect(agt_mdl_fifo.analysis_export); //利用connect连接
    3. mdl.port.connect(agt_mdl_fifo.blocking_get_export);

    书上例子,在agent和monitor中都声明一个通信句柄,但只有monitor中进行实例化。可以通过在agent的connect_phase中进行赋值ap=mon.ap即可。

    加入scoreboard

    比较reference model出来的数据和dut出来的数据。由于从dut得到数据有延时,而从reference model得到的结果不需要延时。

    1. exp_port.get(get_actual) //用到接收端口的get方法,参数为transaction句柄
    2. //在reference mode中发送完一笔数据会write到ap(发送句柄)

    加入field_automation机制

    1. 'uvm_objection_utils_begin(my_transaction) //实现transaction的factory注册
    2. 'uvm_field_int(dmac,UVM_ALL_ON) //uvm_field系列宏注册字段
    3. 'uvm_field_array_int(pload,UVM_ALL_ON) //这样注册后就可以调用copy、compare、print等函数
    4. 'uvm_objection_utils_end
    5. //tr.pack_bytes(data_q) 可以将字段变成byte放入data_q,字段按照宏注册顺序
    6. tr.unpack_bytes(data_array) 将byte流转换成tr字段,参数必须为动态数组

    在验证平台加入sequencer

    sequence机制用于产生激励,driver负责接收激励。

    1. class my_driver extends uvm_driver#(my_transaction) //uvm_driver是参数化的类,如uvm_driver中
    2. //的成员变量req,类型是传递的参数,如transaction
    3. req=new("req");

    sequence机制

    sequence不属于验证平台的一部分,sequencer是uvm_component,sequence是uvm_object。

    1. //第一种transaction例化 driver没有传递transaction类型
    2. task my_monitor::main_phase(uvm_phase phase)
    3. my_transaction tr;
    4. tr=new("tr");
    5. //第二种例化,传递uvm_driver的参数就是transaction类型 没有手动声明句柄,直接使用
    6. class my_driver extends uvm_driver #(my_transaction)
    7. main_phase中
    8. req=new(“req”);
    9. //第三种例化,在sequence中
    10. 先声明句柄
    11. class my_sequence extends uvm_sequence #(my_transaction)
    12. my_transaction m_trans;
    13. virtual tsak body();
    14. 'uvm_do(my_trans) //'uvm_do可以创建实例my_trans;将其随机化;最终送到sequencer;
    15. endtask //不用sequencer还可以用start_item,finish_item产生transaction

    每一个sequence都有一个body任务,当sequence启动后,就会自动执行body中的代码。

    sequence-->(仲裁列表)----->sequencer--->driver

            由于数据由sequence到driver,当sequencer做两件事,检查仲裁列表中是否由sequence发送transaction的请求;检测driver中是否有接受transaction的请求。任意一个不满足,都会等另一个条件满足才进行发送。

           driver的成员变量seq_item_port和sequencer的成员变量seq_item_export可以建立通道,实现driver向sequencer发送请求。

    1. task my_driver::main_phase(uvm_phase phase)
    2. while(1) begin
    3. seq_item_port.get_next_item(req); //通过get_next_item向sequence申请新的transaction
    4. drive_one_pkt(req);
    5. seq_item_port.item_done()
    6. end

    driver使用get_next_item得到transaction,sequencer自己也保留一份。在下次调用get_next_item前,item_done被调用,可表明driver得到了transaction,会删除保留的,否则会再次发送。另外,uvm_do在driver取走transaction后,会等待driver返回item_done,uvm_do才算执行完,返回执行下一个uvm_do,产生新的transaction。

           与get_next_item类似,还有try_next_item,它是非阻塞的,driver会询问sequencer是否有新的transaction,若有则得到该transaction,若无则直接返回。而get_next_transaction会一直等到新的transaction才返回。

    启动sequence

    常在某个component(如my_sequencer、my_env)的main_phase

    1. task my_env::main_phase(uvm_phase phase); //main_phase中
    2. my_sequencese seq;
    3. phase.raise_objection(this); //objection伴随着sequence,通常只在sequence出现的地
    4. 方才提起和撤销objection
    5. seq=my_sequence::type_id::create("seq"); //创建实例
    6. seq.start(i_agt.sqrt); //参数是一个sequencer指针,且需要指明哪一个sequencer
    7. phase.drop_objection;(this);
    8. endtask
    9. //在sequencer中
    10. seq.start(this); //启动时,参数变化了

    default_sequence的使用

    启动更多使用的default_squence进行启动,一般在某个component的build_phase中启动

    虽然是通过set寄信,但不用get获得信

    1. //env中
    2. uvm_config_db#(uvm_object_wrapper)::set(this, //my_env在uvm_test_top,所以为this
    3. "i_agt.sqr.main_phase", //第二个参数为相对第一个参数的相对路径
    4. "defualt_sequence",my_sequence:type:get())
    5. //uvm_config_db在传递virtual interface时,不是类,无法使用指针,故第一个参数设置为null,第二个参数为绝对路径uvm_test_top.xxx
    6. //第二个参数还要指定在sequencer的哪个phase启动
    7. //top_tb中
    8. 参数为setnull,"uvm_test_top.i_agt.sqr.main_phase",……)
    9. //my_agent中
    10. set(this,"sqr.main_phase",……)

    由于sequence出现就有objection的提起和撤销,为此可以在sequence中设置

    1. //sequence启动default_sequence时,会对变量名starting_phase进行如下操作
    2. task my_sequencer::main_phase(uvm_phase phase);
    3. ……
    4. seq.starting_phase=phase;
    5. seq.start(this)
    6. ……
    7. endtask
    1. my_sequence中
    2. ……
    3. virtual task body();
    4. if(starting_phase!=null)
    5. starting_phase.raise_objection(this);
    6. repeat() begin
    7. 'uvm_do(m_trans)
    8. end
    9. #1000
    10. if(starting_phase.drop_objection(this));
    11. endtask

    还可以直接在test中设置启动 

    加上测试用例

    加入base_test

            树根是基于uvm_test派生的类,此时test是uvm_test_top,所以在basic_test中对env进行句柄声明,在build_phase中实例化。

    1. class base_test extends uvm_test;
    2. my_env env;
    3. ……
    4. function void base_test::build_phase(uvm_phase phase);
    5. super.build_phase(phase);
    6. env=my_env::type_id::create("env",this);
    7. //在basic_test中启动sequence
    8. uvm_config_db#(uvm_object_wrapper)::set(this,
    9. "env.i_agt.sqr.main_phase",
    10. "default_sequence",
    11. "my_sequence::type_id::get()");
    12. endfunction

    除了以上操作,还在basic_test中做如下事情,第一,设置整个验证平台的超时退出时间;第二,通过config_db设置验证平台中某些参数的值

    uvm中测试用例的启动

            一种激励作为一个测试用例。当有多个sequence时,可以在命令行中指定参数来启动不同测试用例。(因为sequence在其中吗?)

    1. //启动my_case0
    2. initial begin
    3. run_test("my_case0");
    4. end
    5. //当my_case0运行时,需要修改代码,重新编译才能运行。可以使用不加参数的run_test()
    6. initial begin
    7. run_test();
    8. end

    my_casen      my_env    my_agent(my_sequencer、my_monitor、my_driver)、my_module、my_scoreboard、my_agent(my_monitor)

    uvm_test_top  env  env   i_agt(sqr、drv、mon)、mdl、sco、o_agt(mon)






     

  • 相关阅读:
    在ubuntu20.04上安装arm-linux-gcc 4.4.3
    虚拟机的IP地址自动变为127.0.0.1
    当Github启用PSA之后...
    webdriver.Chrome()没反应
    关于rdflib解析三元组介绍
    美国科技消费品公司Society Brands完成2500万美元融资
    【LeetCode字符串#03】图解翻转字符串中的单词,以及对于for使用的说明
    tomcat日志轮转
    Vue中常见的loader的作用
    如何写出好代码 - 防御式编程
  • 原文地址:https://blog.csdn.net/qq_44455456/article/details/125982755