To download NI software, including the products shown below, visit ni.com/downloads.
This document discusses a state machine implementation of the SPI (Serial Peripheral Interface) digital communication protocol using LabVIEW and the LabVIEW FPGA Module. This implementation has two components: the SPI protocol implemented in LabVIEW FPGA and a LabVIEW host interface to communicate with the FPGA from a host PC or real-time controller. The architecture allows for multiple SPI ports to be controlled from a single host program while still allowing customization of FPGA VI for other data acquisition and processing. The implementation does not use any DMA (Direct Memory Access) channels, allowing use of the NI Scan Engine and RIO Scan Interface and other high speed/high volume data transfer between the FPGA and host.
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.
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.
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.
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.
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.
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.
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 Intelligent DAQ target, the host VI should be moved to the My Computer target in the project.
The items from the FPGA portion of the project that must be copied are the top-level FPGA VI and the target-scoped FPGA FIFOs. New FPGA I/O will need to be added to the project so references can be created for the SPI ports to communicate on the correct lines. If the FPGA I/O names are different than those in the example, the FPGA I/O constant inputs to the port VIs will need to be changed to match the new project’s FPGA I/O.
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.
For more examples of using SPI with LabVIEW FPGA see SPI Bus communication Example Using LabVIEW FPGA .
Example code from the Example Code Exchange in the NI Community is licensed with the MIT license.
Is anyone aware of example code for implementing an SPI slave in LabVIEW FPGA?
Have you had a look at this?:
I have trouble to understand the code to select chip in case of "Set CS"
Can somebody to help figure it out?
Thanks and Regards,
In case of "Initialize", e.g. if Total Bytes is 3 Total Bits is 8
Then both MOSI Byte Index and MISO Byte Index will be -9
Is this right?