• 电力电子转战数字IC20220819day64——uvm实战1A


    目录

    MCDF改造

    APB接口

    验证环境的改造

    实战任务

    apb_config

    apb_master_agent

    apb_master_driver

    1.1实现apb_master_driver的drive_transfer()

    apb_master_monitor

     1.3编写apb_master_monitor中的collect_transfer()

    apb_master_sequence_Lib

    1.2编写apb_master_sequence中的apb_master_write_seq::body()任务

    apb_master_sequencer

    slave端

    apb_slave_driver

     1.4编写apb_slave_driver的drive_response()

    apb_slave_seq_lib

    为什么要让sequence控制driver?


    这里涉及的是VIP的开发和MCDF用APB改造。

    MCDF改造

     所有的接口都发生了改变,对应在硬件中都做了修改。

    APB接口

     特点是一总(AHB或者AXI)多从,1总通过APB桥发放到后面的多从slave;2个clk完成1次W/R操作,用状态机表示为idle->setup->enable

    验证环境的改造

    由于接口几乎全部被更新,所以mcdf这一层的接口和tb的接口也需要做相应的更改。

    这里似乎没有涉及到env的问题,等下看看具体代码。

    实战任务

     

     直接看到APB部分的文件夹,可以看到文件数量大大增加,采取了`include的方式将文件分开了。

    apb_pkg只有简单的几行代码,信息都包含在别的文件中。

    1. `ifndef APB_PKG_SV
    2. `define APB_PKG_SV
    3. package apb_pkg;
    4. import uvm_pkg::*;
    5. `include "uvm_macros.svh"
    6. `include "apb.svh"
    7. endpackage : apb_pkg
    8. `endif // `ifndef APB_PKG_SV

    apb_pkg include了apb.svh,而后者include了主从的各个组件的sv和svh文件。

    1. `ifndef APB_SVH
    2. `define APB_SVH
    3. `include "apb_transfer.sv"
    4. `include "apb_config.sv"
    5. `include "apb_master_driver.svh"
    6. `include "apb_master_monitor.svh"
    7. `include "apb_master_sequencer.svh"
    8. `include "apb_master_agent.svh"
    9. `include "apb_slave_driver.svh"
    10. `include "apb_slave_monitor.svh"
    11. `include "apb_slave_sequencer.svh"
    12. `include "apb_slave_agent.svh"
    13. `include "apb_master_driver.sv"
    14. `include "apb_master_monitor.sv"
    15. `include "apb_master_sequencer.sv"
    16. `include "apb_master_agent.sv"
    17. `include "apb_master_seq_lib.sv"
    18. `include "apb_slave_driver.sv"
    19. `include "apb_slave_monitor.sv"
    20. `include "apb_slave_sequencer.sv"
    21. `include "apb_slave_agent.sv"
    22. `include "apb_slave_seq_lib.sv"
    23. `endif // `ifndef APB_SVH

    在apb_tb中,include了test.svh和if.sv,要跑起仿真只需要编译pkg和tb即可(其他文件都被include了,其实也会被编译到)。tb只做了接口的配置

    1. `timescale 1ps/1ps
    2. import uvm_pkg::*;
    3. `include "uvm_macros.svh"
    4. `include "apb_tests.svh"
    5. `include "apb_if.sv"
    6. module apb_tb;
    7. bit clk, rstn;
    8. initial begin
    9. fork
    10. begin
    11. forever #5ns clk = !clk;
    12. end
    13. begin
    14. #100ns;
    15. rstn <= 1'b1;
    16. #100ns;
    17. rstn <= 1'b0;
    18. #100ns;
    19. rstn <= 1'b1;
    20. end
    21. join_none
    22. end
    23. apb_if intf(clk, rstn);
    24. initial begin
    25. uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.mst", "vif", intf);
    26. uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.slv", "vif", intf);
    27. run_test("apb_single_transaction_test");
    28. end
    29. endmodule

    在apb_if中,包括了接口+时钟块,覆盖组covergroup。接口信号按照时序图给出,时钟块分为master和slave,信号的方向互补,monitor的时钟块所有信号都是input。

    这里又遇到覆盖率收集的问题,复习一下。定义覆盖组后再initial块中例化各个覆盖组。

    https://mp.csdn.net/mp_blog/creation/editor/125731520

    1. `ifndef APB_IF_SV
    2. `define APB_IF_SV
    3. interface apb_if (input clk, input rstn);
    4. logic [31:0] paddr;
    5. logic pwrite;
    6. logic psel;
    7. logic penable;
    8. logic [31:0] pwdata;
    9. logic [31:0] prdata;
    10. // Control flags
    11. bit has_checks = 1;
    12. bit has_coverage = 1;
    13. // Actual Signals
    14. // USER: Add interface signals
    15. clocking cb_mst @(posedge clk);
    16. // USER: Add clocking block detail
    17. default input #1ps output #1ps;
    18. output paddr, pwrite, psel, penable, pwdata;
    19. input prdata;
    20. endclocking : cb_mst
    21. clocking cb_slv @(posedge clk);
    22. // USER: Add clocking block detail
    23. default input #1ps output #1ps;
    24. input paddr, pwrite, psel, penable, pwdata;
    25. output prdata;
    26. endclocking : cb_slv
    27. clocking cb_mon @(posedge clk);
    28. // USER: Add clocking block detail
    29. default input #1ps output #1ps;
    30. input paddr, pwrite, psel, penable, pwdata, prdata;
    31. endclocking : cb_mon
    32. // Coverage and assertions to be implemented here.
    33. // USER: Add assertions/coverage here
    34. // APB command covergroup
    35. covergroup cg_apb_command @(posedge clk iff rstn);
    36. pwrite: coverpoint pwrite{
    37. type_option.weight = 0;
    38. bins write = {1};
    39. bins read = {0};
    40. }
    41. psel : coverpoint psel{
    42. type_option.weight = 0;
    43. bins sel = {1};
    44. bins unsel = {0};
    45. }
    46. cmd : cross pwrite, psel{
    47. bins cmd_write = binsof(psel.sel) && binsof(pwrite.write);
    48. bins cmd_read = binsof(psel.sel) && binsof(pwrite.read);
    49. bins cmd_idle = binsof(psel.unsel);
    50. }
    51. endgroup
    52. // APB transaction timing group
    53. covergroup cg_apb_trans_timing_group @(posedge clk iff rstn);
    54. psel: coverpoint psel{
    55. bins single = (0 => 1 => 1 => 0);
    56. bins burst_2 = (0 => 1 [*4] => 0);
    57. bins burst_4 = (0 => 1 [*8] => 0);
    58. bins burst_8 = (0 => 1 [*16] => 0);
    59. bins burst_16 = (0 => 1 [*32] => 0);
    60. bins burst_32 = (0 => 1 [*64] => 0);
    61. }
    62. penable: coverpoint penable {
    63. bins single = (0 => 1 => 0 [*2:10] => 1);
    64. bins burst = (0 => 1 => 0 => 1);
    65. }
    66. endgroup
    67. // APB write & read order group
    68. covergroup cg_apb_write_read_order_group @(posedge clk iff (rstn && penable));
    69. write_read_order: coverpoint pwrite{
    70. bins write_write = (1 => 1);
    71. bins write_read = (1 => 0);
    72. bins read_write = (0 => 1);
    73. bins read_read = (0 => 0);
    74. }
    75. endgroup
    76. initial begin
    77. automatic cg_apb_command cg0 = new();
    78. automatic cg_apb_trans_timing_group cg1 = new();
    79. automatic cg_apb_write_read_order_group cg2 = new();
    80. end
    81. endinterface : apb_if
    82. `endif // APB_IF_SV

    关于test文件,画了个框图如下:

     接下来再逐个看各个组件

    apb_config

    这是等下用来配置agent是active还是passive用的object

    1. class apb_config extends uvm_object;
    2. `uvm_object_utils(apb_config)
    3. // USER to specify the config items
    4. uvm_active_passive_enum is_active = UVM_ACTIVE;
    5. function new (string name = "apb_config");
    6. super.new(name);
    7. endfunction : new
    8. endclass
    9. `endif // APB_CONFIG_SV

    apb_master_agent

    由于使用了头文件,这里的方法名都要加上apb_master_agent::

    agent做了三件事:build()例化对象,connect()连接driver和sequencer(TLM通信,两个组件自带的seq_item_PORT)

    assign_vi配置接口

    1. //头文件
    2. `ifndef APB_MASTER_AGENT_SVH
    3. `define APB_MASTER_AGENT_SVH
    4. class apb_master_agent extends uvm_agent;
    5. apb_config cfg;
    6. apb_master_driver driver;
    7. apb_master_sequencer sequencer;
    8. apb_master_monitor monitor;
    9. virtual apb_if vif;
    10. `uvm_component_utils_begin(apb_master_agent)
    11. // USER: Register your fields here
    12. `uvm_component_utils_end
    13. // new - constructor
    14. extern function new (string name, uvm_component parent);
    15. // uvm build phase
    16. extern function void build();
    17. // uvm connection phase
    18. extern function void connect();
    19. // This method assigns the virtual interfaces to the agent's children
    20. extern function void assign_vi(virtual apb_if vif);
    21. endclass : apb_master_agent
    22. `endif // APB_MASTER_AGENT_SVH
    23. //sv文件
    24. `ifndef APB_MASTER_AGENT_SV
    25. `define APB_MASTER_AGENT_SV
    26. //由于使用了头文件,这里的方法名都要加上apb_master_agent::
    27. function apb_master_agent::new(string name, uvm_component parent);
    28. super.new(name, parent);
    29. endfunction : new
    30. function void apb_master_agent::build();
    31. super.build();
    32. // get config
    33. if( !uvm_config_db#(apb_config)::get(this,"","cfg", cfg)) begin
    34. `uvm_warning("GETCFG","cannot get config object from config DB")
    35. cfg = apb_config::type_id::create("cfg");
    36. end
    37. // get virtual interface
    38. if( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin
    39. `uvm_fatal("GETVIF","cannot get vif handle from config DB")
    40. end
    41. monitor = apb_master_monitor::type_id::create("monitor",this);
    42. monitor.cfg = cfg;
    43. if(cfg.is_active == UVM_ACTIVE) begin
    44. sequencer = apb_master_sequencer::type_id::create("sequencer",this);
    45. sequencer.cfg = cfg;
    46. driver = apb_master_driver::type_id::create("driver",this);
    47. driver.cfg = cfg;
    48. end
    49. endfunction : build
    50. function void apb_master_agent::connect();
    51. assign_vi(vif);
    52. if(is_active == UVM_ACTIVE) begin
    53. driver.seq_item_port.connect(sequencer.seq_item_export);
    54. end
    55. endfunction : connect
    56. function void apb_master_agent::assign_vi(virtual apb_if vif);
    57. monitor.vif = vif;
    58. if (is_active == UVM_ACTIVE) begin
    59. sequencer.vif = vif;
    60. driver.vif = vif;
    61. end
    62. endfunction : assign_vi
    63. `endif // APB_MASTER_AGENT_SV

    apb_master_driver

    1. `ifndef APB_MASTER_DRIVER_SVH
    2. `define APB_MASTER_DRIVER_SVH
    3. class apb_master_driver extends uvm_driver #(apb_transfer);
    4. apb_config cfg;
    5. `uvm_component_utils_begin(apb_master_driver)
    6. `uvm_component_utils_end
    7. extern function new (string name, uvm_component parent);
    8. extern virtual task run();
    9. virtual apb_if vif;
    10. extern virtual protected task get_and_drive();
    11. extern virtual protected task drive_transfer(apb_transfer t);
    12. extern virtual protected task reset_listener();
    13. extern protected task do_idle();
    14. extern protected task do_write(apb_transfer t);
    15. extern protected task do_read(apb_transfer t);
    16. endclass : apb_master_driver
    17. `endif // APB_MASTER_DRIVER_SVH

     driver的run_phase同时运行两个线程:get and drive和复位。

    而1.1就是要编写drive这个方法,对应APB的idle和读写,分别对应三个方法。在三个方法中根据时序图编写,addr和write在idle时保持不变(为了省电),时序图和代码如下。

     

     

    1. `ifndef APB_MASTER_DRIVER_SV
    2. `define APB_MASTER_DRIVER_SV
    3. function apb_master_driver::new (string name, uvm_component parent);
    4. super.new(name, parent);
    5. endfunction : new
    6. task apb_master_driver::run();
    7. fork
    8. get_and_drive();
    9. reset_listener();
    10. join_none
    11. endtask : run
    12. task apb_master_driver::get_and_drive();
    13. forever begin
    14. seq_item_port.get_next_item(req);
    15. `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
    16. drive_transfer(req);
    17. void'($cast(rsp, req.clone()));
    18. rsp.set_sequence_id(req.get_sequence_id());
    19. seq_item_port.item_done(rsp);
    20. `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
    21. end
    22. endtask : get_and_drive
    23. task apb_master_driver::drive_transfer (apb_transfer t);
    24. `uvm_info(get_type_name(), "drive_transfer", UVM_HIGH)
    25. case(t.trans_kind)
    26. IDLE : this.do_idle();
    27. WRITE : this.do_write(t);
    28. READ : this.do_read(t);
    29. default : `uvm_error("ERRTYPE", "unrecognized transaction type")
    30. endcase
    31. endtask : drive_transfer
    32. task apb_master_driver::do_write(apb_transfer t);
    33. `uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
    34. @(vif.cb_mst);
    35. vif.cb_mst.paddr <= t.addr;
    36. vif.cb_mst.pwrite <= 1;
    37. vif.cb_mst.psel <= 1;
    38. vif.cb_mst.penable <= 0;
    39. vif.cb_mst.pwdata <= t.data;
    40. @(vif.cb_mst);
    41. vif.cb_mst.penable <= 1;
    42. repeat(t.idle_cycles) this.do_idle();
    43. endtask: do_write
    44. task apb_master_driver::do_read(apb_transfer t);
    45. `uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
    46. @(vif.cb_mst);
    47. vif.cb_mst.paddr <= t.addr;
    48. vif.cb_mst.pwrite <= 0;
    49. vif.cb_mst.psel <= 1;
    50. vif.cb_mst.penable <= 0;
    51. @(vif.cb_mst);
    52. vif.cb_mst.penable <= 1;
    53. #100ps;//为了采样,人为添加一个延迟,避免detal cycle
    54. t.data = vif.prdata;
    55. repeat(t.idle_cycles) this.do_idle();
    56. endtask: do_read
    57. task apb_master_driver::do_idle();
    58. `uvm_info(get_type_name(), "do_idle ...", UVM_HIGH)
    59. @(vif.cb_mst);
    60. //vif.cb_mst.paddr <= 0;
    61. //vif.cb_mst.pwrite <= 0;
    62. vif.cb_mst.psel <= 0;
    63. vif.cb_mst.penable <= 0;
    64. vif.cb_mst.pwdata <= 0;
    65. endtask:do_idle
    66. task apb_master_driver::reset_listener();
    67. `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
    68. fork
    69. forever begin
    70. @(negedge vif.rstn); // ASYNC reset
    71. vif.paddr <= 0;
    72. vif.pwrite <= 0;
    73. vif.psel <= 0;
    74. vif.penable <= 0;
    75. vif.pwdata <= 0;
    76. end
    77. join_none
    78. endtask
    79. `endif // APB_MASTER_DRIVER_SV

    1.1实现apb_master_driver的drive_transfer()

    1. task apb_master_driver::drive_transfer (apb_transfer t);
    2. `uvm_info(get_type_name(), "drive_transfer", UVM_HIGH)
    3. case(t.trans_kind)
    4. IDLE : this.do_idle();
    5. WRITE : this.do_write(t);
    6. READ : this.do_read(t);
    7. default : `uvm_error("ERRTYPE", "unrecognized transaction type")
    8. endcase
    9. endtask : drive_transfer
    10. //其中apb_transfer如下,就是个item
    11. `ifndef APB_TRANSFER_SV
    12. `define APB_TRANSFER_SV
    13. typedef enum {IDLE, WRITE, READ } apb_trans_kind;
    14. class apb_transfer extends uvm_sequence_item;
    15. rand bit [31:0] addr;
    16. rand bit [31:0] data;
    17. rand apb_trans_kind trans_kind;
    18. rand int idle_cycles;
    19. constraint cstr{
    20. soft idle_cycles == 1; };
    21. `uvm_object_utils_begin(apb_transfer)
    22. `uvm_field_enum (apb_trans_kind, trans_kind, UVM_ALL_ON)
    23. `uvm_field_int (addr, UVM_ALL_ON)
    24. `uvm_field_int (data, UVM_ALL_ON)
    25. `uvm_field_int (idle_cycles, UVM_ALL_ON)
    26. `uvm_object_utils_end
    27. function new (string name = "apb_transfer_inst");
    28. super.new(name);
    29. endfunction : new
    30. endclass : apb_transfer
    31. `endif // APB_TRANSFER_SV

    再来看monitor

    apb_master_monitor

    TLM通信端口是uvm_analysis_port 

    1. `ifndef APB_MASTER_MONITOR_SVH
    2. `define APB_MASTER_MONITOR_SVH
    3. class apb_master_monitor extends uvm_monitor;
    4. apb_config cfg;
    5. bit checks_enable = 1;
    6. bit coverage_enable = 1;
    7. virtual apb_if vif;
    8. uvm_analysis_port #(apb_transfer) item_collected_port;
    9. `uvm_component_utils_begin(apb_master_monitor)
    10. `uvm_field_int(checks_enable, UVM_ALL_ON)
    11. `uvm_field_int(coverage_enable, UVM_ALL_ON)
    12. `uvm_component_utils_end
    13. extern function new(string name, uvm_component parent=null);
    14. extern function void build();
    15. extern virtual task run();
    16. event apb_master_cov_transaction;
    17. covergroup apb_master_cov_trans @apb_master_cov_transaction;
    18. endgroup : apb_master_cov_trans
    19. protected apb_transfer trans_collected;
    20. extern virtual protected task monitor_transactions();
    21. extern virtual protected task collect_transfer();
    22. extern protected function void perform_transfer_checks();
    23. extern protected function void perform_transfer_coverage();
    24. endclass : apb_master_monitor
    25. `endif // APB_MASTER_MONITOR_SVH
    1. `ifndef APB_MASTER_MONITOR_SV
    2. `define APB_MASTER_MONITOR_SV
    3. function apb_master_monitor::new(string name, uvm_component parent=null);
    4. super.new(name, parent);
    5. item_collected_port = new("item_collected_port",this);
    6. trans_collected = new();
    7. endfunction:new
    8. // build
    9. function void apb_master_monitor::build();
    10. super.build();
    11. endfunction : build
    12. task apb_master_monitor::monitor_transactions();
    13. forever begin
    14. // Extract data from interface into transaction
    15. collect_transfer();
    16. // Check transaction
    17. if (checks_enable)
    18. perform_transfer_checks();
    19. // Update coverage
    20. if (coverage_enable)
    21. perform_transfer_coverage();
    22. // Publish to subscribers
    23. item_collected_port.write(trans_collected);
    24. end
    25. endtask // monitor_transactions
    26. task apb_master_monitor::run();
    27. fork
    28. monitor_transactions();
    29. join_none
    30. endtask // run
    31. task apb_master_monitor::collect_transfer();
    32. apb_transfer t;
    33. // Advance clock
    34. @(vif.cb_mon);
    35. if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
    36. t = apb_transfer::type_id::create("t");
    37. case(vif.cb_slv.pwrite)
    38. 1'b1 : begin
    39. @(vif.cb_mon);//在第二个周期
    40. t.addr = vif.cb_mon.paddr;
    41. t.data = vif.cb_mon.pwdata;
    42. t.trans_kind = WRITE;
    43. end
    44. 1'b0 : begin
    45. @(vif.cb_mon);
    46. t.addr = vif.cb_mon.paddr;
    47. t.data = vif.cb_mon.prdata;
    48. t.trans_kind = READ;
    49. end
    50. default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
    51. endcase
    52. item_collected_port.write(t);
    53. end
    54. endtask: collect_transfer
    55. // perform_transfer_checks
    56. function void apb_master_monitor::perform_transfer_checks();
    57. // USER: do some checks on the transfer here
    58. endfunction : perform_transfer_checks
    59. // perform_transfer_coverage
    60. function void apb_master_monitor::perform_transfer_coverage();
    61. // USER: coverage implementation
    62. -> apb_master_cov_transaction;
    63. endfunction : perform_transfer_coverage
    64. `endif // APB_MASTER_MONITOR_SV

     1.3编写apb_master_monitor中的collect_transfer()

    用于从接口获取item过来。

    在setup阶段判断是读还是写,注意!要多用一次@(vif.cb_mon),APB在第二个周期完成读写操作

    然后把item通过analysis port的write函数写入item

    1. task apb_master_monitor::collect_transfer();
    2. apb_transfer t;
    3. // Advance clock
    4. @(vif.cb_mon);
    5. if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
    6. t = apb_transfer::type_id::create("t");
    7. case(vif.cb_slv.pwrite)
    8. 1'b1 : begin
    9. @(vif.cb_mon);//在第二个周期
    10. t.addr = vif.cb_mon.paddr;
    11. t.data = vif.cb_mon.pwdata;
    12. t.trans_kind = WRITE;
    13. end
    14. 1'b0 : begin
    15. @(vif.cb_mon);
    16. t.addr = vif.cb_mon.paddr;
    17. t.data = vif.cb_mon.prdata;
    18. t.trans_kind = READ;
    19. end
    20. default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
    21. endcase
    22. item_collected_port.write(t);
    23. end
    24. endtask: collect_transfer

    apb_master_sequence_Lib

    包含base seq和各个seq,包括写1个,读1个,写完读1个,连续写,连续读,每次读写都需要一次`uvm_do_with,读完要把总线设为idle

    1.2编写apb_master_sequence中的apb_master_write_seq::body()任务

    base sequence只注册和例化,等待具体的子类来扩展。

    一共有5个seq,如上所示。

    single_write的body,发送一个req过去后获取一个rsp

    1. virtual task body();
    2. `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    3. `uvm_do_with(req, {trans_kind == WRITE; addr == local::addr; data == local::data;})
    4. get_response(rsp);
    5. `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
    6. endtask: body

    single_read的body,发送req过去后获取rsp并将rsp的data给到data,完成读出

    1. virtual task body();
    2. `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    3. `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
    4. get_response(rsp);
    5. data = rsp.data;
    6. `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
    7. endtask: body

     write_read的body,写的时候要限定类型和idle_cycle,拿到rsp后读,读只限定类型和地址即可

    1. virtual task body();
    2. `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    3. `uvm_do_with(req, {trans_kind == WRITE;
    4. addr == local::addr;
    5. data == local::data;
    6. idle_cycles == local::idle_cycles;
    7. })
    8. get_response(rsp);
    9. `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
    10. get_response(rsp);
    11. data = rsp.data;
    12. `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
    13. endtask: body

     burst_write的body,写完拿到rsp后要将类型置为IDLE

    不明白为什么地址要addr == local::addr + (i<<2);

    1. virtual task body();
    2. `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    3. foreach(data[i]) begin
    4. `uvm_do_with(req, {trans_kind == WRITE;
    5. addr == local::addr + (i<<2);
    6. data == local::data[i];
    7. idle_cycles == 0;
    8. })
    9. get_response(rsp);
    10. end
    11. `uvm_do_with(req, {trans_kind == IDLE;})
    12. get_response(rsp);
    13. `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
    14. endtask: body

     burst_read的body同理。

    1. virtual task body();
    2. `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    3. foreach(data[i]) begin
    4. `uvm_do_with(req, {trans_kind == READ;
    5. addr == local::addr + (i<<2);
    6. idle_cycles == 0;
    7. })
    8. get_response(rsp);
    9. data[i] = rsp.data;
    10. end
    11. `uvm_do_with(req, {trans_kind == IDLE;})
    12. get_response(rsp);
    13. `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
    14. endtask: body

    apb_master_sequencer

    只有注册和new函数例化

    slave端

    agent是一样的

    apb_slave_driver

    由于要接收master端的item,这里有个关联数组mem。

    之前的mem出现在test的base_test_sequence中,用来存放读写数据。

    与master相对应的是drive req,这里是drive_response,这也作为一个并行线程(master是两个并行线程)

    复位时要清空mem的数据,因为这边是从端

    1. `ifndef APB_SLAVE_DRIVER_SV
    2. `define APB_SLAVE_DRIVER_SV
    3. function apb_slave_driver::new (string name, uvm_component parent);
    4. super.new(name, parent);
    5. endfunction : new
    6. task apb_slave_driver::run();
    7. fork
    8. get_and_drive();
    9. reset_listener();
    10. drive_response();
    11. join_none
    12. endtask : run
    13. task apb_slave_driver::get_and_drive();
    14. forever begin
    15. seq_item_port.get_next_item(req);
    16. `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
    17. void'($cast(rsp, req.clone()));
    18. rsp.set_sequence_id(req.get_sequence_id());
    19. seq_item_port.item_done(rsp);
    20. `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
    21. end
    22. endtask : get_and_drive
    23. task apb_slave_driver::drive_response();
    24. `uvm_info(get_type_name(), "drive_response", UVM_HIGH)
    25. forever begin
    26. @(vif.cb_slv);
    27. if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
    28. case(vif.cb_slv.pwrite)
    29. 1'b1 : this.do_write();
    30. 1'b0 : this.do_read();
    31. default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
    32. endcase
    33. end
    34. else begin
    35. this.do_idle();
    36. end
    37. end
    38. endtask : drive_response
    39. task apb_slave_driver::reset_listener();
    40. `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
    41. fork
    42. forever begin
    43. @(negedge vif.rstn); // ASYNC reset
    44. vif.prdata <= 0;
    45. this.mem.delete(); // reset internal memory
    46. end
    47. join_none
    48. endtask: reset_listener
    49. task apb_slave_driver::do_idle();
    50. `uvm_info(get_type_name(), "do_idle", UVM_HIGH)
    51. vif.cb_slv.prdata <= 0;
    52. endtask: do_idle
    53. task apb_slave_driver::do_write();
    54. bit[31:0] addr;
    55. bit[31:0] data;
    56. `uvm_info(get_type_name(), "do_write", UVM_HIGH)
    57. @(vif.cb_slv);
    58. addr = vif.cb_slv.paddr;
    59. data = vif.cb_slv.pwdata;
    60. mem[addr] = data;
    61. endtask: do_write
    62. task apb_slave_driver::do_read();
    63. bit[31:0] addr;
    64. bit[31:0] data;
    65. `uvm_info(get_type_name(), "do_read", UVM_HIGH)
    66. wait(vif.penable === 1'b1);
    67. addr = vif.cb_slv.paddr;
    68. if(mem.exists(addr))
    69. data = mem[addr];
    70. else
    71. data = 0;
    72. #1ps;
    73. vif.prdata <= data;
    74. @(vif.cb_slv);
    75. endtask: do_read
    76. `endif // APB_SLAVE_DRIVER_SV

     1.4编写apb_slave_driver的drive_response()

    还是APB的状态机在setup判断是读还是写,对应读写函数。

    1. task apb_slave_driver::drive_response();
    2. `uvm_info(get_type_name(), "drive_response", UVM_HIGH)
    3. forever begin
    4. @(vif.cb_slv);
    5. if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
    6. case(vif.cb_slv.pwrite)
    7. 1'b1 : this.do_write();
    8. 1'b0 : this.do_read();
    9. default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
    10. endcase
    11. end
    12. else begin
    13. this.do_idle();
    14. end
    15. end
    16. endtask : drive_response

     读写函数另外编写,按照时序图给出对应的信号。

    1. task apb_slave_driver::do_idle();
    2. `uvm_info(get_type_name(), "do_idle", UVM_HIGH)
    3. vif.cb_slv.prdata <= 0;
    4. endtask: do_idle
    5. task apb_slave_driver::do_write();
    6. bit[31:0] addr;
    7. bit[31:0] data;
    8. `uvm_info(get_type_name(), "do_write", UVM_HIGH)
    9. @(vif.cb_slv);
    10. addr = vif.cb_slv.paddr;
    11. data = vif.cb_slv.pwdata;
    12. mem[addr] = data;
    13. endtask: do_write
    14. task apb_slave_driver::do_read();
    15. bit[31:0] addr;
    16. bit[31:0] data;
    17. `uvm_info(get_type_name(), "do_read", UVM_HIGH)
    18. wait(vif.penable === 1'b1);
    19. addr = vif.cb_slv.paddr;
    20. if(mem.exists(addr))
    21. data = mem[addr];
    22. else
    23. data = 0;
    24. #1ps;
    25. vif.prdata <= data;
    26. @(vif.cb_slv);
    27. endtask: do_read

    apb_slave_seq_lib

    只是发送了一个item给driver,没法控制driver。

    为什么要让sequence控制driver?

    1. `ifndef APB_SLAVE_SEQ_LIB_SV
    2. `define APB_SLAVE_SEQ_LIB_SV
    3. class example_apb_slave_seq extends uvm_sequence #(apb_transfer);
    4. function new(string name="");
    5. super.new(name);
    6. endfunction : new
    7. `uvm_object_utils(example_apb_slave_seq)
    8. apb_transfer this_transfer;
    9. virtual task body();
    10. `uvm_info(get_type_name(),"Starting example sequence", UVM_HIGH)
    11. `uvm_do(this_transfer)
    12. `uvm_info(get_type_name(),$psprintf("Done example sequence: %s",this_transfer.convert2string()), UVM_HIGH)
    13. endtask
    14. endclass : example_apb_slave_seq
    15. `endif // apb_SLAVE_SEQ_LIB_SV

  • 相关阅读:
    react学习笔记
    YOLO系列算法精讲整理(持续更新)
    【Docker】Docker-compose及Consul多容器编排工具
    [JS入门到进阶] 7条关于 async await 的使用口诀,新学 async await?背10遍,以后要考!快收藏
    [创业之路-71] :创业思维与打工思维的区别
    使用java多线程模拟一个售票系统
    微服务整合公众号告警系统
    java基础、底层实现、面试
    [模型]多目标规划模型
    MySQL中的日志(redo log、undo log、binlog)
  • 原文地址:https://blog.csdn.net/weixin_39668316/article/details/126410182