diff --git a/Firmware/Configuration.h b/Firmware/Configuration.h
index d8731ac7f..7855e04e4 100644
--- a/Firmware/Configuration.h
+++ b/Firmware/Configuration.h
@@ -28,6 +28,7 @@ extern PGM_P sPrinterName;
#endif
#define FW_COMMIT_NR 4481
+
// FW_VERSION_UNKNOWN means this is an unofficial build.
// The firmware should only be checked into github with this symbol.
#define FW_DEV_VERSION FW_VERSION_UNKNOWN
diff --git a/Firmware/Dcodes.cpp b/Firmware/Dcodes.cpp
index dace219f6..5a840ee45 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 856d04ad0..c4b470e4b 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 98e5102c4..c2c5ca933 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 1cf7be2c8..5aca69bdc 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
/*!
@@ -9928,6 +10089,18 @@ if(0)
#endif
check_axes_activity();
mmu_loop();
+
+ // handle longpress
+ if(lcd_longpress_trigger)
+ {
+ // long press is not possible in modal mode, wait until ready
+ if (lcd_longpress_func && lcd_update_enabled)
+ {
+ lcd_longpress_func();
+ lcd_longpress_trigger = 0;
+ }
+ }
+
#if defined(AUTO_REPORT)
host_autoreport();
#endif //AUTO_REPORT
diff --git a/Firmware/SdFatUtil.cpp b/Firmware/SdFatUtil.cpp
index 51da4ee2b..50206ab94 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 c42b74b1c..2a70d98a1 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 000000000..2ffb88ad9
--- /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 6ff086f46..9a77c5abd 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 b0c972fc7..6155e4ec4 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 2ec2077e6..5adba50df 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 6ed115254..497011076 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 01a396523..9a1113b96 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/lcd.cpp b/Firmware/lcd.cpp
index c3f2921d1..37b70f1a1 100644
--- a/Firmware/lcd.cpp
+++ b/Firmware/lcd.cpp
@@ -728,6 +728,10 @@ void lcd_update_enable(uint8_t enabled)
}
}
+bool lcd_longpress_trigger = 0;
+
+// WARNING: this function is called from the temperature ISR.
+// Only update flags, but do not perform any menu/lcd operation!
void lcd_buttons_update(void)
{
static uint8_t lcd_long_press_active = 0;
@@ -749,9 +753,7 @@ void lcd_buttons_update(void)
else if (longPressTimer.expired(LONG_PRESS_TIME))
{
lcd_long_press_active = 1;
- //long press is not possible in modal mode
- if (lcd_longpress_func && lcd_update_enabled)
- lcd_longpress_func();
+ lcd_longpress_trigger = 1;
}
}
}
diff --git a/Firmware/lcd.h b/Firmware/lcd.h
index 12b162e75..65bb9dc92 100644
--- a/Firmware/lcd.h
+++ b/Firmware/lcd.h
@@ -110,6 +110,7 @@ extern uint32_t lcd_next_update_millis;
extern uint8_t lcd_status_update_delay;
extern lcd_longpress_func_t lcd_longpress_func;
+extern bool lcd_longpress_trigger;
extern lcd_charsetup_func_t lcd_charsetup_func;
diff --git a/Firmware/mmu.cpp b/Firmware/mmu.cpp
index c64f1936d..abc84c34d 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/planner.cpp b/Firmware/planner.cpp
index ba3791ae8..b6d012b24 100644
--- a/Firmware/planner.cpp
+++ b/Firmware/planner.cpp
@@ -109,9 +109,9 @@ unsigned char g_uc_extruder_last_move[3] = {0,0,0};
//===========================================================================
//=================semi-private variables, used in inline functions =====
//===========================================================================
-block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions
-volatile unsigned char block_buffer_head; // Index of the next block to be pushed
-volatile unsigned char block_buffer_tail; // Index of the block to process now
+block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions
+volatile uint8_t block_buffer_head; // Index of the next block to be pushed
+volatile uint8_t block_buffer_tail; // Index of the block to process now
#ifdef PLANNER_DIAGNOSTICS
// Diagnostic function: Minimum number of planned moves since the last
@@ -136,17 +136,17 @@ static bool plan_reset_next_e_sched;
// Returns the index of the next block in the ring buffer
// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
-static inline int8_t next_block_index(int8_t block_index) {
+static inline uint8_t next_block_index(uint8_t block_index) {
if (++ block_index == BLOCK_BUFFER_SIZE)
- block_index = 0;
+ block_index = 0;
return block_index;
}
// Returns the index of the previous block in the ring buffer
-static inline int8_t prev_block_index(int8_t block_index) {
+static inline uint8_t prev_block_index(uint8_t block_index) {
if (block_index == 0)
- block_index = BLOCK_BUFFER_SIZE;
+ block_index = BLOCK_BUFFER_SIZE;
-- block_index;
return block_index;
}
@@ -358,14 +358,14 @@ void planner_recalculate(const float &safe_final_speed)
// Reverse pass
// Make a local copy of block_buffer_tail, because the interrupt can alter it
// by consuming the blocks, therefore shortening the queue.
- unsigned char tail = block_buffer_tail;
+ uint8_t tail = block_buffer_tail;
uint8_t block_index;
block_t *prev, *current, *next;
// SERIAL_ECHOLNPGM("planner_recalculate - 1");
// At least three blocks are in the queue?
- unsigned char n_blocks = (block_buffer_head + BLOCK_BUFFER_SIZE - tail) & (BLOCK_BUFFER_SIZE - 1);
+ uint8_t n_blocks = (block_buffer_head + BLOCK_BUFFER_SIZE - tail) & (BLOCK_BUFFER_SIZE - 1);
if (n_blocks >= 3) {
// Initialize the last tripple of blocks.
block_index = prev_block_index(block_buffer_head);
@@ -709,7 +709,7 @@ float junction_deviation = 0.1;
void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate, uint8_t extruder, const float* gcode_target)
{
// Calculate the buffer head after we push this byte
- int next_buffer_head = next_block_index(block_buffer_head);
+ uint8_t next_buffer_head = next_block_index(block_buffer_head);
// If the buffer is full: good! That means we are well ahead of the robot.
// Rest here until there is room in the buffer.
@@ -1475,7 +1475,7 @@ void update_mode_profile()
}
#endif //TMC2130
-unsigned char number_of_blocks()
+uint8_t number_of_blocks()
{
return (block_buffer_head + BLOCK_BUFFER_SIZE - block_buffer_tail) & (BLOCK_BUFFER_SIZE - 1);
}
@@ -1505,8 +1505,8 @@ void planner_add_sd_length(uint16_t sdlen)
uint16_t planner_calc_sd_length()
{
- unsigned char _block_buffer_head = block_buffer_head;
- unsigned char _block_buffer_tail = block_buffer_tail;
+ uint8_t _block_buffer_head = block_buffer_head;
+ uint8_t _block_buffer_tail = block_buffer_tail;
uint16_t sdlen = 0;
while (_block_buffer_head != _block_buffer_tail)
{
diff --git a/Firmware/planner.h b/Firmware/planner.h
index 34899cac4..f7fd849e0 100644
--- a/Firmware/planner.h
+++ b/Firmware/planner.h
@@ -202,16 +202,20 @@ extern uint8_t maxlimit_status;
extern float autotemp_factor;
#endif
-
+// Check for BLOCK_BUFFER_SIZE requirements
+static_assert(!(BLOCK_BUFFER_SIZE & (BLOCK_BUFFER_SIZE - 1)),
+ "BLOCK_BUFFER_SIZE must be a power of two");
+static_assert(BLOCK_BUFFER_SIZE <= (UINT8_MAX>>1),
+ "BLOCK_BUFFER_SIZE too large for uint8_t");
extern block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instfructions
// Index of the next block to be pushed into the planner queue.
-extern volatile unsigned char block_buffer_head;
+extern volatile uint8_t block_buffer_head;
// Index of the first block in the planner queue.
// This is the block, which is being currently processed by the stepper routine,
// or which is first to be processed by the stepper routine.
-extern volatile unsigned char block_buffer_tail;
+extern volatile uint8_t block_buffer_tail;
// Called when the current block is no longer needed. Discards the block and makes the memory
// available for new blocks.
FORCE_INLINE void plan_discard_current_block()
@@ -246,7 +250,7 @@ FORCE_INLINE uint8_t moves_planned() {
}
FORCE_INLINE bool planner_queue_full() {
- unsigned char next_block_index = block_buffer_head;
+ uint8_t next_block_index = block_buffer_head;
if (++ next_block_index == BLOCK_BUFFER_SIZE)
next_block_index = 0;
return block_buffer_tail == next_block_index;
@@ -267,7 +271,7 @@ void reset_acceleration_rates();
void update_mode_profile();
-unsigned char number_of_blocks();
+uint8_t number_of_blocks();
// #define PLANNER_DIAGNOSTICS
#ifdef PLANNER_DIAGNOSTICS
diff --git a/Firmware/stepper.cpp b/Firmware/stepper.cpp
index 614480a33..c9cb8bebc 100644
--- a/Firmware/stepper.cpp
+++ b/Firmware/stepper.cpp
@@ -1559,17 +1559,6 @@ void digitalPotWrite(int address, int value) // From Arduino DigitalPotControl e
}
#endif
-void EEPROM_read_st(int pos, uint8_t* value, uint8_t size)
-{
- do
- {
- *value = eeprom_read_byte((unsigned char*)pos);
- pos++;
- value++;
- }while(--size);
-}
-
-
void st_current_init() //Initialize Digipot Motor Current
{
#ifdef MOTOR_CURRENT_PWM_XY_PIN
diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp
index 4d00089f5..63eb8a367 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 b1d13b8f1..81fef9674 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 a58d48eee..68b933d02 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 cf9b05b70..97ea9fffa 100644
--- a/Firmware/util.cpp
+++ b/Firmware/util.cpp
@@ -1,13 +1,15 @@
#include "Configuration.h"
#include "ultralcd.h"
+#include "menu.h"
#include "sound.h"
#include "language.h"
#include "util.h"
#include
// Allocate the version string in the program memory. Otherwise the string lands either on the stack or in the global RAM.
-const char FW_VERSION_STR[] PROGMEM = FW_VERSION;
+static const char FW_VERSION_STR[] PROGMEM = FW_VERSION;
+static const uint16_t FW_VERSION_NR[4] PROGMEM = { FW_MAJOR, FW_MINOR, FW_REVISION, FW_COMMIT_NR };
const char* FW_VERSION_STR_P()
{
@@ -138,105 +140,14 @@ inline bool strncmp_PP(const char *p1, const char *p2, uint8_t n)
return 0;
}
-// Parse a major.minor.revision version number.
-// Return true if valid.
-inline bool parse_version_P(const char *str, uint16_t version[4])
-{
-#if 0
- SERIAL_ECHOPGM("Parsing version string ");
- SERIAL_ECHORPGM(str);
- SERIAL_ECHOLNPGM("");
-#endif
-
- const char *major = str;
- const char *p = str;
- while (is_digit(char(pgm_read_byte(p)))) ++ p;
- if (pgm_read_byte(p) != '.')
- return false;
- const char *minor = ++ p;
- while (is_digit(char(pgm_read_byte(p)))) ++ p;
- if (pgm_read_byte(p) != '.')
- return false;
- const char *rev = ++ p;
- while (is_digit(char(pgm_read_byte(p)))) ++ p;
- if (! is_whitespace_or_nl_or_eol(char(pgm_read_byte(p))) && pgm_read_byte(p) != '-')
- return false;
-
- char buf[5];
- uint8_t n = minor - major - 1;
- if (n > 4)
- return false;
- memcpy_P(buf, major, n); buf[n] = 0;
- char *endptr = NULL;
- version[0] = strtol(buf, &endptr, 10);
- if (*endptr != 0)
- return false;
- n = rev - minor - 1;
- if (n > 4)
- return false;
- memcpy_P(buf, minor, n); buf[n] = 0;
- version[1] = strtol(buf, &endptr, 10);
- if (*endptr != 0)
- return false;
- n = p - rev;
- if (n > 4)
- return false;
- memcpy_P(buf, rev, n);
- buf[n] = 0;
- version[2] = strtol(buf, &endptr, 10);
- if (*endptr != 0)
- return false;
-
- version[3] = FIRMWARE_REVISION_RELEASED;
- if (pgm_read_byte(p ++) == '-') {
- const char *q = p;
- while (! is_whitespace_or_nl_or_eol(char(pgm_read_byte(q))))
- ++ q;
- n = q - p;
- if (n == strlen_P(STR_REVISION_DEV) && strncmp_PP(p, STR_REVISION_DEV, n) == 0)
- version[3] = FIRMWARE_REVISION_DEV;
- else if (n == strlen_P(STR_REVISION_ALPHA) && strncmp_PP(p, STR_REVISION_ALPHA, n) == 0)
- version[3] = FIRMWARE_REVISION_ALPHA;
- else if (n == strlen_P(STR_REVISION_BETA) && strncmp_PP(p, STR_REVISION_BETA, n) == 0)
- version[3] = FIRMWARE_REVISION_BETA;
- else if ((n == 2 || n == 3) && strncmp_PP(p, STR_REVISION_RC, 2) == 0) {
- if (n == 2)
- version[3] = FIRMWARE_REVISION_RC;
- else {
- p += 2;
- if (is_digit(pgm_read_byte(p)))
- version[3] = FIRMWARE_REVISION_RC + pgm_read_byte(p) - '1';
- else
- return false;
- }
- } else
- return false;
- }
-
-#if 0
- SERIAL_ECHOPGM("Version parsed, major: ");
- SERIAL_ECHO(version[0]);
- SERIAL_ECHOPGM(", minor: ");
- SERIAL_ECHO(version[1]);
- SERIAL_ECHOPGM(", revision: ");
- SERIAL_ECHO(version[2]);
- SERIAL_ECHOPGM(", flavor: ");
- SERIAL_ECHO(version[3]);
- SERIAL_ECHOLNPGM("");
-#endif
- return true;
-}
-
// 1 - yes, 0 - false, -1 - error;
inline int8_t is_provided_version_newer(const char *version_string)
{
- uint16_t ver_gcode[4], ver_current[4];
+ uint16_t ver_gcode[4];
if (! parse_version(version_string, ver_gcode))
return -1;
- if (! parse_version_P(FW_VERSION_STR, ver_current))
- return 0; // this shall not happen
for (uint8_t i = 0; i < 3; ++ i)
- if (ver_gcode[i] > ver_current[i])
+ if (ver_gcode[i] > FW_VERSION_NR[i])
return 1;
return 0;
}
@@ -271,22 +182,17 @@ bool force_selftest_if_fw_version()
bool show_upgrade_dialog_if_version_newer(const char *version_string)
{
- uint16_t ver_gcode[4], ver_current[4];
+ uint16_t ver_gcode[4];
if (! parse_version(version_string, ver_gcode)) {
// SERIAL_PROTOCOLLNPGM("parse_version failed");
return false;
}
- if (! parse_version_P(FW_VERSION_STR, ver_current)) {
-// SERIAL_PROTOCOLLNPGM("parse_version_P failed");
- return false; // this shall not happen
- }
-// SERIAL_PROTOCOLLNPGM("versions parsed");
bool upgrade = false;
for (uint8_t i = 0; i < 4; ++ i) {
- if (ver_gcode[i] > ver_current[i]) {
+ if (ver_gcode[i] > FW_VERSION_NR[i]) {
upgrade = true;
break;
- } else if (ver_gcode[i] < ver_current[i])
+ } else if (ver_gcode[i] < FW_VERSION_NR[i])
break;
}
@@ -311,16 +217,14 @@ bool show_upgrade_dialog_if_version_newer(const char *version_string)
void update_current_firmware_version_to_eeprom()
{
- for (int8_t i = 0; i < FW_PRUSA3D_MAGIC_LEN; ++ i)
+ for (int8_t i = 0; i < FW_PRUSA3D_MAGIC_LEN; ++ i){
eeprom_update_byte((uint8_t*)(EEPROM_FIRMWARE_PRUSA_MAGIC+i), pgm_read_byte(FW_PRUSA3D_MAGIC_STR+i));
- uint16_t ver_current[4];
- if (parse_version_P(FW_VERSION_STR, ver_current)) {
- eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MAJOR, ver_current[0]);
- eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MINOR, ver_current[1]);
- eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_REVISION, ver_current[2]);
- // See FirmwareRevisionFlavorType for the definition of firmware flavors.
- eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_FLAVOR, ver_current[3]);
}
+ eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MAJOR, FW_VERSION_NR[0]);
+ eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MINOR, FW_VERSION_NR[1]);
+ eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_REVISION, FW_VERSION_NR[2]);
+ // See FirmwareRevisionFlavorType for the definition of firmware flavors.
+ eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_FLAVOR, FW_VERSION_NR[3]);
}
diff --git a/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h
index 4c20082e9..7ccd9151f 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 35313a412..4686f0978 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 acd7883dd..aa625c64e 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 ddf7e77a0..ddafa6f45 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 e7b114d96..29ff5b356 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 cde812498..342a9a1fd 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 16fba5d71..aacb7cb7d 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 13fc2a796..8659c582a 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 e9d894a5b..cc758ebf5 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 c9ad5275c..a75abb183 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 000000000..e69c1d3a0
--- /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(addr);
+ 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 000000000..6ece99c68
--- /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 000000000..5d92d6e6c
--- /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 ea92359ce..cc2d940df 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 12a9ed512..ecd2df2c2 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
diff --git a/lang/lang_en_es.txt b/lang/lang_en_es.txt
index 6c629326a..591043d7c 100644
--- a/lang/lang_en_es.txt
+++ b/lang/lang_en_es.txt
@@ -1176,7 +1176,7 @@
#MSG_UNLOAD_SUCCESSFUL c=20 r=2
"Was filament unload successful?"
-"Se cargocon exito el filamento?"
+"Se descargo con exito el filamento?"
#MSG_SELFTEST_WIRINGERROR c=18
"Wiring error"
diff --git a/lang/po/Firmware_es.po b/lang/po/Firmware_es.po
index ab8ecec91..a28eb8612 100644
--- a/lang/po/Firmware_es.po
+++ b/lang/po/Firmware_es.po
@@ -1488,7 +1488,7 @@ msgstr "Cuidado: Ha cambiado el tipo de impresora."
# MSG_UNLOAD_SUCCESSFUL c=20 r=2
#: Marlin_main.cpp:3680
msgid "Was filament unload successful?"
-msgstr "Se cargocon exito el filamento?"
+msgstr "Se descargo con exito el filamento?"
# MSG_SELFTEST_WIRINGERROR c=18
#: messages.c:98
diff --git a/lang/po/new/es.po b/lang/po/new/es.po
index 7f4b7d8bf..9e5088485 100644
--- a/lang/po/new/es.po
+++ b/lang/po/new/es.po
@@ -1488,7 +1488,7 @@ msgstr "Cuidado: Ha cambiado el tipo de impresora."
# MSG_UNLOAD_SUCCESSFUL c=20 r=2
#: Marlin_main.cpp:3680
msgid "Was filament unload successful?"
-msgstr "Se cargocon exito el filamento?"
+msgstr "Se descargo con exito el filamento?"
# MSG_SELFTEST_WIRINGERROR c=18
#: messages.c:98