Releases: uLipe/ulmk_microkernel
Release list
ulmk 0.3.1: Component layout cleanup for a leaner kernel and clear
Companion tags (same version): ulmk_apps 0.3.1 · ulmk_boards 0.3.1
Changes
- Kernel builtins reduced to
hello_worldonly ping_pongmoved toulmk_appsas a standaloneROOT_THREADdemo- Silicon HIL certs nested under
ulmk_apps/silicon/(recursive CMake aggregator) - Removed unused
board_ledsglue andtricore_asclin dev.py/ CI / docs updated for separate hello / ping_pong images
Fix
- TriCore
silicon_pool_exhaust: handle trackers placed in.user_bssso the ISR stack no longer straddles_small_data_atRAM+0x8000
Validation
- TC275 Lite silicon HIL suite (including
silicon_pool_exhaust)
ulmk 0.3.0: public release
Kernel release with a hierarchical timing wheel, board timer migration onto the arch tick, and continued SMP scale work (RISC-V ×4 QEMU + TriCore 3-core silicon path from the 0.2.x line).
Companion tags (same version): ulmk_apps 0.3.0 · ulmk_boards 0.3.0
Highlights
Timing wheel
- New kernel time subsystem (
kernel/time/) — hierarchical timer wheel driven by the arch periodic tick - Public API:
ulmk_sleep_ms()/ulmk_sleep_cancel()ulmk_ep_call_timeout()/ulmk_notif_wait_timeout()ulmk_tick_start()— board bring-up arms the kernel tick once
- QEMU board timers (TriCore / RISC-V / ARMv7-M / ARMv8-M) are thin wrappers over
ulmk_sleep_ms; STM/CLINT/SysTick stay owned by the arch tick (no userspace compare-match sleep server) - Unit + SDK coverage:
tests/timer_unit,sdk_suite/sleep,sleep_cancel,ipc_timeout,notif_timeout
SMP scale (since 0.2.0)
- RISC-V:
boards/qemu_riscv_virt_smp4+ SDK/CI jobs for 4-hart scale (smp_*/smp4_*) - TriCore: CPU2 secondary bring-up path aligned with TC275 3-core HIL
Fixes / hardening
- TriCore
tick_ackpreserves SRC SRPN/TOS (no longer reclaiming a userspacebind_hwon STM0 SR0) — unblocks TC275silicon_irq_stresswith kernel-owned tick - Linker:
.user_datacapture for orphan SDK.dataunder user MPU windows (ARMv8-M)
Compatibility notes
- Board code that slept via a private STM0 CMP/notif server should call
ulmk_tick_start()at bring-up and useulmk_sleep_ms()/board_timer_sleep_*thin wrappers - Soft-IRQ tests on real AURIX must not rely on arbitrary SRC
SETRfrom userspace; prefer a real event source (e.g. STM compare) with a non-tick SRPN
Validation
- QEMU unit + e2e UP: TriCore, RISC-V, MPS2 AN500, MPS2 AN505
- QEMU e2e SMP / SMP×4: RISC-V
- Silicon TC275 Lite HIL: silicon suite + board components (UP/SMP);
silicon_irq_stressPASS on/dev/ttyUSB1
ulmk 0.2.0 — Second public release: SMP support and more.
ulmk 0.2.0
Second public release. Same three-repo companion set as 0.1.x — tag all of:
| Repo | Tag |
|---|---|
| ulmk_microkernel | 0.2.0 |
| ulmk_apps | 0.2.0 |
| ulmk_boards | 0.2.0 |
Use matching tags together for HIL / board builds.
Highlights
SMP (RISC-V QEMU + TriCore TC275)
- Per-CPU runqueues, klocks, and
ENABLE_SMPconfiguration - Thread affinity (
attr.cpu/ulmk_cpu_id()), lazy secondary context, deferred IPI reschedule - Secondary bring-up on RISC-V virt and AURIX TC275 (CPU1)
- SMP-safe IPC / notification paths (lock ordering; flood-safe notif)
- SDK suite:
smp_smoke,smp_affinity,smp_ipc_cross,smp_stress,smp_vs_up - CI: RISC-V e2e with
--include-smp
Syscall WCET (silicon-ready under SMP)
- Per-CPU WCET sample slots (global slot no longer races across cores)
ulmk_wcet_bind()— private sample buffer per measuring thread- Secondary cores enable cycle counters at bring-up
- Builds on 0.1.1 WCET/hot-path work (pure syscall WCET, deferred resched, MPU skip, lean CSA)
Silicon / board companions
- ulmk_apps: silicon suite zero-inits / pins
cpu=0; WCET bind insilicon_wcet;silicon_irq_stressuses STM0 CMP0/SR0 (CMP1→SR1 does not assertSRRon this kit); newsilicon_smp_smoke - ulmk_boards/tc275_lite: CPU1 memory/OpenOCD/HIL wiring,
hil-silicon-smp-smoke.sh,smp_can_demo,wcet_compare.py
Supported platforms (regression matrix for this tag)
| Target | Unit | SDK e2e | Baseline hello+ping | Notes |
|---|---|---|---|---|
QEMU TriCore qemu_tc3xx |
✓ | ✓ UP | ✓ | |
QEMU RISC-V qemu_riscv_virt |
✓ | ✓ UP + SMP | ✓ UP + SMP | SMP CI arch |
QEMU ARMv7-M qemu_mps2_an500 |
✓ | ✓ UP | ✓ | |
QEMU ARMv8-M qemu_mps2_an505 |
✓ | ✓ UP | ✓ | |
| Silicon TC275 lite | — | ✓ UP + SMP silicon_* | board comps UP+SMP | Full HIL green at tag |
Breaking / API notes
- New syscalls:
ULMK_SYS_CPU_ID(21),ULMK_SYS_WCET_BIND(22) ulmk_thread_attr_t.cpu— set explicitly (or rely on kernel clamp to 0 when!SMP); uninitialized stack garbage breaks create/spawn under SMP- Apps measuring WCET under SMP should call
ulmk_wcet_bind()before sampling
Upgrade from 0.1.1
- Update the three repos / tags to
0.2.0in lockstep - Rebuild SDK cache if linking sdk_suite (
rm -rf tests/sdk_suite/_sdk_cacheor letdev.pyrefresh) - For TC275 SMP images: use
--enable-smpand the SMP OpenOCD / HIL scripts fromulmk_boards
Changelog (kernel, since 0.1.1)
kernel: add SMP foundation with per-CPU runqueues and affinitysyscall: use per-CPU WCET slots and ulmk_wcet_bindtests/ci: add RISC-V SMP sdk suite cases and CI job
ulmk 0.1.1 - Patch release focused on scheduling/syscall hot-path cost and cleaner trap/ISR exit semantics
Highlights
Deferred context switch (trap / ISR exit)
Syscall and IRQ handlers now only enqueue/block and set needs_resched. The
actual switch runs once in ulmk_sched_trap_dispatch on the way back to
userspace”.
- Removes mid-handler handoff /
ctx_switchfrom IPC and notif wait paths - Return codes for blocked callers are resolved after dispatch
(ulmk_kern_syscall_ret_resolve) - Syscall WCET metrics no longer fold scheduler switch cost into the call wall
time (blkgone from those samples)
Hot-path trimming
- TriCore CSA path: drop mid-switch
CALLto post-save helpers and redundant
dualisyncon the lean context-switch / vector path - MPU / IRQ: skip redundant MPU reprograms and nested
irq_savewhere the
kernel/ISR gateway already masks IRQs - IPC: envelope staging by pointer, rendezvous handoff prep + stack MPU skip,
IRQ mask held across switch return where needed
Commits since 0.1.0
arch: skip redundant MPU reprograms and nested irq_savekernel: unify sched switch and expose pure syscall WCETipc: cut ep rendezvous cost via handoff and stack MPU skipipc: stage ep envelopes by pointer, keep irq mask on switch returnarch/tricore: lean CSA path — drop mid-switch CALL and dual isynckernel: defer context switch to trap/ISR exitkernel: drop nested irq_save in IRQ binding paths
Compatibility
- Public syscall / userspace API unchanged for apps and board components
- Root-thread / boot model unchanged
Validation (release gate)
- Unit + full SDK suite (
tests unit/tests e2e) on TriCore QEMU,
qemu_riscv_virt,qemu_mps2_an500,qemu_mps2_an505 - Baseline demo
hello_world+ping_pongconsole output on all four boards - Full silicon cert set on TC275 Lite (
silicon_*apps / HIL scripts) - Board component smoke on TC275 (except
gpio_led_notify): ADC, blinky,
CAN loopback, I2C scanner, PWM LED — flash OK + console output
ulmk CI test report
Cases with results: 28
1. Execution results
| Case | Host (unit) | TriCore | RISC-V | ARM |
|---|---|---|---|---|
| atomic_unit | PASS | n/a | n/a | n/a |
| ipc_unit | PASS | n/a | n/a | n/a |
| mem_unit | PASS | n/a | n/a | n/a |
| sched_unit | PASS | n/a | n/a | n/a |
| sdk_suite/abi_smoke | n/a | PASS | PASS | PASS |
| sdk_suite/arch_whitebox/ctx_early_tricore | n/a | PASS | n/a | n/a |
| sdk_suite/atomic_stress | n/a | PASS | PASS | PASS |
| sdk_suite/boot_bss | n/a | PASS | PASS | PASS |
| sdk_suite/cap_neg | n/a | PASS | PASS | PASS |
| sdk_suite/ctx_switch | n/a | PASS | PASS | PASS |
| sdk_suite/destroy_waiters | n/a | PASS | PASS | PASS |
| sdk_suite/fault_policy | n/a | PASS | PASS | PASS |
| sdk_suite/ipc | n/a | PASS | PASS | PASS |
| sdk_suite/ipc_pi | n/a | PASS | PASS | PASS |
| sdk_suite/irq_stress | n/a | PASS | PASS | PASS |
| sdk_suite/irq_sw | n/a | PASS | PASS | PASS |
| sdk_suite/kill_rendezvous | n/a | PASS | PASS | PASS |
| sdk_suite/mem_grant | n/a | PASS | PASS | PASS |
| sdk_suite/mem_heap | n/a | PASS | PASS | PASS |
| sdk_suite/mem_isolation | n/a | PASS | PASS | PASS |
| sdk_suite/pool_exhaust | n/a | PASS | PASS | PASS |
| sdk_suite/preempt | n/a | PASS | PASS | PASS |
| sdk_suite/recv_or_notif_race | n/a | PASS | PASS | PASS |
| sdk_suite/resource_leak | n/a | PASS | PASS | PASS |
| sdk_suite/sched_order | n/a | PASS | PASS | PASS |
| sdk_suite/sleep_timer | n/a | PASS | PASS | PASS |
| sdk_suite/thread_lifecycle | n/a | PASS | PASS | PASS |
| thread_unit | PASS | n/a | n/a | n/a |
Values: PASS | FAIL | PARTIAL | SKIP | n/a. ARM aggregates AN500+AN505 (mixed → PARTIAL).
2. Coverage levels
| Area | TriCore | RISC-V | ARM | Notes |
|---|---|---|---|---|
| thread.basic | covered | covered | covered | |
| sched.order | covered | covered | covered | |
| ipc.basic | covered | covered | covered | |
| ipc.priority_inherit | covered | covered | covered | silicon_ipc_pi + sdk_suite/ipc_pi |
| mem.heap | covered | covered | covered | |
| mem.isolation | covered | covered | partial | ARM mem_grant livelock risk |
| cap.eperm_neg | covered | covered | covered | silicon_cap_neg HIL PASS; sdk_suite/cap_neg |
| mem.grant | covered | covered | covered | silicon_mem_grant HIL PASS; sdk_suite/mem_grant |
| fault.policy | covered | covered | covered | userspace MPU fault kills thread; system continues |
| irq.sw | covered | covered | covered | |
| irq.stress | covered | covered | covered | silicon_irq_stress HIL + sdk_suite/irq_stress |
| wcet.syscall_o1 | covered | n/a | n/a | silicon_wcet HIL only (ULMK_CONFIG_SYSCALL_WCET); QEMU out of scope |
| proc.mgmt | missing | missing | missing | out of scope this cycle — process model TBD; GRANT_CAP is ulmk_cap_grant |
| atomic | covered | covered | covered | |
| sleep.timer | covered | covered | covered | |
| boot.bss | covered | covered | covered | |
| ctx.switch | covered | covered | covered | |
| resource.leak | covered | covered | covered | |
| abi.smoke | covered | covered | covered | |
| arch.ctx_early_tricore | covered | missing | missing | arch-only |
| ipc.destroy_waiters | missing | missing | missing | silicon_destroy_waiters + sdk_suite/destroy_waiters |
| ipc.kill_rendezvous | missing | missing | missing | silicon_kill_rendezvous + sdk_suite/kill_rendezvous |
| pool.exhaust | missing | missing | missing | silicon_pool_exhaust + sdk_suite/pool_exhaust |
| ipc.recv_or_notif_race | missing | missing | missing | silicon_recv_or_notif_race + sdk_suite/recv_or_notif_race |
Values: covered | partial | missing (from tools/coverage_manifest.yaml, independent of run PASS/FAIL).
Coverage summary
| Arch | covered | partial | missing |
|---|---|---|---|
| tricore | 19 | 0 | 5 |
| riscv | 17 | 0 | 7 |
| arm | 16 | 1 | 7 |
Upgrade notes
From 0.1.0: rebuild kernel / SDK artefacts; no application source changes required
for the deferred-reschedule behaviour. Builds with ULMK_CONFIG_SYSCALL_WCET
should treat samples as in-kernel cost, not full blocked RTT.
ulmk 0.1.0 — First public release
The first tagged release of ulmk: a small, seL4-inspired microkernel for microcontroller-class targets. Mechanism in the kernel, policy in userspace, one ELF with MPU/PMP isolation.
Supported platforms
| Arch | Target | Status |
|---|---|---|
| TriCore | QEMU TC3xx + Infineon AURIX TC275 Lite (HIL) | Primary |
| RISC-V | RV32IMAC, QEMU virt |
CI green |
| ARM | Cortex-M7 (mps2-an500) / Cortex-M33 (mps2-an505) |
CI green |
Highlights
- Synchronous IPC with priority inheritance
- Capability-oriented syscalls (threads, endpoints, notifications, memory, IRQ bind)
- O(1) bitmap scheduler, root-thread boot model
- Component build (
tools/dev.py) and distributable SDK packaging - Optional syscall WCET cycle slot (
ULMK_CONFIG_SYSCALL_WCET)
Validation (acceptance: zero FAIL)
- Host unit tests
- Full sdk_suite black-box matrix on TriCore, RISC-V, and both ARM QEMU boards
- Baseline demos (
hello_world+ping_pong) on all QEMU boards - Silicon HIL cert set on TC275 Lite (unit, e2e, stress, WCET, IRQ stress, PI, fault/cap/mem, waiter lifecycle, …)
Companion repositories
Use matching tag 0.1.0:
ulmk_apps— userspace apps + silicon cert componentsulmk_boards— TC275 Lite BSP, drivers, HIL shims
Getting started
git clone https://github.com/uLipe/ulmk_microkernel.git
cd ulmk_microkernel
git checkout 0.1.0
python3 tools/dev.py # builds the multi-arch Docker image on first run
python3 tools/dev.py components enable hello_world ping_pong
python3 tools/dev.py build qemuulmk CI test report
Cases with results: 28
1. Execution results
| Case | Host (unit) | TriCore | RISC-V | ARM |
|---|---|---|---|---|
| atomic_unit | PASS | n/a | n/a | n/a |
| ipc_unit | PASS | n/a | n/a | n/a |
| mem_unit | PASS | n/a | n/a | n/a |
| sched_unit | PASS | n/a | n/a | n/a |
| sdk_suite/abi_smoke | n/a | PASS | PASS | PASS |
| sdk_suite/arch_whitebox/ctx_early_tricore | n/a | PASS | n/a | n/a |
| sdk_suite/atomic_stress | n/a | PASS | PASS | PASS |
| sdk_suite/boot_bss | n/a | PASS | PASS | PASS |
| sdk_suite/cap_neg | n/a | PASS | PASS | PASS |
| sdk_suite/ctx_switch | n/a | PASS | PASS | PASS |
| sdk_suite/destroy_waiters | n/a | PASS | PASS | PASS |
| sdk_suite/fault_policy | n/a | PASS | PASS | PASS |
| sdk_suite/ipc | n/a | PASS | PASS | PASS |
| sdk_suite/ipc_pi | n/a | PASS | PASS | PASS |
| sdk_suite/irq_stress | n/a | PASS | PASS | PASS |
| sdk_suite/irq_sw | n/a | PASS | PASS | PASS |
| sdk_suite/kill_rendezvous | n/a | PASS | PASS | PASS |
| sdk_suite/mem_grant | n/a | PASS | PASS | PASS |
| sdk_suite/mem_heap | n/a | PASS | PASS | PASS |
| sdk_suite/mem_isolation | n/a | PASS | PASS | PASS |
| sdk_suite/pool_exhaust | n/a | PASS | PASS | PASS |
| sdk_suite/preempt | n/a | PASS | PASS | PASS |
| sdk_suite/recv_or_notif_race | n/a | PASS | PASS | PASS |
| sdk_suite/resource_leak | n/a | PASS | PASS | PASS |
| sdk_suite/sched_order | n/a | PASS | PASS | PASS |
| sdk_suite/sleep_timer | n/a | PASS | PASS | PASS |
| sdk_suite/thread_lifecycle | n/a | PASS | PASS | PASS |
| thread_unit | PASS | n/a | n/a | n/a |
Values: PASS | FAIL | PARTIAL | SKIP | n/a. ARM aggregates AN500+AN505 (mixed → PARTIAL).
2. Coverage levels
| Area | TriCore | RISC-V | ARM | Notes |
|---|---|---|---|---|
| thread.basic | covered | covered | covered | |
| sched.order | covered | covered | covered | |
| ipc.basic | covered | covered | covered | |
| ipc.priority_inherit | covered | covered | covered | silicon_ipc_pi + sdk_suite/ipc_pi |
| mem.heap | covered | covered | covered | |
| mem.isolation | covered | covered | partial | ARM mem_grant livelock risk |
| cap.eperm_neg | covered | covered | covered | silicon_cap_neg HIL PASS; sdk_suite/cap_neg |
| mem.grant | covered | covered | covered | silicon_mem_grant HIL PASS; sdk_suite/mem_grant |
| fault.policy | covered | covered | covered | userspace MPU fault kills thread; system continues |
| irq.sw | covered | covered | covered | |
| irq.stress | covered | covered | covered | silicon_irq_stress HIL + sdk_suite/irq_stress |
| wcet.syscall_o1 | covered | n/a | n/a | silicon_wcet HIL only (ULMK_CONFIG_SYSCALL_WCET); QEMU out of scope |
| proc.mgmt | missing | missing | missing | out of scope this cycle — process model TBD; GRANT_CAP is ulmk_cap_grant |
| atomic | covered | covered | covered | |
| sleep.timer | covered | covered | covered | |
| boot.bss | covered | covered | covered | |
| ctx.switch | covered | covered | covered | |
| resource.leak | covered | covered | covered | |
| abi.smoke | covered | covered | covered | |
| arch.ctx_early_tricore | covered | missing | missing | arch-only |
| ipc.destroy_waiters | missing | missing | missing | silicon_destroy_waiters + sdk_suite/destroy_waiters |
| ipc.kill_rendezvous | missing | missing | missing | silicon_kill_rendezvous + sdk_suite/kill_rendezvous |
| pool.exhaust | missing | missing | missing | silicon_pool_exhaust + sdk_suite/pool_exhaust |
| ipc.recv_or_notif_race | missing | missing | missing | silicon_recv_or_notif_race + sdk_suite/recv_or_notif_race |
Values: covered | partial | missing (from tools/coverage_manifest.yaml, independent of run PASS/FAIL).
Coverage summary
| Arch | covered | partial | missing |
|---|---|---|---|
| tricore | 19 | 0 | 5 |
| riscv | 17 | 0 | 7 |
| arm | 16 | 1 | 7 |
Built for fun and learning — contributions and board ports welcome. :)