Porting Guide
How to add support for a new architecture or board to bedrock[RTOS].
Overview
bedrock[RTOS] strictly separates hardware-dependent code from the kernel. Porting requires:
- Implementing the HAL functions declared in
include/bedrock/br_hal.h - Providing a startup file (vector table,
.data/.bssinit) - Providing a linker script
- Adding a build target in
chorus.build
Zero kernel source changes are needed.
Step 1: Create Architecture Directory
arch/<arch_name>/
├── br_hal_timer.c Timer and interrupt control
├── br_hal_context.c Context switch and stack init
└── startup.c Vector table and Reset_Handler
Step 2: Implement Timer HAL
br_hal_timer_init
void br_hal_timer_init(void);
Initialize the hardware timer. Must provide a free-running time source with at least microsecond resolution.
br_hal_timer_get_us
br_time_t br_hal_timer_get_us(void);
Return the current time in microseconds since boot. Must be monotonically increasing and handle overflow of the underlying hardware counter.
br_hal_timer_set_alarm
void br_hal_timer_set_alarm(br_time_t abs_us);
Schedule a one-shot alarm at absolute time abs_us. When the alarm fires, call br_time_alarm_handler() (declared in bedrock/bedrock.h).
br_hal_timer_cancel_alarm
void br_hal_timer_cancel_alarm(void);
Cancel any pending alarm.
Step 3: Implement Interrupt Control HAL
br_hal_irq_disable
uint32_t br_hal_irq_disable(void);
Disable interrupts globally. Return the previous interrupt state for later restoration.
br_hal_irq_restore
void br_hal_irq_restore(uint32_t state);
Restore the interrupt state returned by br_hal_irq_disable().
br_hal_in_isr
bool br_hal_in_isr(void);
Return true if currently executing in an interrupt/exception context.
Step 4: Implement Context Switch HAL
br_hal_stack_init
void *br_hal_stack_init(void *stack_top, br_task_entry_t entry, void *arg);
Build an initial stack frame for a new task. The frame must be arranged so that br_hal_start_first_task() or br_hal_context_switch() can restore it and begin execution at entry(arg).
stack_toppoints to the top (highest address) of the stack- The returned pointer is the initial stack pointer (after frame is built)
- The
spfield of the TCB is the first struct member — assembly code relies on this
br_hal_context_switch
void br_hal_context_switch(void **old_sp, void **new_sp);
Trigger a context switch. Save the current context and store the stack pointer at *old_sp. Restore the context from *new_sp. On Cortex-M, this typically pends PendSV rather than switching immediately.
br_hal_start_first_task
void br_hal_start_first_task(void *sp) __attribute__((noreturn));
Start executing the first task. Load the stack pointer sp, restore the context, and jump to the task entry point. This function must never return.
Step 5: Implement Board Init
void br_hal_board_init(void);
Perform any early board-level initialization (clock setup, GPIO, peripheral enables). This is called before the timer is initialized. Can be a no-op if nothing is needed.
Step 6: Startup Code
Provide a startup.c (or .s) with:
- Vector table placed in
.isr_vectorsection Reset_Handler: copy.datafrom flash to SRAM, zero.bss, callmain()- Default handlers for exceptions
- Entries for
PendSV_HandlerandSysTick_Handler(or equivalent)
Step 7: Linker Script
Create boards/<board_name>/linker.ld with:
MEMORYregions for flash and RAMENTRY(Reset_Handler).textsection withKEEP(*(.isr_vector)).datasection with VMA in RAM, LMA in flash.bsssection in RAM- Export symbols:
_sidata,_sdata,_edata,_sbss,_ebss,_estack
Step 8: Build Configuration
Add compile and link targets for the new architecture in chorus.build.
Reference
See arch/arm-cortex-m/ and boards/qemu-cortex-m3/ for a complete working example targeting the QEMU LM3S6965 Cortex-M3.