Skip navigation
2012

I have developed the following design as the framework for the bus interface of USB2 interfaced FPGA based instruments.

 

This firmware in intended to aid the design of instrumentation using the Morph-IC-II module and other fpga systems interfaced to the USB2 bus using FTDI's FT2232H interface chip.

 

It has been in use in prototype form in my NMR laboratory as the heart of a number of instruments (contributing to a number of papers), and would I believe aid the design of other FPGA based instrumenatation.

 

Here is one of these FPGA based instruments (in 24/24 7/7 use in the lab, but still in prototype) : 5 Channel Precision 24bit DVM intended for thermocouple use.

http://www.lab-tools.com/instrumentation/graphics/DVM-frontpanel_running.jpg

The interface is designed to facilitate the creation of such instruments, and in particular features USB bus interface registers, for instrument configuration, control, status and medium-rate data transfer.

But it also now supports full USB2 byte-wide data pipes to/from the instrument modules.

 

I am currently implementing this design using the FTDIchip (http://www.ftdichip.com/) MORPH-IC-II FPGA module, which uses the FTDI FT2223H USB2 interface chip, but other FPGA systems can benefit, possibly using the FTDI FT2232H mini modules at the prototype stage.

 

For my NMR instrumentation and GUI front panels I use a version of the APL Array Programming Language : APLX (http://www.microapl.co.uk/), which can talk to the FTDI .dlls that interface to the FT2232H, but any other language that can talk to the .dlls can be used.

1_CardStac_USB-to-3-Sub-Module_Bin-Mem_1.jpg

The FTDI 2232H connects to the USB bus and for the data transfer interfaces to an fpga via the A-side octal register and 4 handshake lines (up to you if you use the B-side for programming, as in the MorphicII, but it does also go like the wind).

 

The first block "USB to Addressed Bus Interface" handles the protocol below, and transfers the USB info to three byte-wide busses : "Bank", "Address", "Data".

 

There can be multiple "Banks" of instruments.

 

Each "Instrument" within the fpga has a block of up to 256 byte-wide interface registers (that can of course be used in groups to build wide-word registers - I have used up to 128bit words), plus the streaming byte-wide data-pipe fed at full USB2 rate. These interface registers are used for instrument configuration, control, status and low-rate data transfer.

 

So that one can read or write single or multi-byte words to a particular instrument register/register group, one has to also give an 8-bit "Bank" address as well as the register address - if one omits "Bank" the software defaults to the 1st bank.

 

I use Apl for talking to the firmware - it is interactive which means rapid development, and is ideal for day-to-day use in the lab, controling the instrumentation and analysing the data. The functions run fast, as you can see in the YouTube videos. It talks directly to the .dll for the FTDI D2XX direct drivers, so one has full control over the USB data transfers and configuration. But you can use this firmware with any software that can talk to the .dlls ....

 

The "word" read/write commands are  :

   BnkAddrNb ezww Data                                          © (Data as Nb byte range integers)

   Res ← HndlBnk ezrw AddressRange                      © (or list) (interprets Res as N byte word)

 

For full USB2 rate transfer of vectors of data :

    BnkAddr morphic_vector_write DataVector                     ©  (Where Addr is normally the block start address)

    DataVector  ← BnkAddr morphic_vector_read N             ©  (reads N byte vector)

 

For this vector data transfer I use synchronous FIFO mode in the FT223H for the data channel:

  1. 3.4.2 FT2232H pins used in an FT245 Style Synchronous FIFO Interface :  ADBUS[7:0]  I/O  D7 to D0 bidirectional FIFO data.

I attach a cropped SignalTapII recording, being clocked at 120MHz.

clk_ftdi is a 60 MHz Clock driven from the FTDI chip.

The data being sent is counting up, the remaining byte-count counting down with each byte.

For medium bursts SignalTapII shows the data being clocked in or out at every clock, then there is a short, slightly variable gap between the bursts.

015_Read_OK_v1_crop.jpg

As a test and example of functionality and data transfer speed, I created the following video :

 

Lab-Tools Morphic-II Fast Data Transfer to Altera Cyclone II.

Demonstrating fast block transfer of vectors of data to and from Lab-Tools modular instrumentation over the USB2 interface, using the Morphic II module containing an Altera Cyclone II Field Programmable Gate Array (FPGA).

With up-transfers of 1k word x 16 bit data (being scaled and then converted from 64bit floating-point data in the Apl interpreter), followed by a USB command to read the same block back, convert it to 64-bit floating point, error checked, displayed on a graph, and all timed, a 1.6GHz Windows 7 64bit lap-top (running a 32bit Apl interpreter) can do this continually every 50ms, with transfer delay jitter just starting to go up. Down at 20 ms the system is just not keeping up. The YouTube video was captured at 10 Hz frame rate, so I used 100ms repeat time for this.

 

 

The data-pipe is read/write and normally (as in this case) would go to a memory, but in my second YouTube example it feeds a peripheral with a specific block of firmware that is able to eat the bytes at full USB2 rate :

 

2012-04-07 - Lab-Tools FPGA Binning Co-processor for Apl

This video describes an example USB2 interfaced FPGA co-processor for use with the array processing language Apl.

This co-processor implements a single scientific algorithm : Binning.

As such it is an example of what can be achieved using field-programmable gate array co-processors with Apl.

The FPGA module used is the Morphic II, available from FTDI. This module has the advantage that it does not require a USB Blaster to program it.

Software, firmware and modules are also available from Lab-Tools Ltd, who are also developing a series of credit-card sized interface and data I/O modules to interface with them.

The FPGA co-processor is accessed and controlled from AplX, a version of the array processing language Apl, from Micro-Apl.



I hope to be ready to make this firmware available for download shortly, as soon as the pricing and licensing details are finalised.

But I would like to discuss the concept here and see if people think I am on a useful track.


For further details see : http://www.lab-tools.com/instrumentation .

Instrumentation modules including an analogue transient capture/average/process module, based on this firmware, are currently also being finalised.

 

This firmware in intended to aid the design of instrumentation using the Morph-IC-II module and other fpga systems interfaced to the USB2 bus using FTDI's FT2232H interface chip.

 

So now that I have a working display I guess I had better get started on the SPI temperature sensor. The design process here is a little different as we are not just passing data or signals from one block to the next. What we need is a sub block that can process a few instruction to pull in the data from the SPI device.


max31855-breakout-board-onl.jpg


 

Now in some cases you may consider a state machine, but no. we first need to look at and think about what the SPI device delivers. The device I’ll be using is one I got from TAUTIChttp://store.tautic.com/ and uses a MAX31855http://datasheets.maxim-ic.com/en/ds/MAX31855.pdf. On page 5 we can the data pattern used that comes out of the device. It’s actually a very basic device to interface to. The chip select line goes low (CS) and then as the clock line is driven one bit of data comes out the device. There is a total of 32 bits (31 to 0) that need clocking out.


 

So the design can be done using a counter that counts out a number steps and during each step a different action takes place. We will need a few clock cycles to drive the CS line high, reseting the SPI device back to the start. Lets say we need 36 steps, that 32 for the data and a extra 4 for setting up and CS line etc. That’s more than enough.


 

So the code we write is going to need two sections, one that is a counter from 0 to 35 and another to drive the CS line and store data. It also needs to push the data out at the very end into a 32 bit wide signal what we can use. The counter is easy but what about the other sections?


 

Well we can start by just looking at the counter. When the counter has a value of 0 or 1 we can set the CS line high.  At count 2 we can drive the CS line low knowing that on the next clock we will get our first bit out. So thats count value 3 to 34 that will have valid data in them. These can be placed into a buffer that is shifted one bit every time too. hence data in collected one bit at a time. Once we get to count 34 we can ‘register’ the buffer into our 32 bit output. Registering it means its gets locked in so the buffer can be used again without effecting the output. It's actually nothing more that a big 32 bit latch. Also on count 34 and also during count 35 we will drive the CS line high and because our counter resets itself back to 0 when it sees 35, the whole thing starts all over again with no outside help. This is useful as we want a continuous flow of data from the temperature device.


 

Now this so far has all sounded quite a lot to do but as you will now see is not very much code at all.


 

 

 

 

 

entity SPI_Temp_Master is

    Port ( clk_i : in  STD_LOGIC;

            SPI_clk : out STD_LOGIC;

           SPI_cs : out  STD_LOGIC;

           SPI_Data_In : in  STD_LOGIC;

           SPI_Data_Out : out  STD_LOGIC_VECTOR (31 downto 0));

end SPI_Temp_Master;


 

Starting with the entity for this block we can see we have a incoming clock. We then have the interface to the SPI device which is a SPI_clk (clock output), SPI_cs (chip select) and SPI_Data_In which is the serial data from the device. Then we have the 32 bit output from the block which is called SPI_Data_Out.


 

signal SPI_rx_buff: std_logic_vector(31 downto 0);

signal SPI_cnt    : std_logic_vector(5 downto 0) := B"000000";

signal SPI_cs_sig : std_logic := '0';


 

The above signals will be used internally to the block as consist of the 32 bit buffer, our 6 bit counter (note its std_logic_vector) and then a chip select signal thats internal.


 

I then do a  bit of quick driving of the CS and clock line by having the following two lines.


 

SPI_cs <= SPI_cs_sig;

SPI_clk <= clk_i;


 

Next we need our counter noting that it resets when it sees a value of 35, back to 0. I have used the counter as a std_logic_vector as it fits with the rest of the design we have seen. Keeping consistent. People will be critical of me using the IEEE.STD_LOGIC_UNSIGNED.ALL library but for now this is about keeping it simple so I don't have to start explaining the whole which library debate. Just for now except that IEEE.STD_LOGIC_UNSIGNED.ALL is not great, but I’m using it anyway for now.

 

So our counter will look like this.


 

if SPI_cnt = 35 then

SPI_cnt <= (others => '0');

else

    SPI_cnt <= SPI_cnt + 1;

end if;


 

Notice that I’ve not included the process around this as yet, that because I’ll be wrapping the counter and the SPI block inside one process. This other block is as follows and should be easy to read.


 

if SPI_cnt < 2 then

    SPI_cs_sig <= '0';

    SPI_rx_buff <= (others => '0');

elsif SPI_cnt < 34 then

    SPI_rx_buff(31 downto 1) <= SPI_rx_buff(30 downto 0);

    SPI_rx_buff(0) <= SPI_Data_In;

    SPI_cs_sig <= '0';

elsif SPI_cnt = 34 then

    SPI_Data_Out(31 downto 0) <= SPI_rx_buff(31 downto 0);

    SPI_cs_sig <= '1';

else

    SPI_cs_sig <= '1';

end if;


 

So as you can see this block of code is lot shorter than my explanation.! The full file is included at the end of this post.

 

So we now have a working SPI unit that reads data out of our SPI device and pushes the result out once every 35 clock cycles, that’s quick. Our next stage is to plug this SPI block into our display logic we have done so far to get the final project working. That however will have to wait till next time.


 

Thanks

Paul (@monpjc)

Filter Blog

By date: By tag: