Skip to content

Layers and modules

Lince is structured as a stack of CMake static libraries with strict unidirectional dependencies. This page enumerates each layer, what lives in it, and what it must not depend on.

Dependency graph

flowchart TD
    lince_app[lince_app<br/>CLI executable] --> lince_runtime
    demo_dma[demo_dma_device<br/>examples] --> lince_runtime

    lince_runtime[lince_runtime<br/>Emulator, ElfLoader, Scheduler] --> lince_defaults
    lince_runtime --> lince_peripherals

    lince_defaults[lince_defaults<br/>Loggers, CharDevices] -.-> lince_interfaces

    lince_peripherals[lince_peripherals<br/>IrqMP, GPTimer, ApbUart, MemCtrl] --> lince_bus

    lince_bus[lince_bus<br/>SystemBus, RAM] --> lince_core

    lince_core[lince_core<br/>SPARC V8 ISA, CpuState, Decoder] -.-> lince_interfaces

    lince_interfaces[lince_interfaces<br/>Header-only API]

Solid arrows are link-time dependencies; dotted arrows are header-only contracts (lince_interfaces does not produce a .a file).

The graph is acyclic by constructionlince_core does not know that peripherals exist, lince_bus does not know that there is a CPU, and lince_interfaces knows nothing about anybody.

What goes where

Module CMake target Owns Depends on
Interfaces lince_interfaces (INTERFACE) Strong types, Result<T>, all I* contracts
Core lince_core CpuState, decoder, ISA handlers, trap dispatch interfaces
Bus lince_bus Ram, SystemBus interfaces
Peripherals lince_peripherals IrqMP, GPTimer, ApbUart, MemCtrl interfaces, bus
Runtime lince_runtime Emulator, EmulatorConfig, EventScheduler, ElfLoader, CpuBusBridge, GdbStub interfaces, core, bus, peripherals, defaults
Defaults lince_defaults StdoutLogger, StdoutCharDevice, NullFaultInjector, DebugPublisher interfaces
App lince_app (executable) CLI, argument parsing, post-mortem dump runtime
Demo demo_dma_device (example) A custom peripheral with DMA + IRQ interfaces, runtime (only for the test binary)

Why the layers exist

  • lince_interfaces exists so any module can take a dependency on a contract without pulling in an implementation. It is header-only, contains no logic, and never breaks ABI.
  • lince_core is independent of bus and peripherals so the CPU can be unit-tested with hand-crafted instruction streams against any fake bus.
  • lince_bus does not know about CPUs — it is a pure address router. This lets us reuse it as the DMA target for peripherals (IBusMaster) without circular dependencies.
  • lince_runtime is the only orchestrator. It composes everything; no other module is allowed to instantiate Emulator, EventScheduler, or ElfLoader.
  • lince_defaults is intentionally tiny. Anything an SMP2 wrapper would replace (logger, char device, fault injector) lives here, so a downstream consumer can drop the entire library and substitute their own implementations.

Module-by-module narrative

For deeper module documentation see the dedicated pages under Modules: