lince_runtime¶
The orchestration layer. Owns the Emulator class, the event scheduler,
the ELF loader, the CPU-bus bridge and the GDB stub. This is the only
module that knows about all the others.
Source layout¶
src/runtime/
├── include/lince/runtime/
│ ├── emulator.hpp
│ ├── emulator_config.hpp
│ ├── event_scheduler.hpp
│ ├── run_result.hpp
│ ├── elf_loader.hpp
│ ├── cpu_bus_bridge.hpp
│ ├── gdb_stub.hpp
│ └── version.hpp
└── src/
├── emulator.cpp
├── event_scheduler.cpp
├── elf_loader.cpp
├── cpu_bus_bridge.cpp
└── gdb_stub.cpp
Depends on every other module.
Emulator¶
The public entry point. Owns:
EmulatorConfig(the live copy).- A vector of
core::CpuState. - A
bus::SystemBus. - A
CpuBusBridgeadapter (CPU'sICpuBusview of the bus). - An
EventScheduler. - A vector of
unique_ptr<IPeripheral>and matchingIrqBridges. - Pointers to the four built-in peripherals (
IrqMP,GPTimer,ApbUart,MemCtrl). - A
unique_ptr<ILogger>,unique_ptr<IFaultInjector>,unique_ptr<ICharacterDevice>, and optionalunique_ptr<GdbStub>.
API summary (full signatures in emulator.hpp):
static Result<unique_ptr<Emulator>> create(EmulatorConfig);
~Emulator();
void set_logger(unique_ptr<ILogger>);
void set_character_device(unique_ptr<ICharacterDevice>);
Result<void> initialize();
Result<void> reset();
RunResult run_for (SimTimeNs duration);
RunResult run_until(SimTimeNs deadline);
SimTimeNs current_sim_time() const noexcept;
Result<void> load_elf (const filesystem::path&);
Result<void> load_binary(PhysAddr, span<const byte>, PhysAddr);
Result<void> read_physical (PhysAddr, span<byte>);
Result<void> write_physical(PhysAddr, span<const byte>);
Result<uint32_t> read_physical_u32 (PhysAddr);
Result<void> write_physical_u32(PhysAddr, uint32_t);
Result<void> add_peripheral(unique_ptr<IPeripheral>, IrqLine);
void schedule_event(SimTimeNs, IEvent*);
const core::CpuState& core(size_t) const;
core::CpuState& core(size_t);
size_t num_cores() const noexcept;
GdbStub* gdb_stub() noexcept;
const EmulatorConfig& config() const noexcept;
bus::SystemBus& bus() noexcept;
The class is non-copyable and non-movable — own it through a
unique_ptr.
EmulatorConfig¶
A plain struct, fully documented in Configuration.
The factory gr712rc_config() returns the canonical SoC defaults.
EventScheduler¶
A min-heap of (SimTimeNs when, IEvent* ev) pairs. Used by:
- The runtime itself to schedule periodic peripheral ticks (in addition
to the per-quantum
tick()call). - Custom peripherals via
ctx_.scheduler->schedule_event(when, ev).
The scheduler exposes next_event_time() for the idle-skip logic.
CpuBusBridge¶
Adapts bus::SystemBus (physical, byte-oriented) into ICpuBus
(virtual, instruction-aware). Today the adapter is a passthrough — no
MMU — but it is the natural injection point for the SRMMU when Phase 7
delivers it. The CPU's step() routes all fetches and loads/stores
through this object.
ElfLoader¶
Parses SPARC big-endian ET_EXEC ELF binaries:
- Validates
EI_CLASS == ELFCLASS32,EI_DATA == ELFDATA2MSB,e_machine == EM_SPARC. - Walks
PT_LOADsegments, copies file bytes into RAM, zero-fills BSS. - Sets
core(0).pc()toe_entry(orcfg.entry_point_overrideif non-zero). - Initialises secondary cores into power-down (Decision 28).
Errors map to ErrorCode::ElfLoadError with a descriptive log line.
GDB stub¶
GdbStub listens on 127.0.0.1:cfg.gdb_stub_port and speaks the
GDB Remote Serial Protocol subset required by sparc-rtems-gdb:
| Packet | Meaning | Status |
|---|---|---|
g |
Read all registers | ✅ |
G |
Write all registers | ✅ |
m addr,length |
Read memory | ✅ |
M addr,length:bytes |
Write memory | ✅ |
c / C sig |
Continue | ✅ |
s / S sig |
Single-step | ✅ |
Z0/Z1 + z0/z1 |
Insert / remove software breakpoint | ✅ |
? |
Halt reason | ✅ |
vCont? / vCont |
Multi-thread step/continue | partial |
qSupported |
Capability negotiation | ✅ |
D |
Detach | ✅ |
k |
Kill | ✅ (sets ErrorMode) |
Single-stepping is implemented by calling step() once and returning
to the stub's stop loop. Software breakpoints are tracked in a
per-stub set; the stub patches ta 1 (0x91d02001) at the breakpoint
address and restores the original word on hit.
The stub never blocks the host process — it integrates with the
emulator via cooperative polling between quanta. When
cfg.gdb_stub_wait_for_client = true, initialize() blocks until a
client attaches.
RunResult¶
enum class HaltReason {
DurationExpired, // run_for budget reached
DeadlineReached, // run_until deadline reached
ErrorMode, // a core entered ErrorMode
Breakpoint, // GDB stub stopped on a breakpoint
};
struct RunResult {
HaltReason reason;
SimTimeNs sim_time; // current_sim_time after the run
std::uint64_t instructions_executed;
};
A Breakpoint reason hands control to the GDB stub; a typical CLI
loop is while (run_until(deadline).reason == Breakpoint) gdb->stop_loop();.
What is intentionally not in lince_runtime¶
- No CLI parsing (that is
lince_app). - No file format other than ELF (raw images go through
load_binary). - No virtualisation of host services beyond
ILogger/ICharacterDevice— the runtime is a user of the defaults, not a definer.