32MHz spectrum and SDR in an FPGA

A 0 - 32MHz FPGA based Software Defined Radio assembling ready modules -> cheap and easy!
Last log 19 Jan '23: Keyboard and Mouse Control

Public Chat
Similar projects worth following
KiwiSDR is a very good example of a wideband 0-30 MHz FPGA based SDR receiver but it costs 450 Euro and it needs a BeagleBone computer.

Red Pitaya is a wonderful project with a lot of possibilities and it is open source but the board costs around 600 Euro.

Panoradio ( is much more than I'm trying to build (100 MHz spectrum, 16 bits samples) but its components (FPGA board, A/D board) are much more expensive as well.

My aim is to build a much cheaper (< 100 Euro) 0-30 MHz 12 bits samples FPGA based SDR receiver (including the linux computer), using two main pre-assembled boards and some electronics around.


At the beginning the antenna is directly connected to the AD9226 Analog to digital converter, sampling at 64MHz. The 64MHz clock is generated by the FPGA, so that it can sample it well (more on this later)


Low pass (anti aliasing) 30 MHz filter. It will be useful to avoid interferences from FM radios

Block Diagram

The Zynq block are pretty consolidated while the Windows 10 PC is anything like an hypothesis.


All the number crunching in the fabric logic is perfomed with two's complement. 

When I write xx(yy), e.g. 32(28), I mean that the numbers are 32 bit two's complement but the maximum expected value is 

                                                  - 2^28 <= value <= 2^28-1

ADC - Analog to Digital Converter - AD9226

The board has no clock reference (see the AD9226 board schematic) and therefore the 64MHz clock must be supplied by the FPGA. In this way:

  • the AD9226 outputs data on the dropping edge of the clock. 
  • the FPGA samples on the clock rising edge

See the picture below and for further details see the AD9226 datasheet from Analog Devices.

A concern is about the clock jitter coming from the FPGA. Will it add noise to the received signal?

For example, in this project ADC to DMA to Ethernet with a ZYNQ 7000, the sampling clock is generated outside the FPGA 


The functions operating in the FPGA are the following.

  • It supplies a 64MHz clock to the AD9226 and receive 12 bit data + 1 bit "Over The Range" from it.
  • An internal signal generator 0.1 30 MHz AM modulated at 1KHz can be be used for testing purposes.
  • One "capture block" will capture 16384 samples (at 64MHz) at a time. These samples are sent to the FPGA PS and from there to the SpectrumPy (a Python App running on a Windows PC) to show the full 32 MHz radio spectrum and waterfall.
  • One or more DDC (Digital Down Converters) with selectable bandwidth (10 MHz, 3 MHz, 1 MHz, 300KHz, 100 KHz, 30 KHz, 10 KHz,)  will zoom the radio spectrum and waterfall with increased resolution. (TODO)
  • At lower bandwidths (100KHz, 30KHz, 10KHz and 3KHz) the DDC can be tuned on a specific frequency to demodulate and decode e.g. the FT8 frequencies on 80 40 20 15 and 10 meters. 

Getting started with the EBAZ4205 board

This project is quite challenging, especially for those people who have never worked with Xilinx Zynq developement environment (Vivado, Vitis, Petalinux). Therefore I strongly suggest to start with a getting started project.  See my Hackaday project: EBAZ4205 development environment

Show the full 0-32 MHz spectrum and waterfall

I built this first assembly and wrote some software to test this Hardware.

The shield around the ADC tries to limit EMI from the Zynq.

The empty shielded area will contain:

  • a power filter to attenuate the electric noise eventually carried by +5V to the analog side of the ADC

and / or 

  • a 30 MHz low pass filter

The 16 wires flat cable ADC <-> FPGA (PL) connection carries:


  • 12 bits data 
  • Out of The Range (OTR) signal


  • sampling clock (64 MHz)

The Analog Input

I didn't like that AD9226 12-BIT 65MSPS  board attenuates the input signal by -8,4 dB. So I decided to modify it to get some amplification, using far more 12 bits ADC range. 

I designed a circuit like this: 

obtaining some 16,5 dB gain compared with -8,4 dB of the original circuit that was this one:

The modified board:

The Analog Input Dynamic Range

With the first stage amplification I set, the maximum input signal (the one generating 2 Vpp between VINA and VINB) is around -6 dBm. 


The Ethernet port is connected to the local LAN, obtaining an IP address.

In this way a user can view the spectrum and waterfall  connecting via a TCP socket.

To do this,...

Read more »

AXI PS2.pdf

AXI PS/2 1.0 IP Core User Guide from Digilent

Adobe Portable Document Format - 590.56 kB - 01/16/2023 at 21:24


ebaz4205 schematic.pdf

The electrical schematic of the EBAZ4205. Very useful to choose I/O pins.

Adobe Portable Document Format - 223.98 kB - 07/18/2022 at 13:23


  • Keyboard and Mouse control

    Guido01/16/2023 at 21:21 0 comments

    After obtaining an HDMI video out from the EBAZ4205 board and being able to use it from Linux by /dev/fb0, now I need to interact with Linux and the C / Python app (showing the spectrum, tuning and so on) with standard input devices keyboard and mouse.

    For this need, I examined two options:

    • USB
    • PS/2


    Unfortunately EBAZ4205 has no USB interface, but I found the way to implement it using a cheap external module USB3300

    I'm now looking forward to arriving it from China.


    • It's a bit obsolete but it is very promising because it doesn't need any hardware addition.
    • I found an AXI PS2 IP controller from DIGILENT: AXI PS/2 1.0 IP Core User Guide from DIGILENT,. I tried to use it but I didn't succeed! 
    • So I decided to use some VHDL code I found here PS/2 Mouse Interface (VHDL). Unfortunately this VHDL code manage the two buttons PS/2 mouse only and not the middle button and the wheel (so called IntelliMouse), so I had to integrate the VHDL code. 
    • Then I also found a linux device driver  xilinx_ps2.c with a similar memory mapped interface that I hope to be able to adapt.

    You can follow the full Hackaday project EBAZ4205 PS/2 Mouse

  • Video Out

    Guido01/04/2023 at 09:19 0 comments

    As I explained before, I would like to embed the User Interface App into the PS side of Zynq.

    The App, actually written in Python (Matplotlib + pyQTgraph), allows to see the spectrum, the waterfall and to tune radio stations.

    For this reason the EBAZ4205 should have implemented with any kind of video out.

    I examined a few options.

    • VNC server / X11 forwarding
      • Not difficult to do in SW
      • No need to change hardware
      • It needs an external PC 
    • VGA
      • It needs a simple resistive external ladder
      • a bit old fashioned
      • it's analogic
      • It needs an external VGA display or a VGA/HDMI external adapter
    • HDMI with HW converter
      • It uses an external device (ADV7511 or similar) to convert the Zynq parallel output to the HDMI differential digital signals to handle the highest resolutions 
      • It's digital
      • you can use any HDMI display / TV

  • Graphical User Interface

    Guido12/29/2022 at 21:11 0 comments

    My first will was to embed everything in the Zynq7010 FPGA to show its wonderful capabilities. 

    However I'm still in trouble to find the best way to do it!

    For brainstorming, I can try a little resume of the features I want (ed):

    • wide band (0-30 MHz) radio spectrum
    • zoomable (30MHz ... 10 KHz) radio spectrum and waterfall
    • listen to AM/USB/LSB demodulated station 
    • IF bandwidth selection (1 - 32KHz or more)
    • FT8 decoding and sending to

    and the possible human interfacing solutions could be the followings.

    • standalone:
      • LCD screen connected to HDMI (not present on EBAZ4205)
      • Loudspeaker connected via I2S 
      • switches, commands, frequency selector ... via GPIO
    • WEB (like KIWI SDR):
      • spectrum and waterfall on the web page
      • audio streaming on the  web page
      • switches, commands, frequency selector ... on the web page
    • PC software (like HDSDR, SDR#, ...)

    Any suggestion?

  • Ready to receive HF Radio Stations

    Guido12/29/2022 at 20:37 0 comments

    Now almost everything is ready to receive HF Radio Stations.

    • ADC: done
    • sampler: done
    • spectrum viewer: done but not mandatory for listening
    • Digital down converter: done
    • CIC decimator - FIR filter: done
    • AM demodulator: done
    • SSB demodulator: TODO
    • AGC (*)
    • I2S output: done
    (*) designed and simulated but not tested yet

    After a few attempts, using a simple C++ program running on Linux in the Zynq Programmable System I set:

    • IF GAIN = 4
    • CIC decimation = 1024 corresponding to an audio bandwith of 8KHz
    • Mux switched to Test Generator
    • Test Generator at 10 MHz (1 KHz Amplitude Modulated)
    • Local Oscillator at 10 MHz

    and obtained a clear 1KHz tone at the output of the PCM5102 I2S ADC (around 1,5 Vpp),

    Then I successfully tried a few Radio Stations after verifying they were receivable at that time using

    • IF GAIN = 4 (up to 64)
    • CIC decimation = 1024 corresponding to an audio bandwith of 8KHz
    • Mux switched to ADC (don't forget to supply the ADC and connect the antenna)
    • Local Oscillator at the frequency of the chosen Radio Station e.g. 7215 KHz for China Radio International (English)

  • Test Signal Generator

    Guido12/29/2022 at 18:26 0 comments

    To test the fabric logic and the whole design even without an external signal and without involving the ADC, I rapidly understood the need of an internal test signal. Using a Xilinx DDS compiler IP I designed the following block:

    The RF_test_10MHz generates a sinusoidal signal from 0.1MHz to 30MHz (default 10MHz) and programmable by dds_axi_interface_0.

    The dds_compiler_1KHz generates a 1KHz 12 bits sinusoid (-2048 ... 2047) added to a 2048 constant value to obtain a 12 bits sinusoid between 0 and 4095.

    The 1KHz sinusoid  then multiplies (AM modulates) the test sinusoidal signal (e.g. at 10 MHz) obtaining the desired AM modulated signal roughly between -2048 and 2047 i.e. 12 bits after right shifting inside the multiplier.

  • Signal levels at any stage

    Guido12/29/2022 at 18:03 0 comments

    To keep the "right" digital level at every stage of the SDR looks quite challenging to me. 

    "Right" means:

    • enough high to reduce numerical noise
    • enough low not to saturate the two's complement fixed point numbers

    To test and simulate such a level in any stage (complex multiplier, CIC, FIR etc.)  I designed the test generator so that it outputs the maximum peak to peak signal level, which at 12 bits is between -2048 and 2047. This is the same maximum signal level the ADC can handle before saturating. 

    Then I designed every following stage to output the maximum output level given the maximum input level. For example, see the complex multiplier.

  • AM demodulator

    Guido12/20/2022 at 17:17 0 comments

    After seeing the very good result I obtained designing and simulating the AGC using matlab - simulink, I decided to complete the logical path from RF to audio, designing an AM demodulator.

     In this way I could listen to some radio stations at last. I read some articles on the web but the only one I found really interesting is  "An FPGA SDR HF Transceiver by K6JCA" who very well describe the AM demodulator.

    A little resume, now I have the following items:

    • ADC: done
    • sampler: done
    • spectrum viewer: done
    • Digital down converter, CIC decimator - FIR filter (*)
    • AM demodulator (*)
    • SSB demodulator: TODO
    • AGC (*)
    • I2S output: done
    (*) designed and simulated but not tested yet

    So I'm almost ready to put it all together in my EBAZ4205. 

    Talk to you soon!

  • AGC - Automatic gain Control

    Guido12/19/2022 at 11:49 0 comments

    Maybe I could postpone its design, but I just learn Matlab Simulink a little, so I decided to test my new skills.

    I found this interesting article Design and implementation of a new digital automatic gain control by Etienne Tisserand, Yves Berviller describing the forward AGC control, so I decided to adapt this solution to my requirements:

    • sampling rate = 48000 Hz
    • data format = 32 bit two's complement
    • dynamic range > 40 dB
    • Adjustable Attack and Decay

    I simulated on Simulink and created a VHD function to be used in Vivado.

  • I2S Audio Output

    Guido12/10/2022 at 19:02 0 comments

    As I told before, I'm in the mood to tune any HF radio station and therefore I need to get some audio out the Zynq-7000. 

    To get some audio out of the Zynq I examined a few options:

    1. send the audio stream to the PS (via DMA or Data Capture) and then:
      1. from PS to a Python client (the same I'm using to display the wide spectrum) via a web socket stream
      2. or from PS to a DAC via I2S 
    2. get audio as a digital stream from the PL (via I2S standard) to a DAC (PCM5102 or similar)
    3. get audio as a PWM from the PL (as Raspberry does)

    In the future I'd love to avoid the use of an external PC, so I discarded 1a 

    I discarded 3 because the audio quality is not excellent and you can hear some "clicks" 

    I discarded 1b because I'm not very fond of linux drivers and it is not very clever to move audio from PL to PS and then back to PL

    Therefore the winner is: get audio as a digital stream from the PL (via I2S standard) to a DAC (PCM5102 or similar)

    • I2S is a standard
    • several very cheap I2S DAC exist (PCM5102, MAX98357, ...)  
    • I found a Vivado IP that worked immediately. See I2S in the Details.

  • First spectrum and waterfall result

    Guido11/16/2022 at 18:28 0 comments

    As you can see in the following picture, with my first assembly I received many signals in the full 0-32 MHz band simply connecting my EFLW antenna directly to the analog input. 

    I'm pretty sure that a few of these signals are entering the receiver from the 2nd and 3rd Nyquist zone but I'm not sure because I cannot demodulate them at the moment. 

    Anyway in my opinion this first result encourages me to go on! 

    (Y-axis not in scale)

View all 19 project logs

Enjoy this project?



Jean-Pierre Mallet wrote 02/01/2023 at 15:48 point

 EBAZ4205_SDR is always 0404 Sorry..

  Are you sure? yes | no

Guido wrote 02/01/2023 at 17:26 point

I'm so sorry! I set the repository as "private" during last modifications but I forgot to restore it to "public"! Now it's OK!

  Are you sure? yes | no

Guido wrote 11/12/2022 at 14:30 point

Thank you Gordon, I immediately corrected that mistake!

  Are you sure? yes | no

Gordon wrote 11/12/2022 at 13:48 point

It should be "an FPGA" not "a FPGA"

  Are you sure? yes | no

crun wrote 11/16/2022 at 01:48 point

Hi Guido, as I love pedulumtry as much as the next commentard, I thought I would explain at great length why this is so. The reason for this is that F is spoken as "eff". The spoken sound begins with a vowel, so we use "an" not "a". (spoken as "uh" or "ay") I'm not sure how it feels to your mouth, but for a native english speaker, it feels awkward to say "uh eff" due to the hard stop between sounds, and so we soften the "a" to "an", so it feels easier to speak it a bit run together as "an-eff". (I guess like "al" or "del" in spanish)

It is not really another strange english rule of grammar, but just verbal laziness in action. So the rule is really: if it begins with a vowel _sound_, use "an". This often applies where the letters are spoken eg FPGA, but also in some words where the letter is being  like a vowel e.g. "yvette"

"An A/E/F/H/I/L/M/N/O/R/S/U/X" - some begin with e or a sounds

"a uniform" "an underwater camera"

  Are you sure? yes | no

Guido wrote 11/12/2022 at 07:36 point

Hello Crun, thank you for your suggestions. I would try to build a 9th order elliptic passive filter with a stop band around 70 dB., tuning one of its traps to the FM band, see the log for details.

  Are you sure? yes | no

crun wrote 11/16/2022 at 01:55 point

You also need a high pass to keep AM broadcast band out. They are insanely strong for anyone within several miles of a transmitter. Here is the very front of a Kenwood TS130 radio. C1-3,L1,2 are a high pass filter. L3,4 form a trap for the IF frequency (8.9MHz). T1 ups the voltage, but probably also has a highpass function. Even though radios have the highpass filter to cut it out, AM broadcast is so strong, that this has no effect on the receivers ability to hear it.

OK, looks like I can't put the picture in here - sent to you on the chat...

  Are you sure? yes | no

crun wrote 11/10/2022 at 20:19 point

Great to see you making progress!.

Commercial radios put traps at broadcast frequencies, and don't just rely on lpf/bpf filters. i.e. you have a parallel resonant circuit in series andseries resonant circuit to ground. FM is only a bit over one octave past your passband, so you have to put nulls in to get significant attenuation. Alternatively you can customise an epileptic filter, by making some of the zeros occur exactly where you need to null signals out.

  Are you sure? yes | no

Guido wrote 08/29/2022 at 17:42 point

Thank you Alperen, I hope to be successful!

  Are you sure? yes | no

Alperen wrote 08/29/2022 at 09:50 point

Great project!

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

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