Ti375C529 Efinity flow notes
This directory contains the Efinity project for the Ti375C529 development kit. The port keeps the same RV64 Rocket/GPS subsystem used by the Genesys2 and VCU118 targets. Only the FPGA shell, clocks, pin constraints, programming flow, and JTAG transport are vendor-specific. The hardened RV32 subsystem in the Ti375 device is not used.
What was added
- Added
ti375c529to the root FPGA board selection and build targets. - Added Efinity 2025.2 discovery through
EFINITY_HOME; the current installation is/tools/efinix/efinity/2025.2. - Added a generated Ti375 wrapper around
VCU118RocketSystemso the existing Rocket, DTIM, UART, GPS acquisition, tracking, PPS, and SPI logic is reused. - Added the Efinity XML project, interface script, SDC constraints, PLLs, pin assignments, and JTAG programming target.
- Added FTDI UART operation at 460800 baud. On the current host it enumerates as
/dev/ttyUSB2. - Added host OpenOCD/GDB debug through the Ti375 hard TAP and
JTAG_USER1. - Added Efinity-specific generated-RTL rewrites for Rocket's store-data path and the GPS acquisition/tracking lookup tables that Efinity 2025.2 mis-elaborates.
Clock structure
The Rocket fabric and GPS processing clocks have separate sources:
- The on-board 100 MHz oscillator drives the fabric PLL and the 100 MHz Rocket system clock.
- AX2771
rf_clkenters through the dedicated global-clock input at J13 pin 10 / package pin A12 (GPIOR_95_CLK12). It is the raw AFE sample clock and feeds the GPS PLL chain. J13 pin 1 / G15 is not used for the RF clock because it is a PLL input, not a dedicated fabric GCLK input. - The GPS PLL chain generates 100 MHz acquisition and 125 MHz tracking clocks.
- Acquisition and tracking reset release is gated by the relevant PLL lock signals.
The four AX2771 data bits use J13 pins 2, 3, 4, and 7, corresponding to package pins G16, F16, F17, and G17. The AX2771 clock must be jumpered to J13 pin 10; do not connect it to the former J13 pin 1 location.
Efinity synthesis adaptation
This adaptation does not modify Rocket's native/upstream Chisel sources or
the shared GPS SystemVerilog. The normal Chisel and firtool stages run first
and emit SystemVerilog into hardware/generated/ti375c529-soc-verilog/.
Immediately afterward,
targets/fpga/ti375c529/tools/prepare_efinity_rtl.py patches the generated
Ti375 Rocket.sv and writes Efinity-only GPS replacements under
targets/fpga/ti375c529/out/build/generated/gps_efinity/. The Ti375 source-list
recipe substitutes those generated copies for exactly these three shared GPS
files:
acq_src/acq_nco_carr.svtrck_src/NCO_carr.svacq_src/twiddle_ram.sv
All other GPS RTL remains shared. Efinity consumes the replacements; Genesys2
and VCU118 continue to consume the original GPS files. Deleting either
generated directory and running make FPGA_BOARD=ti375c529 fpga-rtl repeats
the same deterministic generation and substitution.
Rocket store-data path
The CIRCT-generated Rocket.sv contains this block-local packed temporary for
store-data byte-lane replication:
automatic logic [3:0][63:0] _GEN_24 = ...;
mem_reg_rs2 <= _GEN_24[ex_reg_mem_size];
Vivado synthesizes this construct correctly. Efinity 2025.2 reports _GEN_24
as VDB-1002: net does not have a driver, leaving Rocket store data at zero.
This initially looked like a Debug Module failure because abstract GPR/CSR
reads execute a hart instruction that stores the result into Debug Module data
registers. DMI was working, but OpenOCD received zeros and reported the
misleading result misa=0.
../../tools/prepare_efinity_rtl.py replaces only that generated expression
with an equivalent explicit case for byte, half-word, word, and double-word
stores. The script runs automatically after firtool; generated RTL is not
edited by hand. After the rewrite, the Rocket VDB-1002 warning disappears
from the current synthesis run. ti375c529_rocket.warn.log is reused by
Efinity and can contain entries from older runs, so the generated source and
the final warning block should be checked together.
The same Efinity limitation also affected the four-entry packed atomic-operation
LUT emitted into generated TLAtomicAutomata_pbus.sv and
TLAtomicAutomata_cbus.sv. Those _GEN_4/_GEN_13 temporaries select the
TileLink atomic ALU operation; they are unrelated to GPS acquisition but could
break software atomics. The post-generation script rewrites both to an
equivalent four-way case. Questa confirmed all four index mappings, and their
VDB-1002 diagnostics disappear from the current single-run map.out.
The exact paths are:
- Generated failing/fixed source:
hardware/generated/ti375c529-soc-verilog/Rocket.sv - Generated TileLink atomic sources:
hardware/generated/ti375c529-soc-verilog/TLAtomicAutomata_pbus.svandhardware/generated/ti375c529-soc-verilog/TLAtomicAutomata_cbus.sv - Efinity synthesis warning log:
targets/fpga/ti375c529/flow/efinity/outflow/ti375c529_rocket.warn.log - Deterministic repair:
targets/fpga/ti375c529/tools/prepare_efinity_rtl.py - Invocation point:
targets/fpga/ti375c529/Makefile, in thertlrecipe immediately afterfirtool
GPS lookup-table paths
The original acquisition carrier NCO is
hardware/external/gps/rtl/acq_src/acq_nco_carr.sv. It initializes the
frequency and pull-in arrays with whole-package-array assignments and builds
its sine table in an initial loop using $sin. Efinity 2025.2 reports the
loop variable as never assigned and the three arrays as undriven. Stand-alone
post-map comparison confirmed that this is functional, not cosmetic: the
unpatched Efinity netlist produced incorrect sine/cosine samples in all 18,040
tested production-parameter cycles.
The generated Ti375 copy replaces the frequency, pull-in, sine, and cosine
reads with constant functions generated from definitions.sv. Questa compared
41 frequency bins, 11 pull-in bins, and 40 cycles per combination: all 18,040
RTL samples and all 18,040 Efinity post-map samples matched the original RTL.
The current full-project synthesis no longer reports the acquisition arrays or
its $sin loop as undriven.
The separate tracking carrier NCO is
hardware/external/gps/rtl/trck_src/NCO_carr.sv. Its
sinwav_lut = TRCK_SINWAV_LUT package-array initializer triggers the same
Efinity VDB-1002 behavior. The Ti375 copy uses an exact 128-entry constant
function generated from TRCK_SINWAV_LUT. Questa matched every table entry at
RTL and then matched 5,000 Efinity post-map cycles while visiting all 128
addresses.
The acquisition FFT twiddle source is
hardware/external/gps/rtl/acq_src/twiddle_ram.sv. Its whole-array
COS_ROM_ARRAY and SIN_ROM_ARRAY assignments are converted only for Ti375 to
two generated, exactly 15-bit-wide $readmemb files. Efinity infers 30
EFX_RAM10 blocks with embedded INIT data. Efinity still emits its generic
sin_rom/cos_rom does not have a driver elaboration warning for this recognized
ROM form, but this one is demonstrably benign: Questa matched all 8,192 RTL
addresses and all 8,192 addresses in the Efinity post-map RAM netlist. Check
the current ti375c529_rocket.map.out for a single run; the .warn.log and
.info.log files retain records from older builds and can otherwise make fixed
source paths look current.
JTAG and OpenOCD
Ti375 and the Xilinx boards expose the same inner Rocket DTM protocol, but the outer FPGA TAP is different:
| Item | Xilinx flow | Ti375 flow |
|---|---|---|
| Outer TAP IR width | Xilinx-specific | 5 bits |
| User instruction | legacy USER4 handling | JTAG_USER1 = 0x08 |
| Inner DTM IR width | 5 bits | 5 bits |
| DTMCS / DMI IR | 0x10 / 0x11 |
0x10 / 0x11 |
The host OpenOCD must therefore implement:
riscv set_bscan_tunnel_ir 8
A fixed OpenOCD pathname is not required. The target Makefile evaluates
command -v openocd, so the first compatible binary in PATH is used. On the
current machine this resolves to:
/home/onur/.local/bin/openocd
That location is only the installation choice for the patched host OpenOCD; it is not part of the hardware flow. Another compatible build can be selected without changing files:
make FPGA_BOARD=ti375c529 fpga-debug-openocd OPENOCD=/path/to/openocd
The original /usr/local/bin/openocd 0.12 build assumes the Xilinx USER4
instruction and asserts that the outer TAP IR is at least six bits. It cannot
drive the Ti375 5-bit TAP unchanged. ../debug/openocd-0.12-ti375-bscan.patch
adds the configurable outer instruction to OpenOCD 0.12. The OpenOCD shipped
with Efinity is not required by this flow.
The selected paths can be checked with:
make FPGA_BOARD=ti375c529 fpga-config
command -v openocd
strings "$(command -v openocd)" | grep set_bscan_tunnel_ir
The line Disabling abstract command reads/writes from CSRs is informational,
not a failure. OpenOCD falls back to Rocket's Program Buffer for CSR access.
GDB selection
Software is still compiled with the medany toolchain. That toolchain does not contain GDB, so the debug target deliberately selects:
/tools/toolchain/bin/riscv64-unknown-elf-gdb
This can also be overridden with GDB=/path/to/riscv64-unknown-elf-gdb.
Build, program, and debug
From the repository root:
make FPGA_BOARD=ti375c529 fpga-config
make FPGA_BOARD=ti375c529 fpga-rtl
make FPGA_BOARD=ti375c529 fpga-bitstream
make FPGA_BOARD=ti375c529 fpga-program
Start the host OpenOCD server in one terminal:
make FPGA_BOARD=ti375c529 fpga-debug-openocd
Load and run an application, then monitor UART, from another terminal:
make FPGA_BOARD=ti375c529 fpga-debug-load APP=hello_uart
make FPGA_BOARD=ti375c529 fpga-uart
Persistent Lower-flash boot
The kit has Lower and Upper 512-Mbit SPI NOR devices. This port intentionally
uses one MCS-like combined layout in the Lower ISSI IS25WP512 because Efinity's
command-line jtag_bridge mode directly addresses that flash:
| Offset | Image |
|---|---|
0x00000000 |
Compressed, active-x1 FPGA configuration |
0x00800000 |
Rocket boot header followed by the application payload |
The application offset is 64-KiB-sector aligned and leaves roughly 3.2 MB
between the current 5,190,596-byte configuration and the firmware region.
spi_low_power_mode=off is embedded in the configuration so the Lower flash
stays available when Rocket enters user mode.
Rocket's existing MMIO SPI controller is connected to the configuration pins after configuration completes:
spi_sck/spi_csn/spi_dq_* -> A7/G6/H4/H5/G5/B7 -> Lower flash U47
Generate the configuration, per-application, and combined raw Efinity HEX files:
make FPGA_BOARD=ti375c529 fpga-bitstream
make FPGA_BOARD=ti375c529 fpga-mcs APP=hello_uart
Outputs are:
out/bitstream/ti375c529_rocket.hex: configuration regionout/flash/regions/app.hex: application regionout/flash/ti375c529_rocket.hex: combined imageout/flash/ti375c529_rocket.json: offsets, sizes, and SHA-256 manifest
Program only the application, only the configuration, or the combined image:
make FPGA_BOARD=ti375c529 fpga-flash-app APP=hello_uart
make FPGA_BOARD=ti375c529 fpga-flash-bitstream
make FPGA_BOARD=ti375c529 fpga-flash-mcs APP=hello_uart
The Makefile first loads Efinity's device-matched bridge
/tools/efinix/efinity/2025.2/pgm/fli/titanium/u006A0A79.bit, then uses
jtag_bridge programming with on-chip verification. A fixed Efinity install
path is not required; FLASH_LOADER is derived from EFINITY_HOME and can be
overridden.
After bridge programming, the FPGA still contains the bridge design. Power
cycle the kit for a true active-flash boot, or use fpga-program to load the
normal design immediately and test the BootROM/application flash path.
On-board verification
The completed flow was checked on the connected Ti375C529 kit with AX2771 attached:
- Efinity interface, map, PNR, and programming-image stages passed.
- Final patched timing passed with WNS
+0.101 nsand WHS+0.021 ns. - The Ti375 JTAG IDCODE was read as
0x006a0a79. - OpenOCD found one RV64 hart and read
misa=0x800000000080112d. - A raw Program Buffer GPR/store test returned
0x5athrough DMI. /tools/toolchain/bin/riscv64-unknown-elf-gdbloaded 40,880 bytes into DTIM.hello_uartprinted the expected Rocket/DTIM/UART messages at 460800 baud.- The persistent image passed with setup slack
+0.094 nsand hold slack+0.021 ns. - The Lower flash was detected as ISSI IS25WP512, JEDEC
0x9d701a. - The combined 8,429,552-byte HEX completed erase, write, readback, and verify.
- BootROM read the payload at
0x00800040, verified CRC0xed3bbdf5, copied 40,880 bytes into DTIM, and ranhello_uartwithout GDB. - A full board power cycle then configured Ti375 from Lower flash. Host OpenOCD
found the RV64 hart through
JTAG_USER1; its PC was at0x8000017e <_exit>afterhello_uartreturned, and GDBcompare-sectionsmatched the complete ELF.textrange in DTIM. This confirms cold boot of both hardware and software without a volatile JTAG/GDB load.
Relevant files
ti375c529_rocket.xml: Efinity project definition.ti375c529_rocket.isf: generated/copy of the checked-in interface script.../../../../../hardware/fpga/ti375c529/constraints/: source interface and SDC constraints.../../tools/gen_ti375c529_wrapper.py: Efinix board-shell generator.../../tools/prepare_efinity_rtl.py: deterministic Rocket and GPS Efinity-only RTL generation/rewrite.../../tools/make_flash_hex.py: validated Efinity raw-HEX image builder.../debug/openocd_ti375c529_bscan.cfg: Ti375 hard-TAP/OpenOCD configuration.../debug/openocd-0.12-ti375-bscan.patch: host OpenOCD 0.12 compatibility patch.