# 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`; the current installation is `/tools/efinix/efinity/2025.2`. - 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 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.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: ```systemverilog 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: ```text 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: ```text /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: ```bash 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: ```bash 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: ```text /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: ```bash 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: ```bash make FPGA_BOARD=ti375c529 fpga-debug-openocd ``` Load and run an application, then monitor UART, from another terminal: ```bash 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: ```text 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: ```bash 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: ```bash 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 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.