feat(firmware): validate configuration and migrate sensor-driven flight APIs
Validate configuration through its owning schemas and keep board declarations separate from shared F722 resource ownership. Bound USB transitions, isolate transport failures and retain restart-only activation. Migrate navigation and flight consumers with the sensor and schema contracts. Require explicit installation data and synchronous output authority; board motor outputs remain inhibited. Validation: assembled-prefix host tests, strict Clippy, platform/USB tests and both ARM release builds. Final-source coverage and scoped Miri/Kani evidence are retained separately from hardware qualification.
This commit is contained in:
1 parent
fb1b8af2d1
commit
f9a61a086f
211 files changed
+45657
-12740
No files matched your search
+153
-55
@@ -12,22 +12,72 @@ F722 platform / RTIC application ──► data channel ──► MAVLink teleme
|
||||
├── physical sensor drivers
|
||||
└── startup motor-pin inhibition
|
||||
|
||||
portable libraries
|
||||
math ─► navigation ─► control ─► safety
|
||||
Portable flight libraries (not connected to physical actuators)
|
||||
FlightEndpoint ──► Navigation / control / phases / safety
|
||||
│
|
||||
└── caller-owned synchronous output sink
|
||||
```
|
||||
|
||||
Each F722 board contract pairs a board-owned `UserConfig` schema and explicit
|
||||
`FACTORY_USER_CONFIG` with a board-owned `ImmutableConfig`. The immutable root
|
||||
is one literal typestate manifest and the sole source of output inhibition,
|
||||
physical routes, driver setup, sensor mount, bootstrap line rates, status LED,
|
||||
image role, and service frequency. The user root contains only settings
|
||||
consumed by that compiled driver graph.
|
||||
Each F722 board combines `F722UserConfig` with an immutable hardware manifest.
|
||||
The manifest selects the IMU route, driver settings, mount, serial inventory,
|
||||
status LED, optional I²C sensor, motor pins and service frequency. User settings
|
||||
select UART roles, baud and MAVLink settings. Motor pins are inhibited at startup.
|
||||
|
||||
The data channel owns rate selection, mailbox consumption, message encoding,
|
||||
and bounded TX classification. Board code supplies physical routes, target
|
||||
schema, and factory values; it does not receive generic output authority.
|
||||
`F722ImmutableConfig<R>` retains the IMU route type through initialization and
|
||||
interrupt acknowledgement. The sealed `Mpu6000Spi1` and `Icm42688Spi3` routes
|
||||
select IMU wiring and backend independently of serial inventory and LED wiring.
|
||||
|
||||
Configuration and runtime observability are separate planes:
|
||||
Each board declares one `PortCatalog<N>::DESCRIPTORS` array. Both
|
||||
`Ports<Catalog, N>` and `SerialInventory::new(&Catalog::DESCRIPTORS)` use it for
|
||||
stable UART/field identities, supported roles, baud allowlists and resource
|
||||
claims. Validation produces a boot-only `PortPlan<N>`. The same const `N` sizes
|
||||
I/O and protocol storage: five UARTs on each current board. Absent UARTs
|
||||
have no slots. A declared UART set to Disabled retains its slot but initializes
|
||||
no peripheral or pins. USB is fixed normal MAVLink outside that inventory,
|
||||
separate from ROM DFU.
|
||||
|
||||
Each MAVLink `Link` has its own scheduler, encoder and receive framing, with a
|
||||
separate transport queue. A shared collector snapshots sensor mailboxes for all
|
||||
links. See [MAVLink](mavlink.md) for scheduling and transport behavior.
|
||||
|
||||
`kaptan-flight-runtime::FlightEndpoint` runs Quad-X control and
|
||||
`kaptan-navigation::Navigation` without allocation. Callers supply
|
||||
observations, independently captured receipt/processing time, clock ticks and a
|
||||
synchronous output sink. Transport and wire encoding stay outside the core.
|
||||
The board images do not yet construct a flight endpoint.
|
||||
See [Safety boundary](safety-boundary.md#shared-runtime-commit-contract) for
|
||||
epochs, request sequencing and the output commit contract.
|
||||
|
||||
`FlightEndpoint::supervise` checks actual time, cached health and phase/stream
|
||||
deadlines without advancing navigation, integrating control or consuming a
|
||||
control release. It can inhibit and publish outcomes, but cannot renew enabled
|
||||
output. Callers must service the endpoint with actual time even when input
|
||||
stops. The core owns control release and deadline decisions.
|
||||
|
||||
Within `FlightEndpoint`, fusion, current prediction and initialization completion
|
||||
run only when the 100-Hz control service is due. Early `tick` calls supervise
|
||||
cached evidence without advancing navigation. Ingress admission and its
|
||||
diagnostics remain immediate, but receiving or queuing fresh observations alone
|
||||
does not renew capabilities. The actual control gap is checked before a due
|
||||
navigation update can refresh evidence.
|
||||
|
||||
`Navigation` initializes from a stationary window of IMU, fixed-reference local
|
||||
position/velocity, magnetic, pressure and range observations. It derives attitude
|
||||
and a fixed launch origin from sensors, with explicit calibration/reference
|
||||
identities. Its shared 16-state error covariance covers position, velocity,
|
||||
attitude error, gyro bias, accelerometer bias and barometric datum. Bounded
|
||||
delayed fusion and current prediction preserve observation timing and capability
|
||||
evidence.
|
||||
|
||||
The navigation `sensor-snapshots` feature enables driver snapshot conversion.
|
||||
The same flight estimator is available on host and embedded targets.
|
||||
|
||||
Navigation owns the 25-m/s² free-flight acceleration bound checked by
|
||||
complete-profile validation. See
|
||||
[Configuration](configuration.md#flight-configuration) for required installation
|
||||
data and the supported vehicle model.
|
||||
|
||||
Configuration and diagnostics use separate paths:
|
||||
|
||||
```text
|
||||
FILE_CONTROL / FILE_DATA
|
||||
@@ -42,50 +92,97 @@ kaptan-file-mavlink ──► virtual FileProvider
|
||||
kaptan-log / kaptan-err-reportable
|
||||
│
|
||||
▼
|
||||
kaptan-diagnostics-mavlink ──► platform-owned encoder / DMA TX
|
||||
kaptan-diagnostics-mavlink ──► per-link encoder / bounded TX
|
||||
```
|
||||
|
||||
The file dialect is intentionally Kaptan-native rather than standard MAVLink
|
||||
FTP, `PARAM_*`, or a QGC adapter. Its two messages know only bounded paths,
|
||||
bytes, positions, checksums, transfer ownership, and retries. The configuration
|
||||
provider maps `/config/user.kcfg` to a canonical self-describing artifact.
|
||||
KCFG carries generated metadata and a complete committed value set; the target
|
||||
streams it without retaining a whole-file buffer. Saving imports and validates
|
||||
one complete replacement candidate. The core distinguishes only immutable
|
||||
boot-active values from the latest committed values. A save cannot mutate
|
||||
controller or estimator state in flight.
|
||||
The Kaptan-native file dialect owns bounded transfers and retries across links.
|
||||
Its provider maps saved, active and factory-reset endpoints to streaming KCFG v3
|
||||
and the generic configuration lifecycle.
|
||||
|
||||
`ConfigManager` owns boot-active values, known committed values or unknown durable
|
||||
state, and the factory candidate. F722 supplies its UART-disabled setup fallback
|
||||
through `recover_with`. Readable invalid settings remain inspectable for USB
|
||||
repair. Saving or factory reset never changes active consumers until reboot.
|
||||
|
||||
Target-owned stores use private persistence formats. F722 alternates compact
|
||||
`KCSP` snapshots between two erase sectors, retaining only the old and
|
||||
replacement records across the commit boundary. That format is not exposed as the public KCFG file.
|
||||
Motor allocation/mixer topology is immutable compiled vehicle capability data,
|
||||
never a scalar field or persistence member. F722 recovers operational settings
|
||||
before constructing consumers. Its local USB CDC-ACM and directly wired UART
|
||||
accept only exact-target, CRC-valid Kaptan file requests while physical outputs
|
||||
remain inhibited. CRC is integrity-only; a shared or remote GCS still needs
|
||||
production authentication, source binding, and persistent anti-replay state.
|
||||
Disarmed activation and flight-consumer reconstruction remain separate reviewed
|
||||
work.
|
||||
`KCSP` snapshots between two erase sectors, retaining only the old and replacement
|
||||
records across the commit boundary. That format is not exposed as KCFG. Motor
|
||||
allocation/mixer topology remains compiled vehicle capability data.
|
||||
|
||||
The MAVLink dialect root directly includes one-message XML leaves under
|
||||
`publish/` and `transfer/`. The `exchange/` contract remains empty until a
|
||||
current consumer justifies a message. There are no category index XML files or
|
||||
separate enum catalogs. The build watches the definitions tree and generates
|
||||
one `generated::kaptan` module; leaves are never separately generated Rust
|
||||
modules. `kaptan-log` itself has
|
||||
no MAVLink, platform, time, allocation, or filesystem dependency; its sole
|
||||
configuration coupling is compile-time enum metadata for `LevelFilter`.
|
||||
`kaptan-err-reportable` independently owns explicitly assigned reportable-error
|
||||
identity and a bounded nonblocking ingress; it has no logging, MAVLink,
|
||||
platform, filesystem, or clock dependency. Event creation is independent from
|
||||
sink filtering, allowing a future blackbox and MAVLink to select different
|
||||
policies. The platform drains reportable errors before its bounded critical and
|
||||
best-effort log queues and removes a pending record only after TX admission.
|
||||
`LOG` carries one conventional level plus one bounded text field; `ERROR`
|
||||
carries an explicitly assigned 16-bit ID and the bounded `file:line` of the
|
||||
reporting point. Its message kind already identifies a reportable firmware
|
||||
error. Kaptan-native heartbeat and sensor publications remain separate from
|
||||
diagnostics.
|
||||
## I/O ownership
|
||||
|
||||
Sensor handoff uses owned `LatestMailbox<T>` storage split into exactly one
|
||||
non-cloneable producer and one non-cloneable consumer. Publishing replaces an
|
||||
unread value, and taking removes it at most once. Each operation copies or takes
|
||||
an `Option<T>` inside the platform critical section. No callback, allocation,
|
||||
retry or wait occurs inside that section. Storage stays borrowed while endpoints
|
||||
are in use, and moving, dropping or forgetting an endpoint holds no lock. The
|
||||
backend must exclude every participating context, using Cortex-M single-core exclusion on
|
||||
F722 and the `critical-section/std` backend for host threads.
|
||||
|
||||
UART TX and RX are interrupt-driven, without UART DMA. Each I/O owner retains
|
||||
the peripheral, TX queue and a 512-byte RX buffer with a typed `Timestamp` per
|
||||
byte captured at IRQ receipt. The worker takes at most 16 bytes per UART per
|
||||
turn under a short lock. Protocol parsing, scheduling and persistence run
|
||||
outside UART locks. Overflow or a hardware RX fault discards the damaged backlog
|
||||
and resets the decoder before later bytes. These bounds do not establish a
|
||||
measured latency guarantee.
|
||||
|
||||
`kaptan-tx::TransmitQueue` owns bounded admission and one logically active frame.
|
||||
Pending admission cannot replace that active frame. The queue itself is movable
|
||||
and does not promise DMA address stability. UART byte writes and USB endpoint
|
||||
writes borrow or copy its bytes synchronously. MAVLink owns encoding, while
|
||||
platform I/O owners handle transmission.
|
||||
|
||||
USB uses the repository-patched Synopsys driver. Runtime register waits allow
|
||||
higher-priority interrupts and stop after 5 ms or 1,080,000 polls per wait. These
|
||||
are fault limits, not measured execution times. A fault holds USB in peripheral
|
||||
reset until restart and discards its queued data. Brief critical sections protect
|
||||
USB ownership and endpoint buffers. Boot initialization still runs under RTIC's
|
||||
startup interrupt mask.
|
||||
|
||||
SPI1 and SPI3 DMA each use a once-claimed static pair of 32-byte TX/RX buffers.
|
||||
`start_transfer` copies in, so hardware retains no caller slice. Completion
|
||||
copies out only after checking stream disable, SPI quiescence, transfer counts
|
||||
and faults. Failure to establish shutdown or drain RX quarantines the storage
|
||||
and prevents reuse. Moving, dropping or forgetting the owner never releases
|
||||
that claim. The F722 implementation relies on DMA-accessible SRAM, disabled
|
||||
D-cache and device barriers. Physical SPI shutdown, timing and fault behavior
|
||||
still require hardware validation.
|
||||
|
||||
## Monotonic time
|
||||
|
||||
TIM2 is the canonical F722 clock. The configured 108-MHz timer input and PSC=107
|
||||
produce a free-running 32-bit counter at 1 MHz with ARR=`u32::MAX`. All readers
|
||||
and the CC1 service IRQ extend one shared `u64` microsecond epoch inside the same
|
||||
critical section. A consumer may remain silent across wraps if other readers
|
||||
or IRQ service keep extending that epoch. Combined observations must be strictly
|
||||
less than 2^32 microseconds apart, about 71.58 minutes, including interrupt
|
||||
blackouts and debugger halts. NMI and fault handlers must not access this clock.
|
||||
|
||||
CC1 schedules the current 100-Hz service at epoch-aligned 10-ms deadlines.
|
||||
Delayed service skips missed periods in constant work and emits at most one
|
||||
notification per IRQ. Rearming uses two bounded attempts before pending a
|
||||
recovery invocation. Callbacks and notifications run after clock exclusion ends.
|
||||
TIM2 is not reset for each service tick, and no update IRQ or timer DMA is used.
|
||||
Its IRQ is enabled only at the successful end of initialization.
|
||||
|
||||
Ordinary WFI retains the timer clock. Deep sleep, STOP/Standby, clock-tree changes,
|
||||
counter reset/regression and debugger halts spanning a full wrap are unsupported
|
||||
and require platform reset. Uptime must leave one service period before `u64`
|
||||
microsecond overflow. DWT is used only for the CPU-running SRAM flash-poll timeout
|
||||
at 216 MHz, not sensor timestamps or service deadlines. These contracts do not
|
||||
establish measured oscillator accuracy, IRQ latency, jitter or worst-case timing.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
`kaptan-log` has no MAVLink, platform, time, allocation or filesystem dependency.
|
||||
Its configuration coupling is compile-time enum metadata for `LevelFilter`.
|
||||
`kaptan-err-reportable` independently owns explicit error identities and a bounded
|
||||
nonblocking ingress, without logging, transport or clock dependencies.
|
||||
Event creation is independent from sink filtering. The worker drains reportable
|
||||
errors before logs and offers each diagnostic to all MAVLink links. Heartbeat
|
||||
and sensor publications use separate scheduling.
|
||||
|
||||
See [Configuration](configuration.md) for KCFG and the file-transfer contract,
|
||||
[Logs](logs.md) for the receiver contract, and [Errors](errors.md)
|
||||
@@ -96,7 +193,8 @@ for derived-error and failure-reporting scope.
|
||||
- A peripheral owner is local to the platform runtime.
|
||||
- Portable crates use typed units and validated values rather than raw wire or
|
||||
timer representations.
|
||||
- A safety permit is epoch-bound and must be revalidated at the only nonzero
|
||||
output boundary.
|
||||
- A failed observation or numerical check does not partially commit estimator,
|
||||
controller, allocator, or safety state.
|
||||
- `SafetySupervisor::evaluate_and_commit` checks current evidence at the
|
||||
synchronous enabled-output boundary. No transferable permit escapes it.
|
||||
- Rejected numerical updates do not partially commit computational candidates.
|
||||
Fault latches, inhibition and ingress replay watermarks follow their explicit
|
||||
failure contracts rather than rolling back authority or replay history.
|
||||
Reference in new issue
Block a user