【UVM练习】实验lab4

 知识点:

uvm世界运转得必要条件:组件之间进行事务传送,sequence_item是道路上行驶卡车,sequence是道路,sequencer是目的地的关卡,driver是最终的目的地。sequence_item从sequence出发,经过sequencer,到达driver。

 uvm_sequence_item:基于uvm_object类,可以在任何阶段创建;完成所需要的具体数据和控制要求,由于item本身的数据属性,为了充分利用UVM域声明的特性,可以把不要的数据成员通过`uvm_field_xxx宏来声明,以便日后uvm_object的基本数据方法自动实现;

 uvm_sequence:基于uvm_object类,可以在任何阶段创建;sequence只负责生成item的相关内容,驱动激励时序的任务则是由driver完成的。

uvm_sequencer:将数据put给driver;

driver:负责进行处理数据;driver与sequencer之间的TLM通信采用get模式,由driver发起请求,从sequencer获得item,再由sequencer将其传给driver。所以driver是一个永动机,不用的索求item;

实现driver和sequencer的request获取和response返回端口:

item的传送:driver::seq_item_port和sequencer::seq_item_export

端口的连接:driver::seq_item_port::connect(sequencer::seq_item_export)

REQ和RSP的一致性:在自定义sequencer和driver时就表明传递的具体item类型,就不需要额外的转换了,否则在driver得到REQ在进行下一步数据处理的时候,需要动态转换将REQ转换为uvm_sequence_item的子类型才能有效的获取数据。目的是方便统一处理。

1.1 driver与squencer的改建

首先移除邮箱句柄以及句柄通信的方式,然后更改通信方式,这里使用uvm_driver::seq_Item_port的通信方式,通过get_next_item的方式获取句柄,然后等待driver消化了req的,通过item_done的方式告知sequence此次传输已经结束了,然后把rsp返回到fifo里面;移除generator,定义具有相同功能的reg_sequence和reg_sequencer:

1.2 底层sequence的提取

把各个generator中的transaction任务提取为对应的sequence,除了声明reg_sequencer类以外,还声明了reg_sequence类,内容完全是reg_generator类的内容基础上做的改变,移除了邮箱句柄,task start()替换为task body(),mcdf_base_test中的idle_reg、write_reg、read_reg都是通过调用generator来做randomize,现在改用`uvm_do_with 的方法完成随机化。send_trans的目的还是发送reg_trans,以前是通过邮箱握手的方式进行的,使用`uvm_do_with的方式也包含了创建、随机化和发送,调用get_response的函数拿回句柄。把 mcdf_base_test 中的task移到 reg_sequences中去,声明为idle_reg_sequence、write_reg_sequence、read_reg_sequence

  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();
      string s;
      s = {s, "AFTER RANDOMIZATION \n"};
      s = {s, "=======================================\n"};
      s = {s, "reg_base_sequence object content is as below: \n"};
      s = {s, super.sprint()};
      s = {s, "=======================================\n"};
      `uvm_info(get_type_name(), s, UVM_HIGH)
    endfunction
  endclass: reg_base_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 

1.3 sequencer的创建和连接 

在agent里面声明、创建对应的sequencer,并和driver通过TLM port连接起来;声明reg_sequencer的句柄,build_phase中例化,connect_phase中连接。idle_reg、write_reg、read_reg已经被改造成相应的sequence

  class reg_agent extends uvm_agent;
    reg_driver driver;
    reg_monitor monitor;
    reg_sequencer sequencer;
    local virtual reg_intf vif;

    `uvm_component_utils(reg_agent)

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

    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      driver = reg_driver::type_id::create("driver", this);
      monitor = reg_monitor::type_id::create("monitor", this);
      sequencer = reg_sequencer::type_id::create("sequencer", this);
    endfunction

    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      driver.seq_item_port.connect(sequencer.seq_item_export);
    endfunction

    function void set_interface(virtual reg_intf vif);
      this.vif = vif;
      driver.set_interface(vif);
      monitor.set_interface(vif);
    endfunction
  endclass

1.4 移除generator的踪迹

清楚generator在mcdf_base_test中的产物;以前在mcdf_base_test,generator是同一层,目前generator是被移除了 ,而sequencer已经下沉到agent里面了,所有的sequencer将组合到virtual sequencer里面

 

1.5 移除uvm_base_test的transaction发送方法

uvm_base_test之前除了承担着容器的作用,还承担着协调generator的作用,目前我们需要它只是承担容器的作用,内部由mcdf_env、mcdf_config配置对象、被用来挂载的顶层sequence组成。

 

1.6 添加顶层的virtual sequencer

添加virtual sequencer的作用是:单一的sequencer挂载很简单,通过uvm_sequence::start()来挂载root sequence,如果各个模块环境的都可以复用sequence资源,那么就需要一个可以容纳各个sequence的容器,称之为virtual sequence ,对应的有着virtual sequencer。virtual sequence可以承载不同的sequence,而且一般挂载在virtual sequencer中,virtual sequencer一般起到桥接的作用,链接所有底层sequencer句柄的。因为virtual sequencer本身不会传递item数据,所以virtual sequencer不需要和任何的driver进行TLM链接。

  class mcdf_virtual_sequencer extends uvm_sequencer;
    reg_sequencer reg_sqr;
    fmt_sequencer fmt_sqr;
    chnl_sequencer chnl_sqrs[3];
    `uvm_component_utils(mcdf_virtual_sequencer)
    function new (string name = "mcdf_virtual_sequencer", uvm_component parent);
      super.new(name, parent);
    endfunction
  endclass

1.7 完成在mcdf_env中mcdf_virtual_sequencer的声明、例化和连接

1.8 重构mcdf_base_test

将mcdf_base_test的run_phase阶段的发送序列功能移植到mcdf_base_virtual_sequence中去,mcdf_base_test的run_phase只需要挂载对应的底层virtual sequence。以前的mcdf_base_test既包含了环境也包含了测试场景的构建,重构mcdf_base_test把他拆解成了两部分,一部分是mcdf_base_test,承担了容器的功能,只是例化了env,对于场景的描述都是在mcdf_base_virtual_sequence,对各个底层的sequence进行声明,在body()里面,作用相当于mcdf_base_test里面的run_phase,所以场景的调动都交给了mcdf_base_virtual_sequence。

  class mcdf_base_virtual_sequence extends uvm_sequence;
    idle_reg_sequence idle_reg_seq;
    write_reg_sequence write_reg_seq;
    read_reg_sequence read_reg_seq;
    chnl_data_sequence chnl_data_seq;
    fmt_config_sequence fmt_config_seq;

    `uvm_object_utils(mcdf_base_virtual_sequence)
    `uvm_declare_p_sequencer(mcdf_virtual_sequencer)

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

    virtual task body();
      `uvm_info(get_type_name(), "=====================STARTED=====================", UVM_LOW)
      this.do_reg();
      this.do_formatter();
      this.do_data();
      `uvm_info(get_type_name(), "=====================FINISHED=====================", UVM_LOW)
    endtask

    virtual task do_reg();
    endtask


    virtual task do_formatter();
    endtask

    virtual task do_data();
    endtask

    virtual function bit diff_value(int val1, int val2, string id = "value_compare");
      if(val1 != val2) begin
        `uvm_error("[CMPERR]", $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2)) 
        return 0;
      end
      else begin
        `uvm_info("[CMPSUC]", $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2), UVM_LOW)
        return 1;
      end
    endfunction
  endclass

  class mcdf_base_test extends uvm_test;
    //TODO-1.4 remove the generators' handle clarification
    mcdf_env env;
    virtual chnl_intf ch0_vif ;
    virtual chnl_intf ch1_vif ;
    virtual chnl_intf ch2_vif ;
    virtual reg_intf reg_vif  ;
    virtual arb_intf arb_vif  ;
    virtual fmt_intf fmt_vif  ;
    virtual mcdf_intf mcdf_vif;

    `uvm_component_utils(mcdf_base_test)

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

    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      // get virtual interface from top TB
      if(!uvm_config_db#(virtual chnl_intf)::get(this,"","ch0_vif", ch0_vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end
      if(!uvm_config_db#(virtual chnl_intf)::get(this,"","ch1_vif", ch1_vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end
      if(!uvm_config_db#(virtual chnl_intf)::get(this,"","ch2_vif", ch2_vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end
      if(!uvm_config_db#(virtual reg_intf)::get(this,"","reg_vif", reg_vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end
      if(!uvm_config_db#(virtual arb_intf)::get(this,"","arb_vif", arb_vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end
      if(!uvm_config_db#(virtual fmt_intf)::get(this,"","fmt_vif", fmt_vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end
      if(!uvm_config_db#(virtual mcdf_intf)::get(this,"","mcdf_vif", mcdf_vif)) begin
        `uvm_fatal("GETVIF","cannot get vif handle from config DB")
      end

      this.env = mcdf_env::type_id::create("env", this); 
    endfunction

    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);

      this.set_interface(ch0_vif, ch1_vif, ch2_vif, reg_vif, arb_vif, fmt_vif, mcdf_vif);

    endfunction

    function void end_of_elaboration_phase(uvm_phase phase);
      super.end_of_elaboration_phase(phase);
      uvm_root::get().set_report_verbosity_level_hier(UVM_HIGH);
      uvm_root::get().set_report_max_quit_count(1);
      uvm_root::get().set_timeout(10ms);
    endfunction

    task run_phase(uvm_phase phase);
      // NOTE:: raise objection to prevent simulation stopping
      phase.raise_objection(this);

      this.run_top_virtual_sequence();

      // NOTE:: drop objection to request simulation stopping
      phase.drop_objection(this);
    endtask

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

    virtual function void set_interface(virtual chnl_intf ch0_vif 
                                        ,virtual chnl_intf ch1_vif 
                                        ,virtual chnl_intf ch2_vif 
                                        ,virtual reg_intf reg_vif
                                        ,virtual arb_intf arb_vif
                                        ,virtual fmt_intf fmt_vif
                                        ,virtual mcdf_intf mcdf_vif
                                      );
      this.env.chnl_agts[0].set_interface(ch0_vif);
      this.env.chnl_agts[1].set_interface(ch1_vif);
      this.env.chnl_agts[2].set_interface(ch2_vif);
      this.env.reg_agt.set_interface(reg_vif);
      this.env.fmt_agt.set_interface(fmt_vif);
      this.env.chker.set_interface(mcdf_vif, '{ch0_vif, ch1_vif, ch2_vif}, arb_vif);
      this.env.cvrg.set_interface('{ch0_vif, ch1_vif, ch2_vif}, reg_vif, arb_vif, fmt_vif, mcdf_vif);
    endfunction

  endclass: mcdf_base_test

1.9 重构mcdf_dala_consistence_basic_test 

 以前分别的实现三个task,现在交给了virtual sequence,mcdf_data_consistence_basic_virtual_sequence继承于mcdf_base_virtual_sequence ,重新定义了do_reg、do_formatter、do_data;`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR; data == wr_val;})代表调用write_reg_seq,然后挂载到p_sequencer.reg_sqr中去,with是告诉地址和数据分别是多少。 在mcdf_base_virtual_sequenc中声明virtual  sequence的时候就已经声明过p_sequencer.reg_sqr 。

`uvm_declare_p_sequencer(mcdf_virtual_sequencer);

run_top_virtual_sequence任务做两件事,一个是例化,一个数挂载。mcdf_data_consistence_basic_test类的代码变得十分简洁。

 class mcdf_data_consistence_basic_virtual_sequence extends mcdf_base_virtual_sequence;
    `uvm_object_utils(mcdf_data_consistence_basic_virtual_sequence)
    function new (string name = "mcdf_data_consistence_basic_virtual_sequence");
      super.new(name);
    endfunction
    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
      wr_val = (2<<3)+(1<<1)+1;
      `uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR; data == wr_val;})
      `uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR;})
      rd_val = read_reg_seq.data;
      void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv2 with len=32, prio=2, en=1
      wr_val = (3<<3)+(2<<1)+1;
      `uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR; data == wr_val;})
      `uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR;})
      rd_val = read_reg_seq.data;
      void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // 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
    task do_data();
      fork
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[0], {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; })
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[1], {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;})
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[2], {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;})
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask
  endclass: mcdf_data_consistence_basic_virtual_sequence

  class mcdf_data_consistence_basic_test extends mcdf_base_test;

    `uvm_component_utils(mcdf_data_consistence_basic_test)

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

    task run_top_virtual_sequence();
      mcdf_data_consistence_basic_virtual_sequence top_seq = new();
      top_seq.start(env.virt_sqr);
    endtask
  endclass: mcdf_data_consistence_basic_test

1.10 重构mcdf_full_random_test

 class mcdf_full_random_virtual_sequence extends mcdf_base_virtual_sequence;
    `uvm_object_utils(mcdf_base_virtual_sequence)
    function new (string name = "mcdf_base_virtual_sequence");
      super.new(name);
    endfunction

    task do_reg();
      bit[31:0] wr_val, rd_val;
      // slv0 with len={4,8,16,32},  prio={[0:3]}, en={[0:1]}
      wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,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"));

      // slv0 with len={4,8,16,32},  prio={[0:3]}, en={[0:1]}
      wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
      `uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR; data == wr_val;})
      `uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR;})
      rd_val = read_reg_seq.data;
      void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv0 with len={4,8,16,32},  prio={[0:3]}, en={[0:1]}
      wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
      `uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR; data == wr_val;})
      `uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR;})
      rd_val = read_reg_seq.data;
      void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // 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 inside {SHORT_FIFO, ULTRA_FIFO}; bandwidth inside {LOW_WIDTH, ULTRA_WIDTH};})
    endtask
    task do_data();
      fork
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[0], 
          {ntrans inside {[400:600]}; ch_id==0; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};})
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[1], 
          {ntrans inside {[400:600]}; ch_id==0; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};})
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[2], 
          {ntrans inside {[400:600]}; ch_id==0; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};})
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask
  endclass: mcdf_full_random_virtual_sequence

  class mcdf_full_random_test extends mcdf_base_test;

    `uvm_component_utils(mcdf_full_random_test)

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

    task run_top_virtual_sequence();
      mcdf_full_random_virtual_sequence top_seq = new();
      top_seq.start(env.virt_sqr);
    endtask
  endclass: mcdf_full_random_test

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值