Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
376 changes: 376 additions & 0 deletions docs/uvm_integration_guide.md

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions docs/uvm_quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# CHIron UVM Quick-Start Guide

Get a CHI RN testbench running in five minutes.

## 1. Clone CHIron

```bash
git clone https://github.com/RISMicroDevices/CHIron.git
cd CHIron
```

## 2. Build the DPI-C Reference Model

```bash
g++ -std=c++20 -fPIC -shared \
-I. \
-DCHI_ISSUE_EB_ENABLE \
-o chi_refmodel.so \
uvm/chi_refmodel_dpi.cpp
```

## 3. Add Your DUT

Open `uvm/chi_top.sv` and un-comment the DUT block, connecting your RTL to the `chi_bus` interface signals. For an initial smoke test without a DUT, leave the block commented out — the testbench will exercise the CHIron reference model alone.

## 4. Run a Test

### VCS

```bash
vcs -full64 -sverilog -ntb_opts uvm-1.2 \
+incdir+uvm \
uvm/chi_top.sv \
-sv_lib chi_refmodel \
-o simv

./simv +UVM_TESTNAME=chi_rand_test +NUM_TRANSACTIONS=50
```

### Xcelium

```bash
xrun -sv -uvm +incdir+uvm uvm/chi_top.sv \
-sv_lib chi_refmodel.so \
+UVM_TESTNAME=chi_rand_test
```

### Questa

```bash
vlog -sv +incdir+uvm uvm/chi_top.sv
vsim -sv_lib chi_refmodel chi_top +UVM_TESTNAME=chi_rand_test
```

## 5. Inspect the CLog

The monitor writes `chi_sim.clog` by default. Decode it with:

```bash
./app/clog2log/clog2log chi_sim.clog
```

Example output:

```
[0042] RN-F#1 TXREQ ReadShared addr=0x0000_0040 txnid=0x001 size=64B
[0043] HN-F#8 RXRSP CompData txnid=0x001 resp=SC
[0044] RN-F#1 TXRSP CompAck txnid=0x001
```

Generate coverage metrics:

```bash
./app/clog2coverage/clog2coverage chi_sim.clog coverage.txt
cat coverage.txt
```

---

## Available Tests

| Test | Command-line | Description |
|---|---|---|
| `chi_read_test` | `+UVM_TESTNAME=chi_read_test` | 10 × ReadShared transactions |
| `chi_write_test` | `+UVM_TESTNAME=chi_write_test` | 10 × WriteUniqueFull transactions |
| `chi_rand_test` | `+UVM_TESTNAME=chi_rand_test +NUM_TRANSACTIONS=100` | 100 randomised transactions |

---

## Quick Configuration

All parameters are in `uvm/chi_pkg.sv`:

```systemverilog
parameter int unsigned NODEID_W = 7; // 7–11
parameter int unsigned ADDR_W = 48; // 44–52
parameter int unsigned DATA_W = 256; // 128/256/512
parameter int unsigned RSVDC_W = 4; // 0/4/8/12/16/24/32
```

Match these to the `FlitConfiguration<>` used by your DUT and in `uvm/chi_refmodel_dpi.cpp`.

---

## Writing Your First Sequence

```systemverilog
// In your test's run_phase:
chi_write_unique_seq seq = chi_write_unique_seq::type_id::create("seq");
seq.src_id = 7'd1;
seq.tgt_id = 7'd8;
seq.addr = 48'h0000_0000_0040;
seq.write_data = 256'hDEAD_BEEF;
seq.txn_id = 12'd0;
seq.start(env.agent.sequencer);
```

For complete documentation see [docs/uvm_integration_guide.md](uvm_integration_guide.md).
55 changes: 55 additions & 0 deletions uvm/chi_agent.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// chi_agent.sv
// UVM agent encapsulating the CHI driver, monitor, and sequencer.
//
// The agent can operate in active (UVM_ACTIVE) or passive (UVM_PASSIVE) mode:
// - Active: driver + sequencer + monitor
// - Passive: monitor only
//
// The analysis port from the internal monitor is re-exported so that
// higher-level components (scoreboard, coverage) can connect to it.

`ifndef CHI_AGENT_SV
`define CHI_AGENT_SV

`include "uvm_macros.svh"
import uvm_pkg::*;
import chi_pkg::*;

class chi_agent extends uvm_agent;
`uvm_component_utils(chi_agent)

// Sub-components
chi_driver driver;
chi_monitor monitor;
uvm_sequencer #(chi_seq_item) sequencer;

// Re-exported analysis port from the monitor
uvm_analysis_port #(chi_seq_item) ap;

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

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

// The monitor is always created (passive or active)
monitor = chi_monitor::type_id::create("monitor", this);

if (get_is_active() == UVM_ACTIVE) begin
driver = chi_driver::type_id::create("driver", this);
sequencer = uvm_sequencer #(chi_seq_item)::type_id::create("sequencer", this);
end
endfunction

function void connect_phase(uvm_phase phase);
// Export the monitor analysis port at agent level
ap = monitor.ap;

if (get_is_active() == UVM_ACTIVE)
driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction

endclass : chi_agent

`endif // CHI_AGENT_SV
212 changes: 212 additions & 0 deletions uvm/chi_coverage.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// chi_coverage.sv
// Functional coverage collector for CHI RN transactions.
//
// Receives chi_seq_item from the monitor's analysis port and updates
// covergroups for opcodes, cache state responses, QoS values, and
// cross-coverage between transaction types and response types.

`ifndef CHI_COVERAGE_SV
`define CHI_COVERAGE_SV

`include "uvm_macros.svh"
import uvm_pkg::*;
import chi_pkg::*;

class chi_coverage extends uvm_subscriber #(chi_seq_item);
`uvm_component_utils(chi_coverage)

chi_seq_item current_item;

// -------------------------------------------------------------------------
// REQ opcode coverage
// -------------------------------------------------------------------------
covergroup cg_req_opcodes;
cp_opcode: coverpoint current_item.opcode iff
(current_item.channel == chi_seq_item::CH_TXREQ) {
bins read_shared = {REQ_ReadShared};
bins read_clean = {REQ_ReadClean};
bins read_once = {REQ_ReadOnce};
bins read_no_snp = {REQ_ReadNoSnp};
bins read_unique = {REQ_ReadUnique};
bins read_not_shared_dirty = {REQ_ReadNotSharedDirty};
bins read_once_clean_inv = {REQ_ReadOnceCleanInvalid};
bins read_once_make_inv = {REQ_ReadOnceMakeInvalid};
bins write_unique_full = {REQ_WriteUniqueFull};
bins write_unique_ptl = {REQ_WriteUniquePtl};
bins write_no_snp_full = {REQ_WriteNoSnpFull};
bins write_no_snp_ptl = {REQ_WriteNoSnpPtl};
bins write_back_full = {REQ_WriteBackFull};
bins write_back_ptl = {REQ_WriteBackPtl};
bins write_clean_full = {REQ_WriteCleanFull};
bins write_evict_full = {REQ_WriteEvictFull};
bins clean_unique = {REQ_CleanUnique};
bins make_unique = {REQ_MakeUnique};
bins evict = {REQ_Evict};
bins clean_shared = {REQ_CleanShared};
bins clean_invalid = {REQ_CleanInvalid};
bins make_invalid = {REQ_MakeInvalid};
bins atomic_swap = {REQ_AtomicSwap};
bins atomic_compare = {REQ_AtomicCompare};
bins stash_unique = {REQ_StashOnceUnique};
bins stash_shared = {REQ_StashOnceShared};
bins other = default;
}
cp_size: coverpoint current_item.size iff
(current_item.channel == chi_seq_item::CH_TXREQ) {
bins size_1B = {3'd0};
bins size_2B = {3'd1};
bins size_4B = {3'd2};
bins size_8B = {3'd3};
bins size_16B = {3'd4};
bins size_32B = {3'd5};
bins size_64B = {3'd6};
}
cp_ns: coverpoint current_item.ns iff
(current_item.channel == chi_seq_item::CH_TXREQ);
cp_excl: coverpoint current_item.excl iff
(current_item.channel == chi_seq_item::CH_TXREQ);
cross cp_opcode, cp_size;
cross cp_opcode, cp_ns;
endgroup : cg_req_opcodes

// -------------------------------------------------------------------------
// RSP opcode and response coverage
// -------------------------------------------------------------------------
covergroup cg_rsp;
cp_opcode: coverpoint current_item.opcode iff
(current_item.channel inside {chi_seq_item::CH_RXRSP,
chi_seq_item::CH_TXRSP}) {
bins comp = {RSP_Comp};
bins comp_dbid_resp = {RSP_CompDBIDResp};
bins dbid_resp = {RSP_DBIDResp};
bins retry_ack = {RSP_RetryAck};
bins read_receipt = {RSP_ReadReceipt};
bins comp_ack = {RSP_CompAck};
bins snp_resp = {RSP_SnpResp};
bins snp_resp_fwded = {RSP_SnpRespFwded};
bins pcrd_grant = {RSP_PCrdGrant};
bins other = default;
}
cp_resp: coverpoint current_item.resp iff
(current_item.channel inside {chi_seq_item::CH_RXRSP,
chi_seq_item::CH_TXRSP}) {
bins I = {3'b000};
bins SC = {3'b001};
bins UC = {3'b010};
bins UD = {3'b011};
bins SD = {3'b101};
bins UCE = {3'b110};
bins other = default;
}
cp_resp_err: coverpoint current_item.resp_err iff
(current_item.channel inside {chi_seq_item::CH_RXRSP,
chi_seq_item::CH_TXRSP}) {
bins ok = {2'b00};
bins ex_fail = {2'b01};
bins data_err= {2'b10};
bins nderr = {2'b11};
}
cross cp_opcode, cp_resp;
cross cp_opcode, cp_resp_err;
endgroup : cg_rsp

// -------------------------------------------------------------------------
// DAT channel coverage
// -------------------------------------------------------------------------
covergroup cg_dat;
cp_opcode: coverpoint current_item.opcode iff
(current_item.channel inside {chi_seq_item::CH_TXDAT,
chi_seq_item::CH_RXDAT}) {
bins comp_data = {DAT_CompData};
bins copy_back_wr_data = {DAT_CopyBackWrData};
bins non_copy_wr_data = {DAT_NonCopyBackWrData};
bins snp_resp_data = {DAT_SnpRespData};
bins snp_resp_data_ptl = {DAT_SnpRespDataPtl};
bins snp_resp_data_fwd = {DAT_SnpRespDataFwded};
bins data_sep_resp = {DAT_DataSepResp};
bins write_data_cancel = {DAT_WriteDataCancel};
bins other = default;
}
cp_resp: coverpoint current_item.resp iff
(current_item.channel inside {chi_seq_item::CH_TXDAT,
chi_seq_item::CH_RXDAT});
cp_data_id: coverpoint current_item.data_id iff
(current_item.channel inside {chi_seq_item::CH_TXDAT,
chi_seq_item::CH_RXDAT});
endgroup : cg_dat

// -------------------------------------------------------------------------
// SNP opcode coverage
// -------------------------------------------------------------------------
covergroup cg_snp;
cp_opcode: coverpoint current_item.opcode iff
(current_item.channel == chi_seq_item::CH_RXSNP) {
bins snp_once = {SNP_SnpOnce};
bins snp_clean = {SNP_SnpClean};
bins snp_shared = {SNP_SnpShared};
bins snp_not_shared_dirty= {SNP_SnpNotSharedDirty};
bins snp_unique = {SNP_SnpUnique};
bins snp_clean_shared = {SNP_SnpCleanShared};
bins snp_clean_invalid = {SNP_SnpCleanInvalid};
bins snp_make_invalid = {SNP_SnpMakeInvalid};
bins snp_prefer_unique = {SNP_SnpPreferUnique};
bins snp_dvm_op = {SNP_SnpDVMOp};
bins fwd_snoop = {SNP_SnpOnceFwd, SNP_SnpSharedFwd,
SNP_SnpNotSharedDirtyFwd, SNP_SnpUniqueFwd,
SNP_SnpPreferUniqueFwd};
bins other = default;
}
cp_ret_to_src: coverpoint current_item.ret_to_src iff
(current_item.channel == chi_seq_item::CH_RXSNP);
cp_do_not_go_to_sd: coverpoint current_item.do_not_go_to_sd iff
(current_item.channel == chi_seq_item::CH_RXSNP);
cross cp_opcode, cp_ret_to_src;
endgroup : cg_snp

// -------------------------------------------------------------------------
// QoS coverage (all channels)
// -------------------------------------------------------------------------
covergroup cg_qos;
cp_qos: coverpoint current_item.qos {
bins qos_0 = {4'd0};
bins qos_1 = {4'd1};
bins qos_2 = {4'd2};
bins qos_3 = {4'd3};
bins qos_4 = {4'd4};
bins qos_5 = {4'd5};
bins qos_6 = {4'd6};
bins qos_7 = {4'd7};
bins qos_8 = {4'd8};
bins qos_9 = {4'd9};
bins qos_10 = {4'd10};
bins qos_11 = {4'd11};
bins qos_12 = {4'd12};
bins qos_13 = {4'd13};
bins qos_14 = {4'd14};
bins qos_15 = {4'd15};
}
cp_channel: coverpoint current_item.channel;
cross cp_qos, cp_channel;
endgroup : cg_qos

function new(string name, uvm_component parent);
super.new(name, parent);
cg_req_opcodes = new();
cg_rsp = new();
cg_dat = new();
cg_snp = new();
cg_qos = new();
endfunction

function void write(chi_seq_item item);
current_item = item;
cg_req_opcodes.sample();
cg_rsp.sample();
cg_dat.sample();
cg_snp.sample();
cg_qos.sample();
endfunction

endclass : chi_coverage

`endif // CHI_COVERAGE_SV
Loading