Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPI] re-add high-speed mode #730

Merged
merged 3 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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