fpga4fun.com - where FPGAs are fun.
Home
Welcome
Information


FPGA projects
Music box
Pong game
R/C servos
Text LCD module
Quadrature decoder
PWM and one-bit DAC
Debouncer
LED displays
Crossing clock domains
External contributions

FPGA interface projects
RS-232
JTAG
I2C
EPP
SPI
CNC steppers

FPGA advanced projects
Graphic LCD panel
Digital oscilloscope
10BASE-T interface
PCI interface
Spoc CPU core

Hands-on
A simple oscilloscope


FPGA introduction
What are FPGAs?
How FPGAs work
FPGA pins
Clocks and global lines
Download cables
Configuration
Learn more

FPGA software
Design software
Pin assignment
Design-entry/HDL
Simulation/HDL
Synthesis and P&R

FPGA electronic
SMD technology
Crystals and oscillators

HDL info
HDL tutorials
Verilog tips
VHDL tips

Quick-start guides
ISE
Quartus

Site
News
FPGA links
HDL tutorials
Forum


PWM and one-bit DAC

Take one pin of an FPGA, connect a speaker and listen to an MP3?
Sure.

Here, we'll use a PC to decode an MP3, and then send the decoded data to an FPGA that is configured as a one-bit DAC.

Audio output

We require a DAC (digital-to-analog converter) to connect the FPGA (digital) to a speaker (analog).
The conventional approach would be to use a resistor ladder (see here), or use a dedicated DAC IC, like the venerable DAC-08.

Since the frequency at which FPGAs run is so fast compared to the frequencies required in the audio domain (MHZ's compared to KHz's), a one-bit DAC is a better choice.

The simplest way to implement a one-bit DAC is with a PWM generator.

PWM (Pulse Width Modulation)

The PWM generates pulses on its output. The pulses are made in such a way that the average value of highs and lows is proportional to the PWM input. By filtering the pulses, we obtain an analog value proportional to the PWM input.

A PWM input can be of any width. Most common values are 8-bits and 16-bits.
A PWM output is just one-bit wide.

PWMs are also widely used with microcontrollers; see for example AVR131 from Atmel.

Accumulator

The simplest way to create a PWM signal is to use an hardware accumulator.
Every time the accumulator overflows, output a '1'. Otherwise output a '0'.

That's very easily done in an FPGA.

Verilog
module PWM(clk, PWM_in, PWM_out);
input clk;
input [7:0] PWM_in;
output PWM_out;

reg [8:0] PWM_accumulator;
always @(posedge clk) PWM_accumulator <= PWM_accumulator[7:0] + PWM_in;

assign PWM_out = PWM_accumulator[8];
endmodule

VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity PWM is
  port (
   clk : in std_logic;
   PWM_in : in std_logic_vector (7 downto 0) := "00000000";
   PWM_out : out std_logic
  );
end PWM;

architecture PWM_arch of PWM is
  signal  PWM_Accumulator : std_logic_vector(8 downto 0);
begin
  process(clk, PWM_in)
  begin
    if rising_edge(clk) then      
      PWM_Accumulator  <=  ("0" & PWM_Accumulator(7 downto 0)) + ("0" & PWM_in);
    end if;
  end process;

  PWM_out <= PWM_Accumulator(8);
end PWM_arch;

The higher the "data-in" value, the faster the accumulator overflows ("PWM_accumulator[8]"), and the more frequent are the output "1"s. Just what we want.

The more classical way to create the PWM output would be to use a counter and a comparator. But that requires more logic and it is tougher on the low-pass filter when "PWM_in" is wide. So the accumulator approach is better.

One-bit DAC

To create the analog output, we smooth out the pulses by connecting a low-pass filter to the output of the PWM. If the frequency used to generate the pulses is much higher than the DAC output (like will be the case here), a very simple low-pass RC filter is enough.

Playing an MP3

The first step is to decode the MP3. The decoded MP3 data is called "PCM" data.

To keep things simple, we send the PCM data through the serial port of the PC to the FPGA. The maximum rate possible through the serial port is 115200 baud (i.e. about 11.5 KBytes per second), so the music has to be down-sampled to 11KHz 8 bits. All that is easily done by a PC. Here's the software used (with source code).

Now is time to connect a speaker to the FPGA. There are 3 basic ways to do it.



Finally, we modify the HDL code so that the PWM data input comes from the serial port.

module PWM(clk, RxD, PWM_out);
input clk, RxD;
output PWM_out;

wire RxD_data_ready;
wire [7:0] RxD_data;
async_receiver deserialer(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data));

reg [7:0] RxD_data_reg;
always @(posedge clk) if(RxD_data_ready) RxD_data_reg <= RxD_data;

////////////////////////////////////////////////////////////////////////////
reg [8:0] PWM_accumulator;
always @(posedge clk) PWM_accumulator <= PWM_accumulator[7:0] + RxD_data_reg;

assign PWM_out = PWM_accumulator[8];
endmodule

That's it! Listen to your favorite MP3 through an FPGA.

What's next?

Digital audio is a vast topic.
Learn about:






This page was last updated on February 05 2008.