Skip to content

Commit

Permalink
⚠️ rework SPI module (#438)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting authored Nov 3, 2022
2 parents f0ff496 + 3757351 commit bacfe84
Show file tree
Hide file tree
Showing 15 changed files with 530 additions and 512 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mimpid = 0x01040312 => Version 01.04.03.12 => v1.4.3.12

| Date (*dd.mm.yyyy*) | Version | Comment |
|:-------------------:|:-------:|:--------|
| 01.11.2022 | 1.7.7.6 | :warning: rework **SPI module**; [#438](https://github.com/stnolting/neorv32/pull/438) |
| 24.10.2022 | 1.7.7.5 | :test_tube: remove weird Quartus latch warnings by modifying VHDL coding style; [#434](https://github.com/stnolting/neorv32/pull/434) |
| 19.10.2022 | 1.7.7.4 | optimize UART's `RTS` (hardware flow control) behavior; [#433](https://github.com/stnolting/neorv32/pull/433) |
| 15.10.2022 | 1.7.7.3 | :bug: fix bug in `is_power_of_two_f` VHDL function (thanks Alan!); [#482](https://github.com/stnolting/neorv32/pull/428) |
Expand Down
93 changes: 45 additions & 48 deletions docs/datasheet/soc_spi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,26 @@
**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 mode
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 GPIO required).
An optional FIFO can be implemented via the _IO_SPI_FIFO_ generic to support block-based transmissions
without CPU interaction.
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 any external SPI module.
and not by an external SPI module.

The SPI module provides a single control register `CTRL` used to configure the module and to check its status
and a data register `DATA` for receiving/transmitting data. If the data FIFO is implemented, this register
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
any FIFOs if implemented) and will also terminate any a transfer being in process.
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.
Expand All @@ -52,26 +52,24 @@ software should only process the amount of bits that was configured using _SPI_C
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 send buffer.
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_CSx_ bits. When a specific _SPI_CTRL_CSx_ bit is **set**, the according chip-select line `spi_csn_o(x)`
goes **low** (low-active chip-select lines).

[TIP]
The dedicated SPI chip-select signals can be seen as _general purpose_ outputs. These are intended to control
the accessed device's chip-select signal but can also be use for controlling other shift register signals
(like data strobe or output-enables).
_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**
Expand All @@ -92,8 +90,10 @@ image::SPI_timing_diagram2.wikimedia.png[]
| _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.
The following pre-scalers are available:
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"]
Expand All @@ -103,30 +103,26 @@ The following pre-scalers are available:
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================

Based on the _SPI_CTRL_PRSCx_ configuration, the actual SPI clock frequency f~SPI~ is derived from the processor's
main clock f~main~ and is determined by:
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`)
_**f~SPI~**_ = _f~main~[Hz]_ / (2 * `clock_prescaler` * (1 + _SPI_CTRL_CDIVx_))

Hence, the maximum SPI clock is f~main~ / 4.

.High-Speed SPI mode
[TIP]
The module provides a "high-speed" SPI mode. In this mode the clock prescaler configuration (SPI_CTRL_PRSCx) is ignored
and the SPI clock operates at **f~main~ / 2** (half of the processor's main clock). High speed SPI mode is enabled by setting
the control register's _SPI_CTRL_HIGHSPEED_ bit.
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 _IO_SPI_FIFO_ to a value greater than zero. Having a data FIFO
allows (more) CPU-independent operation of the SPI module.
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, those 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) via the SPI control register.
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).
Expand All @@ -135,17 +131,17 @@ 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_ in the SPI module's control register:
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, not available if _IO_SPI_FIFO_ is zero
* `11` : trigger interrupt when TX FIFO _becomes_ empty, not available if _IO_SPI_FIFO_ is zero
* `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 not FIFO is implemented (_IO_SPI_FIFO_ = 0) the _SPI_CTRL_IRQx_ are hardwired to `00` statically configuring
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.


Expand All @@ -156,16 +152,17 @@ If not FIFO is implemented (_IO_SPI_FIFO_ = 0) the _SPI_CTRL_IRQx_ are hardwired
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.15+<| `0xffffffa8` .15+<| `NEORV32_SPI.CTRL` <|`7:0` _SPI_CTRL_CS7_ : _SPI_CTRL_CS0_ ^| r/w <| Direct chip-select 0..7; setting `spi_csn_o(x)` low when set
<|`8` _SPI_CTRL_EN_ ^| r/w <| SPI module enable
<|`9` _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)
<|`12:10` _SPI_CTRL_PRSC2_ : _SPI_CTRL_PRSC0_ ^| r/w <| 3-bit clock prescaler select
<|`14:13` _SPI_CTRL_SIZE1_ : _SPI_CTRL_SIZE0_ ^| r/w <| transfer size (`00`=8-bit, `01`=16-bit, `10`=24-bit, `11`=32-bit)
<|`15` _SPI_CTRL_CPOL_ ^| r/w <| clock polarity
<|`16` _SPI_CTRL_HIGHSPEED_ ^| r/w <| enable SPI high-speed mode (ignoring _SPI_CTRL_PRSC_)
<|`18:17` _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:19` _SPI_CTRL_FIFO_MSB_ : _SPI_CTRL_FIFO_LSB_ ^| r/- <| FIFO depth; log2(_IO_SPI_FIFO_)
<|`23:26` _reserved_ ^| r/- <| reserved, read as zero
.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
Expand Down
29 changes: 14 additions & 15 deletions rtl/core/neorv32_application_image.vhd
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
-- The NEORV32 RISC-V Processor: https://github.com/stnolting/neorv32
-- Auto-generated memory initialization file (for APPLICATION) from source file <blink_led/main.bin>
-- Size: 1008 bytes
-- Auto-generated memory initialization file (for APPLICATION) from source file <demo_blink_led/main.bin>
-- Size: 1004 bytes
-- MARCH: default
-- Built: 26.08.2022 15:55:14
-- Built: 01.11.2022 18:17:55

-- prototype defined in 'neorv32_package.vhd'
package body neorv32_application_image is
Expand All @@ -14,11 +14,10 @@ x"ff810113",
x"80000197",
x"7f418193",
x"00000517",
x"12850513",
x"12450513",
x"30551073",
x"30001073",
x"30401073",
x"34401073",
x"b0001073",
x"b8001073",
x"b0201073",
Expand All @@ -43,11 +42,11 @@ x"00000e13",
x"00000e93",
x"00000f13",
x"00000f93",
x"3f000593",
x"3ec00593",
x"80000617",
x"f7060613",
x"f7460613",
x"80000697",
x"f6868693",
x"f6c68693",
x"00c58e63",
x"00d65c63",
x"0005a703",
Expand All @@ -56,15 +55,15 @@ x"00458593",
x"00460613",
x"fedff06f",
x"80000717",
x"f4470713",
x"f4870713",
x"80000797",
x"f3c78793",
x"f4078793",
x"00f75863",
x"00072023",
x"00470713",
x"ff5ff06f",
x"3f000413",
x"3f000493",
x"3ec00413",
x"3ec00493",
x"00945a63",
x"0009a083",
x"000080e7",
Expand All @@ -75,8 +74,8 @@ x"00000593",
x"088000ef",
x"30047073",
x"34051073",
x"3f000413",
x"3f000493",
x"3ec00413",
x"3ec00493",
x"00945a63",
x"00042083",
x"000080e7",
Expand Down Expand Up @@ -117,7 +116,7 @@ x"00150413",
x"00000593",
x"0ff57513",
x"0cc000ef",
x"10000513",
x"0fa00513",
x"020000ef",
x"00040513",
x"fe5ff06f",
Expand Down
Loading

0 comments on commit bacfe84

Please sign in to comment.