linx-model is a SimQueue-based simulation framework. Modules do not pass
mutable state to each other directly. Instead, they communicate through named
queue ports owned and wired by parent modules.
The execution contract is:
SimSystem::Step()callsWork()on every registered module.SimSystem::Step()then callsXfer()on every registered module.SimQueue::Work()decrements pending delays and promotes ready writes to the readable side.
This enforces a cycle boundary between producer and consumer visibility.
Within each Module, WorkSelf() is event-driven:
- the module tracks the visible-side epoch of every
inputandinnerqueue WorkSelf()only runs when at least one observed input queue changes- inactive modules still advance owned queue timing so delayed packets can wake downstream modules in a later cycle
XferSelf()only runs for modules whoseWorkSelf()executed in the same cycle
SimQueue<T> supports:
- value payloads such as
bool,int, enums, and small structs std::unique_ptr<T>for ownership transfer without object copiesstd::shared_ptr<T>for shared-object scenarios
latency == N means a write becomes readable after exactly N calls to
Work(). latency == 0 is still synchronized to the next Work() boundary.
isa::Minst is the canonical packet type for Linx ISA simulation in this
repository.
- fetch allocates
MinstPtr - decode fills generated v0.4 form metadata and canonical decoded fields
- middle pipeline stages inspect or extend typed views on the same packet
- retire, flush, or DFX consumes and destroys the packet, or converts it to shared ownership only at terminal boundaries
The decode field map is the encode source of truth. Typed operand, immediate, memory, and control views are derived caches for simulator logic and logging.
SimSystem can now own a loaded ProgramImage.
- ELF inputs are parsed from
PT_LOADsegments - raw binaries are loaded as a single executable
.textregion - the ISA disassembler walks executable regions, decodes
Minst, and prints deterministic assembly from the same generated codec used by simulation
The default CLI helpers accept:
--bin <path>to load an ELF or raw binary--raw-base <addr>to set the base PC for raw binaries--disasmto print assembly before simulation--disasm-onlyto print assembly and exit
Every queue-wired module derives from Module<Derived, PortT>.
DescribeInput(name, description)declares an input signal.DescribeOutput(name, description)declares an output signal.DescribeInner(name, description)declares an internal registered link.Bind*()attaches a real queue instance to the named port.CreateOwnedQueue()allocates parent-owned queues with stable addresses.
Leaf or outward-facing modules must declare at least one input and one output
unless they explicitly call SetRequireIOContract(false).
Each declared port carries PortInfo:
indexdirectionnamedescription
This metadata serves three purposes:
- documents signal intent in headers and generated API docs
- supports validation checks for missing names and descriptions
- makes pipe-view and debug output stable across modules
Inside Work(), modules are expected to:
- read current visible values from
inputandinnerqueues - perform combinational logic only
- optionally push results into
outputqueues
Xfer() is reserved for explicit registered-state transfer local to the module.
Cross-module timing should stay queue-driven.