From 293ea91bff9edfe4df15fecf034d65d52c7bdb7c Mon Sep 17 00:00:00 2001 From: zackees Date: Thu, 2 Jul 2026 06:54:17 -0700 Subject: [PATCH] feat(startup): named weak IRQ handlers for LPC845/LPC804 vector slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standard CMSIS startup pattern: every chip-level vector slot gets a peripheral-named handler (SPI0_IRQHandler, DMA0_IRQHandler, ...) weak- aliased to Default_Handler, so a sketch or library installs an ISR by defining the strong symbol. Previously all 32 slots pointed at Default_Handler directly, which made ISR-driven drivers (DMA chunk refill, async UART TX) impossible without a RAM vector table — and the Cortex-M0+ has no VTOR, so that would need SYSMEMREMAP plus a reserved SRAM block in every linker script. Slot names follow each chip's IRQn enum in its vendor CMSIS header. LPC845 and LPC804 get full named maps; other variants keep the legacy all-default table (zero behavior change until their maps are added). Reserved slots stay on Default_Handler. Also make the fault-emit NMI_Handler alias weak, matching the no-fault-emit branch: FastLED routes the WWDT warning interrupt to NMI (SYSCON->NMISRC) for pre-reset wedge backtraces and may want to install a WDT-specific report instead of the generic HardFault print. Downstream consumer: FastLED LPC845 ISR-refillable SPI-DMA streaming + async UART TX drivers (FastLED/FastLED#3453 follow-up). Co-Authored-By: Claude Fable 5 --- cores/lpc8xx/startup_lpc8xx.c | 140 +++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 3 deletions(-) diff --git a/cores/lpc8xx/startup_lpc8xx.c b/cores/lpc8xx/startup_lpc8xx.c index 18f63a9..1d16a1e 100644 --- a/cores/lpc8xx/startup_lpc8xx.c +++ b/cores/lpc8xx/startup_lpc8xx.c @@ -106,10 +106,74 @@ void HardFault_Handler(void) { } } -/* NMI gets the same emit + reboot path as HardFault. */ -void NMI_Handler(void) __attribute__((alias("HardFault_Handler"))); +/* NMI gets the same emit + reboot path as HardFault. Weak so a sketch or + * library can install its own NMI handler (e.g. FastLED routes the WWDT + * warning interrupt to NMI via SYSCON->NMISRC for pre-reset wedge + * backtraces and may want a WDT-specific report). */ +void NMI_Handler(void) __attribute__((weak, alias("HardFault_Handler"))); #endif /* !ARDUINOCORE_LPC8XX_NO_FAULT_EMIT */ +/* --------------------------------------------------------------------------- + * Named weak chip-level IRQ handlers (issue #38). + * + * Standard CMSIS startup pattern: every chip-level vector slot gets a + * peripheral-named handler, weak-aliased to Default_Handler, so a sketch + * or library can install an ISR by simply defining the strong symbol + * (e.g. `void DMA0_IRQHandler(void)`). Previously all 32 slots pointed + * at Default_Handler directly, which made ISR-driven drivers (DMA chunk + * refill, async UART TX) impossible without a RAM vector table — and the + * Cortex-M0+ has no VTOR, so that would need SYSMEMREMAP plus a reserved + * block at the start of SRAM in every linker script. + * + * Slot names follow each chip's IRQn enum in its vendor CMSIS header + * (variants//LPC8xx.h). Reserved slots keep Default_Handler. + * Chips without a per-chip block below keep the legacy all-default + * table — no behavior change until their map is added. + * --------------------------------------------------------------------------- */ +#if defined(__LPC845__) || defined(__LPC804__) +void SPI0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void DAC0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void USART0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void USART1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void I2C1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void I2C0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void MRT0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void CMP_CAPT_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void WDT_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void BOD_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void FLASH_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void WKT_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void CTIMER0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +#endif +#if defined(__LPC845__) +void SPI1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void USART2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SCT0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ADC0_SEQA_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ADC0_SEQB_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ADC0_THCMP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ADC0_OVR_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void DMA0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void I2C2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void I2C3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT5_DAC1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT6_USART3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT7_USART4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +#elif defined(__LPC804__) +void ADC_SEQA_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ADC_SEQB_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ADC_THCMP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ADC_OVR_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT5_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT6_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PIN_INT7_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +#endif + // External symbols from linker script extern void _vStackTop(void); extern void __valid_user_code_checksum(void) __attribute__((weak)); @@ -142,7 +206,76 @@ void (* const g_pfnVectors[])(void) = { PendSV_Handler, // PendSV handler SysTick_Handler, // SysTick handler - // Chip Level - LPC8xx IRQs (32 total) + // Chip Level - LPC8xx IRQs (32 total). Slot names per the chip's + // vendor CMSIS IRQn enum; reserved slots stay on Default_Handler. +#if defined(__LPC845__) + SPI0_IRQHandler, // IRQ0 SPI0 + SPI1_IRQHandler, // IRQ1 SPI1 + DAC0_IRQHandler, // IRQ2 DAC0 + USART0_IRQHandler, // IRQ3 USART0 + USART1_IRQHandler, // IRQ4 USART1 + USART2_IRQHandler, // IRQ5 USART2 + Default_Handler, // IRQ6 reserved + I2C1_IRQHandler, // IRQ7 I2C1 + I2C0_IRQHandler, // IRQ8 I2C0 + SCT0_IRQHandler, // IRQ9 SCT0 + MRT0_IRQHandler, // IRQ10 MRT0 + CMP_CAPT_IRQHandler, // IRQ11 analog comparator / captouch + WDT_IRQHandler, // IRQ12 WWDT + BOD_IRQHandler, // IRQ13 BOD + FLASH_IRQHandler, // IRQ14 flash + WKT_IRQHandler, // IRQ15 self-wake-up timer + ADC0_SEQA_IRQHandler, // IRQ16 ADC0 seq A + ADC0_SEQB_IRQHandler, // IRQ17 ADC0 seq B + ADC0_THCMP_IRQHandler, // IRQ18 ADC0 threshold compare + ADC0_OVR_IRQHandler, // IRQ19 ADC0 overrun + DMA0_IRQHandler, // IRQ20 DMA0 + I2C2_IRQHandler, // IRQ21 I2C2 + I2C3_IRQHandler, // IRQ22 I2C3 + CTIMER0_IRQHandler, // IRQ23 CTIMER0 + PIN_INT0_IRQHandler, // IRQ24 pin int 0 + PIN_INT1_IRQHandler, // IRQ25 pin int 1 + PIN_INT2_IRQHandler, // IRQ26 pin int 2 + PIN_INT3_IRQHandler, // IRQ27 pin int 3 + PIN_INT4_IRQHandler, // IRQ28 pin int 4 + PIN_INT5_DAC1_IRQHandler, // IRQ29 pin int 5 / DAC1 + PIN_INT6_USART3_IRQHandler, // IRQ30 pin int 6 / USART3 + PIN_INT7_USART4_IRQHandler, // IRQ31 pin int 7 / USART4 +#elif defined(__LPC804__) + SPI0_IRQHandler, // IRQ0 SPI0 + Default_Handler, // IRQ1 reserved + DAC0_IRQHandler, // IRQ2 DAC0 + USART0_IRQHandler, // IRQ3 USART0 + USART1_IRQHandler, // IRQ4 USART1 + Default_Handler, // IRQ5 reserved + Default_Handler, // IRQ6 reserved + I2C1_IRQHandler, // IRQ7 I2C1 + I2C0_IRQHandler, // IRQ8 I2C0 + Default_Handler, // IRQ9 reserved + MRT0_IRQHandler, // IRQ10 MRT0 + CMP_CAPT_IRQHandler, // IRQ11 analog comparator / captouch + WDT_IRQHandler, // IRQ12 WWDT + BOD_IRQHandler, // IRQ13 BOD + FLASH_IRQHandler, // IRQ14 flash + WKT_IRQHandler, // IRQ15 self-wake-up timer + ADC_SEQA_IRQHandler, // IRQ16 ADC seq A + ADC_SEQB_IRQHandler, // IRQ17 ADC seq B + ADC_THCMP_IRQHandler, // IRQ18 ADC threshold compare + ADC_OVR_IRQHandler, // IRQ19 ADC overrun + Default_Handler, // IRQ20 reserved + Default_Handler, // IRQ21 reserved + Default_Handler, // IRQ22 reserved + CTIMER0_IRQHandler, // IRQ23 CTIMER0 + PIN_INT0_IRQHandler, // IRQ24 pin int 0 + PIN_INT1_IRQHandler, // IRQ25 pin int 1 + PIN_INT2_IRQHandler, // IRQ26 pin int 2 + PIN_INT3_IRQHandler, // IRQ27 pin int 3 + PIN_INT4_IRQHandler, // IRQ28 pin int 4 + PIN_INT5_IRQHandler, // IRQ29 pin int 5 + PIN_INT6_IRQHandler, // IRQ30 pin int 6 + PIN_INT7_IRQHandler, // IRQ31 pin int 7 +#else + // Chips without a named map yet keep the legacy all-default table. Default_Handler, // IRQ0 Default_Handler, // IRQ1 Default_Handler, // IRQ2 @@ -175,6 +308,7 @@ void (* const g_pfnVectors[])(void) = { Default_Handler, // IRQ29 Default_Handler, // IRQ30 Default_Handler, // IRQ31 +#endif }; void SystemInit(void) {