UVM学习记录3


在之前的中学习中,通过TLM端口/TLM FIFO实现了monitor、reference model与checker之间的通信,相较于之前的mailbox句柄连接,更加容易定制,也使得组件的独立性提高。

本次实验需要实现:

  • 将产生transaction并且发送至driver的generator组件,拆分为sequence与sequencer。
  • 在拆分的基础上,实现底层的sequence。
  • 完成sequencer与driver的连接和通信工作。
  • 构建顶层的virtual sequencer。
  • 将原有的mcdf_base_test拆分为mcdf_base_virtual_sequence与mcdf_base_test,前者发挥产生序列的工作,后者只完成挂载序列的工作。
  • 将原有的mcdf_data_consistence_basic_test和mcdf_full_random_test继续拆分为对应的virtual sequence和轻量化的顶层test。

最终将generator、driver与test的关系移植为sequence、sequencer、driver和test的关系。

driver与sequence的改建

  • 移除原有在各个driver中的mailbox句柄,以及在do_driver()任务中使用mailbox句柄通信的方式,用uvm_driver::seq_item_port进行通信,同时定义对应的uvm_sequencer
    task do_drive();
      reg_trans req, rsp;
      @(posedge intf.rstn);
      forever begin
        //Use seq_item_port to get request item
        seq_item_port.get_next_item(req);
        this.reg_write(req);
        void'($cast(rsp, req.clone()));
        rsp.rsp = 1;
        //Use seq_item_port to put response item
        rsp.set_sequence_id(req.get_sequence_id());
        seq_item_port.item_done(rsp);
      end
    endtask

底层sequence的提取

  • 将原来在各个generator中发送transaction的任务,提取为各个对应的底层sequence。在各个agent中声明、创建对应的sequencer,并且将其与driver通过TLM port连接起来。
  // migrate the generator as uvm_sequencer and uvm_sequence
  class reg_sequencer extends uvm_sequencer #(reg_trans);
    `uvm_component_utils(reg_sequencer)
    function new (string name = "reg_sequencer", uvm_component parent);
      super.new(name, parent);
    endfunction
  endclass: reg_sequencer

  // extract chnl_data_sequence from the reg_generator
  class reg_base_sequence extends uvm_sequence #(reg_trans);
    rand bit[7:0] addr = -1;
    rand bit[1:0] cmd = -1;
    rand bit[31:0] data = -1;

    constraint cstr{
      soft addr == -1;
      soft cmd == -1;
      soft data == -1;
    }

    `uvm_object_utils_begin(reg_base_sequence)
      `uvm_field_int(addr, UVM_ALL_ON)
      `uvm_field_int(cmd, UVM_ALL_ON)
      `uvm_field_int(data, UVM_ALL_ON)
    `uvm_object_utils_end
    `uvm_declare_p_sequencer(reg_sequencer)

    function new (string name = "reg_base_sequence");
      super.new(name);
    endfunction

    task body();
      send_trans();
    endtask

    // generate transaction and put into local mailbox
    task send_trans();
      reg_trans req, rsp;
      `uvm_do_with(req, {local::addr >= 0 -> addr == local::addr;
                         local::cmd >= 0 -> cmd == local::cmd;
                         local::data >= 0 -> data == local::data;
                         })
      `uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
      get_response(rsp);
      `uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
      if(req.cmd == `READ) 
        this.data = rsp.data;
      assert(rsp.rsp)
        else $error("[RSPERR] %0t error response received!", $time);
    endtask

    function void post_randomize();
    ...
    
  endclass: reg_base_sequence
  // migrate the methods in mcdf_base_test to the reg sequences
  // -idle_reg()  -> idle_reg_sequence
  // -write_reg() -> write_reg_sequence
  // -read_reg()  -> read_reg_sequence
  class idle_reg_sequence extends reg_base_sequence;
    constraint cstr{
      addr == 0;
      cmd == `IDLE;
      data == 0;
    }
    `uvm_object_utils(idle_reg_sequence)
    function new (string name = "idle_reg_sequence");
      super.new(name);
    endfunction
  endclass: idle_reg_sequence

  class write_reg_sequence extends reg_base_sequence;
    constraint cstr{
      cmd == `WRITE;
    }
    `uvm_object_utils(write_reg_sequence)
    function new (string name = "write_reg_sequence");
      super.new(name);
    endfunction
  endclass: write_reg_sequence

  class read_reg_sequence extends reg_base_sequence;
    constraint cstr{
      cmd == `READ;
    }
    `uvm_object_utils(read_reg_sequence)
    function new (string name = "read_reg_sequence");
      super.new(name);
    endfunction
  endclass: read_reg_sequence 

移除generator和uvm_base_test中的transaction发送方法

  • 将uvm_base_test中的方法idle_reg()、write_reg()和read_reg()移除并移植到reg_pkg中。由此uvm_base_test只变为了容器的性质,在它内部主要由mcdf_env、mcdf_config配置对象以及被用来挂载的顶层sequence构成。
    task run_phase(uvm_phase phase);
      phase.raise_objection(this);
      this.run_top_virtual_sequence();
      phase.drop_objection(this);
    endtask

    virtual task run_top_virtual_sequence();
      // User to implement this task in the child tests
    endtask

添加顶层的virtual sequencer

  • 由于MCDF验证环境中存在多个底层的sequencer和sequence,因此这就需要有顶层的virtual sequencer与virtual sequence统一调度。在定义了mcdf_virtual_sequencer之后,在mcdf_env中声明、例化,并且完成其与底层sequencer的句柄连接。
  // MCDF top environment
  class mcdf_env extends uvm_env;
    ...
    // declare the virtual sequencer handle
    mcdf_virtual_sequencer virt_sqr;

    `uvm_component_utils(mcdf_env)

    function new (string name = "mcdf_env", uvm_component parent);
      super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      ...
      //  instantiate the virtual sequencer
      virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr", this);
    endfunction

    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      ...
      // connect the sequencer handles of the virtual sequencer to those dedicated sequencer objects inside the agents
      virt_sqr.reg_sqr = reg_agt.sequencer;
      virt_sqr.fmt_sqr = fmt_agt.sequencer;
      foreach(virt_sqr.chnl_sqrs[i]) virt_sqr.chnl_sqrs[i] = chnl_agts[i].sequencer;
    endfunction
  endclass: mcdf_env

重构mcdf_base_test和mcdf_data_consistence_basic_test

  • 原有的mcdf_base_test除了承担其容器的功能,还在其run_phase阶段中实现了sequence的分阶段发送功能。在添加了顶层的virtual sequencer之后,需要将所有发送序列的顺序和组织等内容均移植到mcdf_base_virtual_sequence,因此需要将mcdf_base_test::run_phase()发送序列的功能移植到定义的mcdf_base_virtual_sequence一侧,而在移植后,mcdf_base_test::run_phase()只需要挂载对应的顶层virtual sequence即可。
  • 将其产生和发送transaction的任务,都移植到mcdf_data_consistence_basic_virtual_sequence,而进一步减轻mcdf_data_consistence_basic_test的代码量。测试的动态场景往往都是由virtual sequence统一组织的,而test层往往之后做run_phase前的一些验证环境的配置。
    task do_reg();
      bit[31:0] wr_val, rd_val;
      // slv0 with len=8,  prio=0, en=1
      wr_val = (1<<3)+(0<<1)+1;
      `uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR; data == wr_val;})
      `uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR;})
      rd_val = read_reg_seq.data;
      void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));

      // slv1 with len=16, prio=1, en=1
      // slv2 with len=32, prio=2, en=1
      ...

      // send IDLE command
      `uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
    endtask
    task do_formatter();
      `uvm_do_on_with(fmt_config_seq, p_sequencer.fmt_sqr, {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;})
    endtask
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小破同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值