OS execution and lifecycle contract

OS provides bounded waits and explicit task/synchronization storage; device interfaces do not depend on a scheduler. Default firmware is C11, statically assembled and uses no platform heap. Native pthread bookkeeping is not MCU zero-heap evidence. External products own task roles, priorities, stacks, queue capacities, startup, shutdown, safe outputs and recovery policy.

Layers and dependencies

Current waits/shutdown: ready-arm-ready-wait-ready; external owners manage executors and joins

See FIG-08/source references for execution semantics and FIG-01 for backend/platform boundaries. The simple source diagram below is historical layering only, not the current optional-backend, progression or shutdown contract.

Historical OS layering diagram source:

flowchart TD
    Product[外部产品:任务、预算与恢复] --> Owner[显式 BusOwner / UARTOwner]
    Product --> Wait[公共 nx_wait_port_t]
    Owner --> IO[公共 I/O 与 provider]
    Owner --> Wait
    Wait --> Baremetal[裸机:序号轮询]
    Wait --> Native[Native:pthread]
    Wait --> FreeRTOS[FreeRTOS:显式静态对象]
    FreeRTOS --> Kernel[锁定 Kernel 与 CPU Port]
    FreeRTOS --> Arch[Arch:上下文和短临界区]
  • Core has no OS/Arch dependencies. Published acquire/release semantics are kernel-independent.

  • Arch owns CPU-local IRQ masks, atomics and shallow sleep, not ticks, tasks or devices.

  • OS selects backends and validates actual capabilities. Public Wait does not include task creation, device service or dispatch.

  • SoC owns clocks, controllers, actual IRQs and available timers; Boards own actual pins, electrical facts and external devices.

  • Products create owners/producers/coordinators, provide timer/trace/fail-stop ports and start schedulers. nx_platform_start() never starts them on the product’s behalf.

Public Wait is cross-backend. Tasks, queues and kernel-specific capabilities use explicit backend types rather than implying every RTOS has identical features. Generated factories return existing device faces without creating OS objects.

Waiting, clocks and completion

nx_wait_until() uses ready → arm → ready → wait → ready. Backends must combine sequence observation and wait entry without losing latched wakeups. Wake is a hint; business predicates alone decide results, and notification sequences transfer no payload ownership. One port has one waiter and may allow multiple publishers under its backend contract. Fewer than 2^32 wakes may occur between arm and recheck.

Deadlines are absolute monotonic microseconds in the backend’s domain: Native CLOCK_MONOTONIC, FreeRTOS nx_time_now_us(), or the bare-metal initialization clock. Never switch domains while requests are alive. NX_DEADLINE_NEVER is truly infinite, not UINT64_MAX converted to finite timespec. Microsecond representation does not promise microsecond scheduling; blocking error includes tick quantization and scheduling latency.

Finite notification waits must still check expiry while inspecting hints; irrelevant repeated wakes do not extend budgets. Helpers retain one final ready check after backend errors, letting observed actual completion win. Bare metal returns BUSY when unready so callers schedule explicitly. Immediately available queue operations use try semantics; deadlines are consumed only when blocking, and failures preserve caller outputs.

Waiting never advances UART/SPI service. Shared-controller producers cannot service inside ready; the sole execution owner must keep running during cancel/stop/drain. Timeout/cancel requests are not settlement and release no request, operation, DMA buffer or notification storage.

Interrupt policy and context

CPU/SoC facts describe actual IRQ counts, priority bits and grouping. Runtime policy distinguishes no kernel, PRIMASK kernels and BASEPRI kernels; ceiling=0 alone does not prove no kernel. Mainline kernel ISR calls validate publisher IRQ, priority width, grouping and syscall ceiling. Baseline validates actual configurable IRQs under PRIMASK-port rules. Typed IRQ sinks and priority policy constrain only notification paths implemented by the selected provider/backend/mode. Existing types or valid ceilings establish no new mode, kernel or physical IRQ qualification; unsupported combinations fail explicitly.

NMI, HardFault and kernel-owned exceptions cannot use these kernel wake APIs. Task APIs require current-world privileged Thread mode, restored manual masks and unsuspended scheduling. Bootstrap permits only the selected port’s prescribed mask. Equal numerical masks do not prove ownership. Reject before mutating objects, publishing references or creating kernel objects.

CPU-local masking does not protect SMP, DMA metadata writes or publishers in another security world. IRQ sinks/contexts live until providers stop and publisher quiescence is proven.

Static storage and tasks

Every task explicitly supplies TCB, stack and context. Creation checks actual stack capacity against the port’s initial frame/alignment. Startup minima do not replace runtime task, nested-exception MSP or FP/MVE budgets. Native stacks satisfy pthread minimums plus application budgets; their costs do not carry over to MCUs.

First scheduler startup requires cold privileged Thread/MSP: CONTROL.nPRIV=0, SPSEL=0 and FPCA=0. Do not leave active FP context or switch main to PSP; clearing flags cannot discard live FP state. MPU bootstrap guards bind actual protected startup instructions, basic exception frames and correct EXC_RETURN. One-shot leases are consumed before restore; repeated raw START SVC cannot regain permission. Ports own subsequent FP/MVE task use rather than banning application floating point. Actual context retention still needs hardware acceptance.

Returning joinable tasks follow backend-specific mechanisms: FreeRTOS wrappers publish completion then suspend until external deletion; Native entries return from pthread and pthread_join proves thread exit; ThreadX wrappers publish completion hints then return through the kernel completion path, while join still checks completion and deletes externally. One external lifecycle owner joins each task. TCB/stack/context cannot be reused before successful join; timeouts retain all storage. Native forbids adapter bypass through pthread_cancel, detach or another join. Completion hints alone do not prove actual exit/reference detachment.

Permanent tasks use explicit separate APIs. Entries never return; control/stack/context live until reset. They have no completion semaphore, join or destroy and serve products with genuinely permanent task lifetimes.

Direct task notification is opt-in: callers identify existing receivers and exclusively owned notification indices; only that receiver waits. Stop/join publishers before unbinding. Receiver handles/storage live until all retained ports become unusable. Platforms reserve no application notification slots implicitly.

Queues and closure

Raw queues retain minimum storage/native blocking. Absolute-deadline APIs use one domain without repeatedly expanding relative budgets. Large data should queue stable slots/handles rather than copy full payloads in kernel critical sections; slots still have explicit borrows/reclamation.

Closable queues are separate opt-in types with exact maximum concurrent waiters. Every blocked operation retains independent caller-owned waiter storage. Close stops admission and latches/wakes registered waiters; old items may drain, but sends cannot continue. Destroy requires closed, drained and quiescent users/publishers. Periodic polling is not immediate close wakeup, and raw queues never silently pay this cost.

FreeRTOS closable registries are task-only. Scheduler suspend/resume guards protect admission, waiter registration/exit and close broadcasting. Kernel give can yield; an IRQ critical token does not keep lists unchanged across that yield. maximum_waiters budgets scheduler-pause duration; item-copy/latch-give IRQ masking is measured separately.

Configuration and cost

Optional [os] in platform schema-2 and independent runtime schema-1 TOML is FreeRTOS-only. tools/configure/kernel.py is the sole policy parser and emits immutable KernelProfileIR. standard retains 1 kHz/eight priorities/Idle 128 words/16-byte names; minimal shortens names/priorities and disables unused synchronization; diagnostic enables trace; lowpower explicitly enables custom tickless. Generated capabilities are authoritative; profile names do not imply chip features.

Overrides: tick_source, tick_hz, max_priorities, syscall_priority, idle_stack_words, max_task_name_len, notification_slots, mutexes, counting_semaphores, task_notifications, trace, runtime_stats and tickless. Security profiles additionally use memory_protection, system_call_stack_words, security_model and secure_idle_stack_bytes. MPU defaults off and single-world. Syscall stacks default to 128 words with an even 64–65536-word range. Secure Idle in split mode defaults to 128 bytes, accepts 64–65536 with 8-byte alignment; single mode rejects explicit settings. Physical mpu_regions remains unknown unless declared; runtime policy cannot invent MPU hardware. Maintained combinations are listed below, not inferred from valid parameters. SysTick uses actual core clocks with exact divisibility and valid 24-bit reloads. External ticks require strong nx_freertos_external_tick_setup(uint32_t); runtime statistics require counter start/now hooks. Missing hooks fail linking instead of producing fake clocks or zero-only statistics.

Cost reports separate Wait faces, notifications, raw/closable queues, each waiter, joinable/permanent TCBs, caller stacks, Idle stacks and global kernel objects. Preserve the four-pointer Wait ABI; linked images/instance counts decide whether shared ops tables are worthwhile. Build reports compare RAM/Flash/instructions only; CPU cycles/latency need measurements, not disassembly byte counts.

Product shutdown order

  1. Coordinator closes admission; owner stop requests cancellation of every accepted operation.

  2. Owners continue service/drain; producers/observers only acquire-observe SETTLED.

  3. Coordinator joins producers; successful joins end business borrows of requests/bytes.

  4. Coordinator stops/joins owners and verifies that owner idle includes completed publication.

  5. Stop hardware RX/IRQ publishers and detach wake ports; join any other publishers.

  6. Destroy notifications/queues/guards. Native/bare metal may then stop platforms. Running FreeRTOS retains platform clocks/ticks; coordinators publish status then suspend. Only explicit reset or a complete scheduler-stop port can close kernel infrastructure; I/O idle does not establish stopped kernels.

Failures retain storage and products choose recovery. QUARANTINED retains references; it is not a releasable terminal state. BUSY from destroy is defensive, not proof that future publishers stopped. The external UARTOwner example implements monotonic shutdown milestones for one coordinator; retries resume failed stages without repeating completed joins.

Low power, diagnostics and security boundaries

Arch sequence rechecks/shallow sleep save and restore incoming PRIMASK, execute DSB before WFI and reject default deep sleep. Applications own wake timers/continuous clocks. FreeRTOS tickless uses caller-owned timer ports with separate contracts for exact tick phase, pause/resume, absolute alarms, continuous clocks and abnormal overshoot. Unbound ports retain ordinary ticks without guessed low-power devices or wake pins.

Callers supply diagnostic-ring capacity; full rings preserve old events and saturate lost counters. Kernel trace defaults to weak no-op without large buffers. Products override/consume sinks. Exception hooks are bounded extension ports; products own safe outputs, watchdog/reset, persistence and release policy.

Explicit MPU profiles use actual restricted-task ports, dedicated linker regions and narrow SVC gateways. Callers put TCB/context/syscall stacks in privileged RAM and actual task stacks in user RAM. Protected tasks require at least 64 words, with separate runtime peak budgets. Only RO/RW XN data grants in ordinary RAM are allowed; reject MMIO, privileged RAM, bounds violations, overlaps and illegal contexts. Ordinary code/data/providers remain privileged; only explicit user text/data enters user regions.

Public user services are pointer-free delay/tick-read through actual MPU veneers/SVC. No generic user pools, I/O handles, arbitrary-pointer calls or implicit elevation. Maintained software combinations include M3, M4 integer/FP, M23, M33 integer/FP, M55 integer-MVE, M85 float-MVE and explicit 16-region facts. Native, M0/M0+, M7 kernel MPU and MPU+split fail closed. Basic Arch/ordinary OS support remains separate. User tasks are permanent, not joinable wrappers. One privileged owner deletes another task only after external users quiesce; self-deletion is rejected and no reaper is hidden.

Explicit split builds two independent Runtime images. Secure uses explicit Secure CPU facts, bare-metal runtime and Nexus::SecureContext; Nonsecure uses matching facts and split FreeRTOS. Match ISA/FP/MVE/float ABI. Secure links export seven actual SG veneers and a CMSE import object; NS links consume it and actual CM23/33/55/85 TrustZone-aware non_secure ports. Macros on NTZ ports cannot impersonate cross-world save/load.

Every admitted NS task, including Idle, has caller-provided NS TCB/stack and separate Secure metadata/stack. Secure startup prepares them first; trusted immutable task→context selectors return prepared identities only and reject unknown tasks. Secure stacks align to 8 bytes; allocations include an 8-byte seal. The 64-byte port minimum excludes seals and does not replace business stack budgets. Split consumers provide Idle callbacks/matching Secure Idle storage; policy controls request budgets without default pools.

Leases bind trusted tasks and monotonic epochs. Free retains metadata/epoch history; rebinding requires joins and quiescent observers. Loaded contexts cannot be freed. Actual PSP/PSPLIM assembly saves/loads and restores incoming Secure/NS PRIMASK. Bounded integrity checks detect seal/bounds/context corruption, retain leases and enter nonreturning Secure fail-stop rather than release/recover implicitly. Defaults keep both masks and spin indefinitely without completion bounds; products own explicit safe logging/reset. NMI/HardFault avoid shared metadata. Trusted NS SVC/PendSV kernels form the boundary: ordinary NS Thread cannot impersonate Handler calls, but the mechanism does not defend against a malicious privileged NS kernel forging its own task parameters.

Thirty-one actual Secure/NS pairs, 62 ELFs, verify software linking, veneers and absence of heap/pools for M23/M33/M55/M85 FP/MVE/DSP/float ABI combinations in current CPU authority. Synthetic link regions/TCB identities supply no actual SAU/IDAU, vectors, startup, IRQ targeting, security transfer or wiring. Products/SoC SDKs implement those hardware contracts; physical acceptance is not executed. Single-world, MPU-only and split-only are separate; MPU+split is rejected. Authored v8-M security="single" means Arch state 0, not Nonsecure. Single and explicit secure use the locked port’s traditional FD task return/F9 MSP bootstrap; explicit nonsecure uses BC/B8. NEXUS_CPU_SECURE_ONLY is 1 for the first two solely to select return tuples. Single has no -mcmse and grants no Secure/SAU/cross-world access. Explicit secure is state 1 with CMSE; nonsecure is state 2, with attribution from actual Secure startup. v6/v7 retains its non-cross-world tuple without inheriting v8 security meanings. See Secure companion for lifecycle and OS ledger for implementation/matrix/physical prerequisites. See backend integration for other RTOS admission probes; passing probes does not register or establish kernel support.

Automation and actual evidence

Behavior changes record real GoogleTest/GoogleMock RED before implementation, then GREEN/refactor. Mock external hardware, CPU/kernel boundaries or product scheduling, not implementations. Wait races use barriers, controlled clocks and explicit schedules rather than sleep duration.

Configuration/evidence tools use Python unittest. Hooks/CI execute fully discovered suites and reject zero tests, filtering, skip-only runs, stale JUnit and missing tools. Actual POSIX scheduling validates kernel integration; ARM compile/link/ELF proves ABI/resources, not MCU timing or physical context behavior.

tools/hil/os_acceptance.py provides OS49–55 unbound plans and strict physical-record validation. It reconstructs admission, binds actual ELF/config/CPU ABI/station/workload/instruments, then checks full register banks, MSP/task peaks, per-sample deadlines, lossless low-power events and real safe outputs. It does not run probes, serial ports, supplies or Flash; absent equipment leaves every physical item not_executed.

Kernel-upgrade and new-backend checklist

Pin kernel commit/tree and all used ports in separate branches, not just version strings. Review macros, TCB/queue ABI, initial frames, ISR gates, yield/critical behavior and return wrappers. M7 integer overlays recheck upstream SHA, expected replacement counts, incoming PRIMASK restoration and errata barriers; original vendor files remain auditable. FP/MVE save and coprocessor initialization need matching macros, real assembly/disassembly and eventual hardware switch records.

After all GoogleMock/context tests, real POSIX lifecycle, nine-core links, profile positive/negative paths and formal installed SDK checks pass, record new costs/current source identity. Old candidates, ELFs and physical reports do not qualify new kernels automatically. Products approve actual task budgets, timing/HIL, recovery and release scope.

New RTOS/backends must provide:

  • Explicit startup/running/suspended/task/IRQ contexts, scheduler-start responsibility and interrupt policy.

  • One-domain monotonic clocks, finite/infinite deadlines, arm/recheck/wait races and single-waiter contracts.

  • Exact per-object storage/alignment, zero-side-effect initialization failures and close/destroy conditions.

  • Returning/permanent task lifecycles, single joiners, timeout reference retention and storage-reuse proof.

  • Producer/owner stop and request-drain progress without forced deletion revoking borrows.

  • CPU ports/ABI/context save, actual dependencies, minimum resource reports and missing-hook link failures.

  • Real backend execution with the same GoogleTest contracts; mocks alone cannot verify new kernels.

  • Separate Native/model, ARM link and physical-station qualification without guessed Board wiring.

Error and recovery responsibilities

Result/status

Caller responsibility

BUSY

Retain storage; nonblocking callers continue explicit service/pump or retry under product policy.

TIMEOUT

Only this wait budget ends; unsettled operations/bytes cannot be released.

CONTEXT

Restore legal task/IRQ/mask/scheduler context first; do not retry kernel calls inside guards.

STATE

Check lifecycle, closure, stale tickets, receiver/index and sole ownership.

QUARANTINED

Retain all borrows; owners continue recovery/drain while products choose safe outputs/reset.

UNSUPPORTED

Select actual capabilities during configuration/integration; no silent substitute workers or modes.

The platform does not map statuses automatically to retries, restarts, Flash writes or safe-output restoration. Production policy uses product budgets, alarms/state machines and preserves raw failure evidence.