Introduction

 

I have a small Brevia 2 development board [1] from Lattice Semiconductor, with one of their XP2-family FPGAs

on it, that I bought from Farnell a while back. Last year I got as far as doing these two blogs before getting

distracted and going off in quite different directions.

 

Booards and Boojums: Lattice XP2 Brevia2 Board: Part 1

Booards and Boojums: Lattice XP2 Brevia2 Board: Part 2: Count Those LEDs!

 

It's time to have another play with it.

 

Here's another picture of the board to give an idea of the size and what's on it. [The ruler is an old school

one and has an inch scale - sorry if you prefer metric measure. In metric, the board measures 76.2mm x

76.2mm.]

 

 

The FPGA on it is an LFXP2-5E part. It is the smallest member of the XP2 family with just 5K LUTs [Look-Up

Tables]. The evaluation board has several nice features to it, even though it's very simple: it can be

programmed through the USB connection, without needing a separate programmer, using the Lattice tools as they

understand the interface; the USB provides a second channel that can be used for RS232 communication between a

PC and the FPGA; and there are 0.1" headers that make prototyping of hand-wired daughterboards particularly

easy.

 

In complexity, it's roughly equivalent to the Spartan 6 part on the board that Randall is giving away at the

moment here:

 

Who is Interested in Experimenting with a Spartan-6 FPGA? If You Are, I have 2 Boards to Giveaway for Projects

 

There are several things I'd like to try with it. One of those is (inevitably!) to explore making a

specialized processor of some kind, but that would be too involved to start with so, instead, I thought I'd

have a go at using the board to generate some waveforms.

 

The Circuit

 

The development board doesn't have any analogue I/O so I need to add a DAC (Digital-to-Analogue Converter)

and, in order to keep it all nice and simple, I'm using one with an SPI bus interface from Microchip - an

MCP4821 part. It's 12 bits resolution, has an in-built 2.048V voltage reference and the output is buffered

with an op amp. It's available in an 8-pin DIL package which will make prototyping very easy.

 

Here's the circuit diagram I started with. There's not much to it really. The DAC can work on a supply up to

5V but I'm going to run it on the 3.3V from the Lattice board. I've got a 10uF electrolytic and a 100nF

ceramic on the supply (which the datasheet recommends) but, other than that, no further supply filtering; it

will be interesting to see how good a job the device does of rejecting supply noise.

 

 

The connector on the left is numbered to match connector J3 on the Brevia board, the numbers in brackets are

the FPGA IO pins which they connect to: I'll need those when I'm doing the code, so they're on there as a

reminder and to save me having to look it up.

 

I wired it up on a piece of stripboard; it looks like this:

 

[

It's all very minimal but it will enable me to look at how the DAC behaves before I do anything else with it.

 

Designing the FPGA

 

Before we go any further, a quick word about that heading. The word 'designing' is very deliberate and I chose

it carefully in preference to 'programming' or 'coding'. For all that I'm going to be using a language (VHDL)

that allows for varying degrees of abstraction, and utilising a tool-chain that has a least some similarities

to those used for coding sequential processors, at base what I'm doing here is hardware design.

 

For this blog, I'm going to be driving the DAC with a continually incrementing data value to generate a ramp

at the analogue output.

 

My starting point for doing this is to look at the datasheet for the MCP4821. That gives me this nice waveform

diagram with most of the information I need on it. It shows me the waveforms I'm going to need to generate

with the FPGA; I just need to refer to a few of the items in the AC timing tables and quickly read how those

control bits function and then I'll have a clear idea of where I'm going.

 

The SPI interface to the DAC is very simple: it's write-only, so I'm only going to be sending data to the

device and there is only one single 16-bit command which, very conveniently, combines the few control bits

with the 12-bit data value that will control the DAC and produce the output.

 

The clock input to the FPGA on the Brevia 2 board is 50MHz from a crystal oscillator. The DAC chip can't cope

with a clock of more than 20MHz on the SPI interface, so one way to go about this would be to make the SPI

clock a quarter of the FPGA clock rate, ie 12.5MHz. The 16-bit transfer then takes 1.28us.

 

For the moment I'm going to have a new DAC output every 10us (100k samples per second).

That then means a few counters for the timing and counting and probably a shift register to deal with the data

going out to the SPI data line (an alternative for the last might be a multiplexor).

 

Here's the VHDL I put together. It isn't necessarily very good VHDL. I learned to do this 'on the job' and

didn't have the time to learn properly from basics, so this isn't going to be an expert tutorial. Sorry that the formatting is such a mess.

 

------------------------------------------------------------------
--     ***** Waves_Test *****       --
--  Test of DAC                                                 --
------------------------------------------------------------------
-- JC 24th August 2019           --
------------------------------------------------------------------
-- Rev    Date      Comments         --
-- 01     25-01-18             --
------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity wave_test_con is port(
  clk_in:   in std_logic;        --- system clock in (50 MHz)
  --- DAC connections
  mcp4821_ncs: out std_logic;        --- DAC cs
  mcp4821_sck: out std_logic;        --- DAC sck
  mcp4821_sdi: out std_logic;        --- DAC sdi
  mcp4821_nldac: out std_logic;        --- DAC load
  --- misc control signals on evaluation board that it might be good to hold at fixed levels
  spi_cs:   out std_logic;        --- 
  hold_n:   out std_logic;        --- 
  sram_cen:  out std_logic;        --- 
  sram_oen:  out std_logic;        --- 
  sram_wen:  out std_logic;        --- 
  uart_tx:  out std_logic);        --- 
 
end wave_test_con;
architecture arch_wave_test of wave_test_con is
 
signal spi_send, spi_ncs, spi_ncs_del: std_logic;
signal waveform_count: std_logic_vector (11 downto 0);
signal spi_output_sr_bit_count: std_logic_vector (5 downto 0);
signal spi_output_sr: std_logic_vector (15 downto 0);
signal interval_count: std_logic_vector (8 downto 0);
begin
 -----------------------------------------------------------------------------------
 ---                    ---
 ---          ---
 ---                     --- 
 -----------------------------------------------------------------------------------
 wave_test_stuff: process (clk_in)
  begin
   if (clk_in'event and clk_in='1') then
  
    --- interval_count counts at the clock rate (50MHz)
    --- counts down by 500, so spi_send occurs every 10us (100kHz)
    if (interval_count(8 downto 0) = b"000000000") then    --- if zero
     interval_count(8 downto 0) <= b"111110011";      --- preset to 499
     spi_send <= '1';
    else
     interval_count(8 downto 0) <= interval_count(8 downto 0) - 1; --- count down
     spi_send <= '0';
    end if;
    --- spi ncs goes low when triggered by spi_send, goes hi again when bitcount reaches 31
    
    if (spi_send = '1') then
     spi_ncs <= '0';
    elsif (spi_output_sr_bit_count(5 downto 0) = b"111111") then
     spi_ncs <= '1';
    end if;
    
    spi_ncs_del <= spi_ncs;
    --- spi output bit count only counts when enable spi cs is low
    
    if (spi_ncs = '0') then
     spi_output_sr_bit_count(5 downto 0) <= spi_output_sr_bit_count(5 downto 0) + 1;
    else
     spi_output_sr_bit_count(5 downto 0) <= b"000000";
    end if;
    
    --- waveform count - this will be the data sent to the dac
    
    if (spi_send = '1') then
     waveform_count(11 downto 0) <= waveform_count(11 downto 0) + 1;
    end if;
    --- spi output shift register
    
    if (spi_send = '1') then            --- load...
     spi_output_sr(11 downto 0) <= waveform_count(11 downto 0);   ---   dac data
     spi_output_sr(15 downto 12) <= b"0011";        ---   dac control bits
    elsif (spi_ncs = '0' and spi_output_sr_bit_count(1 downto 0) = b"11") then   --- shift...
     spi_output_sr(15 downto 1) <= spi_output_sr(14 downto 0);   ---  the register contents
     spi_output_sr(0) <= '0';
    end if;
   end if;
  
   ---
   
   mcp4821_ncs <= spi_ncs;
   mcp4821_sck <= spi_output_sr_bit_count(1);
   mcp4821_sdi <= spi_output_sr(15);
   mcp4821_nldac <= '0';
  
   --- hold these device control pins at a fixed level to stop them flapping around
   spi_cs <= '1';
   hold_n <= '1';
   sram_cen <= '1';
   sram_oen <= '1';
   sram_wen <= '1';
   uart_tx <= '1';
  end process wave_test_stuff;
  
end arch_wave_test;

 

Now to test it and get an idea of how the DAC performs.

 

DAC Performance

 

First a quick look at the SPI clock waveform I'm generating. Here's what I saw the first time I tried it.

 

 

Ouch! Look at that overshoot and ringing. There's about 5 or 6cm between the FPGA and the DAC chip without

termination - along the track on the pcb, up the pin of the 0.1" pitch connector and then down a short length

of track on the stripboard -  and that's the result. What's the rise time? The scope says a bit over 1.7nS,

but that's meaningless because that's about the risetime of the oscilloscope itself; it could be faster, but

I'd never know. It also means that the waveform we see isn't necessarily an accurate reconstruction of what's

there, though I think we can take it as read that it overshoots nicely.

 

My first thought was to try terminating it in some way, but FPGAs usually have control over slew rate and

drive strength on the IO pins, so, after a quick scurry through the help material, I worked out I could select

a "SLOW" slew rate and a drive strength of 8mA (that was the lowest option for 3.3V CMOS) and tried that. This

was the outcome.

 

 

That's much more comfortable and will do me for what I need for this blog.

 

Here's the clock [yellow] in relation to the SPI ncs [blue] that I'm generating with the FPGA.

 

 

That shows that I've generated the right number of clocks (16) and the rising edges sit nicely within the chip

select active (low) period, as they should. So far, so good.

 

This next one shows the clock (yellow) relative to the SPI data (blue). The static control bits are in the

right places and have the intended values and we can see that the data values are changing.

 

 

this shows the relationship of the SPI clock edge to the data in more detail

 

 

I was lazy here and the data trace is using the flying earth lead on the probe, so the data doesn't look as

well behaved as the clock, though it is. Plenty of set-up and hold time there, and I managed to get the data

well away from the rising edge of the clock. If the Microchip part can't manage with that, I'll give up on it.

 

Now, moving attention to the output side, this is the Vout of the DAC chip

 

 

It works! I've got a ramp from zero to just above 2V at the right sort of repetition rate [1/(10uS x 4096) =

24.4141 Hz].

 

The portion from the maximum value down to the minimum value will allow me to look more closely at how the op

amp slews - here it is in more detail

 

 

The fall time is around 2.8us, which gives a slew rate of about 0.73V/us. That's faster than the datasheet

typical figure of 0.55V/us but their test is done with 100pF on the output which will be slowing it a bit.

 

This is what happens at the bottom, just above zero volts.

 

 

It makes a bit of a mess of things in the area up to 5mV. More on that a bit further down.

 

Next, I removed the counter that stepped the DAC to produce the ramp and substituted a fixed value of 2048.

That gave me an output level that, on a single shot, looked like this

 

 

That shows the noise [it's with the oscilloscope's 20MHz bandwidth filter on, so some of the high frequency

noise has been removed].

 

This next trace shows the same thing but with multiple traces and we can now see that there's some structure

in the noise. The trigger point is the end of the SPI chip select and is where the DAC gets loaded with its

new value. Just prior to that we are seeing a small amount of breakthrough from the SPI clock and data lines -

probably not surprising given that the analogue side is sharing a ground with the digital lines.

 

 

Whilst I had the output constant I measured the DC level. This for a DAC value of 2048 which should be half

scale, 1.024V.

 

 

After it's been on for a few minutes, that rises and levels out at 1.02436V. Yesterday, I was seeing values of

around 1.02440V.

 

It's not so good at full scale. Rather than 2.048V, it measures 2.0460, though that's much better than the

datasheet min and max values of 2.008V and 2.088V respectively suggest.

 

This next trace shows the DAC output after I'd hacked the VHDL to limit the range and step from 0 up to 63 and

back down again to give a triangle waveform rather than the sawtooth.

 

 

That allowed me to see better how the internal op amp that buffers the DAC output behaves at the bottom of the

range. According to the datasheet it can get within 10mV of the rail but this suggests it is behaving

reasonably from about 5mV upwards and only gets into trouble below that. [That's with a 4.7k load resistor to

ground, which maybe helps the op amp a little.]

 

This particular trace allows the individual steps of the DAC (0.5mV) to show through. This is with the 20MHz

bandwidth filter of the oscilloscope enabled.

 

That's it for the moment. If I have some spare time, I may have a go at generating some more complex waveforms.

 

If you found this interesting and would like to see more blogs I've written, a list can be found here: jc2048 Blog Index

 

[1] http://www.latticesemi.com/en/Products/DevelopmentBoardsAndKits/LatticeXP2Brevia2DevelopmentKit.aspx