ftdi+arm_debug: honor last-bit TMS; ARM7 EmbeddedICE halt/resume
The FTDI MPSSE xfer ignored TMS on data bits, so bscan_set_ir never latched the IR — the bscan exit needs the last bit to clock Shift->Exit1 so the following Update latches. It only ever worked on the Digilent driver. Now the final TMS-flagged bit is clocked through the TMS pin (carrying TDI/TDO), so bscan_set_ir/bscan_shift_dr reach Exit1->Update correctly. Implement ARM7TDMI EmbeddedICE access (SCAN_N + INTEST, 38-bit scan chain 2 register R/W with pipelined read) and halt (force DBGRQ, poll DBGACK) / resume (clear DBGRQ + RESTART). New cpu_halt / cpu_resume commands; arm_debug links bscan. Validated on an LPC2103 over the ARM-USB-OCD: set_ir(IDCODE) reads 0x4F1F0F0F, EmbeddedICE registers round-trip, cpu_halt -> DBGACK, cpu_resume releases the core. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,56 +1,154 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bscan/bscan.h"
|
||||
#include "arm_debug.h"
|
||||
|
||||
/* Not implemented yet — every entry point reports failure for now. The
|
||||
* real work — EmbeddedICE scan chains, halt/resume, memory access over
|
||||
* the bscan_* primitives, and a per-MCU RAM flash loader — slots in
|
||||
* behind these signatures without touching callers. */
|
||||
/*
|
||||
* ARM7TDMI debug over JTAG (EmbeddedICE), built on the bscan_* TAP
|
||||
* primitives. Incremental bring-up:
|
||||
* - done: EmbeddedICE register access, halt (force DBGRQ) / resume
|
||||
* - todo: memory read/write (debug-speed instruction injection) and
|
||||
* the arm_flash backend.
|
||||
*/
|
||||
|
||||
/* ARM7TDMI public JTAG instructions (IR length 4). */
|
||||
#define ARM7_IR_LEN 4
|
||||
#define IR_SCAN_N 0x2
|
||||
#define IR_INTEST 0xC
|
||||
#define IR_RESTART 0x4
|
||||
|
||||
/* EmbeddedICE scan chain (#2) and register addresses. */
|
||||
#define EICE_SCANCHAIN 2
|
||||
#define EICE_DBG_CTRL 0x00
|
||||
#define EICE_DBG_STATUS 0x01
|
||||
|
||||
#define DBG_STATUS_DBGACK (1u << 0)
|
||||
|
||||
/* One EmbeddedICE scan-chain-2 access: 38 bits LSB-first =
|
||||
* data[0..31] | address[32..36] | read/write[37] (1 = write). On a read,
|
||||
* the captured data belongs to the *previously* addressed register. */
|
||||
static int eice_scan(jtag_core *jc, int addr, int rw, uint32_t data, uint32_t *out)
|
||||
{
|
||||
uint8_t buf[5], cap[5];
|
||||
int i, r;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
for (i = 0; i < 32; i++)
|
||||
if (data & (1u << i)) buf[i >> 3] |= (uint8_t)(1u << (i & 7));
|
||||
for (i = 0; i < 5; i++)
|
||||
if (addr & (1 << i)) { int b = 32 + i; buf[b >> 3] |= (uint8_t)(1u << (b & 7)); }
|
||||
if (rw) buf[37 >> 3] |= (uint8_t)(1u << (37 & 7));
|
||||
|
||||
r = bscan_shift_dr(jc, buf, out ? cap : NULL, 38);
|
||||
if (r < 0) return -1;
|
||||
|
||||
if (out) {
|
||||
uint32_t v = 0;
|
||||
for (i = 0; i < 32; i++)
|
||||
if (cap[i >> 3] & (1u << (i & 7))) v |= (1u << i);
|
||||
*out = v;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Select the EmbeddedICE scan chain (#2) and enter INTEST. */
|
||||
static int eice_select(jtag_core *jc)
|
||||
{
|
||||
uint8_t sc = EICE_SCANCHAIN;
|
||||
if (bscan_set_ir(jc, IR_SCAN_N, ARM7_IR_LEN) < 0) return -1;
|
||||
if (bscan_shift_dr(jc, &sc, NULL, ARM7_IR_LEN) < 0) return -1;
|
||||
if (bscan_set_ir(jc, IR_INTEST, ARM7_IR_LEN) < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eice_write(jtag_core *jc, int addr, uint32_t val)
|
||||
{
|
||||
return eice_scan(jc, addr, 1, val, NULL);
|
||||
}
|
||||
|
||||
/* Read an EmbeddedICE register (two scans: request, then capture). */
|
||||
static int eice_read(jtag_core *jc, int addr, uint32_t *val)
|
||||
{
|
||||
if (eice_scan(jc, addr, 0, 0, NULL) < 0) return -1; /* request */
|
||||
if (eice_scan(jc, addr, 0, 0, val) < 0) return -1; /* capture */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_debug_halt(jtag_core *jc, const jtag_target *t)
|
||||
{
|
||||
(void)jc; (void)t;
|
||||
fprintf(stderr, "arm_debug: halt not implemented yet\n");
|
||||
return -1;
|
||||
uint32_t status = 0;
|
||||
(void)t;
|
||||
|
||||
bscan_tap_reset(jc);
|
||||
if (eice_select(jc) < 0)
|
||||
return -1;
|
||||
|
||||
/* Debug Control bit 1 = DBGRQ -> core enters debug at the next
|
||||
* instruction boundary; poll DBGACK (it isn't instantaneous). */
|
||||
if (eice_write(jc, EICE_DBG_CTRL, 0x2) < 0)
|
||||
return -1;
|
||||
|
||||
{
|
||||
int tries;
|
||||
for (tries = 0; tries < 100; tries++) {
|
||||
bscan_idle_cycles(jc, 64);
|
||||
if (eice_read(jc, EICE_DBG_STATUS, &status) < 0)
|
||||
return -1;
|
||||
if (status & DBG_STATUS_DBGACK)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "arm_debug: halt requested but no DBGACK (status 0x%08x)\n", status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int arm_debug_resume(jtag_core *jc, const jtag_target *t)
|
||||
{
|
||||
(void)jc; (void)t;
|
||||
fprintf(stderr, "arm_debug: resume not implemented yet\n");
|
||||
return -1;
|
||||
(void)t;
|
||||
if (eice_select(jc) < 0)
|
||||
return -1;
|
||||
/* Clear DBGRQ, then RESTART exits debug state. */
|
||||
if (eice_write(jc, EICE_DBG_CTRL, 0x0) < 0)
|
||||
return -1;
|
||||
if (bscan_set_ir(jc, IR_RESTART, ARM7_IR_LEN) < 0)
|
||||
return -1;
|
||||
bscan_idle_cycles(jc, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_debug_mem_read(jtag_core *jc, const jtag_target *t,
|
||||
unsigned long addr, void *buf, unsigned long len)
|
||||
{
|
||||
(void)jc; (void)t; (void)addr; (void)buf; (void)len;
|
||||
fprintf(stderr, "arm_debug: mem_read not implemented yet\n");
|
||||
return -1;
|
||||
(void)jc; (void)t; (void)addr; (void)buf; (void)len;
|
||||
fprintf(stderr, "arm_debug: mem_read not implemented yet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int arm_debug_mem_write(jtag_core *jc, const jtag_target *t,
|
||||
unsigned long addr, const void *buf, unsigned long len)
|
||||
{
|
||||
(void)jc; (void)t; (void)addr; (void)buf; (void)len;
|
||||
fprintf(stderr, "arm_debug: mem_write not implemented yet\n");
|
||||
return -1;
|
||||
(void)jc; (void)t; (void)addr; (void)buf; (void)len;
|
||||
fprintf(stderr, "arm_debug: mem_write not implemented yet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int arm_flash_program(jtag_core *jc, const jtag_target *t, const char *file,
|
||||
arm_log_fn log, void *user)
|
||||
{
|
||||
char msg[256];
|
||||
(void)jc; (void)file;
|
||||
if (log) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"arm_flash: backend not implemented yet. "
|
||||
"Target '%s' debug=%d ram=0x%lX+0x%lX flash=0x%lX+0x%lX.",
|
||||
t ? t->name : "?",
|
||||
t ? (int)t->cpu.debug : 0,
|
||||
t ? t->cpu.ram_base : 0UL, t ? t->cpu.ram_size : 0UL,
|
||||
t ? t->cpu.flash_base : 0UL, t ? t->cpu.flash_size : 0UL);
|
||||
log(user, 1, msg);
|
||||
}
|
||||
return -1;
|
||||
char msg[256];
|
||||
(void)jc; (void)file;
|
||||
if (log) {
|
||||
snprintf(msg, sizeof(msg),
|
||||
"arm_flash: backend not implemented yet. "
|
||||
"Target '%s' debug=%d ram=0x%lX+0x%lX flash=0x%lX+0x%lX.",
|
||||
t ? t->name : "?",
|
||||
t ? (int)t->cpu.debug : 0,
|
||||
t ? t->cpu.ram_base : 0UL, t ? t->cpu.ram_size : 0UL,
|
||||
t ? t->cpu.flash_base : 0UL, t ? t->cpu.flash_size : 0UL);
|
||||
log(user, 1, msg);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user