Skip to content

8. The model is crate-private

Context. rvsim-core is published, and every one of its modules was pub: the event kernel, the caches, the DDR5 controller, the pipeline stages, the branch predictors. Two things kept them that way. The Rust test suite lived in an external test crate and could only reach pub items, and the Python bindings built the system by hand from SystemState and the uncore's structures. The result was a semver surface of several thousand items, most of them timing-model internals that change with every accuracy fix, and dead_code could not see anything the model had stopped using, because a pub item is never dead.

Decision. The crate's interface is the system. common, isa, config, arch and system are pub; exec, sim, soc and uarch are pub(crate). Simulator offers what a host needs: loading a program or kernel, running, reaching a hart's registers, CSRs and translations, the statistics tree (re-exported as rvsim_core::stats), the trace and console channels, and a plain-data system::snapshot::PipelineSnapshot of the latches. The test suite lives inside the crate under src/tests, and an accessor that only a test reads is #[cfg(test)].

Consequences. The published surface is what the bindings use and nothing else, so a timing-model change is not a breaking change. Making the model private let the compiler find what the model no longer used: packet variants never sent (Fence, Prefetch, RefreshTick), a MOESI state the MESI protocol never entered, reorder-buffer fields written and never read, a legacy tag-wakeup path in the issue queue, and some sixty accessors and helpers; they are gone. Thirty-one accessors that tests use as probes are #[cfg(test)], which states their only consumer.

The compile-fail doctests that decision 3 cites went with StageCtx's public path; a doctest cannot name a private type. The guarantee is unchanged and rests on the type: StageCtx::hart returns a shared reference, and the view has no CSR or memory write.