uvm实战笔记

本文是uvm实战这本书的笔记

  • P45

    一个sequence在向sequencer发送transaction前,要先向sequencer发送一个请求。sequencer把这个请求放在一个仲裁队列中。作为sequencer,它需要做两件事,第一,检测仲裁队列里面是否有某个sequence发送transaction的请求;第二,检测driver是否申请transaction。

    如果仲裁队列里有发送请求,但是driver没有申请transaction,那么sequencer将会一直处于等待driver的状态,直到driver申请。此时,sequencer同意sequence的发送请求,sequence在得到sequencer的批准后,产生出一个transaction并交给sequencer,后者把这个transaction交给driver。

    如果仲裁队列中没有发送请求,但是driver向sequencer申请新的transaction,那么sequencer会处于等待sequence的状态,一直到有sequence递交发送请求,sequencer马上同意这个请求,sequence产生transaction并交给sequencer,最终driver获得到这个transaction。

    如果仲裁队列中有发送请求,同时driver也在向sequencer申请新的transaction,那么将会同意发送请求,sequence产生transaction并交给sequencer,最终driver获得。

  • P46

    为什么有item_done?

    1
    2
    3
    seq_item_port.get_next_item(req);
    driver_one_pkt(req);
    seq_item_port.item_done();

    当driver使用get_next_item得到一个transaction时,sequencer自己也保留了一份刚发送出的transaction,当出现sequencer发出了而driver没有得到的情况,sequencer会把保留的这份再发送出去,那么sequencer如何知道driver是否成功得到?如果在下次调用get_next_item之前,item_done被调用,那么sequencer就认为driver已经得到,将会把这个保留的transaction删除,换言之,其实是一种为了增加可靠性而使用的握手机制。

    在sequence中,向sequencer发送transaction使用的是uvm_do宏。这个宏什么时候返回呢?uvm_do宏产生了一个transaction并交给sequencer,driver取走这个transaction后,uvm_do并不会立刻返回执行下一次uvm_do,而是等待在那里,知道driver返回Item_done信号,uvm_do宏才算执行完毕,返回后开始执行下一个uvm_do。

  • P57

    uvm hierarchy(摘录自uvm_users_guide_1.1.pdf)

  • P89

    多次config_db,取哪个?

    case中:uvm_config_db#(int)::set(this, "env.i_agt.drv","num",999)

    env中:uvm_config_db#(int)::set(this, "env.i_agt.drv","num",1000)

    都在build_phase中。

    答案是999。因为case的优先级高,uvm树中越靠近根节点,优先级越高。

    这样设置的原因是,希望越外层的优先级高,可以替换掉低层次中配的值,可以不用修改内部模块。

    case中:uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.env.i_agt.drv","num",999)

    env中:uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.env.i_agt.drv","num",1000)

    答案是1000。因为第一个参数相同,则优先级相同,只能比较时间。由于build_phase是自上而下执行的,case先执行,env后执行,所以后设置的替换了先设置的。

    如果是同一层次设置两次呢?

    case中:

    1
    2
    `uvm_config_db#(int)::set(this, "env.i_agt.drv","num",999)`
    `uvm_config_db#(int)::set(this, "env.i_agt.drv","num",1000)`

    答案是1000。同样的,按时间,后设置的有效。

  • P93

    非直线设置与获取

    • 非直线设置,避免使用

      scoreboard中:uvm_config_db#(int)::set(this.parent,"i_agt.drv","uvm",200)

      因为不能保证同级build_phase的执行顺序,可能先get再set,就没有生效。

    • 非直线获取,可以使用

      model中:void'(uvm_config_db#(int)::get(this.parent, "i_agt.drv","num",200));

      某些情况可以减少set的冗余,只需要set一次就可以get两次。

  • P96

    check_config_usage()

    可以检查有哪些set过,没有get过,用来检查是否有字符串写错了。

  • P97

    print_config(0/1)

    1:递归查询

    0:只显示当前component

  • P119

    put和get系列port与analysis_port系列区别

    1. analysis_port可以连接多个imp,一对多通信,而put/get系列一对一通信
    2. put/get系列有阻塞和非阻塞之分,而analysis系列没有
  • P132

    uvm phase

  • P135

    build_phase 自上而下,因为要先例化子模块

    其他function phase,自下而上

    同层次component,按字典顺序

    task phase,自下而上启动,同时执行

  • P139

    树遍历,深度优先

    i_agt,scb,那么i_agt的build_phase先执行,然后是driver,monitor,再试scb的build_phase。

  • P140

    super.xx_phase(phase);

    build_phase,component对其最重要的事是自动获得通过config_db::set设置的参数。

    其他phase几乎没有做任何相关事情。

  • P145

    phase跳转

    往前跳转到从build到start_of_simulation的function phase是不可行的,run phase也不可行。

    往后跳转除了动态运行的phase,还可以是函数phase,如main跳转到final。

  • P170

    sequencer的lock操作

    所谓lock,就是sequence向sequencer发送一个请求,这个请求与其他sequence发送transaction的请求一同被放入sequencer的仲裁队列中,当前面请求处理完毕后,sequencer会开始响应该lock,此后sequencer会一直连续发送此sequence的transaction直到unlock被调用。

  • P172

    grab

    与lock类似,不同的是,lock是插入sequencer仲裁队列后,而grab是之前。比lock优先级更高,一旦发出就拥有了sequencer的所有权。

  • P175

    sequence相关宏

    1
    2
    3
    4
    `uvm_do(SEQ_OR_ITEM)
    `uvm_do_with(SEQ_OR_ITEM,CONSTRAINTS)
    `uvm_do_on(SEQ_OR_ITEM,SEQR)
    `uvm_doon_with(SEQ_OR_ITEM,SEQR,CONSTRAINTS)
    1
    2
    3
    `uvm_create(m_trans)
    m_trans.randomize();
    `uvm_send(m_trans);
    1
    `uvm_rand_send(SEQ_OR_ITEM);//与前者不同的是,会对trans随机化,前提是trans需要被分配了空间
    1
    2
    3
    4
    tr = new("tr");
    start_item(tr);
    tr.randomize();
    finish_item(tr);
  • P183

    嵌套的sequence

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class seq_all extends uvm_sequence#(my_trans);
    virtual task body();
    seq0 seq0_i;
    seq1 seq1_i;
    repeat(10)begin
    `uvm_do(seq0_i)
    `uvm_do(seq1_i)
    end
    endtask
    endclass
  • P185

    有没有办法将两个不同的transaction类型给同一个sequencer呢?

    可以。将数据类型设为uvm_sequence_item,接收时用cast转回来,多态。

  • P186

    p_sequencer

    若在sequence中想获得sequencer,在sequence中使用宏uvm_declare_p_sequencer(my_sequencer)`

    则相当于将m_sequencer给cast为my_sequencer类型的p_sequencer变量。

  • virtual sequence

    是为了解决一个验证环境中,存在多个agent,每个agent中的sequence需要按照不同场景调度问题

    可以用进程语句来控制各个sequence的并行,串行,甚至可以延迟执行

  • sequence library

    在单个agent对应多个不同sequence执行的场景,定向对应每个场景执行每个不同sequence显然不是好办法,我们希望可以执行类似sequence时,一个用例即可随机执行完,可用sequence library。

    在写sequence时,注册sequence library中,然后用例中设置library的执行方式。

  • 各phase任务

    build:用于创建和配置组件,可以动态的更改平台需要运行的组件,通过对构建不同子组件组合,达到一个平台实现多个功能的目的。

    connect:完成内部各个子组件的连接,以及虚拟接口的连接。

    end_of_elaboration:对上述结果做最后的调整。

    start_of_simulation:组件在此阶段初始化完成。

    run:消耗时间,定义组件运行的主要功能。

    extract:收集断言错误,覆盖率,以及组件的统计结果。

    check:对收集到的重要结果进行分析判断,与预期结果比较。

    report:将运行结果输出到屏幕或成文件

  • analysis port/export和port/export的区别

    1. analysis可以连接多个imp,它和imp是一对多通信。而port是一对一的。analysis更像广播。
    2. 作为port/export,有put,get,transport操作。对于普通port来说,一个port要么是put port,要么是get port,要么是transport port,不可能三者兼得。但对于analysis port来说,它只有一个操作,write,意思是广播一下,剩下的事情与它无关了。
    3. port/export有阻塞和非阻塞之分,相应的put/get/transport操作也有阻塞和非阻塞。但analysis由于是广播,没有阻塞和非阻塞之分。