Skip to content

Commit 67747be

Browse files
authored
Merge pull request #15 from lcapossio/feature/multi-ela-design
Add managed multi-core debug slots with dual-clock Arty validation
2 parents 617d174 + 3def084 commit 67747be

43 files changed

Lines changed: 3023 additions & 284 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ vivado/
1313
dfx_runtime.txt
1414

1515
# Build artifacts
16+
dist/
17+
packages/
1618
*.bit
1719
*.bin
1820
!host/fcapz/gui/assets/default_window_state_v6.bin
21+
!host/fcapz/gui/assets/default_window_state_v7.bin
1922

2023
# Simulation artifacts
2124
sim/*.vvp
@@ -28,3 +31,4 @@ Thumbs.db
2831
# Temp test outputs
2932
tests/_tmp/
3033
.codex_pytest_tmp*/
34+
.claude/

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2828
from the repo `VERSION` file before falling back to installed package
2929
metadata, so a stale wheel/editable install cannot mask the working-tree
3030
version.
31+
- **Debug manager:** Added a generic `"CM"` active-slot manager and Xilinx
32+
`fcapz_debug_multi_xilinx7` wrapper so multiple ELAs and an EIO can share
33+
one USER chain. The Arty reference now routes two ELA slots plus two EIO
34+
slots through USER1; EIO CLI/API calls can pass `instance=N` for managed
35+
slots.
36+
- **GUI:** ELA capture now pins itself to manager slot 0 when a USER1 core
37+
manager is present, and the EIO dock can attach to managed EIO slots such as
38+
Arty's USER1 slots 2 and 3.
3139

3240
### Fixed
3341

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -547,12 +547,18 @@ for details.
547547

548548
| Vendor | Primitive | User chains | ELA | EIO (needs 1) | EJTAG-AXI (needs 1) | EJTAG-UART (needs 1) |
549549
|--------|-----------|:-----------:|:---:|:---:|:---:|:---:|
550-
| **Xilinx** | BSCANE2 | 4 (USER1-4) | USER1 control + default burst; optional USER2 legacy burst | USER3 | USER4 | USER4 (shared) |
550+
| **Xilinx** | BSCANE2 | 4 (USER1-4) | USER1 control + default burst; optional USER2 legacy burst | USER3 standalone, or managed USER1 slot with `fcapz_debug_multi_xilinx7` | USER4 | USER4 (shared) |
551551
| **Intel** | sld_virtual_jtag | Unlimited | inst 0 by default; optional inst 1 | inst 2 | inst 3 | inst 5 |
552552
| **ECP5** | JTAGG | 2 (ER1+ER2) | ER1 by default; optional ER2 | `EIO_EN=1` on ER1 | *deferred to v2* | *deferred to v2* |
553553
| **Gowin** | GW_JTAG | One primitive; wrapper selects ER1 or ER2 | No burst | `EIO_EN=1` | *deferred to v2* | *deferred to v2* |
554554
| **PolarFire-family** | UJTAG | 2 (USER1+USER2) | USER1 control + USER2 burst | `EIO_EN=1` on USER1 | *deferred to v2* | *deferred to v2* |
555555

556+
Single-core ELA/EIO wrappers are available across the vendor rows above.
557+
Managed multi-core wrappers are Xilinx 7-series only in this revision
558+
(`fcapz_debug_multi_xilinx7`); the portable `fcapz_core_manager` is shared,
559+
but ECP5, Gowin, Intel, PolarFire, and UltraScale wrappers still need their
560+
own TAP-specific shells.
561+
556562
**Verified Xilinx 7-series IR codes** (xc7a100t, Arty A7):
557563
USER1=0x02, USER2=0x03, USER3=0x22, USER4=0x23.
558564

@@ -633,7 +639,7 @@ the JTAG TAP/register/readout plumbing, not only `fcapz_ela.v`.
633639
| 32b x 1024, dual comparator, `REL_COMPARE=0` | 2,472 | 2,099 | 1.0 | Wider samples mainly add BRAM/FFs |
634640
| `arty_a7_top` (placed, pre-`DEBUG_EN` always-on debug) | 3,244 | 4,562 | 3.5 | Historical baseline for the same validation reference |
635641
| `arty_a7_top` (placed, `DEBUG_EN=0`) | 2,318 | 3,168 | 3.5 | Bridge debug telemetry disabled; previous reference before the EJTAG-AXI FIFO trim |
636-
| **`arty_a7_top` (placed, trimmed EJTAG-AXI FIFOs, `DEBUG_EN=0`)** | **2,371** | **3,356** | **1.5** | Current reference: ELA with `INPUT_PIPE=1`, `DECIM_EN`, `EXT_TRIG_EN`, `TIMESTAMP_W=32`, `NUM_SEGMENTS=4` + EIO 8/8 + EJTAG-AXI + `axi4_test_slave`; bridge debug telemetry disabled; EJTAG-AXI command/response queues set to 16 entries |
642+
| **`arty_a7_top` (placed, trimmed EJTAG-AXI FIFOs, `DEBUG_EN=0`)** | **2,371** | **3,356** | **1.5** | Current reference: USER1 debug manager with 2x ELA (`INPUT_PIPE=1`, `DECIM_EN`, `EXT_TRIG_EN`, `TIMESTAMP_W=32`, `NUM_SEGMENTS=4`) + 2x EIO 8/8 + EJTAG-AXI + `axi4_test_slave`; bridge debug telemetry disabled; EJTAG-AXI command/response queues set to 16 entries |
637643

638644
Optional ELA parameters (`DECIM_EN`, `EXT_TRIG_EN`, `TIMESTAMP_W`,
639645
`NUM_SEGMENTS`, channel mux, etc.) add registers, comparators, and
@@ -688,14 +694,19 @@ GitHub Actions runs on every push and pull request to `main` or `master`:
688694
| `test-host` | `pytest tests/ -v --tb=short` with the default `not hw` marker filter, plus an explicit JTAG readback pipeline regression for burst and timestamp stabilization paths |
689695
| `lint-rtl` | `python sim/run_sim.py --lint-only` — shared `iverilog -Wall` elaboration for the core RTL, vendor wrappers, and simulation stubs |
690696
| `lint-rtl-verilator` | `python sim/run_verilator_lint.py --self-test` -- full-project Verilog RTL driver lint plus an intentional `MULTIDRIVEN` fixture proving the gate catches one reg driven by multiple always blocks |
691-
| `sim` | `python sim/run_sim.py` — runs the same `iverilog -Wall` lint pass, then the default RTL regression: ELA behavior, ELA focused regressions, ELA configuration matrix, burst readout, single-chain pipe readout, EIO, and channel mux testbenches |
697+
| `sim` | `python sim/run_sim.py` — runs the same `iverilog -Wall` lint pass, then the default RTL regression: ELA behavior, ELA focused regressions, ELA configuration matrix, burst readout, single-chain pipe readout, EIO, core manager, and channel mux testbenches |
692698

693699
Hardware integration tests run manually (require physical Arty A7-100T + hw_server).
694700
Optional **GUI + hardware** checks in `tests/test_gui_hw_capture.py` are
695701
documented in [CONTRIBUTING.md](CONTRIBUTING.md) (`FPGACAP_GUI_HW=1`, not run in CI).
696702
Those board-level checks now require every adjacent Arty counter sample to
697703
increment by +1 when decimation is disabled, so partial burst readback
698-
corruption is caught instead of hidden by a shorter valid prefix.
704+
corruption is caught instead of hidden by a shorter valid prefix. The managed
705+
Arty bitstream exercises two ELAs on the same USER chain with independent
706+
sample domains: ELA0 captures a 150 MHz counter and ELA1 captures a 130 MHz
707+
xored counter. In the GUI History panel, select captures from both ELAs and
708+
open/export them as one merged VCD so Surfer or GTKWave can display the two
709+
waveforms together.
699710

700711
[↑ Top](#readme-top)
701712

core.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "fpgacapZero",
33
"version": "0.1.0",
4+
"manifest_note": "Source manifest for FuseSoC-style and external build tooling; keep in sync when adding or removing RTL files.",
45
"description": "Open-source, vendor-agnostic FPGA debug cores: Embedded Logic Analyzer (ELA) for waveform capture and Embedded I/O (EIO) for runtime read/write of fabric signals, all over JTAG. Includes single-instantiation wrappers for Xilinx 7-series/UltraScale, Lattice ECP5, Intel/Altera, and Gowin.",
56
"author": "Leonardo Capossio - bard0 design - <hello@bard0.com>",
67
"license": "MIT",
@@ -47,6 +48,8 @@
4748
},
4849
"files": [
4950
"rtl/fcapz_ela.v",
51+
"rtl/fcapz_core_manager.v",
52+
"rtl/fcapz_debug_multi_xilinx7.v",
5053
"rtl/fcapz_eio.v",
5154
"rtl/jtag_burst_read.v",
5255
"rtl/jtag_reg_iface.v",

docs/01_overview.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,13 @@ chapter 17 for details.
112112

113113
## How the cores fit together
114114

115-
Each core uses a **separate JTAG USER chain** (USER1 through USER4
116-
on Xilinx 7-series / UltraScale, equivalent on other vendors), so
117-
multiple cores can coexist in the same bitstream and the host stack
118-
can talk to all of them in one xsdb / OpenOCD session.
115+
By default, each core uses a **separate JTAG USER chain** (USER1 through
116+
USER4 on Xilinx 7-series / UltraScale, equivalent on other vendors), so
117+
multiple cores can coexist in the same bitstream and the host stack can
118+
talk to all of them in one xsdb / OpenOCD session. Xilinx 7-series
119+
designs can also put multiple ELA and EIO slots behind one USER1 core
120+
manager (`fcapz_debug_multi_xilinx7`) when you want more debug endpoints
121+
than free USER chains.
119122

120123
| Core | Default chain | Default IR (7-series) | Default IR (UltraScale) |
121124
|------|---------------|-----------------------|-------------------------|
@@ -129,12 +132,13 @@ have to memorise either set: the Python transport ships named
129132
`IR_TABLE_XILINX7` and `IR_TABLE_XILINX_ULTRASCALE` (alias
130133
`IR_TABLE_US`) presets — see [chapter 14](14_transports.md).
131134

132-
ELA and EIO are completely independent. The two bridges (AXI and
133-
UART) can share USER4 by being instantiated at the same time only if
135+
ELA and EIO are independent cores, but they may be hosted either on
136+
separate chains or in managed slots on one chain. The two bridges (AXI
137+
and UART) can share USER4 by being instantiated at the same time only if
134138
you arbitrate them externally; in practice you pick one bridge per
135-
bitstream. The reference Arty A7 design includes ELA + EIO + AXI
136-
together, and a separate "uart loopback" bitstream includes ELA +
137-
EIO + UART.
139+
bitstream. The reference Arty A7 design includes a USER1 manager with
140+
2x ELA and 2x EIO plus AXI on USER4, and a separate "uart loopback"
141+
bitstream includes ELA + EIO + UART.
138142

139143
## The host stack
140144

docs/03_first_capture.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
## Step 1: build the reference bitstream
1717

1818
The reference design is at [`../examples/arty_a7/`](../examples/arty_a7/).
19-
It instantiates one ELA core, one EIO core, and one EJTAG-AXI bridge,
20-
all wired into a free-running 8-bit counter and a small AXI test slave
21-
so you have something to look at.
19+
It instantiates a USER1 debug manager with two ELA slots and two EIO
20+
slots, plus one EJTAG-AXI bridge. ELA0 captures a free-running 8-bit
21+
counter in a generated 150 MHz sample domain. ELA1 captures a separate
22+
130 MHz counter xored with `0xA5`. EIO0 drives the board LED/control
23+
test signals, and EIO1 gives a second managed I/O target for
24+
slot-selection checks. The AXI bridge is wired to a small AXI test slave
25+
so you have something to look at from each host path.
2226

2327
```bash
2428
cd /path/to/fpgacapZero
@@ -166,6 +170,9 @@ waveform preview.
166170

167171
In the **ELA** tab:
168172

173+
- Leave **ELA core** set to `core 0` for the plain counter. On the
174+
Arty reference bitstream, `core 1` is a second managed ELA in a 130 MHz
175+
sample domain capturing `counter_130 ^ 0xA5`.
169176
- Set **Pretrigger** to `8`.
170177
- Set **Posttrigger** to `16`.
171178
- Set **Trigger mode** to `value_match`.
@@ -189,6 +196,11 @@ happened before the event, not only after it.
189196

190197
From the GUI you can also save the capture as JSON / CSV / VCD, or open the
191198
VCD in an external viewer if GTKWave, Surfer, or WaveTrace is installed.
199+
To compare the two managed ELAs, capture once with **ELA core** `core 0`,
200+
capture again with `core 1`, Ctrl/Shift-select both rows in History, then
201+
click **Open selected in viewer**. The GUI writes one merged VCD with
202+
`fcapz.ela0.*` and `fcapz.ela1.*` scopes aligned at the trigger sample;
203+
Surfer and GTKWave can show both ELAs in the same waveform window.
192204

193205
## Step 5: repeat the same capture from the CLI
194206

@@ -384,13 +396,18 @@ values 4 apart, because of `decimation=3` (store every N+1 = 4th).
384396

385397
### d. EIO read/write (chapter 06)
386398

387-
The reference design wires the EIO core to a small register file you
388-
can poke at runtime:
399+
The reference design wires two managed EIO slots behind the USER1 core
400+
manager. Slot 2 is EIO0, the board-control EIO used by the GUI; slot 3
401+
is EIO1, an independent second EIO for selection/readback checks. Pass
402+
`--chain 1 --instance N` to select one:
389403

390404
```bash
391-
fcapz --backend hw_server --port 3121 --tap xc7a100t eio-read
392-
fcapz --backend hw_server --port 3121 --tap xc7a100t eio-write 0xA5
393-
fcapz --backend hw_server --port 3121 --tap xc7a100t eio-read
405+
fcapz --backend hw_server --port 3121 --tap xc7a100t \
406+
eio-read --chain 1 --instance 2
407+
fcapz --backend hw_server --port 3121 --tap xc7a100t \
408+
eio-write --chain 1 --instance 2 0xA5
409+
fcapz --backend hw_server --port 3121 --tap xc7a100t \
410+
eio-read --chain 1 --instance 3
394411
```
395412

396413
### e. AXI single read (chapter 07)

docs/04_rtl_integration.md

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -189,38 +189,37 @@ wrappers are available through `ela_rtl_sources()` for custom integration.
189189

190190
## Adding more cores in the same design
191191

192-
The reference Arty A7 design uses three cores in one bitstream:
192+
The reference Arty A7 design uses one USER1 debug manager plus AXI on USER4:
193193

194194
```verilog
195-
// 1. ELA on USER1 (control + burst data by default)
196-
fcapz_ela_xilinx7 #(
197-
.SAMPLE_W (8),
198-
.DEPTH (1024)
199-
) u_ela (
200-
.sample_clk (clk_100mhz),
201-
.sample_rst (rst),
202-
.probe_in (counter[7:0]),
203-
.trigger_in (1'b0),
204-
.trigger_out ()
205-
);
206-
207-
// 2. EIO on USER3
208-
fcapz_eio_xilinx7 #(
209-
.IN_W (8),
210-
.OUT_W (8)
211-
) u_eio (
212-
.probe_in (gpio_in),
213-
.probe_out (gpio_out)
195+
// 1. Managed USER1 debug chain: ELA0, ELA1, EIO0, EIO1.
196+
fcapz_debug_multi_xilinx7 #(
197+
.NUM_ELAS(2),
198+
.EIO_EN(1),
199+
.NUM_EIOS(2),
200+
.SAMPLE_W(8),
201+
.DEPTH(1024),
202+
.EIO_IN_W(8),
203+
.EIO_OUT_W(8)
204+
) u_debug (
205+
.ela_sample_clk({clk_130mhz, clk_150mhz}),
206+
.ela_sample_rst({rst_130mhz, rst_150mhz}),
207+
.ela_probe_in({counter_130 ^ 8'hA5, counter_150}),
208+
.ela_trigger_in(2'b00),
209+
.ela_trigger_out(),
210+
.ela_armed_out(),
211+
.eio_probe_in({eio1_in, eio0_in}),
212+
.eio_probe_out({eio1_out, eio0_out})
214213
);
215214
216-
// 3. EJTAG-AXI bridge on USER4
215+
// 2. EJTAG-AXI bridge on USER4
217216
fcapz_ejtagaxi_xilinx7 #(
218217
.ADDR_W (32),
219218
.DATA_W (32),
220219
.FIFO_DEPTH (16)
221220
) u_axi (
222-
.axi_clk (clk_100mhz),
223-
.axi_rst (rst),
221+
.axi_clk (clk_150mhz),
222+
.axi_rst (rst_150mhz),
224223
// ... 30 AXI signals connected to your AXI slave or interconnect ...
225224
);
226225
```
@@ -320,6 +319,7 @@ module fcapz_ela_xilinx7 #(
320319
| `CTRL_CHAIN` | int | 1..4 | BSCANE2 USER chain for the control register interface. |
321320
| `DATA_CHAIN` | int | 1..4 | BSCANE2 USER chain for the burst data readback. |
322321
| `EIO_EN` | bit | 0/1 | When `1`, the ELA wrapper also instantiates an EIO core and muxes it onto `CTRL_CHAIN` via an address decoder — ELA registers live at `0x0000..0x7FFF`, EIO registers at `0x8000..0xFFFF`. Lets you use both cores on a single USER chain when you want to conserve BSCAN primitives or share a chain for deployment reasons. The standalone `fcapz_eio_xilinx7` / `_xilinxus` wrappers cannot coexist with this — pick one. |
322+
| `NUM_EIOS` | int | 0..N | `fcapz_debug_multi_xilinx7` only. Number of managed EIO slots after the ELA slots. Defaults to `EIO_EN` for one-EIO compatibility. |
323323
| `EIO_IN_W` | int | 1..N | EIO input bus width when `EIO_EN=1`. |
324324
| `EIO_OUT_W` | int | 1..N | EIO output bus width when `EIO_EN=1`. |
325325

@@ -414,8 +414,9 @@ eio.write_outputs(0x1)
414414
- You cannot also instantiate a standalone `fcapz_eio_xilinx7` /
415415
`_xilinxus` elsewhere in the same design (two BSCANE2s on the same
416416
USER chain).
417-
- A future third core on the same chain (EJTAG-AXI/UART) would need a
418-
wider address mux or a hierarchical `fcapz_regbus_mux`.
417+
- For multiple ELAs plus EIO on one Xilinx 7-series chain, prefer
418+
`fcapz_debug_multi_xilinx7`; it uses the descriptor-capable `"CM"`
419+
active-slot manager instead of the older fixed `0x8000` EIO address window.
419420

420421
## EIO wrapper parameter reference
421422

0 commit comments

Comments
 (0)