Tinverse logo Tinverse LLC

Bare Metal Platform

A modern bare-metal platform for embedded control boards.

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.

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.

Chip facts

Which peripherals, pins, interrupt lines, alternate functions, and DMA routes the selected processor supports.

Board connections

Which sensor, drive, encoder, communication bus, or control signal is connected to each processor resource.

Resource policy

Clock rates, signal modes, initial states, flash regions, and other configuration that must stay consistent across the product.

Turn wiring details into interfaces the application can use.

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.

Board description
Hardware facts have one explicit name.

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;
};
Typed boundary
Types and factories preserve valid routes.

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>);
Product behavior
Application code speaks in device terms.

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);
}

API Usage

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();

Board knowledge stays out of product behavior.

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.

Turn a board signal into product behavior.

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.

Explore tin →
#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);
}

Two deployment paths with explicit hardware contracts.

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.