### Super SPI Master Verilog Module With Burst Capability

This robust SPI master module allows fully operational SPI reading and writing as well as burst read/write capability. An accompanying burst read capture state machine is provided as well for complete drop in applications into any FPGA project. Verilog source and test bench can be found on my Github page (direct link here).

SPI Master Top Level Overview

SPI Master Ports

Name

Type

Width

Direction

Description

i_clk

wire

1

input

Main system clock (note: not the actual SPI SCLK)

i_rst

wire

1

input

Active high reset

i_data

wire

DATA_WIDTH

input

Data to be written during data phase of SPI transaction

wire

input

i_rw

wire

1

input

i_rw = 0 will signify a WRITE request, i_rw = 1 will signify a READ request

i_enable

wire

1

input

Active high enable signal that begins a transaction if master is not busy

i_burst_enable

wire

1

input

Active high burst enable signal, if high before a transaction the transaction will be a burst transaction

i_burst_count

wire

16

input

Number of burst transactions to execute

i_divider

wire

16

input

Divider value used to calculate the SPI SCLK

i_cpha

wire

1

input

SPI CPHA config

i_cpol

wire

1

input

SPI CPOL config

i_miso

wire

1

input

SPI MISO connection

o_sclk

wire

1

input

SPI SCLK connection

wire

DATA_WIDTH

output

o_busy

reg

1

output

State machine busy flag (active high)

o_ss

reg

1

output

SPI SS/CS connection

o_mosi

reg

1

output

SPI MOSI connection

reg

output

Data received from slave (full-word) (latched output)

reg

1

output

Active high pulsed signal indicating wether a single word of a longer burst read has been read and latched out

o_burst_write_word_request

reg

1

output

Active high signal indicating next word for burst write should be fed into the i_data input

Parameters

Name

Typical Value

DATA_WIDTH

8/16

7/15

Clock Divider
This module is designed to utilize the same system clock as the rest of your design. By doing this we can minimize situations where they could be potential inter clock path timing violations. However, in most cases such clocks are in the megahertz range and running SPI transactions at those rates are unrealistic, therefore we need a mechanism to divide our clock down. To do so we will be using a simple counter-based divider. The “divider_counter” is a 16 bit register that is continuously incremented. When it is equal to the “i_divider” value, our divider tick wire will pulse high, and the counter will return to zero. This divider tick pulse will signal the main state machine to execute an operation. It is important to still drive the main state machine’s flip flops at the system clock and have the divider tick be a gate keeper with an if statement. Although driving the flip flops using the divider register directly as a clock will look fine in simulation, the implemented design will have erratic behavior because FPGAs typically use dedicated optimized paths for clocks that are different from register paths. The SPI SCLK frequency can be calculated using the equation below.

$$f_{SCLK} = \frac{f_{CLK}}{8+4(divider-1)}$$

SPI Master State Machine Overview

This module operates as a finite state machine with a state for each portion of a typical SPI transaction. The FSM defaults to the IDLE state where it waits for the appropriate control signals to execute a read or write SPI transaction. A flow chart for the FSM and a detailed overview of each state is shown below.

S_IDLE (8’h00)

Stores the following inputs into internal registers: i_rw, i_cpha, i_cpol, i_burst_count, i_addr, i_data. These stored values will be used throughout the transaction.

S_SET_SS (8'h01)

Sets slave select slave select low. If CPHA = 0, it is important to shift the first bit of our message out during the this state before going to the S_TRANSMIT_ADDR state to meet setup time requirements.

Shift out the address of the SPI register we will be reading/writing to. This address includes the actual “rw” bit which will tell the slave whether we want to execute a read or a write. Data that is shifted out from the slave is also recorded in this state and will be available in the o_read_long_word output.

S_TRANSMIT_DATA (8’h03)

If a write was requested the master will begin shifting out the data to be written to the address during this state. Again, all data that is shifted out from the slave is still recorded during this state.

If in burst mode, the state machine will loop through this state until it has done writes equal to the value stored in the “burst_num” register. When shifting out the last bit of the current data to be written, the o_burst_write_word_request flag of the state machine will go high. When this condition occurs, new data should be supplied to the i_data input. The state machine will store this when process_counter = 2 and will write this new data during the next iteration of the burst write sequence. This scheme allows an external state machine ¼ SCLK period in order to latch in new words to be written during the burst sequence.

If a read was requested the master will read data during this state. Throughout the process, the master will continuously shift out 1’b1 on the MOSI line.

If in burst mode, the state machine will loop through this state until it has done reads equal to the value stored in the “burst_num” register.  When the last bit of the current iteration’s word is read the o_burst_read_data_valid output is pulsed high for ¼ SCLK period. During this time, the external burst read state machine must process the read data that is stored in the o_read_word output.
S_STOP (8’h05)

Here SS is set high again to end the SPI transaction. If CPHA = 1 the last bit of the word is read when process_counter  = 0. This is done to meeting SPI timing requirements much like how the first bit is transmitted in the S_IDLE state when CPHA = 0.

SPI Master Recommended Usage

When using this master the "o_busy" is the cirtical flag to monitor in order to easily keep track of what the master is doing. It is recommend to frist set the configruation inputs of the master. This includes i_data, i_addr, i_rw,, i_burst_count, i_cpha, i_cpol and i_divider. Then query the o_busy signal, once it is low set i_enable (and i_burst_enable if a burst is required) high. Once o_busy output goes high, we set i_enable (and i_burst_enable if a burst is required)  low. Finally once o_busy is low again, we know the transaction has been completed. Although you can technically set the configuration inputs at the same time as the enable signal given they are in the same clock domain, it is best to allow at least a clock cycle of setup time for smooth operation across all conditions. An example of a recommended read and write operation can be seen below. These are snapshots of the full test bench available on Github. If a read operation was requested, the read word will be available via the o_miso_data output as well as the o_read_long_word output for a full word. If a burst write is requested, it is important to monitor the o_burst_write_word_request output. When it pulses high, a new word must be supplied to the i_miso input before o_burst_write_word_request goes low again.

SPI Signal and Timing Requirements

This master is capable of operating in all four available SPI modes. Below are examples of how a master will behave for each of the four possible SPI configuration parameters.

CPHA = 0, CPOL = 0

1.    MOSI/MISO setup half SCLK period before RISING edge of SCLK.

2.    Data is sampled after RISING edge of SCLK

3.    SS should be set low half SCLK period before RISING edge of first SCLK pulse

4.    SS should be set high half SCLK period after FALLING edge of last SCLK pulse

CPHA = 0, CPOL = 1

1.    MOSI/MISO setup half SCLK period before FALLING edge of SCLK.

2.    Data is sampled after FALLING edge of SCLK

3.    SS should be set low half SCLK period before the first falling edge of SCLK

4.    SS should be set high half SCLK period after the last RISING edge of SCLK

CPHA = 1, CPOL = 0

1.    MOSI/MISO setup half SCLK period before FALLING edge of SCLK.

2.    Data is sampled after FALLING edge of SCLK

3.    SS should be set low half SCLK period before RISING edge of first SCLK pulse

4.    SS should be set high half SCLK period after FALLING edge of last SCLK pulse

CPHA = 1, CPOL = 1

1.    MOSI/MISO setup half SCLK period before RISING edge of the following SCLK.

2.    Data is sampled after RISING edge of SCLK

3.    SS should be set low half SCLK period before FALLING edge of SCLK

4.    SS should be set high half SCLK period after last RISING edge of SCLK

Burst Read Capture FSM Top Level Overview

The burst read capture state machine is a module that is meant to support the SPI master during burst reads. It's purpose is to demonstrate how the master behaves during a burst read transaction and how to effectively store these burst reads into a memory in a timely fashion in order to guarantee all data is captured during a burst read transaction.

Name

Type

Width

Direction

Description

i_clk

wire

1

input

Main system clock

i_rst

wire

1

input

Active high reset

i_burst_count

wire

16

input

Number of burst reads to process from the SPI Master

i_burst_data_valid

wire

1

input

Pulsed active high signal signifying new SPI data can be processed from SPI Master

i_start

wire

1

input

Active high signal to start the capture FSM

i_spi_output_data

wire

16

input

Read SPI data from SPI Master

o_busy

reg

1

output

State machine busy flag (active high)

o_outbuf_we

reg

1

output

Active high memory write enable signal

o_outbuf_dat

reg

16

output

Data to write into memory

reg

16

output

Address of memory data is written to

Capture State Machine Overview

This module is a simple state machine with three states that is intended to be used in conjunction with the SPI master state machine. It will wait in the default idle state until it is armed with the start single. Once armed, the state machine will capture read data from the SPI master state machine and write them to an external memory module. A flow chart of the state machine and a detailed overview of each state can be found below.

S_IDLE (8’h00)

Waits for i_start to be high in order to start capturing burst read data. Stores i_burst_count into the internal burst_count register when i_start is high just in case it changes mid capture and sets o_busy flag high.
S_CAPTURE (8’h01)

Will wait until a positive edge of the i_burst_data_valid signal is detected. When this condition is met, the read SPI data will be written to memory and a counter will be incremented. This counter is used to keep track of the number of burst read values processed.

S_DONE (8’h02)

After all the burst data is read the busy flag will be set low and all miscellaneous signals will be reset here before looping back into the S_IDLE state.