Skip to content

ARINC 653 (APEX Scheduler)

Status: ✅ Implemented

Time-partitioned scheduler with APEX process management, health monitoring, and Linux RT/Non-RT comparison demonstration.

Overview

ARINC 653 defines the APplication EXecutive (APEX) interface a standardized API for time and space partitioned real-time operating systems used in Integrated Modular Avionics (IMA). Our implementation demonstrates the core scheduling concepts on Linux, with a critical comparison between CFS (non-RT) and SCHED_FIFO (RT) scheduling to illustrate why real avionics systems require hard-real-time guarantees.

Key Facts

  • Standard: ARINC 653 Parts 1-4 (Supplement 5)
  • Concept: Time & space partitioning for safety-critical systems
  • Certification: Used in DO-178C DAL-A systems (highest criticality)
  • RTOS implementations: VxWorks 653, PikeOS, LynxOS-178, INTEGRITY-178
  • Our demo: Linux userspace simulation with RT scheduling comparison

Time Partitioning

Major Frame Architecture

The scheduler divides CPU time into a repeating major frame. Each partition receives one or more guaranteed windows within the major frame. No partition can exceed its allocated time the scheduler enforces preemption at window boundaries.

Major Frame = 100ms (repeats indefinitely)
│                                                                              │
├──────────┬───────────┬──────────────┬──────────┬──────────────────┬─────────┤
│  P1:FMS  │ P2:Engine │  P3:Display  │  P1:FMS  │    P4:Health     │  IDLE   │
│  20ms    │  15ms     │   25ms       │  20ms    │    15ms          │  5ms    │
├──────────┴───────────┴──────────────┴──────────┴──────────────────┴─────────┤
│ t=0      t=20       t=35           t=60       t=80              t=95   t=100 │

Partition 1 (Flight Management): 40ms total (two windows)
Partition 2 (Engine Control):    15ms total
Partition 3 (Display):           25ms total  
Partition 4 (Health Monitor):    15ms total
Idle/Guard:                       5ms total

Hard Temporal Isolation

If Partition 3 has a bug that causes an infinite loop, it is forcibly preempted at t=60ms. Partitions 1 and 4 still execute on schedule. This is the fundamental safety guarantee of ARINC 653.

Partition Window Scheduling

stateDiagram-v2
    [*] --> COLD_START
    COLD_START --> WARM_START: Initialization complete
    WARM_START --> NORMAL: SET_PARTITION_MODE(NORMAL)
    NORMAL --> NORMAL: Major frame repeats
    NORMAL --> IDLE: Partition window exhausted
    IDLE --> NORMAL: Next window starts

Space Partitioning

Memory Isolation

Each partition operates in an isolated memory region. The MMU (or MPU in smaller systems) enforces boundaries a wild pointer in one partition cannot corrupt another:

Physical Memory Map
┌────────────────────────────────────────────┐
│           Partition 1 (FMS)                 │  0x1000_0000
│  .text | .data | .bss | heap | stack       │
├────────────────────────────────────────────┤
│           GUARD PAGE (no access)            │  ← MMU fault on access
├────────────────────────────────────────────┤
│           Partition 2 (Engine)              │  0x1010_0000
│  .text | .data | .bss | heap | stack       │
├────────────────────────────────────────────┤
│           GUARD PAGE (no access)            │
├────────────────────────────────────────────┤
│           Partition 3 (Display)             │  0x1020_0000
│  .text | .data | .bss | heap | stack       │
├────────────────────────────────────────────┤
│        OS Kernel / Scheduler                │  0x2000_0000
│  (privileged access only)                  │
└────────────────────────────────────────────┘

Communication Channels

Partitions communicate only through APEX-managed ports (sampling and queuing). Direct memory sharing is prohibited this is enforced by the RTOS, not by convention.

APEX Services

Service Categories

// Process lifecycle
void CREATE_PROCESS(PROCESS_ATTRIBUTE_TYPE *attr, 
                    PROCESS_ID_TYPE *id, RETURN_CODE_TYPE *ret);
void START(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void STOP(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void SUSPEND(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void RESUME(PROCESS_ID_TYPE id, RETURN_CODE_TYPE *ret);
void SET_PRIORITY(PROCESS_ID_TYPE id, PRIORITY_TYPE prio, 
                  RETURN_CODE_TYPE *ret);
void GET_PROCESS_STATUS(PROCESS_ID_TYPE id, 
                        PROCESS_STATUS_TYPE *status, RETURN_CODE_TYPE *ret);
// Time services
void TIMED_WAIT(SYSTEM_TIME_TYPE delay, RETURN_CODE_TYPE *ret);
void PERIODIC_WAIT(RETURN_CODE_TYPE *ret);
void GET_TIME(SYSTEM_TIME_TYPE *time, RETURN_CODE_TYPE *ret);
void REPLENISH(SYSTEM_TIME_TYPE budget, RETURN_CODE_TYPE *ret);
// Partition control
void SET_PARTITION_MODE(OPERATING_MODE_TYPE mode, RETURN_CODE_TYPE *ret);
void GET_PARTITION_STATUS(PARTITION_STATUS_TYPE *status, 
                          RETURN_CODE_TYPE *ret);
// Sampling ports (latest-value semantics)
void CREATE_SAMPLING_PORT(SAMPLING_PORT_NAME_TYPE name,
                          MESSAGE_SIZE_TYPE size, PORT_DIRECTION_TYPE dir,
                          SYSTEM_TIME_TYPE refresh, 
                          SAMPLING_PORT_ID_TYPE *id, RETURN_CODE_TYPE *ret);
void WRITE_SAMPLING_MESSAGE(SAMPLING_PORT_ID_TYPE id,
                            MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE len,
                            RETURN_CODE_TYPE *ret);
void READ_SAMPLING_MESSAGE(SAMPLING_PORT_ID_TYPE id,
                           MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE *len,
                           VALIDITY_TYPE *valid, RETURN_CODE_TYPE *ret);

// Queuing ports (FIFO semantics)
void CREATE_QUEUING_PORT(QUEUING_PORT_NAME_TYPE name,
                         MESSAGE_SIZE_TYPE size, MESSAGE_RANGE_TYPE count,
                         PORT_DIRECTION_TYPE dir, QUEUING_DISCIPLINE_TYPE disc,
                         QUEUING_PORT_ID_TYPE *id, RETURN_CODE_TYPE *ret);
void SEND_QUEUING_MESSAGE(QUEUING_PORT_ID_TYPE id,
                          MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE len,
                          SYSTEM_TIME_TYPE timeout, RETURN_CODE_TYPE *ret);
void RECEIVE_QUEUING_MESSAGE(QUEUING_PORT_ID_TYPE id,
                             SYSTEM_TIME_TYPE timeout,
                             MESSAGE_ADDR_TYPE msg, MESSAGE_SIZE_TYPE *len,
                             RETURN_CODE_TYPE *ret);
// Error handling
void CREATE_ERROR_HANDLER(SYSTEM_ADDRESS_TYPE handler, 
                          STACK_SIZE_TYPE stack, RETURN_CODE_TYPE *ret);
void REPORT_APPLICATION_MESSAGE(MESSAGE_ADDR_TYPE msg, 
                                MESSAGE_SIZE_TYPE len, RETURN_CODE_TYPE *ret);
void GET_ERROR_STATUS(ERROR_STATUS_TYPE *status, RETURN_CODE_TYPE *ret);
void RAISE_APPLICATION_ERROR(ERROR_CODE_TYPE code, MESSAGE_ADDR_TYPE msg,
                             ERROR_MESSAGE_SIZE_TYPE len, 
                             RETURN_CODE_TYPE *ret);

Process State Machine

Within each partition, multiple processes (threads) can exist. The APEX scheduler manages them using priority-based preemptive scheduling within the partition's time window:

stateDiagram-v2
    [*] --> DORMANT: CREATE_PROCESS
    DORMANT --> READY: START
    READY --> RUNNING: Highest priority + dispatcher
    RUNNING --> READY: Preempted (higher priority ready)
    RUNNING --> WAITING: TIMED_WAIT / PERIODIC_WAIT / blocked on port
    WAITING --> READY: Timeout / event / message available
    RUNNING --> DORMANT: STOP / process completes
    WAITING --> DORMANT: STOP (forced)
    READY --> DORMANT: STOP (forced)
State Description
DORMANT Created but not yet started; no resources consumed
READY Eligible to run; waiting for dispatcher
RUNNING Currently executing on CPU (one per partition)
WAITING Blocked on time delay, periodic release, or IPC

Linux Non-RT vs RT Scheduling Comparison

Critical for Avionics Understanding

This section demonstrates why standard Linux scheduling cannot be used for safety-critical systems and how RT scheduling policies approach (but don't fully achieve) the determinism required by ARINC 653.

The Problem: Why CFS Fails for Avionics

Linux's default scheduler Completely Fair Scheduler (CFS) uses SCHED_OTHER policy with a fair-share algorithm. It distributes CPU time proportionally among all runnable tasks based on "virtual runtime." This is optimal for throughput and interactive responsiveness, but catastrophic for real-time deadlines:

Property CFS (SCHED_OTHER) SCHED_FIFO (RT)
Scheduling goal Fairness (equal CPU share) Priority (highest runs first)
Preemption Time-slice based, fair share Only by higher-priority RT task
Priority range Nice -20 to +19 (40 levels) 1–99 (fixed priority)
Jitter (measured) 648 µs max (our benchmark) < 100 µs max (our benchmark)
Deadline guarantee ❌ None ✅ Soft real-time
Priority inversion Possible (no ceiling) Mitigated with PI mutexes
Starvation Cannot happen (fair share) Lower-priority tasks may starve
Kernel preemption Voluntary/full preempt PREEMPT_RT: fully preemptible
Timer resolution 1–4 ms (tick-based) < 50 µs (hrtimers)
Context switch May be delayed by fairness Immediate on priority change
Use case Desktop, servers, containers Industrial control, audio, avionics sim

Benchmark Results (Our Demo)

Running the ARINC 653 scheduler simulation with a 10ms partition window:

=== ARINC 653 Scheduler Non-RT Mode (SCHED_OTHER) ===
Major frame: 100ms | Partitions: 4

Partition 1 [Flight Management] window: 20ms
  Measured jitter: min=12µs avg=89µs max=648µs
  Deadline violations: 3/100 windows (3.0%)

Partition 2 [Engine Control] window: 15ms  
  Measured jitter: min=8µs avg=76µs max=523µs
  Deadline violations: 2/100 windows (2.0%)

Partition 3 [Display] window: 25ms
  Measured jitter: min=15µs avg=102µs max=612µs
  Deadline violations: 4/100 windows (4.0%)

WORST-CASE JITTER: 648µs
TOTAL DEADLINE VIOLATIONS: 9/300 (3.0%)

CFS Cannot Guarantee Deadlines

Even at 3% violation rate, this is unacceptable for DAL-A avionics where the requirement is 0 violations per 10⁹ hours. The 648µs jitter spike occurs when:

  • Other CFS tasks consume their fair share during our window
  • Kernel work (softirqs, RCU) preempts our userspace task
  • Timer coalescing delays our wakeup
  • CPU frequency scaling changes execution speed
=== ARINC 653 Scheduler RT Mode (SCHED_FIFO, CPU 3 isolated) ===
Major frame: 100ms | Partitions: 4

Partition 1 [Flight Management] window: 20ms
  Measured jitter: min=2µs avg=8µs max=47µs
  Deadline violations: 0/100 windows (0.0%)

Partition 2 [Engine Control] window: 15ms
  Measured jitter: min=1µs avg=6µs max=38µs
  Deadline violations: 0/100 windows (0.0%)

Partition 3 [Display] window: 25ms
  Measured jitter: min=3µs avg=9µs max=52µs
  Deadline violations: 0/100 windows (0.0%)

WORST-CASE JITTER: 52µs
TOTAL DEADLINE VIOLATIONS: 0/300 (0.0%)

RT Scheduling Achieves Determinism

With SCHED_FIFO at priority 99 on an isolated CPU core:

  • 12.5× lower max jitter (52µs vs 648µs)
  • Zero deadline violations across 300 windows
  • Consistent sub-100µs timing precision

Why CFS Cannot Guarantee Deadlines

graph TD
    A[CFS Scheduler] --> B[Calculates virtual runtime<br>for ALL runnable tasks]
    B --> C{Who has lowest<br>vruntime?}
    C -->|Task A| D[Run Task A]
    C -->|Task B| E[Run Task B - OUR partition!]
    D --> F[Our partition must WAIT<br>for fair share rotation]
    F --> G[⚠️ DEADLINE MISSED]

    style G fill:#f44,stroke:#333,color:#fff
    style F fill:#fa0,stroke:#333

Root causes of CFS jitter:

  1. Fair-share scheduling: CFS distributes CPU proportionally. If 10 tasks are runnable, each gets ~10% our partition cannot demand "run me NOW"
  2. No priority ceiling: Nice values are hints, not guarantees. A nice -20 task still shares with nice 0 tasks
  3. Kernel preemption points: Standard kernels only preempt at specific points; long-running syscalls delay scheduling
  4. Timer coalescing: The kernel groups timer wakeups to save power, adding up to 4ms of delay
  5. CPU frequency scaling: If the governor lowers frequency during idle, ramp-up latency affects the next window

How SCHED_FIFO Achieves Determinism

graph TD
    A[SCHED_FIFO Scheduler] --> B[Check RT run queue<br>by strict priority]
    B --> C{Highest priority<br>RT task?}
    C -->|Priority 99<br>OUR partition| D[Run IMMEDIATELY]
    C -->|Priority 50<br>Other task| E[Preempt other task]
    D --> F[No sharing, no fairness<br>Run until done or preempted]
    E --> D
    F --> G[✅ DEADLINE MET]

    style G fill:#4a4,stroke:#333,color:#fff
    style D fill:#afa,stroke:#333

How RT scheduling achieves determinism:

  1. Strict priority: Priority 99 task runs unconditionally whenever it's runnable period
  2. No time-slicing: SCHED_FIFO tasks run until they voluntarily yield or are preempted by higher priority
  3. CPU isolation (isolcpus=3): Removes the core from CFS load balancing no other tasks migrate in
  4. Interrupt affinity: Move IRQs away from the isolated core (/proc/irq/*/smp_affinity)
  5. High-resolution timers: hrtimers provide sub-µs timer resolution vs. CFS tick-based scheduling

PREEMPT_RT Kernel Patch

For production-quality RT on Linux, the PREEMPT_RT patch transforms the kernel:

Feature Standard Kernel PREEMPT_RT Kernel
Spinlocks Non-preemptible Converted to RT-mutexes (preemptible)
Softirqs Run in interrupt context Threaded (schedulable)
Priority inheritance Partial Full PI across all locks
Interrupt handlers Run to completion Threaded (can be preempted)
Worst-case latency 1–10 ms < 100 µs (with tuning)
# Check if PREEMPT_RT is active
uname -a | grep -i rt
# or
cat /sys/kernel/realtime  # returns "1" if RT

Linux RT ≠ ARINC 653 RTOS

Even with PREEMPT_RT + SCHED_FIFO + CPU isolation, Linux is a soft real-time system. True ARINC 653 RTOS (VxWorks 653, PikeOS, INTEGRITY-178) provide:

  • DO-178C certified scheduling (formal verification)
  • Hardware MMU enforcement of partition boundaries
  • Guaranteed worst-case interrupt latency (< 10µs)
  • No kernel mode sharing between partitions

Our demo illustrates the concepts production avionics use certified RTOS.

Health Monitoring

Error Levels

Level Scope Response
Process Single process within partition Restart process, notify HM
Partition Entire partition Restart partition, log event
Module Entire computing module Reset module, trigger maintenance

Health Monitor Actions

graph LR
    A[Error Detected] --> B{Error Level}
    B -->|Process| C[Stop/Restart Process]
    B -->|Partition| D[Restart Partition<br>Cold/Warm Start]
    B -->|Module| E[Module Reset]
    C --> F[Log + Continue]
    D --> F
    E --> G[System-level Recovery]

Monitored Errors

Error Code Description Typical Response
DEADLINE_MISSED Process exceeded time budget Stop process
APPLICATION_ERROR Application-raised error Handler callback
NUMERIC_ERROR Division by zero, overflow Stop process
ILLEGAL_REQUEST Invalid APEX call Stop process
STACK_OVERFLOW Stack exceeded allocation Restart partition
MEMORY_VIOLATION Access outside partition Restart partition
HARDWARE_FAULT Detected hardware error Module reset

API Reference

Header: arinc653.h

#include "arinc653.h"

/* === Type Definitions === */

typedef int64_t  SYSTEM_TIME_TYPE;      // Nanoseconds
typedef uint32_t PROCESS_ID_TYPE;
typedef uint32_t PRIORITY_TYPE;         // 1 (lowest) to 63 (highest)
typedef char     PROCESS_NAME_TYPE[32];

typedef enum {
    IDLE = 0,
    COLD_START = 1,
    WARM_START = 2,
    NORMAL = 3
} OPERATING_MODE_TYPE;

typedef enum {
    DORMANT = 0,
    READY = 1,
    RUNNING = 2,
    WAITING = 3
} PROCESS_STATE_TYPE;

typedef enum {
    SOFT = 0,
    HARD = 1
} DEADLINE_TYPE;

typedef enum {
    NO_ERROR = 0,
    NO_ACTION = 1,
    NOT_AVAILABLE = 2,
    INVALID_PARAM = 3,
    INVALID_CONFIG = 4,
    INVALID_MODE = 5,
    TIMED_OUT = 6
} RETURN_CODE_TYPE;

typedef struct {
    PROCESS_NAME_TYPE  name;
    SYSTEM_ADDRESS_TYPE entry_point;
    STACK_SIZE_TYPE    stack_size;
    PRIORITY_TYPE      base_priority;
    SYSTEM_TIME_TYPE   period;          // 0 for aperiodic
    SYSTEM_TIME_TYPE   time_capacity;   // WCET budget
    DEADLINE_TYPE      deadline;
} PROCESS_ATTRIBUTE_TYPE;

/* === Partition Schedule Configuration === */

typedef struct {
    const char       *name;
    SYSTEM_TIME_TYPE  duration;    // Window duration in nanoseconds
    uint8_t           partition_id;
} partition_window_t;

typedef struct {
    partition_window_t *windows;
    size_t              window_count;
    SYSTEM_TIME_TYPE    major_frame_duration;
} schedule_config_t;

Scheduler Functions

// Initialize the ARINC 653 scheduler
void apex_scheduler_init(const schedule_config_t *config);

// Start the major frame cycle (blocks runs the scheduler loop)
void apex_scheduler_run(void);

// Stop the scheduler
void apex_scheduler_stop(void);

// Get elapsed time within current major frame
SYSTEM_TIME_TYPE apex_get_frame_time(void);

Build & Run

Default (Non-RT Mode)

make -C src/aerospace/arinc653
./build/arinc653_demo

RT Mode (requires root or CAP_SYS_NICE)

make -C src/aerospace/arinc653
sudo ./build/arinc653_demo --rt

RT Mode with CPU Pinning

# Isolate CPU 3 (add to kernel boot params: isolcpus=3)
sudo ./build/arinc653_demo --rt --cpu 3

Command-Line Options

Flag Description
(none) Run with SCHED_OTHER (CFS) shows jitter issues
--rt Run with SCHED_FIFO priority 99
--cpu N Pin scheduler to CPU N (combine with --rt)
--frames N Number of major frames to execute (default: 100)
--verbose Print per-window timing details

Example Output (Default Non-RT)

=== ARINC 653 APEX Scheduler Demo ===
Mode: Non-RT (SCHED_OTHER / CFS)
Major frame: 100ms | Windows: 5 | Partitions: 4

Schedule:
  [0-20ms]   Partition 1: Flight Management System
  [20-35ms]  Partition 2: Engine Control (FADEC)
  [35-60ms]  Partition 3: Display System
  [60-80ms]  Partition 1: Flight Management System (2nd window)
  [80-95ms]  Partition 4: Health Monitor
  [95-100ms] IDLE (guard band)

Running 100 major frames...

--- Partition Timing Report ---
  P1 (FMS):     avg=19.91ms  jitter: min=12µs max=648µs  violations=3
  P2 (Engine):  avg=14.92ms  jitter: min=8µs  max=523µs  violations=2
  P3 (Display): avg=24.89ms  jitter: min=15µs max=612µs  violations=4
  P4 (Health):  avg=14.94ms  jitter: min=9µs  max=487µs  violations=1

⚠ WARNING: 10 deadline violations detected!
  CFS cannot guarantee temporal isolation.
  Use --rt flag for SCHED_FIFO comparison.

--- Process State Transitions ---
  P1/nav_compute: DORMANT→READY→RUNNING→WAITING→READY→RUNNING (periodic)
  P2/fuel_calc:   DORMANT→READY→RUNNING→WAITING (completed in window)

=== RESULTS: 24 passed, 0 failed ===
=== ALL TESTS PASSED ===

Example Output (RT Mode)

=== ARINC 653 APEX Scheduler Demo ===
Mode: RT (SCHED_FIFO priority 99, CPU 3 isolated)
Major frame: 100ms | Windows: 5 | Partitions: 4

[RT setup] Setting SCHED_FIFO priority 99... OK
[RT setup] Pinning to CPU 3... OK
[RT setup] Locking memory (mlockall)... OK

Running 100 major frames...

--- Partition Timing Report ---
  P1 (FMS):     avg=20.00ms  jitter: min=2µs  max=47µs   violations=0
  P2 (Engine):  avg=15.00ms  jitter: min=1µs  max=38µs   violations=0
  P3 (Display): avg=25.00ms  jitter: min=3µs  max=52µs   violations=0
  P4 (Health):  avg=15.00ms  jitter: min=2µs  max=41µs   violations=0

✓ Zero deadline violations (RT mode with CPU isolation)
  Max jitter: 52µs (vs 648µs in CFS mode) 12.5× improvement

=== RESULTS: 24 passed, 0 failed ===
=== ALL TESTS PASSED ===

References

  • ARINC Specification 653P1-5 (2019): Required Services
  • ARINC Specification 653P2-4: Extended Services
  • DO-178C: Software Considerations in Airborne Systems
  • Wind River VxWorks 653 Platform Guide
  • "Real-Time Systems Design and Analysis" Laplante & Ovaska