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; }