1. Download SPI IP
2. SPI Digital Communication
SPI is a commonly used communication protocol for both integrated circuit communication and embedded sensors. The protocol operates in full duplex with a single master and multiple slaves per port. This allows the flexibility of communicating with several different devices through a single port where each device can have its own clock rate and command set.
See Understanding the SPI Bus with NI LabVIEW for more information on the SPI communication protocol.
3. FPGA Implementation
The SPI bus in this document is implemented using LabVIEW FPGA to perform the bus mastering and clocking signals. A single-cycle timed loop (SCTL) is used to perform each step of communication between the SPI master (LabVIEW FPGA VI) and up to 8 slave devices per port. Multiple ports can be created using the same FPGA VI and interface to a host program.
The bus is encapsulated in a higher level LabVIEW FPGA VI, which utilizes a state machine to perform intermediate communication between each SPI port and a host interface. This VI synchronizes the host interface and SPI ports and multiplexes data sent from the host to a specified port.
4. Bus Implementation
State machines are used to execute a particular action and then determine which state to transition to. Because SPI has a specific order in which bus transitions happen, a state machine is a good choice for implementing the protocol. Each state completes one portion of the communication then transitions to the next step in order.
When a command has been received by the SPI engine on the FPGA, one cycle of communication begins. Each byte is read from a VI-Scoped FIFO with data passed from the multiplexer and stored as a boolean array on the FPGA.
Figure 1. Read data to transfer from FIFO.
After receiving the data to be sent to SPI bus, the chip select (CS) line designating the device to be written to is asserted. When the device’s chip select line is asserted, it becomes ready to receive and send data one bit at a time. Only one chip select line can be active at any point during execution. The CS lines are implemented as integers written to digital I/O ports on the FPGA hardware. Each CS port can address 8 individual slaves.
Figure 2. Set Chip Select.
Each device will require a clock signal (SCLK) to be generated by the FPGA master. The rate of this clock may not be consistent for all devices on the port, so the clock rate can be set before each data transmission cycle. Once the chip select has been asserted, both the master and slave are ready for data transmission. The state machine transitions to a wait state and stays in this state long enough to account for the selected clock rate.
When the delay has completed, the first clock signal should be generated to begin data transfer. SPI devices can require one of four clock modes for transmission. These modes are dependent on the clock polarity (CPOL) and clock phase (CPHA). CPOL specifies whether the idle state of the clock is a logic low or logic high (0 or 1 respectively). CPHA designates whether data is clocked in/out on the first edge or second edge (0 or 1 respectively).
When the clock is set, it transitions from its idle state to the active state. If CPHA is 0, data is also clocked during this transition. If CPHA is 1, data is clocked when SCLK is reset from active to idle. Between setting and resetting the SCLK, another waiting state occurs to ensure the correct clock rate. These states are repeated in order for every bit that must be sent.
Figure 3. Set Clock.
After each byte is transferred, the data received by the VI from the slave device is placed into another VI-Scoped FIFO for transfer back to the host. If all the data sent to the state machine has been sent, the chip select line is reset to the inactive state and the program waits for the next command. If the data is more than one byte, a transition back to the Set Clock state occurs and the rest of the data is transferred.
Figure 4. Write data received from slave to FIFO.
Apart from the SPI protocol, the VI for each port also has a configuration state. When the command sent from the host is Configure, SCLK rate, CPOL, CPHA, and the port number are read and used for the next data transfer sequence.
5. FPGA Multiplexer
To allow instantiation of multiple ports with minimal configuration, an intermediate FPGA VI is used to communicate between the SPI port VI and the host VI. This VI consists of a state machine managing host/FPGA handshaking and multiplexing of data to the correct port.
The host is responsible for communicating which state the multiplexer should execute. The multiplexer sits in an idle state waiting for a start command from the host. When that start command is received, the multiplexer writes the port the data is meant for to a global variable accessible by each port. Before beginning execution, each port VI checks if it is the designated port.
After setting the port information, the multiplexer checks the command sent from the host. If the command is Configure, CPOL, CPHA, CS, and the total number of bytes are written to global variables to prepare for the next data transfer. If the command is Read/Write SPI, data received from the host is passed to a target-scoped FIFO accessible by each port. This data is transferred from the target-scoped FIFO to the VI-Scoped FIFO of the intended port to be passed to the SPI bus in the Write and Read states.
Figure 5. Multiplexer communication state machine with one port.
Because the multiplexer sets which port the data is destined for, only one port can be transferring data at a time, even though multiple ports are available. Once all data for one port has been transferred, the next port can begin its transmission sequence.
6. Host Interface
A LabVIEW interface to the SPI communication state machine has been created to facilitate simple transfer of data between a host PC or real-time controller and the FPGA multiplexer. With this high-level API, multiple SPI ports can be instantiated alongside any other LabVIEW FPGA code needed for an application.
The API consists of two VIs: FPGA SPI_Configure and FPGA SPI_Read Write. FPGA SPI_Configure sets SCLK rate, CPOL, CPHA, CS, and the port to be used. This information is passed to the FPGA multiplexer with the Configure command and stored in the FPGA global variable.
FPGA SPI_Read Write takes a total number of bits to be transferred and the port for which that data is designated. An array of U8 bytes is passed to the VI and an array of the same size is returned. All of the handshaking and data transfer between the host and the FPGA is encapsulated in these VIs.
Figure 6. Host API.
If other FPGA code is to be used alongside the SPI bus, the SPI Comm Loop from the FPGA multiplexer and all front panel controls will need present in the top-level VI. Without these, the host API will need to be modified to communicate with front panel controls with the correct labels. The FPGA reference created with the Open FPGA Reference VI will also need to be bound to the FPGA Reference.ctl typedef used by the API.
An example program included with the FPGA core and host API shows how to write and read data from multiple ports. Each time the Write button is pressed, the data in Write Data is sent to the FPGA and Read Data is returned from the SPI bus. Between writes, the port and configuration data can be changed.
Figure 7. Example that writes to multiple ports.
7. Adding Multiple SPI Ports
Adding multiple SPI ports to the FPGA is relatively simple. In the project, new FPGA I/O must be added for the SCLK, MOSI, MISO, and CS for the new port. No other project configuration is necessary.
Figure 8. Project configuration for multiple ports.
In the FPGA SPI_FPGA Top Level VI, all that is needed is to add another instance of the FPGA SPI_SPI Port VI and add inputs for port number and the FPGA I/O to be used for SCLK, MOSI, MISO, and CS. The SPI Comm Loop in the multiplexer VI handles passing data to and from the new port.
Figure 9. Multiple ports in FPGA SPI_FPGA Top Level VI.
8. Changing FPGA Targets
If this example needs to be compiled and run on a different target, the host VI and FPGA Reference should be copied to the non-FPGA part of the new target. For CompactRIO and Single-Board RIO, this would be the RT controller. If moving to an R Series multifunction RIO target, the host VI should be moved to the My Computer target in the project.
There are a couple things you will need to do to move this bus to a different target.
- The first thing you will need to do is create a new target in the project or create a new project with the target type you want to compile for. You will need to copy the FIFOs and any IO that you are going to use under this target.
- The IO of the Single-Board RIO device in the example project will not be the same as the IO types on the R Series board. Specifically, the IO lines have different properties and methods associated with them and the PORTs of an sbRIO are 10 bits wide while the R Series ports are 8 bits. To rectify this:
- Make sure you have the IO set up for Boolean values for SCLK, MOSI, and MISO and a Port set for CS.
- Open FPGA SPI_SPI_Port.vi and change the IO type of each of the controls in the FPGA IO cluster. To do this, right-click each IO Name control and choose Configure I/O Type… Choose the corresponding I/O in your project from the tree on the left side of the dialog that will appear and then click Replace All. This will set the IO type for that control to match the type defined in your project. Save the port VI.
- In the top-level FPGA VI, locate all of the port subVIs. For each of these port VIs, delete the FPGA IO cluster constant and then right-click on the FPGA IO terminal and Create»Constant. This will create a new cluster with the correct IO types and you should be able to select the FPGA IO contained within your project.
- Save and recompile the top-level FPGA VI.
- In the host VI, Configure the Open FPGA VI Reference to be bound to either the newly compiled bitfile or the top-level FPGA VI under your new target. Make sure this reference is bound to the FPGA Reference.ctl file in the project. All the subVIs share this typedef control, so if you are binding to a different control, the subVIs will need to be updated manually.
Figure 10. Requirements for changing the FPGA target.
To use the host API, when Open FPGA VI Reference is called in the host VI, the reference should be bound to the FPGA Reference control in the project. This will propagate the target information to the subVIs of the host API.
The state machine implementation of the SPI protocol has been discussed, including the bus implementation, multiplexer behavior and usage of a host API. The attached code includes the FPGA SPI bus and host API.