Skip to content

Commit

Permalink
[SPI] re-add high-speed mode (#730)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting authored Nov 18, 2023
2 parents 09c92a7 + 16ed6ca commit 56a30c9
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date (*dd.mm.yyyy*) | Version | Comment |
|:-------------------:|:-------:|:--------|
| 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) |
Expand Down
23 changes: 17 additions & 6 deletions docs/datasheet/soc_spi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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`))
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -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!

Expand Down
43 changes: 25 additions & 18 deletions rtl/core/neorv32_spi.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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';
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -208,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;

Expand Down Expand Up @@ -317,8 +326,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;

Expand Down Expand Up @@ -371,25 +380,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;
Expand Down
3 changes: 3 additions & 0 deletions sw/lib/include/neorv32_spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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);
Expand Down
30 changes: 28 additions & 2 deletions sw/lib/source/neorv32_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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;
}

Expand Down

0 comments on commit 56a30c9

Please sign in to comment.