Files
gps/targets/fpga/ti375c529/flow/efinity/README.md
T
2026-08-13 01:47:09 +03:00

14 KiB

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 ti375c529 to the root FPGA board selection and build targets.
  • Added Efinity 2025.2 discovery through EFINITY_HOME. Common /tools, /opt, user-local, and repository-local installation paths are searched.
  • Added a generated Ti375 wrapper around VCU118RocketSystem so 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_clk enters 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 pins 1, 8, and 9 carry the MAX2771 control bus; pin 1 / G15 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.

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.sv
  • trck_src/NCO_carr.sv
  • acq_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.sv and hardware/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 the rtl recipe immediately after firtool

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 patched host binary in PATH is used. 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.

Apply the patch from a clean OpenOCD 0.12 source tree before configuring and building the host tool:

git apply --check /path/to/gps/targets/fpga/ti375c529/flow/debug/openocd-0.12-ti375-bscan.patch
git apply /path/to/gps/targets/fpga/ti375c529/flow/debug/openocd-0.12-ti375-bscan.patch

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
0x03FC0000 Persistent navigation context (two 64-KiB sectors)
0x03FE0000 Persistent ephemeris cache (two 64-KiB sectors)

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 region
  • out/flash/regions/app.hex: application region
  • out/flash/ti375c529_rocket.hex: combined image
  • out/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 $(EFINITY_HOME)/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 ns and 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 0x5a through DMI.
  • /tools/toolchain/bin/riscv64-unknown-elf-gdb loaded 40,880 bytes into DTIM.
  • hello_uart printed the expected Rocket/DTIM/UART messages at 460800 baud.
  • The persistent image passed with setup slack +0.094 ns and 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 CRC 0xed3bbdf5, copied 40,880 bytes into DTIM, and ran hello_uart without GDB.
  • A full board power cycle then configured Ti375 from Lower flash. Host OpenOCD found the RV64 hart through JTAG_USER1; its PC was at 0x8000017e <_exit> after hello_uart returned, and GDB compare-sections matched the complete ELF .text range 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.