All uncommitted work from ESP32 GDB stub development session. Includes: - GDB stub (uos_gdbstub.c/.h/_entry.S) - Startup vector table rewrite - ESP32 HAL integration files - Boot chain design docs - AGENTS.md absolute rules (commit before refactor, no unauthorized changes) - All prior deepseek session work This commit prevents further data loss. No claims of correctness.
20 KiB
ESP-IDF Hardware Init Sequence Audit (ESP32 → QEMU)
For Bare-Metal UniversalisOS CCOMPARE Timer Replication
CRITICAL FINDING: Timer Architecture
ESP32 on QEMU uses the Xtensa CCOUNT/CCOMPARE cycle-counter timer for the FreeRTOS tick, NOT an external hardware timer peripheral. The CCOMPARE timer is entirely internal to the Xtensa core — it has no MMIO register address. It is configured via the wsr/rsr instructions on special registers.
ESP32 Timer Mapping (from core-isa.h)
XCHAL_NUM_TIMERS = 3
CCOMPARE0 → Interrupt 6 (level 1) ← default tick timer when CONFIG_FREERTOS_CORETIMER_0
CCOMPARE1 → Interrupt 15 (level 3) ← alternative when CONFIG_FREERTOS_CORETIMER_1
CCOMPARE2 → Interrupt 16 (level 5)
XCHAL_EXCM_LEVEL = 3 (level masked by PS.EXCM)
Key: XT_CCOMPARE and XT_TIMER_INTEN values
From xtensa_timer.h:
#define XT_CCOMPARE (CCOMPARE + XT_TIMER_INDEX) // CCOMPARE0=0x240, CCOMPARE1=0x241, CCOMPARE2=0x242
#define XT_TIMER_INTNUM XCHAL_TIMER_INTERRUPT(XT_TIMER_INDEX)
#define XT_TIMER_INTEN (1 << XT_TIMER_INTNUM) // For CCOMPARE0: (1<<6) = 0x40
When CONFIG_FREERTOS_CORETIMER_0 (default for ESP32):
XT_TIMER_INDEX = 0XT_CCOMPARE = CCOMPARE0(SR 0x240)XT_TIMER_INTNUM = 6(interrupt bit 6)XT_TIMER_INTEN = 0x00000040
QEMU Timer Model (How It Actually Works)
QEMU implements CCOMPARE entirely in the Xtensa CPU core model (target/xtensa/op_helper.c, pic_cpu.c):
-
CCOUNT (SR 0x234) is derived from QEMU's virtual clock:
CCOUNT = ccount_base + clock_ns_to_ticks(now - time_base) -
Writing CCOMPAREn (
update_ccompare()):- Clears the timer interrupt bit in INTSET
- Schedules a QEMU timer:
timer_mod(ccompare[i].timer, ccount_time + ns_until_match) - The
dcc=CCOMPARE[i] - CCOUNT - 1 + 1cycles until match
-
Timer fires (
xtensa_ccompare_cb()):- Sets
env->irq_inputs[timerint[i]]active → bit in INTSET check_interrupts()checks INTSET & INTENABLE against current cintlevel
- Sets
-
Interrupt delivery:
QEMU'scheck_interrupts()(inpic_cpu.c) checks:
int_set_enabled = INTSET & (INTENABLE | NMI_mask)
If any bit inint_set_enabledis at a level above current cintlevel →cpu_interrupt(CPU_INTERRUPT_HARD)
QEMU does NOT require any external device (INTMATRIX, DPORT) to be configured for the CCOMPARE timer to work. The CCOMPARE timer fires through the internal Xtensa interrupt path, completely independent of the ESP32 interrupt matrix.
ORDERED INIT SEQUENCE (Bootloader → Scheduler Start)
PHASE 1: 2nd-Stage Bootloader (bootloader_init())
1.1 MEMCTL (Xtensa SR)
# File: bootloader_esp32.c:170
wsr MEMCTL, XCHAL_CACHE_MEMCTL_DEFAULT # Only if XCHAL_ERRATUM_572
1.2 Clock Configuration (bootloader_clock_configure())
// File: bootloader_clock_init.c:27
// Calls rtc_clk_init() with CPU at 80MHz (CPU_CLK_FREQ_MHZ_BTLD)
// In rtc_clk_init() (rtc_clk_init.c:31):
// If currently on PLL, switch to XTAL first:
rtc_clk_cpu_freq_to_xtal(40, 1);
// Set SCK_DCAP and CK8M_DFREQ tuning values:
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap);
// RTC_CNTL_REG = 0x3FF48000 + 0x007C = 0x3FF4807C
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq);
// RTC_CNTL_CLK_CONF_REG = 0x3FF48074
// Enable BBPLL via regi2c
regi2c_ctrl_ll_i2c_bbpll_enable();
// Estimate XTAL frequency, then set CPU to 80MHz via PLL
rtc_clk_cpu_freq_set_config(&new_config);
// Configure REF_TICK divider
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_XTAL, xtal_freq);
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_PLL, new_config.freq_mhz);
// Set CCOUNT to correct value for new frequency
esp_cpu_set_cycle_count(ccount * new_freq / old_freq);
// Enable RC_FAST clock, set RTC fast/slow clock sources
rtc_clk_8m_enable(true, false);
rtc_clk_fast_src_set(cfg.fast_clk_src);
rtc_clk_slow_src_set(cfg.slow_clk_src);
1.3 MMU Reset (bootloader_reset_mmu())
// File: bootloader_esp32.c:43
Cache_Read_Disable(0); // Disables ICache for PRO CPU
Cache_Read_Disable(1); // Disables ICache for APP CPU (dual core)
Cache_Flush(0); // Flush ICache PRO
Cache_Flush(1); // Flush ICache APP
mmu_init(0); // Reinit MMU table for PRO
// For dual core:
DPORT_REG_SET_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
// DPORT_APP_CACHE_CTRL1_REG = 0x3FF000C4
mmu_init(1); // Reinit MMU table for APP
DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
// Unmask DROM0 cache:
DPORT_REG_CLR_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DROM0);
// DPORT_PRO_CACHE_CTRL1_REG = 0x3FF00004 (actually 0x3FF00008 for ctrl1)
1.4 Watchdog Configuration (bootloader_config_wdt())
// File: bootloader_init.c:70
// Disable RWDT flashboot protection
wdt_hal_write_protect_disable(&rwdt_ctx); // Write to RTC WDT config regs
wdt_hal_set_flashboot_en(&rwdt_ctx, false); // Clear RTC_CNTL_WDT_OPTIONS register bit
wdt_hal_write_protect_enable(&rwdt_ctx);
// Disable MWDT0 flashboot protection
wdt_hal_write_protect_disable(&mwdt_ctx);
wdt_hal_set_flashboot_en(&mwdt_ctx, false); // Clear TIMG0 WDT options
wdt_hal_write_protect_enable(&mwdt_ctx);
// If CONFIG_BOOTLOADER_WDT_ENABLE:
// Initialize RWDT with CONFIG_BOOTLOADER_WDT_TIME_MS timeout
PHASE 2: App Startup (call_start_cpu0())
2.1 CPU Initialization (init_cpu())
// File: cpu_start.c:386
// NOTE: For Xtensa ESP32, this does very little:
// esp_cpu_intr_set_ivt_addr(&_vector_table);
// → calls xt_utils_set_vecbase((uint32_t)&_vector_table)
// → WSR VECBASE, &_vector_table
// (Sets exception/interrupt vector table base address)
Register write:
| Register | Address | Value | Why |
|---|---|---|---|
| VECBASE | SR 0x0E | &_vector_table | Set IVT base for exception handling |
2.2 Cache Init (cache_init())
// File: cpu_start.c:482
// For ESP32: cache_hal_init() — minimal, ESP32 has ROM-managed cache
// No ESP32-specific cache config blocks (those are for S2/S3)
2.3 RTC Init (sys_rtc_init())
// File: cpu_start.c:541
// If NOT CONFIG_BOOTLOADER_WDT_ENABLE and reset was WDT:
// Disable RTC WDT
// wdt_hal_write_protect_disable(&rtc_wdt_ctx);
// wdt_hal_disable(&rtc_wdt_ctx);
// wdt_hal_write_protect_enable(&rtc_wdt_ctx);
// Configure RTC power:
esp_rtc_init(); // rtc_config_t cfg = RTC_CONFIG_DEFAULT(); rtc_init(cfg);
2.4 MSPI/Flash Init
// spi_flash_init_chip_state();
// mspi_timing_flash_tuning();
2.5 system_early_init() — The Critical Phase
2.5.1 Clock Configuration (esp_clk_init())
// File: esp32/clk.c:122
// Re-enable RC_FAST
rtc_clk_8m_enable(true, rc_fast_d256_is_enabled);
// Set RTC FAST clock to RC_FAST
rtc_clk_fast_src_set(SOC_RTC_FAST_CLK_SRC_RC_FAST);
// Select RTC SLOW clock (default: 150kHz RC_SLOW)
select_rtc_slow_clk(SLOW_CLK_150K);
// Set CPU to CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ (80, 160, or 240)
rtc_clk_cpu_freq_set_config(&new_config);
// This configures BBPLL and sets DPORT_CPUPERIOD_SEL
// Re-adjust CCOUNT
esp_cpu_set_cycle_count(ccount * new_freq / old_freq);
2.5.2 Peripheral Clock Init (esp_perip_clk_init())
// File: esp32/clk.c:206
// Disable unused peripheral clocks, enable RNG
// Writes to DPORT_PERIP_CLK_EN_REG, DPORT_PERIP_RST_EN_REG, etc.
2.5.3 Clear Interrupt Matrix
// File: cpu_start.c:171
static void core_intr_matrix_clear(void)
{
for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) {
esp_rom_route_intr_matrix(core_id, i, ETS_INVALID_INUM);
// Writes to DPORT_PRO_MAC_INTR_MAP_REG + i*4
// Sets all peripheral interrupt sources to "unconnected" (6)
}
}
This clears all 69 ESP32 interrupt sources to INUM 6 (disconnected) in the interrupt matrix.
Key register range: DPORT_PRO_MAC_INTR_MAP_REG (0x3FF00000 + offsets)
PHASE 3: FreeRTOS Scheduler Start
3.1 xPortStartScheduler() — The Timer Arming Point
// File: port.c:329
BaseType_t xPortStartScheduler(void)
{
portDISABLE_INTERRUPTS(); // rsil a2, XCHAL_EXCM_LEVEL → sets PS.INTLEVEL=3
vPortSetupTimer(); // ← ARMS THE CCOMPARE TIMER
port_xSchedulerRunning[core] = 1;
// ... spill windows ...
__asm__ volatile("call0 _frxt_dispatch\n"); // Never returns
}
3.2 vPortSetupTimer() → _frxt_tick_timer_init()
// File: port_systick.c:169
void vPortSetupTimer(void)
{
#if CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
_xt_tick_divisor_init(); // Set _xt_tick_divisor = CPU_FREQ_HZ / TICK_RATE_HZ
_frxt_tick_timer_init(); // Arm the CCOMPARE timer
#else
vSystimerSetup(); // SYSTIMER peripheral (not used on ESP32 QEMU)
#endif
}
_xt_tick_divisor_init():
// File: xtensa_init.c:51
_xt_tick_divisor = esp_clk_cpu_freq() / XT_TICK_PER_SEC;
// For 80MHz CPU, 100Hz tick: _xt_tick_divisor = 800,000
// For 240MHz CPU, 1000Hz tick: _xt_tick_divisor = 240,000
_frxt_tick_timer_init() (portasm.S:385):
_frxt_tick_timer_init:
ENTRY(16)
# Load the tick divisor (cycles per tick)
movi a2, _xt_tick_divisor
l32i a3, a2, 0 # a3 = _xt_tick_divisor
# Read current cycle count
rsr a2, CCOUNT # a2 = current CCOUNT
# Set first interrupt time
add a2, a2, a3 # a2 = CCOUNT + divisor (first tick deadline)
wsr a2, XT_CCOMPARE # Write CCOMPARE0 — arms the timer + clears pending int
# Enable the timer interrupt
movi a6, XT_TIMER_INTEN # a6 = (1 << 6) = 0x40 for CCOMPARE0
movi a3, xt_ints_on
callx4 a3 # Enable interrupt bit 6 in INTENABLE
RET(16)
xt_ints_on() (xtensa_intr_asm.S:156):
xt_ints_on:
ENTRY0
movi a3, 0
xsr a3, INTENABLE # Read+clear INTENABLE (SR 0x00C)
rsync
or a2, a3, a2 # a2 = old INTENABLE | new mask
wsr a2, INTENABLE # Write new INTENABLE
rsync
mov a2, a3 # return old value
RET0
3.3 Timer Interrupt Handler (_frxt_timer_int)
# portasm.S:295
_frxt_timer_int:
ENTRY(16)
# Load tick divisor
movi a3, _xt_tick_divisor
l32i a2, a3, 0
# Read old CCOMPARE, advance it
rsr a3, XT_CCOMPARE # a3 = old CCOMPARE
add a4, a3, a2 # a4 = old + divisor
wsr a4, XT_CCOMPARE # Write new CCOMPARE (clears interrupt)
esync
# Call xPortSysTickHandler() for tick processing
# ... (catch-up loop if multiple ticks missed) ...
REGISTER WRITE TABLE (Ordered, by Phase)
Special Registers (Xtensa Core SRs — via wsr/rsr, NOT MMIO)
| Step | Register | SR# | Value | Source | Purpose |
|---|---|---|---|---|---|
| 1.1 | MEMCTL | 0x3A | XCHAL_CACHE_MEMCTL_DEFAULT | bootloader_esp32.c:170 | L1 cache memory control (erratum workaround) |
| 2.1 | VECBASE | 0x0E | &_vector_table | cpu_start.c:420 | Exception vector base |
| 2.5.1 | CCOUNT | 0x234 | (adjusted) | esp32/clk.c:197 | Adjust cycle count on freq change (via wsr CCOUNT) |
| 3.2 | CCOMPARE0 | 0x240 | CCOUNT + _xt_tick_divisor | portasm.S:399 | Arm first tick deadline |
| 3.2 | INTENABLE | 0x00C | old | 0x00000040 | xtensa_intr_asm.S:178 | Enable timer interrupt bit 6 (CCOMPARE0) |
| 3.3 | CCOMPARE0 | 0x240 | old_CCOMPARE + _xt_tick_divisor | portasm.S:334 | Re-arm for next tick (every interrupt) |
MMIO Registers (Peripheral — via DPORT/RTC)
Clock Subsystem
| Step | Register | Address | Value | Source | Purpose |
|---|---|---|---|---|---|
| 1.2 | RTC_CNTL_REG | 0x3FF4807C | SCK_DCAP field | rtc_clk_init.c:60 | RC slow clock tuning |
| 1.2 | RTC_CNTL_CLK_CONF_REG | 0x3FF48074 | CK8M_DFREQ field | rtc_clk_init.c:61 | RC fast clock tuning |
| 1.2 | RTC_CNTL_CLK_CONF_REG | 0x3FF48074 | CK8M_DIV_DRIVER field | rtc_clk_init.c:64 | RC fast clock divider |
| 1.2 | (various BBPLL regs) | regi2c | PLL config | rtc_clk_cpu_freq_set | CPU PLL frequency |
| 2.5.1 | RTC_CNTL_CLK_CONF_REG | 0x3FF48074 | RTC_CNTL_FAST_CLK_SEL | esp32/clk.c:141 | Select RC_FAST for RTC FAST |
| 2.5.1 | RTC_CNTL_CLK_CONF_REG | 0x3FF48074 | RTC_CNTL_ANA_CLK_RTC_SEL | esp32/clk.c:165 | Select RTC SLOW source |
Cache/MMU Subsystem
| Step | Register | Address | Value | Source | Purpose |
|---|---|---|---|---|---|
| 1.3 | DPORT_PRO_CACHE_CTRL1_REG | 0x3FF00008 | clr DROM0 mask | bootloader_esp32.c:65 | Unmask DROM0 cache for PRO |
| 1.3 | DPORT_APP_CACHE_CTRL1_REG | 0x3FF000C4 | set/clr MMU_IA_CLR | bootloader_esp32.c:58-60 | MMU interrupt clear workaround |
Watchdog Subsystem
| Step | Register | Address | Value | Source | Purpose |
|---|---|---|---|---|---|
| 1.4 | RTC WDT regs | 0x3FF48xxx | flashboot_en=0 | bootloader_init.c:81 | Disable RWDT flashboot protection |
| 1.4 | TIMG0 WDT regs | 0x3FF5F0xx | flashboot_en=0 | bootloader_init.c:98 | Disable MWDT0 flashboot protection |
| 2.3 | RTC WDT regs | 0x3FF48xxx | WDT disabled | cpu_start.c:560 | Disable RWDT (if no bootloader WDT) |
Interrupt Matrix (Cleared — not configured for CCOMPARE)
| Step | Register | Address | Value | Source | Purpose |
|---|---|---|---|---|---|
| 2.5.3 | DPORT_PRO_MAC_INTR_MAP_REG+i*4 | 0x3FF00000+i*4 | 6 (unconnected) | cpu_start.c:184 | Clear all peripheral→CPU interrupt routes |
NOTE: The interrupt matrix is NOT used to route the CCOMPARE timer interrupt. CCOMPARE0 fires on Xtensa internal interrupt 6, which is a core-level interrupt, not routed through the peripheral interrupt matrix.
MINIMAL BARE-METAL CCOMPARE INIT FOR UNIVERSALISOS ON QEMU
Based on the QEMU device model analysis, here is the absolute minimum needed to make CCOMPARE0 work:
; --- 1. Set VECBASE to point to your exception vectors ---
movi a2, _vector_table
wsr a2, VECBASE ; SR 0x0E
isync
; --- 2. Set PS to allow level 1 interrupts (clear EXCM, set INTLEVEL=0) ---
; PS register format: bits[3:0]=INTLEVEL, bit[4]=EXCM
; Need INTLEVEL < 1 (timer is level 1), and EXCM=0
movi a2, 0 ; INTLEVEL=0, EXCM=0 (everything enabled)
wsr a2, PS ; SR 0x0E6... actually PS is SR 0x0E6/230
rsync
; --- 3. Arm CCOMPARE0 with first tick deadline ---
rsr a2, CCOUNT ; SR 0x234 — read current cycle count
movi a3, TICK_DIVISOR ; e.g., 800000 for 100Hz @ 80MHz
add a2, a2, a3 ; first interrupt time
wsr a2, CCOMPARE0 ; SR 0x240 — arm timer, clears pending interrupt
esync ; ensure write completes
; --- 4. Enable timer interrupt bit 6 in INTENABLE ---
movi a2, 0x40 ; (1 << 6) for CCOMPARE0 interrupt
; Read current INTENABLE, OR in the bit, write back
movi a3, 0
xsr a3, INTENABLE ; SR 0x00C — atomic read+clear
rsync
or a2, a3, a2 ; old | 0x40
wsr a2, INTENABLE ; SR 0x00C — enable timer interrupt
rsync
; --- 5. Enable global interrupts (clear PS.INTLEVEL) ---
; Already done in step 2 if PS.INTLEVEL=0
; But if interrupts were disabled via rsil:
rsil a0, 0 ; Set INTLEVEL=0 (enable all unmasked ints)
QEMU-Specific Notes
-
CCOUNT frequency: QEMU's CCOUNT advances at the CPU clock frequency specified in the overlay config (typically 40MHz or 80MHz). The
ccount_baseis set duringxtensa_irq_init()from the current CCOUNT value. -
No peripheral device needed: Unlike the SYSTIMER peripheral (used on S2/S3/C-series), CCOMPARE0 fires through the Xtensa core interrupt path — no interrupt matrix configuration, no DPORT writes, no external device registers needed.
-
INTSET management: QEMU automatically sets INTSET bit 6 when the CCOMPARE timer fires (via
xtensa_ccompare_cb→qemu_set_irq). Writing CCOMPARE0 clears the bit (viaupdate_ccompare→qatomic_and INTSET). -
INTENABLE must be set: Without
INTENABLE |= (1<<6), the timer interrupt will fire butcheck_interrupts()won't deliver it. This is the most common failure point. -
PS.INTLEVEL must be < 1: Since CCOMPARE0 is level 1, PS.INTLEVEL must be 0 to receive it. Also PS.EXCM must be 0.
-
VECBASE must point to valid level-1 interrupt handler: The interrupt vector at VECBASE + 0x180 (level 2 vector offset is 0x180, but level 1 uses offset 0x000+0x50? Check). Actually for ESP32:
- Level 1: no fixed vector offset (uses INTENABLE check in level-2 handler? No...)
- The actual interrupt dispatch uses the exception vector at
VECBASE + level_vector_offset
ESP32 Interrupt Vector Offsets (from core-isa.h)
Level 1: No separate vector (handled by kernel software priority)
Level 2: VECOFS = 0x00000180 → VECBASE + 0x180
Level 3: VECOFS = 0x000001C0 → VECBASE + 0x1C0
Level 4: VECOFS = 0x00000200 → VECBASE + 0x200
Level 5: VECOFS = 0x00000240 → VECBASE + 0x240
Level 6: VECOFS = 0x00000280 (Debug)
Level 7: VECOFS = NMI offset
IMPORTANT: On ESP32, interrupt level 1 does NOT have a dedicated vector offset in the Xtensa hardware. Level 1 interrupts are dispatched through the Level 2 vector (offset 0x180). The interrupt handler then reads INTERRUPT and INTENABLE to determine which specific interrupt fired.
Wait, this is incorrect for standard Xtensa. Let me re-check: in the Xtensa architecture, interrupts at level N use the level-N vector. But ESP32's core-isa.h only defines vectors for levels 2-7. Level 1 interrupts ARE delivered via the level 2 vector mechanism — actually, looking at XCHAL_INTLEVEL1_MASK and the vector table, level 1 interrupts share the level 2 entry point. The ROM/kernel reads INTERRUPT/INTENABLE to dispatch.
Actually, the correct behavior is: Level 1 interrupts use the level 1 vector. But ESP32 defines only vectors for level 2+ because the typical Xtensa dispatch for level 1 IS via reading INTERRUPT at the level 2 entry. Let me not over-think this — the key point is that VECBASE must be set, and your handler must correctly dispatch interrupt 6.
SUMMARY: What QEMU Needs for CCOMPARE Timer
Absolutely Required (4 Steps)
- VECBASE set to valid exception vector table (wsr VECBASE)
- CCOMPARE0 written with CCOUNT + divisor (wsr CCOMPARE0)
- INTENABLE bit 6 set (wsr INTENABLE |= 0x40)
- PS.INTLEVEL = 0 (interrupts globally enabled, timer is level 1)
NOT Required for QEMU (despite being done in ESP-IDF)
- ❌ Clock/PLL configuration (QEMU CCOUNT advances regardless)
- ❌ RTC/init (no effect on CCOMPARE)
- ❌ Cache/MMU setup (no effect on CCOMPARE)
- ❌ Interrupt matrix clearing (CCOMPARE is internal to Xtensa core)
- ❌ Watchdog configuration (no effect on CCOMPARE)
- ❌ Peripheral clock gating (no effect on CCOMPARE)
ISR Re-arm Pattern (in _frxt_timer_int)
rsr a3, CCOMPARE0 ; read old comparator
add a4, a3, divisor ; advance by one tick
wsr a4, CCOMPARE0 ; write new value (clears pending IRQ)
esync
; then call tick handler
Key Source Files Referenced
| File | Path |
|---|---|
| call_start_cpu0 | components/esp_system/port/cpu_start.c |
| bootloader init | components/bootloader_support/src/esp32/bootloader_esp32.c |
| bootloader clock | components/bootloader_support/src/bootloader_clock_init.c |
| bootloader WDT | components/bootloader_support/src/bootloader_init.c |
| rtc_clk_init | components/esp_hw_support/port/esp32/rtc_clk_init.c |
| esp_clk_init | components/esp_system/port/soc/esp32/clk.c |
| FreeRTOS port.c | components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c |
| FreeRTOS portasm.S | components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/portasm.S |
| port_systick.c | components/freertos/port_systick.c |
| xtensa_init.c | components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/xtensa_init.c |
| xtensa_intr_asm.S | components/xtensa/xtensa_intr_asm.S |
| xtensa_timer.h | components/xtensa/include/xtensa_timer.h |
| ESP32 core-isa.h | components/xtensa/esp32/include/xtensa/config/core-isa.h |
| QEMU esp32.c | hw/xtensa/esp32.c |
| QEMU esp32_intc.c | hw/xtensa/esp32_intc.c |
| QEMU pic_cpu.c | hw/xtensa/pic_cpu.c |
| QEMU op_helper.c | target/xtensa/op_helper.c |
| QEMU cpu.h | target/xtensa/cpu.h |
| QEMU core-esp32/core-isa.h | target/xtensa/core-esp32/core-isa.h |