diff --git a/Firmware/Dcodes.cpp b/Firmware/Dcodes.cpp index dace219f..5a840ee4 100644 --- a/Firmware/Dcodes.cpp +++ b/Firmware/Dcodes.cpp @@ -23,28 +23,25 @@ void print_hex_byte(uint8_t val) print_hex_nibble(val & 15); } -void print_hex_word(uint16_t val) -{ - print_hex_byte(val >> 8); - print_hex_byte(val & 255); -} +// debug range address type (fits all SRAM/PROGMEM/XFLASH memory ranges) +#if defined(DEBUG_DCODE6) || defined(DEBUG_DCODES) || defined(XFLASH_DUMP) +#include "xflash.h" +#include "xflash_layout.h" -void print_eeprom(uint16_t address, uint16_t count, uint8_t countperline = 16) +#define DADDR_SIZE 32 +typedef uint32_t daddr_t; // XFLASH requires 24 bits +#else +#define DADDR_SIZE 16 +typedef uint16_t daddr_t; +#endif + +void print_hex_word(daddr_t val) { - while (count) - { - print_hex_word(address); - putchar(' '); - uint8_t count_line = countperline; - while (count && count_line) - { - putchar(' '); - print_hex_byte(eeprom_read_byte((uint8_t*)address++)); - count_line--; - count--; - } - putchar('\n'); - } +#if DADDR_SIZE > 16 + print_hex_byte((val >> 16) & 0xFF); +#endif + print_hex_byte((val >> 8) & 0xFF); + print_hex_byte(val & 0xFF); } int parse_hex(char* hex, uint8_t* data, int count) @@ -71,12 +68,16 @@ int parse_hex(char* hex, uint8_t* data, int count) } -void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperline = 16) +enum class dcode_mem_t:uint8_t { sram, eeprom, progmem, xflash }; + +void print_mem(daddr_t address, daddr_t count, dcode_mem_t type, uint8_t countperline = 16) { +#if defined(DEBUG_DCODE6) || defined(DEBUG_DCODES) || defined(XFLASH_DUMP) + if(type == dcode_mem_t::xflash) + XFLASH_SPI_ENTER(); +#endif while (count) { - if (type == 2) - print_hex_nibble(address >> 16); print_hex_word(address); putchar(' '); uint8_t count_line = countperline; @@ -85,19 +86,75 @@ void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperl uint8_t data = 0; switch (type) { - case 0: data = *((uint8_t*)address++); break; - case 1: data = eeprom_read_byte((uint8_t*)address++); break; - case 2: data = pgm_read_byte_far((uint8_t*)address++); break; + case dcode_mem_t::sram: data = *((uint8_t*)address); break; + case dcode_mem_t::eeprom: data = eeprom_read_byte((uint8_t*)address); break; + case dcode_mem_t::progmem: break; +#if defined(DEBUG_DCODE6) || defined(DEBUG_DCODES) || defined(XFLASH_DUMP) + case dcode_mem_t::xflash: xflash_rd_data(address, &data, 1); break; +#else + case dcode_mem_t::xflash: break; +#endif } + ++address; putchar(' '); print_hex_byte(data); count_line--; count--; + + // sporadically call manage_heater, but only when interrupts are enabled (meaning + // print_mem is called by D2). Don't do anything otherwise: we are inside a crash + // handler where memory & stack needs to be preserved! + if((SREG & (1 << SREG_I)) && !((uint16_t)count % 8192)) + manage_heater(); } putchar('\n'); } } +// TODO: this only handles SRAM/EEPROM 16bit addresses +void write_mem(uint16_t address, uint16_t count, const uint8_t* data, const dcode_mem_t type) +{ + for (uint16_t i = 0; i < count; i++) + { + switch (type) + { + case dcode_mem_t::sram: *((uint8_t*)address) = data[i]; break; + case dcode_mem_t::eeprom: eeprom_write_byte((uint8_t*)address, data[i]); break; + case dcode_mem_t::progmem: break; + case dcode_mem_t::xflash: break; + } + ++address; + } +} + +void dcode_core(daddr_t addr_start, const daddr_t addr_end, const dcode_mem_t type, + uint8_t dcode, const char* type_desc) +{ + KEEPALIVE_STATE(NOT_BUSY); + DBG(_N("D%d - Read/Write %S\n"), dcode, type_desc); + daddr_t count = -1; // RW the entire space by default + if (code_seen('A')) + addr_start = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value(); + if (code_seen('C')) + count = code_value_long(); + if (addr_start > addr_end) + addr_start = addr_end; + if ((addr_start + count) > addr_end || (addr_start + count) < addr_start) + count = addr_end - addr_start; + if (code_seen('X')) + { + uint8_t data[16]; + count = parse_hex(strchr_pointer + 1, data, 16); + write_mem(addr_start, count, data, type); +#if DADDR_SIZE > 16 + DBG(_N("%lu bytes written to %S at address 0x%04lx\n"), count, type_desc, addr_start); +#else + DBG(_N("%u bytes written to %S at address 0x%08x\n"), count, type_desc, addr_start); +#endif + } + print_mem(addr_start, count, type); +} + #if defined DEBUG_DCODE3 || defined DEBUG_DCODES #define EEPROM_SIZE 0x1000 /*! @@ -120,46 +177,7 @@ void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperl */ void dcode_3() { - DBG(_N("D3 - Read/Write EEPROM\n")); - uint16_t address = 0x0000; //default 0x0000 - uint16_t count = EEPROM_SIZE; //default 0x1000 (entire eeprom) - if (code_seen('A')) // Address (0x0000-0x0fff) - address = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value(); - if (code_seen('C')) // Count (0x0001-0x1000) - count = (int)code_value(); - address &= 0x1fff; - if (count > EEPROM_SIZE) count = EEPROM_SIZE; - if ((address + count) > EEPROM_SIZE) count = EEPROM_SIZE - address; - if (code_seen('X')) // Data - { - uint8_t data[16]; - count = parse_hex(strchr_pointer + 1, data, 16); - if (count > 0) - { - for (uint16_t i = 0; i < count; i++) - eeprom_write_byte((uint8_t*)(address + i), data[i]); - printf_P(_N("%d bytes written to EEPROM at address 0x%04x"), count, address); - putchar('\n'); - } - else - count = 0; - } - print_mem(address, count, 1); -/* while (count) - { - print_hex_word(address); - putchar(' '); - uint8_t countperline = 16; - while (count && countperline) - { - uint8_t data = eeprom_read_byte((uint8_t*)address++); - putchar(' '); - print_hex_byte(data); - countperline--; - count--; - } - putchar('\n'); - }*/ + dcode_core(0, EEPROM_SIZE, dcode_mem_t::eeprom, 3, _N("EEPROM")); } #endif //DEBUG_DCODE3 @@ -239,68 +257,34 @@ void dcode_1() eeprom_write_byte((unsigned char*)i, (unsigned char)0xff); softReset(); } +#endif +#if defined DEBUG_DCODE2 || defined DEBUG_DCODES /*! ### D2 - Read/Write RAM D3: Read/Write RAM This command can be used without any additional parameters. It will read the entire RAM. #### Usage - + D2 [ A | C | X ] - + #### Parameters - - `A` - Address (x0000-x1fff) - - `C` - Count (1-8192) + - `A` - Address (x0000-x21ff) + - `C` - Count (1-8704) - `X` - Data #### Notes - The hex address needs to be lowercase without the 0 before the x - - Count is decimal + - Count is decimal - The hex data needs to be lowercase - + */ void dcode_2() { - LOG("D2 - Read/Write RAM\n"); - uint16_t address = 0x0000; //default 0x0000 - uint16_t count = 0x2000; //default 0x2000 (entire ram) - if (code_seen('A')) // Address (0x0000-0x1fff) - address = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value(); - if (code_seen('C')) // Count (0x0001-0x2000) - count = (int)code_value(); - address &= 0x1fff; - if (count > 0x2000) count = 0x2000; - if ((address + count) > 0x2000) count = 0x2000 - address; - if (code_seen('X')) // Data - { - uint8_t data[16]; - count = parse_hex(strchr_pointer + 1, data, 16); - if (count > 0) - { - for (uint16_t i = 0; i < count; i++) - *((uint8_t*)(address + i)) = data[i]; - LOG("%d bytes written to RAM at address %04x", count, address); - } - else - count = 0; - } - print_mem(address, count, 0); -/* while (count) - { - print_hex_word(address); - putchar(' '); - uint8_t countperline = 16; - while (count && countperline) - { - uint8_t data = *((uint8_t*)address++); - putchar(' '); - print_hex_byte(data); - countperline--; - count--; - } - putchar('\n'); - }*/ + dcode_core(RAMSTART, RAMEND+1, dcode_mem_t::sram, 2, _N("SRAM")); } +#endif +#ifdef DEBUG_DCODES /*! ### D4 - Read/Write PIN D4: Read/Write PIN @@ -425,17 +409,32 @@ void dcode_5() } #endif //DEBUG_DCODE5 -#ifdef DEBUG_DCODES - +#if defined(XFLASH) && (defined DEBUG_DCODE6 || defined DEBUG_DCODES) /*! ### D6 - Read/Write external FLASH D6: Read/Write external Flash - Reserved + This command can be used without any additional parameters. It will read the entire XFLASH. + #### Usage + + D6 [ A | C | X ] + + #### Parameters + - `A` - Address (x0000-x3ffff) + - `C` - Count (1-262144) + - `X` - Data + + #### Notes + - The hex address needs to be lowercase without the 0 before the x + - Count is decimal + - The hex data needs to be lowercase + - Writing is currently not implemented */ void dcode_6() { - LOG("D6 - Read/Write external FLASH\n"); + dcode_core(0x0, XFLASH_SIZE, dcode_mem_t::xflash, 6, _N("XFLASH")); } +#endif +#ifdef DEBUG_DCODES /*! ### D7 - Read/Write Bootloader D7: Read/Write Bootloader Reserved @@ -927,5 +926,87 @@ void dcode_9125() } #endif //PAT9125 - #endif //DEBUG_DCODES + +#ifdef XFLASH_DUMP +#include "xflash_dump.h" + +void dcode_20() +{ + if(code_seen('E')) + xfdump_full_dump_and_reset(); + else + { + unsigned long ts = _millis(); + xfdump_dump(); + ts = _millis() - ts; + DBG(_N("dump completed in %lums\n"), ts); + } +} + +void dcode_21() +{ + if(!xfdump_check_state()) + DBG(_N("no dump available\n")); + else + { + KEEPALIVE_STATE(NOT_BUSY); + DBG(_N("D21 - read crash dump\n")); + print_mem(DUMP_OFFSET, sizeof(dump_t), dcode_mem_t::xflash); + } +} + +void dcode_22() +{ + if(!xfdump_check_state()) + DBG(_N("no dump available\n")); + else + { + xfdump_reset(); + DBG(_N("dump cleared\n")); + } +} +#endif + +#ifdef EMERGENCY_SERIAL_DUMP +#include "asm.h" +#include "xflash_dump.h" + +bool emergency_serial_dump = false; + +void __attribute__((noinline)) serial_dump_and_reset(dump_crash_reason reason) +{ + uint16_t sp; + uint32_t pc; + + // we're being called from a live state, so shut off interrupts ... + cli(); + + // sample SP/PC + sp = SP; + GETPC(&pc); + + // extend WDT long enough to allow writing the entire stream + wdt_enable(WDTO_8S); + + // ... and heaters + WRITE(FAN_PIN, HIGH); + disable_heater(); + + // this function can also be called from within a corrupted state, so not use + // printf family of functions that use the heap or grow the stack. + SERIAL_ECHOLNPGM("D23 - emergency serial dump"); + SERIAL_ECHOPGM("error: "); + MYSERIAL.print((uint8_t)reason, DEC); + SERIAL_ECHOPGM(" 0x"); + MYSERIAL.print(pc, HEX); + SERIAL_ECHOPGM(" 0x"); + MYSERIAL.println(sp, HEX); + + print_mem(0, RAMEND+1, dcode_mem_t::sram); + SERIAL_ECHOLNRPGM(MSG_OK); + + // reset soon + softReset(); +} +#endif diff --git a/Firmware/Dcodes.h b/Firmware/Dcodes.h index 856d04ad..c4b470e4 100644 --- a/Firmware/Dcodes.h +++ b/Firmware/Dcodes.h @@ -4,7 +4,10 @@ extern void dcode__1(); //D-1 - Endless loop (to simulate deadlock) extern void dcode_0(); //D0 - Reset extern void dcode_1(); //D1 - Clear EEPROM + +#if defined DEBUG_DCODE2 || defined DEBUG_DCODES extern void dcode_2(); //D2 - Read/Write RAM +#endif #if defined DEBUG_DCODE3 || defined DEBUG_DCODES extern void dcode_3(); //D3 - Read/Write EEPROM @@ -16,13 +19,28 @@ extern void dcode_4(); //D4 - Read/Write PIN extern void dcode_5(); //D5 - Read/Write FLASH #endif //DEBUG_DCODE5 +#if defined DEBUG_DCODE6 || defined DEBUG_DCODES extern void dcode_6(); //D6 - Read/Write external FLASH +#endif + extern void dcode_7(); //D7 - Read/Write Bootloader extern void dcode_8(); //D8 - Read/Write PINDA extern void dcode_9(); //D9 - Read/Write ADC (Write=enable simulated, Read=disable simulated) extern void dcode_10(); //D10 - XYZ calibration = OK extern void dcode_12(); //D12 - Log time. Writes the current time in the log file. +#ifdef XFLASH_DUMP +extern void dcode_20(); //D20 - Generate an offline crash dump +extern void dcode_21(); //D21 - Print crash dump to serial +extern void dcode_22(); //D22 - Clear crash dump state +#endif + +#ifdef EMERGENCY_SERIAL_DUMP +#include "xflash_dump.h" +extern bool emergency_serial_dump; +extern void serial_dump_and_reset(dump_crash_reason); +#endif + #ifdef HEATBED_ANALYSIS extern void dcode_80(); //D80 - Bed check. This command will log data to SD card file "mesh.txt". extern void dcode_81(); //D81 - Bed analysis. This command will log data to SD card file "wldsd.txt". diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 98e5102c..c2c5ca93 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -497,6 +497,7 @@ void marlin_wait_for_click(); void raise_z_above(float target, bool plan=true); extern "C" void softReset(); +void stack_error(); extern uint32_t IP_address; diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index c8d74006..5aca69bd 100755 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -66,6 +66,7 @@ #include "menu.h" #include "ultralcd.h" +#include "conv2str.h" #include "backlight.h" #include "planner.h" @@ -108,6 +109,8 @@ #include "optiboot_xflash.h" #endif //XFLASH +#include "xflash_dump.h" + #ifdef BLINKM #include "BlinkM.h" #include "Wire.h" @@ -994,6 +997,58 @@ void list_sec_lang_from_external_flash() #endif //(LANG_MODE != 0) +static void fw_crash_init() +{ +#ifdef XFLASH_DUMP + dump_crash_reason crash_reason; + if(xfdump_check_state(&crash_reason)) + { + // always signal to the host that a dump is available for retrieval + puts_P(_N("// action:dump_available")); + +#ifdef EMERGENCY_DUMP + if(crash_reason != dump_crash_reason::manual && + eeprom_read_byte((uint8_t*)EEPROM_FW_CRASH_FLAG) != 0xFF) + { + lcd_show_fullscreen_message_and_wait_P( + _i("FIRMWARE CRASH!\n" + "Debug data available for analysis. " + "Contact support to submit details.")); + } +#endif + } +#else //XFLASH_DUMP + dump_crash_reason crash_reason = (dump_crash_reason)eeprom_read_byte((uint8_t*)EEPROM_FW_CRASH_FLAG); + if(crash_reason != dump_crash_reason::manual && (uint8_t)crash_reason != 0xFF) + { + lcd_beeper_quick_feedback(); + lcd_clear(); + + lcd_puts_P(_i("FIRMWARE CRASH!\nCrash reason:\n")); + switch(crash_reason) + { + case dump_crash_reason::stack_error: + lcd_puts_P(_i("Static memory has\nbeen overwritten")); + break; + case dump_crash_reason::watchdog: + lcd_puts_P(_i("Watchdog timeout")); + break; + case dump_crash_reason::bad_isr: + lcd_puts_P(_i("Bad interrupt")); + break; + default: + lcd_print((uint8_t)crash_reason); + break; + } + lcd_wait_for_click(); + } +#endif //XFLASH_DUMP + + // prevent crash prompts to reappear once acknowledged + eeprom_update_byte((uint8_t*)EEPROM_FW_CRASH_FLAG, 0xFF); +} + + static void xflash_err_msg() { lcd_clear(); @@ -1612,6 +1667,9 @@ void setup() if (tmc2130_home_enabled == 0xff) tmc2130_home_enabled = 0; #endif //TMC2130 + // report crash failures + fw_crash_init(); + #ifdef UVLO_SUPPORT if (eeprom_read_byte((uint8_t*)EEPROM_UVLO) != 0) { //previous print was terminated by UVLO /* @@ -1657,10 +1715,45 @@ void setup() KEEPALIVE_STATE(NOT_BUSY); #ifdef WATCHDOG wdt_enable(WDTO_4S); +#ifdef EMERGENCY_HANDLERS + WDTCSR |= (1 << WDIE); +#endif //EMERGENCY_HANDLERS #endif //WATCHDOG } +static inline void crash_and_burn(dump_crash_reason reason) +{ + WRITE(BEEPER, HIGH); + eeprom_update_byte((uint8_t*)EEPROM_FW_CRASH_FLAG, (uint8_t)reason); +#ifdef EMERGENCY_DUMP + xfdump_full_dump_and_reset(reason); +#elif defined(EMERGENCY_SERIAL_DUMP) + if(emergency_serial_dump) + serial_dump_and_reset(reason); +#endif + softReset(); +} + +#ifdef EMERGENCY_HANDLERS +#ifdef WATCHDOG +ISR(WDT_vect) +{ + crash_and_burn(dump_crash_reason::watchdog); +} +#endif + +ISR(BADISR_vect) +{ + crash_and_burn(dump_crash_reason::bad_isr); +} +#endif //EMERGENCY_HANDLERS + +void stack_error() { + crash_and_burn(dump_crash_reason::stack_error); +} + + void trace(); #define CHUNK_SIZE 64 // bytes @@ -9080,7 +9173,9 @@ Sigma_Exit: */ case 1: dcode_1(); break; +#endif +#if defined DEBUG_DCODE2 || defined DEBUG_DCODES /*! ### D2 - Read/Write RAM D3: Read/Write RAM This command can be used without any additional parameters. It will read the entire RAM. @@ -9167,7 +9262,7 @@ Sigma_Exit: case 5: dcode_5(); break; #endif //DEBUG_DCODE5 -#ifdef DEBUG_DCODES +#if defined DEBUG_DCODE6 || defined DEBUG_DCODES /*! ### D6 - Read/Write external FLASH D6: Read/Write external Flash @@ -9175,6 +9270,8 @@ Sigma_Exit: */ case 6: dcode_6(); break; +#endif +#ifdef DEBUG_DCODES /*! ### D7 - Read/Write Bootloader D7: Read/Write Bootloader @@ -9228,8 +9325,72 @@ Sigma_Exit: ### D12 - Time D12: Time Writes the current time in the log file. */ - #endif //DEBUG_DCODES + +#ifdef XFLASH_DUMP + /*! + ### D20 - Generate an offline crash dump + Generate a crash dump for later retrival. + #### Usage + + D20 [E] + + ### Parameters + - `E` - Perform an emergency crash dump (resets the printer). + ### Notes + - A crash dump can be later recovered with D21, or cleared with D22. + - An emergency crash dump includes register data, but will cause the printer to reset after the dump + is completed. + */ + case 20: { + dcode_20(); + break; + }; + + /*! + ### D21 - Print crash dump to serial + Output the complete crash dump (if present) to the serial. + #### Usage + + D21 + + ### Notes + - The starting address can vary between builds, but it's always at the beginning of the data section. + */ + case 21: { + dcode_21(); + break; + }; + + /*! + ### D22 - Clear crash dump state + Clear an existing internal crash dump. + #### Usage + + D22 + */ + case 22: { + dcode_22(); + break; + }; +#endif //XFLASH_DUMP + +#ifdef EMERGENCY_SERIAL_DUMP + /*! + ### D23 - Request emergency dump on serial + On boards without offline dump support, request online dumps to the serial port on firmware faults. + When online dumps are enabled, the FW will dump memory on the serial before resetting. + #### Usage + + D23 [R] + #### Parameters + - `R` - Disable online dumps. + */ + case 23: { + emergency_serial_dump = !code_seen('R'); + }; +#endif + #ifdef HEATBED_ANALYSIS /*! diff --git a/Firmware/SdFatUtil.cpp b/Firmware/SdFatUtil.cpp index 51da4ee2..50206ab9 100644 --- a/Firmware/SdFatUtil.cpp +++ b/Firmware/SdFatUtil.cpp @@ -48,24 +48,16 @@ void SdFatUtil::set_stack_guard() { uint32_t *stack_guard; - stack_guard = (uint32_t*)&__bss_end; + stack_guard = (uint32_t*)(&__bss_end + STACK_GUARD_MARGIN); *stack_guard = STACK_GUARD_TEST_VALUE; } bool SdFatUtil::test_stack_integrity() { - uint32_t* stack_guard = (uint32_t*)&__bss_end; + uint32_t* stack_guard = (uint32_t*)(&__bss_end + STACK_GUARD_MARGIN); return (*stack_guard == STACK_GUARD_TEST_VALUE); } -uint32_t SdFatUtil::get_stack_guard_test_value() -{ - uint32_t* stack_guard; - uint32_t output; - stack_guard = (uint32_t*)&__bss_end; - output = *stack_guard; - return(output); -} //------------------------------------------------------------------------------ /** %Print a string in flash memory. * diff --git a/Firmware/SdFatUtil.h b/Firmware/SdFatUtil.h index c42b74b1..2a70d98a 100644 --- a/Firmware/SdFatUtil.h +++ b/Firmware/SdFatUtil.h @@ -41,11 +41,10 @@ namespace SdFatUtil { void SerialPrintln_P(PGM_P str); void set_stack_guard(); bool test_stack_integrity(); - uint32_t get_stack_guard_test_value(); } using namespace SdFatUtil; // NOLINT #endif // #define SdFatUtil_h -#endif \ No newline at end of file +#endif diff --git a/Firmware/asm.h b/Firmware/asm.h new file mode 100644 index 00000000..2ffb88ad --- /dev/null +++ b/Firmware/asm.h @@ -0,0 +1,24 @@ +#pragma once +#include + +#ifdef __AVR_ATmega2560__ + +// return the current PC (on AVRs with 22bit PC) +static inline void GETPC(uint32_t* v) +{ + uint8_t a, b, c; + asm + ( + "rcall .\n" + "pop %2\n" + "pop %1\n" + "pop %0\n" + : "=r" (a), "=r" (b), "=r" (c) + ); + ((uint8_t*)v)[0] = a; + ((uint8_t*)v)[1] = b; + ((uint8_t*)v)[2] = c; + ((uint8_t*)v)[3] = 0; +} + +#endif diff --git a/Firmware/bootapp.h b/Firmware/bootapp.h index 6ff086f4..9a77c5ab 100644 --- a/Firmware/bootapp.h +++ b/Firmware/bootapp.h @@ -3,10 +3,11 @@ #define BOOTAPP_H #include "config.h" +#include #include -#define RAMSIZE 0x2000 +#define RAMSIZE (RAMEND+1-RAMSTART) #define boot_src_addr (*((uint32_t*)(RAMSIZE - 16))) #define boot_dst_addr (*((uint32_t*)(RAMSIZE - 12))) #define boot_copy_size (*((uint16_t*)(RAMSIZE - 8))) diff --git a/Firmware/cardreader.cpp b/Firmware/cardreader.cpp index b0c972fc..6155e4ec 100644 --- a/Firmware/cardreader.cpp +++ b/Firmware/cardreader.cpp @@ -2,6 +2,8 @@ #include "cmdqueue.h" #include "cardreader.h" #include "ultralcd.h" +#include "conv2str.h" +#include "menu.h" #include "stepper.h" #include "temperature.h" #include "language.h" diff --git a/Firmware/config.h b/Firmware/config.h index 2ec2077e..5adba50d 100644 --- a/Firmware/config.h +++ b/Firmware/config.h @@ -68,4 +68,25 @@ #define COMMUNITY_LANG_SUPPORT #endif +// Sanity checks for correct configuration of XFLASH_DUMP options +#if defined(XFLASH_DUMP) && !defined(XFLASH) +#error "XFLASH_DUMP requires XFLASH support" +#endif +#if (defined(MENU_DUMP) || defined(EMERGENCY_DUMP)) && !defined(XFLASH_DUMP) +#error "MENU_DUMP and EMERGENCY_DUMP require XFLASH_DUMP" +#endif + +// Support for serial dumps is mutually exclusive with XFLASH_DUMP features +#if defined(EMERGENCY_DUMP) && defined(EMERGENCY_SERIAL_DUMP) +#error "EMERGENCY_DUMP and EMERGENCY_SERIAL_DUMP are mutually exclusive" +#endif +#if defined(MENU_DUMP) && defined(MENU_SERIAL_DUMP) +#error "MENU_DUMP and MENU_SERIAL_DUMP are mutually exclusive" +#endif + +// Reduce internal duplication +#if defined(EMERGENCY_DUMP) || defined(EMERGENCY_SERIAL_DUMP) +#define EMERGENCY_HANDLERS +#endif + #endif //_CONFIG_H diff --git a/Firmware/eeprom.h b/Firmware/eeprom.h index 6ed11525..49701107 100644 --- a/Firmware/eeprom.h +++ b/Firmware/eeprom.h @@ -325,8 +325,9 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP | 0x0D0D 3341 | float | EEPROM_UVLO_RETRACT_ACCELL | ??? | ff ff ff ffh | Power panic saved retract acceleration | ??? | D3 Ax0d0d C4 | 0x0D09 3337 | float | EEPROM_UVLO_TRAVEL_ACCELL | ??? | ff ff ff ffh | Power panic saved travel acceleration | ??? | D3 Ax0d09 C4 | 0x0D05 3333 | uint32_t | EEPROM_JOB_ID | ??? | 00 00 00 00h | Job ID used by host software | D3 only | D3 Ax0d05 C4 -| 0x0D01 3329 | uint8_t | EEPROM_ECOOL_ENABLE | ffh 255 | ^ | Disable extruder motor scaling for non-farm print | LCD menu | D3 Ax0d01 FF -| ^ | ^ | ^ | 2ah 42 | ^ | Enable extruder motor scaling for non-farm print | ^ | D3 Ax0d01 42 +| 0x0D04 3332 | uint8_t | EEPROM_ECOOL_ENABLE | ffh 255 | ^ | Disable extruder motor scaling for non-farm print | LCD menu | D3 Ax0d04 C1 +| ^ | ^ | ^ | 2ah 42 | ^ | Enable extruder motor scaling for non-farm print | ^ | D3 Ax0d04 C1 +| 0x0D03 3321 | uint8_t | EEPROM_FW_CRASH_FLAG | 01h 1 | ff/00 | Last FW crash reason (dump_crash_reason) | D21/D22 | D3 Ax0d03 C1 | Address begin | Bit/Type | Name | Valid values | Default/FactoryReset | Description | Gcode/Function| Debug code | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: @@ -541,9 +542,10 @@ static Sheets * const EEPROM_Sheets_base = (Sheets*)(EEPROM_SHEETS_BASE); #define EEPROM_JOB_ID (EEPROM_UVLO_TRAVEL_ACCELL-4) //uint32_t #define EEPROM_ECOOL_ENABLE (EEPROM_JOB_ID-1) // uint8_t +#define EEPROM_FW_CRASH_FLAG (EEPROM_ECOOL_ENABLE-1) // uint8_t //This is supposed to point to last item to allow EEPROM overrun check. Please update when adding new items. -#define EEPROM_LAST_ITEM EEPROM_ECOOL_ENABLE +#define EEPROM_LAST_ITEM EEPROM_FW_CRASH_FLAG // !!!!! // !!!!! this is end of EEPROM section ... all updates MUST BE inserted before this mark !!!!! // !!!!! diff --git a/Firmware/language.c b/Firmware/language.c index 01a39652..9a1113b9 100644 --- a/Firmware/language.c +++ b/Firmware/language.c @@ -9,6 +9,7 @@ #ifdef XFLASH #include "xflash.h" +#include "xflash_layout.h" #endif //XFLASH // Currently active language selection. @@ -110,7 +111,7 @@ uint8_t lang_get_count() #ifdef XFLASH XFLASH_SPI_ENTER(); uint8_t count = 2; //count = 1+n (primary + secondary + all in xflash) - uint32_t addr = 0x00000; //start of xflash + uint32_t addr = LANG_OFFSET; lang_table_header_t header; //table header structure while (1) { @@ -143,7 +144,7 @@ uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* off return (header->magic == LANG_MAGIC)?1:0; //return 1 if magic valid } XFLASH_SPI_ENTER(); - uint32_t addr = 0x00000; //start of xflash + uint32_t addr = LANG_OFFSET; lang--; while (1) { @@ -176,7 +177,7 @@ uint16_t lang_get_code(uint8_t lang) return pgm_read_word(((uint32_t*)(ui + 10))); //return lang code from progmem } XFLASH_SPI_ENTER(); - uint32_t addr = 0x00000; //start of xflash + uint32_t addr = LANG_OFFSET; lang_table_header_t header; //table header structure lang--; while (1) diff --git a/Firmware/mmu.cpp b/Firmware/mmu.cpp index c64f1936..abc84c34 100755 --- a/Firmware/mmu.cpp +++ b/Firmware/mmu.cpp @@ -11,6 +11,7 @@ #include "cardreader.h" #include "cmdqueue.h" #include "ultralcd.h" +#include "menu.h" #include "sound.h" #include "printers.h" #include diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 4d00089f..63eb8a36 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -32,11 +32,13 @@ #include "Marlin.h" #include "cmdqueue.h" #include "ultralcd.h" +#include "menu.h" +#include "conv2str.h" #include "sound.h" #include "temperature.h" #include "cardreader.h" -#include "Sd2PinMap.h" +#include "SdFatUtil.h" #include #include "adc.h" @@ -2060,6 +2062,9 @@ FORCE_INLINE static void temperature_isr() } #endif //BABYSTEPPING + // Check if a stack overflow happened + if (!SdFatUtil::test_stack_integrity()) stack_error(); + #if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1)) check_fans(); #endif //(defined(TACH_0)) diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index b1d13b8f..81fef967 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -5,6 +5,7 @@ #include "temperature.h" #include "ultralcd.h" +#include "conv2str.h" #include "fsensor.h" #include "Marlin.h" #include "language.h" @@ -28,8 +29,6 @@ //#include "Configuration.h" #include "cmdqueue.h" -#include "SdFatUtil.h" - #ifdef FILAMENT_SENSOR #include "pat9125.h" #include "fsensor.h" @@ -1806,6 +1805,61 @@ static void lcd_preheat_menu() lcd_generic_preheat_menu(); } + +#ifdef MENU_DUMP +#include "xflash_dump.h" + +static void lcd_dump_memory() +{ + lcd_beeper_quick_feedback(); + xfdump_dump(); + lcd_return_to_status(); +} +#endif //MENU_DUMP +#ifdef MENU_SERIAL_DUMP +#include "Dcodes.h" + +static void lcd_serial_dump() +{ + serial_dump_and_reset(dump_crash_reason::manual); +} +#endif //MENU_SERIAL_DUMP + +#if defined(DEBUG_BUILD) && defined(EMERGENCY_HANDLERS) +#include + +#ifdef WATCHDOG +static void lcd_wdr_crash() +{ + while (1); +} +#endif + +static uint8_t lcd_stack_crash_(uint8_t arg, uint32_t sp = 0) +{ + // populate the stack with an increasing value for ease of testing + volatile uint16_t tmp __attribute__((unused)) = sp; + + _delay(arg); + uint8_t ret = lcd_stack_crash_(arg, SP); + + // required to avoid tail call elimination and to slow down the stack growth + _delay(ret); + + return ret; +} + +static void lcd_stack_crash() +{ +#ifdef WATCHDOG + wdt_disable(); +#endif + // delay choosen in order to hit the stack-check in the temperature isr reliably + lcd_stack_crash_(10); +} +#endif + + //! @brief Show Support Menu //! //! @code{.unparsed} @@ -1997,8 +2051,20 @@ static void lcd_support_menu() MENU_ITEM_SUBMENU_P(_i("Voltages"), lcd_menu_voltages);////MSG_MENU_VOLTAGES c=18 #endif //defined VOLT_BED_PIN || defined VOLT_PWR_PIN - +#ifdef MENU_DUMP + MENU_ITEM_FUNCTION_P(_i("Dump memory"), lcd_dump_memory); +#endif //MENU_DUMP +#ifdef MENU_SERIAL_DUMP + if (emergency_serial_dump) + MENU_ITEM_FUNCTION_P(_i("Dump to serial"), lcd_serial_dump); +#endif #ifdef DEBUG_BUILD +#ifdef EMERGENCY_HANDLERS +#ifdef WATCHDOG + MENU_ITEM_FUNCTION_P(PSTR("WDR crash"), lcd_wdr_crash); +#endif //WATCHDOG + MENU_ITEM_FUNCTION_P(PSTR("Stack crash"), lcd_stack_crash); +#endif //EMERGENCY_HANDLERS MENU_ITEM_SUBMENU_P(PSTR("Debug"), lcd_menu_debug);////MSG_DEBUG c=18 #endif /* DEBUG_BUILD */ @@ -6695,12 +6761,6 @@ static void lcd_main_menu() } -void stack_error() { - Sound_MakeCustom(1000,0,true); - lcd_display_message_fullscreen_P(_i("Error - static memory has been overwritten"));////MSG_STACK_ERROR c=20 r=4 - //err_triggered = 1; - while (1) delay_keep_alive(1000); -} #ifdef DEBUG_STEPPER_TIMER_MISSED bool stepper_timer_overflow_state = false; @@ -8928,7 +8988,6 @@ void menu_lcd_lcdupdate_func(void) if (lcd_draw_update) lcd_draw_update--; lcd_next_update_millis = _millis() + LCD_UPDATE_INTERVAL; } - if (!SdFatUtil::test_stack_integrity()) stack_error(); lcd_ping(); //check that we have received ping command if we are in farm mode lcd_send_status(); if (lcd_commands_type == LcdCommands::Layer1Cal) lcd_commands(); diff --git a/Firmware/ultralcd.h b/Firmware/ultralcd.h index a58d48ee..68b933d0 100755 --- a/Firmware/ultralcd.h +++ b/Firmware/ultralcd.h @@ -1,15 +1,9 @@ #ifndef ULTRALCD_H #define ULTRALCD_H -#include "Marlin.h" -#include "lcd.h" -#include "conv2str.h" -#include "menu.h" #include "mesh_bed_calibration.h" #include "config.h" -#include "config.h" - extern void menu_lcd_longpress_func(void); extern void menu_lcd_charsetup_func(void); extern void menu_lcd_lcdupdate_func(void); @@ -200,7 +194,6 @@ void mFilamentItemForce(); void lcd_generic_preheat_menu(); void unload_filament(bool automatic = false); -void stack_error(); void lcd_printer_connected(); void lcd_ping(); diff --git a/Firmware/util.cpp b/Firmware/util.cpp index cf9b05b7..5ce21cd0 100644 --- a/Firmware/util.cpp +++ b/Firmware/util.cpp @@ -1,6 +1,7 @@ #include "Configuration.h" #include "ultralcd.h" +#include "menu.h" #include "sound.h" #include "language.h" #include "util.h" diff --git a/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h index 4c20082e..7ccd9151 100644 --- a/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h @@ -418,6 +418,7 @@ THERMISTORS SETTINGS #endif #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h b/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h index 35313a41..4686f097 100644 --- a/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h @@ -407,6 +407,7 @@ THERMISTORS SETTINGS #endif #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h index acd7883d..aa625c64 100644 --- a/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h @@ -119,11 +119,15 @@ #define DEFAULT_SAFETYTIMER_TIME_MINS 30 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul) +// Online crash dumper +#define EMERGENCY_SERIAL_DUMP // Request dump via serial on stack corruption and WDR +#define MENU_SERIAL_DUMP // Enable "Memory dump" in Settings menu + // Filament sensor #define FILAMENT_SENSOR #define PAT9125 - +#define DEBUG_DCODE2 #define DEBUG_DCODE3 //#define DEBUG_BUILD @@ -469,6 +473,7 @@ #define TEMP_SENSOR_PINDA 1 #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h b/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h index ddf7e77a..ddafa6f4 100644 --- a/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h @@ -120,11 +120,15 @@ #define DEFAULT_SAFETYTIMER_TIME_MINS 30 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul) +// Online crash dumper +#define EMERGENCY_SERIAL_DUMP // Request dump via serial on stack corruption and WDR +#define MENU_SERIAL_DUMP // Enable "Memory dump" in Settings menu + // Filament sensor #define FILAMENT_SENSOR #define PAT9125 - +#define DEBUG_DCODE2 #define DEBUG_DCODE3 //#define DEBUG_BUILD @@ -470,6 +474,7 @@ #define TEMP_SENSOR_PINDA 1 #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h index e7b114d9..29ff5b35 100644 --- a/Firmware/variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h @@ -119,11 +119,15 @@ #define DEFAULT_SAFETYTIMER_TIME_MINS 30 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul) +// Online crash dumper +#define EMERGENCY_SERIAL_DUMP // Request dump via serial on stack corruption and WDR +#define MENU_SERIAL_DUMP // Enable "Memory dump" in Settings menu + // Filament sensor #define FILAMENT_SENSOR #define IR_SENSOR - +#define DEBUG_DCODE2 #define DEBUG_DCODE3 //#define DEBUG_BUILD @@ -469,6 +473,7 @@ #define TEMP_SENSOR_PINDA 1 #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h b/Firmware/variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h index cde81249..342a9a1f 100644 --- a/Firmware/variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h @@ -120,11 +120,15 @@ #define DEFAULT_SAFETYTIMER_TIME_MINS 30 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul) +// Online crash dumper +#define EMERGENCY_SERIAL_DUMP // Request dump via serial on stack corruption and WDR +#define MENU_SERIAL_DUMP // Enable "Memory dump" in Settings menu + // Filament sensor #define FILAMENT_SENSOR #define IR_SENSOR - +#define DEBUG_DCODE2 #define DEBUG_DCODE3 //#define DEBUG_BUILD @@ -470,6 +474,7 @@ #define TEMP_SENSOR_PINDA 1 #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h index 16fba5d7..aacb7cb7 100644 --- a/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h @@ -139,6 +139,15 @@ #define DEFAULT_SAFETYTIMER_TIME_MINS 30 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul) +// Offline crash dumper +#define XFLASH_DUMP // enable dump functionality (including D20/D21/D22) +#define MENU_DUMP // enable "Memory dump" in Settings menu +#define EMERGENCY_DUMP // trigger crash on stack corruption and WDR + +// Online crash dumper +//#define EMERGENCY_SERIAL_DUMP // Request dump via serial on stack corruption and WDR +//#define MENU_SERIAL_DUMP // Enable "Memory dump" in Settings menu + // Filament sensor #define FILAMENT_SENSOR #define PAT9125 @@ -155,7 +164,9 @@ #define MINTEMP_MINAMBIENT 10 #define MINTEMP_MINAMBIENT_RAW 1002 +#define DEBUG_DCODE2 #define DEBUG_DCODE3 +#define DEBUG_DCODE6 //#define DEBUG_BUILD //#define DEBUG_SEC_LANG //secondary language debug output at startup @@ -591,6 +602,7 @@ #define TEMP_SENSOR_AMBIENT 2000 #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h index 13fc2a79..8659c582 100644 --- a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h @@ -141,6 +141,15 @@ #define DEFAULT_SAFETYTIMER_TIME_MINS 30 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul) +// Offline crash dumper +#define XFLASH_DUMP // enable dump functionality (including D20/D21/D22) +#define MENU_DUMP // enable "Memory dump" in Settings menu +#define EMERGENCY_DUMP // trigger crash on stack corruption and WDR + +// Online crash dumper +//#define EMERGENCY_SERIAL_DUMP // Request dump via serial on stack corruption and WDR +//#define MENU_SERIAL_DUMP // Enable "Memory dump" in Settings menu + // Filament sensor #define FILAMENT_SENSOR #define IR_SENSOR @@ -157,7 +166,9 @@ #define MINTEMP_MINAMBIENT 10 #define MINTEMP_MINAMBIENT_RAW 1002 +#define DEBUG_DCODE2 #define DEBUG_DCODE3 +#define DEBUG_DCODE6 //#define DEBUG_BUILD //#define DEBUG_SEC_LANG //secondary language debug output at startup @@ -595,6 +606,7 @@ #define TEMP_SENSOR_AMBIENT 2000 #define STACK_GUARD_TEST_VALUE 0xA2A2 +#define STACK_GUARD_MARGIN 32 #define MAX_BED_TEMP_CALIBRATION 50 #define MAX_HOTEND_TEMP_CALIBRATION 50 diff --git a/Firmware/xflash.c b/Firmware/xflash.c index e9d894a5..cc758ebf 100644 --- a/Firmware/xflash.c +++ b/Firmware/xflash.c @@ -90,13 +90,18 @@ void w25x20cl_wr_status_reg(uint8_t val) } #endif -void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt) +static void xflash_send_cmdaddr(uint8_t cmd, uint32_t addr) { - _CS_LOW(); - _SPI_TX(_CMD_RD_DATA); // send command 0x03 + _SPI_TX(cmd); // send command 0x03 _SPI_TX(((uint8_t*)&addr)[2]); // send addr bits 16..23 _SPI_TX(((uint8_t*)&addr)[1]); // send addr bits 8..15 _SPI_TX(((uint8_t*)&addr)[0]); // send addr bits 0..7 +} + +void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt) +{ + _CS_LOW(); + xflash_send_cmdaddr(_CMD_RD_DATA, addr); while (cnt--) // receive data *(data++) = _SPI_RX(); _CS_HIGH(); @@ -105,22 +110,38 @@ void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt) void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt) { _CS_LOW(); - _SPI_TX(_CMD_PAGE_PROGRAM); // send command 0x02 - _SPI_TX(((uint8_t*)&addr)[2]); // send addr bits 16..23 - _SPI_TX(((uint8_t*)&addr)[1]); // send addr bits 8..15 - _SPI_TX(((uint8_t*)&addr)[0]); // send addr bits 0..7 + xflash_send_cmdaddr(_CMD_PAGE_PROGRAM, addr); while (cnt--) // send data _SPI_TX(*(data++)); _CS_HIGH(); } +void xflash_multipage_program(uint32_t addr, uint8_t* data, uint16_t cnt) +{ + while(cnt) + { + xflash_enable_wr(); + _CS_LOW(); + xflash_send_cmdaddr(_CMD_PAGE_PROGRAM, addr); + while(1) + { + // send data + _SPI_TX(*(data++)); + if(!--cnt || !(++addr & 0xFF)) + { + // on a page boundary or end of write + _CS_HIGH(); + xflash_wait_busy(); + break; + } + } + } +} + void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt) { _CS_LOW(); - _SPI_TX(_CMD_PAGE_PROGRAM); // send command 0x02 - _SPI_TX(((uint8_t*)&addr)[2]); // send addr bits 16..23 - _SPI_TX(((uint8_t*)&addr)[1]); // send addr bits 8..15 - _SPI_TX(((uint8_t*)&addr)[0]); // send addr bits 0..7 + xflash_send_cmdaddr(_CMD_PAGE_PROGRAM, addr); while (cnt--) // send data _SPI_TX(pgm_read_byte(data++)); _CS_HIGH(); @@ -129,10 +150,7 @@ void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt) void xflash_erase(uint8_t cmd, uint32_t addr) { _CS_LOW(); - _SPI_TX(cmd); // send command 0x20 - _SPI_TX(((uint8_t*)&addr)[2]); // send addr bits 16..23 - _SPI_TX(((uint8_t*)&addr)[1]); // send addr bits 8..15 - _SPI_TX(((uint8_t*)&addr)[0]); // send addr bits 0..7 + xflash_send_cmdaddr(cmd, addr); _CS_HIGH(); } diff --git a/Firmware/xflash.h b/Firmware/xflash.h index c9ad5275..a75abb18 100644 --- a/Firmware/xflash.h +++ b/Firmware/xflash.h @@ -34,16 +34,25 @@ extern uint8_t xflash_rd_status_reg(void); extern void w25x20cl_wr_status_reg(uint8_t val); #endif extern void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt); -extern void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt); -extern void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt); + extern void xflash_sector_erase(uint32_t addr); extern void xflash_block32_erase(uint32_t addr); extern void xflash_block64_erase(uint32_t addr); extern void xflash_chip_erase(void); -extern void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt); extern void xflash_rd_uid(uint8_t* uid); extern void xflash_wait_busy(void); +// write up to a single page of data (256bytes) +extern void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt); + +// write up to a single page of data from program memory +extern void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt); + +// xflash_multipage_program: high-level interface for multi-page writes. +// Write any amount of data, chunking writes to page boundaries as needed. +// Automatically enables writes and waits for completion. +extern void xflash_multipage_program(uint32_t addr, uint8_t* data, uint16_t cnt); + #if defined(__cplusplus) } #endif //defined(__cplusplus) diff --git a/Firmware/xflash_dump.cpp b/Firmware/xflash_dump.cpp new file mode 100644 index 00000000..7040b502 --- /dev/null +++ b/Firmware/xflash_dump.cpp @@ -0,0 +1,109 @@ +#include + +#include +#include + +#include "xflash_dump.h" +#ifdef XFLASH_DUMP +#include "asm.h" +#include "xflash.h" +#include "Marlin.h" // for softReset + +bool xfdump_check_state(dump_crash_reason* reason) +{ + uint32_t magic; + + XFLASH_SPI_ENTER(); + xflash_rd_data(DUMP_OFFSET + offsetof(dump_t, header.magic), + (uint8_t*)&magic, sizeof(magic)); + if (magic != DUMP_MAGIC) + return false; + + if (reason) + { + xflash_rd_data(DUMP_OFFSET + offsetof(dump_t, header.crash_reason), + (uint8_t*)reason, sizeof(*reason)); + } + return true; +} + + +void xfdump_reset() +{ + XFLASH_SPI_ENTER(); + xflash_enable_wr(); + xflash_sector_erase(DUMP_OFFSET + offsetof(dump_t, header.magic)); + xflash_wait_busy(); +} + + +static void xfdump_erase() +{ + for(uint32_t addr = DUMP_OFFSET; + addr < DUMP_OFFSET + DUMP_SIZE; + addr += 4096) + { + xflash_enable_wr(); + xflash_sector_erase(DUMP_OFFSET); + xflash_wait_busy(); + } +} + + +static void __attribute__((noinline)) xfdump_dump_core(dump_header_t& hdr, uint32_t addr, uint8_t* buf, uint16_t cnt) +{ + XFLASH_SPI_ENTER(); + + // start by clearing all sectors (we need all of them in any case) + xfdump_erase(); + + // sample SP/PC + hdr.sp = SP; + GETPC(&hdr.pc); + + // write header + static_assert(sizeof(hdr) <= 256, "header is larger than a single page write"); + xflash_enable_wr(); + xflash_page_program(DUMP_OFFSET, (uint8_t*)&hdr, sizeof(hdr)); + xflash_wait_busy(); + + // write data + static_assert(sizeof(dump_t::data) <= RAMEND+1, "dump area size insufficient"); + xflash_multipage_program(addr, buf, cnt); +} + + +void xfdump_dump() +{ + dump_header_t buf; + buf.magic = DUMP_MAGIC; + buf.regs_present = false; + buf.crash_reason = (uint8_t)dump_crash_reason::manual; + + // write sram only + xfdump_dump_core(buf, DUMP_OFFSET + offsetof(dump_t, data.sram), + (uint8_t*)RAMSTART, RAMSIZE); +} + + +void xfdump_full_dump_and_reset(dump_crash_reason reason) +{ + dump_header_t buf; + buf.magic = DUMP_MAGIC; + buf.regs_present = true; + buf.crash_reason = (uint8_t)reason; + + // disable interrupts for a cleaner register dump + cli(); + + // ensure there's always enough time (with some margin) to dump + // dump time on w25x20cl: ~150ms + wdt_enable(WDTO_500MS); + + // write all addressable ranges (this will trash bidirectional registers) + xfdump_dump_core(buf, DUMP_OFFSET + offsetof(dump_t, data), 0, RAMEND+1); + + // force a reset even sooner + softReset(); +} +#endif diff --git a/Firmware/xflash_dump.h b/Firmware/xflash_dump.h new file mode 100644 index 00000000..6ece99c6 --- /dev/null +++ b/Firmware/xflash_dump.h @@ -0,0 +1,22 @@ +// XFLASH dumper +#pragma once +#include "xflash_layout.h" + +enum class dump_crash_reason : uint8_t +{ + manual = 0, + stack_error, + watchdog, + bad_isr, +}; + +#ifdef XFLASH_DUMP +void xfdump_reset(); // reset XFLASH dump state +void xfdump_dump(); // create a new SRAM memory dump + +// return true if a dump is present, save type in "reason" if provided +bool xfdump_check_state(dump_crash_reason* reason = NULL); + +// create a new dump containing registers and SRAM, then reset +void xfdump_full_dump_and_reset(dump_crash_reason crash = dump_crash_reason::manual); +#endif diff --git a/Firmware/xflash_layout.h b/Firmware/xflash_layout.h new file mode 100644 index 00000000..5d92d6e6 --- /dev/null +++ b/Firmware/xflash_layout.h @@ -0,0 +1,51 @@ +// XFLASH memory layout +#pragma once +#include +#include "bootapp.h" // for RAMSIZE +#include "config.h" + +#define XFLASH_SIZE 0x40000ul // size of XFLASH +#define LANG_OFFSET 0x0 // offset for language data + +#ifndef XFLASH_DUMP +#define LANG_SIZE XFLASH_SIZE +#else + +#define DUMP_MAGIC 0x55525547ul + +struct dump_header_t +{ + // start with a magic value to indicate the presence of a dump, so that clearing + // a single page is sufficient for resetting the state + uint32_t magic; + + uint8_t regs_present; // true when the lower segment containing registers is present + uint8_t crash_reason; // uses values from dump_crash_source + + uint32_t pc; // PC nearby the crash location + uint16_t sp; // SP nearby the crash location +}; + +struct dump_data_t +{ + // contiguous region containing all addressable ranges + uint8_t regs[RAMSTART]; + uint8_t sram[RAMSIZE]; +}; + +struct dump_t +{ + struct dump_header_t header; + + // data is page aligned (no real space waste, due to the larger + // alignment required for the whole dump) + struct dump_data_t __attribute__((aligned(256))) data; +}; + +// dump offset must be aligned to lower 4kb sector boundary +#define DUMP_OFFSET ((XFLASH_SIZE - sizeof(dump_t)) & ~0xFFFul) + +#define DUMP_SIZE (XFLASH_SIZE - DUMP_OFFSET) // effective dump size area +#define LANG_SIZE DUMP_OFFSET // available language space + +#endif diff --git a/Firmware/xyzcal.cpp b/Firmware/xyzcal.cpp index ea92359c..cc2d940d 100644 --- a/Firmware/xyzcal.cpp +++ b/Firmware/xyzcal.cpp @@ -163,6 +163,9 @@ void xyzcal_meassure_leave(void) ENABLE_STEPPER_DRIVER_INTERRUPT(); #ifdef WATCHDOG wdt_enable(WDTO_4S); +#ifdef EMERGENCY_HANDLERS + WDTCSR |= (1 << WDIE); +#endif //EMERGENCY_HANDLERS #endif //WATCHDOG sm4_stop_cb = 0; sm4_update_pos_cb = 0; diff --git a/lang/fw-build.sh b/lang/fw-build.sh index 12a9ed51..ecd2df2c 100755 --- a/lang/fw-build.sh +++ b/lang/fw-build.sh @@ -198,6 +198,21 @@ if [ -e lang_nl.bin ]; then cat lang_nl.bin >> lang.bin; fi ## New language #if [ -e lang_qr.bin ]; then cat lang_qr.bin >> lang.bin; fi +# Check that the language data doesn't exceed the reserved XFLASH space +echo " checking language data size:" +lang_size=$(wc -c lang.bin | cut -f1 -d' ') +lang_size_pad=$(( ($lang_size+4096-1) / 4096 * 4096 )) + +# TODO: hard-coded! get value by preprocessing LANG_SIZE from xflash_layout.h! +lang_reserved=249856 + +echo " total size usage: $lang_size_pad ($lang_size)" +echo " reserved size: $lang_reserved" +if [ $lang_size_pad -gt $lang_reserved ]; then + echo "NG! - language data too large" >&2 + finish 1 +fi + #convert lang.bin to lang.hex echo -n " converting to hex..." >&2 $OBJCOPY -I binary -O ihex ./lang.bin ./lang.hex