Files
gps/targets/fpga/ti375c529/flow/efinity/README.md
T

328 lines
14 KiB
Markdown

# 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.