Skip to content

enjoy-digital/litex_m2sdr

Repository files navigation

                        __   _ __      _  __    __  ______  _______  ___
                       / /  (_) /____ | |/_/___/  |/  /_  |/ __/ _ \/ _ \
                      / /__/ / __/ -_)>  </___/ /|_/ / __/_\ \/ // / , _/
                     /____/_/\__/\__/_/|_|   /_/  /_/____/___/____/_/|_|
                              LiteX based M2 SDR FPGA board.
                           Copyright (c) 2024-2026 Enjoy-Digital.

License Ask DeepWiki Buy Hardware

[> TL;DR

  • What? LiteX‑based M.2 2280 SDR board featuring a Xilinx Artix‑7 XC7A200T FPGA and an ADI AD9361 RFIC.
  • Why? Open‑source gateware/software, up to 61.44 MSPS (122.88 MSPS†) over PCIe Gen2 ×4, hack‑friendly clocking & debug.
  • Who? SDR tinkerers, FPGA devs, time‑sync enthusiasts, or anyone hitting the limits of other SDRs.
  • How fast? apt install …./build.pystream/record IQ in ≈5 min with our C API/tools or any SoapySDR compatible software.

C API (libm2sdr)

  • Docs: litex_m2sdr/doc/libm2sdr/README.md
  • Examples: litex_m2sdr/doc/libm2sdr/example_sync_rx.c, litex_m2sdr/doc/libm2sdr/example_sync_tx.c
  • Install metadata: litex_m2sdr/software/user/libm2sdr/m2sdr.pc
  • Current public library version: 1.0.0 (ABI 1)
  • The library is built as a runtime PCIe/Ethernet library; external applications only need to link libm2sdr.
  • Recent API additions: backend accessors m2sdr_get_transport() / m2sdr_get_eb_handle() and finer-grained parse/range/state error classes.
LiteX M2 SDR annotated

† Oversampling needs PCIe Gen2 ×2/×4 bandwidth.

[> Intro

We know what you'll first ask when discovering this new SDR project: what's the RFIC? 🤔 Let's answer straight away: Another AD936X-based SDR! 😄

Why yet another SDR based on this RFIC? Because we've been designing FPGA-based projects for clients with this chip for almost 10 years now and still think this RFIC has incredible capabilities and possibilities that haven't been fully tapped by open-source projects. We believe it can provide a fantastic and simple solution when paired with the LiteX framework we're developing. 🚀

Imagine a minimalist AD9361-based SDR with:

  • A compact form factor (M2 2280). 📏
  • Minimal on-board RF frontend that could be specialized externally.
  • 2T2R / 12-bit @ 61.44MSPS (and 2T2R / 12-bit @ 122.88MSPS for those wanting to use/explore Cellwizard/BladeRF findings).
  • PCIe Gen 2 X4 (~14Gbps of TX/RX bandwidth) with LitePCIe, providing MMAP and several possible DMAs (for direct I/Q samples transfer or processed I/Q samples). ⚡
  • A large XC7A200T FPGA where the base infrastructure only uses a fraction of the available resources, allowing you to integrate large RF processing blocks. 💪
  • The option to reuse some of the PCIe lanes of the M2 connector for 1Gbps or 2.5Gbps Ethernet through LiteEth. 🌐
  • Or ... for SATA through the LiteSATA gateware core. 💾
  • Or ... for inter-board SerDes-based communication through LiteICLink. 🔗
  • Powerful debug capabilities through LiteX Host <-> FPGA bridges and LiteScope logic analyzer. 🛠️
  • Multiboot support to allow secure remote update over PCIe (or Ethernet).
  • ...and we hope a welcoming/friendly community as we strive to encourage in LiteX! 🤗

OK, you probably also realized this project is a showcase for LiteX capabilities, haha. 😅 Rest assured, we'll do our best to gather and implement your requests to make this SDR as flexible and versatile as possible!

This board is proudly developed in France 🇫🇷 by Enjoy-Digital, managing the project and litex_m2sdr/gateware/software development, and our partner Lambdaconcept designing the hardware. 🥖🍷

Ideal for SDR enthusiasts, this versatile board fits directly into an M2 slot or can team up with others in a PCIe M2 carrier for more complex projects, including coherent MIMO SDRs. 🔧

For Ethernet support with 1000BaseX/2500BaseX and SATA connectivity to directly record/play samples to/from an SSD, mount it on the LiteX Acorn Mini Baseboard! 💽

Unlock new possibilities in your SDR projects with this cutting-edge board—we'll try our best to meet your needs! 🎉

[> Contents

  1. Hardware Availability
  2. Capabilities Overview
  3. M.2 / GPIO Voltage Levels
  4. PCIe SoC Design
  5. Ethernet SoC Design
  6. Release Artifacts
  7. Quick Start
  8. Contact

[> Hardware Availability

The LiteX-M2SDR board is now fully commercialized and available for purchase from our webshop: Enjoy-Digital Shop.

The hardware has been thoroughly tested with several SDR softwares compatible with SoapySDR as well as with our Bare metal C utilities.

We offer two variants:

  • SI5351C Variant – Uses the SI5351C clock generator with flexible clocking (local XO or external 10MHz via FPGA/uFL). Host-side selection now exposes the dedicated SI5351C FPGA 10MHz CLKIN path (--sync fpga / clock_source=fpga) in addition to the uFL 10MHz input mode. Recommended for general usage. More details
  • SI5351B Variant – Uses the SI5351B clock generator, clocked from the local XO with FPGA-controlled VCXO for software-regulated loops. More details

Note: The differences between the variants are relevant only for specific use cases. The SI5351B variant is mostly intended for advanced users with specialized clock control requirements.

[> Capabilities Overview

Feature Mounted in M.2 Slot Mounted in Baseboard Parameter(s) to Enable
SDR Functionality
SDR TX (AD9361) (always included)
SDR RX (AD9361) (always included)
Oversampling (122.88MSPS) ✅ (PCIe Gen2 x2/x4 only) `--with-pcie --pcie-lanes=2
C API + Utilities (included in software build)
SoapySDR Support (via optional SoapySDR driver)
Connectivity
PCIe (up to Gen2 x4) ✅ (x1 only) `--with-pcie --pcie-lanes=1
Ethernet (1G/2.5G) --with-eth
├─ Ethernet RX (LiteEth) (included with --with-eth)
└─ Ethernet TX (LiteEth) (included with --with-eth)
Timing & Sync
PTM (Precision Time Measurement) ✅ (PCIe Gen2 x1 only) ✅ (PCIe Gen2 x1 only) --with-pcie --pcie-lanes=1 --with-pcie-ptm
Ethernet PTP Time Discipline --with-eth --with-eth-ptp
Ethernet PTP RFIC Ref Clock --with-eth --with-eth-ptp --with-eth-ptp-rfic-clock
White Rabbit Support --with-white-rabbit
External Clocking ✅ (SI5351C: ext. 10MHz) ✅ (SI5351C: ext. 10MHz) (SI5351B VCXO mode in dev for PTM regulation)
Storage
SATA ✅ (source build) --with-sata
System Features
Multiboot / Remote Update (always included)
GPIO (always included)

User LED Behavior

The board exposes a single monochrome user_led, so the gateware uses it as a layered status indicator rather than a simple on/off flag:

  • Not ready yet: double-heartbeat while time is still invalid or while an enabled PCIe/Ethernet transport is not ready. PCIe becomes ready when the link is up and DMA/PPS synchronization is established; Ethernet becomes ready when the link is up.
  • Idle / ready state: gentle low-amplitude breathing.
  • PPS event: short bright accent pulse over the base animation.
  • RF or Ethernet RX/TX activity: bright accent pulse.

When PCIe is not enabled in the build, the PCIe-specific states are naturally skipped and the LED falls back to the generic timing/activity behavior.

[> M.2 / GPIO Voltage Levels

LiteX-M2SDR does not use a single M.2 I/O voltage:

  • FPGA banks 13/14/15/16 on the SDR are powered at 3.3V.
  • FPGA banks 34/35 on the SDR are powered at 1.8V.
  • The general-purpose sideband signals routed directly from the M.2 connector to the FPGA on LiteX-M2SDR (PPS, Synchro_GPIO, PERST#, optional PEWAKE#, SUSCLK, PEDET) sit on 3.3V FPGA banks on the SDR side.
  • The M.2 SMB_CLK / SMB_DATA pins are a special case: on LiteX-M2SDR r02 they reach the FPGA bank-16 pins through optional resistors R82 / R83, which are not mounted by default.
  • PCIe lanes and the PCIe reference clock are transceiver signals, not single-ended 1.8V/3.3V GPIOs.

Additional notes:

  • M.2 pin 44 (ALERT# / SMB_ALERT#) is currently not routed to the FPGA on LiteX-M2SDR r02.
  • M.2 pin 52 (CLKREQ#) is pulled up to 3V3_PCIe and is not routed to the FPGA.
  • M.2 pin 10 (LED#) is not connected on the FPGA side.
  • The dedicated FPGA JTAG/config pins and the Acorn JTAG header are separate 3.3V JTAG paths.
  • When discussing M.2 sideband voltages, distinguish the FPGA bank voltage on the SDR from the connector-side voltage expected by a host/baseboard. For example, the Acorn baseboard implements the M.2 SMBus pins as a 1.8V SMBus domain with translation to 3.3V for the SFP modules.
Signal Connector Location FPGA Pin Bank Voltage On SDR Side Notes
GPIO0 TP1 E22 16 3.3V General-purpose test point (FPGA_GPIO0).
GPIO1 TP2 D22 16 3.3V General-purpose test point (FPGA_GPIO1).
PPS_IN M.2 pin 22 (NC22) K18 15 3.3V Routed to the FPGA.
PPS_OUT M.2 pin 24 (NC24) Y18 14 3.3V Routed to the FPGA.
Synchro_GPIO1 M.2 pin 28 (NC28) A19 16 3.3V Routed to the FPGA.
Synchro_GPIO2 M.2 pin 30 (NC30) A18 16 3.3V Routed to the FPGA.
Synchro_GPIO3 M.2 pin 32 (NC32) A21 16 3.3V Routed to the FPGA.
Synchro_GPIO4 M.2 pin 34 (NC34) A20 16 3.3V Routed to the FPGA.
Synchro_GPIO5 M.2 pin 36 (NC36) B20 16 3.3V Routed to the FPGA.
SMB_CLK M.2 pin 40 A13 16 3.3V FPGA bank on SDR Optional path through R82, not mounted by default; connector-level SMBus compatibility depends on the host/baseboard.
SMB_DATA M.2 pin 42 A14 16 3.3V FPGA bank on SDR Optional path through R83, not mounted by default; connector-level SMBus compatibility depends on the host/baseboard.
ALERT# / SMB_ALERT# M.2 pin 44 - - Host-defined sideband Not routed to the FPGA on LiteX-M2SDR r02.
PERST# M.2 pin 50 A15 16 3.3V Routed to the FPGA.
CLKREQ# M.2 pin 52 - - 3.3V Pulled up to 3V3_PCIe with R59; not routed to the FPGA.
PEWAKE# M.2 pin 54 B16 16 3.3V Optional path through R88, not mounted by default.
SUSCLK M.2 pin 68 B17 16 3.3V Routed through R84 (0R).
PEDET / PRESENT M.2 pin 69 A16 16 3.3V Routed through R85 (0R).
LED# M.2 pin 10 - - Host-defined sideband Not connected on LiteX-M2SDR.

[> PCIe SoC Design

The PCIe design is the first variant developed for the board and does not require an additional baseboard. Just pop the M2SDR into a PCIe M2 slot, connect your antennas, and you're ready to go! 🚀

The SoC has the following architecture:

  • The SoC is built with the LiteX framework, allowing highly efficient HDL coding and integration. 💡
  • You'll also find that most of the complexity is managed by LiteX and LitePCIe. The SoC itself only has an MMAP interface, DMA interface, and integrates the specific SDR/RFIC cores and features. ⚙️
  • It provides debugging over PCIe or JTAG for MMAP peek & poke or LiteScope. 🛠️
  • LitePCIe and its Linux driver (sorry, we only provide Linux support for now 😅) have been battle-tested on several commercial projects. 🏆

The PCIe design has already been validated at the maximum AD9361 specified sample rate: 2T2R @ 61.44MSPS (and also seems to correctly handle the oversampling at 2T2R @ 122.88MSPS with 7.9 Gbps of bandwidth on the PCIe bus; this oversampling feature is already in place and more tests/experiments will be done with it in the future).

[> Ethernet SoC Design (1/2.5Gbps x 1 or 2).

Note

Ethernet support is intended for LiteX Acorn Baseboard Mini deployments and is bandwidth-limited by the selected 1000BaseX/2500BaseX link.

The Ethernet design variant gives flexibility when deploying the SDR. The PCIe connector has 4 SerDes transceivers that are in most cases used for... PCIe :) But these are 4 classical GTP transceivers of the Artix7 FPGA that are connected to the PCIe hardened PHY in the case of a PCIe application but can be used for any other SerDes-based protocol: Ethernet 1000BaseX/2500BaseX, SATA, etc...

In this design, the PCIe core will then be replaced with LiteEth, providing the 1000BaseX or 2500BaseX PHY but also the UDP/IP hardware stack + Streaming/Etherbone front-end cores.

The Ethernet SoC design supports control plus RX/TX sample streaming over the LiteEth UDP path. The achievable 2T2R sample rate is capped by link bandwidth, so Ethernet builds also cap the RFIC clock to the selected link speed.

Ethernet-only baseboard builds can also enable SATA storage with --with-sata, using SATA on PCIe lane 0 and Ethernet on the selected SFP lane. PCIe, Ethernet/White-Rabbit, and SATA cannot all be enabled in one image because the shared QPLL exposes two channels.

When built with --with-eth --with-eth-ptp, LiteEth PTP disciplines the existing time_gen timebase instead of replacing it. This keeps PPS generation, VRT timestamps, RX/TX headers, and the PCIe PTM/PHC view on the same logical board clock while sourcing that time from Ethernet PTP. Runtime servo tuning, master/sourcePortIdentity reporting, and live status/counter monitoring are available from the host side. Ethernet PTP and White Rabbit are mutually exclusive. For SI5351C boards, --with-eth-ptp-rfic-clock adds an optional low-bandwidth PTP-to-FPGA-10MHz discipline loop; software must still select the FPGA clock input with --sync fpga / clock_source=fpga before the AD9361 reference is derived from that path.

[> Wide Analog Bandwidth (RFIC Overclock)

The AD9361's analog baseband filters are normally limited to ~56 MHz. Requesting a sample rate above 61.44 MSPS (up to 122.88) selects the wide-bandwidth mode automatically:

m2sdr_rf --channel-layout 1t1r --sample-rate 122.88e6 ...

Converter half-band stages are bypassed so the data port runs at 2x rate, and the TX/RX baseband filters are force-widened with tuned register words, opening a true ~100 MHz analog passband at 122.88 MSPS. The interface DATA_CLK follows the channel layout: 245.76 MHz in 1T1R (stock gateware) and 491.52 MHz in 2T2R (gateware built with --with-rfic-oversampling). The doubled-rate interface framing can come up misaligned, so the configuration PRBS-verifies the interface and retries the clock programming in place until it is aligned (typically 1-2 attempts).

Measured (loopback TX1->RX1 with 40 dB pad + external-receiver cross-check, 3.6 GHz, 100 MHz NR-FR1-TM3.1 64QAM test model):

Metric Wide (overclock) Stock ~56 MHz
Usable analog bandwidth ~100 MHz ~56 MHz
EVM, 100 MHz NR TM3.1 (PDSCH) ~10% RMS (*) n/a (BW > filter)
EVM, 10 MHz NR TM3.1 ~3% RMS ~3% RMS
No-signal RX floor (rx-gain 55) -31.6 dBFS -34 dBFS
TX image rejection, tx-att 0 ~30 dB ~42 dB
TX image rejection, tx-att >=10 ~44-58 dB ~44-58 dB

(*) With TX magnitude pre-emphasis flattening the widened-filter rolloff the per-band effective SNR is uniform (+/-50 MHz); the ~10% ceiling decomposes into ~7% TX (reference/LO phase noise) and ~8.5% RX (widened-BBF noise floor). Best EVM is obtained on the internal clock reference; an external 10 MHz reference costs ~6% EVM through the Si5351's higher multiplication ratio (N=84 vs N=34).

[> Getting Started

For SDR Enthusiasts

If you are an SDR enthusiast looking to get started with the LiteX-M2SDR board, follow these simple steps to get up and running quickly:

  1. Install Prerequisite Packages:

    • On a fresh Ubuntu system, install the required development and SDR packages to ensure compatibility with the LiteX-M2SDR software:
    sudo apt install build-essential cmake git \
      pkg-config libsdl2-dev libgl1-mesa-dev \
      libsoapysdr-dev soapysdr-tools libsoapysdr0.8 \
      gnuradio gnuradio-dev libgnuradio-soapy3.10.9t64 gqrx-sdr \
      libsndfile1-dev libsamplerate0-dev
    • Note: For non-Ubuntu Linux distributions (e.g., Fedora, Arch), install the equivalent packages using your distribution's package manager (e.g., dnf for Fedora or pacman for Arch).
  2. Connect the Board:

    • Insert the LiteX-M2SDR board into an available M2 slot on your Linux computer and connect your antennas.

Warning

If an error related to DKMS appears during installation, run sudo apt remove --purge xtrx-dkms dkms and then re-execute the installation command.

  1. Clone the Repository:

    • Clone the LiteX-M2SDR repository using the following command:
    git clone https://github.com/enjoy-digital/litex_m2sdr
    
  2. Build Software: Software build uses make and CMake for the C kernel driver and utilities, but since we also like Python 😅, we created a small script on top of it to simplify development and installation:

    cd litex_m2sdr/software
    ./build.py
    
    • This builds the kernel driver, the user-space utilities, libm2sdr, and the SoapySDR driver.
    • The default software build is a runtime PCIe/Ethernet build: the same libm2sdr, user tools, and SoapySDR module can open PCIe or Ethernet devices from the device arguments. Use --interface=litepcie or --interface=liteeth only when you want the legacy shorthand/default transport to favor one side during local testing.
    • If you also want the optional SDL/OpenGL GUI tools (m2sdr_check / m2sdr_scan), first populate the pinned cimgui submodule:
    git submodule update --init --recursive litex_m2sdr/software/user/cimgui
    
    • By default, ./build.py builds incrementally and does not install when run as a normal user.
    • Use ./build.py --clean when you want a full rebuild.
    • Use sudo ./build.py when you also want to install the kernel driver, the user-space utilities / libm2sdr, and the SoapySDR module under the default prefix.
    • m2sdr_check and m2sdr_scan are optional SDL/OpenGL GUI tools. They are built only when SDL2/OpenGL development packages are installed and litex_m2sdr/software/user/cimgui/ has been populated; m2sdr_scan also needs libpng. If these optional GUI dependencies are absent, only the affected GUI tools are skipped; the CLI tools, libm2sdr, and the SoapySDR module still build normally.
  3. Install the Built Software:

    • Install the kernel driver:
    cd litex_m2sdr/software/kernel
    sudo make install
    sudo insmod m2sdr.ko # Optional if you do not want to reboot yet.
    
    • Install the public C API headers/library for external applications:
    cd litex_m2sdr/software/user
    make
    sudo make install_dev PREFIX=/usr/local
    sudo ldconfig
    
    • Install the SoapySDR module:
    cd litex_m2sdr/software/soapysdr/build
    sudo make install
    
    • If you already used sudo ./build.py, the kernel and SoapySDR install steps above are already done. libm2sdr still needs sudo make install_dev ... if you want to develop external applications against the public C API.
    • 🚀 Ready for launch!
  4. Run Your SDR Software:

    • Now, you can launch your preferred SDR software (like GQRX or GNU Radio) and select the LiteX-M2SDR board through SoapySDR. 📡

Host Requirements & Expectations

  • IOMMU / DMA: For PCIe streaming, set IOMMU to passthrough mode. If you don't see I/Q data streams in your SDR app, this is the first thing to check.
  • PCIe Gen & Lanes: Oversampling (122.88 MSPS) requires PCIe Gen2 x2/x4 bandwidth. Gen2 x1 is enough for standard 61.44 MSPS.
  • Runtime transport selection: The installed user tools and SoapySDR module support both transports in one build. Use --device pcie:/dev/m2sdr0 or --device eth:192.168.1.50:1234 with the CLI tools, and driver=LiteXM2SDR,path=/dev/m2sdr0 or driver=LiteXM2SDR,eth_ip=192.168.1.50 with SoapySDR.
  • PCIe PTM host-time sync: Build with --with-pcie --pcie-lanes=1 --with-pcie-ptm and run scripts/m2sdr_pcie_time_sync.py on the host to make the board PHC follow CLOCK_REALTIME through phc2sys. If the host clock is locked by NTP/PTP, the board follows that disciplined host time over PCIe.
  • Ethernet VRT (optional RX path): Build with --with-eth --with-eth-vrt to enable an Ethernet RX VRT UDP streamer in hardware. A simple host receiver utility is available at litex_m2sdr/software/user/m2sdr_vrt_rx.py.
  • Ethernet / SATA: Ethernet RX/TX streaming is supported on the LiteX Acorn Baseboard Mini. Source builds can combine Ethernet and SATA with ./litex_m2sdr.py --variant=baseboard --with-eth --eth-sfp=0 --with-sata --build. m2sdr_sata supports low-level sector tests and named capture workflows for RF-to-SATA recording, host import/export, SATA-to-RF replay, and SATA replay into the normal PCIe/Ethernet RX path used by SoapySDR/GQRX.
  • Ethernet RFIC clocking: Ethernet builds cap the RFIC clock to the link-speed streaming budget for 2T2R SC8: 122.88MHz with 1000basex and 245.76MHz with 2500basex. PCIe builds keep the full 245.76MHz/491.52MHz non-oversample/oversample options.
  • Ethernet PTP (optional timing path): Build with --with-eth --with-eth-ptp to discipline the existing board time_gen from LiteEth PTP. m2sdr_util info, m2sdr_util --watch ptp-status, and m2sdr_util ptp-config expose the current lock/holdover state, learned port identity, runtime servo controls, and board-side discipline counters. While PTP discipline is active, host-side time writes are rejected to avoid two masters steering the same clock.
  • Ethernet PTP RFIC reference (optional clock path): Add --with-eth-ptp-rfic-clock to expose a PTP-referenced FPGA 10MHz monitor/discipline loop. Enable it at runtime with m2sdr_util ptp-clock10-config enable on, verify Reference Locked and Clock Locked, then select the FPGA clock input for RF setup with m2sdr_rf --sync fpga or the matching SoapySDR clock_source=fpga setting. This gives RFIC reference frequency coherence; deterministic sample/RF phase alignment still needs AD9361 synchronization and timestamped stream start.

[> Release Artifacts

Date-named release archives are generated with:

./release.py

The release script checks the final Vivado timing report before creating each archive, so a bitstream with setup/hold timing failures is not packaged. Release manifests include the parsed timing summary; PCIe images may record the known Xilinx PCIe IP pulse-width warning when setup/hold timing is otherwise clean. Ethernet-enabled builds default to a 100MHz system clock for timing margin; PCIe-only M.2 builds keep the 125MHz system clock. The first release matrix builds the core PCIe/Ethernet images plus the validated Ethernet PTP RFIC-reference image:

Archive prefix Build command
litex_m2sdr_baseboard_eth ./litex_m2sdr.py --variant=baseboard --with-eth --eth-sfp=0 --build
litex_m2sdr_baseboard_eth_ptp_rfic_clock ./litex_m2sdr.py --variant=baseboard --with-eth --eth-sfp=0 --with-eth-ptp --with-eth-ptp-rfic-clock --build
litex_m2sdr_baseboard_pcie_x1_eth ./litex_m2sdr.py --variant=baseboard --with-pcie --pcie-lanes=1 --with-eth --eth-sfp=0 --build
litex_m2sdr_m2_pcie_x1 ./litex_m2sdr.py --variant=m2 --with-pcie --pcie-lanes=1 --build
litex_m2sdr_m2_pcie_x2 ./litex_m2sdr.py --variant=m2 --with-pcie --pcie-lanes=2 --build

Each build/*_<YYYY_MM_DD>.zip contains the .bit, .bin, multiboot fallback/operational images, CSR exports, and a JSON manifest. Generated archives and bitstreams are release artifacts and are not committed to git.

GitHub release publication uses the same date string as the archive suffix, with no v prefix on the tag. After generating the archives from the final release commit, validate the upload plan and then publish it with:

scripts/github_release.py --date 2026_05_15 --dry-run
scripts/github_release.py --date 2026_05_15

The helper requires the GitHub CLI gh to be installed and authenticated with release write access. It checks for a clean tracked tree, verifies that the expected build/*_2026_05_15.zip files exist, reads each archive manifest, creates and pushes the annotated 2026_05_15 tag on the manifest git revision, extracts the matching CHANGELOG.md section as release notes, and uploads the archive set to the GitHub release.

Tip

If you don't see I/Q data streams in your SDR app, make sure IOMMU is set to passthrough mode. Add the following to your GRUB configuration:

x86/PC:

# Add to GRUB config (/etc/default/grub):
GRUB_CMDLINE_LINUX="iommu=pt"
sudo update-grub && sudo reboot

ARM (ex NVIDIA Jetson/Orin):

# Add to extlinux.conf (/boot/extlinux/extlinux.conf):
APPEND ... iommu.passthrough=1 arm-smmu.disable=1
sudo reboot

Warning

For intel CPU: if a kernel panic occurs with the message Corrupted page table at address, add intel_iommu=off to GRUB_CMDLINE_LINUX. (This has been observed on an 11th Gen Intel(R) Core(TM) i7-11700B @ 3.20GHz)

Tutorials for your platform

Warning

WiP 🧪 Content below is more our memo as developers than anything useful to read 😅. This will be reworked/integrated differently soon.

For some platforms we created detailed tutorials. For everything else, please follow the earlier Getting Started tutorial.

For Software Developers

For those who want to dive deeper into development with the LiteX-M2SDR board, follow these additional steps after completing the SDR enthusiast steps:

For a broader hardware/software debug workflow, see Debugging Guide.

  1. Test Structure (CI-safe vs hardware scripts):

    • Gateware simulation/unit tests live in test/ and are CI-safe (no hardware needed):
    pytest -v test
    
    • Board control/debug scripts live in scripts/ and require a running board/server:
    python3 scripts/test_xadc.py
    python3 scripts/test_dashboard.py
    
    • CI runs both software build checks and simulation tests with:
    # Software build checks (kernel/user/SoapySDR) are run in CI.
    python3 -m pytest -v test
    
  2. Run Software Tests:

    • Test the kernel:
    cd litex_m2sdr/software/kernel
    make clean all
    sudo make install
    sudo insmod m2sdr.ko (To avoid having to reboot the machine)
    
    • Test the user-space utilities:
    cd litex_m2sdr/software/user
    make clean all
    ./m2sdr_util info
    ./m2sdr_rf --sample-rate=30720000 --tx-freq=2400000000 --rx-freq=2400000000
    ./m2sdr_gen --sample-rate 30720000 --signal tone --tone-freq 1000000 --amplitude 0.5
    
    • C API (libm2sdr) quick start and examples:
    See litex_m2sdr/doc/libm2sdr/README.md
    cd litex_m2sdr/software/user
    make examples
    ../../doc/libm2sdr/example_sync_rx > /tmp/rx.iq
    ../../doc/libm2sdr/example_tone_tx
    
    • libm2sdr is the common host interface used by the user utilities and the SoapySDR module, so example code there is the reference starting point for new host applications.
  3. SoapySDR Detection/Probe:

    • Detect the LiteX-M2SDR board:
    SoapySDRUtil --probe="driver=LiteXM2SDR"
    
  4. Run GNU Radio FM Test:

    • Open and run the GNU Radio FM test:
    gnuradio-companion litex_m2sdr/software/gnuradio/test_fm_rx.grc
    
  5. Enable Debugging in Kernel:

    • Enable debugging:
    sudo sh -c "echo 'module m2sdr +p' > /sys/kernel/debug/dynamic_debug/control"
    

For Software & FPGA Developers

For those who want to explore the full potential of the LiteX-M2SDR board, including FPGA development, follow these additional steps after completing the software developer steps:

  1. Install LiteX:

  2. Ethernet and PCIe Tests:

    • For Ethernet tests, if the board is mounted in an Acorn Mini Baseboard:
    ./litex_m2sdr.py --variant=baseboard --with-eth --eth-sfp=0 --build --load
    ping 192.168.1.50
    
    • After loading an Ethernet image, use m2sdr_util loopback tests to exercise the stream path before starting Soapy/Gqrx:
    cd litex_m2sdr/software/user
    make m2sdr_util
    ./m2sdr_util -i 192.168.1.50 --duration 4 --pace=rx --sample-rate 1920000 --window 32 fpga-phy-loopback-test
    ./m2sdr_util -i 192.168.1.50 --duration 8 --pace=rx --sample-rate 1920000 --window 32 ad9361-loopback-test
    
    • The loopback tests reset FPGA stream state at startup/cleanup, so they can be run after Soapy/Gqrx sessions. Add --verbose to show detailed RF setup logs and LiteEth counters. See Debugging Guide for the full loopback workflow.
    • For Ethernet + SATA source-build tests:
    ./litex_m2sdr.py --variant=baseboard --with-eth --eth-sfp=0 --with-sata --build --load
    cd litex_m2sdr/software/user
    make m2sdr_sata
    ./m2sdr_sata -i 192.168.1.50 info
    ./m2sdr_sata -i 192.168.1.50 diag etherbone-bench
    ./m2sdr_sata -i 192.168.1.50 --pattern counter diag pattern-write 0x8000 4096
    ./m2sdr_sata -i 192.168.1.50 --pattern counter diag pattern-check 0x8000 4096
    ./m2sdr_sata -i 192.168.1.50 init
    ./m2sdr_sata -i 192.168.1.50 capture fm_test --seconds 2 --sample-rate 4M --format sc16 --channel-layout 1t1r --rx-freq 100M --rx-gain 20 --bandwidth 5M
    ./m2sdr_sata -i 192.168.1.50 list
    ./m2sdr_sata -i 192.168.1.50 export fm_test /tmp/fm_test.sigmf-meta
    ./m2sdr_sata -i 192.168.1.50 export fm_test /tmp/fm_test.sc16 --raw
    ./m2sdr_sata -i 192.168.1.50 import tx_test /tmp/tx.sc16 --sample-rate 4M --format sc16 --channel-layout 1t1r --tx-freq 2400M --tx-att 20
    ./m2sdr_sata -i 192.168.1.50 import tx_sigmf /tmp/tx.sigmf-meta
    ./m2sdr_sata -i 192.168.1.50 play tx_test
    ./m2sdr_sata -i 192.168.1.50 play tx_sigmf
    

    To replay a stored capture into an existing SoapySDR/GQRX receive flow, start the RX application normally, then feed the Ethernet RX path from SATA:

    ./m2sdr_sata -i 192.168.1.50 serve fm_test
    

    The SATA Capture Volume is stored on the SATA disk at sector 0x800; automatic capture allocation starts at sector 0x100000, and named captures keep a SigMF metadata region next to the sample data. It is a small capture index, not a general file system: captures stay in contiguous sector ranges for the SATA streamers, SigMF provides interchange metadata, and avoiding FAT/ext keeps validation and recovery simple. init refuses to reset a non-empty volume unless --force is provided; it does not erase sample data sectors. See SATA Workflows for the full m2sdr_sata workflow and SATA Hardware Validation for measured PCIe/Ethernet validity and throughput results.

    • For PCIe + SATA source-build tests:
    ./litex_m2sdr.py --variant=baseboard --with-pcie --pcie-lanes=1 --with-sata --build --load
    cd litex_m2sdr/software/user
    make m2sdr_util m2sdr_sata
    ./m2sdr_util -c 0 info
    ./m2sdr_sata -c 0 info
    ./m2sdr_sata -c 0 --pattern counter diag pattern-write 0x8000 4096
    ./m2sdr_sata -c 0 --pattern counter diag pattern-check 0x8000 4096
    ./m2sdr_sata -c 0 init
    ./m2sdr_sata -c 0 capture fm_test --seconds 2 --sample-rate 4M --format sc16 --channel-layout 1t1r --rx-freq 100M --rx-gain 20 --bandwidth 5M
    ./m2sdr_sata -c 0 export fm_test /tmp/fm_test.sc16 --raw
    ./m2sdr_sata -c 0 export fm_test /tmp/fm_test.sigmf-meta
    ./m2sdr_sata -c 0 serve fm_test
    
    • For Ethernet PTP time-discipline tests on the baseboard:
    ./litex_m2sdr.py --variant=baseboard --with-eth --with-eth-ptp --eth-sfp=0 --build --load
    sudo ptp4l -i <host-eth-iface> -4 -E -S -m
    cd litex_m2sdr/software/user
    make m2sdr_util
    ./m2sdr_util -i 192.168.1.50 info
    ./m2sdr_util -i 192.168.1.50 --watch ptp-status
    
    • m2sdr_util info reports whether the LiteEth PTP core is locked, whether the board time is locked to PTP, and whether the clock is in holdover.
    • m2sdr_util --watch ptp-status shows the live discipline state, learned master identity, and lock/loss counters from the board-time discipline loop. m2sdr_util --json ptp-status and m2sdr_util ptp-smoke provide machine-readable and pass/fail checks for lab automation; use tcpdump when protocol message visibility is needed. m2sdr_util ptp-config exposes runtime servo tuning.
    • To also discipline the FPGA-generated 10MHz RFIC reference path:
    ./litex_m2sdr.py --variant=baseboard --with-eth --with-eth-ptp --with-eth-ptp-rfic-clock --eth-sfp=0 --build --load
    sudo ptp4l -i <host-eth-iface> -4 -E -S -m
    cd litex_m2sdr/software/user
    make m2sdr_util
    ./m2sdr_util -i 192.168.1.50 ptp-clock10-config
    ./m2sdr_util -i 192.168.1.50 ptp-clock10-config enable on
    ./m2sdr_util -i 192.168.1.50 ptp-clock10-config align
    ./m2sdr_util -i 192.168.1.50 --watch ptp-clock10-status
    cd ../../..
    scripts/m2sdr_ptp_check.py smoke --ip 192.168.1.50 --iface <host-eth-iface> --with-clock10 --require-clock10-lock
    
    • After the clk10 loop is stable, use m2sdr_rf --sync fpga or SoapySDR clock_source=fpga so the SI5351C derives the AD9361 reference from the FPGA 10MHz path.
    • See Ethernet PTP Bring-Up for known-good ptp4l configs, host timestamping checks, and smoke/soak validation commands.
    • For PCIe tests, if the board is mounted directly in an M2 slot:
    ./litex_m2sdr.py --with-pcie --variant=m2 --build --load
    lspci
    
    • To have a PCIe/PTM image automatically follow host time, build with PTM and start the host-side sync helper after the kernel driver has created the M2SDR PHC:
    ./litex_m2sdr.py --with-pcie --pcie-lanes=1 --with-pcie-ptm --variant=m2 --build --load
    scripts/m2sdr_pcie_time_sync.py --dry-run
    sudo scripts/m2sdr_pcie_time_sync.py --stdout
    
    • For boot-time use, install a systemd service similar to:
    [Unit]
    Description=Synchronize M2SDR PCIe board time to host time
    After=multi-user.target
    
    [Service]
    ExecStart=/path/to/litex_m2sdr/scripts/m2sdr_pcie_time_sync.py --stdout
    Restart=always
    RestartSec=2
    
    [Install]
    WantedBy=multi-user.target
    
    • The helper auto-detects /sys/class/ptp/ptp*/clock_name == m2sdr and runs phc2sys -s CLOCK_REALTIME -c /dev/ptpN, so the board is the sink and the host is the source. For multi-board systems, pass --phc /dev/ptpN.

    • For PCIe tests, if the board is mounted directly in a LiteX Acorn Baseboard:

    ./litex_m2sdr.py --with-pcie --variant=baseboard --build --load
    lspci
    
  3. White Rabbit (Baseboard):

    • White Rabbit is supported on the baseboard variant only:
    ./litex_m2sdr.py --with-pcie --with-white-rabbit --variant=baseboard --build
    
    • The White Rabbit helper logic is provided by litex_wr_nic; install it, set LITEX_WR_NIC_DIR, or keep a sibling ../litex_wr_nic checkout.
    • --wr-sfp is optional; when omitted, the first available sfp index is auto-selected.
    • Firmware path lookup order:
      1. --wr-firmware
      2. --wr-nic-dir
      3. LITEX_WR_NIC_DIR
      4. auto-discovery of ../litex_wr_nic and ../../litex_wr_nic
    • If a stale local wr-cores/ checkout is detected, refresh it:
    mv wr-cores wr-cores.old
    ./litex_m2sdr.py --with-pcie --with-white-rabbit --variant=baseboard --build
    
  4. Use JTAGBone/PCIeBone:

    • Start the LiteX server for JTAG or PCIe:
    litex_server --jtag --jtag-config=openocd_xc7_ft2232.cfg # JTAGBone
    sudo litex_server --pcie --pcie-bar=04:00.0              # PCIeBone (Adapt bar)
    
  5. Flash the Board Over PCIe:

    • Flash the board:
    cd litex_m2sdr/software
    ./flash.py ../build/litex_m2sdr_platform/litex_m2sdr/gateware/litex_m2sdr_platform.bin
    
  6. Reboot or Rescan PCIe Bus:

    • Rescan the PCIe bus:
    echo 1 | sudo tee /sys/bus/pci/devices/0000\:0X\:00.0/remove # Replace X with actual value
    echo 1 | sudo tee /sys/bus/pci/rescan
    

[> Contact

Got a unique idea or need a tweak? Whether it's custom FPGA/software development or hardware adjustments (like adapter boards) for your LiteX M2 SDR, we're here to help! Feel free to drop us a line or visit our website. We'd love to hear from you!

E-mail: florent@enjoy-digital.fr Website: http://enjoy-digital.fr/

About

LiteX based M2 SDR/RF FPGA board.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors