This repository hosts a UVM-based verification environment for a PCIe Gen3 Endpoint focusing on Transaction Layer correctness. The testbench drives Memory Read/Write and Config Read/Write TLPs, checks completions, collects functional coverage, and uses a self-checking scoreboard that compares DUT TLP streams against a C++ reference model via DPI-C.
In addition to transaction checks, we include bring-up level modeling/checks for Link Training (LTSSM) sequencing and flow-control credits (counter/monitor based), sufficient to validate basic endpoint behavior under randomized traffic. Deeper PHY/elastic-buffer/EIEOS/TS ordering details are planned as extensions.
-
UVM layered TB (env/agent/driver/monitor/scoreboard) + SystemVerilog interface & SVA for valid/ready timing and address stability.
-
DPI-C C++ transaction-layer reference model (“memory/config mirror”), consumed by the scoreboard:
- MRd → CplD with strict tag/len/addr/payload matching.
- MWr updates model memory; verified via write-then-read.
- CfgRd/CfgWr minimal stubs (returns zero for CfgRd; ready to extend to full capability/BAR).
-
LTSSM bring-up: sequenced state progression (Detect→Polling→Configuration→L0) in stimulus; simple checks for entry to L0 and recovery hooks.
-
Flow-control credits: stimulus/monitor maintain posted/non-posted/completion credit counters; assertions for “no send w/o credit”; coverpoints track credit consumption/return under randomized traffic.
-
Functional coverage: request-type/length/address crosses; completion coverage (growing); tag-spray sequences for out-of-order stress.
-
Self-checking: automated pass/fail; waveform optional.
- Full Config Space model (capabilities, BAR sizing/probing) and MSI/MSI-X as posted writes.
- ECRC & malformed-TLP injection and checks.
- Deeper link/PHY modeling (elastic buffer, ordered sets, lane reversal, polarity inversion) if needed.
rtl/ # DUT (bring-up or real IP)
uvm/
pcie_pkg.sv # enums, seq_item, sequences, coverage, agent/env + scoreboard (MVP inline)
pcie_if.sv # interface + SVA (valid/ready, addr-stable, etc.)
# pcie_scoreboard.sv # (optional) split from pkg in later refactor
# pcie_env.sv # (optional) split from pkg in later refactor
dpi/
pcie_ref_dpi_pkg.sv # DPI import package (SV side)
pcie_ref_model.cpp # C++ transaction-layer reference model
tb/
tb_top.sv # clock/reset + run_test
filelist.f # compile order (SV)
docs/
figs/run_log.png
figs/wave_mrd_cpld.png
scripts/
run_questa.sh
run_vcs.sh
+---------------------+ +-------------------+
| UVM Sequences | | C++ Reference |
| (random+directed) | | Model (DPI-C) |
+----------+----------+ +---------+---------+
| ^
v |
+-------+--------+ |
| Driver | |
+-------+--------+ |
| TLP_in |
v |
+---------------+---------------+ |
| DUT (Endpoint) | |
+---------------+---------------+ |
^ TLP_out |
+-------+--------+ +-------+--------+
| Monitor_out |---- tr_cpl -->| Scoreboard |
+-------+--------+ +-------+--------+
^ TLP_in ^
+-------+--------+ |
| Monitor_in |---- tr_req ---------+
+----------------+
- Monitor_in: forwards inbound requests to the scoreboard → scoreboard calls
pcie_model_step_req(...)
to advance the C++ model (update memory/config mirror, enqueue expected outputs). - Monitor_out: forwards DUT completions/posted writes to the scoreboard → scoreboard pops via
pcie_model_pop_expected(...)
and compares tag/len/payload/addr. - LTSSM & Credits: driver/stimulus include state-sequencing hooks; monitors track credit counters and assert no send without credit; coverpoints record credit consumption/return patterns.
SV package: dpi/pcie_ref_dpi_pkg.sv
typedef chandle pcie_model_t; // C++ void*
// lifecycle
import "DPI-C" context function pcie_model_t pcie_model_init();
import "DPI-C" context function void pcie_model_free(pcie_model_t);
import "DPI-C" context function void pcie_model_reset(pcie_model_t);
// feed inbound requests (MRd/MWr/Cfg*)
import "DPI-C" context function void pcie_model_step_req(
pcie_model_t ctx, int tlp_type, longint unsigned addr, int len_dw, int tag,
byte unsigned payload[], int payload_nbytes, int byte_en_first, int byte_en_last
);
// pop one expected output (1=CplD, 2=MWr/MSI; 0=Cpl)
import "DPI-C" context function int pcie_model_pop_expected(
pcie_model_t ctx, output int kind, output longint unsigned addr,
output int len_dw, output int tag,
output byte unsigned payload[], input int payload_capacity, output int payload_nbytes
);
// optional: peek model memory for debug/side checks
import "DPI-C" context function int pcie_model_peek_mem(
pcie_model_t ctx, longint unsigned addr, output byte unsigned data_out[], int nbytes
);
C++ side: dpi/pcie_ref_model.cpp
maintains a 64KB memory mirror and an expected-TLP queue.
- MRd → enqueue CplD with payload from mirror.
- MWr → write mirror (no immediate expected).
- CfgRd/CfgWr → stubs (extendable to true capability/BAR model).
- Endianness: little-endian payload packing;
len_dw
in DW,addr
in bytes.
In pcie_scoreboard.sv
:
build_phase
:m_ctx = pcie_model_init(); m_tmp_buf = new[4096];
final_phase
:pcie_model_free(m_ctx);
write_req(tr)
: convertspcie_seq_item
to DPI args; callspcie_model_step_req(...)
.write_cpl(c)
:pcie_model_pop_expected(...)
→ strict compare:tag/len/payload
(+addr
if present).- Legacy
tag2addr
map kept for debug prints; assertions guard protocol mistakes.
- Valid-hold until Ready (both request & completion channels).
- Address stable while valid && !ready.
- Latency/existence of CplD after MRd (optional bounded property).
- Compile-time guards for fault injection (drop valid, corrupt data) to demonstrate assertion firing & coverage.
pcie_smoke_seq
: minimal MWr → MRd → CfgRd sanity.pcie_cov_seq
: length buckets (small/medium/large), address buckets (BAR0 window), write-then-read side-effect check.pcie_cpl_tag_spray_seq
: stresses tags (out-of-order completions matched by tag).- LTSSM bring-up seq: scripted Detect→Polling→Configuration→L0 (for endpoint bring-up).
- Credit stress seq: randomized non-posted/posted/CPL mixes to exercise credit counters.
- REQ_CG: type × length × address cross (per-instance).
- CPL_CG: completion presence/type × length; tag distribution; out-of-order tolerated per tag rules.
- Credit_CG: bins for credit consumption, depletion, and return under randomized mixes. Typical interim prints observed during bring-up (example):
uvm_test_top.env.cov [COV] REQ_CG=~94% | CPL_CG=growing | CREDIT_CG=exercised (no-send-without-credit holds)
filelist.f
(order matters):
+incdir+.
+incdir+rtl +incdir+uvm +incdir+dpi +incdir+tb
dpi/pcie_ref_dpi_pkg.sv
uvm/pcie_pkg.sv
uvm/pcie_if.sv
# uvm/pcie_scoreboard.sv # (if split later)
rtl/dut.sv
# uvm/pcie_env.sv # (if split later)
tb/tb_top.sv
Commands:
# compile SV
vlog -sv -f filelist.f
# build DPI library (Linux)
g++ -fPIC -shared -O2 -I$QUESTA_HOME/include -o pcie_ref.so dpi/pcie_ref_model.cpp
# simulate (must load the library)
vsim -c -sv_lib pcie_ref work.tb_top -do "run -all; quit"
EDA Playground:
- Put
pcie_ref_model.cpp
under C/C++ Sources; set Library name =pcie_ref
. - Put
pcie_ref_dpi_pkg.sv
in Design files before scoreboard. - Run Options:
-sv_lib pcie_ref -voptargs=+acc=npr
- Transcript should show:
Loading svlib 'pcie_ref'
.
vlogan -full64 -sverilog +incdir+. -f filelist.f
g++ -fPIC -shared -O2 -I$VCS_HOME/include -o pcie_ref.so dpi/pcie_ref_model.cpp
vcs -full64 -R -sv_lib pcie_ref work.tb_top
Transcript snippets to keep in the repo:
# Loading svlib 'pcie_ref'
UVM_INFO @...: uvm_test_top.env.sb [SB] CplD matched OK tag=5 addr=0x00000100 data=0xA5A50001
UVM_INFO @...: uvm_test_top.env.credit [CREDIT] np_credit=... p_credit=... cpl_credit=...
UVM_INFO [UVM/REPORT/SUMMARY] --- Report summary --- Errors: 0 ...
Negative test example (fault injection):
UVM_ERROR @...: uvm_test_top.ifc.sva [SVA] addr_must_stable violated under valid && !ready
UVM_ERROR @...: uvm_test_top.env.sb [SB] CplD data mismatch @idx=0 exp=DE AD BE EF got=BA AD F0 0D tag=7 addr=0x10
Waveforms: add IF/DUT signals as usual. Optionally wave scoreboard counters: sb_matches
, sb_mismatches
, exp_qdepth
, np_credit
, p_credit
, cpl_credit
.
- Transaction layer is the primary scope.
- LTSSM/Credit: implemented at bring-up/verification level (state sequencing + credit counters + assertions). Deep PHY modeling (ordered sets, lane mapping, SKP/elastic buffer) is deferred unless required by the DUT.
- Tag width aligned to sequence config (6–8 bits typical).
- Little-endian payload packing; current BE handling is relaxed (0xF/0xF), can be tightened.
- Full Config Space & BAR decode in the model; MSI/MSI-X as posted writes with address/data checks.
- ECRC/malformed-TLP injections with protocol assertions & coverage.
- Enhanced credit checks (VCs, per-class throttling), scoreboard correlation with DUT credit interfaces if exposed.
- Regression scripts + coverage dashboards (HTML) & CI.
- Transcript lines showing
Loading svlib 'pcie_ref'
and at least one “CplD matched OK …”. - One EPWave screenshot: MRd @0x100 → matching CplD (highlight
tag/addr/len/payload
). - File tree + the exact run commands (or a Makefile/script).
- (Optional) A block diagram (PNG) of the monitor→DPI→scoreboard flow.
MIT (or your choice)
chandle
(SV) ↔void*
(C++): pass an opaque pointer to the model object across the DPI boundary.context
: allows the imported function to call back SV DPI services and access SV context safely across sim phases.- Arrays: SV
byte[]
maps to C++uint8_t*
with open-array handles; prefer explicit nbytes.
Failed to open -sv_lib pcie_ref
→ library name mismatch or library not built in C/C++ panel.Package not defined
→ compile order wrong; buildpcie_ref_dpi_pkg.sv
before scoreboard.- Tons of mismatches with
got=0
frompop_expected
→step_req
not being called (connect Monitor_in) or timing/OOO window too tight.
Figure 1 — Transcript excerpt (self-checking scoreboard active)
Shows multiple CPL matched … lines, MSI hit, coverage summary, and Errors: 0.
Figure 2 — EPWave snapshot: MRd → CplD match (tag-based OOO allowed)
Highlighted signals: req_valid/ready, req_type/len/tag/addr/data and cpl_valid/ready/status/tag/data.
The MRd to addr=0x10 with tag=0x3d is followed by a CplD carrying the expected payload; tags align.