日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁 > > ZYNQ


APB協(xié)議UVM驗(yàn)證環(huán)境的搭建

一、編譯文件

只需編譯這兩個(gè)文件即可

apb_pkg.sv

里面包含了"apb.svh",即編譯apb_pkg.sv這個(gè)文件的同時(shí),也會編譯所需要的所有的頭文件。

`ifndef APB_PKG_SV
`define APB_PKG_SV

package apb_pkg; import uvm_pkg::*;
`include "uvm_macros.svh" `include "apb.svh" endpackage : apb_pkg

   
`endif //  `ifndef APB_PKG_SV 

apb.svh

`ifndef APB_SVH
`define APB_SVH


`include "apb_transfer.sv" `include "apb_config.sv" //master所有的頭文件 `include "apb_master_driver.svh" `include "apb_master_monitor.svh" `include "apb_master_sequencer.svh" `include "apb_master_agent.svh" //slave所有的頭文件 `include "apb_slave_driver.svh" `include "apb_slave_monitor.svh" `include "apb_slave_sequencer.svh" `include "apb_slave_agent.svh" //master頭文件里面具體的實(shí)現(xiàn)方法 `include "apb_master_driver.sv" `include "apb_master_monitor.sv" `include "apb_master_sequencer.sv" `include "apb_master_agent.sv" `include "apb_master_seq_lib.sv" //slave頭文件里面具體的實(shí)現(xiàn)方法 `include "apb_slave_driver.sv" `include "apb_slave_monitor.sv" `include "apb_slave_sequencer.sv" `include "apb_slave_agent.sv" `include "apb_slave_seq_lib.sv" `endif //  `ifndef APB_SVH 

再來編譯apb_tb.sv文件

編譯的同時(shí),也會編譯"apb_tests.svh"、"apb_if.sv"這兩個(gè)文件。例化協(xié)議接口,配置頂層環(huán)境的master和slave,默認(rèn)執(zhí)行“apb_single_transaction_test”這個(gè)測試用例。

`timescale 1ps/1ps import uvm_pkg::*;
`include "uvm_macros.svh" `include "apb_tests.svh" `include "apb_if.sv" module apb_tb;
  bit clk, rstn;
  initial begin
    fork
      begin 
        forever #5ns clk = !clk;
      end
      begin
        #100ns;
        rstn <= 1'b1;
        #100ns;
        rstn <= 1'b0;
        #100ns;
        rstn <= 1'b1; end
    join_none
  end

  apb_if intf(clk, rstn);

  initial begin
    uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.mst", "vif", intf);
    uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.slv", "vif", intf);
    run_test("apb_single_transaction_test");
  end

endmodule

apb_tests.svh

`ifndef APB_TESTS_SV
`define APB_TESTS_SV import apb_pkg::*; class apb_env extends uvm_env; apb_master_agent mst;
  apb_slave_agent slv;
  `uvm_component_utils(apb_env) function new(string name, uvm_component parent);
    super.new(name, parent); endfunction
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    mst = apb_master_agent::type_id::create("mst", this);
    slv = apb_slave_agent::type_id::create("slv", this);
  endfunction
endclass class apb_base_test extends uvm_test; apb_env env;
  `uvm_component_utils(apb_base_test) function new(string name, uvm_component parent);
    super.new(name, parent); endfunction
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = apb_env::type_id::create("env", this);
  endfunction
endclass class apb_base_test_sequence extends uvm_sequence #(apb_transfer); bit[31:0] mem[bit[31:0]]; //關(guān)聯(lián)數(shù)組mem,用來master和slave之間的數(shù)據(jù)比對,test和slave中都有一個(gè)mem `uvm_object_utils(apb_base_test_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new function bit check_mem_data(bit[31:0] addr, bit[31:0] data); if(mem.exists(addr)) begin if(data != mem[addr]) begin
        `uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h%8x != actual 32'h%8x", addr, mem[addr], data)) return 0;
      end else begin
        `uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW) return 1; end
    end else begin if(data != 0) begin
        `uvm_error("CMPDATA", $sformatf("addr 32'h%8x, READ DATA expected 32'h00000000 != actual 32'h%8x", addr, data)) return 0;
      end else begin
        `uvm_info("CMPDATA", $sformatf("addr 32'h%8x, READ DATA 32'h%8x comparing success!", addr, data), UVM_LOW) return 1;
      end
    end
  endfunction: check_mem_data

  task wait_reset_release();
    @(negedge apb_tb.rstn);
    @(posedge apb_tb.rstn); endtask

  task wait_cycles(int n);
    repeat(n) @(posedge apb_tb.clk);
  endtask

  function bit[31:0] get_rand_addr();
    bit[31:0] addr; void'(std::randomize(addr) with {addr[31:12] == 0; addr[1:0] == 0;}); return addr;
  endfunction
endclass class apb_single_transaction_sequence extends apb_base_test_sequence; apb_master_single_write_sequence single_write_seq;
  apb_master_single_read_sequence single_read_seq;
  apb_master_write_read_sequence write_read_seq;
  rand int test_num = 100;
  constraint cstr{
    soft test_num == 100;
  }
  `uvm_object_utils(apb_single_transaction_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new task body();
    bit[31:0] addr; this.wait_reset_release(); this.wait_cycles(10); // TEST continous write transaction `uvm_info(get_type_name(), "TEST continous write transaction...", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
      mem[addr] = addr;
    end // TEST continous read transaction `uvm_info(get_type_name(), "TEST continous read transaction...", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data));
    end // TEST read transaction after write transaction `uvm_info(get_type_name(), "TEST read transaction after write transaction...", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
      mem[addr] = addr;
      `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data));
    end // TEST read transaction immediately after write transaction `uvm_info(get_type_name(), "TEST read transaction immediately after write transaction", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(write_read_seq, {addr == local::addr; data == local::addr;})
      mem[addr] = addr; void'(this.check_mem_data(addr, write_read_seq.data));
    end this.wait_cycles(10);
  endtask
endclass: apb_single_transaction_sequence class apb_single_transaction_test extends apb_base_test; `uvm_component_utils(apb_single_transaction_test) function new(string name, uvm_component parent);
    super.new(name, parent); endfunction
  task run_phase(uvm_phase phase);
    apb_single_transaction_sequence seq = new();
    phase.raise_objection(this);
    super.run_phase(phase);
    seq.start(env.mst.sequencer);
    phase.drop_objection(this);
  endtask
endclass: apb_single_transaction_test class apb_burst_transaction_sequence extends apb_base_test_sequence; apb_master_burst_write_sequence burst_write_seq;
  apb_master_burst_read_sequence burst_read_seq;
  rand int test_num = 100;
  constraint cstr{
    soft test_num == 100;
  }
  `uvm_object_utils(apb_burst_transaction_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new task body();
    bit[31:0] addr; this.wait_reset_release(); this.wait_cycles(10); // TEST continous write transaction repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(burst_write_seq, {addr == local::addr;})
      foreach(burst_write_seq.data[i]) begin
        mem[addr+(i<<2)] = burst_write_seq.data[i];
      end
      `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();})
      foreach(burst_read_seq.data[i]) begin void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));
      end
    end this.wait_cycles(10);
  endtask
endclass: apb_burst_transaction_sequence class apb_burst_transaction_test extends apb_base_test; `uvm_component_utils(apb_burst_transaction_test) function new(string name, uvm_component parent);
    super.new(name, parent); endfunction
  task run_phase(uvm_phase phase);
    apb_burst_transaction_sequence seq = new();
    phase.raise_objection(this);
    super.run_phase(phase);
    seq.start(env.mst.sequencer);
    phase.drop_objection(this);
  endtask
endclass: apb_burst_transaction_test



`endif // APB_TESTS_SV 

apb_if.sv

`ifndef APB_IF_SV
`define APB_IF_SV

interface apb_if (input clk, input rstn);

  logic [31:0] paddr;
  logic        pwrite;
  logic        psel;
  logic        penable;
  logic [31:0] pwdata;
  logic [31:0] prdata; // Control flags bit                has_checks = 1;
  bit                has_coverage = 1; // Actual Signals  // USER: Add interface signals clocking cb_mst @(posedge clk); // USER: Add clocking block detail default input #1ps output #1ps;
    output paddr, pwrite, psel, penable, pwdata;
    input prdata;
  endclocking : cb_mst

  clocking cb_slv @(posedge clk); // USER: Add clocking block detail default input #1ps output #1ps;
    input paddr, pwrite, psel, penable, pwdata;
    output prdata;
  endclocking : cb_slv

  clocking cb_mon @(posedge clk); // USER: Add clocking block detail default input #1ps output #1ps;
    input paddr, pwrite, psel, penable, pwdata, prdata;
  endclocking : cb_mon // Coverage and assertions to be implemented here. // USER: Add assertions/coverage here // APB command covergroup covergroup cg_apb_command @(posedge clk iff rstn);
    pwrite: coverpoint pwrite{
      type_option.weight = 0;
      bins write = {1};
      bins read  = {0};

    }
    psel : coverpoint psel{
      type_option.weight = 0;
      bins sel   = {1};
      bins unsel = {0};
    }
    cmd  : cross pwrite, psel{
      bins cmd_write = binsof(psel.sel) && binsof(pwrite.write);
      bins cmd_read  = binsof(psel.sel) && binsof(pwrite.read);
      bins cmd_idle  = binsof(psel.unsel);
    }
  endgroup // APB transaction timing group covergroup cg_apb_trans_timing_group @(posedge clk iff rstn);
    psel: coverpoint psel{
      bins single   = (0 => 1 => 1 => 0); 
      bins burst_2  = (0 => 1 [*4]  => 0); 
      bins burst_4  = (0 => 1 [*8]  => 0); 
      bins burst_8  = (0 => 1 [*16] => 0); 
      bins burst_16 = (0 => 1 [*32] => 0); 
      bins burst_32 = (0 => 1 [*64] => 0); 
    }
    penable: coverpoint penable {
      bins single = (0 => 1 => 0 [*2:10] => 1);
      bins burst  = (0 => 1 => 0 => 1);
    }
  endgroup // APB write & read order group covergroup cg_apb_write_read_order_group @(posedge clk iff (rstn && penable));
    write_read_order: coverpoint pwrite{
      bins write_write = (1 => 1);
      bins write_read  = (1 => 0);
      bins read_write  = (0 => 1);
      bins read_read   = (0 => 0);
    } 
  endgroup

  initial begin
    automatic cg_apb_command cg0 = new();
    automatic cg_apb_trans_timing_group cg1 = new();
    automatic cg_apb_write_read_order_group cg2 = new();
  end

endinterface : apb_if

`endif // APB_IF_SV 
二、apb_tests.sv代碼分析

apb_base_test_sequence類

check_mem_data()方法原理結(jié)構(gòu)框圖:

關(guān)聯(lián)數(shù)組mem,用來master和slave之間的數(shù)據(jù)比對,test和slave中都有一個(gè)mem,master通過接口發(fā)送數(shù)據(jù)給slave,slave中的mem和test中的mem都會存儲這個(gè)數(shù)據(jù),等從slave讀回?cái)?shù)據(jù)時(shí),就可以和test中mem里面的數(shù)據(jù)進(jìn)行比較。

apb_single_transaction_sequence類

隨機(jī)化addr,測試連續(xù)寫操作

// TEST continous write transaction `uvm_info(get_type_name(), "TEST continous write transaction...", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
      mem[addr] = addr;
    end

隨機(jī)化addr,測試連續(xù)讀操作,并比較數(shù)據(jù)是否一致

// TEST continous read transaction `uvm_info(get_type_name(), "TEST continous read transaction...", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data));
    end

隨機(jī)化addr,先進(jìn)行寫操作,再進(jìn)行讀操作,并比較讀取的數(shù)據(jù)是否一致

// TEST read transaction after write transaction `uvm_info(get_type_name(), "TEST read transaction after write transaction...", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(single_write_seq, {addr == local::addr; data == local::addr;})
      mem[addr] = addr;
      `uvm_do_with(single_read_seq, {addr == local::addr;}) void'(this.check_mem_data(addr, single_read_seq.data));
    end

隨機(jī)化addr,寫完立即讀,中間沒有idle空閑,并檢查讀取數(shù)據(jù)是否一致

// TEST read transaction immediately after write transaction `uvm_info(get_type_name(), "TEST read transaction immediately after write transaction", UVM_LOW)
    repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(write_read_seq, {addr == local::addr; data == local::addr;})
      mem[addr] = addr; void'(this.check_mem_data(addr, write_read_seq.data));
    end

例化并掛載

class apb_single_transaction_test extends apb_base_test; `uvm_component_utils(apb_single_transaction_test) function new(string name, uvm_component parent);
    super.new(name, parent); endfunction
  task run_phase(uvm_phase phase);
    apb_single_transaction_sequence seq = new();
    phase.raise_objection(this);
    super.run_phase(phase);
    seq.start(env.mst.sequencer);
    phase.drop_objection(this);
  endtask
endclass: apb_single_transaction_test

apb_burst_transaction_sequence類

先全部寫操作完畢,在完全讀出來,地址是連續(xù)增長的

// TEST continous write transaction repeat(test_num) begin
      addr = this.get_rand_addr();
      `uvm_do_with(burst_write_seq, {addr == local::addr;})
      foreach(burst_write_seq.data[i]) begin
        mem[addr+(i<<2)] = burst_write_seq.data[i];
      end
      `uvm_do_with(burst_read_seq, {addr == local::addr; data.size() == burst_write_seq.data.size();})
      foreach(burst_read_seq.data[i]) begin void'(this.check_mem_data(addr+(i<<2), burst_write_seq.data[i]));
      end
    end

例化并掛載

class apb_burst_transaction_test extends apb_base_test; `uvm_component_utils(apb_burst_transaction_test) function new(string name, uvm_component parent);
    super.new(name, parent); endfunction
  task run_phase(uvm_phase phase);
    apb_burst_transaction_sequence seq = new();
    phase.raise_objection(this);
    super.run_phase(phase);
    seq.start(env.mst.sequencer);
    phase.drop_objection(this);
  endtask
endclass: apb_burst_transaction_test

三、apb_master_agent.sv代碼分析

agent包括三個(gè)組件driver、sequencer、monitor,以及config和interface。

例化monitor,根據(jù)配置決定是否例化driver和sequencer

function void apb_master_agent::build();
  super.build(); // get config if( !uvm_config_db#(apb_config)::get(this,"","cfg", cfg)) begin
    `uvm_warning("GETCFG","cannot get config object from config DB")
     cfg = apb_config::type_id::create("cfg");
  end // get virtual interface if( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin
    `uvm_fatal("GETVIF","cannot get vif handle from config DB")
  end
  monitor = apb_master_monitor::type_id::create("monitor",this);
  monitor.cfg = cfg; if(cfg.is_active == UVM_ACTIVE) begin
    sequencer = apb_master_sequencer::type_id::create("sequencer",this);
    sequencer.cfg = cfg;
    driver = apb_master_driver::type_id::create("driver",this);
    driver.cfg = cfg;
  end
endfunction : build

根據(jù)配置決定是否連接driver和sequencer

function void apb_master_agent::connect();
  assign_vi(vif); if(is_active == UVM_ACTIVE) begin
    driver.seq_item_port.connect(sequencer.seq_item_export);       
  end

endfunction : connect

根據(jù)配置決定是否vif和driver、sequencer之間的連接

function void apb_master_agent::assign_vi(virtual apb_if vif);
   monitor.vif = vif; if (is_active == UVM_ACTIVE) begin
      sequencer.vif = vif; 
      driver.vif = vif; 
    end
endfunction : assign_vi

四、apb_master_driver.sv代碼分析

并行觸發(fā)get_and_drive()、reset_listener()

task apb_master_driver::run(); fork get_and_drive();
     reset_listener();
   join_none
endtask : run

捕捉到復(fù)位信號以后,所以信號清零

task apb_master_driver::reset_listener();
  `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
  fork
    forever begin
      @(negedge vif.rstn); // ASYNC reset vif.paddr <= 0;
      vif.pwrite <= 0;
      vif.psel <= 0;
      vif.penable <= 0;
      vif.pwdata <= 0;
    end
  join_none
endtask

sequence和sequencer需要握手,獲取transaction以后調(diào)用driver_transfer()發(fā)送。發(fā)送成功以后克隆request生成新的response,作為響應(yīng)發(fā)送回去。

task apb_master_driver::get_and_drive();
  forever begin
    seq_item_port.get_next_item(req);
    `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
    drive_transfer(req); void'($cast(rsp, req.clone()));
    rsp.set_sequence_id(req.get_sequence_id());
    seq_item_port.item_done(rsp);
    `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
  end
endtask : get_and_drive

task apb_master_driver::drive_transfer (apb_transfer t);
  `uvm_info(get_type_name(), "drive_transfer", UVM_HIGH) case(t.trans_kind)
    IDLE    : this.do_idle();
    WRITE   : this.do_write(t);
    READ    : this.do_read(t); default : `uvm_error("ERRTYPE", "unrecognized transaction type")
  endcase
endtask : drive_transfer

根據(jù)trans_kind判斷操作命令,分別調(diào)用相對應(yīng)的方法。

task apb_master_driver::do_write(apb_transfer t);
  `uvm_info(get_type_name(), "do_write ...", UVM_HIGH) //寫操作一共分為兩個(gè)周期,根據(jù)協(xié)議第一個(gè)周期setup準(zhǔn)備階段需要如下操作 @(vif.cb_mst);
  vif.cb_mst.paddr <= t.addr; vif.cb_mst.pwrite <= 1;
  vif.cb_mst.psel <= 1;
  vif.cb_mst.penable <= 0;
  vif.cb_mst.pwdata <= t.data; //第二個(gè)階段拉高penable信號,發(fā)送數(shù)據(jù) @(vif.cb_mst);
  vif.cb_mst.penable <= 1;
  repeat(t.idle_cycles) this.do_idle(); //取決于transaction里面的idle endtask: do_write

task apb_master_driver::do_read(apb_transfer t);
  `uvm_info(get_type_name(), "do_write ...", UVM_HIGH) //第一個(gè)階段 @(vif.cb_mst);
  vif.cb_mst.paddr <= t.addr; vif.cb_mst.pwrite <= 0;
  vif.cb_mst.psel <= 1;
  vif.cb_mst.penable <= 0; //第二個(gè)階段 @(vif.cb_mst);
  vif.cb_mst.penable <= 1;
  #100ps; //需要采樣數(shù)據(jù),人為添加100ps的delay,是為了避免delta-cycle t.data = vif.prdata; //采樣數(shù)據(jù) repeat(t.idle_cycles) this.do_idle();
endtask: do_read

task apb_master_driver::do_idle();
  `uvm_info(get_type_name(), "do_idle ...", UVM_HIGH)
  @(vif.cb_mst); //根據(jù)協(xié)議,paddr、pwrite可以保持不變,等待下一次的傳輸,這是為了省電 //vif.cb_mst.paddr <= 0; //vif.cb_mst.pwrite <= 0; vif.cb_mst.psel <= 0;
  vif.cb_mst.penable <= 0;
  vif.cb_mst.pwdata <= 0;
endtask:do_idle

五、apb_master_monitor.sv代碼分析

collect_transfer()方法

在時(shí)鐘上升沿,同時(shí)psel=1和penabl=0的時(shí)候,判斷當(dāng)前情況下pwrite信號,在第二個(gè)周期進(jìn)行讀或者寫操作。

task apb_master_monitor::collect_transfer();
  apb_transfer t; // Advance clock @(vif.cb_mon); if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin
    t = apb_transfer::type_id::create("t"); case(vif.cb_slv.pwrite) 1'b1    : begin
                  @(vif.cb_mon);
                  t.addr = vif.cb_mon.paddr;
                  t.data = vif.cb_mon.pwdata;
                  t.trans_kind = WRITE;
                end 1'b0    : begin
                  @(vif.cb_mon);
                  t.addr = vif.cb_mon.paddr;
                  t.data = vif.cb_mon.prdata;
                  t.trans_kind = READ;
                end default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
    endcase
    item_collected_port.write(t);
  end
endtask: collect_transfer

六、apb_master_seq_lib.sv代碼分析

apb_master_single_write_sequence類

使用宏'uvm_do_with發(fā)送數(shù)據(jù)。

class apb_master_single_write_sequence extends apb_master_base_sequence; rand bit [31:0]      addr;
  rand bit [31:0]      data;

  `uvm_object_utils(apb_master_single_write_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
   `uvm_do_with(req, {trans_kind == WRITE; addr == local::addr; data == local::data;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

endclass: apb_master_single_write_sequence

apb_master_single_read_sequence類

讀操作,拿到返回的rsp的數(shù)據(jù)后存儲在成員變量data里。

class apb_master_single_read_sequence extends apb_master_base_sequence; rand bit [31:0]      addr;
  rand bit [31:0]      data;

  `uvm_object_utils(apb_master_single_read_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
   `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
    get_response(rsp);
    data = rsp.data;
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

endclass: apb_master_single_read_sequence

apb_master_write_read_sequence類

寫操作后進(jìn)行讀操作,所以idle_cycles == 0

class apb_master_write_read_sequence extends apb_master_base_sequence; rand bit [31:0]    addr;
  rand bit [31:0]    data;
  rand int idle_cycles; 
  constraint cstr{
    idle_cycles == 0;
  }

  `uvm_object_utils(apb_master_write_read_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
   `uvm_do_with(req,  {trans_kind == WRITE; 
                        addr == local::addr; 
                        data == local::data;
                        idle_cycles == local::idle_cycles;
                       })
    get_response(rsp);
    `uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
    get_response(rsp);
    data = rsp.data;
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body

endclass: apb_master_write_read_sequence

apb_master_burst_write_sequence類

連續(xù)的寫操作,按照地址增長的順序,把所有的數(shù)據(jù)寫到data數(shù)組中。因?yàn)槭沁B續(xù)寫操作,所以idle_cycles == 0,即數(shù)據(jù)之間沒有空閑周期。

class apb_master_burst_write_sequence extends apb_master_base_sequence; rand bit [31:0]      addr;
  rand bit [31:0]      data[];
  constraint cstr{
    soft data.size() inside {4, 8, 16, 32};
    foreach(data[i]) soft data[i] == addr + (i << 2);
  }

  `uvm_object_utils(apb_master_burst_write_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    foreach(data[i]) begin
     `uvm_do_with(req, {trans_kind == WRITE; 
                         addr == local::addr + (i<<2); 
                         data == local::data[i];
                         idle_cycles == 0;
                        })
      get_response(rsp);
    end
    `uvm_do_with(req, {trans_kind == IDLE;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body
endclass: apb_master_burst_write_sequence

apb_master_burst_read_sequence類

連續(xù)的讀操作,每次讀取回來的數(shù)據(jù),從rsp中拿出來放到data數(shù)組中。全部讀取完成之后,將總線置為IDLE。

class apb_master_burst_read_sequence extends apb_master_base_sequence; rand bit [31:0]      addr;
  rand bit [31:0]      data[];
  constraint cstr{
    soft data.size() inside {4, 8, 16, 32};
  }
  `uvm_object_utils(apb_master_burst_read_sequence) function new(string name=""); 
    super.new(name);
  endfunction : new virtual task body();
    `uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
    foreach(data[i]) begin
     `uvm_do_with(req, {trans_kind == READ; 
                         addr == local::addr + (i<<2); 
                         idle_cycles == 0;
                        })
      get_response(rsp);
      data[i] = rsp.data;
    end
    `uvm_do_with(req, {trans_kind == IDLE;})
    get_response(rsp);
    `uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
  endtask: body
endclass: apb_master_burst_read_sequence

七、apb_slave_driver.sv代碼分析

slave要接收master發(fā)送過來的數(shù)據(jù),所以要模擬一個(gè)存儲功能,即關(guān)聯(lián)數(shù)組mem。

bit[31:0] mem [bit[31:0]];

run()方法

三個(gè)方法并行執(zhí)行

task apb_slave_driver::run(); fork get_and_drive();
     reset_listener();
     drive_response();
   join_none
endtask : run

get_and_drive()方法

task apb_slave_driver::get_and_drive();
  forever begin
    seq_item_port.get_next_item(req);
    `uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH) void'($cast(rsp, req.clone()));
    rsp.set_sequence_id(req.get_sequence_id());
    seq_item_port.item_done(rsp);
    `uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
  end
endtask : get_and_drive

reset_listener()方法

等待復(fù)位信號,將prdata <= 0,同時(shí)清空mem里面的數(shù)據(jù)。

task apb_slave_driver::reset_listener();
  `uvm_info(get_type_name(), "reset_listener ...", UVM_HIGH)
  fork
    forever begin
      @(negedge vif.rstn); // ASYNC reset vif.prdata <= 0; this.mem.delete(); // reset internal memory end
  join_none
endtask: reset_listener

drive_response()方法

如果當(dāng)前這一周期是SETUP階段,即psel = 1 && penable = 0,進(jìn)而判斷是寫操作還是讀操作,然后調(diào)用相對應(yīng)的方法。

task apb_slave_driver::drive_response();
  `uvm_info(get_type_name(), "drive_response", UVM_HIGH)
  forever begin
    @(vif.cb_slv); if(vif.cb_slv.psel === 1'b1 && vif.cb_slv.penable === 1'b0) begin case(vif.cb_slv.pwrite) 1'b1    : this.do_write(); 1'b0    : this.do_read(); default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
      endcase
    end else begin this.do_idle();
    end
  end
endtask : drive_response

do_write()方法

如果是寫操作,那么等待時(shí)鐘下一拍,拿到addr和data并放到mem中。

task apb_slave_driver::do_write();
  bit[31:0] addr;
  bit[31:0] data;
  `uvm_info(get_type_name(), "do_write", UVM_HIGH)
  @(vif.cb_slv);
  addr = vif.cb_slv.paddr;
  data = vif.cb_slv.pwdata;
  mem[addr] = data;
endtask: do_write

do_read()方法

如果是讀操作,等待penable=1,并且判斷mem中是否寫過該addr,如果有則寫入data,沒有則將data置為0,即還是初始化的數(shù)據(jù)。等待一個(gè)延遲后,將data驅(qū)動到總線上面。

task apb_slave_driver::do_read();
  bit[31:0] addr;
  bit[31:0] data;
  `uvm_info(get_type_name(), "do_read", UVM_HIGH)
  wait(vif.penable === 1'b1);
  addr = vif.cb_slv.paddr; if(mem.exists(addr))
    data = mem[addr]; else data = 0;
  #1ps;
  vif.prdata <= data; @(vif.cb_slv); endtask: do_read

八、運(yùn)行仿真

執(zhí)行命令

run -all

驗(yàn)證環(huán)境結(jié)構(gòu)

寫操作:寫入地址和寫入數(shù)據(jù)相同,只有penable拉高才會寫入,數(shù)據(jù)之間有一個(gè)空閑。

讀操作:只有penable拉高才會讀數(shù)據(jù),沒有寫入過數(shù)據(jù)的地址,讀出來的值為0。

先寫后讀:

寫完立即讀操作:

仿真結(jié)果:


覆蓋率:

// APB command covergroup covergroup cg_apb_command @(posedge clk iff rstn);
    pwrite: coverpoint pwrite{
      type_option.weight = 0;
      bins write = {1};
      bins read  = {0};

    }
    psel : coverpoint psel{
      type_option.weight = 0;
      bins sel   = {1};
      bins unsel = {0};
    }
    cmd  : cross pwrite, psel{
      bins cmd_write = binsof(psel.sel) && binsof(pwrite.write);
      bins cmd_read  = binsof(psel.sel) && binsof(pwrite.read);
      bins cmd_idle  = binsof(psel.unsel);
    }
  endgroup // APB transaction timing group covergroup cg_apb_trans_timing_group @(posedge clk iff rstn);
    psel: coverpoint psel{
      bins single   = (0 => 1 => 1 => 0); 
      bins burst_2  = (0 => 1 [*4]  => 0); 
      bins burst_4  = (0 => 1 [*8]  => 0); 
      bins burst_8  = (0 => 1 [*16] => 0); 
      bins burst_16 = (0 => 1 [*32] => 0); 
      bins burst_32 = (0 => 1 [*64] => 0); 
    }
    penable: coverpoint penable {
      bins single = (0 => 1 => 0 [*2:10] => 1);
      bins burst  = (0 => 1 => 0 => 1);
    }
  endgroup // APB write & read order group covergroup cg_apb_write_read_order_group @(posedge clk iff (rstn && penable));
    write_read_order: coverpoint pwrite{
      bins write_write = (1 => 1);
      bins write_read  = (1 => 0);
      bins read_write  = (0 => 1);
      bins read_read   = (0 => 0);
    } 
  endgroup


本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
關(guān)閉