Commit Graph

9 Commits

Author SHA1 Message Date
cdbeea7b61 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>
2026-05-24 20:09:39 +02:00
2c16a66beb arm_debug: cycle-exact JTAG layer; system-speed memory read works
Read CPU memory over JTAG via system-speed LDM. Validated on the LPC2103:
reads the real ARM reset vectors and contiguous multi-block code.

The core only advances on Run-Test/Idle debug clocks (not Update-DR), so
the trick is keeping that clock count exact:
- "quiet" TAP ops (quiet_set_ir / quiet_shift_dr / quiet_chain_select /
  quiet_eice_read / quiet_latch_chain1) pass through Update but park in
  Pause, never RTI -> they switch chains and read EmbeddedICE WITHOUT
  clocking the core, so they can't clobber the registers a sys-speed LDM
  just loaded.
- clock_core(n) is the only thing that advances the core (n RTI clocks).
- execute_sys_speed: RESTART, then drive the access one clock at a time
  with a quiet DBG_STATUS check between, stopping the instant
  SYSCOMP & DBGACK appear (no over-clock past re-entry).
- after sys-speed: quiet-switch to chain 1, quiet-latch a NOP to displace
  the stale LDMIA, then read_core_regs.
- pre-read pipeline normalization: change_to_arm (17 clocked instrs) for
  a Thumb halt; 17 ARM NOPs for an ARM halt.

WIP: not yet reliable across all halt states - the first read after some
halts times out (SYSCOMP never appears) and leaves the core running.
Within one good halt, reads are consistent and correct. Diagnosis and
next steps in the arm7-debug-dclk-timing note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 20:00:51 +02:00
68229339e9 arm_debug: fix stale c1_xfer comment (bscan, not Pause-DR)
The c1_ctx/c1_xfer comments still described the abandoned Pause-DR
parking model; the access is a single bscan_shift_dr (one debug clock
per access). Comment-only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:28:55 +02:00
fda6aed077 arm_debug: cycle-exact chain-1, Thumb->ARM, sys-speed re-entry
The chain-1 access is now deterministic: a bare 33-bit bscan_shift_dr
(breakpoint | flip32(instr)) is exactly one debug clock per access (the
Update->Run-Test/Idle transition). The earlier "+1 idle" double-clocked
the pipeline and the earlier all-zero/constant reads were the core being
in Thumb state. Validated on the LPC2103 by a known-pattern register
round-trip (write r1..r15, read back -> exact match; r15/PC differs by
the expected pipeline offset).

- c1_xfer: drop the extra idle dwell (one access == one debug clock).
- mem_read: detect Thumb (ITBIT) and change_to_arm in one continuous
  chain-1 session so no chain switch clocks the core mid-sequence.
- execute_sys_speed: drop the post-RESTART idle burst and poll
  DBG_STATUS straight away (matches OpenOCD); the system-speed LDM now
  re-enters debug (DBGACK & SYSCOMP) instead of running free.

WIP: the read_core_regs after a system-speed access is phase-shifted by
the EmbeddedICE<->chain-1 switch, so memory reads come back misaligned
(capturing injected instructions). Next step + diagnosis in the
arm7-debug-dclk-timing note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:23:45 +02:00
e0dc58d09c arm_debug: debug entry (DBGRQ-clear), Thumb->ARM, cpu_read (WIP)
Bring up ARM7TDMI debug toward reading CPU memory/flash over JTAG.
Validated on the LPC2103 (Olimex ARM-USB-OCD): halt holds DBGACK,
RESTART resumes, the Thumb->ARM switch clears ITBIT, and real register
data streams out of the STMIA injection.

- arm_debug:
  - halt: after DBGACK, reprogram DBG_CTRL = DBGACK|INTDIS (deassert
    DBGRQ) per OpenOCD's debug entry; without this, injected
    instructions don't execute. Warn on Thumb (ITBIT).
  - change_to_arm: switch a Thumb-state core to ARM (duplicated-halfword
    Thumb opcodes), needed because the firmware may halt in either state.
  - chain-1 instruction injection: c1_xfer/read_core_regs/
    write_core_regs/load_word_regs + execute_sys_speed (RESTART, poll
    DBGACK&SYSCOMP); arm_debug_mem_read does word-block system-speed LDM.
- script: cpu_read <dev> <addr> <len> <file> <bin|hex> command +
  built-in Intel HEX writer (type 04/00/01 records).

WIP: c1_xfer (on bscan_shift_dr) is not yet cycle-exact (one debug clock
per access), so memory reads can be misaligned. Remaining work and the
diagnosis are in the arm7-debug-dclk-timing note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 17:58:45 +02:00
aecaebdaf1 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>
2026-05-24 16:34:08 +02:00
39963fd6d8 ftdi: replace proprietary libftd2xx with open-source libftdi1
The Olimex ARM-USB-OCD (and any FT2232 with a custom USB id) couldn't be
enumerated by libftd2xx and needed a manual ftdi_sio unbind. libftdi1
(libusb) opens any VID:PID and auto-detaches the kernel driver.

- rewrite drivers/ftdi_jtag on libftdi1: enumerate a known VID:PID list
  (incl. Olimex 15ba:0003/002b) with per-chip channel counts, open by
  bus/addr + interface, MPSSE via ftdi_write_data/ftdi_read_data (+
  SEND_IMMEDIATE for deterministic reads). MPSSE command building, pin
  map and clocking unchanged.
- CMake: link libftdi1 + libusb-1.0 (pkg-config), drop FTDILIB/FTD2XX
  defines and the libftd2xx.a link; remove the vendored src/libs/libftd2xx.
- registry: NXP LPC2103 (ARM7TDMI-S) entry, IDCODE 0x4F1F0F0F.
- docs updated (deps, layout, decision note, roadmap phase 8).

Validated on hardware: ARM-USB-OCD enumerates, jtag_scan reads the
LPC2103 IDCODE 0x4F1F0F0F, target_info -> [cpu, ARM7] prog: arm_flash.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 16:06:52 +02:00
9ad776268e target: generalize the registry to FPGAs + CPUs, add program dispatch
Restructure in anticipation of programming ARM CPUs (ARM7/9 via
EmbeddedICE, e.g. over an Olimex ARM-USB-OCD); FPGA path unchanged.

- modules/fpga -> modules/target; fpga_target -> jtag_target with a
  `kind` (fpga|cpu) and grouped fpga/cpu sub-structs; data/targets.yaml
  (env BS_TARGETS); API target_*; commands target_list/target_info
  (kind-aware). Add arm7/arm9 families, arm_flash prog, embeddedice
  debug, and cpu fields (ram_base/size, flash_base/size).
- new program/: `program <dev> <file>` dispatches by the target's prog
  (svf wired; proxy_spi points at the flash workflow; arm_flash -> arm_debug).
- new arm_debug/: EmbeddedICE halt/resume/mem + arm_flash backend
  declared, not implemented yet.
- bscan_* take const jtag_target* and read the fpga sub-struct.
- data/probes.yaml: arm-usb-ocd profile slot; data/targets.yaml: an ARM7
  example entry. Docs + an ARM-debug design note in CLAUDE.md.

Builds; FPGA path re-validated on the IGLOO2 (target_list shows the CPU
example; jtag_open/autoinit/program 0 <svf> all work).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 15:33:58 +02:00
d1bdce91dc restructure: code+libs under src/, runtime resources under data/
Separate the two concerns the repo root was mixing:
- src/   — bs/, modules/, libs/ (code + vendored libs)
- data/  — fpga_registry.yaml, probes.yaml, bsdl_files/, bscan_proxies/,
           scripts/ (everything the tool reads at runtime, CWD-relative)
- doc/   — kept at the root

CMake: repoint DIR_MODULES/DIR_LIBS and add_subdirectory at src/; emit
the binary at the build/ root (build/bs) via CMAKE_RUNTIME_OUTPUT_DIRECTORY
instead of the nested build/src/bs/. The jtag_core ../../libs path still
resolves since modules and libs moved together.

Runtime default paths now point under data/ (fpga.c, probes.c, script.c
bsdl_files lookup, init.c config.script). Docs (README/tutorial/CLAUDE)
updated for the new layout, src/ module paths, and ./build/bs.

Validated on the IGLOO2/FlashPro: profiles, autoinit, and svf_play all
work run from the repo root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 15:03:25 +02:00