diff --git a/CHANGELOG.md b/CHANGELOG.md index f4f7942e0..607834809 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) | diff --git a/docs/datasheet/soc.adoc b/docs/datasheet/soc.adoc index 08adc8765..b05eb2817 100644 --- a/docs/datasheet/soc.adoc +++ b/docs/datasheet/soc.adoc @@ -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_ diff --git a/docs/datasheet/soc_spi.adoc b/docs/datasheet/soc_spi.adoc index c55b89982..f392d3553 100644 --- a/docs/datasheet/soc_spi.adoc +++ b/docs/datasheet/soc_spi.adoc @@ -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)` @@ -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_. @@ -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"] @@ -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** @@ -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 |======================= diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index b1b634b94..ecbe9a69d 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -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 ------------------------------------------------------- @@ -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)? @@ -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 diff --git a/rtl/core/neorv32_spi.vhd b/rtl/core/neorv32_spi.vhd index 15220a776..3b361f5fc 100644 --- a/rtl/core/neorv32_spi.vhd +++ b/rtl/core/neorv32_spi.vhd @@ -3,7 +3,6 @@ -- # ********************************************************************************************* # -- # Frame format: 8/16/24/32-bit receive/transmit data, always MSB first, 2 clock modes, # -- # 8 pre-scaled clocks (derived from system clock), 8 dedicated chip-select lines (low-active). # --- # Interrupt: "transfer done" # -- # ********************************************************************************************* # -- # BSD 3-Clause License # -- # # @@ -44,6 +43,9 @@ library neorv32; use neorv32.neorv32_package.all; entity neorv32_spi is + 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 @@ -74,27 +76,38 @@ architecture neorv32_spi_rtl of neorv32_spi is constant lo_abb_c : natural := index_size_f(spi_size_c); -- low address boundary bit -- control register -- - constant ctrl_cs0_c : natural := 0; -- r/w: spi CS 0 - constant ctrl_cs1_c : natural := 1; -- r/w: spi CS 1 - constant ctrl_cs2_c : natural := 2; -- r/w: spi CS 2 - constant ctrl_cs3_c : natural := 3; -- r/w: spi CS 3 - constant ctrl_cs4_c : natural := 4; -- r/w: spi CS 4 - constant ctrl_cs5_c : natural := 5; -- r/w: spi CS 5 - constant ctrl_cs6_c : natural := 6; -- r/w: spi CS 6 - constant ctrl_cs7_c : natural := 7; -- r/w: spi CS 7 - constant ctrl_en_c : natural := 8; -- r/w: spi enable - constant ctrl_cpha_c : natural := 9; -- r/w: spi clock phase - constant ctrl_prsc0_c : natural := 10; -- r/w: spi prescaler select bit 0 - constant ctrl_prsc1_c : natural := 11; -- r/w: spi prescaler select bit 1 - constant ctrl_prsc2_c : natural := 12; -- r/w: spi prescaler select bit 2 - constant ctrl_size0_c : natural := 13; -- r/w: data size lsb (00: 8-bit, 01: 16-bit) - constant ctrl_size1_c : natural := 14; -- r/w: data size msb (10: 24-bit, 11: 32-bit) - constant ctrl_cpol_c : natural := 15; -- r/w: spi clock polarity - constant ctrl_highspeed_c : natural := 16; -- r/w: spi high-speed mode enable (ignoring ctrl_prsc) + constant ctrl_cs0_c : natural := 0; -- r/w: spi CS 0 + constant ctrl_cs1_c : natural := 1; -- r/w: spi CS 1 + constant ctrl_cs2_c : natural := 2; -- r/w: spi CS 2 + constant ctrl_cs3_c : natural := 3; -- r/w: spi CS 3 + constant ctrl_cs4_c : natural := 4; -- r/w: spi CS 4 + constant ctrl_cs5_c : natural := 5; -- r/w: spi CS 5 + constant ctrl_cs6_c : natural := 6; -- r/w: spi CS 6 + constant ctrl_cs7_c : natural := 7; -- r/w: spi CS 7 + constant ctrl_en_c : natural := 8; -- r/w: spi enable + constant ctrl_cpha_c : natural := 9; -- r/w: spi clock phase + constant ctrl_prsc0_c : natural := 10; -- r/w: spi prescaler select bit 0 + constant ctrl_prsc1_c : natural := 11; -- r/w: spi prescaler select bit 1 + constant ctrl_prsc2_c : natural := 12; -- r/w: spi prescaler select bit 2 + constant ctrl_size0_c : natural := 13; -- r/w: data size lsb (00: 8-bit, 01: 16-bit) + constant ctrl_size1_c : natural := 14; -- r/w: data size msb (10: 24-bit, 11: 32-bit) + constant ctrl_cpol_c : natural := 15; -- r/w: spi clock polarity + constant ctrl_highspeed_c : natural := 16; -- r/w: spi high-speed mode enable (ignoring ctrl_prsc) + constant ctrl_irq0_c : natural := 17; -- r/w: interrupt mode lsb (0-: PHY going idle) + constant ctrl_irq1_c : natural := 18; -- r/w: interrupt mode msb (10: TX fifo less than half full, 11: TX fifo empty) + -- + constant ctrl_fifo_size0_c : natural := 19; -- r/-: log2(FIFO size), bit 0 (lsb) + constant ctrl_fifo_size1_c : natural := 20; -- r/-: log2(FIFO size), bit 1 + constant ctrl_fifo_size2_c : natural := 21; -- r/-: log2(FIFO size), bit 2 + constant ctrl_fifo_size3_c : natural := 22; -- r/-: log2(FIFO size), bit 3 (msb) -- - constant ctrl_busy_c : natural := 31; -- r/-: spi transceiver is busy + constant ctrl_rx_avail_c : natural := 27; -- r/-: RX FIFO data available (RX FIFO not empty) + constant ctrl_tx_empty_c : natural := 28; -- r/-: TX FIFO empty + constant ctrl_tx_half_c : natural := 29; -- r/-: TX FIFO at least half full + constant ctrl_tx_full_c : natural := 30; -- r/-: TX FIFO full + constant ctrl_busy_c : natural := 31; -- r/-: spi PHY busy or TX FIFO not empty yet -- - signal ctrl : std_ulogic_vector(16 downto 0); + signal ctrl : std_ulogic_vector(18 downto 0); -- access control -- signal acc_en : std_ulogic; -- module access enable @@ -105,6 +118,9 @@ architecture neorv32_spi_rtl of neorv32_spi is -- clock generator -- signal spi_clk_en : std_ulogic; + -- interrupt generator -- + signal irq_gen : std_ulogic_vector(1 downto 0); + -- spi transceiver -- type rtx_engine_t is record state : std_ulogic_vector(02 downto 0); @@ -114,11 +130,33 @@ architecture neorv32_spi_rtl of neorv32_spi is bitcnt : std_ulogic_vector(05 downto 0); bytecnt : std_ulogic_vector(02 downto 0); sdi_sync : std_ulogic; + done : std_ulogic; end record; signal rtx_engine : rtx_engine_t; + -- RX/TX FIFO interface -- + type fifo_t is record + we : std_ulogic; -- write enable + re : std_ulogic; -- read enable + clear : std_ulogic; -- sync reset, high-active + wdata : std_ulogic_vector(31 downto 0); -- write data + rdata : std_ulogic_vector(31 downto 0); -- read data + avail : std_ulogic; -- data available? + free : std_ulogic; -- free entry available? + half : std_ulogic; -- half full + end record; + signal tx_fifo, rx_fifo : fifo_t; + begin + -- Sanity Checks -------------------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- + assert not ((is_power_of_two_f(IO_SPI_FIFO) = false) and (IO_SPI_FIFO /= 0)) + report "NEORV32 PROCESSOR CONFIG ERROR: SPI has to be a power of two." severity error; + assert not (IO_SPI_FIFO > 2**15) + report "NEORV32 PROCESSOR CONFIG ERROR: SPI has to be 1..32768." severity error; + + -- Access Control ------------------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = spi_base_c(hi_abb_c downto lo_abb_c)) else '0'; @@ -142,23 +180,18 @@ begin -- write access -- if (wren = '1') then if (addr = spi_ctrl_addr_c) then -- control register - ctrl(ctrl_cs0_c) <= data_i(ctrl_cs0_c); - ctrl(ctrl_cs1_c) <= data_i(ctrl_cs1_c); - ctrl(ctrl_cs2_c) <= data_i(ctrl_cs2_c); - ctrl(ctrl_cs3_c) <= data_i(ctrl_cs3_c); - ctrl(ctrl_cs4_c) <= data_i(ctrl_cs4_c); - ctrl(ctrl_cs5_c) <= data_i(ctrl_cs5_c); - ctrl(ctrl_cs6_c) <= data_i(ctrl_cs6_c); - ctrl(ctrl_cs7_c) <= data_i(ctrl_cs7_c); - ctrl(ctrl_en_c) <= data_i(ctrl_en_c); - ctrl(ctrl_cpha_c) <= data_i(ctrl_cpha_c); - ctrl(ctrl_prsc0_c) <= data_i(ctrl_prsc0_c); - ctrl(ctrl_prsc1_c) <= data_i(ctrl_prsc1_c); - ctrl(ctrl_prsc2_c) <= data_i(ctrl_prsc2_c); - ctrl(ctrl_size0_c) <= data_i(ctrl_size0_c); - ctrl(ctrl_size1_c) <= data_i(ctrl_size1_c); - ctrl(ctrl_cpol_c) <= data_i(ctrl_cpol_c); - ctrl(ctrl_highspeed_c) <= data_i(ctrl_highspeed_c); + ctrl(ctrl_cs7_c downto ctrl_cs0_c) <= data_i(ctrl_cs7_c downto ctrl_cs0_c); + ctrl(ctrl_en_c) <= data_i(ctrl_en_c); + ctrl(ctrl_cpha_c) <= data_i(ctrl_cpha_c); + ctrl(ctrl_prsc2_c downto ctrl_prsc0_c) <= data_i(ctrl_prsc2_c downto ctrl_prsc0_c); + ctrl(ctrl_size1_c downto ctrl_size0_c) <= data_i(ctrl_size1_c downto ctrl_size0_c); + ctrl(ctrl_cpol_c) <= data_i(ctrl_cpol_c); + ctrl(ctrl_highspeed_c) <= data_i(ctrl_highspeed_c); + if (IO_SPI_FIFO > 0) then -- FIFO implemented: all IRQ options available + ctrl(ctrl_irq1_c downto ctrl_irq0_c) <= data_i(ctrl_irq1_c downto ctrl_irq0_c); + else -- fall back: only the busy state of the SPI PHY can be used as IRQ source if no FIFO is implemented + ctrl(ctrl_irq1_c downto ctrl_irq0_c) <= "00"; + end if; end if; end if; @@ -166,56 +199,142 @@ begin data_o <= (others => '0'); if (rden = '1') then if (addr = spi_ctrl_addr_c) then -- control register - data_o(ctrl_cs0_c) <= ctrl(ctrl_cs0_c); - data_o(ctrl_cs1_c) <= ctrl(ctrl_cs1_c); - data_o(ctrl_cs2_c) <= ctrl(ctrl_cs2_c); - data_o(ctrl_cs3_c) <= ctrl(ctrl_cs3_c); - data_o(ctrl_cs4_c) <= ctrl(ctrl_cs4_c); - data_o(ctrl_cs5_c) <= ctrl(ctrl_cs5_c); - data_o(ctrl_cs6_c) <= ctrl(ctrl_cs6_c); - data_o(ctrl_cs7_c) <= ctrl(ctrl_cs7_c); - data_o(ctrl_en_c) <= ctrl(ctrl_en_c); - data_o(ctrl_cpha_c) <= ctrl(ctrl_cpha_c); - data_o(ctrl_prsc0_c) <= ctrl(ctrl_prsc0_c); - data_o(ctrl_prsc1_c) <= ctrl(ctrl_prsc1_c); - data_o(ctrl_prsc2_c) <= ctrl(ctrl_prsc2_c); - data_o(ctrl_size0_c) <= ctrl(ctrl_size0_c); - data_o(ctrl_size1_c) <= ctrl(ctrl_size1_c); - data_o(ctrl_cpol_c) <= ctrl(ctrl_cpol_c); - data_o(ctrl_highspeed_c) <= ctrl(ctrl_highspeed_c); + data_o(ctrl_cs7_c downto ctrl_cs0_c) <= ctrl(ctrl_cs7_c downto ctrl_cs0_c); + data_o(ctrl_en_c) <= ctrl(ctrl_en_c); + data_o(ctrl_cpha_c) <= ctrl(ctrl_cpha_c); + data_o(ctrl_prsc2_c downto ctrl_prsc0_c) <= ctrl(ctrl_prsc2_c downto ctrl_prsc0_c); + data_o(ctrl_size1_c downto ctrl_size0_c) <= ctrl(ctrl_size1_c downto ctrl_size0_c); + data_o(ctrl_cpol_c) <= ctrl(ctrl_cpol_c); + data_o(ctrl_highspeed_c) <= ctrl(ctrl_highspeed_c); + data_o(ctrl_irq1_c downto ctrl_irq0_c) <= ctrl(ctrl_irq1_c downto ctrl_irq0_c); + -- + data_o(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(IO_SPI_FIFO), 4)); -- - data_o(ctrl_busy_c) <= rtx_engine.busy; + if (IO_SPI_FIFO > 0) then + data_o(ctrl_rx_avail_c) <= rx_fifo.avail; + data_o(ctrl_tx_empty_c) <= not tx_fifo.avail; + data_o(ctrl_tx_half_c) <= tx_fifo.half; + data_o(ctrl_tx_full_c) <= not tx_fifo.free; + data_o(ctrl_busy_c) <= rtx_engine.busy or tx_fifo.avail; -- PHY busy or TX FIFO not empty yet + else + data_o(ctrl_rx_avail_c) <= '0'; + data_o(ctrl_tx_empty_c) <= '0'; + data_o(ctrl_tx_half_c) <= '0'; + data_o(ctrl_tx_full_c) <= '0'; + data_o(ctrl_busy_c) <= rtx_engine.busy; + end if; else -- data register (spi_rtx_addr_c) - data_o <= rtx_engine.sreg; + data_o <= rx_fifo.rdata; end if; end if; end if; end process rw_access; - -- direct chip-select (CS), output is low-active -- - spi_csn_o(7 downto 0) <= not ctrl(ctrl_cs7_c downto ctrl_cs0_c); - - -- trigger new SPI transmission -- - rtx_engine.start <= '1' when (wren = '1') and (addr = spi_rtx_addr_c) else '0'; - - - -- Clock Selection ------------------------------------------------------------------------ - -- ------------------------------------------------------------------------------------------- - clkgen_en_o <= ctrl(ctrl_en_c); -- clock generator enable - spi_clk_en <= clkgen_i(to_integer(unsigned(ctrl(ctrl_prsc2_c downto ctrl_prsc0_c)))) or ctrl(ctrl_highspeed_c); -- clock select - - -- Transmission Data Size ----------------------------------------------------------------- + -- RTX FIFO ("Ring Buffer") --------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - data_size: process(ctrl) - begin - case ctrl(ctrl_size1_c downto ctrl_size0_c) is - when "00" => rtx_engine.bytecnt <= "001"; -- 1-byte mode - when "01" => rtx_engine.bytecnt <= "010"; -- 2-byte mode - when "10" => rtx_engine.bytecnt <= "011"; -- 3-byte mode - when others => rtx_engine.bytecnt <= "100"; -- 4-byte mode - end case; - end process data_size; + rxt_fifo_gen: + if (IO_SPI_FIFO > 0) generate + + -- TX FIFO ------------------------------------------------ + tx_fifo_inst: neorv32_fifo + generic map ( + FIFO_DEPTH => IO_SPI_FIFO, -- number of fifo entries; has to be a power of two; min 1 + FIFO_WIDTH => 32, -- size of data elements in fifo (32-bit only for simulation) + FIFO_RSYNC => false, -- async read + FIFO_SAFE => true -- safe access + ) + port map ( + -- control -- + clk_i => clk_i, -- clock, rising edge + rstn_i => '1', -- async reset, low-active + clear_i => tx_fifo.clear, -- sync reset, high-active + level_o => open, + half_o => tx_fifo.half, -- FIFO at least half-full + -- write port -- + wdata_i => tx_fifo.wdata, -- write data + we_i => tx_fifo.we, -- write enable + free_o => tx_fifo.free, -- at least one entry is free when set + -- read port -- + re_i => tx_fifo.re, -- read enable + rdata_o => tx_fifo.rdata, -- read data + avail_o => tx_fifo.avail -- data available when set + ); + + -- reset -- + tx_fifo.clear <= not ctrl(ctrl_en_c); + + -- write access -- + tx_fifo.we <= '1' when (wren = '1') and (addr = spi_rtx_addr_c) else '0'; + tx_fifo.wdata <= data_i; + + -- read access -- + tx_fifo.re <= '1' when (rtx_engine.state = "100") and (tx_fifo.avail = '1') else '0'; + rtx_engine.start <= tx_fifo.re; + + + -- RX FIFO ------------------------------------------------ + rx_fifo_inst: neorv32_fifo + generic map ( + FIFO_DEPTH => IO_SPI_FIFO, -- number of fifo entries; has to be a power of two; min 1 + FIFO_WIDTH => 32, -- size of data elements in fifo (32-bit only for simulation) + FIFO_RSYNC => false, -- async read + FIFO_SAFE => true -- safe access + ) + port map ( + -- control -- + clk_i => clk_i, -- clock, rising edge + rstn_i => '1', -- async reset, low-active + clear_i => rx_fifo.clear, -- sync reset, high-active + level_o => open, + half_o => rx_fifo.half, -- FIFO at least half-full + -- write port -- + wdata_i => rx_fifo.wdata, -- write data + we_i => rx_fifo.we, -- write enable + free_o => rx_fifo.free, -- at least one entry is free when set + -- read port -- + re_i => rx_fifo.re, -- read enable + rdata_o => rx_fifo.rdata, -- read data + avail_o => rx_fifo.avail -- data available when set + ); + + -- reset -- + rx_fifo.clear <= not ctrl(ctrl_en_c); + + -- write access -- + rx_fifo.wdata <= rtx_engine.sreg; + rx_fifo.we <= rtx_engine.done; + + -- read access -- + rx_fifo.re <= '1' when (rden = '1') and (addr = spi_rtx_addr_c) else '0'; + + end generate; + + + -- no FIFO at all ----------------------------------------- + rtx_fifo_terminate: + if (IO_SPI_FIFO = 0) generate + -- TX -- + tx_fifo.clear <= '0'; + tx_fifo.half <= '0'; + tx_fifo.wdata <= (others => '0'); + tx_fifo.we <= '0'; + tx_fifo.free <= '0'; + tx_fifo.re <= '0'; + tx_fifo.rdata <= data_i; + tx_fifo.avail <= '0'; + -- RX -- + rx_fifo.clear <= '0'; + rx_fifo.half <= '0'; + rx_fifo.wdata <= (others => '0'); + rx_fifo.we <= '0'; + rx_fifo.free <= '0'; + rx_fifo.re <= '0'; + rx_fifo.rdata <= rtx_engine.sreg; + rx_fifo.avail <= '0'; + -- SPI PHY trigger -- + rtx_engine.start <= '1' when (wren = '1') and (addr = spi_rtx_addr_c) else '0'; -- trigger new SPI transmission + end generate; -- SPI Transceiver ------------------------------------------------------------------------ @@ -224,7 +343,7 @@ begin begin if rising_edge(clk_i) then -- defaults -- - irq_o <= '0'; + rtx_engine.done <= '0'; -- serial engine -- rtx_engine.state(2) <= ctrl(ctrl_en_c); @@ -235,7 +354,7 @@ begin spi_sck_o <= ctrl(ctrl_cpol_c); rtx_engine.bitcnt <= (others => '0'); if (rtx_engine.start = '1') then -- trigger new transmission - rtx_engine.sreg <= data_i; + rtx_engine.sreg <= tx_fifo.rdata; rtx_engine.state(1 downto 0) <= "01"; end if; @@ -263,7 +382,7 @@ begin rtx_engine.sreg <= rtx_engine.sreg(30 downto 0) & rtx_engine.sdi_sync; -- shift and set output if (rtx_engine.bitcnt(5 downto 3) = rtx_engine.bytecnt) then -- all bits transferred? spi_sck_o <= ctrl(ctrl_cpol_c); - irq_o <= '1'; -- interrupt! + rtx_engine.done <= '1'; -- done! rtx_engine.state(1 downto 0) <= "00"; -- transmission done else spi_sck_o <= ctrl(ctrl_cpha_c) xor ctrl(ctrl_cpol_c); @@ -281,19 +400,43 @@ begin end if; end process spi_rtx_unit; - -- busy flag -- + -- clock generator -- + clkgen_en_o <= ctrl(ctrl_en_c); -- clock generator enable + spi_clk_en <= clkgen_i(to_integer(unsigned(ctrl(ctrl_prsc2_c downto ctrl_prsc0_c)))) or ctrl(ctrl_highspeed_c); -- clock select + + -- PHY busy flag -- rtx_engine.busy <= '0' when (rtx_engine.state(1 downto 0) = "00") else '1'; - -- output bit select -- - spi_output: process(ctrl, rtx_engine) + -- transmission size -- + data_size: process(ctrl, rtx_engine) begin case ctrl(ctrl_size1_c downto ctrl_size0_c) is - when "00" => spi_sdo_o <= rtx_engine.sreg(07); -- 8-bit mode - when "01" => spi_sdo_o <= rtx_engine.sreg(15); -- 16-bit mode - when "10" => spi_sdo_o <= rtx_engine.sreg(23); -- 24-bit mode - when others => spi_sdo_o <= rtx_engine.sreg(31); -- 32-bit mode + when "00" => rtx_engine.bytecnt <= "001"; spi_sdo_o <= rtx_engine.sreg(07); -- 8-bit mode + when "01" => rtx_engine.bytecnt <= "010"; spi_sdo_o <= rtx_engine.sreg(15); -- 16-bit mode + when "10" => rtx_engine.bytecnt <= "011"; spi_sdo_o <= rtx_engine.sreg(23); -- 24-bit mode + when others => rtx_engine.bytecnt <= "100"; spi_sdo_o <= rtx_engine.sreg(31); -- 32-bit mode end case; - end process spi_output; + end process data_size; + + -- direct chip-select; low-active -- + spi_csn_o(7 downto 0) <= not ctrl(ctrl_cs7_c downto ctrl_cs0_c); + + + -- Interrupt Generator -------------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- + irq_generator: process(clk_i) + begin + if rising_edge(clk_i) then + case ctrl(ctrl_irq1_c downto ctrl_irq0_c) is + when "10" => irq_gen <= irq_gen(0) & (not tx_fifo.half); -- TX FIFO _becomes_ less than half full + when "11" => irq_gen <= irq_gen(0) & (not tx_fifo.avail); -- TX FIFO _becomes_ empty + when others => irq_gen <= irq_gen(0) & rtx_engine.done; -- current SPI transmission done (default) + end case; + end if; + end process irq_generator; + + -- CPU interrupt -- + irq_o <= '1' when (ctrl(ctrl_en_c) = '1') and (irq_gen = "01") else '0'; -- rising edge detector end neorv32_spi_rtl; diff --git a/rtl/core/neorv32_top.vhd b/rtl/core/neorv32_top.vhd index 864007461..49669ae76 100644 --- a/rtl/core/neorv32_top.vhd +++ b/rtl/core/neorv32_top.vhd @@ -126,6 +126,7 @@ entity neorv32_top 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)? @@ -1243,6 +1244,9 @@ begin neorv32_spi_inst_true: if (IO_SPI_EN = true) generate neorv32_spi_inst: neorv32_spi + generic map ( + IO_SPI_FIFO => IO_SPI_FIFO -- SPI RTX fifo depth, has to be zero or a power of two + ) port map ( -- host access -- clk_i => clk_i, -- global clock line diff --git a/rtl/system_integration/neorv32_ProcessorTop_stdlogic.vhd b/rtl/system_integration/neorv32_ProcessorTop_stdlogic.vhd index 0e2d7ad7f..2eb519c91 100644 --- a/rtl/system_integration/neorv32_ProcessorTop_stdlogic.vhd +++ b/rtl/system_integration/neorv32_ProcessorTop_stdlogic.vhd @@ -108,6 +108,7 @@ entity neorv32_ProcessorTop_stdlogic 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 := true; -- 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 := true; -- implement two-wire interface (TWI)? IO_PWM_NUM_CH : natural := 4; -- number of PWM channels to implement (0..60); 0 = disabled IO_WDT_EN : boolean := true; -- implement watch dog timer (WDT)? @@ -352,6 +353,7 @@ begin IO_UART1_RX_FIFO => IO_UART1_RX_FIFO, -- RX fifo depth, has to be a power of two, min 1 IO_UART1_TX_FIFO => IO_UART1_TX_FIFO, -- TX fifo depth, has to be a power of two, min 1 IO_SPI_EN => IO_SPI_EN, -- implement serial peripheral interface (SPI)? + IO_SPI_FIFO => IO_SPI_FIFO, -- SPI RTX fifo depth, has to be zero or a power of two IO_TWI_EN => IO_TWI_EN, -- implement two-wire interface (TWI)? IO_PWM_NUM_CH => IO_PWM_NUM_CH, -- number of PWM channels to implement (0..60); 0 = disabled IO_WDT_EN => IO_WDT_EN, -- implement watch dog timer (WDT)? diff --git a/rtl/system_integration/neorv32_SystemTop_AvalonMM.vhd b/rtl/system_integration/neorv32_SystemTop_AvalonMM.vhd index f05d1b483..01940bf71 100644 --- a/rtl/system_integration/neorv32_SystemTop_AvalonMM.vhd +++ b/rtl/system_integration/neorv32_SystemTop_AvalonMM.vhd @@ -116,6 +116,7 @@ entity neorv32_top_avalonmm 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)? @@ -321,6 +322,7 @@ begin IO_UART1_RX_FIFO => IO_UART1_RX_FIFO, IO_UART1_TX_FIFO => IO_UART1_TX_FIFO, IO_SPI_EN => IO_SPI_EN, + IO_SPI_FIFO => IO_SPI_FIFO, IO_TWI_EN => IO_TWI_EN, IO_PWM_NUM_CH => IO_PWM_NUM_CH, IO_WDT_EN => IO_WDT_EN, diff --git a/rtl/system_integration/neorv32_SystemTop_axi4lite.vhd b/rtl/system_integration/neorv32_SystemTop_axi4lite.vhd index 16dc68d06..ac5f13e94 100644 --- a/rtl/system_integration/neorv32_SystemTop_axi4lite.vhd +++ b/rtl/system_integration/neorv32_SystemTop_axi4lite.vhd @@ -102,8 +102,9 @@ entity neorv32_SystemTop_axi4lite 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 := true; -- 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 := true; -- implement two-wire interface (TWI)? - IO_PWM_NUM_CH : natural := 4; -- number of PWM channels to implement (0..60); 0 = disabled + IO_PWM_NUM_CH : natural := 0; -- number of PWM channels to implement (0..60); 0 = disabled IO_WDT_EN : boolean := true; -- implement watch dog timer (WDT)? IO_TRNG_EN : boolean := true; -- implement true random number generator (TRNG)? IO_TRNG_FIFO : natural := 1; -- TRNG fifo depth, has to be a power of two, min 1 @@ -345,6 +346,7 @@ begin IO_UART1_RX_FIFO => IO_UART1_RX_FIFO, -- RX fifo depth, has to be a power of two, min 1 IO_UART1_TX_FIFO => IO_UART1_TX_FIFO, -- TX fifo depth, has to be a power of two, min 1 IO_SPI_EN => IO_SPI_EN, -- implement serial peripheral interface (SPI)? + IO_SPI_FIFO => IO_SPI_FIFO, -- SPI RTX fifo depth, has to be zero or a power of two IO_TWI_EN => IO_TWI_EN, -- implement two-wire interface (TWI)? IO_PWM_NUM_CH => IO_PWM_NUM_CH, -- number of PWM channels to implement (0..60); 0 = disabled IO_WDT_EN => IO_WDT_EN, -- implement watch dog timer (WDT)? diff --git a/sim/neorv32_tb.vhd b/sim/neorv32_tb.vhd index 83b1760a8..cc0f1771d 100644 --- a/sim/neorv32_tb.vhd +++ b/sim/neorv32_tb.vhd @@ -341,6 +341,7 @@ begin IO_UART1_RX_FIFO => 1, -- RX fifo depth, has to be a power of two, min 1 IO_UART1_TX_FIFO => 1, -- TX fifo depth, has to be a power of two, min 1 IO_SPI_EN => true, -- implement serial peripheral interface (SPI)? + IO_SPI_FIFO => 4, -- SPI RTX fifo depth, has to be zero or a power of two IO_TWI_EN => true, -- implement two-wire interface (TWI)? IO_PWM_NUM_CH => 30, -- number of PWM channels to implement (0..60); 0 = disabled IO_WDT_EN => true, -- implement watch dog timer (WDT)? diff --git a/sim/simple/neorv32_tb.simple.vhd b/sim/simple/neorv32_tb.simple.vhd index 4a1964ff4..6c7704b2f 100644 --- a/sim/simple/neorv32_tb.simple.vhd +++ b/sim/simple/neorv32_tb.simple.vhd @@ -232,6 +232,7 @@ begin IO_UART1_RX_FIFO => 1, -- RX fifo depth, has to be a power of two, min 1 IO_UART1_TX_FIFO => 1, -- TX fifo depth, has to be a power of two, min 1 IO_SPI_EN => true, -- implement serial peripheral interface (SPI)? + IO_SPI_FIFO => 4, -- SPI RTX fifo depth, has to be zero or a power of two IO_TWI_EN => true, -- implement two-wire interface (TWI)? IO_PWM_NUM_CH => 30, -- number of PWM channels to implement (0..60); 0 = disabled IO_WDT_EN => true, -- implement watch dog timer (WDT)? diff --git a/sw/bootloader/bootloader.c b/sw/bootloader/bootloader.c index 630a2d513..2b46e7952 100644 --- a/sw/bootloader/bootloader.c +++ b/sw/bootloader/bootloader.c @@ -278,7 +278,7 @@ int main(void) { #if (SPI_EN != 0) // setup SPI for 8-bit, clock-mode 0 if (neorv32_spi_available()) { - neorv32_spi_setup(SPI_FLASH_CLK_PRSC, 0, 0, 0); + neorv32_spi_setup(SPI_FLASH_CLK_PRSC, 0, 0, 0, 0); } #endif diff --git a/sw/example/demo_spi/main.c b/sw/example/demo_spi/main.c index abc568907..b32a4fd39 100644 --- a/sw/example/demo_spi/main.c +++ b/sw/example/demo_spi/main.c @@ -315,7 +315,7 @@ void spi_setup(void) { } neorv32_uart0_printf("\n+ New SPI data size = %u byte(s)\n\n", tmp); - neorv32_spi_setup(spi_prsc, clk_phase, clk_pol, data_size); + neorv32_spi_setup(spi_prsc, clk_phase, clk_pol, data_size, 0); spi_configured = 1; // SPI is configured now spi_size = tmp; } diff --git a/sw/example/demo_spi_irq/drv/neorv32_spi_irq.c b/sw/example/demo_spi_irq/drv/neorv32_spi_irq.c index 6e869c5e4..52de4b4d2 100644 --- a/sw/example/demo_spi_irq/drv/neorv32_spi_irq.c +++ b/sw/example/demo_spi_irq/drv/neorv32_spi_irq.c @@ -45,6 +45,22 @@ #include "neorv32_spi_irq.h" +/**********************************************************************//** + * Initializes SPI flow control handle. The data structure elements are listed in #t_neorv32_spi. + * + * @param[in,out] *self SPI driver common data handle. See #t_neorv32_spi. + **************************************************************************/ +void neorv32_spi_init(t_neorv32_spi *self) { + + self->uint8IsBusy = 0; + self->uint32Fifo = (uint32_t) neorv32_spi_get_fifo_depth(); // acquire FIFO depth in elements + self->uint32Total = 0; + self->uint32Write = 0; // write element count + self->uint32Read = 0; // read element count + return; +} + + /**********************************************************************//** * SPI interrupt service routine. The data structure elements are listed in #t_neorv32_spi. * @@ -53,46 +69,77 @@ void neorv32_spi_isr(t_neorv32_spi *self) { uint32_t uint32Buf; // help variable + uint32_t uint32Lim; // loop limit + neorv32_cpu_csr_write(CSR_MIP, ~(1<uint32CurrentElem == self->uint32TotalElem ) { // leave if accicently called ISR + if ( 0 == self->uint32Total ) { // leave if accidentally called ISR return; } - uint32Buf = 0; // data type conversion switch (self->uint8SzElem) { case 1: // uint8_t - ((uint8_t *) self->ptrSpiBuf)[self->uint32CurrentElem] = (uint8_t) (NEORV32_SPI.DATA & 0xff); // capture from last transfer - if ( self->uint32CurrentElem == self->uint32TotalElem-1 ) { // transfer done, no new data + // read data from SPI from last transfer + for ( ; self->uint32Readuint32Write; (self->uint32Read)++ ) { + ((uint8_t *) self->ptrSpiBuf)[self->uint32Read] = (uint8_t) (NEORV32_SPI.DATA & 0xff); // capture from last transfer + } + if ( self->uint32Read == self->uint32Total ) { // transfer done, no new data neorv32_spi_cs_dis(self->uint8Csn); // deselect slave - break; // +1, signals complete + self->uint32Total = 0; + self->uint8IsBusy = 0; + break; + } + // write next packet + uint32Lim = min(self->uint32Write+self->uint32Fifo, self->uint32Total); + for ( ; self->uint32Writeuint32Write)++ ) { + uint32Buf = 0; + uint32Buf |= ((uint8_t *) self->ptrSpiBuf)[self->uint32Write]; + NEORV32_SPI.DATA = uint32Buf; // next transfer } - uint32Buf |= ((uint8_t *) self->ptrSpiBuf)[self->uint32CurrentElem+1]; - NEORV32_SPI.DATA = uint32Buf; // next transfer break; case 2: // uint16_t - ((uint16_t *) self->ptrSpiBuf)[self->uint32CurrentElem] = (uint16_t) (NEORV32_SPI.DATA & 0xffff); // capture from last transfer - if ( self->uint32CurrentElem == self->uint32TotalElem-1 ) { // transfer done, no new data + // read data from SPI from last transfer + for ( ; self->uint32Readuint32Write; (self->uint32Read)++ ) { + ((uint16_t *) self->ptrSpiBuf)[self->uint32Read] = (uint16_t) (NEORV32_SPI.DATA & 0xffff); // capture from last transfer + } + if ( self->uint32Read == self->uint32Total ) { // transfer done, no new data neorv32_spi_cs_dis(self->uint8Csn); // deselect slave - break; // +1, signals complete + self->uint32Total = 0; + self->uint8IsBusy = 0; + break; + } + // write next packet + uint32Lim = min(self->uint32Write+self->uint32Fifo, self->uint32Total); + for ( ; self->uint32Writeuint32Write)++ ) { + uint32Buf = 0; + uint32Buf |= ((uint16_t *) self->ptrSpiBuf)[self->uint32Write]; + NEORV32_SPI.DATA = uint32Buf; // next transfer } - uint32Buf |= ((uint16_t *) self->ptrSpiBuf)[self->uint32CurrentElem+1]; - NEORV32_SPI.DATA = uint32Buf; // next transfer break; case 4: // uint32_t - ((uint32_t *) self->ptrSpiBuf)[self->uint32CurrentElem] = NEORV32_SPI.DATA; // capture from last transfer - if ( self->uint32CurrentElem == self->uint32TotalElem-1 ) { // transfer done, no new data + // read data from SPI from last transfer + for ( ; self->uint32Readuint32Write; (self->uint32Read)++ ) { + ((uint32_t *) self->ptrSpiBuf)[self->uint32Read] = NEORV32_SPI.DATA; // capture from last transfer + } + if ( self->uint32Read == self->uint32Total ) { // transfer done, no new data neorv32_spi_cs_dis(self->uint8Csn); // deselect slave - break; // +1, signals complete + self->uint32Total = 0; + self->uint8IsBusy = 0; + break; + } + // write next packet + uint32Lim = min(self->uint32Write+self->uint32Fifo, self->uint32Total); + for ( ; self->uint32Writeuint32Write)++ ) { + uint32Buf = 0; + uint32Buf |= ((uint32_t *) self->ptrSpiBuf)[self->uint32Write]; + NEORV32_SPI.DATA = uint32Buf; // next transfer } - uint32Buf = ((uint32_t *) self->ptrSpiBuf)[self->uint32CurrentElem+1]; // next transfer - NEORV32_SPI.DATA = uint32Buf; // next transfer break; default: // unknown return; } - (self->uint32CurrentElem)++; // next element + return; } @@ -113,18 +160,22 @@ int neorv32_spi_rw(t_neorv32_spi *self, void *spi, uint8_t csn, uint32_t num_ele uint32_t uint32Buf; // help variable - if ( (self->uint32CurrentElem != self->uint32TotalElem) || (0 != neorv32_spi_busy()) ) { + if ( 0 != self->uint8IsBusy ) { return 1; // transfer active, no new request } - self->uint32CurrentElem = 0; - self->uint32TotalElem = num_elem; + self->uint32Total = num_elem; + self->uint32Write = 0; // write element count + self->uint32Read = 0; // read element count self->ptrSpiBuf = spi; self->uint8SzElem = data_byte; self->uint8Csn = csn; + self->uint8IsBusy = 1; // mark as busy + + neorv32_spi_cs_en(self->uint8Csn); // select SPI channel uint32Buf = 0; - switch (self->uint8SzElem) { // start first transfer, rest is handled by ISR + switch (self->uint8SzElem) { // start first transfer, rest is handled by ISR; can only sent one element, otherwise clash with ISR case 1: // uint8_t uint32Buf |= ((uint8_t *) self->ptrSpiBuf)[0]; break; @@ -136,10 +187,9 @@ int neorv32_spi_rw(t_neorv32_spi *self, void *spi, uint8_t csn, uint32_t num_ele break; default: return 2; // unsupported byte size - } - - neorv32_spi_cs_en(self->uint8Csn); // select SPI channel - NEORV32_SPI.DATA = uint32Buf; // next transfer + } + (self->uint32Write)++; + NEORV32_SPI.DATA = uint32Buf; // next transfer return 0; // successful end } @@ -155,7 +205,7 @@ int neorv32_spi_rw(t_neorv32_spi *self, void *spi, uint8_t csn, uint32_t num_ele **************************************************************************/ int neorv32_spi_rw_busy(t_neorv32_spi *self) { - if ( self->uint32TotalElem != self->uint32CurrentElem ) { + if ( 0 != self->uint8IsBusy ) { return 1; } return 0; diff --git a/sw/example/demo_spi_irq/drv/neorv32_spi_irq.h b/sw/example/demo_spi_irq/drv/neorv32_spi_irq.h index 48e9afb86..ac670fc95 100644 --- a/sw/example/demo_spi_irq/drv/neorv32_spi_irq.h +++ b/sw/example/demo_spi_irq/drv/neorv32_spi_irq.h @@ -44,20 +44,31 @@ #ifndef neorv32_spi_irq_h #define neorv32_spi_irq_h +// MIN macro +// https://stackoverflow.com/questions/3437404/min-and-max-in-c +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + // data handle for ISR typedef struct t_neorv32_spi { - void* ptrSpiBuf; /**< SPI buffer data pointer */ - uint8_t uint8SzElem; /**< Element Size in byte */ - uint8_t uint8Csn; /**< SPI chip select channel */ - uint32_t uint32TotalElem; /**< Number of elements in buffer */ - uint32_t uint32CurrentElem; /**< Buffer element in transfer */ + void* ptrSpiBuf; /**< SPI buffer data pointer */ + uint8_t uint8SzElem; /**< Element Size in byte */ + uint8_t uint8Csn; /**< SPI chip select channel */ + uint16_t uint32Fifo; /**< Number of elements in Fifo */ + uint32_t uint32Total; /**< Number of elements in buffer */ + uint32_t uint32Write; /**< To SPI core write elements */ + uint32_t uint32Read; /**< From SPI core read elements */ + uint8_t uint8IsBusy; /**< Spi Core is Busy*/ } t_neorv32_spi; // prototypes -void neorv32_spi_isr(t_neorv32_spi *self); -int neorv32_spi_rw(t_neorv32_spi *self, void *spi, uint8_t csn, uint32_t num_elem, uint8_t data_byte); -int neorv32_spi_rw_busy(t_neorv32_spi *self); +void neorv32_spi_init(t_neorv32_spi *self); +void neorv32_spi_isr(t_neorv32_spi *self); +int neorv32_spi_rw(t_neorv32_spi *self, void *spi, uint8_t csn, uint32_t num_elem, uint8_t data_byte); +int neorv32_spi_rw_busy(t_neorv32_spi *self); #endif // neorv32_spi_irq_h diff --git a/sw/example/demo_spi_irq/main.c b/sw/example/demo_spi_irq/main.c index 41fad1e76..4ec1827bc 100644 --- a/sw/example/demo_spi_irq/main.c +++ b/sw/example/demo_spi_irq/main.c @@ -108,9 +108,13 @@ int main() neorv32_cpu_irq_enable(SPI_FIRQ_ENABLE); // FIRQ6: SPI Interrupt neorv32_cpu_eint(); // enable global interrupts - // configure SPI + // SPI + // SPI Control + neorv32_spi_init(&g_neorv32_spi); + // Configure neorv32_spi_disable(); - neorv32_spi_setup(0, 0, 0, 0); // spi mode 0, 8bit + // neorv32_spi_setup(int prsc, int clk_phase, int clk_polarity, int data_size, int irq_config) + neorv32_spi_setup(0, 0, 0, 0, 3); // spi mode 0, 8bit, IRQ: 0-: PHY going idle, 10: TX fifo less than half full, 11: TX fifo empty neorv32_spi_enable(); // IRQ based data transfer diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index d0a26dbff..fdf3f8447 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -1217,7 +1217,7 @@ int main() { cnt_test++; // configure SPI - neorv32_spi_setup(CLK_PRSC_2, 0, 0, 0); + neorv32_spi_setup(CLK_PRSC_2, 0, 0, 0, 0); // IRQ when SPI PHY becomes idle // enable fast interrupt neorv32_cpu_irq_enable(CSR_MIE_FIRQ6E); diff --git a/sw/lib/include/neorv32.h b/sw/lib/include/neorv32.h index be4a418df..ea2df5e7b 100644 --- a/sw/lib/include/neorv32.h +++ b/sw/lib/include/neorv32.h @@ -1119,7 +1119,15 @@ enum NEORV32_SPI_CTRL_enum { SPI_CTRL_SIZE1 = 14, /**< SPI control register(14) (r/w): Transfer data size msb (00: 8-bit, 01: 16-bit, 10: 24-bit, 11: 32-bit) */ SPI_CTRL_CPOL = 15, /**< SPI control register(15) (r/w): Clock polarity */ SPI_CTRL_HIGHSPEED = 16, /**< SPI control register(16) (r/w): SPI high-speed mode enable (ignoring SPI_CTRL_PRSC) */ - + SPI_CTRL_IRQ0 = 17, /**< SPI control register(17) (r/w): Interrupt configuration lsb (0-: PHY going idle) */ + SPI_CTRL_IRQ1 = 18, /**< SPI control register(18) (r/w): Interrupt configuration lsb (10: TX fifo less than half full, 11: TX fifo empty) */ + SPI_CTRL_FIFO_LSB = 19, /**< SPI control register(19) (r/-): log2(FIFO size), lsb */ + SPI_CTRL_FIFO_MSB = 22, /**< SPI control register(22) (r/-): log2(FIFO size), msb */ + + SPI_CTRL_RX_AVAIL = 27, /**< SPI control register(27) (r/-): RX FIFO data available (RX FIFO not empty) */ + SPI_CTRL_TX_EMPTY = 28, /**< SPI control register(28) (r/-): TX FIFO empty */ + SPI_CTRL_TX_HALF = 29, /**< SPI control register(29) (r/-): TX FIFO at least half full */ + SPI_CTRL_TX_FULL = 30, /**< SPI control register(30) (r/-): TX FIFO full */ SPI_CTRL_BUSY = 31 /**< SPI control register(31) (r/-): SPI busy flag */ }; /**@}*/ diff --git a/sw/lib/include/neorv32_spi.h b/sw/lib/include/neorv32_spi.h index ef433e707..8012d1fc2 100644 --- a/sw/lib/include/neorv32_spi.h +++ b/sw/lib/include/neorv32_spi.h @@ -45,13 +45,14 @@ // prototypes int neorv32_spi_available(void); -void neorv32_spi_setup(uint8_t prsc, uint8_t clk_phase, uint8_t clk_polarity, uint8_t data_size); +void neorv32_spi_setup(int prsc, int clk_phase, int clk_polarity, int data_size, int irq_config); void neorv32_spi_disable(void); void neorv32_spi_enable(void); +int neorv32_spi_get_fifo_depth(void); void neorv32_spi_highspeed_enable(void); void neorv32_spi_highspeed_disable(void); -void neorv32_spi_cs_en(uint8_t cs); -void neorv32_spi_cs_dis(uint8_t cs); +void neorv32_spi_cs_en(int cs); +void neorv32_spi_cs_dis(int cs); uint32_t neorv32_spi_trans(uint32_t tx_data); void neorv32_spi_put_nonblocking(uint32_t tx_data); uint32_t neorv32_spi_get_nonblocking(void); diff --git a/sw/lib/source/neorv32_spi.c b/sw/lib/source/neorv32_spi.c index d8148065f..e7802406d 100644 --- a/sw/lib/source/neorv32_spi.c +++ b/sw/lib/source/neorv32_spi.c @@ -67,27 +67,21 @@ int neorv32_spi_available(void) { * @param[in] clk_phase Clock phase (0=sample on rising edge, 1=sample on falling edge). * @param[in] clk_polarity Clock polarity (when idle). * @param[in] data_size Data transfer size (0: 8-bit, 1: 16-bit, 2: 24-bit, 3: 32-bit). + * @param[in] irq_config Interrupt configuration (0,1: PHY transfer done, 2: TX FIFO becomes less than half full, 3: TX FIFO becomes empty). **************************************************************************/ -void neorv32_spi_setup(uint8_t prsc, uint8_t clk_phase, uint8_t clk_polarity, uint8_t data_size) { +void neorv32_spi_setup(int prsc, int clk_phase, int clk_polarity, int data_size, int irq_config) { NEORV32_SPI.CTRL = 0; // reset - uint32_t ct_enable = 1; - ct_enable = ct_enable << SPI_CTRL_EN; + uint32_t tmp = 0; + tmp = 1 << SPI_CTRL_EN; + tmp |= (uint32_t)(prsc & 0x07) << SPI_CTRL_PRSC0; + tmp |= (uint32_t)(clk_phase & 0x01) << SPI_CTRL_CPHA; + tmp |= (uint32_t)(clk_polarity & 0x01) << SPI_CTRL_CPOL; + tmp |= (uint32_t)(data_size & 0x03) << SPI_CTRL_SIZE0; + tmp |= (uint32_t)(irq_config & 0x03) << SPI_CTRL_IRQ0; - uint32_t ct_prsc = (uint32_t)(prsc & 0x07); - ct_prsc = ct_prsc << SPI_CTRL_PRSC0; - - uint32_t ct_phase = (uint32_t)(clk_phase & 0x01); - ct_phase = ct_phase << SPI_CTRL_CPHA; - - uint32_t ct_polarity = (uint32_t)(clk_polarity & 0x01); - ct_polarity = ct_polarity << SPI_CTRL_CPOL; - - uint32_t ct_size = (uint32_t)(data_size & 0x03); - ct_size = ct_size << SPI_CTRL_SIZE0; - - NEORV32_SPI.CTRL = ct_enable | ct_prsc | ct_phase | ct_polarity | ct_size; + NEORV32_SPI.CTRL = tmp; } @@ -109,6 +103,18 @@ void neorv32_spi_enable(void) { } +/**********************************************************************//** + * Get SPI FIFO depth. + * + * @return FIFO depth (number of entries), zero if no FIFO implemented + **************************************************************************/ +int neorv32_spi_get_fifo_depth(void) { + + uint32_t tmp = (NEORV32_SPI.CTRL >> SPI_CTRL_FIFO_LSB) & 0x0f; + return (int)(1 << tmp); +} + + /**********************************************************************//** * Enable high-speed SPI mode (running at half of the processor clock). * @@ -138,7 +144,7 @@ void neorv32_spi_highspeed_disable(void) { * * @param cs Chip select line to activate (0..7). **************************************************************************/ -void neorv32_spi_cs_en(uint8_t cs) { +void neorv32_spi_cs_en(int cs) { uint32_t cs_mask = (uint32_t)(1 << (cs & 0x07)); cs_mask = cs_mask << SPI_CTRL_CS0; @@ -153,7 +159,7 @@ void neorv32_spi_cs_en(uint8_t cs) { * * @param cs Chip select line to deactivate (0..7). **************************************************************************/ -void neorv32_spi_cs_dis(uint8_t cs) { +void neorv32_spi_cs_dis(int cs) { uint32_t cs_mask = (uint32_t)(1 << (cs & 0x07)); cs_mask = cs_mask << SPI_CTRL_CS0; diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index 2e003e41a..cfd505500 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -771,6 +771,41 @@ [16:16] SPI high-speed mode enable (ignoring SPI_CTRL_PRSC) + + SPI_CTRL_IRQ + [18:17] + Interrupt configuration + + + SPI_CTRL_FIFO + [22:19] + read-only + log2(FIFO size) + + + SPI_CTRL_TX_EMPTY + [25:25] + read-only + TX FIFO is empty + + + SPI_CTRL_RX_AVAIL + [28:28] + read-only + RX FIFO data available (RX FIFO not empty) + + + SPI_CTRL_TX_HALF + [29:29] + read-only + TX FIFO is at least half full + + + SPI_CTRL_TX_FULL + [30:30] + read-only + TX FIFO is full + SPI_CTRL_BUSY [31:31]