tero_defaults¶
Reference implementations of the injected I* interfaces, used by the
standalone build. Each can be replaced by an SMP2 wrapper, a Python
binding, or a custom test harness through the matching Emulator::set_*
setter.
# src/defaults/CMakeLists.txt
target_link_libraries(tero_defaults
PUBLIC tero::interfaces
PRIVATE tero::warnings fmt::fmt)
Responsibility
Provide the smallest correct standalone implementation of every host service the emulator depends on, behind the interfaces, so the whole library can be dropped and re-implemented by a downstream embedder.
Depends on interfaces only
No dependency on core, bus, or peripherals. tero_runtime
links it PRIVATE — these classes are constructed inside
emulator.cpp (and by main.cpp), never exposed on the runtime's
public header surface.
Source layout¶
src/defaults/
├── include/tero/defaults/
│ ├── stdout_logger.hpp
│ ├── stdout_char_device.hpp
│ ├── labeled_stdout_char_device.hpp
│ ├── null_fault_injector.hpp
│ └── debug_publisher.hpp
└── src/
├── stdout_logger.cpp
├── stdout_char_device.cpp
├── labeled_stdout_char_device.cpp
├── null_fault_injector.cpp
└── debug_publisher.cpp
| Class | Implements | Substituted by (SMP2) |
|---|---|---|
StdoutLogger |
ILogger |
Smp::Services::ILogger adapter |
StdoutCharDevice |
ICharacterDevice |
SMP2 output field / linked model |
LabeledStdoutCharDevice |
ICharacterDevice |
(auxiliary-UART helper) |
NullFaultInjector |
IFaultInjector |
custom SMP2 operations |
DebugPublisher |
IPublisher |
Smp::IPublication |
StdoutLogger¶
ILogger implementation that writes each record to stderr as
[LEVEL] [category] message.
class StdoutLogger final : public ILogger { // stdout_logger.hpp:10
public:
explicit StdoutLogger(LogLevel min_level = LogLevel::Info) noexcept;
void log(LogLevel level, std::string_view category,
std::string_view message) override; // 3 args — see note
void set_min_level(LogLevel) noexcept;
LogLevel min_level() const noexcept;
};
log takes three arguments
The signature is log(level, category, message) — the category
short tag sits between level and message. The CLI raises the
threshold to Debug when --verbose is passed
(main.cpp builds StdoutLogger(LogLevel::Debug)).
StdoutCharDevice¶
ICharacterDevice for UART output. write_char goes to stdout;
input is always empty.
class StdoutCharDevice final : public ICharacterDevice { // stdout_char_device.hpp:11
public:
void write_char(char c) override;
std::optional<char> read_char() override { return std::nullopt; }
bool has_input() const override { return false; }
};
The CLI wires one of these to the console UART (APBUART0) via
set_uart_character_device(0, ...) so guest output is visible. (The
ICharacterDevice contract is write_char / read_char /
has_input — there is no transmit/receive API.)
LabeledStdoutCharDevice¶
ICharacterDevice that line-buffers TX bytes and flushes each
completed line as <prefix><line>\n to stdout; RX is permanently empty.
Designed for auxiliary UARTs whose output must interleave readably with
the console UART in a single terminal — the tag keeps each line atomic.
class LabeledStdoutCharDevice final : public ICharacterDevice { // labeled_stdout_char_device.hpp:21
public:
explicit LabeledStdoutCharDevice(std::string prefix) noexcept;
~LabeledStdoutCharDevice() override; // flushes any unterminated tail
void write_char(char c) override;
std::optional<char> read_char() override { return std::nullopt; }
bool has_input() const override { return false; }
};
Bytes accumulate until a \n or \r; consecutive terminators (\r\n)
collapse because flushing an empty buffer emits nothing. The
hello-gr712rc / hello-gr740 examples wire one of these to every
UART (including UART0), prefixed [apbuart<i>].
NullFaultInjector¶
IFaultInjector no-op: signals "never corrupt" on every query. This is
the MVP-shipped (Level-2) implementation; Level-3 FT work adds real
injectors behind the same interface.
class NullFaultInjector final : public IFaultInjector { // null_fault_injector.hpp:11
public:
bool should_corrupt_read(PhysAddr, std::size_t) override { return false; }
void corrupt(std::span<std::byte>) override {}
};
Why a no-op interface at all
- The call sites in the core (read paths, register accesses) are part of the design. Wiring the interface in once and no-op'ing the implementation means a Level-3 injector lands with no refactor.
- SMP2 wrappers may surface fault injection as model operations; the no-op default lets the standalone build ignore them.
DebugPublisher¶
IPublisher that captures every publish_field call into an append-only
list — used by tests to assert a module exposes the expected observable
fields, and as the standalone "dump state" backend.
class DebugPublisher final : public IPublisher { // debug_publisher.hpp:16
public:
enum class FieldKind : std::uint8_t { U8, U16, U32, U64, Bool };
struct Entry { std::string name; FieldKind kind; const void* ptr; };
void publish_field(std::string_view, std::uint8_t*) override;
void publish_field(std::string_view, std::uint16_t*) override;
void publish_field(std::string_view, std::uint32_t*) override;
void publish_field(std::string_view, std::uint64_t*) override;
void publish_field(std::string_view, bool*) override;
const std::vector<Entry>& entries() const noexcept; // captured fields
void clear() noexcept;
};
The five typed overloads mirror IPublisher exactly; an SMP2 wrapper
replaces this with Smp::IPublication, which has the same shape (named
fields, typed pointers).
How implementations are selected¶
Consumers see only the I* interfaces. The concrete class is chosen by
the embedder via the Emulator::set_* injection points — which is exactly
what tero_app/main.cpp does:
emu.set_logger(std::make_unique<tero::defaults::StdoutLogger>(level));
emu.set_uart_character_device(
0, std::make_unique<tero::defaults::StdoutCharDevice>());
A library user in another context substitutes their own implementations
and never includes any header from tero_defaults at all.
See also¶
- Interfaces — the contracts these classes implement.
- Runtime — the
set_*injection points and where the defaults are wired. - App — the canonical example of injecting defaults.