tero_peripherals¶
The GRLIB device models needed to boot RTEMS on the GR712RC and GR740.
Every device is an IPeripheral subclass; each has a dedicated reference
page under Peripherals covering register
layouts bit by bit. This page is the module-level view: the class
set, the shared contract, and the lifecycle.
# src/peripherals/CMakeLists.txt — peripherals sit on the bus, not the core
target_link_libraries(tero_peripherals
PUBLIC tero::interfaces tero::bus
PRIVATE tero::warnings)
Responsibility
Model GRLIB IP cores as IPeripheral MMIO slaves: react to bus
accesses, raise/lower IRQs through the injected bridges, schedule
timed work, and DMA through the bus — all without ever seeing the CPU.
Depends on bus, not core
Peripherals link tero_interfaces and tero_bus. They DMA through
the bus and never reference architectural state, so they cannot couple
to the CPU.
The device set¶
src/peripherals/include/tero/peripherals/
├── irqmp.hpp irqamp.hpp ← interrupt controllers (GR712RC / GR740)
├── gptimer.hpp ← general-purpose timer unit
├── apbuart.hpp ← APB UART (console + aux)
├── memctrl.hpp ← FTMCTRL memory controller (stub regs)
├── prom.hpp ← boot ROM (IMemoryRegion)
├── grgpio.hpp signal_port.hpp ← GPIO port + concrete ISignalPort
└── version.hpp
| Class | Header | Default base | IRQ (recipe) | Implements beyond IPeripheral |
Page |
|---|---|---|---|---|---|
IrqMP |
irqmp.hpp:30 |
0x80000200 |
— | IInterruptController |
IRQMP |
IrqAMP |
irqamp.hpp:33 |
0xFF904000 |
— | IInterruptController |
IRQMP |
GPTimer |
gptimer.hpp:28 |
0x80000300 |
8 (GR712RC) / 1 (GR740) | — | GPTimer |
ApbUart |
apbuart.hpp:22 |
0x80000100 |
2 / 17..21 (GR712RC); 29/30 (GR740) | — | APBUART |
MemCtrl |
memctrl.hpp:22 |
0x80000000 |
— | — | MemCtrl |
Prom |
prom.hpp:27 |
cfg.prom_base |
— | IMemoryRegion |
— |
GrGpio |
grgpio.hpp:36 |
0x80000900 / 0x80000A00 |
1..15 | exposes ISignalPort per pin |
— |
SignalPort |
signal_port.hpp:15 |
— | — | concrete ISignalPort |
— |
IRQMP vs IRQAMP are distinct IP cores
The GR740 IRQ(A)MP is a separate GRLIB IP core from the GR712RC
IRQMP, with a divergent register map and extended-IRQ delivery. They
are modelled as sibling peripherals (IrqMP, IrqAMP), both
satisfying the common IInterruptController surface — GR740 features
are not bolted onto IrqMP.
Who instantiates them¶
Peripherals are not hard-wired into the Emulator. The SoC kits
(gr712rc_config() / gr740_config())
populate EmulatorConfig::peripherals with one PeripheralSpec per
device — each carrying an instance_name, a factory lambda, its IRQ
lines, and an optional chardev_index. Emulator::initialize() runs each
factory in vector order, allocates one IrqBridge per declared IRQ line,
injects any chardev, and calls attach(). PROM is wired separately from
the cfg.prom_* fields. There is no silicon-specific peripheral creation
outside the recipes — the user can inspect, modify, or replace any entry.
The recipe wiring (from emulator_config.cpp):
- GR712RC:
irqmp,memctrl,gptimer0(IRQ 8), six APBUARTs (apbuart0IRQ 2 = console;apbuart1..5IRQ 17..21 via EIRQ), and twogrgpioports. - GR740:
irqamp,memctrl,gptimer0(IRQ 1), two APBUARTs (apbuart0IRQ 29,apbuart1IRQ 30, both via EIRQ).
Common skeleton¶
All devices follow the same shape:
class FooPeripheral final : public IPeripheral {
public:
explicit FooPeripheral(PhysAddr base);
std::string_view device_class() const override; // IP-core kind ("foo"); name() is final = instance name
AddressRange mmio_range() const override;
void attach(const PeripheralContext&) override;
void reset() override;
Result<std::uint32_t> mmio_read (PhysAddr, AccessSize) override;
Result<void> mmio_write(PhysAddr, AccessSize, std::uint32_t) override;
void tick(SimTimeNs now) override;
void publish(IPublisher&) override;
private:
PhysAddr base_;
PeripheralContext ctx_; // cached from attach()
/* per-device register state */
};
Lifecycle¶
| Phase | Call | What happens |
|---|---|---|
| Construct | factory lambda | device built with its MMIO base |
| Attach | attach(ctx) |
caches IBusMaster*, irqs, chardev, scheduler, logger, time, observer, num_cores, ns_per_cycle |
| Connect | connect_ports(resolver) + declared ISignalPort edges |
peer wiring (GPIO etc.); most devices skip this |
| Reset | reset() |
restore power-on register values |
| Run | tick(now) / mmio_* / scheduled IEvents |
react to time and bus |
The tick contract¶
tick(SimTimeNs now) is called once per scheduling round. Which devices
do real work:
GPTimerdecrements its prescaler and sub-timers (and schedules IRQ raises throughctx.irqs[...]).ApbUartpolls itsICharacterDevicefor incoming bytes into the RX FIFO; TX bytes go out as they are written.MemCtrl,IrqMP/IrqAMPdo nothing intick— they react to bus writes and external asserts, not to time.
MMIO size policy¶
The register-backed peripherals (APBUART, IRQMP, GPTIMER, MemCtrl)
reject non-word accesses with ErrorCode::AlignmentError — RTEMS only
ever uses st/ld (word) against them. The SystemBus MMIO constraints
(alignment + single region) are checked before the device is even called.
Capabilities in use¶
IMemoryRegion—Prom(prom.hpp:27,56) implements it so the bus serves span-based bulk reads (debugger, fetch) directly from the ROM bank;memory_region()returnsthis. Register-only devices must not implement it.ISignalPort—GrGpioexposes input pins asISignalPorts ("pin0".."pin31") so an external model can drive them;SignalPort(signal_port.hpp:15) is the reusable concrete one-bit line.
Custom peripherals¶
Anything you build follows the same IPeripheral contract — the runtime
treats your device exactly like a built-in. A fully wired DMA-capable
peripheral with an IRQ is in examples/demo-dma/ (both the
PeripheralSpec and the add_peripheral forms) and walked through in the
custom peripheral guide.
See also¶
- Peripherals reference — per-device register detail.
- Architecture: peripherals — the
IPeripheral/PeripheralContext/port model and lifecycle. - Interfaces —
IPeripheral,IInterruptController,IMemoryRegion,ISignalPort. - Runtime —
PeripheralSpec, the SoC recipes, IRQ bridges.