AUTOMATIXARON
DAC on an FPGA
Aim of the tutorial:
In this tutorial, we will design a pulse width modulated (PWM) signal generator which will serve as the base of a Digital to Analog converter.
Prerequisites:
Previous FPGA tutorials, and some analog electronics/filter design knowledge.
DAC with PWM:
In a digital world with binary logic, we have only two voltage levels 0 or ground, and 1 or the supply voltage. The question is how to create a somewhat continuous time and continuous amplitude signal with a system that works in discrete time, and generates signals with discrete, binary amplitudes?
​
Let's consider a periodic pulse with a period T, the high rail is Vdd, the low rail is 0, and the on-time (high time) is ton.

We can calculate the average of the periodic pulse by integrating the value on the required interval, and by dividing the result by the interval length:

We only need to integrate the signal from 0 to T since it's periodic, and the second portion of the integral (in [ton, T] interval) is 0, so the simplified formula is:

We can see that the average signal depends on the amplitude, the period, and the on-time. The amplitude is fixed, the period could be variable, but it would introduce nonlinearities in the system - and the implementation would require more complex hardware. The on-time, on the other hand, can be variable, it creates a linear function, and it needs a simple device. The ton/T fraction is the duty cycle of this PWM signal.
​
Now that we saw how we could create a variable signal by averaging periodic pulses, we only need to implement said PWM generator, and the integrator.
DAC requirements:
We want a DAC with a resolution of 8 bits, and with a maximum output frequency of 10kHz.
​
If fout = 10kHz, the PWM frequency should be at least ten times faster. We will set fpwm = 20fout. This means that the counter must reach a full cycle in a 1/fpwm period. The clock frequency of our 8 bit counter can be calculated with fcnt = 256fpwm, which, in our case, is fcnt = 51.2MHz.
​
The averager must pass the signal unmodified from the 0-10kHz interval, and cut everything from 10kHz upwards.
The averager circuit:
Let's consider a simple series RC circuit, where the input voltage is parallel to the RC circuit, and the output is the voltage across the capacitor.
The capacitor has a complex impedance of 1/(sC) in s-domain, so the relation between Uin, and Uout can be found by applying Kirchhoff's law to the complex circuit:

The time domain equivalent is:


This clearly shows that the circuit has an averaging/low-pass effect, and we can set the cutting frequency by choosing the C capacitor, and matching the resistor from the formula R = 1/(2*pi*fc*C).
If we want to increase the cutting steepness of the filter, we could chain another RC circuit to the previous one, or we could add an inductor.

The inductor has a complex impedance of sL in s-domain, so the relation between Uin and Uout can be found by applying Kirchhoff's law to the complex circuit:

We can design the components based on this equation, by first choosing a capacitor (or inductance) value, setting the cutting frequency fc. We can calculate the inductance (capacity) with the formula fc = 1/(2*pi*sqrt(LC)). We give a value for the resistor by choosing a damping ratio, and by using the formula xi = R*sqrt(C/L)/2.
​
In our case fc = 10kHz, and the damping is xi = 1, so the component values are R = 14Ω, L = 100uH, C = 2.2uF - Note the R contains the internal resistance of the inductor too (my inductor conveniently has a resistance of 4Ω).

I've created the filter circuit with the components mentioned above. I've used a swept sine wave signal with an amplitude of 3.3V, and a frequency range from 1Hz to 500kHz with logarithmic sweep time.
​
We can see on the oscilloscope figure that the output (orange) has a nice low-pass characteristic just as we wanted.
The PWM design:
For starters let's consider two analogue signals: a ramp signal from 0V to 1V, and a saw-tooth wave with 0.5V amplitude, and a DC offset of 0.5V. What would we get, if we would feed these signals into an analogue comparator?

We get the PWM signal by comparing the saw-tooth signal (-) to the ramp function (+), as the figure on the left shows.
​
In an FPGA we don't have analogue components, so the saw-tooth signal is replaced by an 8bit up-counter, the ramp signal is replaced by a register which stores the duty cycle value, and the analogue comparator has a digital equivalent.
I've created the Verilog design of the 8bit PWM model (figure on the right). This is the simplest PWM generator one can make. I've set the clock to 1ns in the simulation, and I've changed the duty cycle 0x40, 0x80, 0xFA after 256ns (figure below text).
And we encountered the first problem with the simple PWM generator: glitching - we can give a new duty value only when the counter overflows.
​
Let's try to remove these glitches from our design.

The glitch removal is simple, we use a register to store the duty cycle, and we only update the value, when the counter is reset.
​
We could add buttons, or rotary encoders to test different duty cycle values, or we could create something more exciting — a sine wave with 8b depth.
The simplest way to create an arbitrary periodic signal is to use Look-up Tables (LUTs). The LUT contains the data for the signal - this will be implemented as a ROM, for which the address bus will be driven by a counter.
The top module will use the internal oscillator with 53.2MHz frequency. It will contain a frequency divider to 100Hz for the sine wave module - this frequency will be divided further by the number of values in the LUT. Finally, we can connect the output of the sine generator to the input of the PWM generator. The image below shows the implemented design.

If we take a look at the output of our design on the FPGA with an oscilloscope we can see:

Here we can see that the frequency is constant, and the duty cycle changes in a periodic manner - this is expected.
​
We can also see some distortions in the signal, but that is only an impedance matching problem.
​
Now we can connect the previously created second-order low-pass filter circuit.

And the anticipated 8b sine signal appeared.
​
Looking at this image a few questions could arise:
Why is the half value section twice as long as the others?
Because I've included twice the value 128, once at the start, and once at the end.
​
Could we reduce distortion created by the discrete amplitude changes?
Yes, we could, by increasing the LUT values.
Could we simplify this LUT while maintaining a decent signal shape?
We could. The sine wave has some nice properties, which enable us to use only a quarter LUT. In that case, we need an up-down counter and a sign changer. We could also use a CORDIC algorithm.
​
Are there other implementations for generating analogue signals in FPGA?
There are. One example is the sigma-delta or delta-sigma modulation, which we will look at in an upcoming tutorial in the form of an ADC example.
​
As always the full example files are uploaded to the git.