- HARD_REALTIME_EVALUATION.md: full HRT audit - MICROKERNEL_*.md: complete architecture targets and implementation plan - PIKEOS_3LAYER_REPLICATION_PLAN.md: 3-layer replication strategy - PIKEOS_POSIX_AUDIT.md: POSIX compliance audit - RTOS_AUDIT.md: RTOS comparison - XTENSA_AUDIT.md: Xtensa ISA audit - BIBLIOGRAPHY_SAFETY_CRITICAL_HYPERVISOR.md: references
11 KiB
11 KiB
Xtensa/ESP32 Integration Audit — UniversalisOS
ESP-IDF Boot Sequence (Complete)
┌─────────────────────────────────────────────────────────────┐
│ 1. ROM Bootloader (0x40000400) │
│ - Reads flash header │
│ - Loads 2nd stage bootloader to IRAM │
│ - Jumps to 2nd stage entry (0x40080644) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 2nd Stage Bootloader (0x40080400) │
│ - Configures cache, clocks, flash │
│ - Reads partition table │
│ - Finds "factory" app at offset 0x10000 │
│ - Loads app segments to IRAM/DRAM │
│ - Jumps to app entry (0x40080000) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. start_cpu0() — Entry Point │
│ - do_core_init() → core init handlers │
│ - __libc_init_array() → C constructors │
│ - do_secondary_init() → secondary handlers │
│ - esp_startup_start_app() → FreeRTOS │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. esp_startup_start_app() │
│ - esp_int_wdt_init() → interrupt watchdog │
│ - esp_crosscore_int_init() → cross-core interrupts │
│ - xTaskCreatePinnedToCore(main_task) → create main task │
│ - vTaskStartScheduler() → start FreeRTOS │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. xPortStartScheduler() — FreeRTOS Port │
│ - portDISABLE_INTERRUPTS() │
│ - _xt_coproc_init() → co-processor setup │
│ - vPortSetupTimer() → configure CCOUNT or SYSTIMER │
│ - port_xSchedulerRunning[core] = 1 │
│ - xthal_window_spill() → clear window registers │
│ - _frxt_dispatch → first context switch │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 6. First Task Execution │
│ - main_task() runs │
│ - User's main() is called │
└─────────────────────────────────────────────────────────────┘
Xtensa Context Frame (XtExcFrame)
From xtensa_context.h:
STRUCT_FIELD(long, 4, XT_STK_EXIT, exit) /* exit point for dispatch */
STRUCT_FIELD(long, 4, XT_STK_PC, pc) /* return PC */
STRUCT_FIELD(long, 4, XT_STK_PS, ps) /* return PS */
STRUCT_FIELD(long, 4, XT_STK_A0, a0)
STRUCT_FIELD(long, 4, XT_STK_A1, a1) /* stack pointer */
STRUCT_FIELD(long, 4, XT_STK_A2, a2)
STRUCT_FIELD(long, 4, XT_STK_A3, a3)
STRUCT_FIELD(long, 4, XT_STK_A4, a4)
STRUCT_FIELD(long, 4, XT_STK_A5, a5)
STRUCT_FIELD(long, 4, XT_STK_A6, a6)
STRUCT_FIELD(long, 4, XT_STK_A7, a7)
STRUCT_FIELD(long, 4, XT_STK_A8, a8)
STRUCT_FIELD(long, 4, XT_STK_A9, a9)
STRUCT_FIELD(long, 4, XT_STK_A10, a10)
STRUCT_FIELD(long, 4, XT_STK_A11, a11)
STRUCT_FIELD(long, 4, XT_STK_A12, a12)
STRUCT_FIELD(long, 4, XT_STK_A13, a13)
STRUCT_FIELD(long, 4, XT_STK_A14, a14)
STRUCT_FIELD(long, 4, XT_STK_A15, a15)
STRUCT_FIELD(long, 4, XT_STK_SAR, sar)
STRUCT_FIELD(long, 4, XT_STK_EXCCAUSE, exccause)
STRUCT_FIELD(long, 4, XT_STK_EXCVADDR, excvaddr)
/* For CALL0 ABI: exit = _xt_user_exit, PS = PS_UM | PS_EXCM */
Stack Frame Layout (for CALL0 ABI)
HIGH ADDRESS
┌─────────────────────────┐
│ Extra Storage (CPSA) │ ← XT_STK_EXTRA
├─────────────────────────┤
│ XtExcFrame (interrupt │ ← Task's SP points here
│ frame on stack) │
│ [+0] exit = _xt_user_exit
│ [+4] pc = task entry
│ [+8] ps = PS_UM | PS_EXCM
│ [+12] a0 = 0
│ [+16] a1 = SP + XT_STK_FRMSZ
│ [+20] a2 = pvParameters
│ ...
│ [+76] a15
│ [+80] sar
│ [+84] exccause
│ [+88] excvaddr
├─────────────────────────┤
│ Base Save Area │
├─────────────────────────┤
│ Task Stack │
└─────────────────────────┘
LOW ADDRESS
What We Got Wrong
1. Stack Frame Layout
ESP-IDF uses XtExcFrame structure with exit field pointing to _xt_user_exit
frame->exit = _xt_user_exit(exception exit dispatcher)frame->pc = task entry pointframe->ps = PS_UM | PS_EXCM(user mode, EXCM disabled)frame->a1 = SP + XT_STK_FRMSZ(top of stack frame)frame->a2 = pvParameters(task argument for CALL0)
Our code uses simple 16-word frame without exit field
2. Interrupt Handler Flow
ESP-IDF flow:
Hardware interrupt → _xt_int_enter → saves context →
calls C handler → _xt_int_exit → checks for reschedule →
restores context → rfe
Our code:
Hardware interrupt → saves context → calls handler →
restores context → rfe
Missing: _xt_context_save/restore, interrupt nesting tracking, reschedule check
3. First Dispatch
ESP-IDF: xPortStartScheduler() → _frxt_dispatch (assembly)
- Sets
port_xSchedulerRunning[core] = 1 - Calls
_frxt_dispatchwhich does the first context switch
Our code: uos_sched_start() → uos_port_dispatch_first() (C function)
- Simpler but missing: window spill, co-processor init, interrupt nesting
4. Timer Configuration
ESP-IDF uses two options:
- CCOUNT (core timer) —
CONFIG_FREERTOS_SYSTICK_USES_CCOUNT- Uses internal timer 0 or 1
_frxt_tick_timer_init()sets up CCOMPARE0- Interrupt at level 1
- SYSTIMER —
CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER- Uses SYSTIMER peripheral
SysTickIsrHandler()handles the interrupt- Periodic mode with alarm
Our code: Direct CCOUNT manipulation
- Missing: interrupt allocation via
esp_intr_alloc() - Missing: proper timer interrupt handler
UniversalisOS Integration Strategy
Replace Points
| ESP-IDF Component | Our Equivalent | Status |
|---|---|---|
app_startup.c |
esp32_integration.c |
✅ Created |
startup.c |
startup.S |
⚠️ Needs fix |
port.c |
uos_port_context.S |
⚠️ Needs fix |
portasm.S |
esp32_vectors.S |
⚠️ Needs fix |
port_systick.c |
esp32_timer.c |
❌ Not created |
What Needs to Change
- Stack frame must include
exitfield — This is how FreeRTOS returns from exception - Interrupt handlers need
_xt_context_save/restore— Save/restore all registers properly - Timer must use
esp_intr_alloc()— Proper interrupt allocation - First dispatch must use
_frxt_dispatch— Assembly context switch, not C function - VECBASE must be set early — Before any interrupts fire
Key Functions to Implement
/* Our integration layer needs: */
void esp32_app_entry(void) {
/* 1. Set VECBASE (must be first!) */
/* 2. Disable WDT */
/* 3. Configure UART for debug */
/* 4. Init interrupt controller */
/* 5. Init timer (CCOUNT or SYSTIMER) */
/* 6. Call uos_init() */
/* 7. Create tasks */
/* 8. Start scheduler */
}
/* Context switch needs: */
void _frxt_dispatch(void); /* First dispatch — assembly */
void _frxt_int_enter(void); /* Interrupt entry — assembly */
void _frxt_int_exit(void); /* Interrupt exit — assembly */
/* Timer needs: */
void vPortSetupTimer(void); /* Configure CCOUNT/SYSTIMER */
void _frxt_tick_timer_init(void); /* CCOUNT timer init */
Files to Study Further
components/esp_system/startup.c— Boot sequence ✅ Studiedcomponents/freertos/app_startup.c— FreeRTOS startup ✅ Studiedcomponents/freertos/port_systick.c— Timer config ✅ Studiedcomponents/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c— Port layer ✅ Studiedcomponents/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/portasm.S— Context switch ✅ Studiedcomponents/xtensa/include/xtensa_context.h— Context frame ✅ Studiedcomponents/xtensa/— Xtensa HAL and runtimecomponents/bootloader/— 2nd stage bootloader
Next Steps
- Fix stack frame to include
exitfield - Implement proper interrupt handlers with
_xt_context_save/restore - Implement
_frxt_dispatchfor first context switch - Implement
vPortSetupTimerfor proper timer configuration - Test on ESP32 QEMU with flash image approach