Skip to content

Commit

Permalink
[XIP] add clock divider for fine-tuning (#731)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting committed Nov 18, 2023
2 parents 56a30c9 + d456177 commit 5a827ea
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 31 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ 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) |
| 18.11.2023 | 1.9.1.2 | add XIP clock divider to fine-tune SPI frequency; [#731](https://github.com/stnolting/neorv32/pull/731) |
| 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
33 changes: 25 additions & 8 deletions docs/datasheet/soc_xip.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ An example program is provided in `sw/example/demo_xip` that illustrate how to p
an external SPI flash to run a program from it.


**SPI Protocol**
**SPI Configuration**

The XIP module accesses external flash using the standard SPI protocol. The module always sends data MSB-first and
provides all of the standard four clock modes (0..3), which are configured via the `XIP_CTRL_CPOL` (clock polarity)
and `XIP_CTRL_CPHA` (clock phase) control register bits, respectively. The clock speed of the interface (`xip_clk_o`)
is defined by a three-bit clock pre-scaler configured using the `XIP_CTRL_PRSCx` bits:
and `XIP_CTRL_CPHA` (clock phase) control register bits, respectively.

The SPI clock frequency (`xip_clk_o`) is programmed by the 3-bit `XIP_CTRL_PRSCx` clock prescaler for a coarse clock
selection and a 4-bit clock divider `XPI_CTRL_CDIVx` for a fine clock configuration.
The following clock prescalers (`XIP_CTRL_PRSCx`) are available:

.XIP prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
Expand All @@ -55,12 +58,25 @@ is defined by a three-bit clock pre-scaler configured using the `XIP_CTRL_PRSCx`
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================

Based on the clock configuration the actual XIP SPI clock frequency f~XIP~ is derived from the processor's
main clock f~main~ and is determined by:
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 + `XPI_CTRL_CDIVx`))

Hence, the maximum SPI clock is f~main~ / 4 and the lowest SPI clock is f~main~ / 131072. The SPI clock is always
symmetric having a duty cycle of 50%.


**High-Speed Mode**

The XIP module provides a high-speed mode to further boost the maximum SPI clock frequency. When enabled via the control
register's `XIP_CTRL_HIGHSPEED` bit the clock prescaler configuration (`XIP_CTRL_PRSCx` bits) is overridden setting it
to a minimal factor of 1. However, the clock speed can still be fine-tuned using the `XPI_CTRL_CDIVx` bits.

_**f~SPI~**_ = _f~main~[Hz]_ / (2 * 1 * (1 + `XPI_CTRL_CDIVx`))

_**f~XIP~**_ = _f~main~[Hz]_ / (2 * `clock_prescaler`)
Hence, the maximum SPI clock when in high-speed mode is f~main~ / 2.

Hence, the maximum XIP clock speed is f~main~ / 4.

.High-Speed SPI mode
[TIP]
Expand Down Expand Up @@ -170,7 +186,7 @@ By using the XIP burst mode flash read accesses can be accelerated by up to 50%.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.14+<| `0xffffff40` .14+<| `CTRL` <|`0` `XIP_CTRL_EN` ^| r/w <| XIP module enable
.15+<| `0xffffff40` .15+<| `CTRL` <|`0` `XIP_CTRL_EN` ^| r/w <| XIP module enable
<|`3:1` `XIP_CTRL_PRSC2 : XIP_CTRL_PRSC0` ^| r/w <| 3-bit SPI clock prescaler select
<|`4` `XIP_CTRL_CPOL` ^| r/w <| SPI clock polarity
<|`5` `XIP_CTRL_CPHA` ^| r/w <| SPI clock phase
Expand All @@ -181,6 +197,7 @@ By using the XIP burst mode flash read accesses can be accelerated by up to 50%.
<|`21` `XIP_CTRL_SPI_CSEN` ^| r/w <| Allow SPI chip-select to be actually asserted when set
<|`22` `XIP_CTRL_HIGHSPEED` ^| r/w <| enable SPI high-speed mode (ignoring _XIP_CTRL_PRSC_)
<|`23` `XIP_CTRL_BURST_EN` ^| r/w <| Enable XIP burst mode
<|`24:27` `XIP_CTRL_CDIV3 : XIP_CTRL_CDIV0` ^| r/- <| 4-bit clock divider for fine-tuning
<|`29:28` - ^| r/- <| _reserved_, read as zero
<|`30` `XIP_CTRL_PHY_BUSY` ^| r/- <| SPI PHY busy when set
<|`31` `XIP_CTRL_XIP_BUSY` ^| r/- <| XIP access in progress when set
Expand Down
8 changes: 4 additions & 4 deletions rtl/core/neorv32_bootloader_image.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
-- Auto-generated memory initialization file (for BOOTLOADER) from source file <bootloader/main.bin>
-- Size: 4028 bytes
-- MARCH: default
-- Built: 31.10.2023 17:09:02
-- Built: 18.11.2023 18:38:26

-- prototype defined in 'neorv32_package.vhd'
package body neorv32_bootloader_image is
Expand Down Expand Up @@ -879,9 +879,9 @@ x"6f6c746f",
x"72656461",
x"0a3e3e20",
x"444c420a",
x"4f203a56",
x"33207463",
x"30322031",
x"4e203a56",
x"3120766f",
x"30322038",
x"480a3332",
x"203a5657",
x"00000020",
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"01090101"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090102"; -- 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
39 changes: 32 additions & 7 deletions rtl/core/neorv32_xip.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,15 @@ architecture neorv32_xip_rtl of neorv32_xip is
constant ctrl_spi_csen_c : natural := 21; -- r/w: SPI chip-select enabled
constant ctrl_highspeed_c : natural := 22; -- r/w: SPI high-speed mode enable (ignoring ctrl_spi_prsc)
constant ctrl_burst_en_c : natural := 23; -- r/w: XIP burst mode enable
constant ctrl_cdiv0_c : natural := 24; -- r/w: clock divider bit 0
constant ctrl_cdiv1_c : natural := 25; -- r/w: clock divider bit 1
constant ctrl_cdiv2_c : natural := 26; -- r/w: clock divider bit 2
constant ctrl_cdiv3_c : natural := 27; -- r/w: clock divider bit 3
--
constant ctrl_phy_busy_c : natural := 30; -- r/-: SPI PHY is busy when set
constant ctrl_xip_busy_c : natural := 31; -- r/-: XIP access in progress
--
signal ctrl : std_ulogic_vector(23 downto 0);
signal ctrl : std_ulogic_vector(27 downto 0);

-- Direct SPI access registers --
signal spi_data_lo : std_ulogic_vector(31 downto 0);
Expand All @@ -104,11 +108,12 @@ architecture neorv32_xip_rtl of neorv32_xip is
addr_lookahead : std_ulogic_vector(31 downto 0);
xip_acc_err : std_ulogic;
busy : std_ulogic;
tmo_cnt : std_ulogic_vector(04 downto 0); -- timeout counter for auto CS de-assert (burst mode only)
tmo_cnt : std_ulogic_vector(4 downto 0); -- timeout counter for auto CS de-assert (burst mode only)
end record;
signal arbiter : arbiter_t;

-- SPI clock --
-- Clock generator --
signal cdiv_cnt : std_ulogic_vector(3 downto 0);
signal spi_clk_en : std_ulogic;

-- Component: SPI PHY --
Expand All @@ -127,7 +132,7 @@ architecture neorv32_xip_rtl of neorv32_xip is
op_final_i : in std_ulogic; -- end current transmission
op_csen_i : in std_ulogic; -- actually enabled device for transmission
op_busy_o : out std_ulogic; -- transmission in progress when set
op_nbytes_i : in std_ulogic_vector(03 downto 0); -- actual number of bytes to transmit (1..9)
op_nbytes_i : in std_ulogic_vector(3 downto 0); -- actual number of bytes to transmit (1..9)
op_wdata_i : in std_ulogic_vector(71 downto 0); -- write data
op_rdata_o : out std_ulogic_vector(31 downto 0); -- read data
-- SPI interface --
Expand Down Expand Up @@ -188,6 +193,7 @@ begin
ctrl(ctrl_spi_csen_c) <= bus_req_i.data(ctrl_spi_csen_c);
ctrl(ctrl_highspeed_c) <= bus_req_i.data(ctrl_highspeed_c);
ctrl(ctrl_burst_en_c) <= bus_req_i.data(ctrl_burst_en_c);
ctrl(ctrl_cdiv3_c downto ctrl_cdiv0_c) <= bus_req_i.data(ctrl_cdiv3_c downto ctrl_cdiv0_c);
end if;
-- SPI direct data access register lo --
if (bus_req_i.addr(3 downto 2) = "10") then
Expand All @@ -214,6 +220,7 @@ begin
bus_rsp_o.data(ctrl_spi_csen_c) <= ctrl(ctrl_spi_csen_c);
bus_rsp_o.data(ctrl_highspeed_c) <= ctrl(ctrl_highspeed_c);
bus_rsp_o.data(ctrl_burst_en_c) <= ctrl(ctrl_burst_en_c);
bus_rsp_o.data(ctrl_cdiv3_c downto ctrl_cdiv0_c) <= ctrl(ctrl_cdiv3_c downto ctrl_cdiv0_c);
--
bus_rsp_o.data(ctrl_phy_busy_c) <= phy_if.busy;
bus_rsp_o.data(ctrl_xip_busy_c) <= arbiter.busy;
Expand Down Expand Up @@ -362,12 +369,30 @@ begin

-- 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(ctrl_enable_c) = '0') then -- reset/disabled
cdiv_cnt <= (others => '0');
elsif (clkgen_i(to_integer(unsigned(ctrl(ctrl_spi_prsc2_c downto ctrl_spi_prsc0_c)))) = '1') or
(ctrl(ctrl_highspeed_c) = '1') then -- pre-scaled clock
if (cdiv_cnt = ctrl(ctrl_cdiv3_c downto ctrl_cdiv0_c)) 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;
end process clock_generator;

-- enable clock generator --
clkgen_en_o <= ctrl(ctrl_enable_c);

-- clock select --
spi_clk_en <= clkgen_i(to_integer(unsigned(ctrl(ctrl_spi_prsc2_c downto ctrl_spi_prsc0_c)))) or ctrl(ctrl_highspeed_c);


-- SPI Physical Interface -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions sw/bootloader/bootloader.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ int main(void) {
#endif

#if (XIP_EN != 0)
// setup XIP: clock mode 0, bursts enabled
// setup XIP: clock divider 0, clock mode 0, bursts enabled
if (neorv32_xip_available()) {
neorv32_xip_setup(SPI_FLASH_CLK_PRSC, 0, 0, SPI_FLASH_CMD_READ);
neorv32_xip_setup(SPI_FLASH_CLK_PRSC, 0, 0, 0, SPI_FLASH_CMD_READ);
neorv32_xip_burst_mode_enable();
neorv32_xip_start(SPI_FLASH_ADDR_BYTES);
}
Expand Down
13 changes: 7 additions & 6 deletions sw/example/demo_xip/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ int main() {
"XIP base address: 0x%x\n"
"Flash address bytes: %u\n", (uint32_t)FLASH_BASE, (uint32_t)XIP_MEM_BASE_ADDRESS, (uint32_t)FLASH_ABYTES);

neorv32_uart0_printf("XIP SPI clock speed: %u Hz\n\n", neorv32_cpu_get_clk_from_prsc(XIP_CLK_PRSC)/2);


// warning if i-cache is not implemented
if ((NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_ICACHE)) == 0) {
Expand All @@ -134,11 +132,14 @@ int main() {


// reset XIP module and configure basic SPI properties
// * clock prescaler: XIP_CLK_PRSC
// * clock prescaler = XIP_CLK_PRSC (see defines)
// * clock divider = 4
// * clock mode 0 (cpol = 0, cpha = 0)
// * flash read command = SPI_FLASH_CMD_READ
// * flash read command = SPI_FLASH_CMD_READ (see defines)
// -> this function will also send 64 dummy clock cycles via the XIP's SPI port (with CS disabled)
neorv32_xip_setup(XIP_CLK_PRSC, 0, 0, SPI_FLASH_CMD_READ);
neorv32_xip_setup(XIP_CLK_PRSC, 4, 0, 0, SPI_FLASH_CMD_READ);

neorv32_uart0_printf("XIP SPI clock speed: %u Hz\n\n", neorv32_xip_get_clock_speed());


// ----------------------------------------------------------
Expand All @@ -148,7 +149,7 @@ int main() {
"\n"
" Navigate to any example program folder (like 'neorv32/sw/example/hello_word').\n"
" Compile the program but relocate the instruction to the beginning of the Flash:\n"
" make MARCH=rv32i USER_FLAGS+=\"-Wl,--defsym,__neorv32_rom_base=0x%x\" clean_all exe\n\n",
" make MARCH=rv32i_zicsr_zifencei USER_FLAGS+=\"-Wl,--defsym,__neorv32_rom_base=0x%x\" clean_all exe\n\n",
(uint32_t)(XIP_MEM_BASE_ADDRESS + FLASH_BASE));

neorv32_uart0_printf("Press any key when you are ready.\n\n");
Expand Down
7 changes: 6 additions & 1 deletion sw/lib/include/neorv32_xip.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ enum NEORV32_XIP_CTRL_enum {
XIP_CTRL_SPI_CSEN = 21, /**< XIP control register(21) (r/w): SPI chip-select enable */
XIP_CTRL_HIGHSPEED = 22, /**< XIP control register(22) (r/w): SPI high-speed mode enable (ignoring XIP_CTRL_PRSC) */
XIP_CTRL_BURST_EN = 23, /**< XIP control register(23) (r/w): Enable XIP burst mode */
XIP_CTRL_CDIV0 = 24, /**< XIP control register(24) (r/w): Clock divider bit 0 */
XIP_CTRL_CDIV1 = 25, /**< XIP control register(25) (r/w): Clock divider bit 1 */
XIP_CTRL_CDIV2 = 26, /**< XIP control register(26) (r/w): Clock divider bit 2 */
XIP_CTRL_CDIV3 = 27, /**< XIP control register(27) (r/w): Clock divider bit 3 */

XIP_CTRL_PHY_BUSY = 30, /**< XIP control register(20) (r/-): SPI PHY is busy */
XIP_CTRL_XIP_BUSY = 31 /**< XIP control register(31) (r/-): XIP access in progress */
Expand All @@ -88,10 +92,11 @@ enum NEORV32_XIP_CTRL_enum {
**************************************************************************/
/**@{*/
int neorv32_xip_available(void);
void neorv32_xip_setup(int prsc, int cpol, int cpha, uint8_t rd_cmd);
void neorv32_xip_setup(int prsc, int cdiv, int cpol, int cpha, uint8_t rd_cmd);
int neorv32_xip_start(int abytes);
void neorv32_xip_highspeed_enable(void);
void neorv32_xip_highspeed_disable(void);
uint32_t neorv32_xip_get_clock_speed(void);
void neorv32_xip_burst_mode_enable(void);
void neorv32_xip_burst_mode_disable(void);
void neorv32_xip_spi_trans(int nbytes, uint64_t *rtx_data);
Expand Down
30 changes: 29 additions & 1 deletion sw/lib/source/neorv32_xip.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ int neorv32_xip_available(void) {
* @note This function will also send 64 dummy clocks via the SPI port (with chip-select disabled).
*
* @param[in] prsc SPI clock prescaler select (0..7).
* @prama[in] cdiv Clock divider (0..15).
* @param[in] cpol SPI clock polarity (0/1).
* @param[in] cpha SPI clock phase(0/1).
* @param[in] rd_cmd SPI flash read byte command.
**************************************************************************/
void neorv32_xip_setup(int prsc, int cpol, int cpha, uint8_t rd_cmd) {
void neorv32_xip_setup(int prsc, int cdiv, int cpol, int cpha, uint8_t rd_cmd) {

// reset and disable module
NEORV32_XIP->CTRL = 0;
Expand All @@ -82,6 +83,7 @@ void neorv32_xip_setup(int prsc, int cpol, int cpha, uint8_t rd_cmd) {

uint32_t ctrl = 0;
ctrl |= ((uint32_t)(1 )) << XIP_CTRL_EN; // enable module
ctrl |= ((uint32_t)(cdiv & 0x0f)) << XIP_CTRL_CDIV0;
ctrl |= ((uint32_t)(prsc & 0x07)) << XIP_CTRL_PRSC0;
ctrl |= ((uint32_t)(cpol & 0x01)) << XIP_CTRL_CPOL;
ctrl |= ((uint32_t)(cpha & 0x01)) << XIP_CTRL_CPHA;
Expand Down Expand Up @@ -152,6 +154,32 @@ void neorv32_xip_highspeed_disable(void) {
}


/**********************************************************************//**
* Get configured clock speed in Hz.
*
* @return Actual configured XIP clock speed in Hz.
**************************************************************************/
uint32_t neorv32_xip_get_clock_speed(void) {

const uint16_t PRSC_LUT[8] = {2, 4, 8, 64, 128, 1024, 2048, 4096};

uint32_t ctrl = NEORV32_XIP->CTRL;
uint32_t prsc_sel = (ctrl >> XIP_CTRL_PRSC0) & 0x7;
uint32_t clock_div = (ctrl >> XIP_CTRL_CDIV0) & 0xf;

uint32_t tmp;

if (ctrl & (1 << XIP_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;
}


/**********************************************************************//**
* Enable XIP burst mode (incremental reads).
*
Expand Down
10 changes: 10 additions & 0 deletions sw/svd/neorv32.svd
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,11 @@
<bitRange>[23:23]</bitRange>
<description>Enable burst mode (for XIP accesses)</description>
</field>
<field>
<name>XIP_CTRL_CDIV</name>
<bitRange>[24:27]</bitRange>
<description>SPI clock divider</description>
</field>
<field>
<name>XIP_CTRL_PHY_BUSY</name>
<bitRange>[30:30]</bitRange>
Expand Down Expand Up @@ -1054,6 +1059,11 @@
<bitRange>[13:10]</bitRange>
<description>SPI clock divider</description>
</field>
<field>
<name>SPI_CTRL_HIGHSPEED</name>
<bitRange>[14:14]</bitRange>
<description>SPI high-speed mode</description>
</field>
<field>
<name>SPI_CTRL_RX_AVAIL</name>
<bitRange>[16:16]</bitRange>
Expand Down

0 comments on commit 5a827ea

Please sign in to comment.