Skip to content

Commit

Permalink
Merge pull request #381 from stnolting/add_spi_fifo
Browse files Browse the repository at this point in the history
✨ [rtl] add optional SPI data FIFO
  • Loading branch information
stnolting authored Aug 2, 2022
2 parents 2996f60 + 7eaf2c2 commit 860a146
Show file tree
Hide file tree
Showing 21 changed files with 514 additions and 200 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 => 01.04.03.12 => Version 01.04.03.12 => v1.4.3.12

| Date (*dd.mm.yyyy*) | Version | Comment |
|:-------------------:|:-------:|:--------|
| 01.08.2022 | 1.7.4.8 | :sparkles: add configurable data FIFO to **SPI** module; [#381](https://github.com/stnolting/neorv32/pull/381) |
| 31.07.2022 | 1.7.4.7 | :warning: rework **SLINK** module; [#377](https://github.com/stnolting/neorv32/pull/377) |
| 25.07.2022 | 1.7.4.6 | :warning: simplify memory configuration of **linker script**; :sparkles: add in-console configuration option; [#]375(https://github.com/stnolting/neorv32/pull/375) |
| 22.07.2022 | 1.7.4.5 | add `CUSTOM_ID` generic; update bootloader; [#374](https://github.com/stnolting/neorv32/pull/374) |
Expand Down
12 changes: 12 additions & 0 deletions docs/datasheet/soc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,18 @@ implemented reducing access latency by one cycle but eventually increasing the c
|======


:sectnums!:
===== _IO_SPI_FIFO_

[cols="4,4,2"]
[frame="all",grid="none"]
|======
| **IO_SPI_FIFO** | _natural_ | 0
3+| Depth of the <<_serial_peripheral_interface_controller_spi>> FIFO. Has to be zero or a power of two.
Maximum value is 32*1024.
|======


:sectnums!:
===== _IO_TWI_EN_

Expand Down
121 changes: 74 additions & 47 deletions docs/datasheet/soc_spi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,57 @@
| | `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_
| 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>>)
|=======================


**Theory of Operation**
**Overview**

SPI is a synchronous serial transmission interface for fast on-board communications.
The NEORV32 SPI transceiver supports 8-, 16-, 24- and 32-bit wide transmissions.
The unit provides 8 dedicated chip select signals via the top entity's `spi_csn_o` signal, which are
The NEORV32 SPI transceiver module supports 8-, 16-, 24- and 32-bit wide transmissions, all 4 standard clock mode
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.

[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).
and not by any external SPI module.

The SPI unit 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. Furthermore, a transfer being in process
can be terminated at any time by clearing this bit.
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
is used to interface the FIFO.

[IMPORTANT]
Changes to the `CTRL` control register should be made only when the SPI module is idle as they directly effect
transmissions being in-progress.

[TIP]
A transmission can be terminated at any time by disabling the SPI module
by clearing the _SPI_CTRL_EN_ control register bit.
**Theory of Operation**

The data quantity to be transferred within a single transmission is defined via the _SPI_CTRL_SIZEx_ bits.
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 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 transfers data quantity, the transmit data must be placed
into the lowest 8/16/24 bit of `DATA`. Vice versa, the received data is also always LSB-aligned. Application
software should only actually process the amount of bits that were configured using _SPI_CTRL_SIZEx_ when
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 send buffer.

[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
reading `DATA` (for RX) to implement LSB-first transmissions. Note that in both cases data in `DATA` still
needs to be LSB-aligned.

[TIP]
The actual transmission length 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 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)`
Expand All @@ -70,7 +76,7 @@ the accessed device's chip-select signal but can also be use for controlling oth

**SPI Clock Configuration**

The SPI module supports all _standard SPI clock modes_ (0, 1, 2, 3), which is via the two control register bits
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_.

Expand All @@ -87,7 +93,7 @@ image::SPI_timing_diagram2.wikimedia.png[]
|=======================

The SPI clock frequency (`spi_sck_o`) is programmed by the 3-bit _SPI_CTRL_PRSCx_ clock prescaler.
The following prescalers are available:
The following pre-scalers are available:

.SPI prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
Expand All @@ -107,15 +113,40 @@ 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
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.


**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.

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.

[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 to signal "transmission done" to the CPU. Whenever the SPI
module completes the current transfer operation, the interrupt is triggered and has to be explicitly cleared again
by writing zero to the according <<_mip>> CSR bit.
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:

* `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
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
"SPI serial engine _completes_ current transmission" as interrupt condition.


**Register Map**
Expand All @@ -125,24 +156,20 @@ by writing zero to the according <<_mip>> CSR bit.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.19+<| `0xffffffa8` .19+<| `NEORV32_SPI.CTRL` <|`0` _SPI_CTRL_CS0_ ^| r/w .8+<| Direct chip-select 0..7; setting `spi_csn_o(x)` low when set
<|`1` _SPI_CTRL_CS1_ ^| r/w
<|`2` _SPI_CTRL_CS2_ ^| r/w
<|`3` _SPI_CTRL_CS3_ ^| r/w
<|`4` _SPI_CTRL_CS4_ ^| r/w
<|`5` _SPI_CTRL_CS5_ ^| r/w
<|`6` _SPI_CTRL_CS6_ ^| r/w
<|`7` _SPI_CTRL_CS7_ ^| r/w
<|`8` _SPI_CTRL_EN_ ^| r/w <| SPI 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)
<|`10` _SPI_CTRL_PRSC0_ ^| r/w .3+| 3-bit clock prescaler select
<|`11` _SPI_CTRL_PRSC1_ ^| r/w
<|`12` _SPI_CTRL_PRSC2_ ^| r/w
<|`13` _SPI_CTRL_SIZE0_ ^| r/w .2+<| transfer size (`00`=8-bit, `01`=16-bit, `10`=24-bit, `11`=32-bit)
<|`14` _SPI_CTRL_SIZE1_ ^| r/w
<|`15` _SPI_CTRL_CPOL_ ^| r/w <| clock polarity
<|`16` _SPI_CTRL_HIGHSPEED_ ^| r/w <| enable SPI high-speed mode (ignoring _SPI_CTRL_PRSC_)
<|`17:30` ^| r/- <| _reserved, read as zero
<|`31` _SPI_CTRL_BUSY_ ^| r/- <| transmission in progress when set
| `0xffffffac` | `NEORV32_SPI.DATA` |`31:0` | r/w | receive/transmit data, LSB-aligned
.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
<|`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
|=======================
6 changes: 5 additions & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ package neorv32_package is
-- Architecture Constants (do not modify!) ------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant data_width_c : natural := 32; -- native data path width - do not change!
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01070407"; -- NEORV32 version - no touchy!
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01070408"; -- NEORV32 version - no touchy!
constant archid_c : natural := 19; -- official RISC-V architecture ID - hands off!

-- Check if we're inside the Matrix -------------------------------------------------------
Expand Down Expand Up @@ -1025,6 +1025,7 @@ package neorv32_package is
IO_UART1_RX_FIFO : natural := 1; -- RX fifo depth, has to be a power of two, min 1
IO_UART1_TX_FIFO : natural := 1; -- TX fifo depth, has to be a power of two, min 1
IO_SPI_EN : boolean := false; -- implement serial peripheral interface (SPI)?
IO_SPI_FIFO : natural := 0; -- SPI RTX fifo depth, has to be zero or a power of two
IO_TWI_EN : boolean := false; -- implement two-wire interface (TWI)?
IO_PWM_NUM_CH : natural := 0; -- number of PWM channels to implement (0..60); 0 = disabled
IO_WDT_EN : boolean := false; -- implement watch dog timer (WDT)?
Expand Down Expand Up @@ -1751,6 +1752,9 @@ package neorv32_package is
-- Component: Serial Peripheral Interface (SPI) -------------------------------------------
-- -------------------------------------------------------------------------------------------
component neorv32_spi
generic (
IO_SPI_FIFO : natural -- SPI RTX fifo depth, has to be zero or a power of two
);
port (
-- host access --
clk_i : in std_ulogic; -- global clock line
Expand Down
Loading

0 comments on commit 860a146

Please sign in to comment.