172 lines
10 KiB
Text
172 lines
10 KiB
Text
<<<
|
|
:sectnums:
|
|
==== Serial Peripheral Interface Controller (SPI)
|
|
|
|
[cols="<3,<3,<4"]
|
|
[frame="topbot",grid="none"]
|
|
|=======================
|
|
| Hardware source file(s): | neorv32_spi.vhd |
|
|
| Software driver file(s): | neorv32_spi.c |
|
|
| | neorv32_spi.h |
|
|
| Top entity port: | `spi_sck_o` | 1-bit serial clock output
|
|
| | `spi_sdo_o` | 1-bit serial data output
|
|
| | `spi_sdi_i` | 1-bit serial data input
|
|
| | `spi_csn_i` | 8-bit dedicated chip select (low-active)
|
|
| Configuration generics: | _IO_SPI_EN_ | implement SPI controller when _true_
|
|
| | _IO_SPI_FIFO_ | data FIFO size, has to be zero or a power of two
|
|
| CPU interrupts: | fast IRQ channel 6 | transmission done interrupt (see <<_processor_interrupts>>)
|
|
|=======================
|
|
|
|
|
|
**Overview**
|
|
|
|
SPI is a synchronous serial transmission interface for fast on-board communications.
|
|
The NEORV32 SPI transceiver module supports 8-, 16-, 24- and 32-bit wide transmissions, all 4 standard clock modes
|
|
and 8 dedicated chip select signals via the top entity's `spi_csn_o` signal, which are
|
|
directly controlled by the SPI module (no additional GPIOs required). An optional receive/transmit FIFO can be
|
|
implemented via the _IO_SPI_FIFO_ generic to support block-based transmissions without CPU interaction.
|
|
|
|
.Host-Mode Only
|
|
[NOTE]
|
|
The NEORV32 SPI module only supports _host mode_. Transmission are initiated only by the processor's SPI module
|
|
and not by an external SPI module.
|
|
|
|
The SPI module provides a single control register `CTRL` to configure the module and to check it's status
|
|
and a single data register `DATA` for receiving/transmitting data. If the data FIFO is implemented, this register
|
|
is used to interface the FIFO.
|
|
|
|
|
|
**Theory of Operation**
|
|
|
|
The SPI module is enabled by setting the _SPI_CTRL_EN_ bit in the `CTRL` control register. No transfer can be initiated
|
|
and no interrupt request will be triggered if this bit is cleared. Clearing this bit will reset the module (also clearing
|
|
the FIFO if implemented) and will also terminate any transfer being in process.
|
|
|
|
The data quantity to be transferred within a single data transmission is defined via the _SPI_CTRL_SIZEx_ bits.
|
|
The SPI module supports 8-bit (`00`), 16-bit (`01`), 24-bit (`10`) and 32-bit (`11`) transfers.
|
|
|
|
A transmission is started when writing data to the `DATA` register. The data must be LSB-aligned. So if
|
|
the SPI transceiver is configured for less than 32-bit transfer data quantity, the transmit data must be placed
|
|
into the lowest 8/16/24 bits of `DATA`. Vice versa, the received data is also always LSB-aligned. Application
|
|
software should only process the amount of bits that was configured using _SPI_CTRL_SIZEx_ when
|
|
reading `DATA`.
|
|
|
|
The SPI operation is completed as soon as the _SPI_CTRL_BUSY_ flag clears. If a FIFO size greater than zero is configured,
|
|
the busy flag clears when the current serial engine operation is completed and there is no data left in the send buffer.
|
|
|
|
.MSB-first Only
|
|
[NOTE]
|
|
The NEORV32 SPI module only support MSB-first mode. Data can be reversed before writing `DATA` (for TX) / after
|
|
reading `DATA` (for RX) to implement LSB-first transmissions. Note that in both cases data in `DATA` still
|
|
needs to be LSB-aligned.
|
|
|
|
.Arbitrary Transmission Length
|
|
[TIP]
|
|
The total transmission length, which can be an arbitrary number of individual data transfers, is left to the user:
|
|
after asserting chip-select an arbitrary amount of transmission with arbitrary data quantity (_SPI_CTRL_SIZEx_) can
|
|
be made before de-asserting chip-select again.
|
|
|
|
The SPI controller features 8 dedicated chip-select lines. These lines are controlled via the control register's
|
|
_SPI_CTRL_CS_SELx_ and _SPI_CTRL_CS_EN_ bits. The 3-bit _SPI_CTRL_CSx_ bits are used to select one out of the eight
|
|
dedicated chip select line. As soon as _SPI_CTRL_CS_EN_ is _set_ the selected chip select line is activated (driven _low_).
|
|
Note that disabling the SPI module via the _SPI_CTRL_EN_ bit will also deactivate any currently activated chip select line.
|
|
|
|
|
|
**SPI Clock Configuration**
|
|
|
|
The SPI module supports all _standard SPI clock modes_ (0, 1, 2, 3), which are configured via the two control register bits
|
|
_SPI_CTRL_CPHA_ and _SPI_CTRL_CPOL_. The _SPI_CTRL_CPHA_ bit defines the _clock phase_ and the _SPI_CTRL_CPOL_
|
|
bit defines the _clock polarity_.
|
|
|
|
.SPI clock modes; image from https://en.wikipedia.org/wiki/File:SPI_timing_diagram2.svg (license: (Wikimedia) https://en.wikipedia.org/wiki/Creative_Commons[Creative Commons] https://creativecommons.org/licenses/by-sa/3.0/deed.en[Attribution-Share Alike 3.0 Unported])
|
|
image::SPI_timing_diagram2.wikimedia.png[]
|
|
|
|
.SPI standard clock modes
|
|
[cols="<2,^1,^1,^1,^1"]
|
|
[options="header",grid="rows"]
|
|
|=======================
|
|
| | Mode 0 | Mode 1 | Mode 2 | Mode 3
|
|
| _SPI_CTRL_CPOL_ | `0` | `0` | `1` | `1`
|
|
| _SPI_CTRL_CPHA_ | `0` | `1` | `0` | `1`
|
|
|=======================
|
|
|
|
The SPI clock frequency (`spi_sck_o`) is programmed by the 3-bit _SPI_CTRL_PRSCx_ clock prescaler for a coarse selection
|
|
and a 4-bit clock divider _SPI_CTRL_CDIVx_ for a fine selection.
|
|
|
|
The following pre-scalers (_SPI_CTRL_PRSCx_) are available:
|
|
|
|
.SPI prescaler configuration
|
|
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
|
|
[options="header",grid="rows"]
|
|
|=======================
|
|
| **`SPI_CTRL_PRSCx`** | `0b000` | `0b001` | `0b010` | `0b011` | `0b100` | `0b101` | `0b110` | `0b111`
|
|
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|
|
|=======================
|
|
|
|
Based on the _SPI_CTRL_PRSCx_ and _SPI_CTRL_CDIVx_ configuration, the actual SPI clock frequency f~SPI~ is derived
|
|
from the processor's main clock f~main~ according to the following equation:
|
|
|
|
_**f~SPI~**_ = _f~main~[Hz]_ / (2 * `clock_prescaler` * (1 + _SPI_CTRL_CDIVx_))
|
|
|
|
Hence, the maximum SPI clock is f~main~ / 4 and the lowest SPI clock is f~main~ / 131072. The SPI clock is always
|
|
symmetric having a duty cycle of exactly 50%.
|
|
|
|
|
|
**SPI FIFO**
|
|
|
|
An optional FIFO buffer can be implemented by setting the _IO_SPI_FIFO_ generic to a value greater than zero.
|
|
Implementing a data FIFO allows (more) CPU-independent operation of the SPI module.
|
|
|
|
Internally, two FIFOs are implemented: one for TX data and one for RX data. However, these two FIFOs are transparent for
|
|
the software and operate as a single, unified "ring buffer". The status signals of the TX FIFO ("empty", "at least half full",
|
|
"full") are exposed as read-only signals via the SPI control register. In contrast, the RX FIFO only provides a "data available"
|
|
flag (= RX FIFO not empty) also exposed via the SPI control register.
|
|
|
|
.Double-Buffering
|
|
[TIP]
|
|
Application programs can implement "double buffering" when using the "FIFO less than half full" interrupt configuration
|
|
option (see below).
|
|
|
|
|
|
**SPI Interrupt**
|
|
|
|
The SPI module provides a single interrupt that can be used to signal certain transmission states to the CPU.
|
|
The actual interrupt condition is configured by the two _SPI_CTRL_IRQx_ bits in the SPI module's control register:
|
|
|
|
* `00`, `01` : trigger interrupt when SPI serial engine _completes_ current transmission
|
|
* `10` : trigger interrupt when TX FIFO _becomes_ less than half full; this mode is not available if _IO_SPI_FIFO_ is zero
|
|
* `11` : trigger interrupt when TX FIFO _becomes_ empty; this mode is not available if _IO_SPI_FIFO_ is zero
|
|
|
|
Once the SPI CPU is triggered it has to be explicitly cleared again by writing zero to the according
|
|
<<_mip>> CSR bit inside the SPI trap handler.
|
|
|
|
[IMPORTANT]
|
|
If no FIFO is implemented (_IO_SPI_FIFO_ = 0) the _SPI_CTRL_IRQx_ are hardwired to `00` statically configuring
|
|
"SPI serial engine _completes_ current transmission" as interrupt condition.
|
|
|
|
|
|
**Register Map**
|
|
|
|
.SPI register map (`struct NEORV32_SPI`)
|
|
[cols="<2,<2,<4,^1,<7"]
|
|
[options="header",grid="all"]
|
|
|=======================
|
|
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
|
|
.16+<| `0xffffffa8` .16+<| `NEORV32_SPI.CTRL` <|`0` _SPI_CTRL_EN_ ^| r/w <| SPI module enable
|
|
<|`1` _SPI_CTRL_CPHA_ ^| r/w <| clock phase (`0`=sample RX on rising edge & update TX on falling edge; `1`=sample RX on falling edge & update TX on rising edge)
|
|
<|`2` _SPI_CTRL_CPOL_ ^| r/w <| clock polarity
|
|
<|`4:3` _SPI_CTRL_SIZE1_ : _SPI_CTRL_SIZE0_ ^| r/w <| transfer size (`00`=8-bit, `01`=16-bit, `10`=24-bit, `11`=32-bit)
|
|
<|`7:5` _SPI_CTRL_CS_SEL2_ : _SPI_CTRL_CS_SEL0_ ^| r/w <| Direct chip-select 0..7
|
|
<|`8` _SPI_CTRL_CS_EN_ ^| r/w <| Direct chip-select enable; setting `spi_csn_o(x)` low when set
|
|
<|`11:9` _SPI_CTRL_PRSC2_ : _SPI_CTRL_PRSC0_ ^| r/w <| 3-bit clock prescaler select
|
|
<|`15:12` _SPI_CTRL_CDIV2_ : _SPI_CTRL_CDIV0_ ^| r/w <| 4-bit clock divider
|
|
<|`17:16` _SPI_CTRL_IRQ1_ : _SPI_CTRL_IRQ0_ ^| r/w <| interrupt configuration (`0-` = SPI serial engine becomes idle, `10` = TX FIFO _become_ less than half full, `11` = TX FIFO _becomes_ empty)
|
|
<|`22:18` _reserved_ ^| r/- <| reserved, read as zero
|
|
<|`26:23` _SPI_CTRL_FIFO_MSB_ : _SPI_CTRL_FIFO_LSB_ ^| r/- <| FIFO depth; log2(_IO_SPI_FIFO_)
|
|
<|`27` _SPI_CTRL_RX_AVAIL_ ^| r/- <| RX FIFO data available (RX FIFO not empty); zero if FIFO not implemented
|
|
<|`28` _SPI_CTRL_TX_EMPTY_ ^| r/- <| TX FIFO empty; zero if FIFO not implemented
|
|
<|`29` _SPI_CTRL_TX_HALF_ ^| r/- <| TX FIFO at least half full; zero if FIFO not implemented
|
|
<|`30` _SPI_CTRL_TX_FULL_ ^| r/- <| TX FIFO full; zero if FIFO not implemented
|
|
<|`31` _SPI_CTRL_BUSY_ ^| r/- <| SPI module busy when set (serial engine operation in progress and TX FIFO not empty yet)
|
|
| `0xffffffac` | `NEORV32_SPI.DATA` |`31:0` | r/w | receive/transmit data (FIFO), LSB-aligned
|
|
|=======================
|