diff --git a/CHANGELOG.md b/CHANGELOG.md index cb1a8aebd..f6a567a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) | diff --git a/docs/datasheet/soc_xip.adoc b/docs/datasheet/soc_xip.adoc index 6dc270c0d..3b5e90b19 100644 --- a/docs/datasheet/soc_xip.adoc +++ b/docs/datasheet/soc_xip.adoc @@ -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"] @@ -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] @@ -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 @@ -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 diff --git a/rtl/core/neorv32_bootloader_image.vhd b/rtl/core/neorv32_bootloader_image.vhd index 0d0bdd607..ac331d039 100644 --- a/rtl/core/neorv32_bootloader_image.vhd +++ b/rtl/core/neorv32_bootloader_image.vhd @@ -2,7 +2,7 @@ -- Auto-generated memory initialization file (for BOOTLOADER) from source file -- 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 @@ -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", diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index 78d400ca0..105853979 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"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! diff --git a/rtl/core/neorv32_xip.vhd b/rtl/core/neorv32_xip.vhd index e9ac9445b..edfaa5f0b 100644 --- a/rtl/core/neorv32_xip.vhd +++ b/rtl/core/neorv32_xip.vhd @@ -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); @@ -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 -- @@ -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 -- @@ -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 @@ -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; @@ -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 ----------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- diff --git a/sw/bootloader/bootloader.c b/sw/bootloader/bootloader.c index f7bad513b..37f2543b2 100644 --- a/sw/bootloader/bootloader.c +++ b/sw/bootloader/bootloader.c @@ -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); } diff --git a/sw/example/demo_xip/main.c b/sw/example/demo_xip/main.c index 00e2d9534..082fa6eb7 100644 --- a/sw/example/demo_xip/main.c +++ b/sw/example/demo_xip/main.c @@ -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) { @@ -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()); // ---------------------------------------------------------- @@ -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"); diff --git a/sw/lib/include/neorv32_xip.h b/sw/lib/include/neorv32_xip.h index e55ca2cca..b0396b38c 100644 --- a/sw/lib/include/neorv32_xip.h +++ b/sw/lib/include/neorv32_xip.h @@ -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 */ @@ -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); diff --git a/sw/lib/source/neorv32_xip.c b/sw/lib/source/neorv32_xip.c index f0132b71d..bd9e21953 100644 --- a/sw/lib/source/neorv32_xip.c +++ b/sw/lib/source/neorv32_xip.c @@ -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; @@ -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; @@ -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). * diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index 30dd8d8f0..a93bc1895 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -609,6 +609,11 @@ [23:23] Enable burst mode (for XIP accesses) + + XIP_CTRL_CDIV + [24:27] + SPI clock divider + XIP_CTRL_PHY_BUSY [30:30] @@ -1054,6 +1059,11 @@ [13:10] SPI clock divider + + SPI_CTRL_HIGHSPEED + [14:14] + SPI high-speed mode + SPI_CTRL_RX_AVAIL [16:16]