Postmortem Debugging to serial port (#20492)
This commit is contained in:
		| @@ -3732,3 +3732,10 @@ | ||||
|  | ||||
| // Enable Marlin dev mode which adds some special commands | ||||
| //#define MARLIN_DEV_MODE | ||||
|  | ||||
| /** | ||||
|  * Postmortem Debugging captures misbehavior and outputs the CPU status and backtrace to serial. | ||||
|  * When running in the debugger it will break for debugging. This is useful to help understand | ||||
|  * a crash from a remote location. Requires ~400 bytes of SRAM and 5Kb of flash. | ||||
|  */ | ||||
| //#define POSTMORTEM_DEBUGGING | ||||
|   | ||||
| @@ -56,3 +56,10 @@ | ||||
| #if BOTH(HAS_TMC_SW_SERIAL, MONITOR_DRIVER_STATUS) | ||||
|   #error "MONITOR_DRIVER_STATUS causes performance issues when used with SoftwareSerial-connected drivers. Disable MONITOR_DRIVER_STATUS or use hardware serial to continue." | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Postmortem debugging | ||||
|  */ | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|   #error "POSTMORTEM_DEBUGGING is not supported on AVR boards." | ||||
| #endif | ||||
|   | ||||
| @@ -1,342 +0,0 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #ifdef ARDUINO_ARCH_SAM | ||||
|  | ||||
| #include "../../core/macros.h" | ||||
| #include "../../core/serial.h" | ||||
|  | ||||
| #include "../shared/backtrace/unwinder.h" | ||||
| #include "../shared/backtrace/unwmemaccess.h" | ||||
|  | ||||
| #include <stdarg.h> | ||||
|  | ||||
| // Debug monitor that dumps to the Programming port all status when | ||||
| // an exception or WDT timeout happens - And then resets the board | ||||
|  | ||||
| // All the Monitor routines must run with interrupts disabled and | ||||
| // under an ISR execution context. That is why we cannot reuse the | ||||
| // Serial interrupt routines or any C runtime, as we don't know the | ||||
| // state we are when running them | ||||
|  | ||||
| // A SW memory barrier, to ensure GCC does not overoptimize loops | ||||
| #define sw_barrier() __asm__ volatile("": : :"memory"); | ||||
|  | ||||
| // (re)initialize UART0 as a monitor output to 250000,n,8,1 | ||||
| static void TXBegin() { | ||||
|  | ||||
|   // Disable UART interrupt in NVIC | ||||
|   NVIC_DisableIRQ( UART_IRQn ); | ||||
|  | ||||
|   // We NEED memory barriers to ensure Interrupts are actually disabled! | ||||
|   // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) | ||||
|   __DSB(); | ||||
|   __ISB(); | ||||
|  | ||||
|   // Disable clock | ||||
|   pmc_disable_periph_clk( ID_UART ); | ||||
|  | ||||
|   // Configure PMC | ||||
|   pmc_enable_periph_clk( ID_UART ); | ||||
|  | ||||
|   // Disable PDC channel | ||||
|   UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS; | ||||
|  | ||||
|   // Reset and disable receiver and transmitter | ||||
|   UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS; | ||||
|  | ||||
|   // Configure mode: 8bit, No parity, 1 bit stop | ||||
|   UART->UART_MR = UART_MR_CHMODE_NORMAL | US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO; | ||||
|  | ||||
|   // Configure baudrate (asynchronous, no oversampling) to BAUDRATE bauds | ||||
|   UART->UART_BRGR = (SystemCoreClock / (BAUDRATE << 4)); | ||||
|  | ||||
|   // Enable receiver and transmitter | ||||
|   UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN; | ||||
| } | ||||
|  | ||||
| // Send character through UART with no interrupts | ||||
| static void TX(char c) { | ||||
|   while (!(UART->UART_SR & UART_SR_TXRDY)) { WDT_Restart(WDT); sw_barrier(); }; | ||||
|   UART->UART_THR = c; | ||||
| } | ||||
|  | ||||
| // Send String through UART | ||||
| static void TX(const char* s) { | ||||
|   while (*s) TX(*s++); | ||||
| } | ||||
|  | ||||
| static void TXDigit(uint32_t d) { | ||||
|   if (d < 10) TX((char)(d+'0')); | ||||
|   else if (d < 16) TX((char)(d+'A'-10)); | ||||
|   else TX('?'); | ||||
| } | ||||
|  | ||||
| // Send Hex number thru UART | ||||
| static void TXHex(uint32_t v) { | ||||
|   TX("0x"); | ||||
|   for (uint8_t i = 0; i < 8; i++, v <<= 4) | ||||
|     TXDigit((v >> 28) & 0xF); | ||||
| } | ||||
|  | ||||
| // Send Decimal number thru UART | ||||
| static void TXDec(uint32_t v) { | ||||
|   if (!v) { | ||||
|     TX('0'); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   char nbrs[14]; | ||||
|   char *p = &nbrs[0]; | ||||
|   while (v != 0) { | ||||
|     *p++ = '0' + (v % 10); | ||||
|     v /= 10; | ||||
|   } | ||||
|   do { | ||||
|     p--; | ||||
|     TX(*p); | ||||
|   } while (p != &nbrs[0]); | ||||
| } | ||||
|  | ||||
| // Dump a backtrace entry | ||||
| static bool UnwReportOut(void* ctx, const UnwReport* bte) { | ||||
|   int* p = (int*)ctx; | ||||
|  | ||||
|   (*p)++; | ||||
|   TX('#'); TXDec(*p); TX(" : "); | ||||
|   TX(bte->name?bte->name:"unknown"); TX('@'); TXHex(bte->function); | ||||
|   TX('+'); TXDec(bte->address - bte->function); | ||||
|   TX(" PC:");TXHex(bte->address); TX('\n'); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| #ifdef UNW_DEBUG | ||||
|   void UnwPrintf(const char* format, ...) { | ||||
|     char dest[256]; | ||||
|     va_list argptr; | ||||
|     va_start(argptr, format); | ||||
|     vsprintf(dest, format, argptr); | ||||
|     va_end(argptr); | ||||
|     TX(&dest[0]); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| /* Table of function pointers for passing to the unwinder */ | ||||
| static const UnwindCallbacks UnwCallbacks = { | ||||
|   UnwReportOut, | ||||
|   UnwReadW, | ||||
|   UnwReadH, | ||||
|   UnwReadB | ||||
|   #ifdef UNW_DEBUG | ||||
|    , UnwPrintf | ||||
|   #endif | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * HardFaultHandler_C: | ||||
|  * This is called from the HardFault_HandlerAsm with a pointer the Fault stack | ||||
|  * as the parameter. We can then read the values from the stack and place them | ||||
|  * into local variables for ease of reading. | ||||
|  * We then read the various Fault Status and Address Registers to help decode | ||||
|  * cause of the fault. | ||||
|  * The function ends with a BKPT instruction to force control back into the debugger | ||||
|  */ | ||||
| extern "C" | ||||
| void HardFault_HandlerC(unsigned long *sp, unsigned long lr, unsigned long cause) { | ||||
|  | ||||
|   static const char* causestr[] = { | ||||
|     "NMI","Hard","Mem","Bus","Usage","Debug","WDT","RSTC" | ||||
|   }; | ||||
|  | ||||
|   UnwindFrame btf; | ||||
|  | ||||
|   // Dump report to the Programming port (interrupts are DISABLED) | ||||
|   TXBegin(); | ||||
|   TX("\n\n## Software Fault detected ##\n"); | ||||
|   TX("Cause: "); TX(causestr[cause]); TX('\n'); | ||||
|  | ||||
|   TX("R0   : "); TXHex(((unsigned long)sp[0])); TX('\n'); | ||||
|   TX("R1   : "); TXHex(((unsigned long)sp[1])); TX('\n'); | ||||
|   TX("R2   : "); TXHex(((unsigned long)sp[2])); TX('\n'); | ||||
|   TX("R3   : "); TXHex(((unsigned long)sp[3])); TX('\n'); | ||||
|   TX("R12  : "); TXHex(((unsigned long)sp[4])); TX('\n'); | ||||
|   TX("LR   : "); TXHex(((unsigned long)sp[5])); TX('\n'); | ||||
|   TX("PC   : "); TXHex(((unsigned long)sp[6])); TX('\n'); | ||||
|   TX("PSR  : "); TXHex(((unsigned long)sp[7])); TX('\n'); | ||||
|  | ||||
|   // Configurable Fault Status Register | ||||
|   // Consists of MMSR, BFSR and UFSR | ||||
|   TX("CFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED28)))); TX('\n'); | ||||
|  | ||||
|   // Hard Fault Status Register | ||||
|   TX("HFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED2C)))); TX('\n'); | ||||
|  | ||||
|   // Debug Fault Status Register | ||||
|   TX("DFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED30)))); TX('\n'); | ||||
|  | ||||
|   // Auxiliary Fault Status Register | ||||
|   TX("AFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED3C)))); TX('\n'); | ||||
|  | ||||
|   // Read the Fault Address Registers. These may not contain valid values. | ||||
|   // Check BFARVALID/MMARVALID to see if they are valid values | ||||
|   // MemManage Fault Address Register | ||||
|   TX("MMAR : "); TXHex((*((volatile unsigned long *)(0xE000ED34)))); TX('\n'); | ||||
|  | ||||
|   // Bus Fault Address Register | ||||
|   TX("BFAR : "); TXHex((*((volatile unsigned long *)(0xE000ED38)))); TX('\n'); | ||||
|  | ||||
|   TX("ExcLR: "); TXHex(lr); TX('\n'); | ||||
|   TX("ExcSP: "); TXHex((unsigned long)sp); TX('\n'); | ||||
|  | ||||
|   btf.sp = ((unsigned long)sp) + 8*4; // The original stack pointer | ||||
|   btf.fp = btf.sp; | ||||
|   btf.lr = ((unsigned long)sp[5]); | ||||
|   btf.pc = ((unsigned long)sp[6]) | 1; // Force Thumb, as CORTEX only support it | ||||
|  | ||||
|   // Perform a backtrace | ||||
|   TX("\nBacktrace:\n\n"); | ||||
|   int ctr = 0; | ||||
|   UnwindStart(&btf, &UnwCallbacks, &ctr); | ||||
|  | ||||
|   // Disable all NVIC interrupts | ||||
|   NVIC->ICER[0] = 0xFFFFFFFF; | ||||
|   NVIC->ICER[1] = 0xFFFFFFFF; | ||||
|  | ||||
|   // Relocate VTOR table to default position | ||||
|   SCB->VTOR = 0; | ||||
|  | ||||
|   // Disable USB | ||||
|   otg_disable(); | ||||
|  | ||||
|   // Restart watchdog | ||||
|   WDT_Restart(WDT); | ||||
|  | ||||
|   // Reset controller | ||||
|   NVIC_SystemReset(); | ||||
|   for (;;) WDT_Restart(WDT); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void NMI_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#0") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void HardFault_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#1") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void MemManage_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#2") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void BusFault_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#3") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void UsageFault_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#4") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void DebugMon_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#5") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /* This is NOT an exception, it is an interrupt handler - Nevertheless, the framing is the same */ | ||||
| __attribute__((naked)) void WDT_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#6") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void RSTC_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#7") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| #endif // ARDUINO_ARCH_SAM | ||||
| @@ -40,6 +40,8 @@ uint16_t HAL_adc_result; | ||||
| // Public functions | ||||
| // ------------------------ | ||||
|  | ||||
| TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); | ||||
|  | ||||
| // HAL initialization task | ||||
| void HAL_init() { | ||||
|   // Initialize the USB stack | ||||
| @@ -47,6 +49,7 @@ void HAL_init() { | ||||
|     OUT_WRITE(SDSS, HIGH);  // Try to set SDSS inactive before any other SPI users start up | ||||
|   #endif | ||||
|   usb_task_init(); | ||||
|   TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler | ||||
| } | ||||
|  | ||||
| // HAL idle task | ||||
|   | ||||
							
								
								
									
										91
									
								
								Marlin/src/HAL/DUE/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								Marlin/src/HAL/DUE/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #ifdef ARDUINO_ARCH_SAM | ||||
|  | ||||
| #include "../../inc/MarlinConfigPre.h" | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|  | ||||
| #include "../shared/HAL_MinSerial.h" | ||||
|  | ||||
| #include <stdarg.h> | ||||
|  | ||||
| static void TXBegin() { | ||||
|   // Disable UART interrupt in NVIC | ||||
|   NVIC_DisableIRQ( UART_IRQn ); | ||||
|  | ||||
|   // We NEED memory barriers to ensure Interrupts are actually disabled! | ||||
|   // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) | ||||
|   __DSB(); | ||||
|   __ISB(); | ||||
|  | ||||
|   // Disable clock | ||||
|   pmc_disable_periph_clk( ID_UART ); | ||||
|  | ||||
|   // Configure PMC | ||||
|   pmc_enable_periph_clk( ID_UART ); | ||||
|  | ||||
|   // Disable PDC channel | ||||
|   UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS; | ||||
|  | ||||
|   // Reset and disable receiver and transmitter | ||||
|   UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS; | ||||
|  | ||||
|   // Configure mode: 8bit, No parity, 1 bit stop | ||||
|   UART->UART_MR = UART_MR_CHMODE_NORMAL | US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO; | ||||
|  | ||||
|   // Configure baudrate (asynchronous, no oversampling) to BAUDRATE bauds | ||||
|   UART->UART_BRGR = (SystemCoreClock / (BAUDRATE << 4)); | ||||
|  | ||||
|   // Enable receiver and transmitter | ||||
|   UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN; | ||||
| } | ||||
|  | ||||
| // A SW memory barrier, to ensure GCC does not overoptimize loops | ||||
| #define sw_barrier() __asm__ volatile("": : :"memory"); | ||||
| static void TX(char c) { | ||||
|   while (!(UART->UART_SR & UART_SR_TXRDY)) { WDT_Restart(WDT); sw_barrier(); }; | ||||
|   UART->UART_THR = c; | ||||
| } | ||||
|  | ||||
| void install_min_serial() { | ||||
|   HAL_min_serial_init = &TXBegin; | ||||
|   HAL_min_serial_out = &TX; | ||||
| } | ||||
|  | ||||
| #if DISABLED(DYNAMIC_VECTORTABLE) | ||||
| extern "C" { | ||||
|   __attribute__((naked)) void JumpHandler_ASM() { | ||||
|     __asm__ __volatile__ ( | ||||
|       "b CommonHandler_ASM\n" | ||||
|     ); | ||||
|   } | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) HardFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) BusFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) UsageFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) MemManage_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) NMI_Handler(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif // POSTMORTEM_DEBUGGING | ||||
| #endif // ARDUINO_ARCH_SAM | ||||
| @@ -57,5 +57,5 @@ | ||||
| #endif | ||||
|  | ||||
| #if HAS_TMC_SW_SERIAL | ||||
|   #error "TMC220x Software Serial is not supported on this platform." | ||||
|   #error "TMC220x Software Serial is not supported on the DUE platform." | ||||
| #endif | ||||
|   | ||||
| @@ -30,9 +30,13 @@ | ||||
| #endif | ||||
|  | ||||
| #if HAS_TMC_SW_SERIAL | ||||
|   #error "TMC220x Software Serial is not supported on this platform." | ||||
|   #error "TMC220x Software Serial is not supported on ESP32." | ||||
| #endif | ||||
|  | ||||
| #if BOTH(WIFISUPPORT, ESP3D_WIFISUPPORT) | ||||
|   #error "Only enable one WiFi option, either WIFISUPPORT or ESP3D_WIFISUPPORT." | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|   #error "POSTMORTEM_DEBUGGING is not yet supported on ESP32." | ||||
| #endif | ||||
|   | ||||
| @@ -35,5 +35,9 @@ | ||||
| #endif | ||||
|  | ||||
| #if HAS_TMC_SW_SERIAL | ||||
|   #error "TMC220x Software Serial is not supported on this platform." | ||||
|   #error "TMC220x Software Serial is not supported on LINUX." | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|   #error "POSTMORTEM_DEBUGGING is not yet supported on LINUX." | ||||
| #endif | ||||
|   | ||||
| @@ -1,322 +0,0 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #ifdef TARGET_LPC1768 | ||||
|  | ||||
| #include "../../core/macros.h" | ||||
| #include "../../core/serial.h" | ||||
| #include <stdarg.h> | ||||
|  | ||||
| #include "../shared/backtrace/unwinder.h" | ||||
| #include "../shared/backtrace/unwmemaccess.h" | ||||
| #include "watchdog.h" | ||||
| #include <debug_frmwrk.h> | ||||
|  | ||||
|  | ||||
| // Debug monitor that dumps to the Programming port all status when | ||||
| // an exception or WDT timeout happens - And then resets the board | ||||
|  | ||||
| // All the Monitor routines must run with interrupts disabled and | ||||
| // under an ISR execution context. That is why we cannot reuse the | ||||
| // Serial interrupt routines or any C runtime, as we don't know the | ||||
| // state we are when running them | ||||
|  | ||||
| // A SW memory barrier, to ensure GCC does not overoptimize loops | ||||
| #define sw_barrier() __asm__ volatile("": : :"memory"); | ||||
|  | ||||
| // (re)initialize UART0 as a monitor output to 250000,n,8,1 | ||||
| static void TXBegin() { | ||||
| } | ||||
|  | ||||
| // Send character through UART with no interrupts | ||||
| static void TX(char c) { | ||||
|   _DBC(c); | ||||
| } | ||||
|  | ||||
| // Send String through UART | ||||
| static void TX(const char* s) { | ||||
|   while (*s) TX(*s++); | ||||
| } | ||||
|  | ||||
| static void TXDigit(uint32_t d) { | ||||
|   if (d < 10) TX((char)(d+'0')); | ||||
|   else if (d < 16) TX((char)(d+'A'-10)); | ||||
|   else TX('?'); | ||||
| } | ||||
|  | ||||
| // Send Hex number thru UART | ||||
| static void TXHex(uint32_t v) { | ||||
|   TX("0x"); | ||||
|   for (uint8_t i = 0; i < 8; i++, v <<= 4) | ||||
|     TXDigit((v >> 28) & 0xF); | ||||
| } | ||||
|  | ||||
| // Send Decimal number thru UART | ||||
| static void TXDec(uint32_t v) { | ||||
|   if (!v) { | ||||
|     TX('0'); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   char nbrs[14]; | ||||
|   char *p = &nbrs[0]; | ||||
|   while (v != 0) { | ||||
|     *p++ = '0' + (v % 10); | ||||
|     v /= 10; | ||||
|   } | ||||
|   do { | ||||
|     p--; | ||||
|     TX(*p); | ||||
|   } while (p != &nbrs[0]); | ||||
| } | ||||
|  | ||||
| // Dump a backtrace entry | ||||
| static bool UnwReportOut(void* ctx, const UnwReport* bte) { | ||||
|   int* p = (int*)ctx; | ||||
|  | ||||
|   (*p)++; | ||||
|   TX('#'); TXDec(*p); TX(" : "); | ||||
|   TX(bte->name?bte->name:"unknown"); TX('@'); TXHex(bte->function); | ||||
|   TX('+'); TXDec(bte->address - bte->function); | ||||
|   TX(" PC:");TXHex(bte->address); TX('\n'); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| #ifdef UNW_DEBUG | ||||
|   void UnwPrintf(const char* format, ...) { | ||||
|     char dest[256]; | ||||
|     va_list argptr; | ||||
|     va_start(argptr, format); | ||||
|     vsprintf(dest, format, argptr); | ||||
|     va_end(argptr); | ||||
|     TX(&dest[0]); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| /* Table of function pointers for passing to the unwinder */ | ||||
| static const UnwindCallbacks UnwCallbacks = { | ||||
|   UnwReportOut, | ||||
|   UnwReadW, | ||||
|   UnwReadH, | ||||
|   UnwReadB | ||||
|   #ifdef UNW_DEBUG | ||||
|    ,UnwPrintf | ||||
|   #endif | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * HardFaultHandler_C: | ||||
|  * This is called from the HardFault_HandlerAsm with a pointer the Fault stack | ||||
|  * as the parameter. We can then read the values from the stack and place them | ||||
|  * into local variables for ease of reading. | ||||
|  * We then read the various Fault Status and Address Registers to help decode | ||||
|  * cause of the fault. | ||||
|  * The function ends with a BKPT instruction to force control back into the debugger | ||||
|  */ | ||||
| extern "C" | ||||
| void HardFault_HandlerC(unsigned long *sp, unsigned long lr, unsigned long cause) { | ||||
|  | ||||
|   static const char* causestr[] = { | ||||
|     "NMI","Hard","Mem","Bus","Usage","Debug","WDT","RSTC" | ||||
|   }; | ||||
|  | ||||
|   UnwindFrame btf; | ||||
|  | ||||
|   // Dump report to the Programming port (interrupts are DISABLED) | ||||
|   TXBegin(); | ||||
|   TX("\n\n## Software Fault detected ##\n"); | ||||
|   TX("Cause: "); TX(causestr[cause]); TX('\n'); | ||||
|  | ||||
|   TX("R0   : "); TXHex(((unsigned long)sp[0])); TX('\n'); | ||||
|   TX("R1   : "); TXHex(((unsigned long)sp[1])); TX('\n'); | ||||
|   TX("R2   : "); TXHex(((unsigned long)sp[2])); TX('\n'); | ||||
|   TX("R3   : "); TXHex(((unsigned long)sp[3])); TX('\n'); | ||||
|   TX("R12  : "); TXHex(((unsigned long)sp[4])); TX('\n'); | ||||
|   TX("LR   : "); TXHex(((unsigned long)sp[5])); TX('\n'); | ||||
|   TX("PC   : "); TXHex(((unsigned long)sp[6])); TX('\n'); | ||||
|   TX("PSR  : "); TXHex(((unsigned long)sp[7])); TX('\n'); | ||||
|  | ||||
|   // Configurable Fault Status Register | ||||
|   // Consists of MMSR, BFSR and UFSR | ||||
|   TX("CFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED28)))); TX('\n'); | ||||
|  | ||||
|   // Hard Fault Status Register | ||||
|   TX("HFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED2C)))); TX('\n'); | ||||
|  | ||||
|   // Debug Fault Status Register | ||||
|   TX("DFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED30)))); TX('\n'); | ||||
|  | ||||
|   // Auxiliary Fault Status Register | ||||
|   TX("AFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED3C)))); TX('\n'); | ||||
|  | ||||
|   // Read the Fault Address Registers. These may not contain valid values. | ||||
|   // Check BFARVALID/MMARVALID to see if they are valid values | ||||
|   // MemManage Fault Address Register | ||||
|   TX("MMAR : "); TXHex((*((volatile unsigned long *)(0xE000ED34)))); TX('\n'); | ||||
|  | ||||
|   // Bus Fault Address Register | ||||
|   TX("BFAR : "); TXHex((*((volatile unsigned long *)(0xE000ED38)))); TX('\n'); | ||||
|  | ||||
|   TX("ExcLR: "); TXHex(lr); TX('\n'); | ||||
|   TX("ExcSP: "); TXHex((unsigned long)sp); TX('\n'); | ||||
|  | ||||
|   btf.sp = ((unsigned long)sp) + 8*4; // The original stack pointer | ||||
|   btf.fp = btf.sp; | ||||
|   btf.lr = ((unsigned long)sp[5]); | ||||
|   btf.pc = ((unsigned long)sp[6]) | 1; // Force Thumb, as CORTEX only support it | ||||
|  | ||||
|   // Perform a backtrace | ||||
|   TX("\nBacktrace:\n\n"); | ||||
|   int ctr = 0; | ||||
|   UnwindStart(&btf, &UnwCallbacks, &ctr); | ||||
|  | ||||
|   // Disable all NVIC interrupts | ||||
|   NVIC->ICER[0] = 0xFFFFFFFF; | ||||
|   NVIC->ICER[1] = 0xFFFFFFFF; | ||||
|  | ||||
|   // Relocate VTOR table to default position | ||||
|   SCB->VTOR = 0; | ||||
|  | ||||
|   // Clear cause of reset to prevent entering smoothie bootstrap | ||||
|   HAL_clear_reset_source(); | ||||
|  | ||||
|   // Restart watchdog | ||||
|   #if ENABLED(USE_WATCHDOG) | ||||
|     //WDT_Restart(WDT); | ||||
|     watchdog_init(); | ||||
|   #endif | ||||
|  | ||||
|   // Reset controller | ||||
|   NVIC_SystemReset(); | ||||
|  | ||||
|   // Nothing below here is compiled because NVIC_SystemReset loops forever | ||||
|  | ||||
|   for (;;) { TERN_(USE_WATCHDOG, watchdog_init()); } | ||||
| } | ||||
|  | ||||
| extern "C" { | ||||
| __attribute__((naked)) void NMI_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#0") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void HardFault_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#1") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void MemManage_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#2") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void BusFault_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#3") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void UsageFault_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#4") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void DebugMon_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#5") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /* This is NOT an exception, it is an interrupt handler - Nevertheless, the framing is the same */ | ||||
| __attribute__((naked)) void WDT_IRQHandler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#6") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| __attribute__((naked)) void RSTC_Handler() { | ||||
|   __asm__ __volatile__ ( | ||||
|     ".syntax unified" "\n\t" | ||||
|     A("tst lr, #4") | ||||
|     A("ite eq") | ||||
|     A("mrseq r0, msp") | ||||
|     A("mrsne r0, psp") | ||||
|     A("mov r1,lr") | ||||
|     A("mov r2,#7") | ||||
|     A("b HardFault_HandlerC") | ||||
|   ); | ||||
| } | ||||
| } | ||||
| #endif // TARGET_LPC1768 | ||||
							
								
								
									
										50
									
								
								Marlin/src/HAL/LPC1768/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								Marlin/src/HAL/LPC1768/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #ifdef TARGET_LPC1768 | ||||
|  | ||||
| #include "HAL.h" | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|  | ||||
| #include "../shared/HAL_MinSerial.h" | ||||
| #include <debug_frmwrk.h> | ||||
|  | ||||
| static void TX(char c) { _DBC(c); } | ||||
| void install_min_serial() { HAL_min_serial_out = &TX; } | ||||
|  | ||||
| #if DISABLED(DYNAMIC_VECTORTABLE) | ||||
| extern "C" { | ||||
|   __attribute__((naked)) void JumpHandler_ASM() { | ||||
|     __asm__ __volatile__ ( | ||||
|       "b CommonHandler_ASM\n" | ||||
|     ); | ||||
|   } | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) HardFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) BusFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) UsageFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) MemManage_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"))) NMI_Handler(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif // POSTMORTEM_DEBUGGING | ||||
| #endif // TARGET_LPC1768 | ||||
| @@ -270,7 +270,7 @@ static_assert(DISABLED(BAUD_RATE_GCODE), "BAUD_RATE_GCODE is not yet supported o | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) | ||||
|   #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on this platform." | ||||
|   #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on LPC176x." | ||||
| #elif ENABLED(SERIAL_STATS_DROPPED_RX) | ||||
|   #error "SERIAL_STATS_DROPPED_RX is not supported on this platform." | ||||
|   #error "SERIAL_STATS_DROPPED_RX is not supported on LPX176x." | ||||
| #endif | ||||
|   | ||||
| @@ -46,6 +46,8 @@ extern "C" { | ||||
|  | ||||
| void SysTick_Callback() { disk_timerproc(); } | ||||
|  | ||||
| TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); | ||||
|  | ||||
| void HAL_init() { | ||||
|  | ||||
|   // Init LEDs | ||||
| @@ -123,9 +125,7 @@ void HAL_init() { | ||||
|   delay(1000);                              // Give OS time to notice | ||||
|   USB_Connect(true); | ||||
|  | ||||
|   #if HAS_SD_HOST_DRIVE | ||||
|     MSC_SD_Init(0);                         // Enable USB SD card access | ||||
|   #endif | ||||
|   TERN_(HAS_SD_HOST_DRIVE, MSC_SD_Init(0)); // Enable USB SD card access | ||||
|  | ||||
|   const millis_t usb_timeout = millis() + 2000; | ||||
|   while (!USB_Configuration && PENDING(millis(), usb_timeout)) { | ||||
| @@ -137,6 +137,8 @@ void HAL_init() { | ||||
|   } | ||||
|  | ||||
|   HAL_timer_init(); | ||||
|  | ||||
|   TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler | ||||
| } | ||||
|  | ||||
| // HAL idle task | ||||
|   | ||||
| @@ -57,6 +57,8 @@ uint16_t HAL_adc_result; | ||||
| // Public functions | ||||
| // ------------------------ | ||||
|  | ||||
| TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); | ||||
|  | ||||
| // HAL initialization task | ||||
| void HAL_init() { | ||||
|   FastIO_init(); | ||||
| @@ -83,6 +85,8 @@ void HAL_init() { | ||||
|     USB_Hook_init(); | ||||
|   #endif | ||||
|  | ||||
|   TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler | ||||
|  | ||||
|   #if HAS_SD_HOST_DRIVE | ||||
|     MSC_SD_init();                         // Enable USB SD card access | ||||
|   #endif | ||||
|   | ||||
							
								
								
									
										152
									
								
								Marlin/src/HAL/STM32/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								Marlin/src/HAL/STM32/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * Copyright (c) 2017 Victor Perez | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #if defined(ARDUINO_ARCH_STM32) && !defined(STM32GENERIC) | ||||
|  | ||||
| #include "../../inc/MarlinConfigPre.h" | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|  | ||||
| #include "../shared/HAL_MinSerial.h" | ||||
| #include "watchdog.h" | ||||
|  | ||||
| /* Instruction Synchronization Barrier */ | ||||
| #define isb() __asm__ __volatile__ ("isb" : : : "memory") | ||||
|  | ||||
| /* Data Synchronization Barrier */ | ||||
| #define dsb() __asm__ __volatile__ ("dsb" : : : "memory") | ||||
|  | ||||
| // Dumb mapping over the registers of a USART device on STM32 | ||||
| struct USARTMin { | ||||
|   volatile uint32_t SR; | ||||
|   volatile uint32_t DR; | ||||
|   volatile uint32_t BRR; | ||||
|   volatile uint32_t CR1; | ||||
|   volatile uint32_t CR2; | ||||
| }; | ||||
|  | ||||
| #if WITHIN(SERIAL_PORT, 1, 6) | ||||
|   // Depending on the CPU, the serial port is different for USART1 | ||||
|   static const uintptr_t regsAddr[] = { | ||||
|     TERN(STM32F1xx, 0x40013800, 0x40011000), // USART1 | ||||
|     0x40004400, // USART2 | ||||
|     0x40004800, // USART3 | ||||
|     0x40004C00, // UART4_BASE | ||||
|     0x40005000, // UART5_BASE | ||||
|     0x40011400  // USART6 | ||||
|   }; | ||||
|   static USARTMin * regs = (USARTMin*)regsAddr[SERIAL_PORT - 1]; | ||||
| #endif | ||||
|  | ||||
| static void TXBegin() { | ||||
|   #if !WITHIN(SERIAL_PORT, 1, 6) | ||||
|     #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error." | ||||
|     #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port." | ||||
|   #else | ||||
|     // This is common between STM32F1/STM32F2 and STM32F4 | ||||
|     const int nvicUART[] = { /* NVIC_USART1 */ 37, /* NVIC_USART2 */ 38, /* NVIC_USART3 */ 39, /* NVIC_UART4 */ 52, /* NVIC_UART5 */ 53, /* NVIC_USART6 */ 71 }; | ||||
|     int nvicIndex = nvicUART[SERIAL_PORT - 1]; | ||||
|  | ||||
|     struct NVICMin { | ||||
|       volatile uint32_t ISER[32]; | ||||
|       volatile uint32_t ICER[32]; | ||||
|     }; | ||||
|  | ||||
|     NVICMin * nvicBase = (NVICMin*)0xE000E100; | ||||
|     nvicBase->ICER[nvicIndex / 32] |= _BV32(nvicIndex % 32); | ||||
|  | ||||
|     // We NEED memory barriers to ensure Interrupts are actually disabled! | ||||
|     // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) | ||||
|     dsb(); | ||||
|     isb(); | ||||
|  | ||||
|     // Example for USART1 disable:  (RCC->APB2ENR &= ~(RCC_APB2ENR_USART1EN)) | ||||
|     // Too difficult to reimplement here, let's query the STM32duino macro here | ||||
|     #if SERIAL_PORT == 1 | ||||
|       __HAL_RCC_USART1_CLK_DISABLE(); | ||||
|       __HAL_RCC_USART1_CLK_ENABLE(); | ||||
|     #elif SERIAL_PORT == 2 | ||||
|       __HAL_RCC_USART2_CLK_DISABLE(); | ||||
|       __HAL_RCC_USART2_CLK_ENABLE(); | ||||
|     #elif SERIAL_PORT == 3 | ||||
|       __HAL_RCC_USART3_CLK_DISABLE(); | ||||
|       __HAL_RCC_USART3_CLK_ENABLE(); | ||||
|     #elif SERIAL_PORT == 4 | ||||
|       __HAL_RCC_UART4_CLK_DISABLE(); // BEWARE: UART4 and not USART4 here | ||||
|       __HAL_RCC_UART4_CLK_ENABLE(); | ||||
|     #elif SERIAL_PORT == 5 | ||||
|       __HAL_RCC_UART5_CLK_DISABLE(); // BEWARE: UART5 and not USART5 here | ||||
|       __HAL_RCC_UART5_CLK_ENABLE(); | ||||
|     #elif SERIAL_PORT == 6 | ||||
|       __HAL_RCC_USART6_CLK_DISABLE(); | ||||
|       __HAL_RCC_USART6_CLK_ENABLE(); | ||||
|     #endif | ||||
|  | ||||
|     uint32_t brr = regs->BRR; | ||||
|     regs->CR1 = 0; // Reset the USART | ||||
|     regs->CR2 = 0; // 1 stop bit | ||||
|  | ||||
|     // If we don't touch the BRR (baudrate register), we don't need to recompute. | ||||
|     regs->BRR = brr; | ||||
|  | ||||
|     regs->CR1 = _BV(3) | _BV(13); // 8 bits, no parity, 1 stop bit (TE | UE) | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| // A SW memory barrier, to ensure GCC does not overoptimize loops | ||||
| #define sw_barrier() __asm__ volatile("": : :"memory"); | ||||
| static void TX(char c) { | ||||
|   #if WITHIN(SERIAL_PORT, 1, 6) | ||||
|     constexpr uint32_t usart_sr_txe = _BV(7); | ||||
|     while (!(regs->SR & usart_sr_txe)) { | ||||
|       TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); | ||||
|       sw_barrier(); | ||||
|     } | ||||
|     regs->DR = c; | ||||
|   #else | ||||
|     // Let's hope a mystical guru will fix this, one day by writting interrupt-free USB CDC ACM code (or, at least, by polling the registers since interrupt will be queued but will never trigger) | ||||
|     // For now, it's completely lost to oblivion. | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| void install_min_serial() { | ||||
|   HAL_min_serial_init = &TXBegin; | ||||
|   HAL_min_serial_out = &TX; | ||||
| } | ||||
|  | ||||
| #if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't jump to a symbol that's too far from the current function, so we work around this in exception_arm.cpp | ||||
| extern "C" { | ||||
|   __attribute__((naked)) void JumpHandler_ASM() { | ||||
|     __asm__ __volatile__ ( | ||||
|       "b CommonHandler_ASM\n" | ||||
|     ); | ||||
|   } | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) HardFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) BusFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) UsageFault_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) MemManage_Handler(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) NMI_Handler(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif // POSTMORTEM_DEBUGGING | ||||
| #endif // ARDUINO_ARCH_STM32 | ||||
| @@ -47,9 +47,9 @@ | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) | ||||
|   #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on this platform." | ||||
|   #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on STM32." | ||||
| #elif ENABLED(SERIAL_STATS_DROPPED_RX) | ||||
|   #error "SERIAL_STATS_DROPPED_RX is not supported on this platform." | ||||
|   #error "SERIAL_STATS_DROPPED_RX is not supported on STM32." | ||||
| #endif | ||||
|  | ||||
| #if ANY(TFT_COLOR_UI, TFT_LVGL_UI, TFT_CLASSIC_UI) && NOT_TARGET(STM32F4xx, STM32F1xx) | ||||
|   | ||||
| @@ -272,6 +272,8 @@ static void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { | ||||
|   } } | ||||
| #endif | ||||
|  | ||||
| TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); | ||||
|  | ||||
| void HAL_init() { | ||||
|   NVIC_SetPriorityGrouping(0x3); | ||||
|   #if PIN_EXISTS(LED) | ||||
| @@ -287,6 +289,7 @@ void HAL_init() { | ||||
|     delay(1000);                                         // Give OS time to notice | ||||
|     OUT_WRITE(USB_CONNECT_PIN, USB_CONNECT_INVERTING); | ||||
|   #endif | ||||
|   TERN_(POSTMORTEM_DEBUGGING, install_min_serial());    // Install the minimal serial handler | ||||
| } | ||||
|  | ||||
| // HAL idle task | ||||
|   | ||||
							
								
								
									
										118
									
								
								Marlin/src/HAL/STM32F1/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								Marlin/src/HAL/STM32F1/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * Copyright (c) 2017 Victor Perez | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #ifdef __STM32F1__ | ||||
|  | ||||
| #include "../../inc/MarlinConfigPre.h" | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|  | ||||
| #include "../shared/HAL_MinSerial.h" | ||||
| #include "watchdog.h" | ||||
|  | ||||
| #include <libmaple/usart.h> | ||||
| #include <libmaple/rcc.h> | ||||
| #include <libmaple/nvic.h> | ||||
|  | ||||
| /* Instruction Synchronization Barrier */ | ||||
| #define isb() __asm__ __volatile__ ("isb" : : : "memory") | ||||
|  | ||||
| /* Data Synchronization Barrier */ | ||||
| #define dsb() __asm__ __volatile__ ("dsb" : : : "memory") | ||||
|  | ||||
| static void TXBegin() { | ||||
|   #if !WITHIN(SERIAL_PORT, 1, 6) | ||||
|     #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error." | ||||
|     #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port." | ||||
|   #else | ||||
|     // We use MYSERIAL0 here, so we need to figure out how to get the linked register | ||||
|     struct usart_dev* dev = MYSERIAL0.c_dev(); | ||||
|  | ||||
|     // Or use this if removing libmaple | ||||
|     // int irq = dev->irq_num; | ||||
|     // int nvicUART[] = { NVIC_USART1 /* = 37 */, NVIC_USART2 /* = 38 */, NVIC_USART3 /* = 39 */, NVIC_UART4 /* = 52 */, NVIC_UART5 /* = 53 */ }; | ||||
|     // Disabling irq means setting the bit in the NVIC ICER register located at | ||||
|     // Disable UART interrupt in NVIC | ||||
|     nvic_irq_disable(dev->irq_num); | ||||
|  | ||||
|     // Use this if removing libmaple | ||||
|     //NVIC_BASE->ICER[1] |= _BV(irq - 32); | ||||
|  | ||||
|     // We NEED memory barriers to ensure Interrupts are actually disabled! | ||||
|     // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) | ||||
|     dsb(); | ||||
|     isb(); | ||||
|  | ||||
|     rcc_clk_disable(dev->clk_id); | ||||
|     rcc_clk_enable(dev->clk_id); | ||||
|  | ||||
|     usart_reg_map *regs = dev->regs; | ||||
|     regs->CR1 = 0; // Reset the USART | ||||
|     regs->CR2 = 0; // 1 stop bit | ||||
|  | ||||
|     // If we don't touch the BRR (baudrate register), we don't need to recompute. Else we would need to call | ||||
|     usart_set_baud_rate(dev, 0, BAUDRATE); | ||||
|  | ||||
|     regs->CR1 = (USART_CR1_TE | USART_CR1_UE); // 8 bits, no parity, 1 stop bit | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| // A SW memory barrier, to ensure GCC does not overoptimize loops | ||||
| #define sw_barrier() __asm__ volatile("": : :"memory"); | ||||
| static void TX(char c) { | ||||
|   #if WITHIN(SERIAL_PORT, 1, 6) | ||||
|     struct usart_dev* dev = MYSERIAL0.c_dev(); | ||||
|     while (!(dev->regs->SR & USART_SR_TXE)) { | ||||
|       TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); | ||||
|       sw_barrier(); | ||||
|     } | ||||
|     dev->regs->DR = c; | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| void install_min_serial() { | ||||
|   HAL_min_serial_init = &TXBegin; | ||||
|   HAL_min_serial_out = &TX; | ||||
| } | ||||
|  | ||||
| #if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them | ||||
| extern "C" { | ||||
|   __attribute__((naked)) void JumpHandler_ASM() { | ||||
|     __asm__ __volatile__ ( | ||||
|       "b CommonHandler_ASM\n" | ||||
|     ); | ||||
|   } | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_hardfault(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_busfault(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_usagefault(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_memmanage(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_nmi(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception7(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception8(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception9(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception10(); | ||||
|   void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception13(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif // POSTMORTEM_DEBUGGING | ||||
| #endif // __STM32F1__ | ||||
| @@ -34,9 +34,9 @@ | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) | ||||
|   #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on this platform." | ||||
|   #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on the STM32F1 platform." | ||||
| #elif ENABLED(SERIAL_STATS_DROPPED_RX) | ||||
|   #error "SERIAL_STATS_DROPPED_RX is not supported on this platform." | ||||
|   #error "SERIAL_STATS_DROPPED_RX is not supported on the STM32F1 platform." | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(NEOPIXEL_LED) | ||||
|   | ||||
| @@ -34,5 +34,9 @@ | ||||
| #endif | ||||
|  | ||||
| #if HAS_TMC_SW_SERIAL | ||||
|   #error "TMC220x Software Serial is not supported on this platform." | ||||
|   #error "TMC220x Software Serial is not supported on Teensy 3.1/3.2." | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|   #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 3.1/3.2." | ||||
| #endif | ||||
|   | ||||
| @@ -34,5 +34,9 @@ | ||||
| #endif | ||||
|  | ||||
| #if HAS_TMC_SW_SERIAL | ||||
|   #error "TMC220x Software Serial is not supported on this platform." | ||||
|   #error "TMC220x Software Serial is not supported on Teensy 3.5/3.6." | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|   #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 3.5/3.6." | ||||
| #endif | ||||
|   | ||||
| @@ -34,5 +34,9 @@ | ||||
| #endif | ||||
|  | ||||
| #if HAS_TMC_SW_SERIAL | ||||
|   #error "TMC220x Software Serial is not supported on this platform." | ||||
|   #error "TMC220x Software Serial is not supported on Teensy 4.0/4.1." | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|   #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 4.0/4.1." | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										33
									
								
								Marlin/src/HAL/shared/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Marlin/src/HAL/shared/HAL_MinSerial.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #include "HAL_MinSerial.h" | ||||
|  | ||||
| #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|  | ||||
| void HAL_min_serial_init_default() {} | ||||
| void HAL_min_serial_out_default(char ch) { SERIAL_CHAR(ch); } | ||||
| void (*HAL_min_serial_init)() = &HAL_min_serial_init_default; | ||||
| void (*HAL_min_serial_out)(char) = &HAL_min_serial_out_default; | ||||
|  | ||||
| bool MinSerial::force_using_default_output = false; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										79
									
								
								Marlin/src/HAL/shared/HAL_MinSerial.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								Marlin/src/HAL/shared/HAL_MinSerial.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../../core/serial.h" | ||||
| #include <stdint.h> | ||||
|  | ||||
| // Serial stuff here | ||||
| // Inside an exception handler, the CPU state is not safe, we can't expect the handler to resume | ||||
| // and the software to continue. UART communication can't rely on later callback/interrupt as it might never happen. | ||||
| // So, you need to provide some method to send one byte to the usual UART with the interrupts disabled | ||||
| // By default, the method uses SERIAL_CHAR but it's 100% guaranteed to break (couldn't be worse than nothing...)7 | ||||
| extern void (*HAL_min_serial_init)(); | ||||
| extern void (*HAL_min_serial_out)(char ch); | ||||
|  | ||||
| struct MinSerial { | ||||
|   static bool force_using_default_output; | ||||
|   // Serial output | ||||
|   static void TX(char ch) { | ||||
|     if (force_using_default_output) | ||||
|       SERIAL_CHAR(ch); | ||||
|     else | ||||
|       HAL_min_serial_out(ch); | ||||
|   } | ||||
|   // Send String through UART | ||||
|   static void TX(const char* s) { while (*s) TX(*s++); } | ||||
|   // Send a digit through UART | ||||
|   static void TXDigit(uint32_t d) { | ||||
|     if (d < 10) TX((char)(d+'0')); | ||||
|     else if (d < 16) TX((char)(d+'A'-10)); | ||||
|     else TX('?'); | ||||
|   } | ||||
|  | ||||
|   // Send Hex number through UART | ||||
|   static void TXHex(uint32_t v) { | ||||
|     TX("0x"); | ||||
|     for (uint8_t i = 0; i < 8; i++, v <<= 4) | ||||
|       TXDigit((v >> 28) & 0xF); | ||||
|   } | ||||
|  | ||||
|   // Send Decimal number through UART | ||||
|   static void TXDec(uint32_t v) { | ||||
|     if (!v) { | ||||
|       TX('0'); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     char nbrs[14]; | ||||
|     char *p = &nbrs[0]; | ||||
|     while (v != 0) { | ||||
|       *p++ = '0' + (v % 10); | ||||
|       v /= 10; | ||||
|     } | ||||
|     do { | ||||
|       p--; | ||||
|       TX(*p); | ||||
|     } while (p != &nbrs[0]); | ||||
|   } | ||||
|   static void init() { if (!force_using_default_output) HAL_min_serial_init(); } | ||||
| }; | ||||
| @@ -25,7 +25,7 @@ | ||||
| #include "unwinder.h" | ||||
| #include "unwmemaccess.h" | ||||
|  | ||||
| #include "../../../core/serial.h" | ||||
| #include "../HAL_MinSerial.h" | ||||
| #include <stdarg.h> | ||||
|  | ||||
| // Dump a backtrace entry | ||||
| @@ -34,10 +34,12 @@ static bool UnwReportOut(void* ctx, const UnwReport* bte) { | ||||
|  | ||||
|   (*p)++; | ||||
|  | ||||
|   SERIAL_CHAR('#'); SERIAL_ECHO(*p); SERIAL_ECHOPGM(" : "); | ||||
|   SERIAL_ECHOPGM(bte->name ? bte->name : "unknown"); SERIAL_ECHOPGM("@0x"); SERIAL_PRINT(bte->function, PrintBase::Hex); | ||||
|   SERIAL_CHAR('+'); SERIAL_ECHO(bte->address - bte->function); | ||||
|   SERIAL_ECHOPGM(" PC:"); SERIAL_PRINT(bte->address, PrintBase::Hex); SERIAL_CHAR('\n'); | ||||
|   const uint32_t a = bte->address, f = bte->function; | ||||
|   MinSerial::TX('#');    MinSerial::TXDec(*p);    MinSerial::TX(" : "); | ||||
|   MinSerial::TX(bte->name?:"unknown");            MinSerial::TX('@');   MinSerial::TXHex(f); | ||||
|   MinSerial::TX('+');    MinSerial::TXDec(a - f); | ||||
|   MinSerial::TX(" PC:"); MinSerial::TXHex(a); | ||||
|   MinSerial::TX('\n'); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| @@ -48,7 +50,7 @@ static bool UnwReportOut(void* ctx, const UnwReport* bte) { | ||||
|     va_start(argptr, format); | ||||
|     vsprintf(dest, format, argptr); | ||||
|     va_end(argptr); | ||||
|     TX(&dest[0]); | ||||
|     MinSerial::TX(&dest[0]); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| @@ -63,10 +65,10 @@ static const UnwindCallbacks UnwCallbacks = { | ||||
|   #endif | ||||
| }; | ||||
|  | ||||
| // Perform a backtrace to the serial port | ||||
| void backtrace() { | ||||
|  | ||||
|   UnwindFrame btf; | ||||
|   uint32_t sp = 0, lr = 0, pc = 0; | ||||
|   unsigned long sp = 0, lr = 0, pc = 0; | ||||
|  | ||||
|   // Capture the values of the registers to perform the traceback | ||||
|   __asm__ __volatile__ ( | ||||
| @@ -79,6 +81,12 @@ void backtrace() { | ||||
|     :: | ||||
|   ); | ||||
|  | ||||
|   backtrace_ex(sp, lr, pc); | ||||
| } | ||||
|  | ||||
| void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc) { | ||||
|   UnwindFrame btf; | ||||
|  | ||||
|   // Fill the traceback structure | ||||
|   btf.sp = sp; | ||||
|   btf.fp = btf.sp; | ||||
| @@ -86,7 +94,7 @@ void backtrace() { | ||||
|   btf.pc = pc | 1; // Force Thumb, as CORTEX only support it | ||||
|  | ||||
|   // Perform a backtrace | ||||
|   SERIAL_ERROR_MSG("Backtrace:"); | ||||
|   MinSerial::TX("Backtrace:"); | ||||
|   int ctr = 0; | ||||
|   UnwindStart(&btf, &UnwCallbacks, &ctr); | ||||
| } | ||||
| @@ -95,4 +103,4 @@ void backtrace() { | ||||
|  | ||||
| void backtrace() {} | ||||
|  | ||||
| #endif | ||||
| #endif // __arm__ || __thumb__ | ||||
|   | ||||
| @@ -23,3 +23,6 @@ | ||||
|  | ||||
| // Perform a backtrace to the serial port | ||||
| void backtrace(); | ||||
|  | ||||
| // Perform a backtrace to the serial port | ||||
| void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc); | ||||
|   | ||||
| @@ -41,27 +41,16 @@ | ||||
|   #define START_FLASH_ADDR  0x00000000 | ||||
|   #define END_FLASH_ADDR    0x00080000 | ||||
|  | ||||
| #elif 0 | ||||
|  | ||||
|   // For STM32F103CBT6 | ||||
|   //  SRAM  (0x20000000 - 0x20005000) (20kb) | ||||
|   //  FLASH (0x00000000 - 0x00020000) (128kb) | ||||
|   // | ||||
|   #define START_SRAM_ADDR   0x20000000 | ||||
|   #define END_SRAM_ADDR     0x20005000 | ||||
|   #define START_FLASH_ADDR  0x00000000 | ||||
|   #define END_FLASH_ADDR    0x00020000 | ||||
|  | ||||
| #elif defined(__STM32F1__) || defined(STM32F1xx) || defined(STM32F0xx) | ||||
|  | ||||
|   // For STM32F103ZET6/STM32F103VET6/STM32F0xx | ||||
|   //  SRAM  (0x20000000 - 0x20010000) (64kb) | ||||
|   //  FLASH (0x00000000 - 0x00080000) (512kb) | ||||
|   //  FLASH (0x08000000 - 0x08080000) (512kb) | ||||
|   // | ||||
|   #define START_SRAM_ADDR   0x20000000 | ||||
|   #define END_SRAM_ADDR     0x20010000 | ||||
|   #define START_FLASH_ADDR  0x00000000 | ||||
|   #define END_FLASH_ADDR    0x00080000 | ||||
|   #define START_FLASH_ADDR  0x08000000 | ||||
|   #define END_FLASH_ADDR    0x08080000 | ||||
|  | ||||
| #elif defined(STM32F4) || defined(STM32F4xx) | ||||
|  | ||||
| @@ -142,20 +131,57 @@ | ||||
|   #define START_FLASH_ADDR  0x00000000 | ||||
|   #define END_FLASH_ADDR    0x00100000 | ||||
|  | ||||
| #else | ||||
|   // Generic ARM code, that's testing if an access to the given address would cause a fault or not | ||||
|   // It can't guarantee an address is in RAM or Flash only, but we usually don't care | ||||
|  | ||||
|   #define NVIC_FAULT_STAT         0xE000ED28  // Configurable Fault Status Reg. | ||||
|   #define NVIC_CFG_CTRL           0xE000ED14  // Configuration Control Register | ||||
|   #define NVIC_FAULT_STAT_BFARV   0x00008000  // BFAR is valid | ||||
|   #define NVIC_CFG_CTRL_BFHFNMIGN 0x00000100  // Ignore bus fault in NMI/fault | ||||
|   #define HW_REG(X)  (*((volatile unsigned long *)(X))) | ||||
|  | ||||
|   static bool validate_addr(uint32_t read_address) { | ||||
|     bool     works = true; | ||||
|     uint32_t intDisabled = 0; | ||||
|     // Read current interrupt state | ||||
|     __asm__ __volatile__ ("MRS %[result], PRIMASK\n\t" : [result]"=r"(intDisabled) :: ); // 0 is int enabled, 1 for disabled | ||||
|  | ||||
|     // Clear bus fault indicator first (write 1 to clear) | ||||
|     HW_REG(NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV; | ||||
|     // Ignore bus fault interrupt | ||||
|     HW_REG(NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN; | ||||
|     // Disable interrupts if not disabled previously | ||||
|     if (!intDisabled) __asm__ __volatile__ ("CPSID f"); | ||||
|     // Probe address | ||||
|     *(volatile uint32_t*)read_address; | ||||
|     // Check if a fault happened | ||||
|     if ((HW_REG(NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0) | ||||
|       works = false; | ||||
|     // Enable interrupts again if previously disabled | ||||
|     if (!intDisabled) __asm__ __volatile__ ("CPSIE f"); | ||||
|     // Enable fault interrupt flag | ||||
|     HW_REG(NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN; | ||||
|  | ||||
|     return works; | ||||
|   } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| static bool validate_addr(uint32_t addr) { | ||||
| #ifdef START_SRAM_ADDR | ||||
|   static bool validate_addr(uint32_t addr) { | ||||
|  | ||||
|   // Address must be in SRAM range | ||||
|   if (addr >= START_SRAM_ADDR && addr < END_SRAM_ADDR) | ||||
|     return true; | ||||
|     // Address must be in SRAM range | ||||
|     if (addr >= START_SRAM_ADDR && addr < END_SRAM_ADDR) | ||||
|       return true; | ||||
|  | ||||
|   // Or in FLASH range | ||||
|   if (addr >= START_FLASH_ADDR && addr < END_FLASH_ADDR) | ||||
|     return true; | ||||
|     // Or in FLASH range | ||||
|     if (addr >= START_FLASH_ADDR && addr < END_FLASH_ADDR) | ||||
|       return true; | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|     return false; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| bool UnwReadW(const uint32_t a, uint32_t *v) { | ||||
|   if (!validate_addr(a)) | ||||
|   | ||||
							
								
								
									
										379
									
								
								Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,379 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * Copyright (c) 2020 Cyril Russo | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /*************************************************************************** | ||||
|  * ARM CPU Exception handler | ||||
|  ***************************************************************************/ | ||||
|  | ||||
| #if defined(__arm__) || defined(__thumb__) | ||||
|  | ||||
|  | ||||
| /* | ||||
|   On ARM CPUs exception handling is quite powerful. | ||||
|  | ||||
|   By default, upon a crash, the CPU enters the handlers that have a higher priority than any other interrupts, | ||||
|   so, in effect, no (real) interrupt can "interrupt" the handler (it's acting like if interrupts were disabled). | ||||
|  | ||||
|   If the handler is not called as re-entrant (that is, if the crash is not happening inside an interrupt or an handler), | ||||
|   then it'll patch the return address to a dumping function (resume_from_fault) and save the crash state. | ||||
|   The CPU will exit the handler and, as such, re-allow the other interrupts, and jump to the dumping function. | ||||
|   In this function, the usual serial port (USB / HW) will be used to dump the crash (no special configuration required). | ||||
|  | ||||
|   The only case where it requires hardware UART is when it's crashing in an interrupt or a crash handler. | ||||
|   In that case, instead of returning to the resume_from_fault function (and thus, re-enabling interrupts), | ||||
|   it jumps to this function directly (so with interrupts disabled), after changing the behavior of the serial output | ||||
|   wrapper to use the HW uart (and in effect, calling MinSerial::init which triggers a warning if you are using | ||||
|   a USB serial port). | ||||
|  | ||||
|   In the case you have a USB serial port, this part will be disabled, and only that part (so that's the reason for | ||||
|   the warning). | ||||
|   This means that you can't have a crash report if the crash happens in an interrupt or an handler if you are using | ||||
|   a USB serial port since it's physically impossible. | ||||
|   You will get a crash report in all other cases. | ||||
| */ | ||||
|  | ||||
| #include "exception_hook.h" | ||||
| #include "../backtrace/backtrace.h" | ||||
| #include "../HAL_MinSerial.h" | ||||
|  | ||||
| #define HW_REG(X)  (*((volatile unsigned long *)(X))) | ||||
|  | ||||
| // Default function use the CPU VTOR register to get the vector table. | ||||
| // Accessing the CPU VTOR register is done in assembly since it's the only way that's common to all current tool | ||||
| unsigned long get_vtor() { return HW_REG(0xE000ED08); } // Even if it looks like an error, it is not an error | ||||
| void * hook_get_hardfault_vector_address(unsigned vtor)  { return (void*)(vtor + 0x03); } | ||||
| void * hook_get_memfault_vector_address(unsigned vtor)   { return (void*)(vtor + 0x04); } | ||||
| void * hook_get_busfault_vector_address(unsigned vtor)   { return (void*)(vtor + 0x05); } | ||||
| void * hook_get_usagefault_vector_address(unsigned vtor) { return (void*)(vtor + 0x06); } | ||||
| void * hook_get_reserved_vector_address(unsigned vtor)   { return (void*)(vtor + 0x07); } | ||||
|  | ||||
| // Common exception frame for ARM, should work for all ARM CPU | ||||
| // Described here (modified for convenience): https://interrupt.memfault.com/blog/cortex-m-fault-debug | ||||
| struct __attribute__((packed)) ContextStateFrame { | ||||
|   uint32_t r0; | ||||
|   uint32_t r1; | ||||
|   uint32_t r2; | ||||
|   uint32_t r3; | ||||
|   uint32_t r12; | ||||
|   uint32_t lr; | ||||
|   uint32_t pc; | ||||
|   uint32_t xpsr; | ||||
| }; | ||||
|  | ||||
| struct __attribute__((packed)) ContextSavedFrame { | ||||
|   uint32_t R0; | ||||
|   uint32_t R1; | ||||
|   uint32_t R2; | ||||
|   uint32_t R3; | ||||
|   uint32_t R12; | ||||
|   uint32_t LR; | ||||
|   uint32_t PC; | ||||
|   uint32_t XPSR; | ||||
|  | ||||
|   uint32_t CFSR; | ||||
|   uint32_t HFSR; | ||||
|   uint32_t DFSR; | ||||
|   uint32_t AFSR; | ||||
|   uint32_t MMAR; | ||||
|   uint32_t BFAR; | ||||
|  | ||||
|   uint32_t ESP; | ||||
|   uint32_t ELR; | ||||
| }; | ||||
|  | ||||
| #if DISABLED(STM32F0xx) | ||||
|   extern "C" | ||||
|   __attribute__((naked)) void CommonHandler_ASM() { | ||||
|     __asm__ __volatile__ ( | ||||
|       // Bit 2 of LR tells which stack pointer to use (either main or process, only main should be used anyway) | ||||
|       "tst lr, #4\n" | ||||
|       "ite eq\n" | ||||
|       "mrseq r0, msp\n" | ||||
|       "mrsne r0, psp\n" | ||||
|       // Save the LR in use when being interrupted | ||||
|       "mov r1, lr\n" | ||||
|       // Get the exception number from the ICSR register | ||||
|       "ldr r2, =0xE000ED00\n" | ||||
|       "ldr r2, [r2, #4]\n" | ||||
|       "b CommonHandler_C\n" | ||||
|     ); | ||||
|   } | ||||
| #else // Cortex M0 does not support conditional mov and testing with a constant, so let's have a specific handler for it | ||||
|   extern "C" | ||||
|   __attribute__((naked)) void CommonHandler_ASM() { | ||||
|     __asm__ __volatile__ ( | ||||
|       ".syntax unified\n" | ||||
|       // Save the LR in use when being interrupted | ||||
|       "mov  r1, lr\n" | ||||
|       // Get the exception number from the ICSR register | ||||
|       "ldr  r2, =0xE000ED00\n" | ||||
|       "ldr  r2, [r2, #4]\n" | ||||
|       "movs r0, #4\n" | ||||
|       "tst  r1, r0\n" | ||||
|       "beq _MSP\n" | ||||
|       "mrs  r0, psp\n" | ||||
|       "b CommonHandler_C\n" | ||||
|       "_MSP:\n" | ||||
|       "mrs  r0, msp\n" | ||||
|       "b CommonHandler_C\n" | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   #if DISABLED(DYNAMIC_VECTORTABLE) // Cortex M0 requires the handler's address to be within 32kB to the actual function to be able to branch to it | ||||
|     extern "C" { | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_hardfault(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_busfault(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_usagefault(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_memmanage(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_nmi(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception7(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception8(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception9(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception10(); | ||||
|       void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception13(); | ||||
|     } | ||||
|     //TODO When going off from libmaple, you'll need to replace those by the one from STM32/HAL_MinSerial.cpp | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| // Must be a macro to avoid creating a function frame | ||||
| #define HALT_IF_DEBUGGING()             \ | ||||
|   do {                                  \ | ||||
|     if (HW_REG(0xE000EDF0) & _BV(0)) {  \ | ||||
|       __asm("bkpt 1");                  \ | ||||
|     }                                   \ | ||||
| } while (0) | ||||
|  | ||||
| // Resume from a fault (if possible) so we can still use interrupt based code for serial output | ||||
| // In that case, we will not jump back to the faulty code, but instead to a dumping code and then a | ||||
| // basic loop with watchdog calling or manual resetting | ||||
| static ContextSavedFrame savedFrame; | ||||
| static uint8_t           lastCause; | ||||
| bool resume_from_fault() { | ||||
|   static const char* causestr[] = { "Thread", "Rsvd", "NMI", "Hard", "Mem", "Bus", "Usage", "7", "8", "9", "10", "SVC", "Dbg", "13", "PendSV", "SysTk", "IRQ" }; | ||||
|   // Reinit the serial link (might only work if implemented in each of your boards) | ||||
|   MinSerial::init(); | ||||
|  | ||||
|   MinSerial::TX("\n\n## Software Fault detected ##\n"); | ||||
|   MinSerial::TX("Cause: "); MinSerial::TX(causestr[min(lastCause, (uint8_t)16)]); MinSerial::TX('\n'); | ||||
|  | ||||
|   MinSerial::TX("R0   : "); MinSerial::TXHex(savedFrame.R0);   MinSerial::TX('\n'); | ||||
|   MinSerial::TX("R1   : "); MinSerial::TXHex(savedFrame.R1);   MinSerial::TX('\n'); | ||||
|   MinSerial::TX("R2   : "); MinSerial::TXHex(savedFrame.R2);   MinSerial::TX('\n'); | ||||
|   MinSerial::TX("R3   : "); MinSerial::TXHex(savedFrame.R3);   MinSerial::TX('\n'); | ||||
|   MinSerial::TX("R12  : "); MinSerial::TXHex(savedFrame.R12);  MinSerial::TX('\n'); | ||||
|   MinSerial::TX("LR   : "); MinSerial::TXHex(savedFrame.LR);   MinSerial::TX('\n'); | ||||
|   MinSerial::TX("PC   : "); MinSerial::TXHex(savedFrame.PC);   MinSerial::TX('\n'); | ||||
|   MinSerial::TX("PSR  : "); MinSerial::TXHex(savedFrame.XPSR); MinSerial::TX('\n'); | ||||
|  | ||||
|   // Configurable Fault Status Register | ||||
|   // Consists of MMSR, BFSR and UFSR | ||||
|   MinSerial::TX("CFSR : "); MinSerial::TXHex(savedFrame.CFSR); MinSerial::TX('\n'); | ||||
|  | ||||
|   // Hard Fault Status Register | ||||
|   MinSerial::TX("HFSR : "); MinSerial::TXHex(savedFrame.HFSR); MinSerial::TX('\n'); | ||||
|  | ||||
|   // Debug Fault Status Register | ||||
|   MinSerial::TX("DFSR : "); MinSerial::TXHex(savedFrame.DFSR); MinSerial::TX('\n'); | ||||
|  | ||||
|   // Auxiliary Fault Status Register | ||||
|   MinSerial::TX("AFSR : "); MinSerial::TXHex(savedFrame.AFSR); MinSerial::TX('\n'); | ||||
|  | ||||
|   // Read the Fault Address Registers. These may not contain valid values. | ||||
|   // Check BFARVALID/MMARVALID to see if they are valid values | ||||
|   // MemManage Fault Address Register | ||||
|   MinSerial::TX("MMAR : "); MinSerial::TXHex(savedFrame.MMAR); MinSerial::TX('\n'); | ||||
|  | ||||
|   // Bus Fault Address Register | ||||
|   MinSerial::TX("BFAR : "); MinSerial::TXHex(savedFrame.BFAR); MinSerial::TX('\n'); | ||||
|  | ||||
|   MinSerial::TX("ExcLR: "); MinSerial::TXHex(savedFrame.ELR); MinSerial::TX('\n'); | ||||
|   MinSerial::TX("ExcSP: "); MinSerial::TXHex(savedFrame.ESP); MinSerial::TX('\n'); | ||||
|  | ||||
|   // The stack pointer is pushed by 8 words upon entering an exception, so we need to revert this | ||||
|   backtrace_ex(savedFrame.ESP + 8*4, savedFrame.LR, savedFrame.PC); | ||||
|  | ||||
|   // Call the last resort function here | ||||
|   hook_last_resort_func(); | ||||
|  | ||||
|   const uint32_t start = millis(), end = start + 100; // 100ms should be enough | ||||
|   // We need to wait for the serial buffers to be output but we don't know for how long | ||||
|   // So we'll just need to refresh the watchdog for a while and then stop for the system to reboot | ||||
|   uint32_t last = start; | ||||
|   while (PENDING(last, end)) { | ||||
|     watchdog_refresh(); | ||||
|     while (millis() == last) { /* nada */ } | ||||
|     last = millis(); | ||||
|     MinSerial::TX('.'); | ||||
|   } | ||||
|  | ||||
|   // Reset now by reinstantiating the bootloader's vector table | ||||
|   HW_REG(0xE000ED08) = 0; | ||||
|   // Restart watchdog | ||||
|   #if DISABLED(USE_WATCHDOG) | ||||
|     // No watchdog, let's perform ARMv7 reset instead by writing to AIRCR register with VECTKEY set to SYSRESETREQ | ||||
|     HW_REG(0xE000ED0C) = (HW_REG(0xE000ED0C) & 0x0000FFFF) | 0x05FA0004; | ||||
|   #endif | ||||
|  | ||||
|   while(1) {} // Bad luck, nothing worked | ||||
| } | ||||
|  | ||||
| // Make sure the compiler does not optimize the frame argument away | ||||
| extern "C" | ||||
| __attribute__((optimize("O0"))) | ||||
| void CommonHandler_C(ContextStateFrame * frame, unsigned long lr, unsigned long cause) { | ||||
|  | ||||
|   // If you are using it'll stop here | ||||
|   HALT_IF_DEBUGGING(); | ||||
|  | ||||
|   // Save the state to backtrace later on (don't call memcpy here since the stack can be corrupted) | ||||
|   savedFrame.R0  = frame->r0; | ||||
|   savedFrame.R1  = frame->r1; | ||||
|   savedFrame.R2  = frame->r2; | ||||
|   savedFrame.R3  = frame->r3; | ||||
|   savedFrame.R12 = frame->r12; | ||||
|   savedFrame.LR  = frame->lr; | ||||
|   savedFrame.PC  = frame->pc; | ||||
|   savedFrame.XPSR= frame->xpsr; | ||||
|   lastCause = cause & 0x1FF; | ||||
|  | ||||
|   volatile uint32_t &CFSR = HW_REG(0xE000ED28); | ||||
|   savedFrame.CFSR = CFSR; | ||||
|   savedFrame.HFSR = HW_REG(0xE000ED2C); | ||||
|   savedFrame.DFSR = HW_REG(0xE000ED30); | ||||
|   savedFrame.AFSR = HW_REG(0xE000ED3C); | ||||
|   savedFrame.MMAR = HW_REG(0xE000ED34); | ||||
|   savedFrame.BFAR = HW_REG(0xE000ED38); | ||||
|   savedFrame.ESP  = (unsigned long)frame; // Even on return, this should not be overwritten by the CPU | ||||
|   savedFrame.ELR  = lr; | ||||
|  | ||||
|   // First check if we can resume from this exception to our own handler safely | ||||
|   // If we can, then we don't need to disable interrupts and the usual serial code | ||||
|   // can be used | ||||
|  | ||||
|   //const uint32_t non_usage_fault_mask = 0x0000FFFF; | ||||
|   //const bool non_usage_fault_occurred = (CFSR & non_usage_fault_mask) != 0; | ||||
|   // the bottom 8 bits of the xpsr hold the exception number of the | ||||
|   // executing exception or 0 if the processor is in Thread mode | ||||
|   const bool faulted_from_exception = ((frame->xpsr & 0xFF) != 0); | ||||
|   if (!faulted_from_exception) { // Not sure about the non_usage_fault, we want to try anyway, don't we ? && !non_usage_fault_occurred) | ||||
|     // Try to resume to our handler here | ||||
|     CFSR |= CFSR; // The ARM programmer manual says you must write to 1 all fault bits to clear them so this instruction is correct | ||||
|     // The frame will not be valid when returning anymore, let's clean it | ||||
|     savedFrame.CFSR = 0; | ||||
|  | ||||
|     frame->pc = (uint32_t)resume_from_fault; // Patch where to return to | ||||
|     frame->lr = 0xdeadbeef;  // If our handler returns (it shouldn't), let's make it trigger an exception immediately | ||||
|     frame->xpsr = _BV(24);   // Need to clean the PSR register to thumb II only | ||||
|     MinSerial::force_using_default_output = true; | ||||
|     return; // The CPU will resume in our handler hopefully, and we'll try to use default serial output | ||||
|   } | ||||
|  | ||||
|   // Sorry, we need to emergency code here since the fault is too dangerous to recover from | ||||
|   MinSerial::force_using_default_output = false; | ||||
|   resume_from_fault(); | ||||
| } | ||||
|  | ||||
| void hook_cpu_exceptions() { | ||||
|   #if ENABLED(DYNAMIC_VECTORTABLE) | ||||
|     // On ARM 32bits CPU, the vector table is like this: | ||||
|     // 0x0C => Hardfault | ||||
|     // 0x10 => MemFault | ||||
|     // 0x14 => BusFault | ||||
|     // 0x18 => UsageFault | ||||
|  | ||||
|     // Unfortunately, it's usually run from flash, and we can't write to flash here directly to hook our instruction | ||||
|     // We could set an hardware breakpoint, and hook on the fly when it's being called, but this | ||||
|     // is hard to get right and would probably break debugger when attached | ||||
|  | ||||
|     // So instead, we'll allocate a new vector table filled with the previous value except | ||||
|     // for the fault we are interested in. | ||||
|     // Now, comes the issue to figure out what is the current vector table size | ||||
|     // There is nothing telling us what is the vector table as it's per-cpu vendor specific. | ||||
|     // BUT: we are being called at the end of the setup, so we assume the setup is done | ||||
|     // Thus, we can read the current vector table until we find an address that's not in flash, and it would mark the | ||||
|     // end of the vector table (skipping the fist entry obviously) | ||||
|     // The position of the program in flash is expected to be at 0x08xxx xxxx on all known platform for ARM and the | ||||
|     // flash size is available via register 0x1FFFF7E0 on STM32 family, but it's not the case for all ARM boards | ||||
|     // (accessing this register might trigger a fault if it's not implemented). | ||||
|  | ||||
|     // So we'll simply mask the top 8 bits of the first handler as an hint of being in the flash or not -that's poor and will | ||||
|     // probably break if the flash happens to be more than 128MB, but in this case, we are not magician, we need help from outside. | ||||
|  | ||||
|     unsigned long * vecAddr = (unsigned long*)get_vtor(); | ||||
|     SERIAL_ECHO("Vector table addr: "); | ||||
|     SERIAL_PRINTLN(get_vtor(), HEX); | ||||
|  | ||||
|     #ifdef VECTOR_TABLE_SIZE | ||||
|       uint32_t vec_size = VECTOR_TABLE_SIZE; | ||||
|       alignas(128) static unsigned long vectable[VECTOR_TABLE_SIZE] ; | ||||
|     #else | ||||
|       #ifndef IS_IN_FLASH | ||||
|         #define IS_IN_FLASH(X) (((unsigned long)X & 0xFF000000) == 0x08000000) | ||||
|       #endif | ||||
|  | ||||
|       // When searching for the end of the vector table, this acts as a limit not to overcome | ||||
|       #ifndef VECTOR_TABLE_SENTINEL | ||||
|         #define VECTOR_TABLE_SENTINEL 80 | ||||
|       #endif | ||||
|  | ||||
|       // Find the vector table size | ||||
|       uint32_t vec_size = 1; | ||||
|       while (IS_IN_FLASH(vecAddr[vec_size]) && vec_size < VECTOR_TABLE_SENTINEL) | ||||
|         vec_size++; | ||||
|  | ||||
|       // We failed to find a valid vector table size, let's abort hooking up | ||||
|       if (vec_size == VECTOR_TABLE_SENTINEL) return; | ||||
|       // Poor method that's wasting RAM here, but allocating with malloc and alignment would be worst | ||||
|       // 128 bytes alignement is required for writing the VTOR register | ||||
|       alignas(128) static unsigned long vectable[VECTOR_TABLE_SENTINEL]; | ||||
|  | ||||
|       SERIAL_ECHO("Detected vector table size: "); | ||||
|       SERIAL_PRINTLN(vec_size, HEX); | ||||
|     #endif | ||||
|  | ||||
|     uint32_t defaultFaultHandler = vecAddr[(unsigned)7]; | ||||
|     // Copy the current vector table into the new table | ||||
|     for (uint32_t i = 0; i < vec_size; i++) { | ||||
|       vectable[i] = vecAddr[i]; | ||||
|       // Replace all default handler by our own handler | ||||
|       if (vectable[i] == defaultFaultHandler) | ||||
|         vectable[i] = (unsigned long)&CommonHandler_ASM; | ||||
|     } | ||||
|  | ||||
|     // Let's hook now with our functions | ||||
|     vectable[(unsigned long)hook_get_hardfault_vector_address(0)]  = (unsigned long)&CommonHandler_ASM; | ||||
|     vectable[(unsigned long)hook_get_memfault_vector_address(0)]   = (unsigned long)&CommonHandler_ASM; | ||||
|     vectable[(unsigned long)hook_get_busfault_vector_address(0)]   = (unsigned long)&CommonHandler_ASM; | ||||
|     vectable[(unsigned long)hook_get_usagefault_vector_address(0)] = (unsigned long)&CommonHandler_ASM; | ||||
|  | ||||
|     // Finally swap with our own vector table | ||||
|     // This is supposed to be atomic, but let's do that with interrupt disabled | ||||
|  | ||||
|     HW_REG(0xE000ED08) = (unsigned long)vectable | _BV32(29); // 29th bit is for telling the CPU the table is now in SRAM (should be present already) | ||||
|  | ||||
|     SERIAL_ECHOLN("Installed fault handlers"); | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| #endif // __arm__ || __thumb__ | ||||
							
								
								
									
										28
									
								
								Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #include "exception_hook.h" | ||||
|  | ||||
| void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned) { return 0; } | ||||
| void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned) { return 0; } | ||||
| void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned) { return 0; } | ||||
| void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned) { return 0; } | ||||
| void __attribute__((weak)) hook_last_resort_func() {} | ||||
							
								
								
									
										54
									
								
								Marlin/src/HAL/shared/cpu_exception/exception_hook.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Marlin/src/HAL/shared/cpu_exception/exception_hook.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| /* Here is the expected behavior of a system producing a CPU exception with this hook installed: | ||||
|    1. Before the system is crashed | ||||
|      1.1 Upon validation (not done yet in this code, but we could be using DEBUG flags here to allow/disallow hooking) | ||||
|      1.2 Install the hook by overwriting the vector table exception handler with the hooked function | ||||
|    2. Upon system crash (for example, by a dereference of a NULL pointer or anything else) | ||||
|      2.1 The CPU triggers its exception and jump into the vector table for the exception type | ||||
|      2.2 Instead of finding the default handler, it finds the updated pointer to our hook | ||||
|      2.3 The CPU jumps into our hook function (likely a naked function to keep all information about crash point intact) | ||||
|      2.4 The hook (naked) function saves the important registers (stack pointer, program counter, current mode) and jumps to a common exception handler (in C) | ||||
|      2.5 The common exception handler dumps the registers on the serial link and perform a backtrace around the crashing point | ||||
|      2.6 Once the backtrace is performed the last resort function is called (platform specific). | ||||
|          On some platform with a LCD screen, this might display the crash information as a QR code or as text for the | ||||
|          user to capture by taking a picture | ||||
|      2.7 The CPU is reset and/or halted by triggering a debug breakpoint if a debugger is attached */ | ||||
|  | ||||
| // Hook into CPU exception interrupt table to call the backtracing code upon an exception | ||||
| // Most platform will simply do nothing here, but those who can will install/overwrite the default exception handler | ||||
| // with a more performant exception handler | ||||
| void hook_cpu_exceptions(); | ||||
|  | ||||
| // Some platform might deal without a hard fault handler, in that case, return 0 in your platform here or skip implementing it | ||||
| void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned base_address); | ||||
| // Some platform might deal without a memory management fault handler, in that case, return 0 in your platform here or skip implementing it | ||||
| void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned base_address); | ||||
| // Some platform might deal without a bus fault handler, in that case, return 0 in your platform here or skip implementing it | ||||
| void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned base_address); | ||||
| // Some platform might deal without a usage fault handler, in that case, return 0 in your platform here or skip implementing it | ||||
| void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned base_address); | ||||
|  | ||||
| // Last resort function that can be called after the exception handler was called. | ||||
| void __attribute__((weak)) hook_last_resort_func(); | ||||
| @@ -36,6 +36,7 @@ | ||||
|  | ||||
| #include "HAL/shared/Delay.h" | ||||
| #include "HAL/shared/esp_wifi.h" | ||||
| #include "HAL/shared/cpu_exception/exception_hook.h" | ||||
|  | ||||
| #ifdef ARDUINO | ||||
|   #include <pins_arduino.h> | ||||
| @@ -935,6 +936,8 @@ void setup() { | ||||
|     while (/*!WIFISERIAL && */PENDING(millis(), serial_connect_timeout)) { /*nada*/ } | ||||
|   #endif | ||||
|  | ||||
|   TERN_(DYNAMIC_VECTORTABLE, hook_cpu_exceptions()); // If supported, install Marlin exception handlers at runtime | ||||
|  | ||||
|   SETUP_RUN(HAL_init()); | ||||
|  | ||||
|   // Init and disable SPI thermocouples; this is still needed | ||||
|   | ||||
| @@ -152,8 +152,7 @@ | ||||
|         NOMORE(len, FLASH_SIZE - addr); | ||||
|         if (parser.seenval('X')) { | ||||
|           // TODO: Write the hex bytes after the X | ||||
|           //while (len--) { | ||||
|           //} | ||||
|           //while (len--) {} | ||||
|         } | ||||
|         else { | ||||
|           // while (len--) { | ||||
| @@ -180,7 +179,25 @@ | ||||
|         for (int i = 10000; i--;) DELAY_US(1000UL); | ||||
|         ENABLE_ISRS(); | ||||
|         SERIAL_ECHOLNPGM("FAILURE: Watchdog did not trigger board reset."); | ||||
|       } break; | ||||
|  | ||||
|       #if ENABLED(POSTMORTEM_DEBUGGING) | ||||
|       case 451: { // Trigger all kind of faults to test exception catcher | ||||
|         SERIAL_ECHOLNPGM("Disabling heaters"); | ||||
|         thermalManager.disable_all_heaters(); | ||||
|         delay(1000); // Allow time to print | ||||
|         volatile uint8_t type[5] = { parser.byteval('T', 1) }; | ||||
|  | ||||
|         // The code below is obviously wrong and it's full of quirks to fool the compiler from optimizing away the code | ||||
|         switch (type[0]) { | ||||
|           case 1: default: *(int*)0 = 451; break; // Write at bad address | ||||
|           case 2: { volatile int a = 0; volatile int b = 452 / a; *(int*)&a = b; } break; // Divide by zero (some CPUs accept this, like ARM) | ||||
|           case 3: { *(uint32_t*)&type[1] = 453; volatile int a = *(int*)&type[1]; type[0] = a / 255; } break; // Unaligned access (some CPUs accept this) | ||||
|           case 4: { volatile void (*func)() = (volatile void (*)()) 0xE0000000; func(); } break; // Invalid instruction | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       #endif | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
							
								
								
									
										104
									
								
								buildroot/share/PlatformIO/scripts/exc.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								buildroot/share/PlatformIO/scripts/exc.S
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| /* ***************************************************************************** | ||||
|  * The MIT License | ||||
|  * | ||||
|  * Copyright (c) 2010 Perry Hung. | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * ****************************************************************************/ | ||||
|  | ||||
| # On an exception, push a fake stack thread mode stack frame and redirect | ||||
| # thread execution to a thread mode error handler | ||||
|  | ||||
| # From RM008: | ||||
| # The SP is decremented by eight words by the completion of the stack push. | ||||
| # Figure 5-1 shows the contents of the stack after an exception pre-empts the | ||||
| # current program flow. | ||||
| # | ||||
| # Old SP--> <previous> | ||||
| #           xPSR | ||||
| #           PC | ||||
| #           LR | ||||
| #           r12 | ||||
| #           r3 | ||||
| #           r2 | ||||
| #           r1 | ||||
| #    SP-->  r0 | ||||
|  | ||||
| .text | ||||
| .globl __exc_nmi | ||||
| .weak  __exc_nmi | ||||
| .globl __exc_hardfault | ||||
| .weak  __exc_hardfault | ||||
| .globl __exc_memmanage | ||||
| .weak  __exc_memmanage | ||||
| .globl __exc_busfault | ||||
| .weak  __exc_busfault | ||||
| .globl __exc_usagefault | ||||
| .weak  __exc_usagefault | ||||
|  | ||||
| .code 16 | ||||
| .thumb_func | ||||
| __exc_nmi: | ||||
|     mov r0, #1 | ||||
|     b __default_exc | ||||
|  | ||||
| .thumb_func | ||||
| __exc_hardfault: | ||||
|     mov r0, #2 | ||||
|     b __default_exc | ||||
|  | ||||
| .thumb_func | ||||
| __exc_memmanage: | ||||
|     mov r0, #3 | ||||
|     b __default_exc | ||||
|  | ||||
| .thumb_func | ||||
| __exc_busfault: | ||||
|     mov r0, #4 | ||||
|     b __default_exc | ||||
|  | ||||
| .thumb_func | ||||
| __exc_usagefault: | ||||
|     mov r0, #5 | ||||
|     b __default_exc | ||||
|  | ||||
| .thumb_func | ||||
| __default_exc: | ||||
|     ldr r2, NVIC_CCR            @ Enable returning to thread mode even if there are | ||||
|     mov r1 ,#1                  @ pending exceptions. See flag NONEBASETHRDENA. | ||||
|     str r1, [r2] | ||||
|     cpsid i                     @ Disable global interrupts | ||||
|     ldr r2, SYSTICK_CSR         @ Disable systick handler | ||||
|     mov r1, #0 | ||||
|     str r1, [r2] | ||||
|     ldr r1, CPSR_MASK           @ Set default CPSR | ||||
|     push {r1} | ||||
|     ldr r1, TARGET_PC           @ Set target pc | ||||
|     push {r1} | ||||
|     sub sp, sp, #24             @ Don't care | ||||
|     ldr r1, EXC_RETURN          @ Return to thread mode | ||||
|     mov lr, r1 | ||||
|     bx lr                       @ Exception exit | ||||
|  | ||||
| .align 4 | ||||
| CPSR_MASK:     .word 0x61000000 | ||||
| EXC_RETURN:    .word 0xFFFFFFF9 | ||||
| TARGET_PC:     .word __error | ||||
| NVIC_CCR:      .word 0xE000ED14    @ NVIC configuration control register | ||||
| SYSTICK_CSR:   .word 0xE000E010    @ Systick control register | ||||
							
								
								
									
										29
									
								
								buildroot/share/PlatformIO/scripts/fix_framework_weakness.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								buildroot/share/PlatformIO/scripts/fix_framework_weakness.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| from os.path import join, isfile | ||||
| import shutil | ||||
| from pprint import pprint | ||||
|  | ||||
| Import("env") | ||||
|  | ||||
| if env.MarlinFeatureIsEnabled("POSTMORTEM_DEBUGGING"): | ||||
|     FRAMEWORK_DIR = env.PioPlatform().get_package_dir("framework-arduinoststm32-maple") | ||||
|     patchflag_path = join(FRAMEWORK_DIR, ".exc-patching-done") | ||||
|  | ||||
|     # patch file only if we didn't do it before | ||||
|     if not isfile(patchflag_path): | ||||
|         print("Patching libmaple exception handlers") | ||||
|         original_file = join(FRAMEWORK_DIR, "STM32F1", "cores", "maple", "libmaple", "exc.S") | ||||
|         backup_file = join(FRAMEWORK_DIR, "STM32F1", "cores", "maple", "libmaple", "exc.S.bak") | ||||
|         src_file = join("buildroot", "share", "PlatformIO", "scripts", "exc.S") | ||||
|  | ||||
|         assert isfile(original_file) and isfile(src_file) | ||||
|         shutil.copyfile(original_file, backup_file) | ||||
|         shutil.copyfile(src_file, original_file); | ||||
|  | ||||
|         def _touch(path): | ||||
|             with open(path, "w") as fp: | ||||
|                 fp.write("") | ||||
|  | ||||
|         env.Execute(lambda *args, **kwargs: _touch(patchflag_path)) | ||||
|         print("Done patching exception handler") | ||||
|  | ||||
|     print("Libmaple modified and ready for post mortem debugging") | ||||
| @@ -64,6 +64,7 @@ default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared> | ||||
|   -<src/sd/usb_flashdrive/Sd2Card_FlashDrive.cpp> | ||||
|   -<src/sd/cardreader.cpp> -<src/sd/Sd2Card.cpp> -<src/sd/SdBaseFile.cpp> -<src/sd/SdFatUtil.cpp> -<src/sd/SdFile.cpp> -<src/sd/SdVolume.cpp> -<src/gcode/sd> | ||||
|   -<src/HAL/shared/backtrace> | ||||
|   -<src/HAL/shared/cpu_exception> | ||||
|   -<src/HAL/shared/eeprom_if_i2c.cpp> | ||||
|   -<src/HAL/shared/eeprom_if_spi.cpp> | ||||
|   -<src/feature/babystep.cpp> | ||||
| @@ -223,6 +224,8 @@ YHCB2004                = red-scorp/LiquidCrystal_AIP31068@^1.0.4, red-scorp/Sof | ||||
| HAS_TFT_LVGL_UI         = lvgl=https://github.com/makerbase-mks/LVGL-6.1.1-MKS/archive/master.zip | ||||
|                           src_filter=+<src/lcd/extui/lib/mks_ui> | ||||
|                           extra_scripts=download_mks_assets.py | ||||
| POSTMORTEM_DEBUGGING    = src_filter=+<src/HAL/shared/cpu_exception> +<src/HAL/shared/backtrace> | ||||
|                           build_flags=-funwind-tables | ||||
| MKS_WIFI_MODULE         = QRCode=https://github.com/makerbase-mks/QRCode/archive/master.zip | ||||
| HAS_TRINAMIC_CONFIG     = TMCStepper@~0.7.1 | ||||
|                           src_filter=+<src/feature/tmc_util.cpp> +<src/module/stepper/trinamic.cpp> +<src/gcode/feature/trinamic/M122.cpp> +<src/gcode/feature/trinamic/M906.cpp> +<src/gcode/feature/trinamic/M911-M914.cpp> | ||||
| @@ -637,14 +640,6 @@ platform = atmelsam | ||||
| extends  = env:DUE | ||||
| board    = dueUSB | ||||
|  | ||||
| [env:DUE_debug] | ||||
| # Used when WATCHDOG_RESET_MANUAL is enabled | ||||
| platform    = atmelsam | ||||
| extends     = env:DUE | ||||
| build_flags = ${common.build_flags} | ||||
|   -funwind-tables | ||||
|   -mpoke-function-name | ||||
|  | ||||
| # | ||||
| # Archim SAM | ||||
| # | ||||
| @@ -662,12 +657,6 @@ extra_scripts            = ${common.extra_scripts} | ||||
| platform = ${common_DUE_archim.platform} | ||||
| extends  = common_DUE_archim | ||||
|  | ||||
| # Used when WATCHDOG_RESET_MANUAL is enabled | ||||
| [env:DUE_archim_debug] | ||||
| platform    = ${common_DUE_archim.platform} | ||||
| extends     = common_DUE_archim | ||||
| build_flags = ${common_DUE_archim.build_flags} -funwind-tables -mpoke-function-name | ||||
|  | ||||
| ################################# | ||||
| #                               # | ||||
| #      SAMD51 Architecture      # | ||||
| @@ -763,6 +752,8 @@ lib_ignore        = SPI, FreeRTOS701, FreeRTOS821 | ||||
| lib_deps          = ${common.lib_deps} | ||||
|   SoftwareSerialM | ||||
| platform_packages = tool-stm32duino | ||||
| extra_scripts     = ${common.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/fix_framework_weakness.py | ||||
|  | ||||
| # | ||||
| # STM32F103RC | ||||
| @@ -788,7 +779,7 @@ build_flags       = ${common_stm32f1.build_flags} | ||||
|                     -DUSE_USB_COMPOSITE | ||||
|                     -DVECT_TAB_OFFSET=0x2000 | ||||
|                     -DGENERIC_BOOTLOADER | ||||
| extra_scripts     = ${common.extra_scripts} | ||||
| extra_scripts     = ${common_stm32f1.extra_scripts} | ||||
|   pre:buildroot/share/PlatformIO/scripts/STM32F1_create_variant.py | ||||
|   buildroot/share/PlatformIO/scripts/STM32F103RC_MEEB_3DP.py | ||||
| lib_deps          = ${common.lib_deps} | ||||
| @@ -934,7 +925,7 @@ upload_protocol   = serial | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = common_stm32f1 | ||||
| board         = genericSTM32F103VE | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/STM32F103VE_longer.py | ||||
| build_flags   = ${common_stm32f1.build_flags} | ||||
|   -DMCU_STM32F103VE -DSTM32F1xx -USERIAL_USB -DU20 -DTS_V12 | ||||
| @@ -948,7 +939,7 @@ build_unflags = ${common_stm32f1.build_unflags} | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = common_stm32f1 | ||||
| board         = genericSTM32F103VE | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/mks_robin_mini.py | ||||
| build_flags   = ${common_stm32f1.build_flags} | ||||
|   -DMCU_STM32F103VE | ||||
| @@ -974,7 +965,7 @@ upload_protocol = jlink | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = common_stm32f1 | ||||
| board         = genericSTM32F103ZE | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/mks_robin.py | ||||
| build_flags   = ${common_stm32f1.build_flags} | ||||
|   -DSS_TIMER=4 -DSTM32_XL_DENSITY | ||||
| @@ -1008,7 +999,7 @@ lib_deps             = | ||||
| [env:mks_robin_pro] | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = env:mks_robin | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/mks_robin_pro.py | ||||
|  | ||||
| # | ||||
| @@ -1017,7 +1008,7 @@ extra_scripts = ${common.extra_scripts} | ||||
| [env:trigorilla_pro] | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = env:mks_robin | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|  | ||||
| # | ||||
| # MKS Robin E3D (STM32F103RCT6) and | ||||
| @@ -1041,7 +1032,7 @@ build_flags   = ${common_stm32f1.build_flags} | ||||
| platform        = ${common_stm32f1.platform} | ||||
| extends         = common_stm32f1 | ||||
| board           = genericSTM32F103VE | ||||
| extra_scripts   = ${common.extra_scripts} | ||||
| extra_scripts   = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/mks_robin_e3p.py | ||||
| build_flags     = ${common_stm32f1.build_flags} | ||||
|   -DMCU_STM32F103VE -DSS_TIMER=4 | ||||
| @@ -1055,7 +1046,7 @@ upload_protocol = jlink | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = common_stm32f1 | ||||
| board         = genericSTM32F103RC | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/mks_robin_lite.py | ||||
|  | ||||
| # | ||||
| @@ -1065,7 +1056,7 @@ extra_scripts = ${common.extra_scripts} | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = common_stm32f1 | ||||
| board         = genericSTM32F103RC | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/mks_robin_lite3.py | ||||
|  | ||||
| # | ||||
| @@ -1075,7 +1066,7 @@ extra_scripts = ${common.extra_scripts} | ||||
| platform      = ${common_stm32f1.platform} | ||||
| extends       = common_stm32f1 | ||||
| board         = genericSTM32F103ZE | ||||
| extra_scripts = ${common.extra_scripts} | ||||
| extra_scripts = ${common_stm32f1.extra_scripts} | ||||
|   buildroot/share/PlatformIO/scripts/jgaurora_a5s_a1_with_bootloader.py | ||||
| build_flags   = ${common_stm32f1.build_flags} | ||||
|   -DSTM32F1xx -DSTM32_XL_DENSITY | ||||
| @@ -1230,7 +1221,7 @@ extra_scripts     = ${common.extra_scripts} | ||||
| platform          = ${common_stm32f1.platform} | ||||
| extends           = common_stm32f1 | ||||
| board             = genericSTM32F103RC | ||||
| extra_scripts     = ${common.extra_scripts} | ||||
| extra_scripts     = ${common_stm32f1.extra_scripts} | ||||
|  buildroot/share/PlatformIO/scripts/fly_mini.py | ||||
| build_flags       = ${common_stm32f1.build_flags} | ||||
|  -DDEBUG_LEVEL=0 -DSS_TIMER=4 | ||||
| @@ -1557,7 +1548,6 @@ build_flags          = ${common_stm32.build_flags} -DENABLE_HWSERIAL3 -DTIMER_SE | ||||
| build_unflags        = ${common_stm32.build_unflags} -DUSBCON -DUSBD_USE_CDC | ||||
| extra_scripts        = ${common.extra_scripts} pre:buildroot/share/PlatformIO/scripts/generic_create_variant.py buildroot/share/PlatformIO/scripts/stm32_bootloader.py | ||||
|  | ||||
|  | ||||
| ################################# | ||||
| #                               # | ||||
| #      Other Architectures      # | ||||
|   | ||||
		Reference in New Issue
	
	Block a user