# SPI peripheral

The spi perihperal made here is mainly meant to be used as a peripheral for
a microcontroller. But it can be used as standalone in fpga, with driving
logic. Its interface of reception and transmission should be quite a standard
way - there are valid and ready signals.

Currently master is supported only. The maximum speed to drive the peripheral
at is `f_clk / 2`, where clk is the input clock frequency. The clk is used
as the clock throughout the design, no clock domain crossing is introduced.

## Design structure

The blocks in the design are quite simple.

There is `spi_peripheral` that wraps `spi_masterslave`.
The `spi_masterslave` is meant to hold both slave and master
controllers. Currently only master controller is implemented.

Apart from that, there is `spi_clkgen` that generates the clock,
dividing by selected divider. `spi_clkmon` for monitoring the clock,
as a slave. Then `shift_register` for transmitting and receiving the data.
Lastly, there is `reg`, as a register for transmitting the data from shift
register at correct shifting edge.

## Inputs/Outputs

### SPI peripheral

For each spi signal (miso, mosi, sck, csn) there are two outputs and
one input. One output, \_t, is for making the pin high impedance,
\_o is the current output of the pin, valid only if \_t is high.
The \_i is the pin value.

For writing `waddress_i` specifies the address, `wdata_i` is the data to write,
`write_i` says when to write the data into the address.

For reading, `raddress_i` specifies the address to read from,
`rdata_i` says the data read on the address. `read_i` says that
read from an address is performed. The data appear immediately,
but `read_i` should be asserted, to say the data are read, as
there are side effects even on reading, specifically in the DATA
register. A read will empty the rx buffer.

The peripheral also has an interrupt, that is an or from the
status register, for unmasked status fields.

### SPI masterslave

The master slave also has i/o for spi, same as the peripheral.
Other inputs and outputs map to what is described in the registers
below.

## Verification

For verification, cocotb has been used. Cocotb is a Python framework
for co-simulation, supporting various simulators like ModelSim,
Vivado simulator or open-source GHDL.

There are numerous tests for the spi_masterslave as well as
spi_peripheral that uses the registers for transmitting and receiving data.

## Memory mapped peripheral

The peripheral has 4 registers,

: Peripheral registers

+----------+---------+---------------------------------------------------+
| Register | Address | Description                                       |
+==========+=========+===================================================+
| CTRL     | 0       | Control register                                  |
+----------+---------+---------------------------------------------------+
| INTMASK  | 1       | Interrupt masks                                   |
+----------+---------+---------------------------------------------------+
| STATUS   | 2       | Status of the peripheral                          |
+----------+---------+---------------------------------------------------+
| DATA     | 2       | Received data on read, transmit data on write     |
+----------+---------+---------------------------------------------------+


: CTRL register fields

+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field                | Off | Len | Description                                                                                                                                                                      |
+======================+=====+=====+==================================================================================================================================================================================+
| EN                   | 0   | 1   | Enable the peripheral. If disabled, the peripheral won't function and the outputs will be high impedance                                                                         |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| MASTER               | 1   | 1   | Whether to turn on master mode (currently only master supported)                                                                                                                 |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| TX EN                | 2   | 1   | Enable transmission. If disabled, mosi is high impedance. To transmit, you still write to data register                                                                          |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| RX EN                | 3   | 1   | Enable reception. If disabled, no data are filled to rx buffer.                                                                                                                  |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| CLOCK POL            | 4   | 1   | Clock polarity of the clock when not transmitting.                                                                                                                               |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| CLOCK PHA            | 5   | 1   | Phase of the clock, if 0, first edge is sampling, second is shifting. The edge type depends on clock polarity. For clock polarity 0, clock phase 0 means sampling on rising edge |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| LSBFRST              | 6   | 1   | What bit to send first, lsb if high                                                                                                                                              |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SIZE                 | 10  | 1   | The size of data to select, selected through generic, default is 0 for 8, 1 for 16                                                                                               |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| DIV                  | 20  | 3   | The divisor of spi clock. Selected through generic. Default is `clk frequency / 2**(DIV_SEL + 1)`                                                                                |
+----------------------+-----+-----+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

If a field in INTMASK register is set to '1', when the corresponding
field in STATUS register goes high, an interrupt will be fired.

: INTMASK register fields

+-----------------+--------+--------+--------------------------+
| Field           | Offset | Length | Description              |
+=================+========+========+==========================+
| RX BUFFER FULL  | 0      | 1      | Mask for rx buffer full  |
+-----------------+--------+--------+--------------------------+
| TX BUFFER EMPTY | 1      | 1      | Mask for tx buffer empty |
+-----------------+--------+--------+--------------------------+

: STATUS register fields

+-----------------+--------+--------+--------------------------------------------------------------------------+
| Field           | Offset | Length | Description                                                              |
+=================+========+========+==========================================================================+
| RX BUFFER FULL  | 0      | 1      | The receive buffer is full, data can be received via DATA register       |
+-----------------+--------+--------+--------------------------------------------------------------------------+
| TX BUFFER EMPTY | 1      | 1      | The transmit buffer is empty, data can be transmitted by writing to DATA |
+-----------------+--------+--------+--------------------------------------------------------------------------+
| BUSY            | 3      | 1      | The peripheral is currently busy transmitting or receiving               |
+-----------------+--------+--------+--------------------------------------------------------------------------+
| LOST RX DATA    | 4      | 1      | Data has been lost, since they weren't read in time                      |
+-----------------+--------+--------+--------------------------------------------------------------------------+

DATA register fields:
The data register size is based on the format specified in CTRL
register. If you write to the register, the data will be transmitted,
if you read, the received data are read. Data can be transmitted
only if TX_BUFFER\_EMPTY. Data can be received only if
RX\_BUFFER\_FULL.

## Demonstration

To test the peripheral, Zynq has been used. This repository contains
both Vivado and Vitis projects. As a demonstration, the Zynq is used
as an spi master. It has a counter and sends numbers to the slave,
incrementing by one every second. On STM Nucleo board, a slave has
been made. This slave receives the number and shows it on a seven
segment display on Funduino board.

## Project structure

- `hdl_spi/src` - VHDL code describing the spi peripheral itself
- `hdl_spi/tests` - tests in Python, utilizing cocotb for co-simulation
- `vivado` - Vivado project, with AXI peripheral wrapper for spi peripheral, for Zynq Z20
- `vitis` - Vitis projects, utilizing the project from vivado as a platform
- `stm_spi_funduino` - spi slave on Nucleo, using Funduino board for displaying the numbers
