Implement generic MAX2771 raw Ethernet recorder

This commit is contained in:
onur
2026-08-01 01:15:47 +03:00
commit 79d07fdda7
57 changed files with 4810 additions and 0 deletions
+232
View File
@@ -0,0 +1,232 @@
# MAX2771 raw recorder — Efinix Titanium Ti375 C529 kit
This is a separate board target for the Efinix Titanium Ti375 C529 development
kit. It does not modify the Genesys 2 top-level, constraints, or Vivado flow.
The protocol-compatible capture FIFO, packetizer, CRC and raw Ethernet MAC are
the read-only common RTL in `../../rtl/common`; every Efinix-specific source is
contained in this directory and the source list is explicit in
`scripts/generate_project.py`.
No RISC-V/SoC block, CPU, AXI fabric, generated Ethernet MAC, IP stack, or
Efinix Ethernet IP is used. Only the Efinity periphery resources needed for
GPIO DDIO and PLL clocking are generated.
## Data path
```text
MAX2771 J13/PMOD0
clock -> A12/GPIOR_95_CLK12 global clock -> GPIO DDR input registers
data ---------------------> rising/falling sample selection
-> sample index + loss marker
-> handwritten 16,384 x 69 async FIFO
-> 2936-sample packetizer
-> handwritten Ethernet MAC/FCS
100 MHz oscillator -> Ethernet PLL -> 125 MHz / 125 MHz +90 degrees
-> GPIO DDIO RGMII -> RTL8211FDI -> RJ45
```
`CAPTURE_RISING=1` selects the GPIO `_HI` samples; `0` selects `_LO` samples.
`EARLIER_SAMPLE_HIGH_NIBBLE=1` produces the required byte order: earlier sample
in bits 7:4, later sample in bits 3:0.
## MAX2771 wiring
Use J13, identified as PMOD0 in the official kit guide. The PMOD header is
3.3 V. Do not reuse the Genesys 2 JA ball names on this board.
| RTL signal | J13 pin | Ti375 logical pin | C529 ball | Level |
|---|---:|---|---|---|
| RF clock input | 10 | GPIOR_95_CLK12 | A12 | 3.3 V |
| data bit 0 | 2 | GPIOR_89 | G16 | 3.3 V |
| data bit 1 | 3 | GPIOR_90 | F16 | 3.3 V |
| data bit 2 | 4 | GPIOR_91 | F17 | 3.3 V |
| data bit 3 | 7 | GPIOR_92 | G17 | 3.3 V |
| ground | 5 or 11 | — | — | 0 V |
| PMOD supply | 6 or 12 | — | — | 3.3 V |
The MAX2771 clock-to-data minimum/maximum values and cable/adapter skew are not
known. Interface Designer generates constraints for its internal GPIO register
paths; those numbers are not a substitute for MAX2771 external timing. Measure
or obtain the external values before claiming complete board-level RF input
timing closure. See `docs/hardware_assumptions.md`.
## Ethernet hardware
The board PHY is RTL8211FDI and the RGMII bus is 1.8 V. The board straps PHY
address 2, receive delay enabled and transmit delay disabled. Receive is not
implemented. The FPGA creates the required transmit-clock phase shift with a
125 MHz, +90-degree PLL output and sends TXD/TXCTL with the unshifted 125 MHz
clock. PHY reset remains low for 10 ms, followed by a 30 ms settle period.
The MDIO controller selects Realtek page `0xa43`, reads PHYSR register `0x1a`,
restores page 0, and only asserts `link_ready` for real-time link=1,
speed=1000 Mb/s and full duplex. A 100 Mb/s link deliberately transmits no
frames: the 130.944 Mb/s raw sample stream already exceeds a 100 Mb/s line
before Ethernet overhead.
## Clocks
| Function | Source | PLL | Outputs |
|---|---|---|---|
| Ethernet | board 100 MHz oscillator, U4 | PLL_BL0, EXT_CLK1 | CLK0 100 MHz dummy, CLK1 125 MHz, CLK2 125 MHz +90° |
| RF capture | MAX2771 clock, J13.10/A12 | direct GPIOR_95_CLK12 GCLK | nominal 32.736 MHz |
The 100 MHz dummy output satisfies the Titanium CLKOUT0 restriction. The
Ethernet PLL outputs and their physical reference resources are checked by the Efinity
Interface Designer API during every build. The direct RF GCLK period defaults
to 30.547 ns and can be overridden with `RF_CLOCK_PERIOD_NS` when the actual
MAX2771 clock differs.
## Raw Ethernet protocol
Frames use destination `ff:ff:ff:ff:ff:ff`, source `02:27:71:00:00:01`, and
EtherType `0x88b5`. A normal Ethernet payload is exactly 1500 bytes: the
32-byte `MX27` header followed by 1468 packed bytes/2936 samples. All metadata
is network byte order. The byte protocol is unchanged from
[`docs/raw_ethernet_protocol.md`](../../docs/raw_ethernet_protocol.md).
The FPGA emits preamble, SFD, Ethernet header, payload, FCS and 12-byte IFG.
Normal host NICs remove preamble/SFD/FCS before delivery to `AF_PACKET`.
## Efinity headless CLI build
“Non-project mode” is Vivado terminology and is not used here. This target has
the Efinity equivalent headless flow: scripts generate the periphery and a
temporary project XML, then invoke the supported command-line compile flow.
The installed path on this machine is:
```bash
export EFINITY_HOME=/tools/efinix/efinity/2026.1
cd targets/efinix_ti375_c529
make build
```
Outputs are under `build/outflow/`, including the pinout, interface timing,
synthesis, P&R, timing reports and `ti375_max2771_recorder.bit`. The script
fails on API/DRC/compile errors, absent required clocks, missing timing report,
negative timing slack, or missing bitstream.
The final A12/GPIOR_95 Efinity 2026.1 C4 build completed successfully on
1 August 2026. Post-route slack was +0.636 ns setup/+0.094 ns hold at 125 MHz
and +25.614 ns setup/+0.031 ns hold at 32.736 MHz. The generated CDC report says
`No Synchronizer warnings to report`. Placement used 36,253/362,880 XLRs
(9.99%), 138/2,688 memory blocks (5.13%), and no DSP blocks.
The kit guide revision describes a C4 timing grade and the local Efinity kit
example also uses C4. Confirm the marking on the actual FPGA. Override only the
timing model, not the base package device, when necessary:
```bash
EFINIX_TIMING_MODEL=I3 make build
```
## Programming
Connect the board USB/JTAG interface, power the fan as required by the kit, and
run:
```bash
export EFINITY_HOME=/tools/efinix/efinity/2026.1
make program
```
This calls `efx_run.py ... --flow program --pgm_opts mode=jtag` with the
generated `.bit` file. It does not alter the boot flash.
For an RF-independent physical Ethernet test, a separate diagnostic top-level
generates the repeating nibble sequence 0 through 15 and uses the production
packetizer, MAC, FCS, MDIO and RGMII blocks:
```bash
make selftest-build
make selftest-program
```
Restore the recorder afterwards with `make program`; JTAG configuration is
volatile. On the tested board/direct cable, a 3.034-second self-test capture
received 35,446 consecutive frames/104,069,456 samples with no protocol gap,
FPGA drop, socket drop, or pattern mismatch.
## Host capture
The raw protocol and host application are board-independent:
```bash
make -C ../../host
sudo ip link set dev enp3s0 up
sudo ip addr flush dev enp3s0
sudo ethtool enp3s0
sudo ethtool -K enp3s0 gro off lro off # unsupported options may be ignored
sudo sysctl -w net.core.rmem_max=268435456
sudo ../../host/max2771_raw_rx \
--interface enp3s0 \
--output max2771_capture.bin \
--gap-mode zero-fill
```
No IP address, ARP or firewall change is required. Prefer a direct cable or an
isolated switch because every frame is broadcast. For non-root operation:
```bash
sudo setcap cap_net_raw,cap_net_admin=eip ../../host/max2771_raw_rx
```
Use a dedicated Gigabit NIC and storage capable of sustained writes above
16.368 MB/s.
## Throughput
At 32.736 Msps, the source is 130.944 Mb/s (16.368 MB/s). Full frames occur at
approximately 11,149.864 frames/s. Including preamble/SFD, MAC header, 1500-byte
payload, FCS and IFG, each frame occupies 1538 byte-times, or 12,304 bits. Line
use is approximately 137.188 Mb/s, 13.719% of 1 Gb/s; raw line-rate margin is
about 7.29x.
The current packetizer first loads a frame buffer and then transmits it. At 125
MHz, its conservative three-clock read handshake loads 2936 samples in about
70.464 us; the frame plus IFG takes 12.304 us. This gives about 8.3% scheduler
margin over the required frame rate. During loading the FIFO drains faster than
the RF source; during frame transmission about 403 new samples arrive. In
steady state it reaches the 2936-sample launch threshold, finishes near 2709
samples, then refills. Link-down occupancy can instead reach the full 16,384
entries; all further input samples are counted as drops, never overwritten.
## LEDs
| LED | Meaning |
|---|---|
| LED9 | Ethernet PLL/reset system ready; may toggle only while Ethernet PLL is acquiring lock |
| LED10 | qualified 1000/full link ready |
| LED11 | RF sample activity |
| LED12 | FIFO has data; frame-count divider gives visible TX activity |
| LED13 | FIFO overflow/sample loss latched |
There are five verified user LEDs, so FIFO and transmit activity share LED12.
## Simulation
```bash
cd targets/efinix_ti375_c529
make sim
```
The target tests verify RGMII half-cycle/control mapping and RTL8211FDI PHYSR
link qualification/link-down counting. The script then runs the full shared
recorder regression: standard CRC vector, complete Ethernet bytes/FCS/IFG,
random async FIFO CDC, two-frame sample reconstruction, header/index/sequence,
reset behavior and overflow handling.
## Troubleshooting
- No Ethernet frames: verify `ethtool` reports 1000 Mb/s/full duplex and LED10
is on. A 100 Mb/s negotiation is intentionally rejected.
- LED13 on before link: capture cannot be backpressured; the FIFO fills while
link is unavailable, and the loss is correctly counted.
- Interface DRC failure: check that the base device is `Ti375C529` and the
board wiring/pins have not been changed.
- RF timing failure or corrupted nibbles: confirm 3.3 V levels, ground, capture
edge and the actual MAX2771 clock-to-data window with measurement.
- Efinity warns about `libstdc++.so`: this is emitted by the installed 2026.1
setup script on this host. Do not delete tool files automatically; follow the
vendor’s installation guidance if the tools fail to run.