Simultaneous Multithreading (SMT)
GSys LibreCore supports two hardware threads (NrHarts=2) sharing pipeline
resources with per-hart architectural state. Default packages keep
NrHarts=1 for identity netlists.
Scaffold + bring-up: architecture/multi-threading/
(README.md, smt2-bringup.md, dts-linux-smt.md, smt-linux-rootfs.md).
Software view
| Item | Behavior |
|---|---|
mhartid for thread h | hart_id_base + h |
| ISA string | Same on both threads |
| SBI / OpenSBI | Two harts in the domain |
| Linux | Two cpu@ nodes; enable after OpenSBI knows both |
What the RTL guarantees
- Per-hart arch state — RF banks, CSR banks, PC bank, RAS banks, GHR banks
- Fine-grain, drain-friendly switch — IF flush + drop unissued; EX/scoreboard drain; BP tables preserved where not banked
- Commit isolation — CSR writes keyed by instruction
hart_id - Fetch view — privilege / MMU CSR mux by active fetch hart
On OoO profiles (OoOEn && NrHarts=2), residency switches by drained
handoff: the thread selector changes only when the scoreboard and store
queues are empty, so the hart-blind OoO backend never interleaves two harts’
work. Mixed residency (SmtDrainedHandoff=0) is promoted to production on the
single-core g6lc64_smt2_ooo_int package (T9d/M2, evidence re-baselined on the
eWT tree); on multi-core packages it stays qualification-gated behind
G6LC_OOO_SMT_MIXED_QUALIFY. FP on the OoO path is legal single-hart and under
the drained handoff (M4 — g6lc64_ooo_int2_l3 is RV64GC+B); FP under mixed
residency stays refused pending the hart-tagged lazy-FS audit.
Window geometry. Both OoO SMT2 packages run a 16-entry scoreboard ring
(NrScoreboardEntries=16, adopted T10d: the int2_l3 four-hart boot 18,297,381
cycles vs 18,419,779 at ring 8, the mixed smt2_ooo_int boot 10,459,588 vs
10,556,456, branch-bound IPC unchanged, mixed-commit FO4 31.0). The full T9f/M3
uplift bundle (ring 32, TAGE_LITE, BPCkptDepth 32, DeepSpec+MemDepPred, FTQ 8 /
FDIP / loop buffer) was measured and not adopted: every knob regressed the
strict boot on its own (TAGE_LITE at 3×64 entries multiplies boot mispredicts
~4.75×, DeepSpec/memdep replays dominate, FTQ/FDIP taxes every control
transfer), and mispredict recovery is mark-and-drain through the commit head, so
a deeper window pays more per mispredict — the M3b fast-squash candidate is
sketched in plan T9f. Ring 32 additionally exposed a drained-handoff hole: a
drain can pend for millions of cycles while the resident hart holds scoreboard
entries. The bounded-drain machinery (SmtDrainForceCycles, N1c: no-commit
counter, WFI-immediate force, hart-scoped flush + PC rewind, PMU events, leaf and
bounded proof) is landed and inert on every qualified package (force count 0,
anchors byte-identical), but on the ring-32 boot the force never armed: the
stalled head was uncommittable, so the head-class/killable gates masked it. The
bound is therefore conditional, not construction-grade; the head-level root cause
is the open item (plan T10f) and ring 32 stays off.
Landed from the M3 work is the switch-safety fix: smt_restore_i reseeds the
FTQ and steps the cursor (arch_step=FtqEn) and clears the loop buffer on
every switch (formal g6lc_fetch_restore + the tb_g6lc_fetch_restore leaf;
the NOFLUSH mutant is detected).
Qualification anchors (strict OpenSBI, single-threaded Verilator, byte-exact
per-hart traces): g6lc64_smt2 12,406,273 cycles, g6lc64_smt2_ooo_int
(mixed, ring 16) 10,459,588, g6lc64_smt2_l3 13,691,686. A run killed by the
wall clock now reports TERMINATED (exit 124) instead of a truncated SUCCESS.
Modules under core/smt/ include thread select, hart state, banked RF, PC
bank, and CSR bank helpers.
Switch policies
| Policy | Intent |
|---|---|
| Round-robin quantum | Bound IF unfairness (SmtFetchQuantum) |
| Switch-on-miss | Peer switch on D$/I$ miss when peer ready |
| Hybrid (typical SMT2 package) | Miss switch + anti-starvation (SmtStarveLimit) |
Enable
core/include/g6lc64_smt2_config_pkg.sv # NrHarts=2, SMT_HYBRID, in-order (anchor)
core/include/g6lc64_smt2_l3_config_pkg.sv # + non-inclusive L3
core/include/g6lc64_smt2_ooo_int_config_pkg.sv # NrHarts=2 over OoO, mixed residency, ring 16 (production)
core/include/g6lc64_ooo_int2_config_pkg.sv # two-core WT + L2 COH_OOO (integer anchor, drained)
core/include/g6lc64_ooo_int2_l3_config_pkg.sv # + L3, RV64GC+B, ring 16 (drained)g6lc-build verify --target g6lc64_smt2
g6lc-build verify --target g6lc64_smt2_ooo_int
g6lc-build test --suite smt-linux-boot-path # if cataloguedBring-up sequence (product)
- Keep
NrHarts=1green on default packages. - Select SMT2 package; bare-metal boot hart 0 only.
- Park hart 1 in WFI; exercise switch-on-miss.
- Dual-thread litmus /
rdcyclebefore Linux. - OpenSBI
expected_harts/ domain for two harts. - Linux
maxcpus=2smoke with validated DTS.
DTS of record: corev_apu/bootrom/ariane-smt2.dts
Validate: build-platform/scripts/validate-cva6-dts.* (after fetch-linux-dts).
Software track: software/smt2-linux/.
Known limits
- Shared BHT/BTB (RAS/GHR banked) → mild cross-hart pollution
- Dual-commit of two different harts in one cycle is not a specialized product path (single CSR port discipline)
- Full KVM-on-SMT is out of scope for SMT bring-up — use H/server packages separately after bare-metal SMT is green
Verification suites (optional / residual)
| Suite id (typical) | Focus |
|---|---|
smt-linux-boot-path | DTS + IRQ path / boot path |
smt-linux-rootfs | Rootfs preflight + dual-park; full image via CVA6_LINUX_PAYLOAD |
Diagnostics compartment smt2 exists under g6lc-build diag for focused
Verilator surfaces.
Related
- L2 / L3 Cache — MLP for dual-hart traffic
- Server Profiles
- Upgrade Program
- Spec-adjacent: precise traps + memory model across harts (
AGENTS-soc-readiness)