From a6cf65d4b074fdc21aba82e9886db99a1ae490cd Mon Sep 17 00:00:00 2001 From: stnolting <22944758+stnolting@users.noreply.github.com> Date: Sat, 18 Nov 2023 17:39:52 +0100 Subject: [PATCH 1/3] [SPI] re-add SPI high-speed mode --- docs/datasheet/soc_spi.adoc | 23 +++++++++++++++++------ rtl/core/neorv32_package.vhd | 2 +- rtl/core/neorv32_spi.vhd | 31 +++++++++++++++++-------------- sw/lib/include/neorv32_spi.h | 3 +++ sw/lib/source/neorv32_spi.c | 30 ++++++++++++++++++++++++++++-- 5 files changed, 66 insertions(+), 23 deletions(-) diff --git a/docs/datasheet/soc_spi.adoc b/docs/datasheet/soc_spi.adoc index 49492811c..7482ba304 100644 --- a/docs/datasheet/soc_spi.adoc +++ b/docs/datasheet/soc_spi.adoc @@ -68,8 +68,7 @@ image::SPI_timing_diagram2.wikimedia.png[] The SPI clock frequency (`spi_clk_o`) is programmed by the 3-bit `SPI_CTRL_PRSCx` clock prescaler for a coarse clock selection and a 4-bit clock divider `SPI_CTRL_CDIVx` for a fine clock configuration. - -The following clock prescalers (_SPI_CTRL_PRSCx_) are available: +The following clock prescalers (`SPI_CTRL_PRSCx`) are available: .SPI prescaler configuration [cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"] @@ -79,7 +78,7 @@ The following clock prescalers (_SPI_CTRL_PRSCx_) are available: | Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096 |======================= -Based on the programmen clock configuration, the actual SPI clock frequency f~SPI~ is derived +Based on the programmed clock 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`)) @@ -88,6 +87,17 @@ Hence, the maximum SPI clock is f~main~ / 4 and the lowest SPI clock is f~main~ symmetric having a duty cycle of 50%. +**High-Speed Mode** + +The SPI provides a high-speed mode to further boost the maximum SPI clock frequency. When enabled via the control +register's `SPI_CTRL_HIGHSPEED` bit the clock prescaler configuration (`SPI_CTRL_PRSCx` bits) is overridden setting it +to a minimal factor of 1. However, the clock speed can still be fine-tuned using the `SPI_CTRL_CDIVx` bits. + +_**f~SPI~**_ = _f~main~[Hz]_ / (2 * 1 * (1 + `SPI_CTRL_CDIVx`)) + +Hence, the maximum SPI clock when in high-speed mode is f~main~ / 2. + + **SPI Interrupt** The SPI module provides a set of programmable interrupt conditions based on the level of the RX/TX FIFO. The different @@ -107,14 +117,15 @@ Furthermore, an active SPI interrupt has to be explicitly cleared again by writi [options="header",grid="all"] |======================= | Address | Name [C] | Bit(s), Name [C] | R/W | Function -.18+<| `0xfffff800` .18+<| `CTRL` <|`0` `SPI_CTRL_EN` ^| r/w <| SPI module enable +.19+<| `0xfffff800` .19+<| `CTRL` <|`0` `SPI_CTRL_EN` ^| r/w <| SPI module enable <|`1` `SPI_CTRL_CPHA` ^| r/w <| clock phase <|`2` `SPI_CTRL_CPOL` ^| r/w <| clock polarity <|`5:3` `SPI_CTRL_CS_SEL2 : SPI_CTRL_CS_SEL0` ^| r/w <| Direct chip-select 0..7 <|`6` `SPI_CTRL_CS_EN` ^| r/w <| Direct chip-select enable: setting `spi_csn_o(SPI_CTRL_CS_SEL)` low when set <|`9:7` `SPI_CTRL_PRSC2 : SPI_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select - <|`13:10` `SPI_CTRL_CDIV2 : SPI_CTRL_CDIV0` ^| r/w <| 4-bit clock divider - <|`15:14` _reserved_ ^| r/- <| reserved, read as zero + <|`13:10` `SPI_CTRL_CDIV2 : SPI_CTRL_CDIV0` ^| r/w <| 4-bit clock divider for fine-tuning + <|`14` `SPI_CTRL_HIGHSPEED` ^| r/w <| high-speed mode enable (overriding `SPI_CTRL_PRSC`) + <|`15` _reserved_ ^| r/- <| reserved, read as zero <|`16` `SPI_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available (RX FIFO not empty) <|`17` `SPI_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty <|`18` `SPI_CTRL_TX_NHALF` ^| r/- <| TX FIFO _not_ at least half full diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index 770ceb3d1..78d400ca0 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -59,7 +59,7 @@ package neorv32_package is -- Architecture Constants ----------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090100"; -- hardware version + constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090101"; -- hardware version constant archid_c : natural := 19; -- official RISC-V architecture ID constant XLEN : natural := 32; -- native data path width, do not change! diff --git a/rtl/core/neorv32_spi.vhd b/rtl/core/neorv32_spi.vhd index 3385fc366..e8a12f35f 100644 --- a/rtl/core/neorv32_spi.vhd +++ b/rtl/core/neorv32_spi.vhd @@ -75,6 +75,7 @@ architecture neorv32_spi_rtl of neorv32_spi is constant ctrl_cdiv1_c : natural := 11; -- r/w: clock divider bit 1 constant ctrl_cdiv2_c : natural := 12; -- r/w: clock divider bit 2 constant ctrl_cdiv3_c : natural := 13; -- r/w: clock divider bit 3 + constant ctrl_highspeed_c : natural := 14; -- r/w: high-speed mode -- constant ctrl_rx_avail_c : natural := 16; -- r/-: rx fifo data available (fifo not empty) constant ctrl_tx_empty_c : natural := 17; -- r/-: tx fifo empty @@ -99,6 +100,7 @@ architecture neorv32_spi_rtl of neorv32_spi is cs_en : std_ulogic; prsc : std_ulogic_vector(2 downto 0); cdiv : std_ulogic_vector(3 downto 0); + highspeed : std_ulogic; irq_rx_avail : std_ulogic; irq_tx_empty : std_ulogic; irq_tx_nhalf : std_ulogic; @@ -116,7 +118,7 @@ architecture neorv32_spi_rtl of neorv32_spi is sreg : std_ulogic_vector(7 downto 0); bitcnt : std_ulogic_vector(3 downto 0); sdi_sync : std_ulogic; - sck : std_ulogic; + sck : std_ulogic; done : std_ulogic; end record; signal rtx_engine : rtx_engine_t; @@ -151,6 +153,7 @@ begin ctrl.cs_en <= '0'; ctrl.prsc <= (others => '0'); ctrl.cdiv <= (others => '0'); + ctrl.highspeed <= '0'; ctrl.irq_rx_avail <= '0'; ctrl.irq_tx_empty <= '0'; ctrl.irq_tx_nhalf <= '0'; @@ -171,6 +174,7 @@ begin ctrl.cs_en <= bus_req_i.data(ctrl_cs_en_c); ctrl.prsc <= bus_req_i.data(ctrl_prsc2_c downto ctrl_prsc0_c); ctrl.cdiv <= bus_req_i.data(ctrl_cdiv3_c downto ctrl_cdiv0_c); + ctrl.highspeed <= bus_req_i.data(ctrl_highspeed_c); ctrl.irq_rx_avail <= bus_req_i.data(ctrl_irq_rx_avail_c); ctrl.irq_tx_empty <= bus_req_i.data(ctrl_irq_tx_empty_c); ctrl.irq_tx_nhalf <= bus_req_i.data(ctrl_irq_tx_nhalf_c); @@ -186,6 +190,7 @@ begin bus_rsp_o.data(ctrl_cs_en_c) <= ctrl.cs_en; bus_rsp_o.data(ctrl_prsc2_c downto ctrl_prsc0_c) <= ctrl.prsc; bus_rsp_o.data(ctrl_cdiv3_c downto ctrl_cdiv0_c) <= ctrl.cdiv; + bus_rsp_o.data(ctrl_highspeed_c) <= ctrl.highspeed; -- bus_rsp_o.data(ctrl_rx_avail_c) <= rx_fifo.avail; bus_rsp_o.data(ctrl_tx_empty_c) <= not tx_fifo.avail; @@ -317,8 +322,8 @@ begin -- ------------------------------------------------------------ rtx_engine.sck <= ctrl.cpol; rtx_engine.bitcnt <= (others => '0'); - rtx_engine.sreg <= tx_fifo.rdata; if (tx_fifo.avail = '1') then -- trigger new transmission + rtx_engine.sreg <= tx_fifo.rdata; rtx_engine.state(1 downto 0) <= "01"; end if; @@ -371,25 +376,23 @@ begin spi_clk_o <= rtx_engine.sck; - -- clock generator -- + -- SPI Clock Generator -------------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- clock_generator: process(rstn_i, clk_i) begin if (rstn_i = '0') then spi_clk_en <= '0'; cdiv_cnt <= (others => '0'); elsif rising_edge(clk_i) then + spi_clk_en <= '0'; -- default if (ctrl.enable = '0') then -- reset/disabled - spi_clk_en <= '0'; - cdiv_cnt <= (others => '0'); - else - spi_clk_en <= '0'; -- default - if (clkgen_i(to_integer(unsigned(ctrl.prsc))) = '1') then -- pre-scaled clock - if (cdiv_cnt = ctrl.cdiv) then -- clock divider for fine-tuning - spi_clk_en <= '1'; - cdiv_cnt <= (others => '0'); - else - cdiv_cnt <= std_ulogic_vector(unsigned(cdiv_cnt) + 1); - end if; + cdiv_cnt <= (others => '0'); + elsif (clkgen_i(to_integer(unsigned(ctrl.prsc))) = '1') or (ctrl.highspeed = '1') then -- pre-scaled clock + if (cdiv_cnt = ctrl.cdiv) then -- clock divider for fine-tuning + spi_clk_en <= '1'; + cdiv_cnt <= (others => '0'); + else + cdiv_cnt <= std_ulogic_vector(unsigned(cdiv_cnt) + 1); end if; end if; end if; diff --git a/sw/lib/include/neorv32_spi.h b/sw/lib/include/neorv32_spi.h index 3641d3c0c..80c46fdee 100644 --- a/sw/lib/include/neorv32_spi.h +++ b/sw/lib/include/neorv32_spi.h @@ -72,6 +72,7 @@ enum NEORV32_SPI_CTRL_enum { SPI_CTRL_CDIV1 = 11, /**< SPI control register(11) (r/w): Clock divider bit 1 */ SPI_CTRL_CDIV2 = 12, /**< SPI control register(12) (r/w): Clock divider bit 2 */ SPI_CTRL_CDIV3 = 13, /**< SPI control register(13) (r/w): Clock divider bit 3 */ + SPI_CTRL_HIGHSPEED = 14, /**< SPI control register(14) (r/w): High-speed mode */ SPI_CTRL_RX_AVAIL = 16, /**< SPI control register(16) (r/-): RX FIFO data available (RX FIFO not empty) */ SPI_CTRL_TX_EMPTY = 17, /**< SPI control register(17) (r/-): TX FIFO empty */ @@ -96,6 +97,8 @@ enum NEORV32_SPI_CTRL_enum { /**@{*/ int neorv32_spi_available(void); void neorv32_spi_setup(int prsc, int cdiv, int clk_phase, int clk_polarity, uint32_t irq_mask); +void neorv32_spi_highspeed_enable(void); +void neorv32_spi_highspeed_disable(void); uint32_t neorv32_spi_get_clock_speed(void); void neorv32_spi_disable(void); void neorv32_spi_enable(void); diff --git a/sw/lib/source/neorv32_spi.c b/sw/lib/source/neorv32_spi.c index 0ba7dd34f..ee8621b7b 100644 --- a/sw/lib/source/neorv32_spi.c +++ b/sw/lib/source/neorv32_spi.c @@ -85,6 +85,24 @@ void neorv32_spi_setup(int prsc, int cdiv, int clk_phase, int clk_polarity, uint } +/**********************************************************************//** + * Enable high-speed mode. + **************************************************************************/ +void neorv32_spi_highspeed_enable(void) { + + NEORV32_SPI->CTRL |= ((uint32_t)(1 << SPI_CTRL_HIGHSPEED)); +} + + +/**********************************************************************//** + * Disable high-speed mode. + **************************************************************************/ +void neorv32_spi_highspeed_disable(void) { + + NEORV32_SPI->CTRL &= ~((uint32_t)(1 << SPI_CTRL_HIGHSPEED)); +} + + /**********************************************************************//** * Get configured clock speed in Hz. * @@ -95,10 +113,18 @@ uint32_t neorv32_spi_get_clock_speed(void) { const uint16_t PRSC_LUT[8] = {2, 4, 8, 64, 128, 1024, 2048, 4096}; uint32_t ctrl = NEORV32_SPI->CTRL; - uint32_t prsc_sel = (ctrl >> SPI_CTRL_PRSC0) & 0x7; + uint32_t prsc_sel = (ctrl >> SPI_CTRL_PRSC0) & 0x7; uint32_t clock_div = (ctrl >> SPI_CTRL_CDIV0) & 0xf; - uint32_t tmp = 2 * PRSC_LUT[prsc_sel] * (1 + clock_div); + uint32_t tmp; + + if (ctrl & (1 << SPI_CTRL_HIGHSPEED)) { // high-speed mode enabled? + tmp = 2 * 1 * (1 + clock_div); + } + else { + tmp = 2 * PRSC_LUT[prsc_sel] * (1 + clock_div); + } + return NEORV32_SYSINFO->CLK / tmp; } From 8c499c5cd20aa384e3ab2ab0afc9c3f1bd1f1d17 Mon Sep 17 00:00:00 2001 From: stnolting <22944758+stnolting@users.noreply.github.com> Date: Sat, 18 Nov 2023 17:47:30 +0100 Subject: [PATCH 2/3] [changelog] add v1.9.1.1 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9405033f0..cb1a8aebd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12 | Date (*dd.mm.yyyy*) | Version | Comment | |:-------------------:|:-------:|:--------| +| 18.11.2023 | 1.9.1.1 | (re-)add SPI high-speed mode, :bug: fix bug in SPI shift register - introduced in v1.9.0.9; [730](https://github.com/stnolting/neorv32/pull/730) | | 14.11.2023 | [**:rocket:1.9.1**](https://github.com/stnolting/neorv32/releases/tag/v1.9.1) | **New release** | | 11.11.2023 | 1.9.0.9 | :test_tube: add full hardware reset for **all** flip flops in CPU/processor; [#724](https://github.com/stnolting/neorv32/pull/724) | | 09.11.2023 | 1.9.0.8 | minor rtl code cleanups; [#723](https://github.com/stnolting/neorv32/pull/723) | From 16ed6ca663d77e62dda709b589943e2652a04ce8 Mon Sep 17 00:00:00 2001 From: stnolting <22944758+stnolting@users.noreply.github.com> Date: Sat, 18 Nov 2023 17:47:51 +0100 Subject: [PATCH 3/3] [spi] add output registers for CS lines --- rtl/core/neorv32_spi.vhd | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rtl/core/neorv32_spi.vhd b/rtl/core/neorv32_spi.vhd index e8a12f35f..a1f47a45f 100644 --- a/rtl/core/neorv32_spi.vhd +++ b/rtl/core/neorv32_spi.vhd @@ -213,11 +213,15 @@ begin end process bus_access; -- direct chip-select (low-active) -- - chip_select: process(ctrl) + chip_select: process(rstn_i, clk_i) begin - spi_csn_o <= (others => '1'); -- default: all disabled - if (ctrl.cs_en = '1') and (ctrl.enable = '1') then - spi_csn_o(to_integer(unsigned(ctrl.cs_sel))) <= '0'; + if (rstn_i = '0') then + spi_csn_o <= (others => '1'); + elsif rising_edge(clk_i) then + spi_csn_o <= (others => '1'); -- default: all disabled + if (ctrl.cs_en = '1') and (ctrl.enable = '1') then + spi_csn_o(to_integer(unsigned(ctrl.cs_sel))) <= '0'; + end if; end if; end process chip_select;