arm_debug: OpenOCD-style debug entry + warm-up read
Make the system-speed memory read reliable within a halt: - debug entry mirrors OpenOCD's arm7_9_debug_entry: change_to_arm when the core halted in Thumb, then read all 16 core registers. That fixed STMIA+NOP+NOP+16 sequence flushes the firmware out of the pipeline and leaves a deterministic state for both Thumb and ARM halts. - warm-up read: the first system-speed read after debug entry normalizes the sys-speed pipeline but its own result is unreliable, so do one throwaway read block and discard it. Every read after it is consistent and correct (analogous to the FTDI stale-first-read). Within one clean halt, reads now come back correct (no misalignment). Repeated halt/read cycles without a power-cycle still degrade (the read clobbers r0..r14, so a later re-halt/resume is messy) - the intended flow is power-on -> one halt -> dump. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -553,21 +553,43 @@ int arm_debug_mem_read(jtag_core *jc, const jtag_target *t,
|
|||||||
if (chain_select(jc, SC_EICE) < 0) return -1;
|
if (chain_select(jc, SC_EICE) < 0) return -1;
|
||||||
if (eice_read(jc, EICE_DBG_STATUS, &status) < 0) return -1;
|
if (eice_read(jc, EICE_DBG_STATUS, &status) < 0) return -1;
|
||||||
if (chain_select(jc, SC_DEBUG) < 0) return -1;
|
if (chain_select(jc, SC_DEBUG) < 0) return -1;
|
||||||
/* Normalize the core to a known ARM pipeline state regardless of the
|
/* Debug entry, mirroring OpenOCD's arm7_9_debug_entry to leave a
|
||||||
* halt state. In Thumb, change_to_arm (17 clocked instructions)
|
* deterministic pipeline regardless of halt state: switch Thumb->ARM
|
||||||
* switches to ARM and flushes the firmware out of the pipeline; the
|
* if needed, then read all 16 core registers. That STMIA+NOP+NOP+16
|
||||||
* read alignment is tuned for that. In ARM, run the same NUMBER of
|
* sequence flushes the firmware out of the pipeline and ends in the
|
||||||
* clocked NOPs so the read sees the same pipeline phase (skipping it
|
* same known state for both the Thumb and ARM paths, so the first
|
||||||
* left the firmware's arbitrary pipeline and the read misaligned). */
|
* system-speed read reliably re-enters debug. */
|
||||||
|
{
|
||||||
|
uint32_t scratch[16];
|
||||||
c1_init(&c1, jc);
|
c1_init(&c1, jc);
|
||||||
if (status & DBG_STATUS_ITBIT) {
|
if (status & DBG_STATUS_ITBIT)
|
||||||
if (change_to_arm(&c1) < 0) return -1;
|
if (change_to_arm(&c1) < 0) return -1;
|
||||||
} else {
|
memset(scratch, 0, sizeof(scratch));
|
||||||
int k;
|
if (read_core_regs(&c1, 0, 0xffff, scratch) < 0) return -1;
|
||||||
for (k = 0; k < 17; k++)
|
|
||||||
if (c1_xfer(&c1, ARM_NOP, 0, NULL) < 0) return -1;
|
|
||||||
}
|
|
||||||
c1_end(&c1);
|
c1_end(&c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WARM-UP: the first system-speed read after debug entry normalizes
|
||||||
|
* the sys-speed pipeline but its own result is unreliable. Do one
|
||||||
|
* throwaway read block and discard it; every read after it is
|
||||||
|
* consistent and correct. (Like the FTDI stale-first-read, but for
|
||||||
|
* the ARM debug pipeline.) */
|
||||||
|
{
|
||||||
|
uint32_t scratch[16];
|
||||||
|
r0 = (uint32_t)base;
|
||||||
|
c1_init(&c1, jc);
|
||||||
|
if (write_core_regs(&c1, 0, 0x1, &r0) < 0) return -1;
|
||||||
|
if (load_word_regs(&c1, 0x7ffe) < 0) return -1; /* r1..r14 */
|
||||||
|
c1_end(&c1);
|
||||||
|
if (execute_sys_speed(jc) < 0) return -1;
|
||||||
|
if (quiet_chain_select(jc, SC_DEBUG) < 0) return -1;
|
||||||
|
if (quiet_latch_chain1(jc, ARM_NOP) < 0) return -1;
|
||||||
|
quiet_exit(jc);
|
||||||
|
memset(scratch, 0, sizeof(scratch));
|
||||||
|
c1_init(&c1, jc);
|
||||||
|
if (read_core_regs(&c1, 0, 0x7ffe, scratch) < 0) return -1;
|
||||||
|
c1_end(&c1);
|
||||||
|
}
|
||||||
|
|
||||||
r0 = (uint32_t)base;
|
r0 = (uint32_t)base;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user