Prusa-Firmware/Firmware/xflash_dump.cpp
Yuri D'Elia dd8c6c064c xfdump: simplify stack debugging (sample pc+sp)
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.
2021-06-17 01:29:25 +02:00

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