Source browser

Reusable SVIP examples

Original SystemVerilog examples for AXI4, APB, JTAG, SPI, and Clock / Reset / Signal. They demonstrate the documented APIs. Running them requires the full SVIP runtime and VCS/UVM 1.2; the static snapshot contains their source, not simulation results.

axi/testsuite/examples/svip_axi_callbacks_example.svh

Download original source · 32 lines

// SPDX-License-Identifier: Apache-2.0
// Recipe: hooks are nonblocking functions; request fields change on the private snapshot.
class svip_axi_recipe_callbacks extends svip_axi_callbacks;
  function new(string name="svip_axi_recipe_callbacks"); super.new(name); endfunction
  virtual function void request(svip_axi_txn req); req.region=4'hc; endfunction
  virtual function void write_response(svip_axi_txn req); req.buser=svip_axi_user_t'('h1_01234567_89abcdef); endfunction
  virtual function void read_response(svip_axi_txn req,ref bit [1:0] rresp[],ref svip_axi_user_t ruser[]);
    foreach(ruser[i]) ruser[i]=svip_axi_user_t'('h2_fedcba98_76543210) ^ i;
  endfunction
endclass
class svip_axi_callbacks_recipe_seq extends svip_axi_contract_seq;
  `uvm_object_utils(svip_axi_callbacks_recipe_seq)
  function new(string name="svip_axi_callbacks_recipe_seq"); super.new(name); endfunction
  virtual task body();
    svip_axi_txn t,r;
    wait(ctrl.resetn===1); repeat(5) @(negedge ctrl.clk);
    t=request(0,'h1000,4); completed(t,r);
    if(r.buser!=svip_axi_user_t'('h1_01234567_89abcdef)) `uvm_error("RECIPE_CALLBACK","write response hook effect missing")
    t=request(1,'h1000,4); completed(t,r);
    foreach(r.ruser[i]) if(r.ruser[i]!=(svip_axi_user_t'('h2_fedcba98_76543210)^i)) `uvm_error("RECIPE_CALLBACK","read response hook effect missing")
  endtask
endclass
class svip_axi_callbacks_example extends svip_axi_test;
  `uvm_component_utils(svip_axi_callbacks_example)
  svip_axi_recipe_callbacks hooks;
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase); hooks=new("hooks");
    mgr.add_callback(hooks); sub.add_callback(hooks);
  endfunction
  virtual function svip_axi_contract_seq create_sequence(); return svip_axi_callbacks_recipe_seq::type_id::create("callbacks_recipe"); endfunction
endclass

axi/testsuite/examples/svip_axi_controls_example.svh

Download original source · 26 lines

// SPDX-License-Identifier: Apache-2.0
// Recipe: force only a READY channel owned by that agent; always release explicit controls.
class svip_axi_controls_recipe_seq extends svip_axi_contract_seq;
  `uvm_object_utils(svip_axi_controls_recipe_seq)
  function new(string name="svip_axi_controls_recipe_seq"); super.new(name); endfunction
  virtual task body();
    svip_axi_txn t;
    wait(ctrl.resetn===1); repeat(5) @(negedge ctrl.clk);
    write_check('h1000,4);
    sub.force_ready(SVIP_AXI_AR,1,0);
    t=request(1,'h1000,4); send(t); repeat(8) @(negedge ctrl.clk);
    if(mgr.cfg.vif.arvalid!==1 || mgr.cfg.vif.arready!==0) `uvm_error("CONTROL_AR","AR READY override did not stall")
    sub.force_ready(SVIP_AXI_AR,0,0); wait_all();
    sub.hold_responses(1,1); // Pauses new responses, not an already presented VALID.
    t=request(0,'h1100,4); send(t); t=request(1,'h1000,4); send(t);
    repeat(20) @(negedge ctrl.clk);
    if(mgr.cfg.vif.bvalid!==0 || mgr.cfg.vif.rvalid!==0) `uvm_error("CONTROL_HOLD","hold started a response")
    sub.hold_responses(0,0); wait_all();
    read_check('h1100,4);
  endtask
endclass
class svip_axi_controls_example extends svip_axi_test;
  `uvm_component_utils(svip_axi_controls_example)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  virtual function svip_axi_contract_seq create_sequence(); return svip_axi_controls_recipe_seq::type_id::create("controls_recipe"); endfunction
endclass

axi/testsuite/examples/svip_axi_observation_example.svh

Download original source · 36 lines

// SPDX-License-Identifier: Apache-2.0
// Recipe: analysis subscribers observe; they never participate in bus scheduling.
class svip_axi_recipe_listener extends uvm_subscriber #(svip_axi_txn);
  `uvm_component_utils(svip_axi_recipe_listener)
  int seen;
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  virtual function void write(svip_axi_txn item);
    seen++; `uvm_info("OBSERVED_TRANSACTION",item.convert2string(),UVM_LOW)
  endfunction
endclass
class svip_axi_observation_recipe_seq extends svip_axi_contract_seq;
  `uvm_object_utils(svip_axi_observation_recipe_seq)
  function new(string name="svip_axi_observation_recipe_seq"); super.new(name); endfunction
  virtual task body();
    wait(ctrl.resetn===1); repeat(5) @(negedge ctrl.clk);
    write_check('h1000,4); read_check('h1000,4); repeat(5) @(negedge ctrl.clk);
  endtask
endclass
class svip_axi_observation_example extends svip_axi_test;
  `uvm_component_utils(svip_axi_observation_example)
  svip_axi_recipe_listener listener;
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  virtual function void configure_test();
    listener=svip_axi_recipe_listener::type_id::create("listener",this);
    mc.tracker_mode=SVIP_AXI_TRACKER_CHANNEL; mc.tracker_filename="example_mgr.tracker.log";
    sc.tracker_mode=SVIP_AXI_TRACKER_TXN; sc.tracker_filename="example_sub.tracker.log";
  endfunction
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase); mgr.ap.connect(listener.analysis_export);
  endfunction
  function void check_phase(uvm_phase phase);
    super.check_phase(phase);
    if(listener.seen!=2) `uvm_error("OBSERVATION_RECIPE","expected one complete write and one complete read")
  endfunction
  virtual function svip_axi_contract_seq create_sequence(); return svip_axi_observation_recipe_seq::type_id::create("observation_recipe"); endfunction
endclass

axi/testsuite/examples/svip_axi_patterns_example.svh

Download original source · 38 lines

// SPDX-License-Identifier: Apache-2.0
// Recipe: write AA/55 data with alternating byte enables; untouched bytes stay initialized.
class svip_axi_patterns_recipe_seq extends svip_axi_generation_seq;
  `uvm_object_utils(svip_axi_patterns_recipe_seq)
  function new(string name="svip_axi_patterns_recipe_seq"); super.new(name); endfunction
  virtual task body();
    svip_axi_addr_t address=64'h1234567800001000;
    byte unsigned old_bytes[svip_axi_addr_t];
    svip_axi_txn t,r;
    svip_axi_generation_goal_t goal;
    svip_axi_strb_e modes[3]='{SVIP_AXI_STRB_FULL,SVIP_AXI_STRB_RANDOM,SVIP_AXI_STRB_MIXED};
    int bytes=p_sequencer.cfg.data_bytes;
    wait(ctrl.resetn===1); repeat(5) @(negedge ctrl.clk);
    write_check(address,1,$clog2(bytes));
    old_bytes=oracle; t=request(0,address,1,$clog2(bytes)); oracle=old_bytes;
    for(int lane=0;lane<bytes;lane++) begin
      t.data[0][lane*8+:8]=lane%2 ? 8'h55 : 8'haa;
      t.strb[0][lane]=(lane%2)==0;
      if((lane%2)==0) oracle[address+lane]=8'haa;
    end
    completed(t,r); read_check(address,1,$clog2(bytes));
    // Public helper modes: initialize untouched bytes, send the filled object, check readback.
    foreach(modes[m]) begin
      goal=make_goal(SVIP_AXI_WR,address+'h1000,2,$clog2(bytes),SVIP_AXI_INCR,'h710+m);
      write_check(goal.addr,goal.beats,goal.size,goal.burst,91);
      t=randomized_request(goal);
      t.fill_wdata(modes[m]);
      send_generated(t,goal,modes[m]);
    end
  endtask
endclass
class svip_axi_patterns_example extends svip_axi_test;
  `uvm_component_utils(svip_axi_patterns_example)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  virtual function svip_axi_contract_seq create_sequence();
    return svip_axi_patterns_recipe_seq::type_id::create("patterns_recipe");
  endfunction
endclass

axi/testsuite/examples/svip_axi_reset_example.svh

Download original source · 26 lines

// SPDX-License-Identifier: Apache-2.0
// Recipe: reset outstanding traffic without calling the driver's software flush.
class svip_axi_reset_recipe_seq extends svip_axi_contract_seq;
  `uvm_object_utils(svip_axi_reset_recipe_seq)
  function new(string name="svip_axi_reset_recipe_seq"); super.new(name); endfunction
  virtual task body();
    svip_axi_txn t;
    wait(ctrl.resetn===1); repeat(5) @(negedge ctrl.clk);
    write_check('h1000,4);
    mgr.force_ready(SVIP_AXI_B,1,0); mgr.force_ready(SVIP_AXI_R,1,0);
    repeat(3) @(negedge ctrl.clk);
    t=request(0,'h1000,4); expect_abort[t.get_transaction_id()]=1; send(t);
    t=request(1,'h1000,4); expect_abort[t.get_transaction_id()]=1; send(t);
    wait(mgr.cfg.vif.bvalid===1 && mgr.cfg.vif.rvalid===1);
    ctrl.pulse_reset(); wait_all();
    mgr.force_ready(SVIP_AXI_B,0,0); mgr.force_ready(SVIP_AXI_R,0,0);
    read_check('h1000,4); write_check('h1200,4); read_check('h1200,4);
  endtask
endclass
class svip_axi_reset_example extends svip_axi_test;
  `uvm_component_utils(svip_axi_reset_example)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  virtual function svip_axi_contract_seq create_sequence();
    return svip_axi_reset_recipe_seq::type_id::create("reset_recipe");
  endfunction
endclass

axi/testsuite/examples/svip_axi_slave_model_example.svh

Download original source · 50 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Recipe: a sequence chooses when the subordinate processes and replies to requests.
class svip_axi_slave_model_recipe_seq extends svip_axi_contract_seq;
  `uvm_object_utils(svip_axi_slave_model_recipe_seq)
  function new(string name="svip_axi_slave_model_recipe_seq"); super.new(name); endfunction
  virtual task body();
    int size,reply_count;
    wait(ctrl.resetn===1); repeat(5) @(negedge ctrl.clk);
    size=p_sequencer.cfg.max_size()<3 ? p_sequencer.cfg.max_size() : 3;
    if(sub.cfg.memory_mode!=SVIP_AXI_MEMORY || sub.cfg.mem==null)
      `uvm_fatal("SLAVE_RECIPE_MEMORY","default internal memory was not installed")
    fork
      begin
        svip_axi_txn t,r;
        t=request(0,'h1000,2,size,SVIP_AXI_INCR,1,37); completed(t,r);
        if(r.buser!=svip_axi_user_t'('h31)) `uvm_error("SLAVE_RECIPE_BUSER","sequence-selected BUSER was lost")
        t=request(1,'h1000,2,size,SVIP_AXI_INCR,1); completed(t,r);
        if(r.rresp.size()!=2 || r.ruser.size()!=2) `uvm_fatal("SLAVE_RECIPE_READ","read response omitted beats")
        foreach(r.rresp[i])
          if(r.rresp[i]!=SVIP_AXI_OKAY || r.ruser[i]!=svip_axi_user_t'('h51+i))
            `uvm_error("SLAVE_RECIPE_RUSER","sequence-selected read status/USER was lost")
      end
      begin
        svip_axi_txn req,rsp;
        repeat(2) begin
          sub.get_request(req);
          repeat(3) @(negedge ctrl.clk);
          // This explicit call commits a write or snapshots a read exactly once.
          rsp=sub.default_response(req);
          if(rsp==null) `uvm_fatal("SLAVE_RECIPE_DEFAULT","default processing rejected a live request")
          if(req.dir==SVIP_AXI_WR) rsp.buser=svip_axi_user_t'('h31);
          else foreach(rsp.ruser[i]) rsp.ruser[i]=svip_axi_user_t'('h51+i);
          if(!sub.send_response(req,rsp)) `uvm_fatal("SLAVE_RECIPE_SEND","live response was rejected")
          reply_count++;
        end
      end
    join
    wait_all(); repeat(8) @(negedge ctrl.clk);
    if(n_sent!=2 || n_done!=2 || reply_count!=2)
      `uvm_error("SLAVE_RECIPE_COMPLETIONS","manager and responder did not complete exactly two requests")
  endtask
endclass

class svip_axi_slave_model_example extends svip_axi_test;
  `uvm_component_utils(svip_axi_slave_model_example)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  virtual function void configure_test(); sc.sub_response_mode=SVIP_AXI_SUB_USER; endfunction
  virtual function svip_axi_contract_seq create_sequence(); return svip_axi_slave_model_recipe_seq::type_id::create("slave_model_recipe"); endfunction
endclass

axi/testsuite/examples/svip_axi_timing_example.svh

Download original source · 30 lines

// SPDX-License-Identifier: Apache-2.0
// Recipe: timing policies are runtime objects; no driver subclass or pin force is needed.
class svip_axi_timing_recipe_seq extends svip_axi_contract_seq;
  `uvm_object_utils(svip_axi_timing_recipe_seq)
  function new(string name="svip_axi_timing_recipe_seq"); super.new(name); endfunction
  virtual task body();
    longint unsigned predecessor;
    wait(ctrl.resetn===1); repeat(5) @(negedge ctrl.clk);
    mgr.cfg.timing.apply(SVIP_AXI_TIMING_ZERO);
    mgr.cfg.timing.aw_valid.set_fixed(1);
    mgr.cfg.timing.w_valid.set_range(0,2);
    sub.cfg.timing.aw_ready.set_bursty(0,1,100,3,3);
    sub.cfg.timing.b_lat.set_fixed(3);
    sub.cfg.timing.r_lat.set_fixed(1);
    sub.cfg.timing.r_beat.set_fixed(2);
    write_check('h1000,4);
    predecessor=ctrl.last_aw_cycle>ctrl.last_wlast_cycle ? ctrl.last_aw_cycle : ctrl.last_wlast_cycle;
    if(ctrl.last_b_cycle-predecessor!=4) `uvm_error("TIMING_RECIPE_B","fixed B latency3 must handshake4 physical cycles later")
    read_check('h1000);
    if(ctrl.last_r_cycle-ctrl.last_ar_cycle!=2) `uvm_error("TIMING_RECIPE_R","fixed R latency1 must handshake2 physical cycles later")
    read_check('h1000,4);
    // Restore a simple profile before this sequence yields the shared timing objects.
    mgr.cfg.timing.apply(SVIP_AXI_TIMING_ZERO); sub.cfg.timing.apply(SVIP_AXI_TIMING_ZERO);
  endtask
endclass
class svip_axi_timing_example extends svip_axi_test;
  `uvm_component_utils(svip_axi_timing_example)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  virtual function svip_axi_contract_seq create_sequence(); return svip_axi_timing_recipe_seq::type_id::create("timing_recipe"); endfunction
endclass

apb/testsuite/examples/svip_apb_control_examples.svh

Download original source · 137 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Independent response fixture owns PREADY; requester controls remain unchanged.
class svip_apb_backpressure_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_backpressure_test)
  function new(string name,uvm_component parent);super.new(name,parent);endfunction
  task stalled(svip_apb_data_t expected,int cycles);
    svip_apb_strb_t expected_strb=0;
    if(cfg.apb4)for(int lane=0;lane<cfg.data_width/8;lane++)expected_strb[lane]=1;
    do @(vif.mon_cb);while(vif.mon_cb.psel!==1 || vif.mon_cb.penable!==1);
    for(int i=0;i<cycles;i++)begin
      if(vif.mon_cb.pready!==0 || vif.mon_cb.psel!==1 || vif.mon_cb.penable!==1 ||
         vif.mon_cb.paddr!=='h80 || vif.mon_cb.pwrite!==1 || vif.mon_cb.pwdata!==expected ||
         vif.mon_cb.pprot!==0 || vif.mon_cb.pstrb!==expected_strb)
        `uvm_fatal("TEST_BACKPRESSURE","selected write changed while response was withheld")
      #1ps;
      if(ctrl.bus_completions!=1 || terminal.items.size()!=1)
        `uvm_fatal("TEST_BACKPRESSURE","request completed before PREADY was released")
      if(i+1<cycles)@(vif.mon_cb);
    end
  endtask
  task run_case();
    svip_apb_txn t=cfg.create_txn("blocked_write");
    svip_apb_data_t next_data=data_pattern('h2468ace0);
    bit abort_write=(case_name=="backpressure_reset_write");
    checked_write('h80,'h13579bdf);
    t.write=1;t.addr='h80;t.data=next_data;t.strb='1;
    ctrl.manual_response=1;ctrl.manual_ready=0;ctrl.manual_error=0;
    fork
      submit(t);
      begin
        stalled(next_data,16);
        if(abort_write)begin
          @(negedge ctrl.clk);ctrl.resetn=0;
          #1ps;
          if(vif.psel!==0 || vif.penable!==0) `uvm_fatal("TEST_BACKPRESSURE","reset did not idle pins")
          @(negedge ctrl.clk);ctrl.resetn=1;
        end else ctrl.manual_ready=1;
      end
    join
    if(t.status!==(abort_write?SVIP_APB_RESET_ABORTED:SVIP_APB_OK) || t.wait_cycles!=16)
      `uvm_fatal("TEST_BACKPRESSURE","held request lost its result or exact wait count")
    ctrl.manual_response=0;ctrl.manual_ready=1;
    if(!abort_write)for(int lane=0;lane<cfg.data_width/8;lane++)gold['h80+lane]=next_data[lane*8+:8];
    // This fixture commits memory on the actual bus handshake, never on a stall.
    checked_read('h80);
    if(ctrl.bus_completions!=(abort_write?2:3) || observed.items.size()!=(abort_write?2:3) || terminal.items.size()!=3)
      `uvm_fatal("TEST_BACKPRESSURE","false completion or lost recovery")
  endtask
endclass

// The convenience sequence expresses an exact, reproducible address/data list.
class svip_apb_directed_pattern_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_directed_pattern_test)
  function new(string name,uvm_component parent);super.new(name,parent);endfunction
  task run_case();
    svip_apb_rw_seq seq=svip_apb_rw_seq::type_id::create("directed");
    svip_apb_data_t words[6]='{'0,'1,32'haa55aa55,32'h55aa55aa,32'h01234567,32'h89abcdef};
    svip_apb_addr_t addresses[6]='{'h40,'h44,'h48,'h4c,'h50,'h54};
    seq.cfg=cfg;
    foreach(words[i])begin
      svip_apb_data_t expected=data_pattern(words[i]);
      logic [2:0] protection=cfg.apb4?3'(i):3'b000;
      seq.addr=addresses[i];seq.write=1;seq.data=expected;seq.strb='1;seq.prot=protection;
      sent++;seq.start(agent.sqr);#1ps;
      if(seq.result.status!==SVIP_APB_OK || observed.items[$].addr!==addresses[i] ||
         observed.items[$].data!==expected || observed.items[$].prot!==protection)
        `uvm_fatal("TEST_DIRECTED_PATTERN","write pins did not match the requested literal/protection")
      seq.write=0;sent++;seq.start(agent.sqr);#1ps;
      if(seq.result.status!==SVIP_APB_OK || seq.data!==expected ||
         observed.items[$].data!==expected || observed.items[$].prot!==protection)
        `uvm_fatal("TEST_DIRECTED_PATTERN","readback or read protection differed from the literal")
    end
    if(ctrl.bus_completions!=12 || observed.items.size()!=12)
      `uvm_fatal("TEST_DIRECTED_PATTERN","a directed transfer was lost or duplicated")
  endtask
endclass

// Fixed bits and random bits coexist: low-byte high nibble A, low nibble random.
class svip_apb_constrained_pattern_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_constrained_pattern_test)
  function new(string name,uvm_component parent);super.new(name,parent);endfunction
  task run_case();
    bit [15:0] low_values=0;
    svip_apb_addr_t first_address;
    bit varied_address=0;
    svip_apb_strb_t full_strb=0;
    logic [2:0] protection=cfg.apb4?3'b101:3'b000;
    for(int lane=0;lane<cfg.data_width/8;lane++)full_strb[lane]=1;
    for(int i=0;i<40;i++)begin
      svip_apb_txn t=cfg.create_txn("constrained_pattern");
      svip_apb_addr_t address;
      svip_apb_data_t expected;
      if(!t.randomize() with {write==1;addr inside {['h40:'h7f]};data[7:4]==4'ha;
                             strb==local::full_strb;prot==local::protection;})
        `uvm_fatal("TEST_CONSTRAINED_PATTERN","randomize failed")
      address=t.addr;expected=t.data;
      if(i==0)first_address=address;else if(address!=first_address)varied_address=1;
      submit(t);
      if(t.status!==SVIP_APB_OK || observed.items[$].addr!==address ||
         observed.items[$].data!==expected || observed.items[$].data[7:4]!==4'ha ||
         observed.items[$].prot!==protection || observed.items[$].strb!==full_strb)
        `uvm_fatal("TEST_CONSTRAINED_PATTERN","actual pattern did not satisfy the requested constraints")
      low_values[observed.items[$].data[3:0]]=1;
      for(int lane=0;lane<cfg.data_width/8;lane++)gold[longint'(address)+lane]=expected[lane*8+:8];
      checked_read(address,protection);
    end
    if($countones(low_values)<2 || !varied_address || ctrl.bus_completions!=80)
      `uvm_fatal("TEST_CONSTRAINED_PATTERN","sent traffic did not vary or lost a transfer")
  endtask
endclass

class svip_apb_byte_lane_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_byte_lane_test)
  function new(string name,uvm_component parent);super.new(name,parent);endfunction
  task run_case();
    svip_apb_rw_seq seq=svip_apb_rw_seq::type_id::create("byte_lane");
    checked_write('h80,0);
    seq.cfg=cfg;seq.addr='h80;seq.write=1;seq.prot=3'b101;
    for(int lane=0;lane<cfg.data_width/8;lane++)begin
      bit [7:0] literal=8'('h5a+lane*'h11);
      svip_apb_data_t expected=0;
      svip_apb_strb_t strobe=0;
      expected[lane*8+:8]=literal;strobe[lane]=1;
      seq.data=expected;seq.strb=strobe;sent++;seq.start(agent.sqr);#1ps;
      if(seq.result.status!==SVIP_APB_OK || observed.items[$].data!==expected ||
         observed.items[$].strb!==strobe || observed.items[$].prot!==3'b101)
        `uvm_fatal("TEST_BYTE_LANE","byte data was not placed in the selected lane")
      gold['h80+lane]=literal;checked_read('h80);
    end
    // Zero strobe is a legal completed transfer with no byte update in this fixture.
    seq.data=data_pattern('1);seq.strb=0;sent++;seq.start(agent.sqr);#1ps;
    if(seq.result.status!==SVIP_APB_OK || observed.items[$].strb!==0)
      `uvm_fatal("TEST_BYTE_LANE","zero-strobe transfer did not complete")
    checked_read('h80);
  endtask
endclass

apb/testsuite/examples/svip_apb_examples.svh

Download original source · 241 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
class svip_apb_smoke_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_smoke_test)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  task run_case();
    checked_write('h80,'h12345678);
    checked_read('h80);
    if (ctrl.bus_completions!=2 || observed.items.size()!=2)
      `uvm_fatal("TEST_COUNT","smoke requires exactly two pin/monitor completions")
  endtask
endclass

class svip_apb_sequence_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_sequence_test)
  function new(string name,uvm_component parent);super.new(name,parent);endfunction
  task run_case();
    svip_apb_rw_seq seq=svip_apb_rw_seq::type_id::create("user_sequence");
    seq.cfg=cfg;seq.addr='h80;seq.write=1;seq.data=data_pattern('h59a63cc3);
    sent=2;
    seq.start(agent.sqr);
    if(seq.result.status!==SVIP_APB_OK) `uvm_fatal("TEST_SEQUENCE","write sequence failed")
    seq.write=0;seq.start(agent.sqr);#1ps;
    if(seq.result.status!==SVIP_APB_OK || seq.data!==data_pattern('h59a63cc3))
      `uvm_fatal("TEST_SEQUENCE","read sequence lost response")
  endtask
endclass

class svip_apb_random_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_random_test)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  task run_case();
    bit saw_read=0,saw_write=0;
    bit [7:0] prot_seen=0;
    svip_apb_data_t data_or=0,data_and='1;
    repeat(120) begin
      svip_apb_txn t=cfg.create_txn("random_pattern");
      if (!t.randomize() with {addr inside {['h40:'h7f]};}) `uvm_fatal("TEST_RAND","randomize failed")
      ctrl.wait_cycles=$urandom_range(0,5);
      if(t.write) begin
        checked_write(t.addr,t.data,t.strb,t.prot); saw_write=1;
        data_or |= t.data; data_and &= t.data;
      end else begin checked_read(t.addr,t.prot); saw_read=1; end
      prot_seen[observed.items[$].prot]=1;
    end
    if (!saw_read || !saw_write || data_pattern(data_or)!==data_pattern('1) || data_pattern(data_and)!==0)
      `uvm_fatal("TEST_PATTERN","actual random traffic missed direction/data variation")
    if(cfg.apb4 && $countones(prot_seen)<2) `uvm_fatal("TEST_PATTERN","actual PROT never varied")
  endtask
endclass

class svip_apb_stream_seq extends uvm_sequence #(svip_apb_txn);
  `uvm_object_utils(svip_apb_stream_seq)
  svip_apb_cfg cfg;
  virtual svip_apb_test_ctrl_if ctrl;
  function new(string name="stream");super.new(name);endfunction
  task body();
    realtime previous_end=0,period_ns=2.0*ctrl.clock_half_ps*0.001;
    realtime first_gap=-1;
    bit varied_gap=0;
    bit next_write=1;
    svip_apb_data_t literal='h3c965aa5;
    repeat(16)begin
      svip_apb_txn t=cfg.create_txn("stream_request");
      svip_apb_data_t expected=0;
      realtime gap;
      for(int i=0;i<cfg.data_width;i++)expected[i]=literal[i];
      start_item(t);
      t.write=next_write;next_write=!next_write;
      t.addr='h80;t.data=expected;t.strb=t.write?'1:'0;
      finish_item(t);
      if(t.status!==SVIP_APB_OK || t.data!==expected) `uvm_fatal("TEST_STREAM","stream payload/status failed")
      if(previous_end!=0)begin
        gap=t.t_start-previous_end;
        if(cfg.back_to_back && (gap< -0.0001 || gap>0.0001))
          `uvm_fatal("TEST_B2B","back-to-back inserted a bubble")
        if(!cfg.back_to_back && (gap<cfg.min_idle_cycles*period_ns-0.0001 || gap>cfg.max_idle_cycles*period_ns+0.0001))
          `uvm_fatal("TEST_IDLE_GAP","actual idle latency outside configured bounds")
        if(first_gap<0)first_gap=gap;
        else if(gap<first_gap-0.0001 || gap>first_gap+0.0001)varied_gap=1;
      end
      previous_end=t.t_end;
    end
    if(!cfg.back_to_back && cfg.max_idle_cycles>cfg.min_idle_cycles && !varied_gap)
      `uvm_fatal("TEST_IDLE_GAP","random idle latency never varied on pins")
  endtask
endclass

class svip_apb_timing_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_timing_test)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  task run_case();
    svip_apb_stream_seq stream=svip_apb_stream_seq::type_id::create("stream");
    if(case_name=="example_timing")begin
      cfg.back_to_back=0;cfg.min_idle_cycles=2;cfg.max_idle_cycles=2;ctrl.wait_cycles=3;
    end
    stream.cfg=cfg;stream.ctrl=ctrl;sent=16;stream.start(agent.sqr);#1ps;
    if(case_name=="example_timing")begin
      cfg.max_idle_cycles=5;sent+=16;stream.start(agent.sqr);#1ps;
    end
    if(ctrl.bus_completions!=sent || observed.items.size()!=sent) `uvm_fatal("TEST_STREAM","stream lost completions")
  endtask
endclass

class svip_apb_hold_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_hold_test)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  task run_case();
    svip_apb_txn t=cfg.create_txn("held");
    t.write=0; t.addr='h80; t.strb=0; cfg.hold=1;
    fork
      submit(t);
      begin
        repeat(4) @(vif.mon_cb);
        if(vif.psel!==0 || terminal.items.size()!=0 || ctrl.bus_completions!=0)
          `uvm_fatal("TEST_HOLD","held request reached pins or completed")
        cfg.hold=0;
      end
    join
    if(t.status!==SVIP_APB_OK) `uvm_fatal("TEST_HOLD","request did not resume")
    ctrl.wait_cycles=4;t=cfg.create_txn("already_presented");t.addr='h80;t.strb=0;
    fork
      submit(t);
      begin wait(vif.psel===1 && vif.penable===1);cfg.hold=1;end
    join
    if(t.status!==SVIP_APB_OK || t.wait_cycles!=4)
      `uvm_fatal("TEST_HOLD","hold cancelled an already presented transfer")
    repeat(2)@(vif.mon_cb);#1ps;
    if(vif.psel!==0 || vif.penable!==0 || ctrl.bus_completions!=2)
      `uvm_fatal("TEST_HOLD","requester did not idle after draining the transfer")
    cfg.hold=0;ctrl.wait_cycles=0;
    checked_write('h80,'ha55aa55a); checked_read('h80);
  endtask
endclass

class svip_apb_error_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_error_test)
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  task run_case();
    svip_apb_txn t=cfg.create_txn("error_read");
    ctrl.response_error=1; ctrl.error_data_x=1; ctrl.wait_cycles=2;
    t.write=0;t.addr='h80;t.strb=0;submit(t);
    if(t.status!==SVIP_APB_SLVERR || t.slverr!==1 || t.wait_cycles!=2)
      `uvm_fatal("TEST_ERROR","PSLVERR result lost")
    t=cfg.create_txn("error_write");t.write=1;t.addr='h80;
    t.data=data_pattern('hbadc0ffe);t.strb='1;submit(t);
    if(t.status!==SVIP_APB_SLVERR || observed.items[$].status!==SVIP_APB_SLVERR || t.wait_cycles!=2)
      `uvm_fatal("TEST_ERROR","write PSLVERR result / monitor lost")
    ctrl.response_error=0;ctrl.error_data_x=0;ctrl.wait_cycles=0;
    checked_write('h80,'h76543210);checked_read('h80);
  endtask
endclass

class svip_apb_test_reg extends uvm_reg;
  `uvm_object_utils(svip_apb_test_reg)
  uvm_reg_field value;
  uvm_reg_field upper;
  function new(string name="csr",int bits=32); super.new(name,bits,UVM_NO_COVERAGE); endfunction
  function void build(int bits);
    value=uvm_reg_field::type_id::create("value");
    value.configure(this,8,0,"RW",0,0,1,1,1);
    if(bits>8)begin
      upper=uvm_reg_field::type_id::create("upper");
      upper.configure(this,bits-8,8,"RW",0,0,1,1,0);
    end
  endfunction
endclass

class svip_apb_test_block extends uvm_reg_block;
  function new(string name);super.new(name,UVM_NO_COVERAGE);endfunction
endclass

class svip_apb_ral_test extends svip_apb_base_test;
  `uvm_component_utils(svip_apb_ral_test)
  svip_apb_test_block block;
  uvm_reg_map map;
  svip_apb_test_reg csr;
  uvm_reg_predictor #(svip_apb_txn) predictor;
  function new(string name,uvm_component parent); super.new(name,parent); endfunction
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    block=new("registers");
    block.configure(null,"");
    map=block.create_map("map",0,cfg.data_width/8,UVM_LITTLE_ENDIAN,1);
    csr=new("csr",cfg.data_width);csr.configure(block);csr.build(cfg.data_width);
    map.add_reg(csr,'h80,"RW");block.lock_model();
    predictor=uvm_reg_predictor#(svip_apb_txn)::type_id::create("predictor",this);
  endfunction
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    agent.connect_reg_map(map);agent.connect_predictor(predictor,map);
  endfunction
  task run_case();
    uvm_status_e status;
    uvm_reg_data_t value;
    svip_apb_data_t expected=data_pattern('h96a53cc3);
    // RAL owns submissions here; count actual requested frontdoor operations.
    sent=1;
    csr.write(status,expected,UVM_FRONTDOOR,map);
    #1ps;
    if(status!==UVM_IS_OK || csr.get_mirrored_value()!==expected)
      `uvm_fatal("TEST_RAL","frontdoor write / explicit prediction failed")
    if(case_name=="ral_byte_enable")begin
      sent++;
      csr.value.write(status,'h5a,UVM_FRONTDOOR,map);#1ps;
      expected[7:0]='h5a;
      if(status!==UVM_IS_OK || observed.items[$].strb!==1 || csr.get_mirrored_value()!==expected)
        `uvm_fatal("TEST_RAL_BYTE","field write lost byte enable / mirror")
    end
    if(case_name=="ral_reset")begin
      ctrl.wait_cycles=100;sent++;
      fork
        csr.read(status,value,UVM_FRONTDOOR,map);
        begin
          wait(vif.psel===1 && vif.penable===1);@(negedge ctrl.clk);ctrl.resetn=0;
          #1ps;if(vif.psel!==0 || vif.penable!==0) `uvm_fatal("TEST_RAL_RESET","pins not idle")
          @(negedge ctrl.clk);ctrl.resetn=1;
        end
      join
      #1ps;
      if(status!==UVM_NOT_OK || csr.get_mirrored_value()!==expected)
        `uvm_fatal("TEST_RAL_RESET","abort returned success or changed mirror")
      ctrl.wait_cycles=0;
    end
    if(case_name=="ral_error")begin
      ctrl.response_error=1;ctrl.error_data_x=1;sent++;
      csr.read(status,value,UVM_FRONTDOOR,map);#1ps;
      if(status!==UVM_NOT_OK || csr.get_mirrored_value()!==expected)
        `uvm_fatal("TEST_RAL_ERROR",$sformatf("status=%s mirror=%h expected=%h",status.name(),csr.get_mirrored_value(),expected))
      sent++;csr.write(status,data_pattern('h57ca83b6),UVM_FRONTDOOR,map);#1ps;
      if(status!==UVM_NOT_OK || csr.get_mirrored_value()!==expected)
        `uvm_fatal("TEST_RAL_ERROR","error write returned success / changed mirror")
      ctrl.response_error=0;ctrl.error_data_x=0;
    end
    sent++;
    csr.read(status,value,UVM_FRONTDOOR,map);
    #1ps;
    if(status!==UVM_IS_OK || value!==expected || ctrl.bus_completions!=(case_name=="ral_reset"?2:sent))
      `uvm_fatal("TEST_RAL","frontdoor read differs from fixed value")
  endtask
endclass

jtag/testsuite/examples/svip_jtag_examples.svh

Download original source · 40 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
task examples();
  svip_jtag_txn r;
  case(scenario)
    "example_basic":begin
      reset_tms(r);goto_state(SVIP_JTAG_RTI,r);
      scan_word(0,0,32,r);
      insist(r.status==SVIP_JTAG_DONE,"IDCODE scan");
      foreach(r.rx_bits[i])insist(r.rx_bits[i]===((32'h149511c3>>i)&1),"IDCODE");
    end
    "example_patterns":begin setup_bypass();for(int p=0;p<5;p++)bypass_check(132,p);end
    "example_long":begin setup_bypass();bypass_check(4097);end
    "example_callback":begin cb.change=1;setup_bypass();bypass_check(1,1);cb.change=0;end
    "example_passive":begin
      // External master: passive agent receives no sequence items or driver flags.
      repeat(5)manual_cycle(1);manual_cycle(0);
      manual_cycle(1);manual_cycle(1);manual_cycle(0);manual_cycle(0);
      repeat(3)manual_cycle(0,1);manual_cycle(1,1);manual_cycle(1);manual_cycle(0);
      insist(agent.mon.n_scans==1,"passive IR commit");
    end
    "example_raw_pause":begin
      bit tms[],tdi[];
      setup_bypass();goto_state(SVIP_JTAG_SHIFT_DR,r);
      tms=new[4];tdi=new[4];tms='{0,0,1,0};tdi='{1,0,1,0};raw(tms,tdi,r);
      insist(ctrl.state==6,"Pause-DR");
      #123ps; // TCK stopped low; this does not itself move the TAP.
      insist(ctrl.state==6,"stopped clock holds Pause");
      tms='{1,0,0,1,1,0};tdi=new[6];raw(tms,tdi,r);
      insist(ctrl.state==1 && agent.mon.last_scan.tdo_bits.size()==5,"one cumulative committed scan");
    end
    "example_register":begin
      setup_bypass();scan_word(1,2,4,r);scan_word(0,32'h89abcdef,32,r);
      insist(ctrl.value==32'h89abcdef && ctrl.updates==1,"Update side effect");
      scan_word(0,0,32,r);
      foreach(r.rx_bits[i])insist(r.rx_bits[i]===((32'h89abcdef>>i)&1),"register readback");
    end
    default:p1();
  endcase
endtask

spi/testsuite/examples/svip_spi_examples.svh

Download original source · 53 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Each example checks results; the same public API is usable outside this TB.
// ctrl/manual below are independent peer fixtures, replaced by the DUT in a project.
task example_basic_api();
  svip_spi_transfer_seq seq=svip_spi_transfer_seq::type_id::create("example");
  profile(0,8,0);ctrl.reply='{'h3d,'ha7};ctrl.seen.delete();
  seq.cs=0;seq.tx_words='{'h96,'h23};
  seq.check_rx=1;seq.expected_words='{'h3d,'ha7};
  seq.start(spi.sequencer);
  if(seq.result.status!=SVIP_SPI_OK || seq.result.n_received!=2 || seq.result.n_sampled!=16 ||
     ctrl.seen.size()!=2 || ctrl.seen[0]!=='h96 || ctrl.seen[1]!=='h23)
    `uvm_fatal("SPI_ORACLE","public transfer sequence did not complete expected exchange")
endtask

task example_slave_api();
  svip_spi_txn response=svip_spi_txn::type_id::create("response");
  svip_spi_word_t command[]='{'h96,'h23};svip_spi_sample_t returned[];
  profile(0,8,0);response.tx_words='{'h3d,'ha7};
  if(!spi.enqueue_response(response))`uvm_fatal("SPI_TEST","response rejected")
  manual(command,returned);
  if(returned.size()!=2 || returned[0]!=='h3d || returned[1]!=='ha7 ||
     spi.pending_responses()!=0 || completed.items.size()!=1 || completed.items[0].status!=SVIP_SPI_OK ||
     completed.items[0].rx_words[0]!=='h96 || completed.items[0].rx_words[1]!=='h23)
    `uvm_fatal("SPI_ORACLE","queued response or received command incorrect")
endtask

task run_example(string which,output bit handled);
  handled=1;
  case(which)
    "example_basic":example_basic_api();
    "example_modes":begin simple_master(0);simple_master(1);simple_master(2);simple_master(3);end
    "example_nonbyte":simple_master(2,9,1);
    "example_multi_cs":begin simple_master(0,8,0,0);simple_master(3,8,1,`SVIP_SPI_MAX_CS-1);end
    "example_patterns":patterns();
    "example_random":patterns(1);
    "example_slave_queue":example_slave_api();
    "example_callback":slave_generated(0,1);
    "example_passive":passive_modes();
    "example_clock_hold":clock_hold();
    "example_reset":reset_master();
    "example_compare":masked_compare();
    "example_timing":begin
      cfg.t_low_max=cfg.t_low+half_ps*1ps;cfg.t_high_max=cfg.t_high+half_ps*1ps;
      cfg.word_gap=half_ps*1ps;cfg.word_gap_max=half_ps*3ps;
      cfg.frame_delay=0;cfg.frame_delay_max=half_ps*2ps;
      simple_master(0,9,0);simple_master(3,9,1);
      if(observe.frames[$].min_half_period==observe.frames[$].max_half_period)
        `uvm_fatal("SPI_ORACLE","timing variation did not reach pins")
    end
    default:handled=0;
  endcase
endtask

control/clock/examples/svip_clock_example.sv

Download original source · 44 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Only common + clock are compiled. No reset/signal/coordinator package.
module svip_clock_example;
  timeunit 1ps; timeprecision 1ps;
  import svip_control_common_pkg::*;
  import svip_clock_pkg::*;
  wire clk;
  svip_control_pin_if pin(clk,clk);
  assign clk=pin.drive_value;
  initial begin
    svip_control_registry resources;
    svip_control_sv_binding#(1) binding;
    svip_control_endpoint endpoint;
    svip_clock_controller clock;
    svip_control_result r;
    svip_control_tick_t accepted,first,edges;
    string reason;
    #1;resources=new;binding=new(pin);
    if(!resources.register_pin("clock",binding,"dut.clk",0,"tb",0,SVIP_CONTROL_CLOCK,
      SVIP_CONTROL_DIRECT_PIN,endpoint,reason))$fatal(1,"%s",reason);
    clock=new(endpoint,20,30);
    fork clock.run();clock.monitor();join_none
    accepted=svip_control_now();clock.start(accepted+500,r);
    if(r.status!=SVIP_CONTROL_COMPLETED)$fatal(1,"start");
    clock.wait_edges(1,svip_control_now()+500,r);first=binding.last_rise();
    if(r.status!=SVIP_CONTROL_COMPLETED || first-accepted!=30)$fatal(1,"full first low");
    #3;clock.stop_low(svip_control_now()+500,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || clk!==0 || binding.last_fall()-first!=20)$fatal(1,"complete high/stop low");
    edges=binding.rises();#47;
    if(binding.rises()!=edges)$fatal(1,"unexpected stopped edge");
    accepted=svip_control_now();clock.start(accepted+500,r);
    clock.wait_edges(1,svip_control_now()+500,r);
    if(binding.last_rise()-accepted!=30)$fatal(1,"restart full low");
    clock.set_period_at_boundary(12,18,svip_control_now()+500,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || clock.period()!=30)$fatal(1,"boundary timing");
    clock.wait_edges(1,svip_control_now()+500,r);
    if(binding.last_rise()-clock.boundary_edge()!=30)$fatal(1,"actual new period");
    clock.stop_low(svip_control_now()+500,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || clock.unexpected_edges!=0)$fatal(1,"final stop");
    $display("SVIP_COMPONENT_PASS component=clock");$finish;
  end
  initial begin #2000;$fatal(1,"clock example deadline");end
endmodule

control/reset/examples/svip_reset_example.sv

Download original source · 63 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Project-owned HDL clock; no clock generator UVC or signal package is needed.
module svip_reset_example;
  timeunit 1ps; timeprecision 1ps;
  import svip_control_common_pkg::*;
  import svip_reset_pkg::*;
  bit clk=0;
  always #25 clk=~clk;
  wire rst_n;
  logic received=1;
  svip_control_pin_if#(1,1'b1) pin(rst_n,clk);
  svip_control_pin_if clock_pin(clk,clk);
  assign rst_n=pin.drive_value;
  always @(posedge clk or negedge rst_n)if(!rst_n)received<=0;else received<=1;

  class project_clock_ref extends svip_control_clock_ref;
    function new(svip_control_endpoint endpoint);super.new(endpoint);endfunction
    virtual function bit is_running();return 1;endfunction
    // This reset example only uses actual edge IDs, not period prediction.
    virtual function bit predicts_rise(svip_control_tick_t at_tick);return 0;endfunction
  endclass
  initial begin
    svip_control_registry resources;
    svip_control_sv_binding#(1,1'b1) binding;
    svip_control_sv_binding#(1) clock_binding;
    svip_control_endpoint endpoint,clock_endpoint;
    svip_reset_controller reset;
    project_clock_ref reference_clock;
    svip_control_result r,old_release;
    svip_control_tick_t epoch;
    string reason;bit ok;
    #1;resources=new;binding=new(pin);clock_binding=new(clock_pin,"",0);
    if(!resources.register_pin("reset",binding,"dut.rst_n",0,"tb",0,SVIP_CONTROL_RESET,
      SVIP_CONTROL_DIRECT_PIN,endpoint,reason))$fatal(1,"%s",reason);
    if(!resources.register_pin("external_clock",clock_binding,"dut.clk",0,"project",1,SVIP_CONTROL_CLOCK,
      SVIP_CONTROL_DIRECT_PIN,clock_endpoint,reason))$fatal(1,"%s",reason);
    reset=new(endpoint,1);reference_clock=new(clock_endpoint);
    fork reset.monitor();join_none
    reset.set_source("software",1,svip_control_now()+300,r);epoch=reset.watch.epoch();
    if(r.status!=SVIP_CONTROL_COMPLETED || rst_n!==0 || received!==0 || epoch!=1)$fatal(1,"actual async reset");
    reset.set_source("software",1,svip_control_now()+300,r);
    if(reset.watch.epoch()!=epoch)$fatal(1,"repeated assertion epoch");
    reset.set_source("watchdog",1,svip_control_now()+300,r);
    reset.set_source("software",0,svip_control_now()+300,r);
    if(rst_n!==0)$fatal(1,"other source lost");
    reset.set_source("watchdog",0,svip_control_now()+300,r);
    if(rst_n!==1)$fatal(1,"physical release");
    reset.source_after_edge("scheduled",1,reference_clock,clock_binding.rises()+1,svip_control_now()+300,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || r.applied_tick!=clock_binding.last_rise() || received!==0)$fatal(1,"external-clock assertion");
    reset.source_after_edge("scheduled",0,reference_clock,clock_binding.rises()+1,svip_control_now()+300,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || rst_n!==1 || received!==0)$fatal(1,"release is not receiver ready");
    clock_binding.wait_rises(clock_binding.rises()+1,svip_control_now()+300,ok);#1;
    if(!ok || received!==1)$fatal(1,"receiver did not recover");
    fork
      reset.source_at("software",0,svip_control_now()+20,svip_control_now()+300,old_release);
      begin #3;reset.set_source("software",1,svip_control_now()+300,r);end
    join
    if(old_release.status!=SVIP_CONTROL_CANCELLED || rst_n!==0)$fatal(1,"stale source release");
    $display("SVIP_COMPONENT_PASS component=reset");$finish;
  end
  initial begin #2000;$fatal(1,"reset example deadline");end
endmodule

control/signal/examples/svip_signal_example.sv

Download original source · 54 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Existing HDL clock plus a narrow reference: no clock/reset UVC is compiled.
module svip_signal_example;
  timeunit 1ps; timeprecision 1ps;
  import svip_control_common_pkg::*;
  import svip_signal_pkg::*;
  bit clk=0;
  always #25 clk=~clk;
  wire [64:0] cfg;
  logic [64:0] received='0;
  svip_control_pin_if#(65) pin(cfg,clk);
  svip_control_pin_if clock_pin(clk,clk);
  assign cfg=pin.drive_value;
  always @(posedge clk)received<=cfg;
  class project_clock_ref extends svip_control_clock_ref;
    function new(svip_control_endpoint endpoint);super.new(endpoint);endfunction
    virtual function bit is_running();return 1;endfunction
    virtual function bit predicts_rise(svip_control_tick_t at_tick);
      return at_tick>=25 && (at_tick-25)%50==0;
    endfunction
  endclass
  initial begin
    svip_control_registry resources;
    svip_control_sv_binding#(65) binding;
    svip_control_sv_binding#(1) clock_binding;
    svip_control_endpoint endpoint,clock_endpoint;
    svip_signal_controller signal_bank;
    project_clock_ref reference_clock;
    svip_control_when when_at;
    svip_control_result r;
    string reason;
    #1;resources=new;binding=new(pin);clock_binding=new(clock_pin,"",0);
    if(!resources.register_pin("cfg",binding,"dut.cfg",0,"tb",0,SVIP_CONTROL_SIGNAL,
      SVIP_CONTROL_DIRECT_PIN,endpoint,reason))$fatal(1,"%s",reason);
    if(!resources.register_pin("external_clock",clock_binding,"dut.clk",0,"project",1,SVIP_CONTROL_CLOCK,
      SVIP_CONTROL_DIRECT_PIN,clock_endpoint,reason))$fatal(1,"%s",reason);
    reference_clock=new(clock_endpoint);signal_bank=new(endpoint,reference_clock);
    when_at=new(SVIP_CONTROL_ASYNC_AT,svip_control_now()+3);
    signal_bank.set(65'h10000000000000001,svip_control_mask(65),when_at,svip_control_now()+300,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || r.actual!==65'h10000000000000001)$fatal(1,"65-bit actual data");
    when_at=new(SVIP_CONTROL_BEFORE_CAPTURE,75);when_at.setup_ticks=3;
    signal_bank.set(65'h10000000000000008,svip_control_mask(65),when_at,svip_control_now()+300,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || r.applied_tick!=72 || r.observed_tick!=75 || received!==65'h10000000000000008)$fatal(1,"external-clock capture");
    when_at=new(SVIP_CONTROL_AFTER_SOURCE_EDGE,0,clock_binding.rises()+1);
    signal_bank.set('h55,'hff,when_at,svip_control_now()+300,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || r.applied_tick!=clock_binding.last_rise() ||
      cfg!==65'h10000000000000055 || received!==65'h10000000000000008)$fatal(1,"masked after-edge old/new views");
    signal_bank.expect_value(65'h10000000000000055,svip_control_mask(65),svip_control_now()+100,r);
    if(r.status!=SVIP_CONTROL_COMPLETED)$fatal(1,"actual observation");
    $display("SVIP_COMPONENT_PASS component=signal");$finish;
  end
  initial begin #2000;$fatal(1,"signal example deadline");end
endmodule

control/coordinator/examples/svip_clock_signal_example.sv

Download original source · 40 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// A selected pair shares ownership/backend once; reset/coordinator are absent.
module svip_clock_signal_example;
  timeunit 1ps;timeprecision 1ps;
  import svip_control_common_pkg::*;
  import svip_clock_pkg::*;
  import svip_signal_pkg::*;
  wire clk;
  wire [31:0] cfg;
  logic [31:0] received=0;
  svip_control_pin_if clock_pin(clk,clk);
  svip_control_pin_if#(32) data_pin(cfg,clk);
  assign clk=clock_pin.drive_value;assign cfg=data_pin.drive_value;
  always @(posedge clk)received<=cfg;
  initial begin
    svip_control_registry resources;
    svip_control_sv_binding#(1) clock_binding;
    svip_control_sv_binding#(32) data_binding;
    svip_control_endpoint clock_endpoint,data_endpoint;
    svip_clock_controller clock;
    svip_signal_controller signal_bank;
    svip_control_when when_at;
    svip_control_result r;
    string reason;
    #1;resources=new;clock_binding=new(clock_pin);data_binding=new(data_pin);
    if(!resources.register_pin("clock",clock_binding,"clk",0,"tb",0,SVIP_CONTROL_CLOCK,SVIP_CONTROL_DIRECT_PIN,clock_endpoint,reason))$fatal(1,"%s",reason);
    if(!resources.register_pin("data",data_binding,"cfg",0,"tb",0,SVIP_CONTROL_SIGNAL,SVIP_CONTROL_DIRECT_PIN,data_endpoint,reason))$fatal(1,"%s",reason);
    clock=new(clock_endpoint,20,30);signal_bank=new(data_endpoint,clock);
    fork clock.run();clock.monitor();join_none
    clock.start(svip_control_now()+500,r);
    when_at=new(SVIP_CONTROL_BEFORE_CAPTURE,clock.next_rise_tick());when_at.setup_ticks=3;
    signal_bank.set(32'h80402001,svip_control_mask(32),when_at,svip_control_now()+500,r);
    if(r.status!=SVIP_CONTROL_COMPLETED || received!==32'h80402001)$fatal(1,"pair capture");
    clock.stop_low(svip_control_now()+500,r);
    if(r.status!=SVIP_CONTROL_COMPLETED)$fatal(1,"pair stop");
    $display("SVIP_COMPONENT_PASS component=clock_signal");$finish;
  end
  initial begin #2000;$fatal(1,"pair example deadline");end
endmodule

control/testsuite/examples/svip_control_examples.svh

Download original source · 142 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Checked examples using the same public API as customer test sequences.

  task automatic example_basic();
    svip_control_when at_tick;
    svip_control_result r;
    svip_control_value_t value='0;
    value[256]=1;value[64]=1;value[0]=1;
    check(!core_domain.ready(),"A29 accepting start is not actual clock availability");
    control.domains.wait_ready("core",$time+3,r);check(r.status==SVIP_CONTROL_TIMEOUT,"A29 ready cannot precede first actual edge");
    at_tick=new(SVIP_CONTROL_BEFORE_CAPTURE,core_clock.next_rise_tick());at_tick.setup_ticks=100;
    wide_signal.set(value,svip_control_mask(257),at_tick,$time+20000,r);
    check(r.status==SVIP_CONTROL_COMPLETED,"before capture complete");
    check(wide_q[256]===1 && wide_q[64]===1 && wide_q[0]===1,"receiver actual high bits");
    check(r.observed && !r.effective,"pin observed is distinct from destination effect");
    control.domains.wait_ready("core",$time+100,r);check(r.status==SVIP_CONTROL_COMPLETED,"domain clock/reset availability observed");
  endtask
  

task automatic test_profile_policies();
  svip_control_profile profile;
  svip_control_result r;
  svip_control_transition_plan plan;
  svip_control_when at_tick;
  svip_control_value_t old_a,old_b;
  svip_control_value_t a='0;
  a[64]=1;a[0]=1;
  profile=new("tuple");profile.add(cfg_a_signal,a,svip_control_mask(65));profile.add(cfg_b_signal,32'h83579bdf,svip_control_mask(32));
  control.capture_coherent(profile,core_clock,core_clock.next_rise_tick(),100,$time+20000,r);
  check(r.status==SVIP_CONTROL_COMPLETED,"B01 profile coherent");
  check(tuple_q=={65'h10000000000000001,32'h83579bdf},"B01 real RTL tuple complete");
  control.capture_coherent(profile,core_clock,core_clock.next_rise_tick(),100,$time+20000,r);
  check(r.status==SVIP_CONTROL_COMPLETED,"B05 same value no value-change dependency");
  old_a=cfg_a;old_b=cfg_b;profile.values[0]=0;profile.values[1]='0;profile.values[1][32]=1;
  control.capture_coherent(profile,core_clock,core_clock.next_rise_tick(),100,$time+20000,r);
  check(r.status==SVIP_CONTROL_REJECTED && cfg_a==old_a && cfg_b==old_b,"B02 invalid second field no partial first write");
  profile=new("cross_domain");profile.add(cfg_a_signal,1,1);profile.add(data64_signal,1,1);
  control.capture_coherent(profile,core_clock,core_clock.next_rise_tick(),100,$time+20000,r);
  check(r.status==SVIP_CONTROL_REJECTED,"B02 cross-domain capture coherence rejected");
  plan=new(profile);at_tick=new(SVIP_CONTROL_ASYNC_AT,$time+10);plan.steps.push_back(at_tick);
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time+20);plan.steps.push_back(at_tick);
  plan.request_global_atomic=1;control.transition(plan,$time+20000,r);check(r.status==SVIP_CONTROL_REJECTED,"B02 unsupported global atomic rejected");
  plan.request_global_atomic=0;control.transition(plan,$time+20000,r);
  check(r.status==SVIP_CONTROL_COMPLETED && plan.progress.size()==2,"B03 per-domain barrier reports each step");
  check(plan.progress[0].applied_tick<plan.progress[1].applied_tick,"B03 barrier does not imply simultaneous effects");
endtask



task automatic test_lifecycle_policies();
  svip_control_result r;
  svip_control_when at_tick;
  svip_control_value_t image='0;
  tieoff_control.configure(1,$time+1000,r);check(r.status==SVIP_CONTROL_COMPLETED,"B06 tieoff bootstrap");
  check(tieoff_control.lock(),"B06 tieoff lock");
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time);
  tieoff_signal.set(0,1,at_tick,$time+1000,r);
  check(r.status==SVIP_CONTROL_REJECTED && tieoff===1,"B06 low-level signal path cannot bypass lock");
  tieoff_signal.expect_stable(1,$time+101,$time+200,r);
  check(r.status==SVIP_CONTROL_COMPLETED,"B06 independent actual-pin stability window");
  image[64]=1;image[5]=1;
  fuse_control.load_image(image,$time+1000,r);check(r.status==SVIP_CONTROL_REJECTED,"B08 no POR window");
  reset_high.set_source("por",1,$time+1000,r);
  fuse_control.load_image(image,$time+1000,r);
  check(r.status==SVIP_CONTROL_COMPLETED && fuse_image==65'h10000000000000020,"B07 image loaded with high bit");
  fuse_control.load_image(1,$time+1000,r);check(r.status==SVIP_CONTROL_REJECTED,"B08 consumed POR epoch locked");
  check(!ep_fuse.open_load("FUNCTIONAL_RESET"),"B08 wrong load event rejected");
  reset.set_source("functional",1,$time+1000,r);reset.set_source("functional",0,$time+1000,r);
  check(fuse_image==65'h10000000000000020 && tieoff===1,"B07 functional reset retained image/tieoff");
  reset_high.set_source("por",0,$time+1000,r);reset_high.set_source("por",1,$time+1000,r);
  fuse_control.load_image(65'h10000000000000001,$time+1000,r);
  check(r.status==SVIP_CONTROL_COMPLETED && fuse_image==65'h10000000000000001,"B07 new observed POR epoch can reload");
  reset_high.set_source("por",0,$time+1000,r);
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time);
  fuse_signal.set(0,svip_control_mask(65),at_tick,$time+1000,r);
  check(r.status==SVIP_CONTROL_REJECTED && fuse_image[64]===1,"B08 no ordinary write after window");
endtask



task automatic test_irq();
  svip_control_result r,pulse_result,observation_result;
  svip_control_when at_tick;
  svip_control_tick_t measured,first_tick;
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time+7);
  fork
    short_irq_observer.measure_pulse($time+1000,measured,observation_result);
    irq_source.pulse_time(1,0,1,23,at_tick,$time+1000,pulse_result);
  join
  check(pulse_result.status==SVIP_CONTROL_COMPLETED && observation_result.status==SVIP_CONTROL_COMPLETED && measured==23,"B10 exact short 23ps pulse, no stretch");
  check(irq_q===0,"B10 sub-cycle pulse can be missed by RTL; no hidden synchronizer");
  #1; // Visible idle separates the two examples even in settled-only FSDB.
  first_tick=$time;irq_source.pulse_cycles(1,0,1,2,$time+20000,pulse_result);
  check(pulse_result.status==SVIP_CONTROL_COMPLETED && irq===0 && irq_q===1,"B10 N future captures, release after last capture");
  check(pulse_result.applied_tick==first_tick,"B10 cycle pulse starts at accepted source tick");
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time+17);
  irq_inputs_source.level(4'b1010,at_tick,$time+1000,r);
  check(irq_inputs===4'b1010,"B09 multiple IRQ lines together");
  reference_clock.wait_edges(1,$time+20000,r);
  irq_observer.expect_level(1,$time+1000,r);check(r.status==SVIP_CONTROL_COMPLETED,"B11 DUT output observed");
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time);irq_inputs_source.level(0,at_tick,$time+1000,r);
  check(dut_irq===1,"B11 clearing source does not silently drive sticky DUT output");
  csr.write("pending",4'b1010,$time+50000,r);check(r.status==SVIP_CONTROL_COMPLETED,"B11 clear via real W1C");
  irq_observer.expect_level(0,$time+1000,r);check(r.status==SVIP_CONTROL_COMPLETED,"B11 output clears after CSR");
endtask



task automatic test_csr();
  svip_control_result bus_result,r;
  svip_control_value_t value;
  svip_control_when at_tick;
  svip_control_endpoint bad;
  svip_control_sv_binding#(32) active_binding;
  int before_reads;
  string why;
  csr.write("control",32'h80402001,$time+80000,bus_result);
  check(bus_result.status==SVIP_CONTROL_COMPLETED && bus_result.applied_kind=="CSR_TRANSACTION_COMPLETED","B12 typed bus completion");
  check(!bus_result.observed && !bus_result.effective && csr_output==0,"B12 APB done precedes destination output effect");
  csr_observer.expect_value(32'h80402001,svip_control_mask(32),$time+80000,r,1,SVIP_CONTROL_CAPTURE_VALUE,b_csr_output.capture_id());
  check(r.status==SVIP_CONTROL_COMPLETED && r.observed_tick>bus_result.applied_tick,"B12 actual delayed target-domain observation");
  $display("SVIP_CONTROL_CSR_EFFECT bus_tick=%0d actual_capture_tick=%0d value=%0h",bus_result.applied_tick,r.observed_tick,r.actual);
  csr.read("control",$time+80000,r,value);check(value==32'h80402001,"real CSR readback");
  csr.write("control_byte",8'ha5,$time+80000,r);check(r.status==SVIP_CONTROL_COMPLETED,"native byte strobe write");
  csr.read("control",$time+80000,r,value);check(value==32'h8040a501,"byte update preserved other bytes without RMW");
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time);irq_inputs_source.level(4'b1010,at_tick,$time+1000,r);
  reference_clock.wait_edges(1,$time+20000,r);
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time);irq_inputs_source.level(0,at_tick,$time+1000,r);
  before_reads=csr_reads;csr.write("pending",2,$time+80000,r);
  check(r.status==SVIP_CONTROL_COMPLETED && pending==8 && csr_reads==before_reads,"B14 W1C preserves unrelated pending, no blind read-modify-write");
  csr.write("pending",8,$time+80000,r);check(dut_irq===0,"B14 real pending clear");
  csr.read("read_clear",$time+80000,r,value);check(value==32'h80000001,"B14 explicit side-effect read");
  csr.read("read_clear",$time+80000,r,value);check(value==0,"B14 read-clear hardware effect");
  csr.write("read_clear",1,$time+80000,r);check(r.status==SVIP_CONTROL_REJECTED,"B14 read-clear cannot be RMW-written");
  active_binding=new(csr_output_pin,"svip_control_tb.csr_output_pin.observed",1);
  check(!registry.register_pin("direct_fallback",active_binding,"csr_output",0,"bad",0,SVIP_CONTROL_SIGNAL,SVIP_CONTROL_DIRECT_PIN,bad,why),"B18 CSR canonical output rejects DirectPin owner");
  at_tick=new(SVIP_CONTROL_ASYNC_AT,$time);csr_observer.set(0,svip_control_mask(32),at_tick,$time+1000,r);
  check(r.status==SVIP_CONTROL_REJECTED,"B18 CSR observer cannot drive through low API");
endtask


control/testbench/svip_control_quickstart.sv

Download original source · 52 lines

// SPDX-License-Identifier: Apache-2.0
// Author: syan
// Standalone customer-style integration: core filelist, no APB dependency.
module svip_control_quickstart;
  timeunit 1ps;timeprecision 1ps;
  import svip_control_pkg::*;
  wire clk,rst_n;
  wire [31:0] cfg;
  logic [31:0] received='0;
  svip_control_pin_if clock_pin(clk,clk);
  svip_control_pin_if#(1,1'b1) reset_pin(rst_n,clk);
  svip_control_pin_if#(32) cfg_pin(cfg,clk);
  assign clk=clock_pin.drive_value;
  assign rst_n=reset_pin.drive_value;
  assign cfg=cfg_pin.drive_value;
  always @(posedge clk or negedge rst_n)if(!rst_n)received<=0;else received<=cfg;
  initial begin
    svip_control_registry resources;
    svip_control_sv_binding#(1) clock_binding;
    svip_control_sv_binding#(1,1'b1) reset_binding;
    svip_control_sv_binding#(32) cfg_binding;
    svip_control_endpoint clock_ep,reset_ep,cfg_ep;
    svip_clock_controller clock;
    svip_reset_controller reset;
    svip_signal_controller signal_bank;
    svip_control_when before_capture;
    svip_control_result result;
    string reason;
    #1; // All physical startup pins are known before model initialization.
    resources=new;
    clock_binding=new(clock_pin);reset_binding=new(reset_pin);cfg_binding=new(cfg_pin);
    if(!resources.register_pin("core_clock",clock_binding,"dut.clk",0,"tb",0,
      SVIP_CONTROL_CLOCK,SVIP_CONTROL_DIRECT_PIN,clock_ep,reason))$fatal(1,"%s",reason);
    if(!resources.register_pin("core_reset",reset_binding,"dut.rst_n",0,"tb",0,
      SVIP_CONTROL_RESET,SVIP_CONTROL_DIRECT_PIN,reset_ep,reason))$fatal(1,"%s",reason);
    if(!resources.register_pin("cfg",cfg_binding,"dut.cfg",0,"tb",0,
      SVIP_CONTROL_SIGNAL,SVIP_CONTROL_DIRECT_PIN,cfg_ep,reason))$fatal(1,"%s",reason);
    clock=new(clock_ep,2000,3000);reset=new(reset_ep,1);
    reset.attach(cfg_ep);signal_bank=new(cfg_ep,clock);
    fork clock.run();clock.monitor();reset.monitor();join_none
    clock.start($time+20000,result);
    if(result.status!=SVIP_CONTROL_COMPLETED)$fatal(1,"start: %s",result.reason);
    before_capture=new(SVIP_CONTROL_BEFORE_CAPTURE,clock.next_rise_tick());
    before_capture.setup_ticks=100;
    signal_bank.set(32'h80402001,svip_control_mask(32),before_capture,$time+20000,result);
    if(result.status!=SVIP_CONTROL_COMPLETED || received!==32'h80402001)$fatal(1,"actual capture failed");
    clock.stop_low($time+20000,result);
    if(result.status!=SVIP_CONTROL_COMPLETED || clk!==0)$fatal(1,"stop failed");
    $display("SVIP_CONTROL_QUICKSTART_PASS received=%h",received);$finish;
  end
  initial begin #100000;$fatal(1,"quickstart deadline");end
endmodule