• 电力电子转战数字IC20220818day63——uvm入门实验5


    目录

    实验目标

    问题清单

    什么是uvm_reg类?

    如何定义uvm_reg?

    声明,例化和配置field的语句是什么?

    什么是uvm_reg_block?

    什么是uvm_reg_adapter?

    如何编写adapter?

    实现了adapter之后,如何将其集成到环境中?

    uvm_reg_adapter和uvm_reg_block的关系?

    adapter,block,predictor三者的关系是什么?

    如何用uvm_reg的操作方式去发送reg序列?

    如何用内建寄存器序列做全面的reg测试?

    reg功能覆盖率收集方式1:内部自动收集,如何实现?

    reg功能覆盖率收集方式2:事件触发外部收集,如何实现?

    寄存器模型rgm是什么?

    rgm包含了什么?

    实验任务

    1.1 实现reg2mcdf_adapter类的两个方法

     1.2 在mcdf_env中声明并例化block、adapter、predictor,最后connect起来

    2.1 reg block句柄的传递

    2.2 mcdf_data_consistence_basic_virtual_sequence中原来是由总线seq实现的reg读写,现在改为由rgm操作的reg读写方式

     2.3 mcdf_full_random_virtual_sequence做相同的操作,改为由rgm预先设置寄存器值,再统一做总线寄存器更新的方式,稍后由后门读取的方式取得寄存器值加以比较。

    3.1 利用寄存器的内建序列做测试


    实验目标

    问题清单

    什么是uvm_reg类?

    • 用来例化和配置reg中各个field

    如何定义uvm_reg?

    • 是一个class,各个子类继承于它。子类(控制寄存器+只读寄存器)在完成field的声明后需要new函数和build函数(例化与配置都在build中)

    声明,例化和配置field的语句是什么?

    1. uvm_reg_field reserved;
    2. rand uvm_reg_field pkt_len;//声明域,且可以随机化
    3. rand uvm_reg_field prio_level;
    4. rand uvm_reg_field chnl_en;
    5. reserved = uvm_reg_field::type_id::create("");
    6. pkt_len = uvm_reg_field::type_id::create("");
    7. prio_level = uvm_reg_field::type_id::create("");
    8. chnl_en = uvm_reg_field::type_id::create("");//例化
    9. reserved.configure(this, 26, 6, "RO", 0, 26'h0, 1, 0 , 0);//配置
    10. pkt_len.configure(this,3,3,"RW",0,3'h0,1,1,0);//前两个表示位,后面的具体数表示默认值
    11. prio_level.configure(this,2,1,"RW",0,2'h3,1,1,0);
    12. chnl_en.configure(this,1,0,"RW",0,1'h0,1,1,0);

    什么是uvm_reg_block?

    • 一个class包含了map,rgm和mem

    什么是uvm_reg_adapter?

    • 完成rgm到硬件reg的桥接,将reg_item转换成总线UVC需要的信息bus_seq_item,再由总线给到硬件reg

    如何编写adapter?

    • 主要是定义2个reg和bus之间的函数
    1. class reg2mcdf_adapter extends uvm_reg_adapter;
    2. `uvm_object_utils()
    3. function new(string name ="");
    4. super.new(name);
    5. provides_responses=1;//enable了provides_responses,总线可以返回rsp的数据
    6. endfunction
    7. //预定义的,也是必须实现的,名字一个字都不能差
    8. function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);
    9. mcdf_bus_trans t=mcdf_bus_trnas::type_id::create("t");//创建子类对象
    10. t.cmd=(rw.kind == UVM_WRITE)? `WRITE : `READ;
    11. t.addr=rw.addr;
    12. t.wdata=rw.data;
    13. return t;//做了隐式转换
    14. endfunction//完成了寄存器级别操作rw到bus上的桥接,下面的func反之
    15. function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    16. //从driver将数据写会sequencer,adapter从sqr拿到rsp(t)后自动调用
    17. mcdf_bus_trans t;
    18. if(!$cast(t, bus_item)) begin//父类句柄,转换成子类mcdf_bus_trans句柄
    19. `uvm_fatal("", "")
    20. return; end
    21. rw.kind=(t.cmd==`WRITE)? UVM_WRITE : UVM_READ;//转换后才可以访问子类对象
    22. rw.addr=t.addr;
    23. rw.data=(t.cmd==`WRITE)? t.wdata : t.rdata;
    24. rw.status=UVM_IS_OK;
    25. endfunction
    26. endclass

    实现了adapter之后,如何将其集成到环境中?

    • 从test层传入寄存器模型rgm句柄,在顶层例化后通过uvm_config_db进行配置
    • rgm在创建后要调用build()函数,因为uvm_reg_block是object类型,其预定义的build()不会自动执行
    • 顶层连接时,需要将rgm的map组件和bus sequencer和adapter连接(reg信息-总线侧激励驱动-reg级别和硬件总线级别的桥接),adapter的桥接功能才可以工作
    1. class mcdf_bus_env extends uvm_env;
    2. mcdf_bus_agent agent;
    3. mcdf_rgm rgm;
    4. reg2mcdf_adapter reg2mcdf;//先声明
    5. ...//注册+例化
    6. function void build_phase(u p);
    7. agent=mcdf_bus_agent::type_id::create("", this);
    8. if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
    9. `uvm_info()
    10. rgm=mcdf_rgm::type_id::create("", this);
    11. end
    12. rgm.build();//创建,配置reg,调用各个reg的build,以及它们的field
    13. rgm.map.set_auto_predict();//还没集成predictor,所以调用set_auto_predict(),采用auto prediction方式
    14. reg2mcdf=reg2mcdf_adapter::type_id::create("");//例化adapter
    15. endfunction
    16. function void connect_phase(u p);
    17. rgm.map.set_sequencer(agent.sequencer, reg2mcdf);//!连接:将adapter连接到sequencer上
    18. endfunction
    19. endclass
    20. class test1 extends uvm_test;
    21. mcdf_rgm rgm;
    22. mcdf_bus_env env;
    23. ...//注册+例化
    24. function void build_phase(u p);
    25. rgm=mcdf_rgm::type_id::create("", this);
    26. uvm_config_db#(mcdf_rgm)::set(this, "env*", "rgm", rgm)
    27. env=mcdf_bus_env::type_id::create("", this);
    28. endfunction
    29. task run_phase(u p); ...
    30. endclass

    uvm_reg_adapter和uvm_reg_block的关系?

    • block>rgm>map>adapter
    • uvm_reg_predictor是什么?
      • 跟踪寄存器值,分为自动预测ap和显式预测ep
      • 显式预测ep其实就是在物理总线上用monitor来捕捉trans,通过analysis port传递给在外部例化的predictor(集成在顶层环境),集成时需要将adapter和map的句柄也传给predictor
    1. //和上一段代码的差别并不大,注释的部分就是新加的
    2. class mcdf_bus_env extends uvm_env;
    3. mcdf_bus_agent agent;
    4. mcdf_rgm rgm;
    5. reg2mcdf_adapter reg2mcdf;
    6. uvm_reg_predictor mcdf2reg_predictor;//先声明
    7. ...
    8. function void build_phase(u p);
    9. agent=mcdf_bus_agent::type_id::create("", this);
    10. if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
    11. `uvm_info()
    12. rgm=mcdf_rgm::type_id::create("", this);
    13. end
    14. rgm.build();
    15. mcdf2reg_predictor=uvm_reg_predictor::type_id::create("",this);//例化
    16. reg2mcdf=reg2mcdf_adapter::type_id::create("");
    17. mcdf2reg_predictor.map=rgm.map;//把rgm的map给到predictor的map
    18. mcdf2reg_predictor.adapter=reg2mcdf;//把rgm的adapter给到predictor的map
    19. endfunction
    20. function void connect_phase(u p);
    21. rgm.map.set_sequencer(agent.sequencer, reg2mcdf);
    22. agent.monitor.ap.connect(mcdf2reg_predictor.bus_in);//!连接:将adapter连接到sequencer上
    23. endfunction
    24. endclass

    adapter,block,predictor三者的关系是什么?

    • 见示意图

     

    如何用uvm_reg的操作方式去发送reg序列?

     

    如何用内建寄存器序列做全面的reg测试?

    • 在body()中例化内建序列,将rgm给到内建序列的model并调用序列的start即可,参数是m_sequencer
    • 放置功能覆盖率covergroup在rgm中,通过reg model generator的功能可以使生成的rgm模型自动包含各个field的功能覆盖率,rgm已经内置了方法来使能covergroup,同时调用读写方法时会自动调用covergroup::sample()完成功能覆盖率的收集
    1. class mcdf_example_seq extends uvm_reg_sequence;
    2. mcdf_rgm rgm;
    3. `uvm_object_utils()
    4. `uvm_declare_p_sequencer()
    5. ...
    6. task body();
    7. uvm_status_e status;
    8. uvm_reg_data_t data;
    9. uvm_reg_hw_reset_seq reg_rst_seq=new();//先例化
    10. uvm_reg_bit_bash_seq reg_bit_bas_seq=new();
    11. uvm_reg_access_seq reg_acc_seq=new();
    12. if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name(), "rgm", rgm)) begin
    13. `uvm_error() end
    14. @(negedge p_sequencer.vif.rstn);
    15. @(posedge p_sequencer.vif.rstn);
    16. `uvm_info()
    17. reg_rst_seq.model=rgm;//给模型
    18. reg_rst_seq.start(m_sequencer);//start即可
    19. `uvm_info()
    20. `uvm_info()
    21. reg_bit_bash_seq.model=rgm;
    22. reg_bit_bash_seq.start(m_sequencer);
    23. `uvm_info()
    24. `uvm_info()
    25. reg_acc_seq.model=rgm;
    26. reg_acc_seq.start(m_sequencer);
    27. `uvm_info()
    28. endtask
    29. endclass

    reg功能覆盖率收集方式1:内部自动收集,如何实现?

    1. //缺点:默认采样所有field,不够灵活不够智能
    2. class ctrl_reg extends uvm_reg;
    3. `uvm_object_utils(ctrl_reg)
    4. uvm_reg_field reserved;
    5. rand uvm_reg_field pkt_len;
    6. rand uvm_reg_field prio_level;
    7. rand uvm_reg_field chnl_en;
    8. covergroup value_cg;//定义一个覆盖组
    9. option.per_instance=1;
    10. reserved: coverpoint reserved.value[25:0];
    11. pkt_len: coverpoint pkt_len.value[2:0];
    12. prio_level: coverpoint prio_level.value[1:0];
    13. chnl_en: coverpoint chnl_en.value[0:0];
    14. endgroup
    15. function new(string name ="ctrl_reg");
    16. super.new(name, 32, UVM_CVR_ALL);//UVM_NO_COVERAGE改为UVM_CVR_ALL
    17. set_coverage(has_coverage(UVM_CVR_FIELD_VALS)); begin
    18. //has_coverage()查询ctrl_reg::m_has_cover是否具备特定的覆盖率类型,判断是否需要例化
    19. //是否具备对应的covergroup?->是否例化?
    20. //由于上面new已经是UVM_CVR_ALL,所以可以例化
    21. value_cg=new(); end//covergroup例化
    22. endfunction
    23. virtual function build();//这个没有变化
    24. reserved = uvm_reg_field::type_id::create("");
    25. pkt_len = uvm_reg_field::type_id::create("");
    26. prio_level = uvm_reg_field::type_id::create("");
    27. chnl_en = uvm_reg_field::type_id::create("");
    28. reserved.configure(this, 26, 6, "RO", 0, 26'h0, 1, 0 , 0);
    29. pkt_len.configure(this,3,3,"RW",0,3'h0,1,1,0);
    30. prio_level.configure(this,2,1,"RW",0,2'h3,1,1,0);
    31. chnl_en.configure(this,1,0,"RW",0,1'h0,1,1,0);
    32. endfunction
    33. function void sample(uvm_reg_data_t data,//理解为读写方法的回调函数
    34. uvm_reg_data_t byte_en,
    35. bit is_read,
    36. uvm_reg_map ,ap);
    37. super.sample(data, byte_en, is_read, map);
    38. sample_values();
    39. endfunction
    40. function void sample_values();//外部调用时ctrl_reg::sample_values()
    41. super.sample_values();
    42. if(get_coverage(UVM_CVR_FIELD_VALS)) begin
    43. //是否允许使用对应的covergroup进行采样,->是否采样?
    44. value_cg.sample(); end
    45. endfunction
    46. endclass

    reg功能覆盖率收集方式2:事件触发外部收集,如何实现?

    1. //定义一个覆盖率class
    2. class mcdf_coverage extends uvm_subscriber #(mcdf_bus_trans);
    3. //信息来自于mcdf_bus_monitor,通过其uvm_analysis_port发到
    4. //mcdf_coverage的uvm_analysis_export
    5. mcdf_rgm rgm;
    6. `uvm_component_utils(mcdf_coverage)
    7. covergroup reg_value_cg;
    8. option.per_instance=1;
    9. //指定了感兴趣的field和值范围
    10. CH0LEN: coverpoint rgm.chnl0_ctrl_reg.pkt_len.value[2:0] {bins len[]={0,1,2,3,[4:7]};}
    11. CH0PRI: coverpoint rgm.chnl0_ctrl_reg.prio_level.value[1:0];
    12. CH0CEN: coverpoint rgm.chnl0_ctrl_reg.chnl_en.value[0:0];
    13. CH1LEN: coverpoint rgm.chnl1_ctrl_reg.pkt_len.value[2:0] {bins len[]={0,1,2,3,[4:7]};}
    14. CH1PRI: coverpoint rgm.chnl1_ctrl_reg.prio_level.value[1:0];
    15. CH1CEN: coverpoint rgm.chnl1_ctrl_reg.chnl_en.value[0:0];
    16. CH2LEN: coverpoint rgm.chnl2_ctrl_reg.pkt_len.value[2:0] {bins len[]={0,1,2,3,[4:7]};}
    17. CH2PRI: coverpoint rgm.chnl2_ctrl_reg.prio_level.value[1:0];
    18. CH2CEN: coverpoint rgm.chnl2_ctrl_reg.chnl_en.value[0:0];
    19. CH0AVL: coverpoint rgm.chnl0_stat_reg.fifo_avail.value[7:0] {bins avail[]={0,1,[2:7],[8:55],[56:61], 62, 63};}
    20. CH1AVL: coverpoint rgm.chnl1_stat_reg.fifo_avail.value[7:0] {bins avail[]={0,1,[2:7],[8:55],[56:61], 62, 63};}
    21. CH2AVL: coverpoint rgm.chnl2_stat_reg.fifo_avail.value[7:0] {bins avail[]={0,1,[2:7],[8:55],[56:61], 62, 63};}
    22. LEN_COMB: cross CH0LEN, CH1LEN, CH2LEN;//cross是干嘛的?待查
    23. PRI_COMB: cross CH0PRI, CH1PRI, CH2PRI;
    24. CEN_COMB: cross CH0CEN, CH1CEN, CH2CEN;
    25. AVL_COMB: cross CH0AVL, CH1AVL, CH2AVL;
    26. endgroup
    27. function new(string name, uvm_component parent);//这个是component,要有parent
    28. if(!uvm_config_db #(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
    29. `uvm_info() end
    30. reg_value_cg=new();
    31. endfunction
    32. function void write(T t);
    33. reg_value_cg.sample();
    34. endfunction
    35. endclass

    寄存器模型rgm是什么?

    • 用软件描述了硬件的寄存器模型,类比mcdf的model,这里就是由3个控制寄存器+3个只读寄存器构成的软件模型。包含的信息一样

    rgm包含了什么?

    声明所有的寄存器+map,class必有new函数。在build函数对所有reg进行例化、配置、build(),对map例化,加入寄存器到map

    1. class mcdf_rgm extends uvm_reg_block;
    2. `uvm_object_utils(mcdf_rgm)
    3. rand ctrl_reg chnl0_ctrl_reg;
    4. rand ctrl_reg chnl1_ctrl_reg;
    5. rand ctrl_reg chnl2_ctrl_reg;
    6. rand stat_reg chnl0_stat_reg;
    7. rand stat_reg chnl1_stat_reg;
    8. rand stat_reg chnl2_stat_reg;
    9. uvm_reg_map map;
    10. function new(string name ="")
    11. super.new(name, UVM_NO_COVERAGE);
    12. endfunction
    13. virtual function build();
    14. chnl0_ctrl_reg=ctrl_reg::type_id::create("");
    15. chnl0_ctrl_reg.configure(this);//创建,配置,
    16. chnl0_ctrl_reg.build();
    17. chnl1_ctrl_reg=ctrl_reg::type_id::create("");
    18. chnl1_ctrl_reg.configure(this);
    19. chnl1_ctrl_reg.build();
    20. chnl2_ctrl_reg=ctrl_reg::type_id::create("");
    21. chnl2_ctrl_reg.configure(this);
    22. chnl2_ctrl_reg.build();
    23. chnl0_stat_reg=stat_reg::type_id::create("");
    24. chnl0_stat_reg.configure(this);
    25. chnl0_stat_reg.build();
    26. chnl1_stat_reg=stat_reg::type_id::create("");
    27. chnl1_stat_reg.configure(this);
    28. chnl1_stat_reg.build();
    29. chnl2_stat_reg=stat_reg::type_id::create("");
    30. chnl2_stat_reg.configure(this);
    31. chnl2_stat_reg.build();
    32. map=create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);//map名字,基地址,位宽,endianess
    33. map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");//偏移地址offset addr
    34. map.add_reg(chnl1_ctrl_reg, 32'h00000004, "RW");//完整reg地址addr=base addr+offset addr
    35. map.add_reg(chnl2_ctrl_reg, 32'h00000008, "RW");
    36. map.add_reg(chnl0_stat_reg, 32'h00000010, "RO");//添加每个reg的偏移地址和访问属性(模式)
    37. map.add_reg(chnl1_stat_reg, 32'h00000014, "RO");
    38. map.add_reg(chnl2_stat_reg, 32'h00000018, "RO");
    39. lock_model();//reg构建后锁住这个模型,不允许外部访问寄存器内部,只可以用预测组件来改变
    40. endfunction
    41. endclass
    • 8位地址线32位数据线总线UVC的例码如下,具体细节应该在学习各个组件的时候已经被覆盖,这里主要关注结构。
    1. class mcdf_bus_trans extends uvm_sequence_item;
    2. rand bit[1:0] cmd;
    3. rand bit[7:0] addr;
    4. rand bit[31:0] data;
    5. bit[31:0] rdata;//从总线读回来的,不可随机化
    6. `uvm_object_utils_begin()...//注册以及域的自动化
    7. ...endclass
    8. class mcdf_bus_sequencer extends uvm_sequencer;
    9. virtual mcdf_if vif;
    10. `uvm_component_utils()
    11. ...//注册,例化
    12. function void build_phase(u p);
    13. if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif))begin
    14. `uvm_error("", "") end
    15. endfunction
    16. endclass
    17. class mcdf_bus_monitor extends uvm_monitor;//等下连接到uvm_reg_predictor
    18. virtual mcdf_if vif;
    19. uvm_analysis_port #(mcdf_bus_trans) ap;//monitor要广播
    20. `uvm_component_utils()
    21. ...
    22. function void build_phase(u p);
    23. if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif))begin
    24. `uvm_error("", "") end
    25. ap =new("", this);//不能用create,因为port不是object
    26. endfunction
    27. task run_phase(u p);
    28. forever begin
    29. mon_trnas() end
    30. endtask
    31. task mon_trans();
    32. mcdf_bus_trans t;
    33. @(posedge vif.clk);
    34. if(vif.cmd==`WRITE) begin
    35. t=new();
    36. t.cmd=`WRITE;
    37. t.addr=vif.addr;
    38. t.wdata=vif.wdata;
    39. ap.write(t);
    40. end
    41. else if(vif.cmd==`READ) begin
    42. t=new();
    43. t.cmd=`READ;
    44. t.addr=vif.addr;
    45. fork begin//等下一个周期
    46. @(posedge vif.clk);
    47. #10ps;
    48. t.rdata=vif.rdata;
    49. ap.write(t); end
    50. join_none//的同时不错过下一拍
    51. end
    52. endtask
    53. endclass
    54. class mcdf_bus_driver extends uvm_driver;
    55. virtual mcdf_if vif;
    56. ...//注册+例化
    57. function void build_phase(u p);
    58. if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif))begin
    59. `uvm_error("", "") end
    60. endfunction
    61. task run_phase(u p);
    62. REQ tmp;
    63. mcdf_bus_trans req, rsp;
    64. reset_listener();
    65. forever begin
    66. seq_item_port.get_next_item(tmp);
    67. void'($cast(req,tmp));
    68. `uvm_info("got")
    69. void'($cast(rsp, req.clone()));
    70. rsp.set_sequence_id(req.get_sequence_id());
    71. rsp.set_transaction_id(req.get_transaction_id());
    72. driver_bus(rsp);
    73. set_item_port.item_done(rsp);
    74. `uvm_info("sent")
    75. end
    76. endtask
    77. task reset_listener();
    78. fork
    79. forever begin
    80. @(negdege vif.rstn) drive_idle();
    81. end
    82. join_none
    83. endtask
    84. task drive_bus(mcdf_bus_trans t);
    85. case (t.cmd)
    86. `WRITE:drive_write(t);
    87. `READ :drive_read(t);
    88. `IDLE :drive_idle();
    89. default: `uvm_error()
    90. endcase
    91. endtask
    92. task drive_write(mcdf_bus_trans t);
    93. @(posedge vif.clk);
    94. vif.cmd<=t.cmd;
    95. vif.addr<=t.addr;
    96. vif.data<=t.wdata;
    97. endtask
    98. task drive_read(mcdf_bus_trans t);
    99. @(posedge vif.clk);
    100. vif.cmd<=t.cmd;
    101. vif.addr<=t.addr;
    102. @(posedge vif.clk);
    103. #10ps;
    104. t.rdata=vif.rdata;
    105. endtask
    106. task drive_idle(bit is_sync=0);
    107. if(is_sync) @(posedge vif.clk);
    108. vif.cmd<='h0;
    109. vif.addr<='h0;
    110. vif.data<='h0;
    111. endtask
    112. endclass
    113. class mcdf_bus_agent extends uvm_agent;
    114. mcdf_bus_driver driver;
    115. mcdf_bus_sequencer sequencer;
    116. mcdf_bus_monitor monitor;
    117. ...
    118. function void build_phase(u p);
    119. driver =mcdf_bus_driver::type_id::create("", this);
    120. sequencer=mcdf_bus_sequencer::type_id::create("", this);
    121. monitor =mcdf_bus_monitor::type_id::create("", this);
    122. endfunction
    123. function void connect_phase(u p);
    124. driver.seq_item_port.connect(sequencer.seq_item_export);
    125. endfunction
    126. endclass

    实验任务

    1.1 实现reg2mcdf_adapter类的两个方法

    这里是对应adapter的定义,主要就是两个方法的书写,一个是reg2bus,另一个是bus2reg,在做reg和总线的转换之间。

    先复习一下之前的例码

    1. function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);
    2. mcdf_bus_trans t=mcdf_bus_trnas::type_id::create("t");
    3. t.cmd=(rw.kind == UVM_WRITE)? `WRITE : `READ;
    4. t.addr=rw.addr;
    5. t.wdata=rw.data;
    6. return t;
    7. endfunction
    8. function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    9. //从driver将数据写会sequencer,adapter从sqr拿到rsp(t)后自动调用
    10. mcdf_bus_trans t;
    11. if(!$cast(t, bus_item)) begin
    12. `uvm_fatal("", "")
    13. return; end
    14. rw.kind=(t.cmd==`WRITE)? UVM_WRITE : UVM_READ;
    15. rw.addr=t.addr;
    16. rw.data=(t.cmd==`WRITE)? t.wdata : t.rdata;
    17. rw.status=UVM_IS_OK;
    18. endfunction

    实验5代码如下,rgm是单独的pkg文件

    1. class reg2mcdf_adapter extends uvm_reg_adapter;
    2. `uvm_object_utils(reg2mcdf_adapter)
    3. function new(string name = "reg2mcdf_adapter");
    4. super.new(name);
    5. provides_responses = 1;//一样需要置1
    6. endfunction
    7. function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    8. reg_trans t = reg_trans::type_id::create("t");//例化寄存器事务的句柄
    9. t.cmd = (rw.kind == UVM_WRITE) ? `WRITE : `READ;
    10. t.addr = rw.addr;//完全相同,判断cmd后将信息给到对象上
    11. t.data = rw.data;
    12. return t;
    13. endfunction
    14. function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    15. reg_trans t;//声明
    16. if (!$cast(t, bus_item)) begin//总线句柄转换成子类reg事务句柄
    17. `uvm_fatal("CASTFAIL","Provided bus_item is not of the correct type")
    18. return;
    19. end
    20. rw.kind = (t.cmd == `WRITE) ? UVM_WRITE : UVM_READ;//完全相同
    21. rw.addr = t.addr;
    22. rw.data = t.data;
    23. rw.status = UVM_IS_OK;
    24. endfunction
    25. endclass

     1.2 在mcdf_env中声明并例化block、adapter、predictor,最后connect起来

    也就是集成到env中,先声明,build_phase中创建对象并传递对应地图,最后connect_phase连接ap端口。

    先复习下例码的继承adapter+predictor

    1. //和上一段代码的差别并不大,注释的部分就是新加的
    2. class mcdf_bus_env extends uvm_env;
    3. mcdf_bus_agent agent;
    4. mcdf_rgm rgm;
    5. reg2mcdf_adapter reg2mcdf;
    6. uvm_reg_predictor mcdf2reg_predictor;//先声明
    7. ...
    8. function void build_phase(u p);
    9. agent=mcdf_bus_agent::type_id::create("", this);
    10. if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
    11. `uvm_info()
    12. rgm=mcdf_rgm::type_id::create("", this);
    13. end
    14. rgm.build();
    15. mcdf2reg_predictor=uvm_reg_predictor::type_id::create("",this);//例化
    16. reg2mcdf=reg2mcdf_adapter::type_id::create("");
    17. mcdf2reg_predictor.map=rgm.map;//把rgm的map给到predictor的map
    18. mcdf2reg_predictor.adapter=reg2mcdf;//把rgm的adapter给到predictor的map
    19. endfunction
    20. function void connect_phase(u p);
    21. rgm.map.set_sequencer(agent.sequencer, reg2mcdf);
    22. agent.monitor.ap.connect(mcdf2reg_predictor.bus_in);//!连接:将adapter连接到sequencer上
    23. endfunction
    24. endclass

    实验5代码如下:

    1. class mcdf_env extends uvm_env;
    2. chnl_agent chnl_agts[3];
    3. reg_agent reg_agt;
    4. fmt_agent fmt_agt;
    5. mcdf_checker chker;
    6. mcdf_coverage cvrg;
    7. mcdf_virtual_sequencer virt_sqr;
    8. mcdf_rgm rgm;//声明三者的句柄
    9. reg2mcdf_adapter adapter;
    10. uvm_reg_predictor #(reg_trans) predictor;
    11. `uvm_component_utils(mcdf_env)
    12. function new (string name = "mcdf_env", uvm_component parent);
    13. super.new(name, parent);
    14. endfunction
    15. function void build_phase(uvm_phase phase);
    16. super.build_phase(phase);
    17. this.chker = mcdf_checker::type_id::create("chker", this);
    18. foreach(chnl_agts[i]) begin
    19. this.chnl_agts[i] = chnl_agent::type_id::create($sformatf("chnl_agts[%0d]",i), this);
    20. end
    21. this.reg_agt = reg_agent::type_id::create("reg_agt", this);
    22. this.fmt_agt = fmt_agent::type_id::create("fmt_agt", this);
    23. this.cvrg = mcdf_coverage::type_id::create("cvrg", this);
    24. virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr", this);
    25. //进行例化,注意rgm还要手动调用build
    26. rgm = mcdf_rgm::type_id::create("rgm", this);
    27. rgm.build();
    28. adapter = reg2mcdf_adapter::type_id::create("adapter", this);
    29. predictor = uvm_reg_predictor#(reg_trans)::type_id::create("predictor", this);
    30. endfunction
    31. function void connect_phase(uvm_phase phase);
    32. super.connect_phase(phase);
    33. chnl_agts[0].monitor.mon_bp_port.connect(chker.chnl0_bp_imp);
    34. chnl_agts[1].monitor.mon_bp_port.connect(chker.chnl1_bp_imp);
    35. chnl_agts[2].monitor.mon_bp_port.connect(chker.chnl2_bp_imp);
    36. reg_agt.monitor.mon_bp_port.connect(chker.reg_bp_imp);
    37. fmt_agt.monitor.mon_bp_port.connect(chker.fmt_bp_imp);
    38. virt_sqr.reg_sqr = reg_agt.sequencer;
    39. virt_sqr.fmt_sqr = fmt_agt.sequencer;
    40. foreach(virt_sqr.chnl_sqrs[i]) virt_sqr.chnl_sqrs[i] = chnl_agts[i].sequencer;
    41. //进行连接
    42. rgm.map.set_sequencer(reg_agt.sequencer, adapter);
    43. //连接adapter到reg的agent中的sequencer。rgm的连接时调用map中的set_sequencer
    44. reg_agt.monitor.mon_ana_port.connect(predictor.bus_in);
    45. //monitor的ap连接到predictor的bus_in
    46. predictor.map = rgm.map;//rgm的map和adapter配置到predictor
    47. predictor.adapter = adapter;
    48. virt_sqr.rgm = rgm;
    49. endfunction
    50. endclass: mcdf_env

    2.1 reg block句柄的传递

    在上面例码中的connect_phase,将rgm对象给到vser中的rgm句柄

    1. //TODO-2.1 connect the virtual sequencer's rgm handle with rgm object
    2. virt_sqr.rgm = rgm;

    在vser类mcdf_base_virtual_sequence的body()中,将p_sequencer的rgm赋给rgm句柄

    1. class mcdf_base_virtual_sequence extends uvm_sequence;
    2. idle_reg_sequence idle_reg_seq;
    3. write_reg_sequence write_reg_seq;
    4. read_reg_sequence read_reg_seq;
    5. chnl_data_sequence chnl_data_seq;
    6. fmt_config_sequence fmt_config_seq;
    7. mcdf_rgm rgm;
    8. `uvm_object_utils(mcdf_base_virtual_sequence)
    9. `uvm_declare_p_sequencer(mcdf_virtual_sequencer)
    10. function new (string name = "mcdf_base_virtual_sequence");
    11. super.new(name);
    12. endfunction
    13. virtual task body();
    14. `uvm_info(get_type_name(), "=====================STARTED=====================", UVM_LOW)
    15. //TODO-2.1 connect rgm handle
    16. rgm = p_sequencer.rgm;
    17. this.do_reg();
    18. this.do_formatter();
    19. this.do_data();
    20. `uvm_info(get_type_name(), "=====================FINISHED=====================", UVM_LOW)
    21. endtask
    22. virtual task do_reg();
    23. endtask
    24. virtual task do_formatter();
    25. endtask
    26. virtual task do_data();
    27. endtask
    28. virtual function bit diff_value(int val1, int val2, string id = "value_compare");
    29. if(val1 != val2) begin
    30. `uvm_error("[CMPERR]", $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2))
    31. return 0;
    32. end
    33. else begin
    34. `uvm_info("[CMPSUC]", $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2), UVM_LOW)
    35. return 1;
    36. end
    37. endfunction
    38. endclass

    2.2 mcdf_data_consistence_basic_virtual_sequence中原来是由总线seq实现的reg读写,现在改为由rgm操作的reg读写方式

    原来的      rd_val = read_reg_seq.data;和`uvm_do_on_with()被替换成rgm.对应rge.write/read

    总线seq实现reg读写是利用了uvm提供的`uvm_do_on_with(),

    1. class mcdf_data_consistence_basic_virtual_sequence extends mcdf_base_virtual_sequence;
    2. `uvm_object_utils(mcdf_data_consistence_basic_virtual_sequence)
    3. function new (string name = "mcdf_data_consistence_basic_virtual_sequence");
    4. super.new(name);
    5. endfunction
    6. task do_reg();
    7. bit[31:0] wr_val, rd_val;
    8. uvm_status_e status;//这里多声明了status
    9. // slv0 with len=8, prio=0, en=1
    10. wr_val = (1<<3)+(0<<1)+1;
    11. //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR; data == wr_val;})
    12. //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR;})
    13. //将原来用do on with做的换成rgm的readwrite
    14. rgm.chnl0_ctrl_reg.write(status, wr_val);
    15. rgm.chnl0_ctrl_reg.read(status, rd_val);
    16. //rd_val = read_reg_seq.data;
    17. void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));
    18. // slv1 with len=16, prio=1, en=1
    19. wr_val = (2<<3)+(1<<1)+1;
    20. //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR; data == wr_val;})
    21. //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR;})
    22. //rd_val = read_reg_seq.data;
    23. rgm.chnl1_ctrl_reg.write(status, wr_val);
    24. rgm.chnl1_ctrl_reg.read(status, rd_val);
    25. void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));
    26. // slv2 with len=32, prio=2, en=1
    27. wr_val = (3<<3)+(2<<1)+1;
    28. //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR; data == wr_val;})
    29. //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR;})
    30. //rd_val = read_reg_seq.data;
    31. rgm.chnl2_ctrl_reg.write(status, wr_val);
    32. rgm.chnl2_ctrl_reg.read(status, rd_val);
    33. void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));
    34. // send IDLE command
    35. `uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
    36. endtask
    37. task do_formatter();
    38. `uvm_do_on_with(fmt_config_seq, p_sequencer.fmt_sqr, {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;})
    39. endtask
    40. task do_data();
    41. fork
    42. `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; })
    43. `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;})
    44. `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;})
    45. join
    46. #10us; // wait until all data haven been transfered through MCDF
    47. endtask
    48. endclass: mcdf_data_consistence_basic_virtual_sequence

     2.3 mcdf_full_random_virtual_sequence做相同的操作,改为由rgm预先设置寄存器值,再统一做总线寄存器更新的方式,稍后由后门读取的方式取得寄存器值加以比较。

      //TODO-2.3 Follow the instructions below
      //  -reset the register block
      //  -set all value of WR registers via uvm_reg::set()
      //  -update them via uvm_reg_block::update()
      //  -compare the register value via uvm_reg::mirror() with backdoor access

    1. class mcdf_full_random_virtual_sequence extends mcdf_base_virtual_sequence;
    2. `uvm_object_utils(mcdf_base_virtual_sequence)
    3. function new (string name = "mcdf_base_virtual_sequence");
    4. super.new(name);
    5. endfunction
    6. //最顶格开始的为修改代码
    7. task do_reg();
    8. //bit[31:0] wr_val, rd_val;
    9. bit[31:0] ch0_wr_val;//换成三个,不需要rd_val
    10. bit[31:0] ch1_wr_val;
    11. bit[31:0] ch2_wr_val;
    12. uvm_status_e status;
    13. rgm.reset();//重启reg block
    14. // slv0 with len={4,8,16,32}, prio={[0:3]}, en={[0:1]}原来是要*3
    15. //wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
    16. //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR; data == wr_val;})
    17. //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR;})
    18. //rd_val = read_reg_seq.data;
    19. //void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));
    20. ch0_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
    21. ch1_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
    22. ch2_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
    23. rgm.chnl0_ctrl_reg.set(ch0_wr_val);
    24. rgm.chnl1_ctrl_reg.set(ch1_wr_val);//通过uvm_reg::set()设置rgm中reg的wr_val
    25. rgm.chnl2_ctrl_reg.set(ch2_wr_val);
    26. rgm.update(status);//通过uvm_reg_block::update()刷新rgm数据
    27. #100ns;//等待dut中update完成
    28. rgm.chnl0_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);//后门访问获取reg值
    29. rgm.chnl1_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);//比较所有读写值
    30. rgm.chnl2_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);
    31. // send IDLE command
    32. `uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
    33. endtask
    34. task do_formatter();
    35. `uvm_do_on_with(fmt_config_seq, p_sequencer.fmt_sqr, {fifo inside {SHORT_FIFO, ULTRA_FIFO}; bandwidth inside {LOW_WIDTH, ULTRA_WIDTH};})
    36. endtask
    37. task do_data();
    38. fork
    39. `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[0],
    40. {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};})
    41. `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[1],
    42. {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};})
    43. `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[2],
    44. {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};})
    45. join
    46. #10us; // wait until all data haven been transfered through MCDF
    47. endtask
    48. endclass: mcdf_full_random_virtual_sequence

    3.1 利用寄存器的内建序列做测试

    用到的三个内建序列 -uvm_reg_hw_reset_seq -uvm_reg_bit_bash_seq -uvm_reg_access_seq

    先复习一下例码,在body中例化对应序列后,将rgm给到序列的model,然后调用序列的start(参数是m_sequencer,应该是说对应的user自己的寄存器sequencer)

    1. class mcdf_example_seq extends uvm_reg_sequence;
    2. mcdf_rgm rgm;
    3. `uvm_object_utils()
    4. `uvm_declare_p_sequencer()
    5. ...
    6. task body();
    7. uvm_status_e status;
    8. uvm_reg_data_t data;
    9. uvm_reg_hw_reset_seq reg_rst_seq=new();
    10. uvm_reg_bit_bash_seq reg_bit_bas_seq=new();
    11. uvm_reg_access_seq reg_acc_seq=new();
    12. if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name(), "rgm", rgm)) begin
    13. `uvm_error() end
    14. @(negedge p_sequencer.vif.rstn);
    15. @(posedge p_sequencer.vif.rstn);
    16. `uvm_info()
    17. reg_rst_seq.model=rgm;
    18. reg_rst_seq.start(m_sequencer);
    19. `uvm_info()
    20. `uvm_info()
    21. reg_bit_bash_seq.model=rgm;
    22. reg_bit_bash_seq.start(m_sequencer);
    23. `uvm_info()
    24. `uvm_info()
    25. reg_acc_seq.model=rgm;
    26. reg_acc_seq.start(m_sequencer);
    27. `uvm_info()
    28. endtask
    29. endclass

    实验5代码如下

    1. class mcdf_reg_builtin_virtual_sequence extends mcdf_base_virtual_sequence;
    2. `uvm_object_utils(mcdf_reg_builtin_virtual_sequence)
    3. function new (string name = "mcdf_reg_builtin_virtual_sequence");
    4. super.new(name);
    5. endfunction
    6. task do_reg();//这里是do_reg不是body?回头复习整理时查查
    7. uvm_reg_hw_reset_seq reg_rst_seq = new(); //一样的先例化内建序列
    8. uvm_reg_bit_bash_seq reg_bit_bash_seq = new();
    9. uvm_reg_access_seq reg_acc_seq = new();
    10. // 等待复位信号
    11. @(negedge p_sequencer.intf.rstn);
    12. @(posedge p_sequencer.intf.rstn);
    13. `uvm_info("BLTINSEQ", "register reset sequence started", UVM_LOW)
    14. rgm.reset();//先调用reset
    15. reg_rst_seq.model = rgm;
    16. reg_rst_seq.start(p_sequencer.reg_sqr);//这里的参数是p_sequencer的reg_sqr?
    17. `uvm_info("BLTINSEQ", "register reset sequence finished", UVM_LOW)
    18. `uvm_info("BLTINSEQ", "register bit bash sequence started", UVM_LOW)
    19. // 在执行下个序列前先复位
    20. p_sequencer.intf.rstn <= 'b0;
    21. repeat(5) @(posedge p_sequencer.intf.clk);
    22. p_sequencer.intf.rstn <= 'b1;
    23. rgm.reset();
    24. reg_bit_bash_seq.model = rgm;
    25. reg_bit_bash_seq.start(p_sequencer.reg_sqr);
    26. `uvm_info("BLTINSEQ", "register bit bash sequence finished", UVM_LOW)
    27. `uvm_info("BLTINSEQ", "register access sequence started", UVM_LOW)
    28. p_sequencer.intf.rstn <= 'b0;
    29. repeat(5) @(posedge p_sequencer.intf.clk);
    30. p_sequencer.intf.rstn <= 'b1;
    31. rgm.reset();
    32. reg_acc_seq.model = rgm;
    33. reg_acc_seq.start(p_sequencer.reg_sqr);
    34. `uvm_info("BLTINSEQ", "register access sequence finished", UVM_LOW)
    35. endtask
    36. endclass: mcdf_reg_builtin_virtual_sequence
  • 相关阅读:
    如何借助网关实现组态软件的摄像头视频监控?
    SSM--关联关系映射
    【css】H8_定位
    【STM32学习(4)】STM32简述定时器
    2024.7.1 刷题总结
    leetcode:69. x 的平方根
    技术干货 | 基于 MindSpore 实现图像分割之豪斯多夫距离
    第六章 Java newCachedThreadPool 示例
    为了买个硬盘,我专门写了篇笔记
    GitHub:新手如何快速上手
  • 原文地址:https://blog.csdn.net/weixin_39668316/article/details/126394946