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