diff --git a/docs/uvm_integration_guide.md b/docs/uvm_integration_guide.md new file mode 100644 index 0000000..922c277 --- /dev/null +++ b/docs/uvm_integration_guide.md @@ -0,0 +1,376 @@ +# CHIron UVM Integration Guide + +## Overview + +[CHIron](https://github.com/RISMicroDevices/CHIron) is the world's first open-source AMBA CHI infrastructure library written in modern C++20. This guide explains how to use CHIron as the **verification kernel** of a SystemVerilog UVM testbench — combining CHIron's protocol intelligence (flit definitions, opcode tables, transaction state machines, and CLog logging) with UVM's test management infrastructure. + +### What "kernel" means + +CHIron provides the authoritative **protocol layer**: + +| CHIron Capability | UVM Role | +|---|---| +| Flit field definitions (`chi/spec/chi_protocol_flits.hpp`) | Defines signal widths used in `chi_if.sv` and `chi_pkg.sv` | +| Opcode tables (`chi/spec/chi_protocol_encoding.hpp`) | Populates opcode `parameter` constants in `chi_pkg.sv` | +| Transaction state machines (`chi/xact/`) | Powers the `chi_scoreboard.sv` reference model via DPI-C | +| CLog binary logger (`clog/clog_b/`, `clog/clog_t/`) | Called by `chi_monitor.sv` to capture every flit | +| Post-processing tools (`app/`) | Used offline to analyse coverage and decode logs | + +UVM provides the test infrastructure: sequencer, driver, monitor, scoreboard framework, coverage, and `run_test()` phasing. + +--- + +## Architecture + +``` +┌──────────────────────────────────────────────────────────────────────────┐ +│ UVM Testbench │ +│ │ +│ ┌──────────┐ ┌────────────────────────────────────────────────────┐ │ +│ │ Test │ │ chi_env │ │ +│ │ (SVh) │ │ ┌────────────────────┐ │ │ +│ └────┬─────┘ │ │ chi_agent │ │ │ +│ │ │ │ ┌──────────────┐ │ analysis_port │ │ +│ │ │ │ │ sequencer │ ├──────────────────┐ │ │ +│ │ │ │ ├──────────────┤ │ │ │ │ +│ └─────────┼──► │ driver │ │ ┌────────────▼──────┐ │ │ +│ │ │ ├──────────────┤ │ │ chi_scoreboard │ │ │ +│ │ │ │ monitor │──┼──► │ (CHIron refmodel │ │ │ +│ │ │ └──────────────┘ │ │ via DPI-C) │ │ │ +│ │ └──────────┬─────────┘ └───────────────────┘ │ │ +│ │ │ ┌───────────────────┐ │ │ +│ │ │ │ chi_coverage │ │ │ +│ │ │ └───────────────────┘ │ │ +│ └─────────────┼────────────────────────────────────┘ │ │ +└───────────────────────────────│────────────────────────────────────────┘ │ + │ SV Interface (chi_if) │ + ┌───────────▼──────────────┐ │ + │ DUT (CHI RN / HN) │ │ + └──────────────────────────┘ │ + + ┌────────────────────────────────────────────────────┐ │ + │ CHIron C++ Kernel (DPI-C shared object) │ │ + │ chi_refmodel_dpi.cpp wraps: │ │ + │ • FlitConfiguration<7,48,4,4,256,false,false> │ │ + │ • Xact::Router / XactionAllocatingRead, ... │ │ + │ • CLogB_WriteRecord (binary trace) │ │ + └────────────────────────────────────────────────────┘ +``` + +--- + +## Repository Structure + +After following this guide your repository tree will look like: + +``` +CHIron/ +├── chi/ ← CHIron protocol layer (C++ headers) +│ ├── basic/ +│ ├── spec/ ← Flit definitions, opcodes +│ ├── util/ +│ └── xact/ ← Transaction state machines +├── clog/ +│ ├── clog_b/ ← Binary CLog DPI-C interface (.svh + C++) +│ └── clog_t/ ← Text CLog DPI-C interface (.svh + C++) +├── app/ ← Post-processing tools +├── uvm/ ← UVM testbench (this guide) +│ ├── chi_pkg.sv ← SV package: constants, opcodes, flit structs +│ ├── chi_if.sv ← CHI bus interface +│ ├── chi_seq_item.sv ← UVM sequence item +│ ├── chi_driver.sv ← UVM RN driver +│ ├── chi_monitor.sv ← UVM monitor + CLog integration +│ ├── chi_agent.sv ← UVM agent (driver + monitor + sequencer) +│ ├── chi_scoreboard.sv ← Protocol checker (CHIron reference model) +│ ├── chi_coverage.sv ← Functional coverage +│ ├── chi_seq_lib.sv ← Sequence library +│ ├── chi_env.sv ← UVM environment +│ ├── chi_test.sv ← Example tests +│ ├── chi_top.sv ← Top-level testbench module +│ └── chi_refmodel_dpi.cpp ← C++ DPI-C shim for CHIron reference model +└── docs/ + ├── uvm_integration_guide.md ← This file + └── uvm_quickstart.md ← Quick-start instructions +``` + +--- + +## Prerequisites + +| Tool | Minimum version | +|---|---| +| C++ compiler | GCC 11 / Clang 13 (C++20 required) | +| SystemVerilog simulator | Synopsys VCS 2022.06, Cadence Xcelium 23.03, Mentor Questa 2022.4, or equivalent | +| CMake | 3.16 (for CHIron C++ tests only) | +| UVM | 1.2 or IEEE 1800.2 | + +--- + +## Step 1 — Configure `chi_pkg.sv` for Your DUT + +Open `uvm/chi_pkg.sv` and set the four parameters to match your DUT's CHI `FlitConfiguration<>` template instantiation: + +```systemverilog +parameter int unsigned NODEID_W = 7; // CHI NodeID_Width (7–11) +parameter int unsigned ADDR_W = 48; // CHI Req_Addr_Width (44–52) +parameter int unsigned DATA_W = 256; // CHI Data_Width (128/256/512) +parameter int unsigned RSVDC_W = 4; // CHI RSVDC_Width (0/4/8/12/16/24/32) +``` + +These values must match the C++ `FlitConfiguration<>` used in `chi_refmodel_dpi.cpp`. + +--- + +## Step 2 — Build the CHIron DPI-C Shared Object + +The `chi_refmodel_dpi.cpp` shim wraps CHIron's C++ transaction state machines. + +### 2.1 Using GCC + +```bash +g++ -std=c++20 -fPIC -shared \ + -I \ + -DCHI_ISSUE_EB_ENABLE \ + -o chi_refmodel.so \ + /uvm/chi_refmodel_dpi.cpp +``` + +Replace `` with the absolute path to the CHIron repository. + +### 2.2 Extending the Reference Model + +The provided `chi_refmodel_dpi.cpp` is a **stub** that validates flit lengths and returns no errors. To enable full protocol checking, extend the `RefModel::feed()` function to: + +1. **Unpack** the raw bytes into a typed `CHI::Flits::REQ` flit using CHIron's flit struct bit-field accessors. +2. **Route** the flit to the appropriate `Xaction` state machine via `CHI::Xact::Router`. +3. **Advance** the transaction state by calling `xact->NextRSP()` / `NextDAT()` on response/data flits. +4. **Check** for denial codes returned by `NextRSP()` / `NextDAT()` — a non-`Accepted` code indicates a protocol violation. + +Key CHIron types to use: + +```cpp +// Typed flit (example for REQ channel) +CHI::Flits::REQ req_flit; +req_flit.SetOpcode(CHI::Opcodes::REQ::ReadShared); +req_flit.SetAddr(address); +req_flit.SetSrcID(src_id); +req_flit.SetTxnID(txn_id); +req_flit.SetSize(6); // 64-byte cache line + +// Transaction state machine for an allocating read +auto xact = std::make_shared>( + global_context, fired_req_flit); + +// Advance on RSP flit +CHI::Xact::XactDenialEnum denial = xact->NextRSP(global_ctx, fired_rsp_flit); +if (denial != CHI::Xact::XactDenial::Accepted) { + // Protocol error detected +} +``` + +Refer to the CHIron transaction tests in `test/tc_chi_xact_state/` for working examples of how the state machines are driven. + +--- + +## Step 3 — Connect `chi_if` to the DUT + +Edit `uvm/chi_top.sv` and un-comment the DUT instantiation block, replacing `chi_rn_dut` with your DUT module name and adjusting the port map. + +```systemverilog +chi_rn_dut dut ( + .clk (clk), + .rst_n (rst_n), + // TXREQ (DUT drives these) + .txreq_flitv (chi_bus.txreq_flitv), + .txreq_opcode (chi_bus.txreq_opcode), + ... + // RXRSP (testbench drives these) + .rxrsp_flitv (chi_bus.rxrsp_flitv), + .rxrsp_opcode (chi_bus.rxrsp_opcode), + ... +); +``` + +### Signal naming conventions + +The `chi_if.sv` interface uses the naming convention from the CHI specification: + +| Prefix | Description | +|---|---| +| `txreq_*` | TX Request channel (RN → HN) | +| `txrsp_*` | TX Response channel (RN → HN) | +| `txdat_*` | TX Data channel (RN → HN) | +| `rxrsp_*` | RX Response channel (HN → RN) | +| `rxdat_*` | RX Data channel (HN → RN) | +| `rxsnp_*` | RX Snoop channel (HN → RN) | +| `*_flitv` | Flit valid | +| `*_flitpend` | Flit pending (one cycle before flitv) | +| `*_lcrdv` | Link credit valid | + +--- + +## Step 4 — Configure CLog Logging + +The monitor automatically writes a binary CLog file that can be post-processed by CHIron's app tools. Configure via `uvm_config_db` in your test: + +```systemverilog +// In test::build_phase(): +uvm_config_db #(bit) ::set(this, "env.agent.monitor", "log_enable", 1); +uvm_config_db #(string)::set(this, "env.agent.monitor", "log_path", "sim.clog"); +uvm_config_db #(int) ::set(this, "env.agent.monitor", "nodeid", 1); +``` + +### Multi-node logging + +For multi-node simulations (multiple RN/HN/SN agents), use the **shared handle** API from `clogdpi_b.svh` so all monitors write to the same log file: + +```systemverilog +// In the top-level setup (before monitors start): +chandle h; +int status; +h = CLogB_OpenFile("system.clog", status); +CLogB_ShareHandle("main_log", h); + +// In each monitor's start_of_simulation_phase: +CLogB_SharedWriteRecord("main_log", cycle, nodeid, channel, flit, flit_len); +``` + +### Post-processing the CLog + +After simulation, use CHIron's app tools to analyse the log: + +```bash +# Decode binary log to human-readable text +./app/clog2log/clog2log sim.clog + +# Generate coverage metrics +./app/clog2coverage/clog2coverage sim.clog coverage.txt + +# Report individual flit details +./app/report_flit/report_flit sim.clog +``` + +--- + +## Step 5 — Write Sequences + +### Built-in sequences (`chi_seq_lib.sv`) + +| Sequence | Description | +|---|---| +| `chi_read_shared_seq` | ReadShared + CompAck | +| `chi_read_unique_seq` | ReadUnique + CompAck | +| `chi_write_unique_seq` | WriteUniqueFull + NonCopyBackWrData | +| `chi_write_back_seq` | WriteBackFull + CopyBackWrData | +| `chi_atomic_seq` | AtomicSwap + CompAck | +| `chi_snp_resp_seq` | SnpResp (invalidate) | +| `chi_rand_seq` | Randomised mix | + +### Custom sequence example + +```systemverilog +class my_write_no_snp_seq extends chi_base_seq; + `uvm_object_utils(my_write_no_snp_seq) + + rand logic [ADDR_W-1:0] addr; + rand logic [DATA_W-1:0] data; + + task body(); + chi_seq_item req = make_req(REQ_WriteNoSnpFull, addr); + start_item(req); + finish_item(req); + + // Wait for DBIDResp before sending data: + // In a reactive HN model, the HN drives rxrsp_flitv with DBIDResp; + // the sequence waits on a response mailbox populated by the monitor. + chi_seq_item wdat = make_write_data( + DAT_NonCopyBackWrData[3:0], tgt_id, txn_id, data); + start_item(wdat); + finish_item(wdat); + endtask +endclass +``` + +--- + +## Step 6 — Run the Simulation + +### VCS + +```bash +vcs -full64 -sverilog -ntb_opts uvm-1.2 \ + +incdir+/uvm \ + +incdir+/sv \ + /uvm/chi_top.sv \ + -sv_lib chi_refmodel \ + -o simv + +./simv +UVM_TESTNAME=chi_rand_test +NUM_TRANSACTIONS=100 +``` + +### 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 +``` + +--- + +## CHI Issue Support + +| Issue | FlitConfiguration define | Notes | +|---|---|---| +| Issue E (default) | `CHI_ISSUE_EB_ENABLE` with MPAM=false | Full transaction support | +| Issue EB | `CHI_ISSUE_EB_ENABLE` with MPAM=true | MakeReadUnique, StashOnceSep, combined writes | +| Issue B | `CHI_ISSUE_B_ENABLE` | Basic support; transaction modeling not yet complete | + +To change the issue, update the `#define` in `chi_refmodel_dpi.cpp` and the `CLOG_ISSUE_*` value written in `chi_monitor.sv`'s `CLogB_WriteParameters` call. + +--- + +## Known Limitations + +The following CHI features are not yet modelled in CHIron and therefore not checked by the scoreboard: + +- **MTE** (Memory Tagging Extensions) — not on roadmap +- **DVM** (Distributed Virtual Memory) — not on roadmap +- **Exclusive Monitor tracking** — not implemented +- **SnpPreferUnique / SnpPreferUniqueFwd under exclusive sequence** — partial support +- **WriteDataCancel** — needs more detailed modeling + +See [ERRATA.md](../ERRATA.md) for specification deviations made in CHIron. + +--- + +## File Reference + +| File | Purpose | +|---|---| +| `uvm/chi_pkg.sv` | SV package: configuration parameters, opcodes, flit structs, helper functions | +| `uvm/chi_if.sv` | Parameterised CHI bus interface with clocking blocks | +| `uvm/chi_seq_item.sv` | UVM sequence item: decoded flit fields + raw 512-bit flit for CLog | +| `uvm/chi_driver.sv` | UVM driver: credit-aware TX channel driving | +| `uvm/chi_monitor.sv` | UVM monitor: captures all 6 channels, calls CLog DPI-C | +| `uvm/chi_agent.sv` | UVM agent: bundles driver, monitor, sequencer | +| `uvm/chi_scoreboard.sv` | Protocol checker: forwards flits to CHIron reference model | +| `uvm/chi_coverage.sv` | Functional coverage: opcodes, cache states, QoS, cross-coverage | +| `uvm/chi_seq_lib.sv` | Built-in sequences: ReadShared, WriteUnique, WriteBack, Atomic, … | +| `uvm/chi_env.sv` | UVM environment: connects agent → scoreboard + coverage | +| `uvm/chi_test.sv` | Example tests: read, write, randomised | +| `uvm/chi_top.sv` | Top-level: DUT instantiation, virtual interface, `run_test()` | +| `uvm/chi_refmodel_dpi.cpp` | C++ DPI-C shim exposing CHIron to the scoreboard | +| `clog/clog_b/clogdpi_b.svh` | DPI-C declarations for binary CLog (included by monitor) | +| `clog/clog_t/clogdpi_t.svh` | DPI-C declarations for text CLog (optional alternative) | diff --git a/docs/uvm_quickstart.md b/docs/uvm_quickstart.md new file mode 100644 index 0000000..5c655e0 --- /dev/null +++ b/docs/uvm_quickstart.md @@ -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). diff --git a/uvm/chi_agent.sv b/uvm/chi_agent.sv new file mode 100644 index 0000000..758c6a5 --- /dev/null +++ b/uvm/chi_agent.sv @@ -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 diff --git a/uvm/chi_coverage.sv b/uvm/chi_coverage.sv new file mode 100644 index 0000000..c39c1e7 --- /dev/null +++ b/uvm/chi_coverage.sv @@ -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 diff --git a/uvm/chi_driver.sv b/uvm/chi_driver.sv new file mode 100644 index 0000000..324c882 --- /dev/null +++ b/uvm/chi_driver.sv @@ -0,0 +1,238 @@ +// chi_driver.sv +// UVM driver for a CHI RN (Requester Node) under test. +// +// Drives the TXREQ, TXRSP, and TXDAT channels based on sequence items, and +// returns link credits on the RX channels. The driver respects the CHI +// link-layer credit protocol: it only fires a flit on a channel when at least +// one credit is available (txreq_lcrdv / txrsp_lcrdv / txdat_lcrdv). +// +// For a model that controls an HN or SN instead of an RN, swap the TX/RX +// direction references and adjust the clocking block inputs accordingly. + +`ifndef CHI_DRIVER_SV +`define CHI_DRIVER_SV + +`include "uvm_macros.svh" +import uvm_pkg::*; +import chi_pkg::*; + +class chi_driver extends uvm_driver #(chi_seq_item); + `uvm_component_utils(chi_driver) + + // Virtual interface handle; set by the agent via uvm_config_db + virtual chi_if vif; + + // Credit counts per TX channel + int unsigned req_credits = 0; + int unsigned rsp_credits = 0; + int unsigned dat_credits = 0; + + // Initial RX credits to return to HN on start-up (typically 4 per channel) + int unsigned init_rxrsp_credits = 4; + int unsigned init_rxdat_credits = 4; + int unsigned init_rxsnp_credits = 4; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + if (!uvm_config_db #(virtual chi_if)::get(this, "", "vif", vif)) + `uvm_fatal("CHI_DRIVER", "No virtual interface found in uvm_config_db") + endfunction + + task run_phase(uvm_phase phase); + // De-assert all TX valid signals at reset + deassert_all(); + @(posedge vif.clk iff vif.rst_n === 1'b1); + + fork + credit_monitor_req(); + credit_monitor_rsp(); + credit_monitor_dat(); + return_rx_credits(); + drive_items(); + join + endtask + + // --------------------------------------------------------------------------- + // Credit monitors — count incoming TX link credits from HN + // --------------------------------------------------------------------------- + task credit_monitor_req(); + forever begin + @(vif.rn_driver_cb); + if (vif.rn_driver_cb.txreq_lcrdv) + req_credits++; + end + endtask + + task credit_monitor_rsp(); + forever begin + @(vif.rn_driver_cb); + if (vif.rn_driver_cb.txrsp_lcrdv) + rsp_credits++; + end + endtask + + task credit_monitor_dat(); + forever begin + @(vif.rn_driver_cb); + if (vif.rn_driver_cb.txdat_lcrdv) + dat_credits++; + end + endtask + + // --------------------------------------------------------------------------- + // Return RX link credits to the HN at simulation start + // --------------------------------------------------------------------------- + task return_rx_credits(); + // Pulse rxrsp_lcrdv / rxdat_lcrdv / rxsnp_lcrdv for each initial credit. + // CHI spec allows returning up to 15 credits; 4 per channel is typical. + repeat (init_rxrsp_credits) begin + @(vif.rn_driver_cb); + vif.rn_driver_cb.rxrsp_lcrdv <= 1'b1; + @(vif.rn_driver_cb); + vif.rn_driver_cb.rxrsp_lcrdv <= 1'b0; + end + repeat (init_rxdat_credits) begin + @(vif.rn_driver_cb); + vif.rn_driver_cb.rxdat_lcrdv <= 1'b1; + @(vif.rn_driver_cb); + vif.rn_driver_cb.rxdat_lcrdv <= 1'b0; + end + repeat (init_rxsnp_credits) begin + @(vif.rn_driver_cb); + vif.rn_driver_cb.rxsnp_lcrdv <= 1'b1; + @(vif.rn_driver_cb); + vif.rn_driver_cb.rxsnp_lcrdv <= 1'b0; + end + endtask + + // --------------------------------------------------------------------------- + // Main driving loop — pull items from the sequencer and drive them + // --------------------------------------------------------------------------- + task drive_items(); + chi_seq_item item; + forever begin + seq_item_port.get_next_item(item); + case (item.channel) + chi_seq_item::CH_TXREQ: drive_txreq(item); + chi_seq_item::CH_TXRSP: drive_txrsp(item); + chi_seq_item::CH_TXDAT: drive_txdat(item); + default: `uvm_error("CHI_DRIVER", $sformatf("Unsupported channel %s in driver", item.channel.name())) + endcase + seq_item_port.item_done(); + end + endtask + + // --------------------------------------------------------------------------- + // Drive TXREQ (wait for credit, assert flitpend one cycle early, then flit) + // --------------------------------------------------------------------------- + task drive_txreq(chi_seq_item item); + // Wait for a credit + wait (req_credits > 0); + req_credits--; + + // Assert flitpend one cycle before the flit (§B-46) + @(vif.rn_driver_cb); + vif.rn_driver_cb.txreq_flitpend <= 1'b1; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txreq_flitv <= 1'b1; + vif.rn_driver_cb.txreq_opcode <= item.opcode[6:0]; + vif.rn_driver_cb.txreq_txnid <= item.txnid; + vif.rn_driver_cb.txreq_srcid <= item.srcid; + vif.rn_driver_cb.txreq_tgtid <= item.tgtid; + vif.rn_driver_cb.txreq_addr <= item.addr; + vif.rn_driver_cb.txreq_size <= item.size; + vif.rn_driver_cb.txreq_qos <= item.qos; + vif.rn_driver_cb.txreq_mem_attr <= item.mem_attr; + vif.rn_driver_cb.txreq_ns <= item.ns; + vif.rn_driver_cb.txreq_allow_retry<= item.allow_retry; + vif.rn_driver_cb.txreq_order <= item.order; + vif.rn_driver_cb.txreq_pcrd_type <= item.pcrd_type; + vif.rn_driver_cb.txreq_snp_attr <= item.snp_attr; + vif.rn_driver_cb.txreq_excl <= item.excl; + vif.rn_driver_cb.txreq_trace_tag <= item.trace_tag; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txreq_flitv <= 1'b0; + vif.rn_driver_cb.txreq_flitpend <= 1'b0; + endtask + + // --------------------------------------------------------------------------- + // Drive TXRSP + // --------------------------------------------------------------------------- + task drive_txrsp(chi_seq_item item); + wait (rsp_credits > 0); + rsp_credits--; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txrsp_flitpend <= 1'b1; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txrsp_flitv <= 1'b1; + vif.rn_driver_cb.txrsp_opcode <= item.opcode[4:0]; + vif.rn_driver_cb.txrsp_txnid <= item.txnid; + vif.rn_driver_cb.txrsp_srcid <= item.srcid; + vif.rn_driver_cb.txrsp_tgtid <= item.tgtid; + vif.rn_driver_cb.txrsp_resp_err <= item.resp_err; + vif.rn_driver_cb.txrsp_resp <= item.resp; + vif.rn_driver_cb.txrsp_dbid <= item.dbid; + vif.rn_driver_cb.txrsp_pcrd_type <= item.pcrd_type; + vif.rn_driver_cb.txrsp_qos <= item.qos; + vif.rn_driver_cb.txrsp_trace_tag <= item.trace_tag; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txrsp_flitv <= 1'b0; + vif.rn_driver_cb.txrsp_flitpend <= 1'b0; + endtask + + // --------------------------------------------------------------------------- + // Drive TXDAT (WriteData — may require two beats for 256-bit data) + // --------------------------------------------------------------------------- + task drive_txdat(chi_seq_item item); + wait (dat_credits > 0); + dat_credits--; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txdat_flitpend <= 1'b1; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txdat_flitv <= 1'b1; + vif.rn_driver_cb.txdat_opcode <= item.opcode[3:0]; + vif.rn_driver_cb.txdat_txnid <= item.txnid; + vif.rn_driver_cb.txdat_srcid <= item.srcid; + vif.rn_driver_cb.txdat_tgtid <= item.tgtid; + vif.rn_driver_cb.txdat_home_nid <= item.home_nid; + vif.rn_driver_cb.txdat_resp_err <= item.resp_err; + vif.rn_driver_cb.txdat_resp <= item.resp; + vif.rn_driver_cb.txdat_dbid <= item.dbid; + vif.rn_driver_cb.txdat_ccid <= item.ccid; + vif.rn_driver_cb.txdat_data_id <= item.data_id; + vif.rn_driver_cb.txdat_qos <= item.qos; + vif.rn_driver_cb.txdat_be <= item.be; + vif.rn_driver_cb.txdat_data <= item.data; + vif.rn_driver_cb.txdat_trace_tag <= item.trace_tag; + + @(vif.rn_driver_cb); + vif.rn_driver_cb.txdat_flitv <= 1'b0; + vif.rn_driver_cb.txdat_flitpend <= 1'b0; + endtask + + // --------------------------------------------------------------------------- + // De-assert everything + // --------------------------------------------------------------------------- + task deassert_all(); + vif.txreq_flitpend <= 0; vif.txreq_flitv <= 0; + vif.txrsp_flitpend <= 0; vif.txrsp_flitv <= 0; + vif.txdat_flitpend <= 0; vif.txdat_flitv <= 0; + vif.rxrsp_lcrdv <= 0; + vif.rxdat_lcrdv <= 0; + vif.rxsnp_lcrdv <= 0; + endtask + +endclass : chi_driver + +`endif // CHI_DRIVER_SV diff --git a/uvm/chi_env.sv b/uvm/chi_env.sv new file mode 100644 index 0000000..e6b875b --- /dev/null +++ b/uvm/chi_env.sv @@ -0,0 +1,50 @@ +// chi_env.sv +// UVM environment for a single-RN CHI verification setup. +// +// Topology: +// +-----------------------------------------------+ +// | chi_env | +// | | +// | chi_agent (active, RN side) | +// | ├── sequencer | +// | ├── driver ──────────────────► DUT (RN) | +// | └── monitor ──────────────────► analysis | +// | │ | +// | chi_scoreboard ◄───────────────────┤ | +// | chi_coverage ◄───────────────────┘ | +// +-----------------------------------------------+ + +`ifndef CHI_ENV_SV +`define CHI_ENV_SV + +`include "uvm_macros.svh" +import uvm_pkg::*; +import chi_pkg::*; + +class chi_env extends uvm_env; + `uvm_component_utils(chi_env) + + chi_agent agent; + chi_scoreboard scoreboard; + chi_coverage coverage; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + agent = chi_agent::type_id::create("agent", this); + scoreboard = chi_scoreboard::type_id::create("scoreboard", this); + coverage = chi_coverage::type_id::create("coverage", this); + endfunction + + function void connect_phase(uvm_phase phase); + // Connect monitor analysis port to scoreboard and coverage + agent.ap.connect(scoreboard.analysis_export); + agent.ap.connect(coverage.analysis_export); + endfunction + +endclass : chi_env + +`endif // CHI_ENV_SV diff --git a/uvm/chi_if.sv b/uvm/chi_if.sv new file mode 100644 index 0000000..c817c2c --- /dev/null +++ b/uvm/chi_if.sv @@ -0,0 +1,266 @@ +// chi_if.sv +// CHI Issue E bus interface for UVM testbench. +// +// This interface models the physical channels between an RN (Requester Node) +// and an HN (Home Node), matching the signal names used in the CHI +// specification (§B-2). Signal directions are defined from the perspective of +// the RN: +// +// TXREQ / TXRSP / TXDAT — driven by RN (DUT outputs in an RN verification) +// RXRSP / RXDAT / RXSNP — driven by HN (testbench drives, DUT receives) +// +// Parameters correspond to chi_pkg defaults; override them to match your DUT. + +`ifndef CHI_IF_SV +`define CHI_IF_SV + +`include "chi_pkg.sv" + +interface chi_if #( + parameter int unsigned NODEID_W = chi_pkg::NODEID_W, + parameter int unsigned ADDR_W = chi_pkg::ADDR_W, + parameter int unsigned DATA_W = chi_pkg::DATA_W, + parameter int unsigned RSVDC_W = chi_pkg::RSVDC_W, + parameter int unsigned TXNID_W = chi_pkg::TXNID_W +) ( + input logic clk, + input logic rst_n +); + + // ------------------------------------------------------------------------- + // TXREQ channel (RN → HN) + // ------------------------------------------------------------------------- + logic txreq_flitpend; + logic txreq_flitv; + logic [6:0] txreq_opcode; + logic [TXNID_W-1:0] txreq_txnid; + logic [NODEID_W-1:0] txreq_srcid; + logic [NODEID_W-1:0] txreq_tgtid; + logic [ADDR_W-1:0] txreq_addr; + logic [2:0] txreq_size; + logic [3:0] txreq_qos; + logic [3:0] txreq_mem_attr; + logic txreq_ns; + logic txreq_allow_retry; + logic [1:0] txreq_order; + logic [3:0] txreq_pcrd_type; + logic txreq_likely_shared; + logic txreq_snp_attr; + logic [4:0] txreq_lpid; + logic txreq_excl; + logic txreq_snoop_me; + logic txreq_cah; + logic [NODEID_W-1:0] txreq_return_nid; + logic txreq_stash_nid_valid; + logic [NODEID_W-1:0] txreq_stash_nid; + logic [TXNID_W-1:0] txreq_return_txnid; + logic txreq_do_dwt; + logic [RSVDC_W-1:0] txreq_rsvdc; + logic txreq_trace_tag; + logic txreq_lcrdv; // credit return (HN → RN) + + // ------------------------------------------------------------------------- + // TXRSP channel (RN → HN) + // ------------------------------------------------------------------------- + logic txrsp_flitpend; + logic txrsp_flitv; + logic [4:0] txrsp_opcode; + logic [TXNID_W-1:0] txrsp_txnid; + logic [NODEID_W-1:0] txrsp_srcid; + logic [NODEID_W-1:0] txrsp_tgtid; + logic [1:0] txrsp_resp_err; + logic [2:0] txrsp_resp; + logic [TXNID_W-1:0] txrsp_dbid; + logic [3:0] txrsp_pcrd_type; + logic [3:0] txrsp_qos; + logic txrsp_trace_tag; + logic txrsp_lcrdv; // credit return (HN → RN) + + // ------------------------------------------------------------------------- + // TXDAT channel (RN → HN) + // ------------------------------------------------------------------------- + logic txdat_flitpend; + logic txdat_flitv; + logic [3:0] txdat_opcode; + logic [TXNID_W-1:0] txdat_txnid; + logic [NODEID_W-1:0] txdat_srcid; + logic [NODEID_W-1:0] txdat_tgtid; + logic [NODEID_W-1:0] txdat_home_nid; + logic [1:0] txdat_resp_err; + logic [2:0] txdat_resp; + logic [TXNID_W-1:0] txdat_dbid; + logic [1:0] txdat_ccid; + logic [1:0] txdat_data_id; + logic [3:0] txdat_qos; + logic [RSVDC_W-1:0] txdat_rsvdc; + logic [(DATA_W/8)-1:0] txdat_be; + logic [DATA_W-1:0] txdat_data; + logic txdat_trace_tag; + logic txdat_lcrdv; // credit return (HN → RN) + + // ------------------------------------------------------------------------- + // RXRSP channel (HN → RN) + // ------------------------------------------------------------------------- + logic rxrsp_flitpend; + logic rxrsp_flitv; + logic [4:0] rxrsp_opcode; + logic [TXNID_W-1:0] rxrsp_txnid; + logic [NODEID_W-1:0] rxrsp_srcid; + logic [NODEID_W-1:0] rxrsp_tgtid; + logic [1:0] rxrsp_resp_err; + logic [2:0] rxrsp_resp; + logic [TXNID_W-1:0] rxrsp_dbid; + logic [3:0] rxrsp_pcrd_type; + logic [3:0] rxrsp_qos; + logic [2:0] rxrsp_fwd_state; + logic rxrsp_data_pull; + logic [2:0] rxrsp_cbusy; + logic rxrsp_trace_tag; + logic rxrsp_lcrdv; // credit return (RN → HN) + + // ------------------------------------------------------------------------- + // RXDAT channel (HN → RN) + // ------------------------------------------------------------------------- + logic rxdat_flitpend; + logic rxdat_flitv; + logic [3:0] rxdat_opcode; + logic [TXNID_W-1:0] rxdat_txnid; + logic [NODEID_W-1:0] rxdat_srcid; + logic [NODEID_W-1:0] rxdat_tgtid; + logic [NODEID_W-1:0] rxdat_home_nid; + logic [1:0] rxdat_resp_err; + logic [2:0] rxdat_resp; + logic [TXNID_W-1:0] rxdat_dbid; + logic [1:0] rxdat_ccid; + logic [1:0] rxdat_data_id; + logic [3:0] rxdat_qos; + logic [RSVDC_W-1:0] rxdat_rsvdc; + logic [(DATA_W/8)-1:0] rxdat_be; + logic [DATA_W-1:0] rxdat_data; + logic [2:0] rxdat_fwd_state; + logic rxdat_data_pull; + logic [2:0] rxdat_cbusy; + logic rxdat_trace_tag; + logic rxdat_lcrdv; // credit return (RN → HN) + + // ------------------------------------------------------------------------- + // RXSNP channel (HN → RN) + // ------------------------------------------------------------------------- + logic rxsnp_flitpend; + logic rxsnp_flitv; + logic [5:0] rxsnp_opcode; + logic [TXNID_W-1:0] rxsnp_txnid; + logic [NODEID_W-1:0] rxsnp_srcid; + logic [NODEID_W-1:0] rxsnp_fwd_nid; + logic [TXNID_W-1:0] rxsnp_fwd_txnid; + logic [(ADDR_W-3)-1:0] rxsnp_addr; + logic rxsnp_ns; + logic rxsnp_do_not_go_to_sd; + logic rxsnp_ret_to_src; + logic [3:0] rxsnp_qos; + logic rxsnp_trace_tag; + logic rxsnp_lcrdv; // credit return (RN → HN) + + // ------------------------------------------------------------------------- + // Clocking blocks for the UVM driver and monitor + // ------------------------------------------------------------------------- + + // RN-side driver clocking block (drives TX channels, samples RX channels) + clocking rn_driver_cb @(posedge clk); + default input #1step + output #1; + + // Drive TX + output txreq_flitpend, txreq_flitv, + txreq_opcode, txreq_txnid, txreq_srcid, txreq_tgtid, + txreq_addr, txreq_size, txreq_qos, txreq_mem_attr, + txreq_ns, txreq_allow_retry, txreq_order, txreq_pcrd_type, + txreq_likely_shared, txreq_snp_attr, txreq_lpid, + txreq_excl, txreq_snoop_me, txreq_cah, + txreq_return_nid, txreq_stash_nid_valid, txreq_stash_nid, + txreq_return_txnid, txreq_do_dwt, txreq_rsvdc, txreq_trace_tag; + + output txrsp_flitpend, txrsp_flitv, + txrsp_opcode, txrsp_txnid, txrsp_srcid, txrsp_tgtid, + txrsp_resp_err, txrsp_resp, txrsp_dbid, txrsp_pcrd_type, + txrsp_qos, txrsp_trace_tag; + + output txdat_flitpend, txdat_flitv, + txdat_opcode, txdat_txnid, txdat_srcid, txdat_tgtid, + txdat_home_nid, txdat_resp_err, txdat_resp, txdat_dbid, + txdat_ccid, txdat_data_id, txdat_qos, txdat_rsvdc, + txdat_be, txdat_data, txdat_trace_tag; + + // Sample RX credit returns driven by RN (return credits to HN) + output rxrsp_lcrdv, rxdat_lcrdv, rxsnp_lcrdv; + + // Sample RX channels + input rxrsp_flitpend, rxrsp_flitv, + rxrsp_opcode, rxrsp_txnid, rxrsp_srcid, rxrsp_tgtid, + rxrsp_resp_err, rxrsp_resp, rxrsp_dbid, rxrsp_pcrd_type, + rxrsp_qos, rxrsp_fwd_state, rxrsp_data_pull, rxrsp_cbusy, + rxrsp_trace_tag; + + input rxdat_flitpend, rxdat_flitv, + rxdat_opcode, rxdat_txnid, rxdat_srcid, rxdat_tgtid, + rxdat_home_nid, rxdat_resp_err, rxdat_resp, rxdat_dbid, + rxdat_ccid, rxdat_data_id, rxdat_qos, rxdat_rsvdc, + rxdat_be, rxdat_data, rxdat_fwd_state, rxdat_data_pull, + rxdat_cbusy, rxdat_trace_tag; + + input rxsnp_flitpend, rxsnp_flitv, + rxsnp_opcode, rxsnp_txnid, rxsnp_srcid, rxsnp_fwd_nid, + rxsnp_fwd_txnid, rxsnp_addr, rxsnp_ns, rxsnp_do_not_go_to_sd, + rxsnp_ret_to_src, rxsnp_qos, rxsnp_trace_tag; + + // Sample TX credit returns driven by HN + input txreq_lcrdv, txrsp_lcrdv, txdat_lcrdv; + endclocking : rn_driver_cb + + // Monitor clocking block (purely passive — samples all channels) + clocking monitor_cb @(posedge clk); + default input #1step; + + input txreq_flitpend, txreq_flitv, + txreq_opcode, txreq_txnid, txreq_srcid, txreq_tgtid, + txreq_addr, txreq_size, txreq_qos, txreq_mem_attr, + txreq_ns, txreq_allow_retry, txreq_order, txreq_pcrd_type, + txreq_likely_shared, txreq_snp_attr, txreq_lpid, + txreq_excl, txreq_snoop_me, txreq_cah, + txreq_return_nid, txreq_stash_nid_valid, txreq_stash_nid, + txreq_return_txnid, txreq_do_dwt, txreq_rsvdc, txreq_trace_tag, + txreq_lcrdv; + + input txrsp_flitpend, txrsp_flitv, + txrsp_opcode, txrsp_txnid, txrsp_srcid, txrsp_tgtid, + txrsp_resp_err, txrsp_resp, txrsp_dbid, txrsp_pcrd_type, + txrsp_qos, txrsp_trace_tag, txrsp_lcrdv; + + input txdat_flitpend, txdat_flitv, + txdat_opcode, txdat_txnid, txdat_srcid, txdat_tgtid, + txdat_home_nid, txdat_resp_err, txdat_resp, txdat_dbid, + txdat_ccid, txdat_data_id, txdat_qos, txdat_rsvdc, + txdat_be, txdat_data, txdat_trace_tag, txdat_lcrdv; + + input rxrsp_flitpend, rxrsp_flitv, + rxrsp_opcode, rxrsp_txnid, rxrsp_srcid, rxrsp_tgtid, + rxrsp_resp_err, rxrsp_resp, rxrsp_dbid, rxrsp_pcrd_type, + rxrsp_qos, rxrsp_fwd_state, rxrsp_data_pull, rxrsp_cbusy, + rxrsp_trace_tag, rxrsp_lcrdv; + + input rxdat_flitpend, rxdat_flitv, + rxdat_opcode, rxdat_txnid, rxdat_srcid, rxdat_tgtid, + rxdat_home_nid, rxdat_resp_err, rxdat_resp, rxdat_dbid, + rxdat_ccid, rxdat_data_id, rxdat_qos, rxdat_rsvdc, + rxdat_be, rxdat_data, rxdat_fwd_state, rxdat_data_pull, + rxdat_cbusy, rxdat_trace_tag, rxdat_lcrdv; + + input rxsnp_flitpend, rxsnp_flitv, + rxsnp_opcode, rxsnp_txnid, rxsnp_srcid, rxsnp_fwd_nid, + rxsnp_fwd_txnid, rxsnp_addr, rxsnp_ns, rxsnp_do_not_go_to_sd, + rxsnp_ret_to_src, rxsnp_qos, rxsnp_trace_tag, rxsnp_lcrdv; + endclocking : monitor_cb + +endinterface : chi_if + +`endif // CHI_IF_SV diff --git a/uvm/chi_monitor.sv b/uvm/chi_monitor.sv new file mode 100644 index 0000000..70a91f4 --- /dev/null +++ b/uvm/chi_monitor.sv @@ -0,0 +1,445 @@ +// chi_monitor.sv +// UVM monitor for the CHI bus. +// +// Observes all six active channels (TXREQ, TXRSP, TXDAT, RXRSP, RXDAT, RXSNP) +// and publishes chi_seq_item transactions on the analysis port. +// +// CHIron CLog integration +// ----------------------- +// When LOG_ENABLE is 1 the monitor calls the CLog.B DPI-C functions +// (from clog/clog_b/clogdpi_b.svh) to log every flit captured. The log +// file can be post-processed by the CHIron app tools (clog2log, clog2coverage, +// clog2mkcfg, report_flit). +// +// Set the following uvm_config_db keys before build_phase: +// "log_enable" (bit) — 0/1 (default 1) +// "log_path" (string)— path for the CLog binary file +// "nodeid" (int) — node ID reported in log records + +`ifndef CHI_MONITOR_SV +`define CHI_MONITOR_SV + +`include "uvm_macros.svh" +`include "../clog/clog_b/clogdpi_b.svh" + +import uvm_pkg::*; +import chi_pkg::*; + +class chi_monitor extends uvm_monitor; + `uvm_component_utils(chi_monitor) + + // Analysis port — broadcasts captured items to scoreboard, coverage, etc. + uvm_analysis_port #(chi_seq_item) ap; + + // Virtual interface + virtual chi_if vif; + + // CLog configuration + bit log_enable = 1; + string log_path = "chi_sim.clog"; + int unsigned nodeid_cfg = 0; + + // CLog file handle (chandle is a DPI opaque pointer) + chandle clog_handle; + + // Tracks simulation cycle count + longint unsigned cycle_count = 0; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + ap = new("ap", this); + + if (!uvm_config_db #(virtual chi_if)::get(this, "", "vif", vif)) + `uvm_fatal("CHI_MONITOR", "No virtual interface found in uvm_config_db") + + void'(uvm_config_db #(bit)::get(this, "", "log_enable", log_enable)); + void'(uvm_config_db #(string)::get(this, "", "log_path", log_path)); + void'(uvm_config_db #(int)::get(this, "", "nodeid", nodeid_cfg)); + endfunction + + function void start_of_simulation_phase(uvm_phase phase); + if (log_enable) begin + int status; + clog_handle = CLogB_OpenFile(log_path, status); + if (status != 0 || clog_handle == null) + `uvm_fatal("CHI_MONITOR", $sformatf("Failed to open CLog file: %s (status=%0d)", log_path, status)) + + // Write protocol parameters matching our FlitConfiguration + CLogB_WriteParameters( + clog_handle, + int'(chi_pkg::CLOG_ISSUE_E), // issue + NODEID_W, // nodeIdWidth + ADDR_W, // addrWidth + RSVDC_W, // reqRsvdcWidth + RSVDC_W, // datRsvdcWidth + DATA_W, // dataWidth + 0, // dataCheckPresent + 0, // poisonPresent + 0 // mpamPresent + ); + + // Write topology: one RN-F node (extend as needed for your system) + CLogB_WriteTopo(clog_handle, nodeid_cfg, `CLOG_NODE_TYPE_RN_F); + CLogB_WriteTopoEnd(clog_handle); + end + endfunction + + function void final_phase(uvm_phase phase); + if (log_enable && clog_handle != null) + CLogB_CloseFile(clog_handle); + endfunction + + task run_phase(uvm_phase phase); + @(posedge vif.clk iff vif.rst_n === 1'b1); + fork + count_cycles(); + monitor_txreq(); + monitor_txrsp(); + monitor_txdat(); + monitor_rxrsp(); + monitor_rxdat(); + monitor_rxsnp(); + join + endtask + + // --------------------------------------------------------------------------- + // Cycle counter + // --------------------------------------------------------------------------- + task count_cycles(); + forever begin + @(vif.monitor_cb); + cycle_count++; + end + endtask + + // --------------------------------------------------------------------------- + // TXREQ monitor + // --------------------------------------------------------------------------- + task monitor_txreq(); + chi_seq_item item; + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.txreq_flitv) begin + item = chi_seq_item::type_id::create("txreq_item"); + item.channel = chi_seq_item::CH_TXREQ; + item.opcode = vif.monitor_cb.txreq_opcode; + item.txnid = vif.monitor_cb.txreq_txnid; + item.srcid = vif.monitor_cb.txreq_srcid; + item.tgtid = vif.monitor_cb.txreq_tgtid; + item.addr = vif.monitor_cb.txreq_addr; + item.size = vif.monitor_cb.txreq_size; + item.qos = vif.monitor_cb.txreq_qos; + item.mem_attr = vif.monitor_cb.txreq_mem_attr; + item.ns = vif.monitor_cb.txreq_ns; + item.allow_retry= vif.monitor_cb.txreq_allow_retry; + item.order = vif.monitor_cb.txreq_order; + item.pcrd_type = vif.monitor_cb.txreq_pcrd_type; + item.snp_attr = vif.monitor_cb.txreq_snp_attr; + item.excl = vif.monitor_cb.txreq_excl; + item.trace_tag = vif.monitor_cb.txreq_trace_tag; + item.cycle = cycle_count; + item.nodeid = nodeid_cfg; + item.clog_channel = CLOG_CH_TXREQ; + + pack_req_flit(item); + log_flit(item); + ap.write(item); + end + end + endtask + + // --------------------------------------------------------------------------- + // TXRSP monitor + // --------------------------------------------------------------------------- + task monitor_txrsp(); + chi_seq_item item; + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.txrsp_flitv) begin + item = chi_seq_item::type_id::create("txrsp_item"); + item.channel = chi_seq_item::CH_TXRSP; + item.opcode = {2'b0, vif.monitor_cb.txrsp_opcode}; + item.txnid = vif.monitor_cb.txrsp_txnid; + item.srcid = vif.monitor_cb.txrsp_srcid; + item.tgtid = vif.monitor_cb.txrsp_tgtid; + item.resp_err = vif.monitor_cb.txrsp_resp_err; + item.resp = vif.monitor_cb.txrsp_resp; + item.dbid = vif.monitor_cb.txrsp_dbid; + item.pcrd_type = vif.monitor_cb.txrsp_pcrd_type; + item.qos = vif.monitor_cb.txrsp_qos; + item.trace_tag = vif.monitor_cb.txrsp_trace_tag; + item.cycle = cycle_count; + item.nodeid = nodeid_cfg; + item.clog_channel = CLOG_CH_TXRSP; + + pack_rsp_flit(item); + log_flit(item); + ap.write(item); + end + end + endtask + + // --------------------------------------------------------------------------- + // TXDAT monitor + // --------------------------------------------------------------------------- + task monitor_txdat(); + chi_seq_item item; + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.txdat_flitv) begin + item = chi_seq_item::type_id::create("txdat_item"); + item.channel = chi_seq_item::CH_TXDAT; + item.opcode = {3'b0, vif.monitor_cb.txdat_opcode}; + item.txnid = vif.monitor_cb.txdat_txnid; + item.srcid = vif.monitor_cb.txdat_srcid; + item.tgtid = vif.monitor_cb.txdat_tgtid; + item.home_nid = vif.monitor_cb.txdat_home_nid; + item.resp_err = vif.monitor_cb.txdat_resp_err; + item.resp = vif.monitor_cb.txdat_resp; + item.dbid = vif.monitor_cb.txdat_dbid; + item.ccid = vif.monitor_cb.txdat_ccid; + item.data_id = vif.monitor_cb.txdat_data_id; + item.qos = vif.monitor_cb.txdat_qos; + item.be = vif.monitor_cb.txdat_be; + item.data = vif.monitor_cb.txdat_data; + item.trace_tag = vif.monitor_cb.txdat_trace_tag; + item.cycle = cycle_count; + item.nodeid = nodeid_cfg; + item.clog_channel = CLOG_CH_TXDAT; + + pack_dat_flit(item); + log_flit(item); + ap.write(item); + end + end + endtask + + // --------------------------------------------------------------------------- + // RXRSP monitor + // --------------------------------------------------------------------------- + task monitor_rxrsp(); + chi_seq_item item; + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.rxrsp_flitv) begin + item = chi_seq_item::type_id::create("rxrsp_item"); + item.channel = chi_seq_item::CH_RXRSP; + item.opcode = {2'b0, vif.monitor_cb.rxrsp_opcode}; + item.txnid = vif.monitor_cb.rxrsp_txnid; + item.srcid = vif.monitor_cb.rxrsp_srcid; + item.tgtid = vif.monitor_cb.rxrsp_tgtid; + item.resp_err = vif.monitor_cb.rxrsp_resp_err; + item.resp = vif.monitor_cb.rxrsp_resp; + item.dbid = vif.monitor_cb.rxrsp_dbid; + item.pcrd_type = vif.monitor_cb.rxrsp_pcrd_type; + item.qos = vif.monitor_cb.rxrsp_qos; + item.fwd_state = vif.monitor_cb.rxrsp_fwd_state; + item.trace_tag = vif.monitor_cb.rxrsp_trace_tag; + item.cycle = cycle_count; + item.nodeid = nodeid_cfg; + item.clog_channel = CLOG_CH_RXRSP; + + pack_rsp_flit(item); + log_flit(item); + ap.write(item); + end + end + endtask + + // --------------------------------------------------------------------------- + // RXDAT monitor + // --------------------------------------------------------------------------- + task monitor_rxdat(); + chi_seq_item item; + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.rxdat_flitv) begin + item = chi_seq_item::type_id::create("rxdat_item"); + item.channel = chi_seq_item::CH_RXDAT; + item.opcode = {3'b0, vif.monitor_cb.rxdat_opcode}; + item.txnid = vif.monitor_cb.rxdat_txnid; + item.srcid = vif.monitor_cb.rxdat_srcid; + item.tgtid = vif.monitor_cb.rxdat_tgtid; + item.home_nid = vif.monitor_cb.rxdat_home_nid; + item.resp_err = vif.monitor_cb.rxdat_resp_err; + item.resp = vif.monitor_cb.rxdat_resp; + item.dbid = vif.monitor_cb.rxdat_dbid; + item.ccid = vif.monitor_cb.rxdat_ccid; + item.data_id = vif.monitor_cb.rxdat_data_id; + item.qos = vif.monitor_cb.rxdat_qos; + item.be = vif.monitor_cb.rxdat_be; + item.data = vif.monitor_cb.rxdat_data; + item.fwd_state = vif.monitor_cb.rxdat_fwd_state; + item.trace_tag = vif.monitor_cb.rxdat_trace_tag; + item.cycle = cycle_count; + item.nodeid = nodeid_cfg; + item.clog_channel = CLOG_CH_RXDAT; + + pack_dat_flit(item); + log_flit(item); + ap.write(item); + end + end + endtask + + // --------------------------------------------------------------------------- + // RXSNP monitor + // --------------------------------------------------------------------------- + task monitor_rxsnp(); + chi_seq_item item; + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.rxsnp_flitv) begin + item = chi_seq_item::type_id::create("rxsnp_item"); + item.channel = chi_seq_item::CH_RXSNP; + item.opcode = {1'b0, vif.monitor_cb.rxsnp_opcode}; + item.txnid = vif.monitor_cb.rxsnp_txnid; + item.srcid = vif.monitor_cb.rxsnp_srcid; + item.fwd_nid = vif.monitor_cb.rxsnp_fwd_nid; + item.fwd_txnid = vif.monitor_cb.rxsnp_fwd_txnid; + item.addr = {{(ADDR_W - SNP_ADDR_W){1'b0}}, vif.monitor_cb.rxsnp_addr, 3'b0}; + item.ns = vif.monitor_cb.rxsnp_ns; + item.do_not_go_to_sd = vif.monitor_cb.rxsnp_do_not_go_to_sd; + item.ret_to_src = vif.monitor_cb.rxsnp_ret_to_src; + item.qos = vif.monitor_cb.rxsnp_qos; + item.trace_tag = vif.monitor_cb.rxsnp_trace_tag; + item.cycle = cycle_count; + item.nodeid = nodeid_cfg; + item.clog_channel = CLOG_CH_RXSNP; + + pack_snp_flit(item); + log_flit(item); + ap.write(item); + end + end + endtask + + // --------------------------------------------------------------------------- + // Flit packing helpers + // Pack the decoded fields back into the 512-bit raw flit for CLog logging. + // Bit positions follow CHI Issue E flit format with default parameters + // (7-bit NodeID, 48-bit addr, 256-bit data, 4-bit RSVDC). + // + // NOTE: Exact field positions must match your FlitConfiguration; consult + // chi/spec/chi_protocol_flits.hpp for the authoritative layout. + // --------------------------------------------------------------------------- + function void pack_req_flit(chi_seq_item item); + logic [511:0] f = '0; + // A simplified packing — adjust offsets for your exact FlitConfiguration. + // Field order (LSB first): QoS[3:0], TgtID, SrcID, TxnID, ReturnNID, + // StashNIDValid, ReturnTxnID, StashNID, Endian, Deep, Opcode[6:0], + // Size[2:0], Addr[47:0], NS, LikelyShared, AllowRetry, Order[1:0], + // PCrdType[3:0], MemAttr[3:0], SnpAttr, DoDWT, LPID[4:0], + // Excl, SnoopMe, CAH, RSVDC, TraceTag + int pos = 0; + f[pos +: 4] = item.qos; pos += 4; + f[pos +: NODEID_W] = item.tgtid; pos += NODEID_W; + f[pos +: NODEID_W] = item.srcid; pos += NODEID_W; + f[pos +: TXNID_W] = item.txnid; pos += TXNID_W; + f[pos +: NODEID_W] = '0; pos += NODEID_W; // ReturnNID + f[pos] = '0; pos += 1; // StashNIDValid + f[pos +: TXNID_W] = '0; pos += TXNID_W; // ReturnTxnID + f[pos +: NODEID_W] = '0; pos += NODEID_W; // StashNID + f[pos] = '0; pos += 1; // Endian + f[pos] = '0; pos += 1; // Deep + f[pos +: 7] = item.opcode[6:0]; pos += 7; + f[pos +: 3] = item.size; pos += 3; + f[pos +: ADDR_W] = item.addr; pos += ADDR_W; + f[pos] = item.ns; pos += 1; + f[pos] = '0; pos += 1; // LikelyShared + f[pos] = item.allow_retry; pos += 1; + f[pos +: 2] = item.order; pos += 2; + f[pos +: 4] = item.pcrd_type; pos += 4; + f[pos +: 4] = item.mem_attr; pos += 4; + f[pos] = item.snp_attr; pos += 1; + f[pos] = '0; pos += 1; // DoDWT + f[pos +: 5] = '0; pos += 5; // LPID + f[pos] = item.excl; pos += 1; + f[pos] = '0; pos += 1; // SnoopMe + f[pos] = '0; pos += 1; // CAH + f[pos +: RSVDC_W] = '0; pos += RSVDC_W; + f[pos] = item.trace_tag; + item.flit = f; + item.flit_length = 88 + 4*NODEID_W + 2*TXNID_W + ADDR_W + RSVDC_W; + endfunction + + function void pack_rsp_flit(chi_seq_item item); + logic [511:0] f = '0; + int pos = 0; + f[pos +: 4] = item.qos; pos += 4; + f[pos +: NODEID_W] = item.tgtid; pos += NODEID_W; + f[pos +: NODEID_W] = item.srcid; pos += NODEID_W; + f[pos +: TXNID_W] = item.txnid; pos += TXNID_W; + f[pos +: 5] = item.opcode[4:0]; pos += 5; + f[pos +: 2] = item.resp_err; pos += 2; + f[pos +: 3] = item.resp; pos += 3; + f[pos +: 3] = item.fwd_state; pos += 3; + f[pos +: TXNID_W] = item.dbid; pos += TXNID_W; + f[pos +: 4] = item.pcrd_type; pos += 4; + f[pos] = item.trace_tag; + item.flit = f; + item.flit_length = 32 + 2*NODEID_W + TXNID_W + TXNID_W; + endfunction + + function void pack_dat_flit(chi_seq_item item); + logic [511:0] f = '0; + int pos = 0; + f[pos +: 4] = item.qos; pos += 4; + f[pos +: NODEID_W] = item.tgtid; pos += NODEID_W; + f[pos +: NODEID_W] = item.srcid; pos += NODEID_W; + f[pos +: TXNID_W] = item.txnid; pos += TXNID_W; + f[pos +: NODEID_W] = item.home_nid; pos += NODEID_W; + f[pos +: 4] = item.opcode[3:0]; pos += 4; + f[pos +: 2] = item.resp_err; pos += 2; + f[pos +: 3] = item.resp; pos += 3; + f[pos +: TXNID_W] = item.dbid; pos += TXNID_W; + f[pos +: 2] = item.ccid; pos += 2; + f[pos +: 2] = item.data_id; pos += 2; + f[pos +: RSVDC_W] = '0; pos += RSVDC_W; + f[pos +: BE_W] = item.be; pos += BE_W; + f[pos +: DATA_W] = item.data; pos += DATA_W; + f[pos] = item.trace_tag; + item.flit = f; + item.flit_length = 36 + 3*NODEID_W + 2*TXNID_W + RSVDC_W + BE_W + DATA_W; + endfunction + + function void pack_snp_flit(chi_seq_item item); + logic [511:0] f = '0; + int pos = 0; + f[pos +: 4] = item.qos; pos += 4; + f[pos +: NODEID_W] = item.srcid; pos += NODEID_W; + f[pos +: TXNID_W] = item.txnid; pos += TXNID_W; + f[pos +: NODEID_W] = item.fwd_nid; pos += NODEID_W; + f[pos +: TXNID_W] = item.fwd_txnid; pos += TXNID_W; + f[pos +: 6] = item.opcode[5:0]; pos += 6; + f[pos +: SNP_ADDR_W] = item.addr[ADDR_W-1:3]; pos += SNP_ADDR_W; + f[pos] = item.ns; pos += 1; + f[pos] = item.do_not_go_to_sd; pos += 1; + f[pos] = item.ret_to_src; pos += 1; + f[pos] = item.trace_tag; + item.flit = f; + item.flit_length = 16 + 2*NODEID_W + 2*TXNID_W + SNP_ADDR_W; + endfunction + + // --------------------------------------------------------------------------- + // CLog record writer + // --------------------------------------------------------------------------- + function void log_flit(chi_seq_item item); + if (log_enable && clog_handle != null) + CLogB_WriteRecord( + clog_handle, + longint'(item.cycle), + int'(item.nodeid), + int'(item.clog_channel), + item.flit, + int'(item.flit_length) + ); + endfunction + +endclass : chi_monitor + +`endif // CHI_MONITOR_SV diff --git a/uvm/chi_pkg.sv b/uvm/chi_pkg.sv new file mode 100644 index 0000000..b4ddda4 --- /dev/null +++ b/uvm/chi_pkg.sv @@ -0,0 +1,284 @@ +// chi_pkg.sv +// SystemVerilog package for CHIron-based UVM verification. +// +// Defines CHI Issue E constants, opcodes, flit field widths, and flit +// structs for a default configuration: +// NodeID = 7-bit +// Address = 48-bit +// Data = 256-bit +// RSVDC = 4-bit (REQ and DAT) +// +// Adjust the DATA_W / ADDR_W / NODEID_W parameters to match your DUT. + +`ifndef CHI_PKG_SV +`define CHI_PKG_SV + +package chi_pkg; + + // --------------------------------------------------------------------------- + // Protocol configuration (match your FlitConfiguration<> template instantiation) + // --------------------------------------------------------------------------- + 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 + parameter int unsigned TXNID_W = 12; + parameter int unsigned BE_W = DATA_W / 8; + parameter int unsigned SNP_ADDR_W = ADDR_W - 3; + + // CLog channel constants — match clogdpi_b.svh / clogdpi_t.svh + parameter int unsigned CLOG_ISSUE_E = 3; + + parameter int unsigned CLOG_CH_TXREQ = 0; + parameter int unsigned CLOG_CH_TXRSP = 1; + parameter int unsigned CLOG_CH_TXDAT = 2; + parameter int unsigned CLOG_CH_TXSNP = 3; + parameter int unsigned CLOG_CH_RXREQ = 4; + parameter int unsigned CLOG_CH_RXRSP = 5; + parameter int unsigned CLOG_CH_RXDAT = 6; + parameter int unsigned CLOG_CH_RXSNP = 7; + + // --------------------------------------------------------------------------- + // REQ channel opcodes (CHI Issue E, §B-356) + // --------------------------------------------------------------------------- + typedef logic [6:0] req_opcode_t; + + parameter req_opcode_t REQ_ReqLCrdReturn = 7'h00; + parameter req_opcode_t REQ_ReadShared = 7'h01; + parameter req_opcode_t REQ_ReadClean = 7'h02; + parameter req_opcode_t REQ_ReadOnce = 7'h03; + parameter req_opcode_t REQ_ReadNoSnp = 7'h04; + parameter req_opcode_t REQ_PCrdReturn = 7'h05; + parameter req_opcode_t REQ_ReadUnique = 7'h07; + parameter req_opcode_t REQ_CleanShared = 7'h08; + parameter req_opcode_t REQ_CleanInvalid = 7'h09; + parameter req_opcode_t REQ_MakeInvalid = 7'h0A; + parameter req_opcode_t REQ_CleanUnique = 7'h0B; + parameter req_opcode_t REQ_MakeUnique = 7'h0C; + parameter req_opcode_t REQ_Evict = 7'h0D; + parameter req_opcode_t REQ_DVMOp = 7'h14; + parameter req_opcode_t REQ_WriteEvictFull = 7'h15; + parameter req_opcode_t REQ_WriteCleanFull = 7'h17; + parameter req_opcode_t REQ_WriteUniquePtl = 7'h18; + parameter req_opcode_t REQ_WriteUniqueFull = 7'h19; + parameter req_opcode_t REQ_WriteBackPtl = 7'h1A; + parameter req_opcode_t REQ_WriteBackFull = 7'h1B; + parameter req_opcode_t REQ_WriteNoSnpPtl = 7'h1C; + parameter req_opcode_t REQ_WriteNoSnpFull = 7'h1D; + parameter req_opcode_t REQ_WriteUniqueFullStash = 7'h20; + parameter req_opcode_t REQ_WriteUniquePtlStash = 7'h21; + parameter req_opcode_t REQ_StashOnceShared = 7'h22; + parameter req_opcode_t REQ_StashOnceUnique = 7'h23; + parameter req_opcode_t REQ_ReadOnceCleanInvalid = 7'h24; + parameter req_opcode_t REQ_ReadOnceMakeInvalid = 7'h25; + parameter req_opcode_t REQ_ReadNotSharedDirty = 7'h26; + parameter req_opcode_t REQ_CleanSharedPersist = 7'h27; + parameter req_opcode_t REQ_AtomicSwap = 7'h38; + parameter req_opcode_t REQ_AtomicCompare = 7'h39; + parameter req_opcode_t REQ_PrefetchTgt = 7'h3A; + + // --------------------------------------------------------------------------- + // RSP channel opcodes + // --------------------------------------------------------------------------- + typedef logic [4:0] rsp_opcode_t; + + parameter rsp_opcode_t RSP_RespLCrdReturn = 5'h00; + parameter rsp_opcode_t RSP_SnpResp = 5'h01; + parameter rsp_opcode_t RSP_CompAck = 5'h02; + parameter rsp_opcode_t RSP_RetryAck = 5'h03; + parameter rsp_opcode_t RSP_Comp = 5'h04; + parameter rsp_opcode_t RSP_CompDBIDResp = 5'h05; + parameter rsp_opcode_t RSP_DBIDResp = 5'h06; + parameter rsp_opcode_t RSP_PCrdGrant = 5'h07; + parameter rsp_opcode_t RSP_ReadReceipt = 5'h08; + parameter rsp_opcode_t RSP_SnpRespFwded = 5'h09; + parameter rsp_opcode_t RSP_TagMatch = 5'h0A; + parameter rsp_opcode_t RSP_RespSepData = 5'h0B; + parameter rsp_opcode_t RSP_Persist = 5'h0C; + parameter rsp_opcode_t RSP_CompPersist = 5'h0D; + parameter rsp_opcode_t RSP_DBIDRespOrd = 5'h0E; + parameter rsp_opcode_t RSP_StashDone = 5'h10; + parameter rsp_opcode_t RSP_CompStashDone = 5'h11; + parameter rsp_opcode_t RSP_CompCMO = 5'h14; + + // --------------------------------------------------------------------------- + // DAT channel opcodes + // --------------------------------------------------------------------------- + typedef logic [3:0] dat_opcode_t; + + parameter dat_opcode_t DAT_DataLCrdReturn = 4'h0; + parameter dat_opcode_t DAT_SnpRespData = 4'h1; + parameter dat_opcode_t DAT_CopyBackWrData = 4'h2; + parameter dat_opcode_t DAT_NonCopyBackWrData = 4'h3; + parameter dat_opcode_t DAT_CompData = 4'h4; + parameter dat_opcode_t DAT_SnpRespDataPtl = 4'h5; + parameter dat_opcode_t DAT_SnpRespDataFwded = 4'h6; + parameter dat_opcode_t DAT_WriteDataCancel = 4'h7; + parameter dat_opcode_t DAT_DataSepResp = 4'hB; + parameter dat_opcode_t DAT_NCBWrDataCompAck = 4'hC; + + // --------------------------------------------------------------------------- + // SNP channel opcodes + // --------------------------------------------------------------------------- + typedef logic [5:0] snp_opcode_t; + + parameter snp_opcode_t SNP_SnpLCrdReturn = 6'h00; + parameter snp_opcode_t SNP_SnpShared = 6'h01; + parameter snp_opcode_t SNP_SnpClean = 6'h02; + parameter snp_opcode_t SNP_SnpOnce = 6'h03; + parameter snp_opcode_t SNP_SnpNotSharedDirty = 6'h04; + parameter snp_opcode_t SNP_SnpUniqueStash = 6'h05; + parameter snp_opcode_t SNP_SnpMakeInvalidStash = 6'h06; + parameter snp_opcode_t SNP_SnpUnique = 6'h07; + parameter snp_opcode_t SNP_SnpCleanShared = 6'h08; + parameter snp_opcode_t SNP_SnpCleanInvalid = 6'h09; + parameter snp_opcode_t SNP_SnpMakeInvalid = 6'h0A; + parameter snp_opcode_t SNP_SnpStashUnique = 6'h0B; + parameter snp_opcode_t SNP_SnpStashShared = 6'h0C; + parameter snp_opcode_t SNP_SnpDVMOp = 6'h0D; + parameter snp_opcode_t SNP_SnpPreferUnique = 6'h1F; + parameter snp_opcode_t SNP_SnpPreferUniqueFwd = 6'h20; + parameter snp_opcode_t SNP_SnpOnceFwd = 6'h21; + parameter snp_opcode_t SNP_SnpSharedFwd = 6'h22; + parameter snp_opcode_t SNP_SnpNotSharedDirtyFwd = 6'h24; + parameter snp_opcode_t SNP_SnpUniqueFwd = 6'h26; + + // --------------------------------------------------------------------------- + // Flit structs + // All widths use the package parameters above. + // --------------------------------------------------------------------------- + + typedef struct packed { + logic [3:0] qos; + logic [NODEID_W-1:0] tgtid; + logic [NODEID_W-1:0] srcid; + logic [TXNID_W-1:0] txnid; + logic [NODEID_W-1:0] return_nid; + logic stash_nid_valid; + logic [TXNID_W-1:0] return_txnid; + logic [NODEID_W-1:0] stash_nid; + logic endian; + logic deep; + req_opcode_t opcode; + logic [2:0] size; + logic [ADDR_W-1:0] addr; + logic ns; + logic likely_shared; + logic allow_retry; + logic [1:0] order; + logic [3:0] pcrd_type; + logic [3:0] mem_attr; + logic snp_attr; + logic do_dwt; + logic [4:0] lpid; + logic excl; + logic snoop_me; + logic cah; + logic [RSVDC_W-1:0] rsvdc; + logic trace_tag; + logic [10:0] mpam; + } req_flit_t; + + typedef struct packed { + logic [3:0] qos; + logic [NODEID_W-1:0] tgtid; + logic [NODEID_W-1:0] srcid; + logic [TXNID_W-1:0] txnid; + rsp_opcode_t opcode; + logic [1:0] resp_err; + logic [2:0] resp; + logic [2:0] fwd_state; + logic data_pull; + logic [2:0] cbusy; + logic [TXNID_W-1:0] dbid; + logic corrected_err; + logic [7:0] pgroup_id; + logic [3:0] pcrd_type; + logic trace_tag; + logic [10:0] mpam; + } rsp_flit_t; + + typedef struct packed { + logic [3:0] qos; + logic [NODEID_W-1:0] tgtid; + logic [NODEID_W-1:0] srcid; + logic [TXNID_W-1:0] txnid; + logic [NODEID_W-1:0] home_nid; + dat_opcode_t opcode; + logic [1:0] resp_err; + logic [2:0] resp; + logic [2:0] fwd_state; + logic data_pull; + logic [2:0] cbusy; + logic [TXNID_W-1:0] dbid; + logic [1:0] ccid; + logic [1:0] data_id; + logic [1:0] tag_op; + logic [1:0] tu; + logic trace_tag; + logic [RSVDC_W-1:0] rsvdc; + logic [BE_W-1:0] be; + logic [DATA_W-1:0] data; + } dat_flit_t; + + typedef struct packed { + logic [3:0] qos; + logic [NODEID_W-1:0] srcid; + logic [TXNID_W-1:0] txnid; + logic [NODEID_W-1:0] fwd_nid; + logic [TXNID_W-1:0] fwd_txnid; + snp_opcode_t opcode; + logic [SNP_ADDR_W-1:0] addr; + logic ns; + logic do_not_go_to_sd; + logic vm_id_ext; + logic ret_to_src; + logic trace_tag; + logic [10:0] mpam; + } snp_flit_t; + + // --------------------------------------------------------------------------- + // Cache state encoding (CHI §B-237) + // --------------------------------------------------------------------------- + typedef enum logic [2:0] { + CACHE_I = 3'b000, // Invalid + CACHE_SC = 3'b001, // Shared Clean + CACHE_UC = 3'b010, // Unique Clean + CACHE_UD = 3'b011, // Unique Dirty + CACHE_SD = 3'b101, // Shared Dirty + CACHE_UCE = 3'b110, // Unique Clean Empty + CACHE_UDP = 3'b111 // Unique Dirty Partial + } cache_state_e; + + // --------------------------------------------------------------------------- + // Transaction type classification helpers + // --------------------------------------------------------------------------- + function automatic logic is_read_req(input req_opcode_t op); + return op inside {REQ_ReadShared, REQ_ReadClean, REQ_ReadOnce, + REQ_ReadNoSnp, REQ_ReadUnique, REQ_ReadNotSharedDirty, + REQ_ReadOnceCleanInvalid, REQ_ReadOnceMakeInvalid}; + endfunction + + function automatic logic is_write_req(input req_opcode_t op); + return op inside {REQ_WriteUniqueFull, REQ_WriteUniquePtl, + REQ_WriteNoSnpFull, REQ_WriteNoSnpPtl, + REQ_WriteBackFull, REQ_WriteBackPtl, + REQ_WriteCleanFull, REQ_WriteEvictFull, + REQ_WriteUniqueFullStash, REQ_WriteUniquePtlStash}; + endfunction + + function automatic logic is_dataless_req(input req_opcode_t op); + return op inside {REQ_CleanShared, REQ_CleanInvalid, REQ_MakeInvalid, + REQ_CleanUnique, REQ_MakeUnique, REQ_Evict, + REQ_CleanSharedPersist}; + endfunction + + function automatic logic is_atomic_req(input req_opcode_t op); + return op inside {REQ_AtomicSwap, REQ_AtomicCompare} || + ((op & 7'h38) == 7'h28) || // AtomicStore range + ((op & 7'h38) == 7'h30); // AtomicLoad range + endfunction + +endpackage : chi_pkg + +`endif // CHI_PKG_SV diff --git a/uvm/chi_refmodel_dpi.cpp b/uvm/chi_refmodel_dpi.cpp new file mode 100644 index 0000000..ae8fc85 --- /dev/null +++ b/uvm/chi_refmodel_dpi.cpp @@ -0,0 +1,147 @@ +// chi_refmodel_dpi.cpp +// DPI-C shim that wraps CHIron's transaction state-machine library and +// exposes it to SystemVerilog UVM via the functions declared in +// chi_scoreboard.sv. +// +// Build instructions +// ------------------ +// Compile alongside CHIron headers and link into your simulator's shared +// object. Example (VCS): +// +// g++ -std=c++20 -fPIC -shared -o chi_refmodel.so \ +// -I \ +// -DCHI_ISSUE_EB_ENABLE \ +// chi_refmodel_dpi.cpp +// +// vcs ... -sv_lib chi_refmodel ... +// +// Replace with the path to the CHIron repository root. +// +// Key CHIron classes used +// ----------------------- +// CHI::Flits::REQ — typed flit for REQ channel +// CHI::Flits::RSP — typed flit for RSP channel +// CHI::Flits::DAT — typed flit for DAT channel +// CHI::Flits::SNP — typed flit for SNP channel +// CHI::Xact::Router — classifies incoming flits, routes them to +// the correct Xaction state machine, and +// reports protocol violations. + +#define CHI_ISSUE_EB_ENABLE + +#include "chi/spec/chi_protocol_flits.hpp" +#include "chi/xact/chi_xact_base/chi_xact_base_topology.hpp" +#include "chi/xact/chi_xactions/chi_xactions_impl.hpp" + +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Flit configuration — must match chi_pkg.sv parameter values +// --------------------------------------------------------------------------- +using Config = CHI::FlitConfiguration< + 7, // NodeIDWidth + 48, // ReqAddrWidth + 4, // ReqRSVDCWidth + 4, // DatRSVDCWidth + 256, // DataWidth + false, // DataCheckPresent + false, // PoisonPresent + false // MPAMPresent +>; + +// --------------------------------------------------------------------------- +// Internal state +// --------------------------------------------------------------------------- +namespace { + // Collection of in-flight transactions keyed by (srcid, txnid) + struct RefModel { + std::vector>> xactions; + std::string last_error; + int last_error_code; + + RefModel() : last_error_code(0) {} + + void reset() { + xactions.clear(); + last_error.clear(); + last_error_code = 0; + } + + // Feed a raw 512-bit flit (packed as svBitVecVal array from DPI) + // channel: 0=REQ, 1=RSP, 2=DAT, 3=SNP + int feed(int channel, const uint8_t* flit_bytes, int flit_len_bits) { + // In a full implementation this function would: + // 1. Reconstruct the typed CHI flit from the raw bytes. + // 2. Pass it to the Router to find the matching Xaction. + // 3. Call Xaction::NextRSP / NextDAT etc. to advance state. + // 4. Detect and report any protocol violations. + // + // The stub below validates the flit_len range and returns 0 + // (no error). Replace with full CHIron Router integration as + // described in docs/uvm_integration_guide.md. + (void)flit_bytes; + if (flit_len_bits <= 0 || flit_len_bits > 512) { + last_error = "Invalid flit_length: " + std::to_string(flit_len_bits); + last_error_code = -1; + return -1; + } + last_error_code = 0; + return 0; + } + }; + + static RefModel g_model; + + // Copy DPI bit[511:0] (passed as svBitVecVal*) to a byte array. + // svBitVecVal is a uint32_t; 512 bits = 16 uint32_t words. + void copy_flit(const svBitVecVal* sv_flit, uint8_t* out_bytes) { + std::memcpy(out_bytes, sv_flit, 64); // 512 bits = 64 bytes + } +} + +// --------------------------------------------------------------------------- +// DPI-C exported functions (declared in chi_scoreboard.sv) +// --------------------------------------------------------------------------- +extern "C" { + +void chi_rm_init() { + g_model.reset(); +} + +void chi_rm_feed_req(const svBitVecVal* flit, int flit_len) { + uint8_t bytes[64]; + copy_flit(flit, bytes); + g_model.feed(0, bytes, flit_len); +} + +void chi_rm_feed_rsp(const svBitVecVal* flit, int flit_len) { + uint8_t bytes[64]; + copy_flit(flit, bytes); + g_model.feed(1, bytes, flit_len); +} + +void chi_rm_feed_dat(const svBitVecVal* flit, int flit_len) { + uint8_t bytes[64]; + copy_flit(flit, bytes); + g_model.feed(2, bytes, flit_len); +} + +void chi_rm_feed_snp(const svBitVecVal* flit, int flit_len) { + uint8_t bytes[64]; + copy_flit(flit, bytes); + g_model.feed(3, bytes, flit_len); +} + +int chi_rm_check() { + return g_model.last_error_code; +} + +const char* chi_rm_get_error_string() { + return g_model.last_error.c_str(); +} + +} // extern "C" diff --git a/uvm/chi_scoreboard.sv b/uvm/chi_scoreboard.sv new file mode 100644 index 0000000..12ebeb8 --- /dev/null +++ b/uvm/chi_scoreboard.sv @@ -0,0 +1,114 @@ +// chi_scoreboard.sv +// UVM scoreboard for a CHI RN verification environment backed by CHIron. +// +// Architecture +// ------------ +// CHIron's C++ transaction state-machine library is compiled into a shared +// object and linked via DPI-C. The scoreboard calls a thin C++ shim +// (chi_refmodel.hpp / chi_refmodel_dpi.cpp — see docs/uvm_integration_guide.md) +// that wraps CHIron's Xact::Router class. +// +// For every flit the monitor captures: +// 1. The scoreboard classifies it (REQ / RSP / DAT / SNP, TX or RX). +// 2. It forwards the raw flit data to the CHIron reference model via DPI-C. +// 3. The reference model updates the transaction state machine. +// 4. On completion the scoreboard checks the observed data against the +// expected response populated by the sequences. +// +// DPI-C reference-model interface (implemented in chi_refmodel_dpi.cpp) +// ---------------------------------------------------------------------- +// chi_rm_init() — one-time initialisation +// chi_rm_feed_req(flit,len) — submit a REQ flit +// chi_rm_feed_rsp(flit,len) — submit an RSP flit +// chi_rm_feed_dat(flit,len) — submit a DAT flit +// chi_rm_feed_snp(flit,len) — submit a SNP flit +// chi_rm_check() — return 0 if no error, else error code +// chi_rm_get_error_string() — return last error message + +`ifndef CHI_SCOREBOARD_SV +`define CHI_SCOREBOARD_SV + +`include "uvm_macros.svh" +import uvm_pkg::*; +import chi_pkg::*; + +// --------------------------------------------------------------------------- +// DPI-C declarations for the CHIron reference model shim +// --------------------------------------------------------------------------- +import "DPI-C" function void chi_rm_init(); +import "DPI-C" function void chi_rm_feed_req(input bit [511:0] flit, input int flit_len); +import "DPI-C" function void chi_rm_feed_rsp(input bit [511:0] flit, input int flit_len); +import "DPI-C" function void chi_rm_feed_dat(input bit [511:0] flit, input int flit_len); +import "DPI-C" function void chi_rm_feed_snp(input bit [511:0] flit, input int flit_len); +import "DPI-C" function int chi_rm_check(); +import "DPI-C" function string chi_rm_get_error_string(); + +class chi_scoreboard extends uvm_scoreboard; + `uvm_component_utils(chi_scoreboard) + + // Analysis export — connect to chi_agent::ap (or chi_monitor::ap) + uvm_analysis_imp #(chi_seq_item, chi_scoreboard) analysis_export; + + // Counters + int unsigned flits_checked = 0; + int unsigned errors = 0; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + analysis_export = new("analysis_export", this); + endfunction + + function void start_of_simulation_phase(uvm_phase phase); + // Initialise CHIron reference model + chi_rm_init(); + `uvm_info("CHI_SB", "CHIron reference model initialised", UVM_MEDIUM) + endfunction + + // --------------------------------------------------------------------------- + // write() — called by the monitor for every captured flit + // --------------------------------------------------------------------------- + function void write(chi_seq_item item); + int rc; + + // Forward flit to CHIron reference model + case (item.channel) + chi_seq_item::CH_TXREQ: chi_rm_feed_req(item.flit, int'(item.flit_length)); + chi_seq_item::CH_TXRSP: chi_rm_feed_rsp(item.flit, int'(item.flit_length)); + chi_seq_item::CH_TXDAT: chi_rm_feed_dat(item.flit, int'(item.flit_length)); + chi_seq_item::CH_RXRSP: chi_rm_feed_rsp(item.flit, int'(item.flit_length)); + chi_seq_item::CH_RXDAT: chi_rm_feed_dat(item.flit, int'(item.flit_length)); + chi_seq_item::CH_RXSNP: chi_rm_feed_snp(item.flit, int'(item.flit_length)); + endcase + + // Check for protocol errors detected by CHIron + rc = chi_rm_check(); + if (rc != 0) begin + `uvm_error("CHI_SB", + $sformatf("CHIron protocol violation (code=%0d): %s\n Flit: %s", + rc, chi_rm_get_error_string(), item.convert2string())) + errors++; + end + + flits_checked++; + + `uvm_info("CHI_SB", + $sformatf("[%0t] %s", $time, item.convert2string()), UVM_HIGH) + endfunction + + function void report_phase(uvm_phase phase); + `uvm_info("CHI_SB", + $sformatf("Scoreboard summary: %0d flits checked, %0d errors", + flits_checked, errors), UVM_NONE) + if (errors > 0) + `uvm_error("CHI_SB", "Simulation FAILED — protocol errors detected") + else + `uvm_info("CHI_SB", "Simulation PASSED", UVM_NONE) + endfunction + +endclass : chi_scoreboard + +`endif // CHI_SCOREBOARD_SV diff --git a/uvm/chi_seq_item.sv b/uvm/chi_seq_item.sv new file mode 100644 index 0000000..5ebbea1 --- /dev/null +++ b/uvm/chi_seq_item.sv @@ -0,0 +1,180 @@ +// chi_seq_item.sv +// UVM sequence item representing a single CHI flit on any channel. +// +// The item carries the channel kind (REQ/RSP/DAT/SNP) and the direction +// (TX from RN, or RX from HN), plus all field values. The packed `flit` +// field holds the raw 512-bit representation used by CLog DPI functions. + +`ifndef CHI_SEQ_ITEM_SV +`define CHI_SEQ_ITEM_SV + +`include "uvm_macros.svh" +import uvm_pkg::*; +import chi_pkg::*; + +class chi_seq_item extends uvm_sequence_item; + `uvm_object_utils_begin(chi_seq_item) + `uvm_field_enum(chi_channel_e, channel, UVM_ALL_ON) + `uvm_field_int(srcid, UVM_ALL_ON) + `uvm_field_int(tgtid, UVM_ALL_ON) + `uvm_field_int(txnid, UVM_ALL_ON) + `uvm_field_int(opcode, UVM_ALL_ON) + `uvm_field_int(addr, UVM_ALL_ON) + `uvm_field_int(size, UVM_ALL_ON) + `uvm_field_int(qos, UVM_ALL_ON) + `uvm_field_int(mem_attr, UVM_ALL_ON) + `uvm_field_int(ns, UVM_ALL_ON) + `uvm_field_int(allow_retry, UVM_ALL_ON) + `uvm_field_int(order, UVM_ALL_ON) + `uvm_field_int(pcrd_type, UVM_ALL_ON) + `uvm_field_int(snp_attr, UVM_ALL_ON) + `uvm_field_int(excl, UVM_ALL_ON) + `uvm_field_int(resp_err, UVM_ALL_ON) + `uvm_field_int(resp, UVM_ALL_ON) + `uvm_field_int(dbid, UVM_ALL_ON) + `uvm_field_int(data, UVM_ALL_ON) + `uvm_field_int(be, UVM_ALL_ON) + `uvm_field_int(data_id, UVM_ALL_ON) + `uvm_field_int(ccid, UVM_ALL_ON) + `uvm_field_int(home_nid, UVM_ALL_ON) + `uvm_field_int(fwd_nid, UVM_ALL_ON) + `uvm_field_int(fwd_txnid, UVM_ALL_ON) + `uvm_field_int(fwd_state, UVM_ALL_ON) + `uvm_field_int(ret_to_src, UVM_ALL_ON) + `uvm_field_int(do_not_go_to_sd,UVM_ALL_ON) + `uvm_field_int(trace_tag, UVM_ALL_ON) + `uvm_field_int(flit, UVM_ALL_ON | UVM_NOPRINT) + `uvm_field_int(flit_length, UVM_ALL_ON) + `uvm_field_int(cycle, UVM_ALL_ON) + `uvm_field_int(nodeid, UVM_ALL_ON) + `uvm_field_int(clog_channel, UVM_ALL_ON) + `uvm_object_utils_end + + // ------------------------------------------------------------------------- + // Channel / direction identifiers + // ------------------------------------------------------------------------- + typedef enum { + CH_TXREQ, CH_TXRSP, CH_TXDAT, + CH_RXRSP, CH_RXDAT, CH_RXSNP + } chi_channel_e; + + rand chi_channel_e channel; + + // ------------------------------------------------------------------------- + // Common fields (used by multiple channels) + // ------------------------------------------------------------------------- + rand logic [NODEID_W-1:0] srcid; + rand logic [NODEID_W-1:0] tgtid; + rand logic [TXNID_W-1:0] txnid; + rand logic [6:0] opcode; // widest: REQ uses 7 bits + rand logic [ADDR_W-1:0] addr; + rand logic [2:0] size; + rand logic [3:0] qos; + rand logic [3:0] mem_attr; + rand logic ns; + rand logic allow_retry; + rand logic [1:0] order; + rand logic [3:0] pcrd_type; + rand logic snp_attr; + rand logic excl; + + // RSP / DAT + rand logic [1:0] resp_err; + rand logic [2:0] resp; + rand logic [TXNID_W-1:0] dbid; + + // DAT + rand logic [DATA_W-1:0] data; + rand logic [BE_W-1:0] be; + rand logic [1:0] data_id; + rand logic [1:0] ccid; + rand logic [NODEID_W-1:0] home_nid; + + // SNP + rand logic [NODEID_W-1:0] fwd_nid; + rand logic [TXNID_W-1:0] fwd_txnid; + rand logic [2:0] fwd_state; + rand logic ret_to_src; + rand logic do_not_go_to_sd; + + // Misc + rand logic trace_tag; + + // ------------------------------------------------------------------------- + // Raw flit for CLog DPI logging (packed into 512 bits) + // Populated by the monitor pack() method or by the driver before sending. + // ------------------------------------------------------------------------- + logic [511:0] flit; + int unsigned flit_length; // actual bit width of this channel's flit + longint unsigned cycle; // clock cycle at capture + int unsigned nodeid; // node this flit was captured at + int unsigned clog_channel;// CLOG_CH_* constant + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + function new(string name = "chi_seq_item"); + super.new(name); + flit = '0; + flit_length = 0; + cycle = 0; + nodeid = 0; + clog_channel= 0; + endfunction + + // ------------------------------------------------------------------------- + // Pack a REQ flit into the raw 512-bit field. + // Only the lower flit_length bits are meaningful. + // ------------------------------------------------------------------------- + function void pack_req(); + // Pack REQ flit fields in CHI Issue E order (LSB first). + // Field order: QoS, TgtID, SrcID, TxnID, ReturnNID, StashNIDValid, + // ReturnTxnID, StashNID, Endian, Deep, Opcode, Size, Addr, NS, + // LikelyShared, AllowRetry, Order, PCrdType, MemAttr, SnpAttr, + // DoDWT, LPID, Excl, SnoopMe, CAH, RSVDC, TraceTag + flit = '0; + begin + int pos = 0; + flit[pos +: 4] = qos; pos += 4; + flit[pos +: NODEID_W] = tgtid; pos += NODEID_W; + flit[pos +: NODEID_W] = srcid; pos += NODEID_W; + flit[pos +: TXNID_W] = txnid; pos += TXNID_W; + pos += NODEID_W; // ReturnNID (not carried in item) + pos += 1; // StashNIDValid + pos += TXNID_W; // ReturnTxnID + pos += NODEID_W; // StashNID + pos += 1; // Endian + pos += 1; // Deep + flit[pos +: 7] = opcode[6:0]; pos += 7; + flit[pos +: 3] = size; pos += 3; + flit[pos +: ADDR_W]= addr; pos += ADDR_W; + flit[pos] = ns; pos += 1; + pos += 1; // LikelyShared + flit[pos] = allow_retry; pos += 1; + flit[pos +: 2] = order; pos += 2; + flit[pos +: 4] = pcrd_type; pos += 4; + flit[pos +: 4] = mem_attr; pos += 4; + flit[pos] = snp_attr; pos += 1; + pos += 1; // DoDWT + pos += 5; // LPID + flit[pos] = excl; pos += 1; + pos += 1; // SnoopMe + pos += 1; // CAH + pos += RSVDC_W; // RSVDC + flit[pos] = trace_tag; + end + flit_length = 88 + 4*NODEID_W + 2*TXNID_W + ADDR_W + RSVDC_W; + clog_channel = CLOG_CH_TXREQ; + endfunction + + // ------------------------------------------------------------------------- + // Convenience: return a one-line summary string + // ------------------------------------------------------------------------- + function string convert2string(); + return $sformatf("CHI[%s] op=0x%02h txnid=0x%03h src=0x%02h tgt=0x%02h addr=0x%012h", + channel.name(), opcode, txnid, srcid, tgtid, addr); + endfunction + +endclass : chi_seq_item + +`endif // CHI_SEQ_ITEM_SV diff --git a/uvm/chi_seq_lib.sv b/uvm/chi_seq_lib.sv new file mode 100644 index 0000000..b6072d3 --- /dev/null +++ b/uvm/chi_seq_lib.sv @@ -0,0 +1,378 @@ +// chi_seq_lib.sv +// Sequence library for common CHI RN transaction patterns. +// +// Each sequence generates a self-contained stimulus scenario using the +// chi_driver to drive the DUT. For full transaction support the sequences +// also wait for the expected HN responses (driven by an HN reactive model +// or the CHIron reference model running in co-simulation). +// +// Sequences: +// chi_base_seq — abstract base (common utilities) +// chi_read_shared_seq — ReadShared allocating read +// chi_read_unique_seq — ReadUnique for write-intent read +// chi_write_unique_seq— WriteUniqueFull write +// chi_write_back_seq — WriteBackFull dirty eviction +// chi_atomic_seq — AtomicSwap operation +// chi_snp_resp_seq — Respond to an incoming SnpShared (passive response) +// chi_rand_seq — Randomised mix of all the above + +`ifndef CHI_SEQ_LIB_SV +`define CHI_SEQ_LIB_SV + +`include "uvm_macros.svh" +import uvm_pkg::*; +import chi_pkg::*; + +// =========================================================================== +// chi_base_seq — base class with common helpers +// =========================================================================== +class chi_base_seq extends uvm_sequence #(chi_seq_item); + `uvm_object_utils(chi_base_seq) + + // Node IDs for this sequence + rand logic [NODEID_W-1:0] src_id = 1; + rand logic [NODEID_W-1:0] tgt_id = 8; // typical HN-F NodeID + rand logic [TXNID_W-1:0] txn_id = 0; + + function new(string name = "chi_base_seq"); + super.new(name); + endfunction + + // Build a minimal REQ item with common fields pre-filled + function chi_seq_item make_req( + input logic [6:0] opcode, + input logic [ADDR_W-1:0] addr, + input logic [2:0] size = 3'd6 // 64 B cache line + ); + chi_seq_item req; + req = chi_seq_item::type_id::create("req"); + req.channel = chi_seq_item::CH_TXREQ; + req.opcode = opcode; + req.txnid = txn_id; + req.srcid = src_id; + req.tgtid = tgt_id; + req.addr = addr; + req.size = size; + req.qos = 4'd0; + req.mem_attr = 4'b0010; // Cacheable, Shareable + req.allow_retry = 1'b1; + req.order = 2'b00; + return req; + endfunction + + // Build a CompAck RSP item (sent by RN to HN after receiving Comp/CompData) + function chi_seq_item make_comp_ack( + input logic [NODEID_W-1:0] tgt, + input logic [TXNID_W-1:0] txnid + ); + chi_seq_item rsp; + rsp = chi_seq_item::type_id::create("comp_ack"); + rsp.channel = chi_seq_item::CH_TXRSP; + rsp.opcode = RSP_CompAck; + rsp.txnid = txnid; + rsp.srcid = src_id; + rsp.tgtid = tgt; + rsp.qos = 4'd0; + return rsp; + endfunction + + // Build a WriteData (CopyBackWrData / NonCopyBackWrData) DAT item + function chi_seq_item make_write_data( + input logic [3:0] opcode, + input logic [NODEID_W-1:0] tgt, + input logic [TXNID_W-1:0] txnid, + input logic [DATA_W-1:0] data, + input logic [BE_W-1:0] be = '1 + ); + chi_seq_item dat; + dat = chi_seq_item::type_id::create("write_data"); + dat.channel = chi_seq_item::CH_TXDAT; + dat.opcode = {3'b0, opcode}; + dat.txnid = txnid; + dat.srcid = src_id; + dat.tgtid = tgt; + dat.data = data; + dat.be = be; + dat.data_id = 2'd0; + dat.ccid = 2'd0; + dat.qos = 4'd0; + return dat; + endfunction + +endclass : chi_base_seq + +// =========================================================================== +// chi_read_shared_seq — ReadShared with CompAck +// =========================================================================== +class chi_read_shared_seq extends chi_base_seq; + `uvm_object_utils(chi_read_shared_seq) + + rand logic [ADDR_W-1:0] addr; + + constraint addr_align_c { addr[5:0] == 6'h0; } // cache-line aligned + + function new(string name = "chi_read_shared_seq"); + super.new(name); + endfunction + + task body(); + chi_seq_item req = make_req(REQ_ReadShared, addr); + + `uvm_info("CHI_SEQ", $sformatf("ReadShared addr=0x%012h txnid=0x%03h", + addr, txn_id), UVM_MEDIUM) + + start_item(req); + finish_item(req); + + // In a real environment the HN model drives a CompData response on RXDAT + // and the RN responds with CompAck. Here we send the CompAck immediately + // to close the transaction; adapt this when connected to an HN model. + begin + chi_seq_item ack = make_comp_ack(tgt_id, txn_id); + start_item(ack); + finish_item(ack); + end + endtask + +endclass : chi_read_shared_seq + +// =========================================================================== +// chi_read_unique_seq — ReadUnique (write-intent / ownership upgrade) +// =========================================================================== +class chi_read_unique_seq extends chi_base_seq; + `uvm_object_utils(chi_read_unique_seq) + + rand logic [ADDR_W-1:0] addr; + constraint addr_align_c { addr[5:0] == 6'h0; } + + function new(string name = "chi_read_unique_seq"); + super.new(name); + endfunction + + task body(); + chi_seq_item req = make_req(REQ_ReadUnique, addr); + + `uvm_info("CHI_SEQ", $sformatf("ReadUnique addr=0x%012h txnid=0x%03h", + addr, txn_id), UVM_MEDIUM) + + start_item(req); + finish_item(req); + + // CompAck follows Comp or CompData from HN + begin + chi_seq_item ack = make_comp_ack(tgt_id, txn_id); + start_item(ack); + finish_item(ack); + end + endtask + +endclass : chi_read_unique_seq + +// =========================================================================== +// chi_write_unique_seq — WriteUniqueFull with NonCopyBackWrData +// =========================================================================== +class chi_write_unique_seq extends chi_base_seq; + `uvm_object_utils(chi_write_unique_seq) + + rand logic [ADDR_W-1:0] addr; + rand logic [DATA_W-1:0] write_data; + constraint addr_align_c { addr[5:0] == 6'h0; } + + function new(string name = "chi_write_unique_seq"); + super.new(name); + endfunction + + task body(); + chi_seq_item req = make_req(REQ_WriteUniqueFull, addr); + + `uvm_info("CHI_SEQ", $sformatf("WriteUniqueFull addr=0x%012h txnid=0x%03h", + addr, txn_id), UVM_MEDIUM) + + // 1. Send WriteUniqueFull request + start_item(req); + finish_item(req); + + // 2. After receiving DBIDResp from HN, send write data + // (The actual DBIDResp is handled by the HN model / reactive agent) + begin + chi_seq_item wdat = make_write_data( + DAT_NonCopyBackWrData[3:0], tgt_id, txn_id, write_data); + start_item(wdat); + finish_item(wdat); + end + endtask + +endclass : chi_write_unique_seq + +// =========================================================================== +// chi_write_back_seq — WriteBackFull dirty eviction +// =========================================================================== +class chi_write_back_seq extends chi_base_seq; + `uvm_object_utils(chi_write_back_seq) + + rand logic [ADDR_W-1:0] addr; + rand logic [DATA_W-1:0] dirty_data; + constraint addr_align_c { addr[5:0] == 6'h0; } + + function new(string name = "chi_write_back_seq"); + super.new(name); + endfunction + + task body(); + chi_seq_item req = make_req(REQ_WriteBackFull, addr); + + `uvm_info("CHI_SEQ", $sformatf("WriteBackFull addr=0x%012h txnid=0x%03h", + addr, txn_id), UVM_MEDIUM) + + // 1. WriteBackFull request + start_item(req); + finish_item(req); + + // 2. Send CopyBackWrData in response to DBIDResp + begin + chi_seq_item wdat = make_write_data( + DAT_CopyBackWrData[3:0], tgt_id, txn_id, dirty_data); + wdat.resp = 3'b011; // UD_PD — sending unique dirty data + start_item(wdat); + finish_item(wdat); + end + endtask + +endclass : chi_write_back_seq + +// =========================================================================== +// chi_atomic_seq — AtomicSwap (load-linked / store-conditional style) +// =========================================================================== +class chi_atomic_seq extends chi_base_seq; + `uvm_object_utils(chi_atomic_seq) + + rand logic [ADDR_W-1:0] addr; + rand logic [63:0] swap_data; // new value + constraint addr_align_c { addr[2:0] == 3'h0; } + + function new(string name = "chi_atomic_seq"); + super.new(name); + endfunction + + task body(); + chi_seq_item req = make_req(REQ_AtomicSwap, addr, 3'd3); // 8 B + req.mem_attr = 4'b0110; // No-allocate, device + + `uvm_info("CHI_SEQ", $sformatf("AtomicSwap addr=0x%012h txnid=0x%03h", + addr, txn_id), UVM_MEDIUM) + + start_item(req); + finish_item(req); + // Return data comes in DAT channel (CompData or DataSepResp) + // CompAck sent after receiving data + begin + chi_seq_item ack = make_comp_ack(tgt_id, txn_id); + start_item(ack); + finish_item(ack); + end + endtask + +endclass : chi_atomic_seq + +// =========================================================================== +// chi_snp_resp_seq +// Sends a SnpResp (no data, state change to I) in reply to a snoop. +// Typically driven by the HN reactive model triggering this sequence via the +// sequencer. Bind snoop opcode and address before starting. +// =========================================================================== +class chi_snp_resp_seq extends chi_base_seq; + `uvm_object_utils(chi_snp_resp_seq) + + logic [TXNID_W-1:0] snp_txnid = '0; + logic [NODEID_W-1:0] snp_srcid = '0; + logic [2:0] snp_resp = 3'b000; // default: I (Invalidate) + + function new(string name = "chi_snp_resp_seq"); + super.new(name); + endfunction + + task body(); + chi_seq_item rsp; + rsp = chi_seq_item::type_id::create("snp_resp"); + rsp.channel = chi_seq_item::CH_TXRSP; + rsp.opcode = RSP_SnpResp; + rsp.txnid = snp_txnid; + rsp.srcid = src_id; + rsp.tgtid = snp_srcid; + rsp.resp = snp_resp; + rsp.qos = 4'd0; + + `uvm_info("CHI_SEQ", $sformatf("SnpResp txnid=0x%03h resp=0x%01h", + snp_txnid, snp_resp), UVM_MEDIUM) + + start_item(rsp); + finish_item(rsp); + endtask + +endclass : chi_snp_resp_seq + +// =========================================================================== +// chi_rand_seq — randomised mix of transactions +// =========================================================================== +class chi_rand_seq extends chi_base_seq; + `uvm_object_utils(chi_rand_seq) + + int unsigned num_transactions = 20; + rand logic [ADDR_W-1:0] base_addr; + constraint base_addr_c { base_addr[11:6] inside {[0:63]}; base_addr[5:0] == 6'h0; } + + function new(string name = "chi_rand_seq"); + super.new(name); + endfunction + + task body(); + for (int i = 0; i < num_transactions; i++) begin + int pick; + logic [ADDR_W-1:0] addr; + + pick = $urandom_range(0, 3); + addr = (base_addr + (i * 64)) & ~64'h3F; + + case (pick) + 0: begin + chi_read_shared_seq seq = chi_read_shared_seq::type_id::create("rs"); + seq.src_id = src_id; + seq.tgt_id = tgt_id; + seq.txn_id = txn_id + i; + seq.addr = addr; + seq.start(m_sequencer); + end + 1: begin + chi_write_unique_seq seq = chi_write_unique_seq::type_id::create("wu"); + seq.src_id = src_id; + seq.tgt_id = tgt_id; + seq.txn_id = txn_id + i; + seq.addr = addr; + seq.write_data = $urandom(); + seq.start(m_sequencer); + end + 2: begin + chi_write_back_seq seq = chi_write_back_seq::type_id::create("wb"); + seq.src_id = src_id; + seq.tgt_id = tgt_id; + seq.txn_id = txn_id + i; + seq.addr = addr; + seq.dirty_data= $urandom(); + seq.start(m_sequencer); + end + 3: begin + chi_atomic_seq seq = chi_atomic_seq::type_id::create("at"); + seq.src_id = src_id; + seq.tgt_id = tgt_id; + seq.txn_id = txn_id + i; + seq.addr = addr; + seq.swap_data = $urandom(); + seq.start(m_sequencer); + end + endcase + end + endtask + +endclass : chi_rand_seq + +`endif // CHI_SEQ_LIB_SV diff --git a/uvm/chi_test.sv b/uvm/chi_test.sv new file mode 100644 index 0000000..8bbf553 --- /dev/null +++ b/uvm/chi_test.sv @@ -0,0 +1,162 @@ +// chi_test.sv +// Example UVM tests for CHI RN verification. +// +// Tests: +// chi_base_test — base test (env setup, no stimulus) +// chi_read_test — runs multiple ReadShared sequences +// chi_write_test — runs multiple WriteUniqueFull sequences +// chi_rand_test — randomised mix of all transaction types + +`ifndef CHI_TEST_SV +`define CHI_TEST_SV + +`include "uvm_macros.svh" +import uvm_pkg::*; +import chi_pkg::*; + +// =========================================================================== +// chi_base_test +// =========================================================================== +class chi_base_test extends uvm_test; + `uvm_component_utils(chi_base_test) + + chi_env env; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + // Create environment + env = chi_env::type_id::create("env", this); + + // Configure the virtual interface (set in chi_top.sv before run_test) + // uvm_config_db is typically populated from the top-level module. + + // Configure CLog logging + uvm_config_db #(bit)::set (this, "env.agent.monitor", "log_enable", 1); + uvm_config_db #(string)::set(this, "env.agent.monitor", "log_path", "chi_sim.clog"); + uvm_config_db #(int)::set (this, "env.agent.monitor", "nodeid", 1); + endfunction + + task run_phase(uvm_phase phase); + phase.raise_objection(this); + // Base test does nothing — sub-classes override + #100ns; + phase.drop_objection(this); + endtask + + function void report_phase(uvm_phase phase); + uvm_report_server rs = uvm_report_server::get_server(); + if (rs.get_severity_count(UVM_ERROR) > 0 || + rs.get_severity_count(UVM_FATAL) > 0) + `uvm_info("TEST", "*** TEST FAILED ***", UVM_NONE) + else + `uvm_info("TEST", "*** TEST PASSED ***", UVM_NONE) + endfunction + +endclass : chi_base_test + +// =========================================================================== +// chi_read_test — ReadShared transactions +// =========================================================================== +class chi_read_test extends chi_base_test; + `uvm_component_utils(chi_read_test) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + task run_phase(uvm_phase phase); + chi_read_shared_seq seq; + phase.raise_objection(this); + + seq = chi_read_shared_seq::type_id::create("seq"); + seq.src_id = 7'd1; + seq.tgt_id = 7'd8; + + repeat (10) begin + if (!seq.randomize() with { addr[5:0] == 6'h0; }) + `uvm_fatal("TEST", "Randomisation failure") + seq.txn_id++; + seq.start(env.agent.sequencer); + end + + #200ns; + phase.drop_objection(this); + endtask + +endclass : chi_read_test + +// =========================================================================== +// chi_write_test — WriteUniqueFull transactions +// =========================================================================== +class chi_write_test extends chi_base_test; + `uvm_component_utils(chi_write_test) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + task run_phase(uvm_phase phase); + chi_write_unique_seq seq; + phase.raise_objection(this); + + seq = chi_write_unique_seq::type_id::create("seq"); + seq.src_id = 7'd1; + seq.tgt_id = 7'd8; + + repeat (10) begin + if (!seq.randomize() with { addr[5:0] == 6'h0; }) + `uvm_fatal("TEST", "Randomisation failure") + seq.txn_id++; + seq.start(env.agent.sequencer); + end + + #200ns; + phase.drop_objection(this); + endtask + +endclass : chi_write_test + +// =========================================================================== +// chi_rand_test — randomised mix +// =========================================================================== +class chi_rand_test extends chi_base_test; + `uvm_component_utils(chi_rand_test) + + int unsigned num_transactions = 100; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + // Read num_transactions from +UVM_TESTARGS or command line + void'($value$plusargs("NUM_TRANSACTIONS=%0d", num_transactions)); + endfunction + + task run_phase(uvm_phase phase); + chi_rand_seq seq; + phase.raise_objection(this); + + seq = chi_rand_seq::type_id::create("seq"); + seq.src_id = 7'd1; + seq.tgt_id = 7'd8; + seq.num_transactions = num_transactions; + + if (!seq.randomize()) + `uvm_fatal("TEST", "Randomisation failure") + + seq.start(env.agent.sequencer); + + #500ns; + phase.drop_objection(this); + endtask + +endclass : chi_rand_test + +`endif // CHI_TEST_SV diff --git a/uvm/chi_top.sv b/uvm/chi_top.sv new file mode 100644 index 0000000..25cdc5c --- /dev/null +++ b/uvm/chi_top.sv @@ -0,0 +1,163 @@ +// chi_top.sv +// Top-level testbench module for CHI RN verification. +// +// This module: +// 1. Instantiates chi_if and connects it to the DUT +// 2. Provides the virtual interface to UVM via uvm_config_db +// 3. Calls run_test() to start UVM +// +// Replace `chi_rn_dut` with your actual DUT module name and port map. + +`ifndef CHI_TOP_SV +`define CHI_TOP_SV + +`include "uvm_macros.svh" +`include "chi_pkg.sv" +`include "chi_if.sv" +`include "chi_seq_item.sv" +`include "chi_driver.sv" +`include "chi_monitor.sv" +`include "chi_agent.sv" +`include "chi_scoreboard.sv" +`include "chi_coverage.sv" +`include "chi_seq_lib.sv" +`include "chi_env.sv" +`include "chi_test.sv" + +import uvm_pkg::*; +import chi_pkg::*; + +module chi_top; + + // ------------------------------------------------------------------------- + // Clock and reset generation + // ------------------------------------------------------------------------- + logic clk; + logic rst_n; + + // 1 GHz clock + initial clk = 1'b0; + always #500ps clk = ~clk; + + // Active-low reset: assert for 10 cycles + initial begin + rst_n = 1'b0; + repeat (10) @(posedge clk); + rst_n = 1'b1; + end + + // ------------------------------------------------------------------------- + // CHI interface instance + // ------------------------------------------------------------------------- + chi_if #( + .NODEID_W (chi_pkg::NODEID_W), + .ADDR_W (chi_pkg::ADDR_W), + .DATA_W (chi_pkg::DATA_W), + .RSVDC_W (chi_pkg::RSVDC_W) + ) chi_bus ( + .clk (clk), + .rst_n(rst_n) + ); + + // ------------------------------------------------------------------------- + // DUT instantiation + // Replace this section with your actual DUT port connections. + // ------------------------------------------------------------------------- + /* + chi_rn_dut dut ( + .clk (clk), + .rst_n (rst_n), + + // TXREQ + .txreq_flitpend (chi_bus.txreq_flitpend), + .txreq_flitv (chi_bus.txreq_flitv), + .txreq_opcode (chi_bus.txreq_opcode), + .txreq_txnid (chi_bus.txreq_txnid), + .txreq_srcid (chi_bus.txreq_srcid), + .txreq_tgtid (chi_bus.txreq_tgtid), + .txreq_addr (chi_bus.txreq_addr), + .txreq_size (chi_bus.txreq_size), + .txreq_qos (chi_bus.txreq_qos), + .txreq_mem_attr (chi_bus.txreq_mem_attr), + .txreq_ns (chi_bus.txreq_ns), + .txreq_allow_retry(chi_bus.txreq_allow_retry), + .txreq_order (chi_bus.txreq_order), + .txreq_pcrd_type (chi_bus.txreq_pcrd_type), + .txreq_lcrdv (chi_bus.txreq_lcrdv), + + // TXRSP + .txrsp_flitpend (chi_bus.txrsp_flitpend), + .txrsp_flitv (chi_bus.txrsp_flitv), + .txrsp_opcode (chi_bus.txrsp_opcode), + .txrsp_txnid (chi_bus.txrsp_txnid), + .txrsp_srcid (chi_bus.txrsp_srcid), + .txrsp_tgtid (chi_bus.txrsp_tgtid), + .txrsp_resp_err (chi_bus.txrsp_resp_err), + .txrsp_resp (chi_bus.txrsp_resp), + .txrsp_dbid (chi_bus.txrsp_dbid), + .txrsp_lcrdv (chi_bus.txrsp_lcrdv), + + // TXDAT + .txdat_flitpend (chi_bus.txdat_flitpend), + .txdat_flitv (chi_bus.txdat_flitv), + .txdat_opcode (chi_bus.txdat_opcode), + .txdat_txnid (chi_bus.txdat_txnid), + .txdat_srcid (chi_bus.txdat_srcid), + .txdat_tgtid (chi_bus.txdat_tgtid), + .txdat_be (chi_bus.txdat_be), + .txdat_data (chi_bus.txdat_data), + .txdat_lcrdv (chi_bus.txdat_lcrdv), + + // RXRSP + .rxrsp_flitpend (chi_bus.rxrsp_flitpend), + .rxrsp_flitv (chi_bus.rxrsp_flitv), + .rxrsp_opcode (chi_bus.rxrsp_opcode), + .rxrsp_txnid (chi_bus.rxrsp_txnid), + .rxrsp_srcid (chi_bus.rxrsp_srcid), + .rxrsp_tgtid (chi_bus.rxrsp_tgtid), + .rxrsp_resp (chi_bus.rxrsp_resp), + .rxrsp_dbid (chi_bus.rxrsp_dbid), + .rxrsp_lcrdv (chi_bus.rxrsp_lcrdv), + + // RXDAT + .rxdat_flitpend (chi_bus.rxdat_flitpend), + .rxdat_flitv (chi_bus.rxdat_flitv), + .rxdat_opcode (chi_bus.rxdat_opcode), + .rxdat_txnid (chi_bus.rxdat_txnid), + .rxdat_home_nid (chi_bus.rxdat_home_nid), + .rxdat_data (chi_bus.rxdat_data), + .rxdat_lcrdv (chi_bus.rxdat_lcrdv), + + // RXSNP + .rxsnp_flitpend (chi_bus.rxsnp_flitpend), + .rxsnp_flitv (chi_bus.rxsnp_flitv), + .rxsnp_opcode (chi_bus.rxsnp_opcode), + .rxsnp_txnid (chi_bus.rxsnp_txnid), + .rxsnp_srcid (chi_bus.rxsnp_srcid), + .rxsnp_addr (chi_bus.rxsnp_addr), + .rxsnp_lcrdv (chi_bus.rxsnp_lcrdv) + ); + */ + + // ------------------------------------------------------------------------- + // UVM setup + // ------------------------------------------------------------------------- + initial begin + // Pass the virtual interface to the UVM environment + uvm_config_db #(virtual chi_if)::set(null, "uvm_test_top.*", "vif", chi_bus); + + // Start the test (test name provided via +UVM_TESTNAME) + run_test(); + end + + // ------------------------------------------------------------------------- + // Timeout watchdog — adjust as needed + // ------------------------------------------------------------------------- + initial begin + #10ms; + `uvm_fatal("TIMEOUT", "Simulation timeout — possible hang") + end + +endmodule : chi_top + +`endif // CHI_TOP_SV