Commit Graph

82 Commits

Author SHA1 Message Date
1fcc9bdae6 doc: design note for boundary-scan board test
Capture the plan for the strongest fit of the tool: feed a board netlist
+ the chain devices' BSDL and auto-generate/run boundary-scan
interconnect tests (opens/shorts/stuck-at) + chain integrity.

Covers inputs (netlist, BSDL, board.yaml), a new bstest/ layer whose
central primitive is a whole-chain boundary register (lifts the current
single-device assumption), the test types, the hard parts (safety/
contention, control-cell mapping, multi-device bit order, vector gen),
and a 5-phase plan.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 20:53:19 +02:00
22feb66467 doc: describe the ARM7 memory-read operating context
The ARM7TDMI memory read (cpu_read/cpu_halt/cpu_resume) works; document
when and how:
- tutorial: rewrite the "CPU targets" section from "structure only" to a
  working cpu_read walkthrough (dump LPC2103 flash to Intel HEX), state
  the operating envelope (power-on -> one halt -> dump; reads clobber
  r0..r14/PC, no context save/restore, so resume isn't clean and a
  re-halt in the same session can time out -> power-cycle), plus a
  troubleshooting row for the sys-speed timeout.
- CLAUDE.md: roadmap phase 7 + ARM-debug note now say the read works
  (flash dump validated), with context save/restore + arm_flash write as
  the remaining steps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 20:32:46 +02:00
44cb9dfbae arm_debug: doc - memory read validated by 32 KB flash dump
cpu_read dumped the LPC2103's full 32 KB flash to Intel HEX
(objcopy-verified: all records/checksums valid, correct vectors). Update
the comments to reflect the working state and the power-on -> one halt ->
dump flow (context save/restore for repeated reads is the next step).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 20:23:04 +02:00
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
cc2ee5d92c doc: refresh README/tutorial/CLAUDE for profiles, clock, SVF
Bring the docs up to date and keep each in its lane:
- README (overview): both programming paths (Xilinx proxy flash + SVF),
  probe profiles, neutral JTAG clock + per-device cap, runtime YAML
  registry, IGLOO2 bundled; run-from-repo-root fixed
- tutorial (user view): probe profiles + jtag_close, the prog tag, a
  JTAG-clock section, a new "Programming via SVF" section, prog/max_tck
  in the add-a-target table, troubleshooting rows
- CLAUDE.md (design): architecture tree lists the project modules + YAML
  data files; roadmap gains phases 5 (probes/JTAG-link) and 6 (SVF)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 14:50:02 +02:00
c77d86efd0 svf: initial SVF player + svf_play command
New modules/svf/ plays the single-device SVF subset over the bscan_*
primitives: SIR/SDR with masked TDO compare, RUNTEST (TCK/SEC), STATE
(RESET/IDLE), ENDIR/ENDDR (IDLE only), HIR/HDR/TIR/TDR (length 0), TRST,
FREQUENCY. Exposed as `svf_play <file>`. It warms up the FTDI link
first — the MPSSE's first data read after open returns stale FIFO
content, normally hidden because jtag_scan runs before anything.

Also adds the bscan_tap_reset prototype. Validated on the live IGLOO2
M2GL010T: a hand-written IDCODE-check SVF passes (masked compare) and a
deliberately wrong one is caught at the mismatching bit. A generic
program dispatch off the prog tag, multi-device chains and non-IDLE end
states are still TODO.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 14:36:41 +02:00
37efccaf50 bscan: rename from bscan_spi, add TAP primitives for the SVF player
The module outgrew its "SPI bridge" name — it's mostly generic
single-device JTAG TAP primitives now. Rename modules/bscan_spi ->
modules/bscan (dir, files, library target, includes, doc paths);
bscan_* function names and bscan_spi_xfer() kept.

Add the two primitives the SVF player needs beyond shift_dr:
- bscan_shift_ir: general IR scan with TDO capture (bscan_set_ir is
  opcode-only, no readback)
- bscan_tap_reset: force Test-Logic-Reset, land in Run-Test/Idle

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 12:00:38 +02:00
2a03cb1145 jtag/fpga: prog method tag + RTCK link setting (phase C)
- fpga_target gains a prog method (proxy_spi/svf/none), set in the
  registry or inferred when omitted (proxy_bitstream -> proxy_spi;
  Microsemi/Lattice -> svf); shown by fpga_info/fpga_list and exposed via
  fpga_prog_method_name() for the future program dispatch
- generalise RTCK as a neutral JTAG_RTCK, mirrored to
  PROBE_FTDI_JTAG_ENABLE_RTCK at open (FTDI-only)
- reset abstraction deferred (no clean neutral form yet); the program
  dispatch command itself lands with the SVF player

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 11:53:02 +02:00
ac883237ac jtag: cap the clock by device max_tck_khz at autoinit (phase B)
- fpga_target gains max_tck_khz (registry key), the max safe JTAG TCK
  for a part/board (0 = unspecified)
- jtag_autoinit, after identifying the chain, resolves the clock: if the
  requested JTAG_TCK_FREQ_KHZ exceeds the smallest device max, it clamps
  it and re-opens the probe once (stored probe id) to apply, then
  re-scans; within-cap / unset just report the cap
- autoinit body extracted into autoinit_run() so it can re-run after the
  re-tune; fpga_list shows maxtck

Validated on the IGLOO2/FlashPro (req 500 -> clamp 200 -> reopen -> still
detected; within-cap and unset paths don't reopen).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 11:45:53 +02:00
3aad5e2308 jtag: driver-neutral JTAG_TCK_FREQ_KHZ clock (phase A)
One clock knob across probes instead of per-driver names:
- jtag_open mirrors JTAG_TCK_FREQ_KHZ into PROBE_FTDI_TCK_FREQ_KHZ for
  the Viveris FTDI driver (read-only at init); unset leaves the existing
  value untouched
- the Digilent driver reads JTAG_TCK_FREQ_KHZ directly instead of
  hardcoding 4 MHz (falls back to 4 MHz when unset)
- documented in probes.yaml; CLAUDE.md design note marks phase A done

FTDI path validated on the IGLOO2/FlashPro (250 kHz, mirror confirmed);
Digilent path not hardware-tested.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 11:39:14 +02:00
4ee1c2b631 probes: add probe-config profiles loaded from probes.yaml
- new modules/probes/ parses probes.yaml (libyaml): a defaults: map
  applied on every jtag_open + named profiles: selected with
  `jtag_open <idx> <profile>` (jtag_profiles lists them); each value is
  pushed into the script envvar store the driver reads at open time
- ships a flashpro profile (ADBUS4 high-Z) that lets the IGLOO2 kit's
  embedded FlashPro (FT4232H, port 0) detect the chain
- CLAUDE.md: decision entry for probes.yaml + a design note on the
  probe / JTAG-link / device config strategy (driver-neutral link layer)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 11:22:19 +02:00
00320d87ec script: add jtag_close to release the current probe
Lets a session hand a probe back (frees its USB handle) without opening
another, e.g. to flash two FPGAs on different probes in turn:
jtag_close, then jtag_open the next one and jtag_autoinit. Mirrors the
DeInit teardown jtagcore_loaddriver already does when switching drivers.

Also fix the help printer: no-arg commands (whose params slot is "")
printed an empty body because "" was treated as the terminator. Params
are now optional and the description always shows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:48:01 +02:00
70091cc4d3 fpga: add Microsemi IGLOO2 M2GL010T to the registry
IDCODE 0x0F8031CF (mask 0x0FFFFFFF, top nibble = revision) and IR
length 8 read from bsdl_files/m2gl010t-fg484.bsd. No proxy or IR
opcodes: IGLOO2 internal flash is programmed by playing a Libero SVF,
not via the Xilinx BSCAN-proxy SPI path, so those fields don't apply.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:47:05 +02:00
d579c56de6 fpga: don't warn on BSDLs that share an IDCODE in autoinit
Several BSDL files can legitimately match one IDCODE (same die in
different packages, or twins like SmartFusion2 / IGLOO2). The autoinit
loader loaded every match onto the device, silently overwriting the
first, and flagged it as "ID conflit ?". Keep the first match and skip
later files with the same IDCODE instead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:46:58 +02:00
3579c5efb0 fpga: load registry from yaml at runtime, not compile-time
- registry moves from the array in fpga.c to fpga_registry.yaml at the
  repo root, parsed via libyaml (pkg-config yaml-0.1); adding a part is
  now a YAML edit, no rebuild
- looked up CWD-relative (like bsdl_files/), overridable with
  $BS_FPGA_REGISTRY, loaded lazily once; public API unchanged
- fpga_list shows the source file (fpga_registry_source())
- add microsemi_igloo2/smartfusion2 and lattice_machxo2/3 families,
  ready for the non-Xilinx targets
- docs updated: CLAUDE.md, README, tutorial "add a target" walkthrough

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:19:53 +02:00
27836d63bb doc: design note for a standalone embedded port (Arduino GIGA R1)
Captures feasibility and shape of an MCU-based programmer: the MCU is
the JTAG master (GPIO/SPI-assisted driver behind the existing
drv_TX_TMS/drv_TXRX_DATA seam), jtag_core/registry/SVF-player/flash
logic reused as-is, REPL/os_interface/BSDL-parse dropped or replaced,
and the one real rework — stream payloads from SD/USB in chunks instead
of one big malloc. SVF-from-SD is the ideal fit for small Lattice/IGLOO2.
2026-05-24 01:28:20 +02:00
956f26f6bf doc: reframe programming-backends note around a universal SVF player
Capture the vision: a single SVF player is a near-universal backend
(Xilinx fabric config, Lattice, Microsemi IGLOO2, Altera, CPLDs from
vendor-exported files); native backends are exceptions where streaming/
speed/control matter — chiefly Xilinx external SPI flash via the proxy.
Adds a per-target path table and what an SVF player must implement.
2026-05-24 01:20:45 +02:00
4329030ab9 doc: design note for extending to Lattice and Microsemi IGLOO2
Both hold config in internal flash (programmed directly over JTAG), so
the Xilinx external-flash+proxy path doesn't apply. Records two backend
strategies — native per-family (Lattice ISC / IEEE 1532) vs a generic
SVF/STAPL player (recommended for IGLOO2, whose algorithm is
proprietary) — what already generalises, and the registry/dispatch
changes needed.
2026-05-24 01:15:32 +02:00
4b924629be doc: update README for the proxy flash path and current state
Reflect what now works: per-FPGA registry, BSCAN proxy SPI bridge, and
flash detect/read/erase/program/verify (~100 KB/s). Switch the typical
flow to the proxy path, add the fpga/bscan/flash command rows, note
persistent history, the KU040 BSDL, the new modules + bscan_proxies/ in
the layout, and the quartiq MIT proxies in the license section.
2026-05-24 01:11:39 +02:00
d6b5fb572f doc: how to build a not-pre-built proxy (KU15P) in the tutorial
quartiq ships no UltraScale+ proxy, so the KU15P .bit must be built from
xilinx_bscan_spi.py (Migen + Vivado) after adding the part to the
generator's device table. Put the operational steps in the tutorial's
Phase 2.5 (where users look for a bitstream); CLAUDE.md just points to
it.
2026-05-24 01:04:44 +02:00
f4ff305cd1 doc: flesh out "add an FPGA target", explain caveats
Rework tutorial section 6 with a worked XCKU040 example: how to pull IR
opcodes/IDCODE from the BSDL (with a grep), a field-by-field table, the
verbatim registry entry, and a dedicated section on what `caveats` are
(bit-flags for hardware gotchas, not free text) and how to add one.
Also fixes the broken in-page anchor links to the section.
2026-05-24 00:57:45 +02:00
65ebe6b00c bs: persist REPL command history across sessions
Load and save readline history in $HOME/.bs_explorer_history (capped at
1000 entries), so up-arrow recalls commands from previous sessions.
Saved on exit/quit, EOF and Ctrl-C.
2026-05-24 00:55:13 +02:00
e987afc624 doc: mark Phase 4 done
Full flash command set (detect/read/erase/write/verify) validated on the
KCU105 via a save/erase/write-random/verify/restore round-trip.
2026-05-24 00:53:07 +02:00
d6f843e081 script: add flash_erase/write/verify, flash_read to file (Phase 4)
Completes the flash command set over the BSCAN proxy:
 - flash_erase <dev> <addr> <len>  : erase covering sectors (destructive)
 - flash_write <dev> <addr> <file> : program from a file (no auto-erase)
 - flash_verify <dev> <addr> <file>: compare flash against a file
 - flash_read gains an optional [file] arg for a raw binary dump (backup)

Validated on the KCU105 with a save -> erase -> write-random -> verify
-> erase -> restore round-trip (region left untouched). Apparent write
throughput ~100 KB/s once the proxy is loaded.
2026-05-24 00:52:50 +02:00
6330326513 script: jtag_scan reports the devices found
jtag_scan only printed "JTAG Scan done". Now it prints the device count
and each device's IDCODE (it scans the chain but, unlike jtag_autoinit,
loads no BSDL). Also replaces the placeholder help text.

Validated on the KCU105: shows device 0 IDCODE 0x03822093.
2026-05-24 00:36:46 +02:00
71b74fa03d fpga: rename "quirk" to "caveat"
"quirk" was unclear jargon; "caveat" matches the wording already used in
the README/CLAUDE.md ("Xilinx caveats"). Renames the struct field, the
FPGA_QUIRK_* macro, the fpga_info output and the docs. No behaviour
change.
2026-05-24 00:31:47 +02:00
35b2022362 script: label the identify/load phases in jtag_autoinit
autoinit parses every bsdl twice for a matching part — once to read the
IDCODE, once to actually attach it — producing two identical loader
blocks with no hint why. Bracket each with an [identify] / [load] line.
2026-05-24 00:22:48 +02:00
ca611b96fa doc: mark Phase 3 done, Phase 4 in progress
spi_flash/ landed (detect+read validated on KCU105); flash_detect and
flash_read commands done, write/erase/verify still pending.
2026-05-24 00:14:28 +02:00
350918dbe8 script: add flash_detect and flash_read commands
Read-only SPI flash commands over the BSCAN proxy (via a small adapter
to spi_flash's xfer callback). flash_detect identifies the chip from
its JEDEC ID; flash_read hex-dumps a region.

Validated on the KCU105: flash_detect -> Micron MT25QU256, flash_read
shows the stored bitstream (sync word 0xAA995566 at 0x50). Erase/program
commands deferred to Phase 4.
2026-05-24 00:13:29 +02:00
c4afe877ce spi_flash: add generic SPI NOR flash layer (Phase 3)
Transport-agnostic (xfer callback) layer with a JEDEC-ID chip database
and detect/read/erase_sector/program_page/program/verify. Standard
opcode set, 3- and 4-byte addressing (4-byte command opcodes for parts
over 16 MB). Seeded with the KCU105's MT25QU256 plus common
Winbond/Macronix/ISSI/Micron parts.

detect + read validated on the KCU105 over the proxy. erase/program are
implemented but not yet hardware-tested (destructive on a config flash).
2026-05-24 00:13:29 +02:00
4f46bc6d3c doc: proxy SPI path validated on KCU105 (JEDEC 0x20 BB 19)
bscan_load_bitstream + bscan_jedec confirmed end to end on a KCU105:
reads the Micron MT25QU256 config flash (0x20 BB 19) through the
quartiq XCKU040 proxy. Replace the illustrative JEDEC output with the
real one.
2026-05-24 00:04:31 +02:00
4d0637e997 digilent: heap-allocate shift buffers, drop the size cap
The TX/TXRX functions used fixed ~1-2 KB stack buffers and rejected
anything larger, so any big shift failed: bscan_idle_cycles(10000) and
above all the ~19 Mbit bitstream load for the BSCAN proxy. Size the
pack/capture buffers to the shift on the heap instead.

This is what unblocked the proxy load — see next commit.
2026-05-24 00:04:31 +02:00
1814c4cf0c doc: explain what a JEDEC ID is
The bscan_jedec command and tutorial referenced the JEDEC ID without
defining it. Describe the 0x9F RDID command and the manufacturer +
device byte layout, in the tutorial and the command help.
2026-05-23 17:18:09 +02:00
f1c8a8aac7 doc: document SPI flashing through the BSCAN proxy
Rewrite the Phase 2.5 tutorial section for the now-working path: fetch
the proxy bitstream, bscan_load_bitstream, bscan_jedec, and the
bscan_spi_xfer primitive. Note the JPROGRAM reconfiguration caveat. The
shown JEDEC output is illustrative — not yet hardware-confirmed.
2026-05-23 17:16:36 +02:00
8e3428788c build: Digilent backend on by default on UNIX
The driver dlopen's libdjtg/libdmgr and degrades to "no probe" if
they're absent, so building it in has no cost or dependency.
BS_ENABLE_DIGILENT now defaults ON on UNIX (needs <dlfcn.h>); disable
with -DBS_ENABLE_DIGILENT=OFF. Docs updated; also fixes the quartiq
license note in CLAUDE.md (MIT, not BSD-2).
2026-05-23 17:16:36 +02:00
888f0eed5b bscan_proxies: vendor XCKU040 proxy bitstream (MIT, quartiq)
bscan_spi_xcku040.bit from quartiq/bscan_spi_bitstreams (c) QUARTIQ
GmbH, MIT — LICENSE.quartiq and README.md included for attribution.
Lets the BSCAN proxy path work out of the box on the KCU105.
2026-05-23 17:16:36 +02:00
12f358981f script: add bscan_jedec command
bscan_jedec <device> reads the SPI flash JEDEC ID (0x9F + 3 bytes)
through a loaded BSCAN proxy, via bscan_spi_xfer. Validation command for
the proxy path; not yet exercised on hardware.
2026-05-23 17:16:19 +02:00
0c9cc679f1 bscan_spi: implement bscan_spi_xfer over the jtagspi proxy
One CS-framed transaction: marker + 32-bit count + MOSI + read-latency
skip + MISO, MSB-first on the wire, matching OpenOCD's jtagspi so the
quartiq proxy bitstreams work unchanged. Half-duplex (tx,txlen,rx,rxlen)
signature, single-device chain.

NOT yet validated on hardware — protocol follows the OpenOCD reference
but has not been confirmed against a live proxy + flash. Validation
(read JEDEC ID on the KCU105) is the next step.
2026-05-23 17:16:19 +02:00
ba9372c8b2 fpga: add XCKU040 (KCU105) registry entry
IDCODE 0x03822093, UltraScale, opcodes from xcku040_ffva1156.bsd
(same as the family: USER1 0x02, CFG_IN 0x05, JPROGRAM 0x0B,
JSTART 0x0C). Points proxy_bitstream at bscan_spi_xcku040.bit.
2026-05-23 17:16:19 +02:00
952c010c63 script: shorten jtag_* command names
Drop the get_/set_/_pin/_list noise from the JTAG commands (e.g.
jtag_get_probes_list -> jtag_probes, jtag_set_spi_cs_pin -> jtag_spi_cs,
jtag_spi_rd_wr -> jtag_spi_xfer). jtag_open_probe -> jtag_open (not
jtag_probe, which would clash with jtag_probes under tab-completion).

Hard rename, no aliases. Updates the state-dump emitter, help text,
example script and docs accordingly.
2026-05-23 16:33:23 +02:00
d82a1e0739 script: jtag_autoinit returns success, count via last_data_value
It returned the loaded-BSDL count, which the engine flagged as an error
code ("Command failed with code: 1") whenever a BSDL matched. Stash the
count in last_data_value and return JTAG_CORE_NO_ERROR instead.
2026-05-23 12:09:13 +02:00
cacbb9d00e doc: document Digilent backend and probe-open by index
CLAUDE.md/README/tutorial: optional BS_ENABLE_DIGILENT backend, why
SMT2 modules need libdjtg, and the new jtag_open_probe index. Mark
phases 2 and 2.5 done.
2026-05-23 12:07:26 +02:00