FPGA + 3 R + 1 C = MW and SW SDR Receiver

Look mum, no ADC!

Public Chat
Similar projects worth following
A fun project, a Verilog receiver with a minimal component's count. Lattice MachXO2 and a few passive components are enough. Now with a port to DE0 Nano Cyclone 3 board by Steven Groom

Hear for yourself:

This project has been created as a way to learn Verilog and have some fun with FPGA and SDR. Main goal is to receive AM broadcast stations with as little components as possible. The FPGA chosen, Lattice MachXO2, is also among the simplest components that can be used. I was able, with 20 meters of electrical wire as an antenna, to receive stations from thousands of kilometers, located in three continents. Smallest BOM consists of a 30 euros Lattice MachXO2 breakout board, three resistors, one capacitor and a speaker. For better performance it is best to add a crystal oscillator, sensitivity and audio quality are better than with the internal oscillator.

The antenna is directly connected to an input pin, as you know static charges can damage the board. When you are tired of record-breaking component count it is better to add a series capacitor and two limiting diodes. Audio is more practical with an RC filter, a series capacitor and an active speaker.

The radio architecture is quite standard, a direct-conversion receiver. We have an ADC, a Mixer, two CIC filters for the quadrature signals, an AM demodulator which takes the square root of the sum of the squares of the signals, and a PWM for audio output. Frequency tuning is obtained by an NCO, a 64-bit accumulator whose increment is controlled by an UART, it is possible to send some characters from the controlling PC and change frequency. The board has a spare UART channel for this. Main channel is used for programming, it is necessary to connect the second channel of the USB-to-serial converter to the FPGA by soldering a bridge on the lower side of the board, see the documentation.

ADC uses an LVDS comparator as input, but it works differently than the Sigma-Delta converter like

In that case the feedback signal tracks the input by using a low RC time costant. At radio frequencies this is impractical and the feedback is used to keep the comparator near the switching level by a large RC constant. Sampling is performed by the random RF noise superimposed to the desired signal. We are oversampling at 80 MHz a 6 KHz bandwith signal. A brief explanation for this: if the input is truly random its mean value over a certain time will be almost zero. But it is summed with a very small radio signal from a distant transmitter and this is enough to slightly influence the output.

CIC filter decimation is 4096 so the theoretical increase in resolution of the ADC is 6 bit.

It would be better to have a lower CIC decimation followed by a FIR filter. It can be done, not all of the FPGA is used. There is no IP for it from Lattice, MachXO2 lacks DSP blocks and was never intendeded for it.

This was one of my first projects with FPGA, code quality is at a fun-project level.

In the GitHub repository there are files for the Intel Cyclone 3 FPGA. Port was made by Steven Groom, thanks.

Another low component count radio:

Home made 1 bit ADC GPS receiver from 1991



Adobe Portable Document Format - 22.58 kB - 04/13/2020 at 09:56



Block Diagram

JPEG Image - 2.78 MB - 04/12/2020 at 12:52




sch - 7.48 kB - 04/12/2020 at 12:47


  • 1 × Lattice MachXO2 Breakout Board
  • 3 × 10 K resistor
  • 1 × 220 p capacitor

View all 3 project logs

  • 1
    Step 1

    GitHub hosts the complete project, it should be ready to synthesize and flash.

    Lattice Diamond 3.11 for Windows is the toolchain I used. There is a Linux version for it.

    Connect the secondary UART channel to pin 73 and 74 by shorting R14 and R15 on the lower side of the board. 

    I used an 8 MHz crystal oscillator, other frequencies can be used. You then need to change the relevant parameter in the PLL component. 

    Local oscillator frequency is generated by an NCO. It is a 64 bit register, we add a constant to it at every clock cycle, therefore its value is 2^64 * Fout / Fclock. In Python with a 80 MHz clock, 1 MHz NCO can be calculated with 

     print(hex(pow(2,64) * 1000000 // 80000000))

    Internal oscillator can be +-15% off. Measure the actual frequency or receive a test signal and calibrate. Selectivity is not so good so it should be easy to find a station.

    Reception of MW and LW station is better after sunset, propagation brings very strong signals from far away.

    Use an online SDR close to you to have an idea of the stronger signals.

View all instructions

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates