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

[XIP] add clock divider for fine-tuning #731

Merged
merged 5 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
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