Standalone FPGA AVB endpoint on a single XC7A50T. One bitstream now carries:
- gPTP (IEEE 802.1AS slave, ±30 ns servo lock to a Class-A master)
- AVTP (IEEE 1722) AVDECC (1722.1, Milan v1.2 compliant in Hive), MSRP
- AAF 8-channel listener + talker, CRF media-clock recovery with PI servo
- AES3 (S/PDIF/AES3id) digital I/O
- PCM5102A I2S DAC for monitoring
- USB UAC2 High-Speed device (
1209:eab1) — playback/capture for DAW - Gigabit RGMII to the AVB switch
VexRiscv runs all the protocol stacks bare-metal — no Linux on the device.
| Subsystem | State |
|---|---|
| gPTP | Locks ±30 ns to MOTU AVB Switch / Auvitran GM (last bench: 30 s avg |off|=7 ns) |
| AVDECC | Hive sees entity, full AEM tree, patchable, no spurious flicker |
| MSRP | Talker + Listener registrar; READY substate at CONNECT_RX |
| ACMP | Fast-path + slow-path (zero-stream-id) listener flows |
| AAF | 8 ch RX + TX at 48 kHz / 24 bit, with jitter buffer |
| CRF | Gateware extractor + firmware PI servo: max|d|≈0.8 µs, stays locked past 40 s |
| MCR | NCO PI-tuned by CRF servo, drives I2S BCK/LRCK (sample-locked DAC) |
| AES3 | TX wired to AAF RX ch 0+1 (loopback bench-verified) |
| I2S TX | Bit-clock derived from MCR NCO phase — sample-locked to talker |
| USB UAC2 | HS enumerate (1209:eab1) + playback robust; capture TBD |
| USB→AAF | Gateware AAF TX packetizer (aaf_packetizer.py) — USB FIFO → AVTP-AAF on the wire, CPU out of the per-sample path, paced by the MCR media clock (= CRF rate when cs=1+locked). Firmware drain kept as a fallback behind the enable CSR |
| Eth gigabit | eth_tx_clk 150-176 MHz robust (TX-only sys-datapath patch) |
Latest verified bitstream archived at
bitstreams/378311e-dirty_2026-05-30_1024_gateware-aaf-packetizer.bit
— covers all of the above on one bit (boots, gPTP locks to 7 ns, gateware
AAF packetizer present + idle until an AAF talker is connected). Sidecar
.info carries git log -1 + uncommitted-diff stat.
# Environment (LiteX/Migen + openXC7 toolchain)
export CHIPDB=/home/lisp/FPGA/demo-projects/chipdb
export PRJXRAY_DB_DIR=/home/lisp/openxc7/openxc7/opt/nextpnr-xilinx/external/prjxray-db
# Build firmware then gateware (one combined build, seed pinned to 4,
# floorplan OFF by default — see §8 for why)
cd /home/lisp/FPGA/avb-aes3
( cd firmware && make )
python3 avb_soc.py --build --firmware firmware/firmware.bin --seed 4
# Flash to SRAM (volatile — see §6.2 for SPI-flash recipe)
sudo /home/lisp/openocd/src/openocd \
-s /home/lisp/openocd/tcl \
-f /home/lisp/FPGA/Colorlight-FPGA-Projects/tools/ch347.cfg \
-c "init; pld load 0 build/colorlight_i9plus/gateware/colorlight_i9plus.bit; exit"
# Console (NOTE: 1 Mbaud, not the old 115200)
sudo picocom -b 1000000 /dev/ttyACM0
# Archive the .bit so the next build can't overwrite it
./bitstreams/archive.sh some-meaningful-label- Colorlight i9plus v6.1 (Xilinx XC7A50T-FGG484)
- Board pinout:
/home/lisp/FPGA/Colorlight-FPGA-Projects/colorlight_i9plus_v6.1.md - The cabled-up RJ45 jack on this specific board is U9 = PHY1 (MDIO address 1), so
LiteEth uses
eth_clocks/ethindex 1.
| CH347T pin | Signal | FPGA pin | Direction |
|---|---|---|---|
| TXD1 | UART | M3 | CH347 → FPGA |
| RXD1 | UART | R3 | FPGA → CH347 |
| TCK/TMS/TDI/TDO | JTAG | (header) | bidirectional |
Both UART and JTAG land on /dev/ttyACM0/the same CH347 USB. UART runs at
1 Mbaud + 64-byte HW FIFO so periodic prints (gPTP/SRP/AVDECC dumps)
never block the main loop.
| Signal | FPGA pin | SODIMM pin | Notes |
|---|---|---|---|
| CLK | T4 | 51 | MRCC pin (clock-capable, required) |
| DIR | T3 | 49 | |
| NXT | U2 | 57 | |
| STP | U3 | 59 | |
| RST | R2 | 41 | Active HIGH (USB3300 datasheet) |
| D0–D7 | V2 V3 W1 W2 Y1 AA1 AB1 Y2 | 61–75 | bidirectional bus |
ULPI wiring critical: each timing-critical signal twisted with its own GND,
never another signal — see feedback_ulpi_twisted_pair_wiring memory.
| DAC label | FPGA pin | SODIMM pin | Notes |
|---|---|---|---|
| VIN | 3V3 | — | 3.3 V power |
| GND | GND | — | Ground |
| SCK | GND | — | Tie LOW so the PCM5102A uses its internal PLL |
| BCK | U7 | 46 | 3.072 MHz bit clock |
| LCK | U6 | 48 | 48 kHz word select (LRCK) |
| DIN | U5 | 50 | Serial audio data |
| Signal | FPGA pin | SODIMM pin | External hardware |
|---|---|---|---|
| AES3 OUT | P5 | 42 | RS-422 driver → 110 Ω xfmr → XLR-3M |
| AES3 IN | T6 | 44 | XLR-3F → 110 Ω xfmr → RS-422 receiver |
export CHIPDB=/home/lisp/FPGA/demo-projects/chipdb
export PRJXRAY_DB_DIR=/home/lisp/openxc7/openxc7/opt/nextpnr-xilinx/external/prjxray-dbThese two env vars are mandatory for openXC7 nextpnr-xilinx. The Python tooling
(migen / litex / amaranth) is found automatically through
~/.local/lib/python3.11/site-packages/litex-tools.pth — no venv activation needed.
The bitstream embeds the firmware in its BRAM ROM, so the firmware must be compiled before the gateware build:
cd /home/lisp/FPGA/avb-aes3/firmware && make # rebuilds firmware.bin
cd .. # back to repo root
python3 avb_soc.py --build --firmware firmware/firmware.bin --seed 4If you --build without --firmware, the ROM bakes the LiteX BIOS only and
you get a litex> prompt instead of the [AVB-AES3] boot banner.
A clean build takes ~6-10 min on this box. Watch for:
Info: Max frequency for clock 'eth_tx_clk': ~150 MHz (PASS at 125)
Info: Max frequency for clock 'eth_rx_clk': ~140 MHz (PASS at 125)
2 warnings, 0 errors
--seed 4is pinned inavb_soc.pydefaults. The TX-only sys-datapath patch (LITEETH_PATCHES.md #3) madeeth_tx_clkrobust across seeds — the pin is just for build reproducibility, not because the design is on a timing edge.- The
--floorplanflag is OFF by default. It was previously the only way to recover gigabiteth_tx_clkwith the USB block present, but patch #3 superseded it and the floorplan was actively harming USB ULPI sampling (see §8). Only pass--floorplanif you're reproducing the pre-patch-#3 recipe.
After a build, before declaring it good:
ls -la build/colorlight_i9plus/gateware/colorlight_i9plus.bit # fresh
grep "Max frequency.*'eth_tx_clk'" build/.../colorlight_i9plus.log # > 125After flashing, the boot output on the 1 Mbaud serial console should show:
[gPTP] Initialized. MAC=02:00:00:00:00:42
[SRP] Initialized (MSRP)
[AVDECC] Entity ID=02:00:00:ff:fe:00:00:42
…and on the host side lsusb should list
1209:eab1 Generic N-Series AVB Switchover.
This project's original sin was losing a known-working bitstream
("fp_build5") when a later --build overwrote the same output path —
source was in git but yosys non-determinism meant rebuilds didn't reproduce.
bitstreams/archive.sh exists to make that class of loss impossible:
./bitstreams/archive.sh [label]Snapshots the current build/.../colorlight_i9plus.bit to
bitstreams/<short-sha>[-dirty]_YYYY-MM-DD_HHMM[_label].bit and writes a
sidecar .info with git log -1 + uncommitted-diff stat. Run it after every
verified-working build.
.bit and .info are gitignored — the script + ignore are tracked.
sudo /home/lisp/openocd/src/openocd \
-s /home/lisp/openocd/tcl \
-f /home/lisp/FPGA/Colorlight-FPGA-Projects/tools/ch347.cfg \
-c "init; pld load 0 build/colorlight_i9plus/gateware/colorlight_i9plus.bit; exit"The board does not have openFPGALoader available — only the local
openocd build above.
Two steps. The MX25L128 flash is write-protected from the factory; unlock once per board, then write.
# Step 1 — unlock (first time only per board)
sudo /home/lisp/openocd/src/openocd -s /home/lisp/openocd/tcl \
-f /home/lisp/FPGA/Colorlight-FPGA-Projects/tools/ch347.cfg \
-c "init; pld load 0 /home/lisp/FPGA/Colorlight-FPGA-Projects/tools/unlock_flash_xc7a50t.bit; exit"
# Step 2 — write + reboot
TOOLS=/home/lisp/FPGA/Colorlight-FPGA-Projects/tools
BIT=/home/lisp/FPGA/avb-aes3/build/colorlight_i9plus/gateware/colorlight_i9plus.bit
sudo /home/lisp/openocd/src/openocd -s /home/lisp/openocd/tcl -f $TOOLS/ch347.cfg -c "
set XC7_JSHUTDOWN 0x0d
set XC7_JPROGRAM 0x0b
set XC7_BYPASS 0x3f
init
pld load 0 $TOOLS/bscan_spi_xc7a50t.bit
reset halt
flash probe 0
flash protect 0 0 50 off
flash write_image erase $BIT 0x0 bin
irscan xc7.tap \$XC7_JSHUTDOWN
irscan xc7.tap \$XC7_JPROGRAM
runtest 60000
runtest 2000
irscan xc7.tap \$XC7_BYPASS
runtest 2000
reset
" -c exitsudo picocom -b 1000000 /dev/ttyACM0 # Ctrl-A Ctrl-X to exitIf /dev/ttyACM0 is a regular file (sometimes recreated wrong after kernel
oddities), restore the char device:
sudo rm /dev/ttyACM0
sudo mknod /dev/ttyACM0 c 166 0
sudo chgrp dialout /dev/ttyACM0 && sudo chmod 660 /dev/ttyACM0| Key | Action |
|---|---|
h |
Show command help |
s |
Status — gPTP, SRP, AVDECC, AVTP TX/RX counters |
e |
RX dispatcher stats (ptp / avtp / msrp / stream_mcast) |
m |
MCR/CRF state — locked, increment, |
a |
AAF channel state — RX bound stream / TX activity |
t |
Toggle AVB talker (also enables SRP advertise) |
T |
Force a fresh AVDECC TalkerAdvertise burst |
b |
Benchmark — 1-second main-loop iteration and event-rate window |
D |
DAC diagnostics — I2S sample count, underruns |
r |
Reboot CPU |
This is the engineering log of how the current build came to work, so the next round of changes doesn't undo years of debugging.
RGMII bring-up on the B50612D PHY needed a nibble-swap in the LiteEth
s7rgmii RX IDDR (o_Q1↔o_Q2) — without it the SFD 0xD5 decodes as
0x5D and every frame fails preamble. PHY power-down testing pinned the
cabled RJ45 to U9 / PHY1 (MDIO address 1). gPTP locks ±30 ns; the LiteEth TSU
needed a shift-add tree replacement for full_addend * 1e9 so nextpnr-xilinx
could route the multiplier without infering an unrouteable DSP cascade.
CRF parser + MCR PI NCO; AAF 8ch RX with per-channel jitter buffer; full
AVDECC AEM tree with the Milan-compliant MEDIA_CLOCK_SINK / AEM_SUPPORTED
capability bits. Many small interop fixes were needed before Hive would
patch us:
- The cd-bit in ADP/AECP/ACMP byte 0 must be
0xFA/0xFB/0xFC, not0x7A/…(feedback_avdecc_cd_bitmemory). - MSRP
AttrListLenmust include the inner EndMark — off-by-2 makes bridges drop everything but the first attribute. - The applicant must emit
MRPDU_NEW(0)for the first two cycles after attach, thenJoinIn(1)—JoinMt(3)forever makes the bridge bounceTalkerFailedback at the talker. - AECP
SET_STREAM_FORMATmust be permissive on the listener (accept any input format); strict on output. - The listener must declare
READYatsrp_listener_enabletime, notAskingFailed— Auvitran/MOTU otherwise refuse to stream. - AVTP RX dispatcher must drain all pending slots per call. One-shot drains lose 99 %+ of CRF/MSRP frames under load.
Started as a separate standalone Amaranth+LUNA project on the same board.
Many false leads — see project_luna_openxc7_status memory for the long
form. The keys that finally worked:
- USB_PHASE=0 (PLLE2_ADV no shift). VCO must be ≥ 800 MHz on the -1
Artix-7 part — MULT=16, CLKOUT0_DIVIDE=16, VCO=960 (
artix7-pll-vco-minimum). - SAMPLE_EDGE=off — feed the ultraembedded ULPI wrapper the raw ULPI input pins, no extra resampling layer.
- Startup reset pulse for the wrapper —
ResetSignal("usb")is never asserted, so the wrapper never kicked off its register-config sequence. Pulsei_ulpi_rst_ihigh for ~64 cycles at boot, then release. - PHY RESET pin is active-HIGH on the USB3300 (
usb3300-rst-polarity). - Twisted-pair wiring with each critical signal paired with its own
GND, never another signal (
ulpi-twisted-pair-wiring). interface.claimmust be asserted in custom handler branches or LUNA's request multiplexer routes every UAC2 class request to the fallback stall handler.
The Verilog wrapper from avb-usb-host (rtl/generated/usb_avb_subsystem.v +
the ultraembedded ULPI wrapper) drops into avb-aes3 as a Migen.Instance
with a TSTriple for the ULPI data bus. Elaborated cleanly first try.
But: with the full SoC, eth_tx_clk collapsed from 131 MHz (no USB) to
~112 MHz (with USB) — failing the 125 MHz gigabit RGMII target. A seed sweep
(1..30) gave 92-112 MHz across the board; no seed cleared 125. A
floorplan was added (floorplan_usb.py) confining the USB block to
X >= 78 (right half of the die) to keep the eth TX cluster's region clear.
With floorplan + seed sweep, seed 4 cleared 134 MHz.
This is where commit 675fb9e landed — Gigabit USB+AVB: TX-only sys-datapath fix + USB floorplan, seed 4. On the specific build verified
("fp_build5"), USB enumerated 1209:eab1 and AVB was patchable in Hive.
The fp_build5 .bit was overwritten by a subsequent --build. Source was
in git, but yosys synthesis is non-deterministic on this toolchain — rebuilds
landed different placements, most of which failed USB enumeration with
error -71 (HS device-not-accepting-address). AVB was unaffected (separate
clock domain, separate side of chip).
This kicked off a brutal multi-session debugging round chasing the wrong mechanism:
- The "fix" sitting in memory was to add
Misc("IOB=TRUE")toulpi_io(). But the working standalonetop.fasmhas 0 IFF cells — meaningAttrs(IOB="TRUE")doesn't actually pack input flops into IOBs on Amaranth/yosys/nextpnr-xilinx. The theory was wrong. - An IDELAY-based sweep (firmware-tunable
IDELAYE2taps on the ULPI inputs) was built but never validated — the host port disables after a fewerror -71retries, making it impossible to sweep the eye-tap in real time.
The floorplan was the bug. It pinned the USB block to X = 78..114
(right edge of the die), while the ULPI input pins are at X = 1 (left
edge). The wrapper's first-stage sampling FFs were being forced ~77 columns
of routing away from where they were sampling — guaranteed long propagation,
60 MHz ULPI setup time blown on most placements.
The standalone build, with no floorplan, lets the wrapper land naturally near the IO. That's why it just worked.
And critically: LITEETH_PATCHES.md #3 (the TX-only sys-datapath patch,
commit 7bff87c) had already made eth_tx_clk robust across seeds — no
floorplan needed for eth anymore. The floorplan kept being applied because
it was on by default in avb_soc.py.
The fix is one line of intent: flip --no-floorplan (opt-out) to
--floorplan (opt-in), default OFF. eth_tx still clears 150 MHz comfortably,
USB cells land near the ULPI pins, both work on one bitstream, robust
across rebuilds.
Same day: bitstreams/archive.sh was added so we can't lose another
working build to an overwrite.
After USB+AVB coexistence shipped (9bdae1b), the rest of the day landed
the CRF gateware path, the MCR-driven I2S, and a paradoxical floorplan
reversal:
a493962 — Gateware CRF timestamp extractor. Stage 2b for CRF: a
Migen CRFTimestampExtractor (208 lines, observe-only mac.core.source
snoop) matches the bound CRF stream_id, latches the TSU timestamp at
frame arrival, and FIFOs (avtp_ts, local_ts) pairs for firmware to
drain via mcr_pump_hw. Two consequences:
- Jitter: firmware-RX-latency stops dominating.
max|d|drops from ~18 µs (firmware timestamps) to ~0.8 µs (hardware timestamps). - 40 s drop fixed: before, the firmware was just busy enough parsing every CRF PDU at 8 kHz that the 1 s MSRP refresh occasionally slipped past the bridge's 4× LeaveAll window (~39 s) and the bridge pruned our Listener registration. With gateware capture taking the CRF load, refreshes stay on time and the stream survives indefinitely.
The original f44a9b8 attempt at this looked like a code regression
("entity vanishes from Hive, main loop dead") but was actually the
stale-firmware build-cache trap: make link-failed because csr.h didn't
yet have the new crf_ts_* accessors, the gateware build silently
embedded the previous firmware.bin against the new CSR layout, and every
CSR access landed at the wrong address. Caught this session with a clean
make clean && make after the cherry-pick. See
[[feedback_firmware_stale_csr_addr_trap]] memory.
d7a60dd — MCR_STALE_THRESHOLD_MS 200 → 1000. Even with locked
CRF, Hive's CRF patch line was "lighting up" intermittently — turned
out to be unsolicited STREAM_INPUT MEDIA_LOCKED/UNLOCKED AECP pushes
fired by track_clock_lock when the MCR stale watchdog briefly flipped
servo_locked on packet-loss bursts > 200 ms. Widening to 1 s absorbs
those without any audible drift (sub-ppm NCO tuning ⇒ sub-µs/s wander
across 1 s).
7199108 — I2S TX bit-clock from MCR NCO (task #60). Replaced the
audio-domain Verilog i2s_tx (running off the free PLL @ 12.288 MHz)
with a sys-domain Migen MCRI2STx that derives BCK = nco.phase[25]
(64 × fs), LRCK = nco.phase[31] (fs), and shifts data on each BCK
falling edge — all phase-bits straight off the NCO that the PI servo
is actively tuning. The PCM5102A DAC bit-clock now moves with the AVB
talker by construction. Cycle-to-cycle jitter on BCK is ~20 ns; the
PCM5102A's internal CRPLL averages that out.
df4fb3e — floorplan ON again, but in the OPPOSITE direction. The
gateware CRF extractor + MCRI2STx changed the overall placement pressure
enough that nextpnr's free placement scattered USB cells too far from
the ULPI pins again — back to error-71 / full-speed-only enumeration.
The fix: re-enable floorplan_usb.py, but pin USB to X≤30, Y=10-70
(toward the ULPI pins at X=1, Y=23-49) instead of the original X≥78
(away from them). One commit reversed the entire 9bdae1b intent for
a structurally different reason — a reminder that floorplans aren't
inherently good or bad: the direction matters, and it depends on
what's competing for placement.
df4fb3e floorplan: USB-near-ULPI (X<=30, Y=10-70), ON by default
7199108 i2s: sample-lock TX bit-clock to MCR NCO (task #60)
d7a60dd mcr: widen stale-watchdog 200→1000 ms — kills Hive cosmetic flicker
a493962 Gateware CRF timestamp extractor — kills firmware-RX-latency jitter and 39 s drop
3dc6abf docs: refresh README with current status + USB UAC2 + the full journey
9bdae1b floorplan off by default: it was breaking USB ULPI sampling
336fce4 tooling: bitstream archive script with git-log sidecar
675fb9e Gigabit USB+AVB: TX-only sys-datapath fix + USB floorplan, seed 4
All on master; five working .bit files archived in bitstreams/.
- #67 USB ↔ AVB bridge (DAW → USB → AAF on the wire). The first
attempt in P3.3 added a gateware AsyncFIFO (cd_usb → sys); even with
the floorplan pulling its cells into the X≤30 region, any added
cd_usb-side logic re-broke 60 MHz ULPI HS chirp. Lesson: the
proximity floorplan that works for the USB wrapper is too marginal
to share with new cd_usb consumers. The fix was to put the FIFO
inside
usb_avb_subsystem.v(regenerated from the avb-usb-host Amaranth source with an internal cd_usb→sys FIFO + a simple sys-side read interface), so the CDC lives in the wrapper's already-validated placement domain. - #67 USB→AAF, firmware then gateware. With the wrapper exposing a
sys-domain sample handshake, the first working bridge was firmware
(
usb_aaf_drainreads the FIFO over a 2-op CSR, builds the AAF PDU inaaf.c). It runs at the right ~48 k frames/s but the CPU is in the per-sample path (Wishbone reads) → ~2 % loss at sustained max stress. The scalable path — now implemented — is a gateware AAF TX packetizer (aaf_packetizer.py): the USB FIFO feeds an 8-channel assembler → elastic block FIFO → a builder FSM that emits the full AVTP-AAF frame on its own MAC TX stream, stamping presentation_time straight from the TSU (DSP-free ×1e9). A frame-atomicTXFrameArbitermuxes it ontomac.core.sinkalongside the firmware control plane (which keeps priority); the gPTP TX-timestamp latch is gated to firmware frames so 8000 AAF frames/s can't clobber the Sync/Pdelay egress stamp. Pacing is the MCRsample_strobe— so when firmware servos the NCO to CRF (cs=1 + locked) the AAF stream rate is the recovered media clock, identical to how the I2S DAC already locks. Firmware (aaf_gw_set) pushes the binding + flipsenableon AVDECC talker connect and stops the software drain/TX; the firmware path stays as a fallback whenenable=0. Verified 2026-05-30: boots, gPTP locks to 7 ns, packetizer idle until a talker is connected. - #58 MCR cs=0 → gPTP-tuned NCO — when no CRF is patched, tune the NCO from gPTP rate so the DAC stays sample-locked to the GM.
- Scaling (when needed): move CPU RAM from BRAM to the on-board SDRAM via LiteDRAM to free ~190 KB BRAM for audio FIFOs.
avb-aes3/
├── avb_soc.py # LiteX SoC top-level (Python/Migen)
├── crf_extractor.py # Gateware CRF (avtp_ts, local_ts) extractor
├── avtp_extractor.py # Stage 2a/b AVTP (AAF) per-sample RX extractor
├── aaf_packetizer.py # Gateware AAF TX packetizer + TXFrameArbiter (USB→AVB, CPU-free, MCR-paced)
├── floorplan_usb.py # nextpnr --pre-place hook (ON by default, --no-floorplan to skip)
├── LITEETH_PATCHES.md # The 3 in-tree LiteEth patches (#3 is THE fix)
├── BENCHMARK_BASELINE.md # Stage-0 firmware-on-audio baseline (2026-05-21)
├── bitstreams/
│ ├── archive.sh # Snapshot tool — run after every working build
│ └── .gitignore # (*.bit and *.info kept locally, not in git)
├── rtl/
│ ├── aes3_rx.v / aes3_tx.v # AES3 RX/TX (biphase-mark + DPLL)
│ ├── i2s_tx.v # I2S transmitter (PCM5102A)
│ └── generated/
│ └── usb_avb_subsystem.v # USB UAC2 sink, generated by avb-usb-host
├── firmware/ # Bare-metal C running on VexRiscv
│ ├── main.c # Entry, RX dispatch, UART CLI
│ ├── gptp.[ch] # IEEE 802.1AS gPTP slave + PI servo
│ ├── avtp.[ch] # IEEE 1722 AVTP
│ ├── srp.[ch] # IEEE 802.1Q SRP/MRP
│ ├── avdecc.[ch] # IEEE 1722.1 AVDECC (ADP + ACMP + AECP + MVU)
│ ├── mcr.[ch] # Media-clock recovery (CRF → NCO PI servo)
│ ├── aaf.[ch] # AAF 8ch RX with jitter buffer + 8ch TX
│ └── aes3.[ch] # AES3 ↔ AVTP bridge
└── build/ # Generated; gitignored
LiteEth lives at /home/lisp/litex/liteeth/, branch
colorlight-i9plus-b50612d-fixes — pair with this repo's HEAD.
- avb-usb-host (
/home/lisp/FPGA/avb-usb-host) — the standalone Amaranth/LUNA USB UAC2 device. Itsrtl/generated/usb_avb_subsystem.vis what we instance here. - Colorlight-FPGA-Projects (
/home/lisp/FPGA/Colorlight-FPGA-Projects) — i9plus board pinout, ch347.cfg, unlock_flash bitstream. - openXC7 (
/home/lisp/openxc7/) — yosys + nextpnr-xilinx + prjxray-db used for synthesis.