dd8c6c064c
Instead of having to guess the PC where the SP was sampled, always take both. This allows "seamless" stack decoding for both serial and xflash dumps, since we don't have to guess which function generated the dump. Make the core functions (doing the sampling) be ``noinline`` as well, so that they always have valid frame.
110 lines
2.6 KiB
C++
110 lines
2.6 KiB
C++
#include <stddef.h>
|
|
|
|
#include <avr/wdt.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
#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
|