{"id":49336247,"url":"https://github.com/datanoisetv/jtag-idf-component","last_synced_at":"2026-04-27T01:02:00.299Z","repository":{"id":350519277,"uuid":"1207177657","full_name":"DatanoiseTV/jtag-idf-component","owner":"DatanoiseTV","description":"An ESP-IDF v6.0 component for JTAG for example for communicating / flashing XMOS XU2 and XU3 series multicore processors or FPGAs.","archived":false,"fork":false,"pushed_at":"2026-04-10T21:19:50.000Z","size":187,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-10T22:20:37.634Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DatanoiseTV.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-10T16:56:16.000Z","updated_at":"2026-04-10T21:19:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/DatanoiseTV/jtag-idf-component","commit_stats":null,"previous_names":["datanoisetv/xmos-jtag-idf-component","datanoisetv/jtag-idf-component"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/DatanoiseTV/jtag-idf-component","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Fjtag-idf-component","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Fjtag-idf-component/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Fjtag-idf-component/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Fjtag-idf-component/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DatanoiseTV","download_url":"https://codeload.github.com/DatanoiseTV/jtag-idf-component/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DatanoiseTV%2Fjtag-idf-component/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32318417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"ssl_error","status_checked_at":"2026-04-26T23:26:25.802Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-27T01:01:59.486Z","updated_at":"2026-04-27T01:02:00.266Z","avatar_url":"https://github.com/DatanoiseTV.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jtag-idf-component\n\nA general-purpose JTAG programmer component for ESP-IDF. Scan chains, program FPGAs, flash microcontrollers, play SVF files -- all from an ESP32 with a browser UI.\n\n\u003e Built for **ESP-IDF v6.0**. Tested on **ESP32-S3** (WiFi) and **ESP32-P4** (Ethernet + DMA-accelerated JTAG).\n\n---\n\n\u003cimg width=\"766\" height=\"579\" alt=\"Screenshot 2026-04-10 at 22 47 39\" src=\"https://github.com/user-attachments/assets/7fd63ede-c594-4aef-853a-fe4208c1809c\" /\u003e\n\n---\n\n## At a Glance\n\n| | |\n|---|---|\n| **Chain scan** | Auto-detect every device on the JTAG chain -- XMOS, Lattice, Xilinx, Espressif, ARM DAP and more |\n| **SVF player** | Play Serial Vector Format files to program any JTAG-capable device *(coming soon)* |\n| **XMOS support** | Full xCORE programming: load `.xe` to RAM, program SPI flash, debug register access |\n| **iCE40 support** | Program Lattice iCE40 FPGAs via SPI (CRAM and flash) *(coming soon)* |\n| **Boundary scan** | Capture all I/O pin states, auto-detect BSR length, live refresh from the browser |\n| **Two backends** | GPIO bit-bang on any ESP32 (1-5 MHz) **or** PARLIO DMA on ESP32-P4 (up to 40 MHz) |\n| **Web UI** | Visual chain diagram, firmware inspector, one-click flash -- no tools to install |\n\n## Supported Targets\n\n### XMOS (fully implemented)\n\n| Family | Chips | IDCODE | Tiles |\n|---|---|---|---|\n| **xCORE.ai** (XS3) | XU316 | `0x00006633` | 2 |\n| **xCORE-200** (XS2) | XU208, XU216 | `0x00005633` | 1--2 |\n| **XS1** (legacy) | XS1-G1, G4, SU | `0x00002633` `0x00104731` `0x00003633` | 1--4 |\n\n- Load firmware to RAM via JTAG boot (`.xe` or raw ELF)\n- Program SPI flash (direct or via JTAG-loaded stub)\n- Debug register access (PSWITCH/SSWITCH)\n- XE file parser verified against real multi-tile satellite firmware\n\n### FPGAs \u0026 Other Devices (chain scan + SVF)\n\nThe chain scanner recognises **Lattice ECP5/iCE40**, **Xilinx 7-Series**, **Espressif SoCs**, and **ARM CoreSight DAPs** by IDCODE. Any JTAG device can be driven via SVF file playback.\n\n| Device | Programming Method | Status |\n|---|---|---|\n| Lattice iCE40UP5K | SPI slave (CRAM/flash) | *planned* |\n| Lattice ECP5 | JTAG (SVF) | *via SVF player* |\n| Xilinx 7-Series | JTAG (SVF) | *via SVF player* |\n| Any JTAG device | SVF file playback | *planned* |\n\n\u003e **1.8 V I/O** -- XMOS JTAG signals are 1.8 V. Most FPGAs support configurable I/O banks. Use a level shifter between the ESP32 (3.3 V) and 1.8 V targets. On the ESP32-P4-NANO the `LDO_VO4` header pin supplies 1.8 V for the shifter's low-voltage rail.\n\n---\n\n## Use Cases\n\n**Production \u0026 Manufacturing**\n- Flash firmware on the assembly line without a PC -- just an ESP32, a browser, and a JTAG cable\n- Boundary scan for board-level continuity testing before functional test\n- Chain scan to verify correct device population and orientation\n\n**CI / CD \u0026 Automated Testing**\n- Drive the ESP32 flasher from a test script over HTTP (`curl -X POST /api/upload ...`)\n- Load nightly builds to RAM, run tests, repeat -- no flash wear, instant boot\n- Gate firmware releases on automated JTAG connectivity checks\n\n**Field Updates \u0026 OTA**\n- ESP32 pulls new firmware over WiFi/Ethernet, then JTAG-boots the target -- true over-the-air updates for devices with no native network stack\n- Dual-bank strategy: load to RAM for validation, then commit to SPI flash only if self-test passes\n\n**Development \u0026 Debugging**\n- Rapid edit-compile-load cycle without an XTAG adapter or expensive JTAG probe\n- Inspect I/O pin states in real time via boundary scan from any browser\n- Identify unknown or mislabelled parts via IDCODE + chain scan\n\n**Multi-Device Systems**\n- Single ESP32 manages the JTAG chain of an entire board (XMOS + FPGA + MCU)\n- Chain visualization shows every device -- useful for bring-up of complex PCBs\n\n---\n\n## Getting Started\n\n### 1. Add the component\n\n```sh\ncd your-project/components\ngit clone https://github.com/DatanoiseTV/jtag-idf-component.git jtag_prog\n```\n\n### 2. Configure\n\n```sh\nidf.py menuconfig   # -\u003e JTAG Programmer\n```\n\n| Option | Default | |\n|---|---|---|\n| `XMOS_JTAG_BACKEND` | GPIO | GPIO bit-bang **or** PARLIO DMA (ESP32-P4) |\n| `XMOS_JTAG_TCK_FREQ_KHZ` | 1 000 / 10 000 | Clock frequency |\n| `XMOS_JTAG_PARLIO_DMA_BUF_SIZE` | 4 096 | DMA buffer (PARLIO only) |\n\n### 3. Use the API\n\n```c\n#include \"xmos_jtag.h\"\n\n/* Initialise */\nxmos_jtag_pins_t pins = {\n    .tck = GPIO_NUM_47, .tms = GPIO_NUM_48,\n    .tdi = GPIO_NUM_46, .tdo = GPIO_NUM_45,\n    .trst_n = GPIO_NUM_53, .srst_n = GPIO_NUM_54,\n};\nxmos_jtag_handle_t jtag;\nESP_ERROR_CHECK(xmos_jtag_init(\u0026pins, \u0026jtag));\n\n/* Scan the chain -- works with any JTAG device */\njtag_chain_t chain;\nxmos_jtag_scan_chain(jtag, \u0026chain);\nfor (int i = 0; i \u003c chain.num_devices; i++)\n    printf(\"[%d] %s  0x%08lx\\n\", i, chain.devices[i].name,\n           (unsigned long)chain.devices[i].idcode);\n\n/* Identify XMOS device specifically */\nxmos_chip_info_t info;\nxmos_jtag_identify(jtag, \u0026info);\n\n/* Load XMOS firmware */\nextern const uint8_t fw[] asm(\"_binary_firmware_xe_start\");\nextern const uint8_t fw_end[] asm(\"_binary_firmware_xe_end\");\nxmos_jtag_load_xe(jtag, fw, fw_end - fw, true);\n```\n\n### Program an iCE40 FPGA\n\n```c\n#include \"jtag_ice40.h\"\n\n/* SPI + control pins for the iCE40 */\nice40_pins_t ice_pins = {\n    .spi_cs   = GPIO_NUM_20,   /* directly to iCE40 SPI_SS */\n    .spi_clk  = GPIO_NUM_23,   /* SPI clock */\n    .spi_mosi = GPIO_NUM_5,    /* data to iCE40 */\n    .spi_miso = GPIO_NUM_4,    /* data from iCE40 (or GPIO_NUM_NC) */\n    .creset   = GPIO_NUM_21,   /* CRESET_B -- hold low to reset FPGA */\n    .cdone    = GPIO_NUM_22,   /* CDONE -- goes high when configured */\n};\n\n/* Embed the .bin bitstream (output of icepack) */\nextern const uint8_t bitstream[] asm(\"_binary_top_bin_start\");\nextern const uint8_t bitstream_end[] asm(\"_binary_top_bin_end\");\nsize_t bitstream_len = bitstream_end - bitstream;\n\n/*\n * Load to CRAM -- volatile, bitstream lost on power cycle.\n * Fastest option for development (no flash erase/write).\n *\n * ice40_program_cram(pins, data, length, timeout_ms)\n *   timeout_ms: how long to wait for CDONE to go high (3000 = 3 sec)\n *               set to 0 to skip the CDONE check\n */\nESP_ERROR_CHECK(ice40_program_cram(\u0026ice_pins, bitstream, bitstream_len, 3000));\n\n/*\n * Write to SPI flash -- persistent, FPGA boots from flash on power-up.\n *\n * ice40_program_flash(pins, data, length, offset)\n *   offset: flash start address (usually 0)\n */\nESP_ERROR_CHECK(ice40_program_flash(\u0026ice_pins, bitstream, bitstream_len, 0));\n\n/*\n * Reset the FPGA (toggle CRESET, wait for CDONE).\n *\n * ice40_reset(pins, timeout_ms)\n *   timeout_ms: max wait for CDONE (0 to skip)\n */\nESP_ERROR_CHECK(ice40_reset(\u0026ice_pins, 3000));\n```\n\n### Play an SVF file\n\nSVF (Serial Vector Format) is a standard ASCII format for JTAG operations.\nTools like Lattice Diamond, Xilinx Vivado, and openFPGALoader can export `.svf` files.\nThe player streams through the file and executes each command via the JTAG transport.\n\n```c\n#include \"jtag_svf.h\"\n\n/* SVF data -- from embedded binary, SPIFFS, HTTP download, PSRAM, etc. */\nextern const char svf_data[] asm(\"_binary_program_svf_start\");\nextern const char svf_data_end[] asm(\"_binary_program_svf_end\");\n\n/* Optional progress callback -- called every ~100 commands */\nvoid on_progress(size_t bytes_done, size_t bytes_total,\n                 size_t commands_done, void *user_ctx) {\n    int pct = (int)(bytes_done * 100 / bytes_total);\n    printf(\"SVF: %d%% (%zu commands)\\n\", pct, commands_done);\n}\n\nsvf_config_t cfg = {\n    .progress_cb      = on_progress,   /* NULL to disable */\n    .user_ctx         = NULL,          /* passed to callback */\n    .stop_on_mismatch = false,         /* true = abort if TDO != expected */\n};\nsvf_result_t result;\n\n/*\n * svf_play(jtag_handle, svf_text, length, config, result)\n *   jtag_handle: from xmos_jtag_init() -- works with any JTAG device\n *   svf_text:    raw SVF file content (ASCII)\n *   length:      file size in bytes\n *   config:      playback options (NULL for defaults)\n *   result:      output stats (NULL to ignore)\n */\nESP_ERROR_CHECK(svf_play(\n    jtag,\n    svf_data, svf_data_end - svf_data,\n    \u0026cfg, \u0026result\n));\n\nprintf(\"SVF complete: %zu commands executed, %zu TDO mismatches\\n\",\n       result.commands_executed, result.tdo_mismatches);\n```\n\n---\n\n## Web Flasher Example\n\nA ready-to-flash example with a browser UI lives in `example/`.\n\n```sh\ncd example\n\n# ESP32-S3 -- WiFi AP\nidf.py set-target esp32s3 \u0026\u0026 idf.py build flash monitor\n# Connect to \"XMOS-Flasher\" (pw: xmosjtag), open http://192.168.4.1\n\n# ESP32-P4-NANO -- Ethernet (IP101GRI)\nidf.py set-target esp32p4 \u0026\u0026 idf.py build flash monitor\n# Plug in Ethernet, check serial log for DHCP address\n```\n\n**What the UI does:**\n\n| Section | |\n|---|---|\n| **Device** | One-click identify -- IDCODE, family, tiles, revision |\n| **JTAG Chain** | Visual diagram: `TDI -\u003e [XU316] -\u003e [ECP5] -\u003e TDO` |\n| **Boundary Scan** | Live pin-state capture with hex + bit view, auto-refresh |\n| **Firmware** | Drag-and-drop `.xe` / `.bin` / `.svf`, shows file details |\n| **Program** | Select target (XMOS / iCE40 / SVF) and method (RAM / Flash / CRAM), one-click go |\n\n---\n\n## Wiring\n\n```\nESP32              Target JTAG\n──────             ───────────\nGPIO  (TCK)   ──\u003e  TCK\nGPIO  (TMS)   ──\u003e  TMS\nGPIO  (TDI)   ──\u003e  TDI         Use a level shifter if\nGPIO  (TDO)   \u003c──  TDO         target I/O != 3.3 V\nGPIO  (TRST)  ──\u003e  TRST_N      (active low, optional)\nGPIO  (SRST)  ──\u003e  RST_N       (open-drain, optional)\nGND           ──\u003e  GND\n```\n\n### Waveshare ESP32-P4-NANO\n\n\u003cimg src=\"docs/jtag_pinout.svg\" alt=\"ESP32-P4-NANO Pinout\" /\u003e\n\nJTAG and SPI use separate headers so both can be wired simultaneously.\n\n**JTAG -- Right Header** (rows 7-10)\n\n| Signal | GPIO | Position |\n|---|---|---|\n| TCK | 47 | Row 8 inner |\n| TMS | 48 | Row 8 outer |\n| TDI | 46 | Row 9 inner |\n| TDO | 45 | Row 10 inner |\n| TRST | 53 | Row 7 outer |\n| SRST | 54 | Row 7 inner |\n\n**SPI / iCE40 -- Left Header** (rows 4, 6-8)\n\n| Signal | GPIO | Position |\n|---|---|---|\n| SPI_CLK | 23 | Row 4 outer |\n| SPI_MOSI | 5 | Row 6 outer |\n| SPI_MISO | 4 | Row 6 inner |\n| SPI_CS | 20 | Row 7 outer |\n| ICE40_CRESET | 21 | Row 8 outer |\n| ICE40_CDONE | 22 | Row 8 inner |\n\nEthernet (IP101GRI over RMII) handles networking on internal GPIOs. The **LDO_VO4** pin (Right Row 1, outer) provides **1.8 V** for a JTAG level shifter if the target uses 1.8 V I/O.\n\n---\n\n## Architecture\n\n```\ncomponents/xmos_jtag/\n include/\n   xmos_jtag.h            XMOS API (chain scan, identify, bscan, load, flash)\n   xmos_xe.h              XE / ELF parser\n   jtag_svf.h             SVF player API\n   jtag_ice40.h           iCE40 SPI programmer API\n src/\n   jtag_transport.h        Backend vtable (shift_ir, shift_dr, reset, idle)\n   jtag_gpio.c             GPIO bit-bang   -- any ESP32\n   jtag_parlio.c           PARLIO + DMA    -- ESP32-P4\n   svf_player.c            SVF file parser and executor\n   ice40.c                 iCE40 CRAM + SPI flash programmer\n   xmos_regs.h             XMOS TAP, MUX, PSWITCH, debug register map\n   xmos_jtag.c             XMOS protocol layer + known-device database\n   xmos_xe.c               XE container + ELF32 segment parser\n```\n\n### JTAG Backends\n\n**GPIO bit-bang** -- portable `gpio_set_level` / `gpio_get_level`. Runs on every ESP32 variant at 1--5 MHz TCK.\n\n**PARLIO DMA** (ESP32-P4) -- the Parallel IO peripheral clocks out TMS + TDI on two data lines while a second DMA channel captures TDO, all in hardware:\n\n- TX `data_width = 2`, one byte = 4 JTAG cycles\n- RX `data_width = 1`, clocked from TCK via GPIO matrix loopback\n- Up to **40 MHz TCK** from the 160 MHz PLL\n\n### Protocol Stack\n\nThe component is layered so device-specific logic sits on top of a generic JTAG transport:\n\n```\n ┌─────────────┐  ┌──────────────┐  ┌─────────────┐\n │  XMOS xCORE │  │  SVF Player  │  │ iCE40 (SPI) │   Device-specific\n │  (MUX, DBG) │  │  (generic)   │  │             │\n └──────┬──────┘  └──────┬───────┘  └──────┬──────┘\n        │                │                  │\n ┌──────┴────────────────┴──────────────────┘\n │          Generic JTAG Transport\n │    (shift_ir, shift_dr, reset, idle)\n ├─────────────────┬─────────────────┐\n │  GPIO bit-bang  │   PARLIO DMA    │               Hardware backends\n └─────────────────┴─────────────────┘\n```\n\n### XMOS Protocol\n\n1. **Top-level TAP** -- 4-bit IR (IEEE 1149.1). IDCODE = `0x1`, SET_TEST_MODE = `0x8`.\n2. **MUX** (IR `0x4`) -- opens the internal chain: OTP + xCORE + CHIP + BSCAN = 20-bit IR.\n3. **Register access** -- xCORE TAP IR = `(reg \u003c\u003c 2) | rw`. DR = 32/33 bits.\n4. **Debug mode** -- enter via `PSWITCH_DBG_INT`, memory R/W through scratch-register mailbox.\n5. **JTAG boot** -- SET_TEST_MODE bit 29, upload code, set PC, resume.\n\n### XE File Format\n\nVerified against `tool_axe` reference parser **and** real multi-tile satellite firmware:\n\n- 12-byte sector header with uint64 length + padding descriptor\n- Sector types: ELF `0x02`, BINARY `0x01`, GOTO `0x05`, CALL `0x06`, XN `0x08`\n- Per-tile ELF32 with PT_LOAD segments; raw ELF accepted too\n\n---\n\n## Tests\n\n\n\u003cimg width=\"1728\" height=\"1030\" alt=\"Logic analyzer capture\" src=\"https://github.com/user-attachments/assets/e37209d1-0885-47a6-9ec6-7d385134b64d\" /\u003e\n\n**56 host-side tests** -- run on your dev machine, no hardware needed:\n\n```sh\ncd test \u0026\u0026 make test\n```\n\nCovers XE parsing (synthetic + real `.xe` files), ELF edge cases, JTAG chain-IR encoding (all 256 registers verified to fit 20 bits), MUX constants, PARLIO bit packing, TAP state-machine simulation, and structural validation of real firmware images.\n\n---\n\n## Known Limitations\n\n- **XMOS debug commands** and PSWITCH register addresses were reverse-engineered from `sc_jtag` and forum posts -- may need tuning on untested silicon.\n- **SVF player** and **iCE40 SPI programmer** are planned but not yet implemented.\n- **XS3** IDCODE `0x00006633` confirmed from datasheet but end-to-end protocol not tested on hardware yet.\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatanoisetv%2Fjtag-idf-component","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatanoisetv%2Fjtag-idf-component","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatanoisetv%2Fjtag-idf-component/lists"}