Chip facts
Which peripherals, pins, interrupt lines, alternate functions, and DMA routes the selected processor supports.
Bare Metal Platform
thal turns the exposed hardware details of a board into typed C++
software interfaces. Application code works with names such as
gate_enable, phase_u_current, and
field_can instead of repeating peripheral, pin, and
register numbers.
Typed interfaces, ownership types, compile-time route validation, and coroutine-aware APIs make hardware access explicit and reviewable.
Describe the board
A board description is not a replacement for the schematic or PCB design. It records the parts of that design that firmware depends on: the processor, memory layout, available peripherals, connected devices, pins, clocks, interrupts, bus routes, DMA channels, and startup resources.
Which peripherals, pins, interrupt lines, alternate functions, and DMA routes the selected processor supports.
Which sensor, drive, encoder, communication bus, or control signal is connected to each processor resource.
Clock rates, signal modes, initial states, flash regions, and other configuration that must stay consistent across the product.
Define the software boundary
thal validates the board description against the processor metadata and resource schemas, then exposes board-specific aliases and factories. Those factories consume owned processor resources and return the typed GPIO, bus, ADC, PWM, or other object requested by the application.
Compile-time route validation checks that a peripheral can actually use the selected pin, alternate function, timer channel, or DMA role on that processor. An invalid combination stops the build instead of becoming a fault on the robot. This validates the software description against the processor metadata; it does not verify the physical PCB wiring.
The description connects product concepts such as a gate enable or phase-current input to the processor resources that implement them.
aliases {
gate-enable =
&gate_enable;
phase-u-current =
&phase_u_current;
};
The C++ interface exposes named construction functions while compile-time checks reject unsupported peripheral and pin combinations.
static_assert(
tin::io::gpio_output<
board::gate_enable>);
static_assert(
tin::io::adc<
board::phase_u_current>);
tin actors, tasks, and state machines depend on portable I/O contracts rather than board wiring or processor registers.
template<
tin::io::gpio_output GateEnable>
tin::io::bus_status
disable_gate(
GateEnable& gate_enable)
{
return gate_enable.write(
tin::io::level::low);
}
Platform startup establishes the processor and register-backed port objects. The application then asks the board namespace for the resources it needs. Pin ownership and routing remain inside the board-specific boundary.
The example uses real factory names from the TLX-SMN24-1A board description. It contains no application-level pin or peripheral numbers.
#include "thal/boards.h"
namespace board = thal::boards::tlx_smn24_1a;
auto board_peripherals = thal::stm32::init<board::chip>();
auto gate_enable =
board::make_gate_enable(board_peripherals, motor_control_gpio);
auto gate_fault =
board::make_gate_fault(board_peripherals, motor_control_gpio);
const auto gate_enable_status =
gate_enable.write(tin::io::level::low);
const auto gate_fault_level = gate_fault.read();
What the boundary buys you
| Board fact | Software interface | Application effect |
|---|---|---|
| A gate-enable GPIO connection | make_gate_enable |
The motor-control code does not contain a port or pin number. |
| A PWM timer and channel route | A typed phase-output factory | An unsupported timer and pin pairing is rejected during the build. |
| A CAN peripheral and its pins | A named field-bus object | Protocol code depends on the bus contract, not processor registers. |
| Bootloader and application flash regions | A board-specific flash layout | Boot and application code share the same explicit memory contract. |
thal + tin
thal provides the concrete GPIO objects. The supervisor depends only on
the portable tin::io contracts and sends a typed event into
the application runtime. A host test can provide different GPIO objects
without changing the supervisor or state machine.
#include "thal/boards.h"
#include "tin/io.h"
#include "tin/runtime.h"
namespace board = thal::boards::tlx_smn24_1a;
inline constexpr auto gate_disabled = tin::io::level::low;
inline constexpr auto gate_fault_asserted =
board::gate_fault_active_high
? tin::io::level::high
: tin::io::level::low;
struct GateFaultDetected {};
template<tin::io::gpio_output GateEnable,
tin::io::gpio_input GateFault,
typename Runtime>
void supervise_gate(GateEnable& gate_enable,
GateFault& gate_fault,
Runtime& runtime)
{
if (gate_fault.read() != gate_fault_asserted) {
return;
}
const auto status = gate_enable.write(gate_disabled);
if (status == tin::io::bus_status::ok) {
static_cast<void>(
runtime.send_event(GateFaultDetected{}));
}
}
template<typename Runtime>
void control_step(board::gate_enable& gate_enable,
board::gate_fault& gate_fault,
Runtime& runtime)
{
supervise_gate(gate_enable, gate_fault, runtime);
}
Embedded and Linux systems
Tinverse Hardware Platform captures the physical board, signal, power, and safe-state facts. thal turns the embedded portion into typed C++ resources from DTS and YAML, while Tinverse Linux carries host-compute requirements into device trees, kernels, drivers, firmware, and image configuration.
Contact engineering@tinverse.com.