universalisos/docs/esp32-idf-hwinit-audit.md
Fábio Coutada 98ed638f3c WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules
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.
2026-07-17 01:36:58 +01:00

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 = 0
  • XT_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):

  1. CCOUNT (SR 0x234) is derived from QEMU's virtual clock:
    CCOUNT = ccount_base + clock_ns_to_ticks(now - time_base)

  2. 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 + 1 cycles until match
  3. Timer fires (xtensa_ccompare_cb()):

    • Sets env->irq_inputs[timerint[i]] active → bit in INTSET
    • check_interrupts() checks INTSET & INTENABLE against current cintlevel
  4. Interrupt delivery:
    QEMU's check_interrupts() (in pic_cpu.c) checks:
    int_set_enabled = INTSET & (INTENABLE | NMI_mask)
    If any bit in int_set_enabled is 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

  1. CCOUNT frequency: QEMU's CCOUNT advances at the CPU clock frequency specified in the overlay config (typically 40MHz or 80MHz). The ccount_base is set during xtensa_irq_init() from the current CCOUNT value.

  2. 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.

  3. INTSET management: QEMU automatically sets INTSET bit 6 when the CCOMPARE timer fires (via xtensa_ccompare_cbqemu_set_irq). Writing CCOMPARE0 clears the bit (via update_ccompareqatomic_and INTSET).

  4. INTENABLE must be set: Without INTENABLE |= (1<<6), the timer interrupt will fire but check_interrupts() won't deliver it. This is the most common failure point.

  5. PS.INTLEVEL must be < 1: Since CCOMPARE0 is level 1, PS.INTLEVEL must be 0 to receive it. Also PS.EXCM must be 0.

  6. 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)

  1. VECBASE set to valid exception vector table (wsr VECBASE)
  2. CCOMPARE0 written with CCOUNT + divisor (wsr CCOMPARE0)
  3. INTENABLE bit 6 set (wsr INTENABLE |= 0x40)
  4. 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