Skip to content

Repository files navigation

AES67

A full AES67 Audio-over-IP implementation on a single FPGA. A Cyclone 10LP handles both the data plane (Ethernet MAC, PTPv2, RTP audio) and the control plane (LiteX RISC-V SoC running Zephyr RTOS). Some code was LLM generated, but human-checked and debugged.

For transparency, this is primarily a learning project. I had no FPGA experience before and only basic embedded experience (ESP32 + temperature sensor level).

System Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│ FPGA (Cyclone 10LP) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────────┐ │
│ │ Ethernet │ │ PTPv2 │ │ Wallclock │ │ Audio TX/RX │ │
│ │ MAC (YOL) │◄──┤ Controller │──►│ 48b:32b │──►│ RTP Packets │ │
│ │ + Timestamp │ │ + Servo PI │ │ + Media Clk │ │ + I2S I/O │ │
│ └──────┬──────┘ └─────────────┘ └──────┬──────┘ └───────────────┘ │
│ │ │ │
│ │ ┌─────────────────────────────┴──────────────────┐ │
│ │ │ Wishbone Bus / LiteX CSR Interface │ │
│ │ │ - Register-mapped config │ │
│ │ │ - ETH TX/RX packet buffers (dual-port RAM) │ │
│ └─────┴────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ┌────────────────────────────────┼───────────────────────────────────┐ │
│ │ LiteX VexRiscv SoC (80 MHz) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Zephyr RTOS │ │ HyperRAM │ │ SPI Flash │ │ │
│ │ │ (Firmware) │ │ (16 MB RAM) │ │ (Boot+FW) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ PTP BMC │ │ SAP/SDP │ │ Webserver │ │ │
│ │ │ Algorithm │ │ Announce │ │ Config UI │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Si5351A │ │ SSD1306 │ │ │
│ │ │ PLL Ctrl │ │ Display │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────────┘

The external STM32H753ZI MCU has been replaced by a LiteX-generated RISC-V softcore (VexRiscv) running directly on the FPGA. The SoC boots from external SPI flash via a boot stub that copies the BIOS into HyperRAM, then loads Zephyr firmware. Communication with the FPGA data plane uses LiteX CSR registers over the Wishbone bus instead of the previous FMC memory-mapped interface.

FPGA Architecture (Data Plane)

The FPGA handles all time-critical audio processing. Key modules in FPGA/:

Ethernet

ModuleFileDescription
Ethernet MACFPGA_Ethernet/Fork of YOL MAC with SOF timestamp output
Timestampethernet_timestamp.vhdLatches 48b:32b wallclock at SOF delimiter

PTP (IEEE 1588)

ModuleFileDescription
Controllerptp/ptpv2_controller.vhdState machine for Sync, Follow_Up, Announce, Delay_Resp
Parserptp/ptpv2_parser.vhdExtracts timestamps & computes offset/path delay
Servoptp/ptpv2_servo.vhdPI controller for clock discipline (PPB correction)
Servo (Median)ptp/ptpv2_servo_median.vhdVariant with median filtering
Senderptp/ptpv2_sender.vhdConstructs PTP packets

Clock & Timing

ModuleFileDescription
Wallclockwallclock.vhdPTP-disciplined 48b seconds + 32b nanoseconds
NCOwallclock.vhdDirect audio clock synthesis (BCLK, LRCK)
Media Clockwallclock.vhdRTP timestamp counter derived from wallclock
PPB Meterclock_ppb_meter.vhdMeasures PPB offset for external PLL correction

Audio

ModuleFileDescription
TX Routeraudio_tx/tx_router.vhdMulti-stream config RAM, sample aggregation
TX Transmitteraudio_tx/tx_transmitter.vhdRTP packet construction with SSRC
TX Sample Bufferaudio_tx/tx_sample_buffer.vhdRing buffer for outgoing samples
TDM8 Inputaudio_tx/tdm8_in.vhd8-channel TDM input mux
RX Ringbufferaudio_rx/rx_ringbuffer.vhdStream demux, playout buffer
I2S InputI2S_IN.vhd48kHz/24bit I2S deserializer
I2S Outputaudio_rx/i2s_out.vhdI2S serializer to DAC
TDM8 Outputaudio_rx/tdm8_out.vhd8-channel TDM output mux

Data Flow

 ┌───────────────────────────────────────────────────────────┐
I2S IN │ TX Path │
──────►│ I2S_IN → tx_sample_buffer → tx_router → tx_transmitter │───► RTP out
│ ↑ │
│ config from SoC │
└───────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────┐
RTP IN │ RX Path │
──────►│ UDP parser → rx_ringbuffer (stream demux) → i2s_out │───► I2S OUT
│ ↑ │
│ stream_ram config │
└───────────────────────────────────────────────────────────┘

LiteX SoC

The control plane runs on a LiteX-generated VexRiscv RISC-V softcore, generated by litex_soc/generate.py.

SoC Resources

ResourceDetails
CPUVexRiscv RISC-V @ 80 MHz (PLL from 50 MHz input)
RAMIS66WVH16M8ALL HyperRAM, 16 MB @ 0x20000000
FlashW25Q64 SPI flash @ 0x30000000 (BIOS + firmware)
CSRPeripheral registers @ 0xf0000000
I2C0SSD1306 display + Si5351A PLL
I2C1AD/DA card control
SPI0SD card
UART0Console

Boot Flow

  1. FPGA configures from internal flash, bringing up both data plane and SoC
  2. Boot stub at SPI flash reset vector (0x30000000) copies LiteX BIOS to HyperRAM (0x207F0000)
  3. Sets HyperRAM controller latency (6 CK for power-on default)
  4. BIOS loads Zephyr firmware image (.fbi format with CRC-32 header) from flash
  5. Zephyr boots, initializes drivers, starts DHCP and application threads

Firmware Architecture (Control Plane)

Zephyr RTOS runs on the LiteX VexRiscv SoC and handles all non-realtime tasks. Source in soc_firmware/app/:

Core Modules

ModuleFileDescription
Mainsrc/main.cInit, DHCP, network setup
PTP BMCsrc/ptp_bmc.cIEEE 1588 Best Leader Clock algorithm on 224.0.1.129:320
SAP/SDPsrc/sap_sdp.cSession announcement (239.255.255.255:9875), foreign stream discovery
SDP Utilssrc/aes67_sdp_utils.cSDP parsing/formatting, PTP clock ID formatting
RTSPsrc/rtsp.cRAVENNA RTSP server/client (stream subscription, session control)
mDNS/DNS-SDsrc/mdns_sd.cmDNS responder + DNS-SD service advertisement (RFC 6762/6763)
Webserversrc/webserver.cREST API + gzipped static web UI
Configsrc/aes67_config.cCentralized runtime configuration with defaults
Config JSONsrc/config_json.cJSON serialization/parsing (shared by SD & flash storage)
SD Configsrc/sd_config.cSD card config persistence (FAT, crash-safe A/B slots)
Flash Configsrc/flash_config.cSPI flash config storage (8KB slots, CRC-32 header)
FW Updatesrc/fw_update.cHTTP + shell firmware update, FBI format verification
Card Managersrc/card_manager.cI2C board detection & runtime I/O card selection
UI Displaysrc/ui_display.cSSD1306 OLED status rendering
FPGA Regssrc/fpga_regs.cHigh-level register write helpers (via FPGA HAL)
FPGA Pollsrc/fpga_poll.cStatus polling (PTP lock, link state)
PLL Controlsrc/pll_ctrl.cSi5351A PPB correction from FPGA measurements

Drivers

DriverPathDescription
FPGA HALdrivers/fpga_hal/Backend-agnostic hardware abstraction (LiteX CSR or FMC)
LiteX Ethernetdrivers/eth_litex/Zephyr network interface via LiteX CSR + Wishbone packet buffers
Si5351Adrivers/si5351a/I2C clock generator with PPB correction
SPI Flashdrivers/spi_flash/LiteSPI master for firmware updates & config storage
Displaydrivers/display_ctrl/LED/button/7-segment UART control
MI Carddrivers/mi_card/8-channel ADC preamp control (I2C)
LO Carddrivers/lo_card/Line output DA/ADC control (I2C)
IO Carddrivers/io_card/Flexible I/O card control (I2C)

FPGA HAL

Application code accesses FPGA registers through fpga_hal.h, which abstracts the underlying transport:

  • LiteX backend (fpga_hal_litex.c): Uses LiteX CSR registers. FPGA is always ready (integrated SoC).
  • FMC backend (fpga_hal_fmc.c): Legacy STM32H7 FMC memory-mapped access. Retained for backward compatibility.

Backend selection via Kconfig: CONFIG_FPGA_HAL_LITEX=y (default) or CONFIG_FPGA_HAL_FMC=y.

CSR Register Map (SoC ↔ FPGA)

AddressR/WDescription
0x00-0x02WETH TX length + control
0x10-0x20WETH TX frame data
0x20-0x22RETH RX length + status
0x30-0x40RETH RX frame data
0x40WMAC address (6 bytes)
0x41WIP address (4 bytes)
0x50R/WFlags: PLL, reset, PTP mode
0x51REthernet link status
0x52-0x54RPTP metrics (path delay, offset, PPB)
0x55WPTP config (leader identity, intervals)
0x57WAudio destination IP:port
0x58WTX stream config (20 bytes/stream)
0x59WRX stream config (18 bytes/stream)

Full register map: see config_ram_address_map.md

Build Instructions

LiteX SoC Generation

cd litex_soc
make # Generates SoC HDL + device tree + CSR headers

Firmware (Zephyr)

cd soc_firmware/app
source ../.venv/bin/activate # Activate Python venv for west
west build -b litex_vexriscv -p # Clean build

The build produces a .fbi flash image (Zephyr binary with length + CRC-32 header) for loading via LiteX BIOS.

FPGA

Open FPGA/FPGA.qpf in Intel Quartus Prime 25.1. Target device: 10CL025YU256I7G.

Current Status

Working

  • Ethernet RX + TX via LiteX CSR interface

  • Network config (MAC, DHCP IP)

  • PTPv2 Leader and Follower mode with BMC

  • Wallclock discipline and media clock derivation

  • Si5351A driver with PPB correction

  • Audio TX/RX paths (48kHz/24bit I2S, TDM8)

  • RTP packet generation and parsing

  • SAP/SDP announcements with foreign stream discovery

  • Webserver with REST API + gzipped web UI

  • Persistent configuration (SD card FAT filesystem with crash-safe A/B slots, SPI flash fallback)

  • HTTP + shell-based firmware updates (FBI format with CRC-32 verification)

  • Internal audio routing matrix

  • LiteX SoC boot from SPI flash via HyperRAM

  • Runtime detection of multiple I/O card types (MI, LO, IO)

  • SSD1306 OLED status display

Todo

  • Further tune PI controller (currently ±30ns jitter when locked)
  • FPGA resource optimization (PTP servo uses ~1600 LUTs)
  • Phase jump handling
  • Fix RAVENNA RTSP server/client for stream subscription
  • Fix mDNS responder + DNS-SD service advertisement

Technical Details

PTP Clock Discipline

The FPGA implements a PI controller in ptpv2_servo.vhd:

  • Filters offset measurements
  • Outputs frequency correction in PPB
  • Lock detection with hysteresis (500ns lock / 5µs unlock threshold)
  • Message interval awareness (scales gains for different sync rates)

Media Clock Generation

wallclock.vhd generates reference clocks using an NCO for PLL discipline:

  • NCO outputs (BCLK, LRCK) are used to measure phase error against external PLL
  • clock_ppb_meter.vhd compares NCO edges vs Si5351A edges → PPB correction
  • Si5351A (external I2C PLL) provides the actual low-jitter audio clocks
  • Media clock counter: (seconds × 48000 + sample_in_second) for RTP timestamps

The NCO has ±8ns jitter (1 sys_clk period), which is fine for measurement but not for direct I2S use.

License

See LICENSE.md

About

AES67 FPGA Implementation

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages