Skip to content

Commit

Permalink
add additional SPI and SDI interrupt conditions (#870)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting committed Apr 4, 2024
2 parents d43f9af + 60eba51 commit 77492f6
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 80 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Link |
|:----:|:-------:|:--------|:----:|
| 04.04.2024 | 1.9.7.10 | extend SPI and SDI interrupt conditions | [#870](https://github.com/stnolting/neorv32/pull/870) |
| 04.04.2024 | 1.9.7.9 | RISC-V `B` ISA extension (bit-manipulation) only contains sub-extensions `Zba+Zbb+Zbs`; :warning: remove support for `Zbc` ISA extension | [#869](https://github.com/stnolting/neorv32/pull/869) |
| 03.04.2024 | 1.9.7.8 | split SLINK interrupt into two individual FIRQs (SLINK RX and SLINK TX) | [#868](https://github.com/stnolting/neorv32/pull/868) |
| 01.04.2024 | 1.9.7.7 | add back TWI clock stretching option | [#867](https://github.com/stnolting/neorv32/pull/867) |
Expand Down
35 changes: 20 additions & 15 deletions docs/datasheet/soc_sdi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_sdi.vhd |
| Software driver file(s): | neorv32_sdi.c |
| | neorv32_sdi.h |
| Top entity port: | `sdi_clk_i` | 1-bit serial clock input
| | `sdi_dat_o` | 1-bit serial data output
| | `sdi_dat_i` | 1-bit serial data input
| | `sdi_csn_i` | 1-bit chip-select input (low-active)
| Configuration generics: | `IO_SDI_EN` | implement SDI controller when `true`
| | `IO_SDI_FIFO` | data FIFO size, has to a power of two, min 1
| CPU interrupts: | fast IRQ channel 11 | configurable SDI interrupt (see <<_processor_interrupts>>)
| Hardware source files: | neorv32_sdi.vhd |
| Software driver files: | neorv32_sdi.c |
| | neorv32_sdi.h |
| Top entity ports: | `sdi_clk_i` | 1-bit serial clock input
| | `sdi_dat_o` | 1-bit serial data output
| | `sdi_dat_i` | 1-bit serial data input
| | `sdi_csn_i` | 1-bit chip-select input (low-active)
| Configuration generics: | `IO_SDI_EN` | implement SDI controller when `true`
| | `IO_SDI_FIFO` | data FIFO size, has to a power of two, min 1
| CPU interrupts: | fast IRQ channel 11 | configurable SDI interrupt (see <<_processor_interrupts>>)
|=======================


Expand Down Expand Up @@ -45,6 +45,9 @@ data can be retrieved by reading the RX FIFO via the `DATA` register. The curren
via the control register's `SDI_CTRL_RX_*` and `SDI_CTRL_TX_*` flags. The RX FIFO can be manually cleared at any time
by setting the `SDI_CTRL_CLR_RX` bit.

If no data is available in the TX FIFO while an external device performs a transmission the external device will
read all-zero from the SDI controller.

.MSB-first Only
[NOTE]
The NEORV32 SDI module only supports MSB-first mode.
Expand All @@ -66,7 +69,7 @@ clock domain to simplify timing behavior. However, the clock synchronization req
**SDI Interrupt**

The SDI module provides a set of programmable interrupt conditions based on the level of the RX & TX FIFOs. The different
interrupt sources are enabled by setting the according control register's `SDI_CTRL_IRQ` bits. All enabled interrupt
interrupt sources are enabled by setting the according control register's `SDI_CTRL_IRQ_*` bits. All enabled interrupt
conditions are logically OR-ed so any enabled interrupt source will trigger the module's interrupt signal.

Once the SDI interrupt has fired it will remain active until the actual cause of the interrupt is resolved; for
Expand All @@ -80,7 +83,7 @@ example if just the `SDI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.16+<| `0xfffff700` .16+<| `CTRL` <|`0` `SDI_CTRL_EN` ^| r/w <| SDI module enable
.18+<| `0xfffff700` .18+<| `CTRL` <|`0` `SDI_CTRL_EN` ^| r/w <| SDI module enable
<|`1` `SDI_CTRL_CLR_RX` ^| -/w <| clear RX FIFO when set, bit auto-clears
<|`3:2` _reserved_ ^| r/- <| reserved, read as zero
<|`7:4` `SDI_CTRL_FIFO_MSB : SDI_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(_IO_SDI_FIFO_)
Expand All @@ -89,12 +92,14 @@ example if just the `SDI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
<|`16` `SDI_CTRL_IRQ_RX_HALF` ^| r/w <| fire interrupt if RX FIFO is at least half full
<|`17` `SDI_CTRL_IRQ_RX_FULL` ^| r/w <| fire interrupt if if RX FIFO is full
<|`18` `SDI_CTRL_IRQ_TX_EMPTY` ^| r/w <| fire interrupt if TX FIFO is empty
<|`22:19` _reserved_ ^| r/- <| reserved, read as zero
<|`19` `SDI_CTRL_IRQ_TX_NHALF` ^| r/w <| fire interrupt if TX FIFO is not at least half full
<|`22:20` _reserved_ ^| r/- <| reserved, read as zero
<|`23` `SDI_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available (RX FIFO not empty)
<|`24` `SDI_CTRL_RX_HALF` ^| r/- <| RX FIFO at least half full
<|`25` `SDI_CTRL_RX_FULL` ^| r/- <| RX FIFO full
<|`26` `SDI_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty
<|`27` `SDI_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<|`31:28` _reserved_ ^| r/- <| reserved, read as zero
<|`27` `SDI_CTRL_TX_NHALF` ^| r/- <| TX FIFO not at least half full
<|`28` `SDI_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<|`31:29` _reserved_ ^| r/- <| reserved, read as zero
| `0xfffff704` | `DATA` |`7:0` | r/w | receive/transmit data (FIFO)
|=======================
27 changes: 14 additions & 13 deletions docs/datasheet/soc_spi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_spi.vhd |
| Software driver file(s): | neorv32_spi.c |
| | neorv32_spi.h |
| Top entity port: | `spi_clk_o` | 1-bit serial clock output
| | `spi_dat_o` | 1-bit serial data output
| | `spi_dat_i` | 1-bit serial data input
| | `spi_csn_o` | 8-bit dedicated chip select output (low-active)
| Configuration generics: | `IO_SPI_EN` | implement SPI controller when `true`
| | `IO_SPI_FIFO` | FIFO depth, has to be a power of two, min 1
| CPU interrupts: | fast IRQ channel 6 | configurable SPI interrupt (see <<_processor_interrupts>>)
| Hardware source files: | neorv32_spi.vhd |
| Software driver files: | neorv32_spi.c |
| | neorv32_spi.h |
| Top entity ports: | `spi_clk_o` | 1-bit serial clock output
| | `spi_dat_o` | 1-bit serial data output
| | `spi_dat_i` | 1-bit serial data input
| | `spi_csn_o` | 8-bit dedicated chip select output (low-active)
| Configuration generics: | `IO_SPI_EN` | implement SPI controller when `true`
| | `IO_SPI_FIFO` | FIFO depth, has to be a power of two, min 1
| CPU interrupts: | fast IRQ channel 6 | configurable SPI interrupt (see <<_processor_interrupts>>)
|=======================


Expand Down Expand Up @@ -114,7 +114,7 @@ example if just the `SPI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.19+<| `0xfffff800` .19+<| `CTRL` <|`0` `SPI_CTRL_EN` ^| r/w <| SPI module enable
.20+<| `0xfffff800` .20+<| `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
Expand All @@ -130,8 +130,9 @@ example if just the `SPI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
<|`20` `SPI_CTRL_IRQ_RX_AVAIL` ^| r/w <| Trigger IRQ if RX FIFO not empty
<|`21` `SPI_CTRL_IRQ_TX_EMPTY` ^| r/w <| Trigger IRQ if TX FIFO empty
<|`22` `SPI_CTRL_IRQ_TX_NHALF` ^| r/w <| Trigger IRQ if TX FIFO _not_ at least half full
<|`26:23` `SPI_CTRL_FIFO_MSB : SPI_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(_IO_SPI_FIFO_)
<|`30:27` _reserved_ ^| r/- <| reserved, read as zero
<|`23` `SPI_CTRL_IRQ_IDLE` ^| r/w <| Trigger IRQ if TX FIFO is empty and SPI bus engine is idle
<|`27:24` `SPI_CTRL_FIFO_MSB : SPI_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(_IO_SPI_FIFO_)
<|`30:28` _reserved_ ^| r/- <| reserved, read as zero
<|`31` `SPI_CTRL_BUSY` ^| r/- <| SPI module busy when set (serial engine operation in progress and TX FIFO not empty yet)
| `0xfffff804` | `DATA` |`7:0` | r/w | receive/transmit data (FIFO)
|=======================
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ package neorv32_package is

-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090709"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090710"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

Expand Down
38 changes: 20 additions & 18 deletions rtl/core/neorv32_sdi.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,17 @@ architecture neorv32_sdi_rtl of neorv32_sdi is
constant ctrl_fifo_size2_c : natural := 6; -- r/-: log2(FIFO size), bit 2
constant ctrl_fifo_size3_c : natural := 7; -- r/-: log2(FIFO size), bit 3 (msb)
--
constant ctrl_irq_rx_avail_c : natural := 15; -- r/-: RX FIFO not empty
constant ctrl_irq_rx_half_c : natural := 16; -- r/-: RX FIFO at least half full
constant ctrl_irq_rx_full_c : natural := 17; -- r/-: RX FIFO full
constant ctrl_irq_tx_empty_c : natural := 18; -- r/-: TX FIFO empty
constant ctrl_irq_rx_avail_c : natural := 15; -- r/w: RX FIFO not empty
constant ctrl_irq_rx_half_c : natural := 16; -- r/w: RX FIFO at least half full
constant ctrl_irq_rx_full_c : natural := 17; -- r/w: RX FIFO full
constant ctrl_irq_tx_empty_c : natural := 18; -- r/w: TX FIFO empty
constant ctrl_irq_tx_nhalf_c : natural := 19; -- r/w: TX FIFO not at least half full
--
constant ctrl_rx_avail_c : natural := 23; -- r/-: RX FIFO not empty
constant ctrl_rx_half_c : natural := 24; -- r/-: RX FIFO at least half full
constant ctrl_rx_full_c : natural := 25; -- r/-: RX FIFO full
constant ctrl_tx_empty_c : natural := 26; -- r/-: TX FIFO empty
constant ctrl_tx_nhalf_c : natural := 27; -- r/-: TX FIFO not at least half-full
constant ctrl_tx_full_c : natural := 27; -- r/-: TX FIFO full

-- control register (see bit definitions above) --
Expand All @@ -89,6 +91,7 @@ architecture neorv32_sdi_rtl of neorv32_sdi is
irq_rx_half : std_ulogic;
irq_rx_full : std_ulogic;
irq_tx_empty : std_ulogic;
irq_tx_nhalf : std_ulogic;
end record;
signal ctrl : ctrl_t;

Expand Down Expand Up @@ -143,19 +146,17 @@ begin
ctrl.irq_rx_half <= '0';
ctrl.irq_rx_full <= '0';
ctrl.irq_tx_empty <= '0';
ctrl.irq_tx_nhalf <= '0';
elsif rising_edge(clk_i) then
-- bus handshake --
bus_rsp_o.ack <= bus_req_i.stb;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= (others => '0');

-- defaults --
ctrl.clr_rx <= '0';

-- read/write access --
ctrl.clr_rx <= '0'; -- default
if (bus_req_i.stb = '1') then

-- write access --
if (bus_req_i.rw = '1') then
if (bus_req_i.rw = '1') then -- write access
if (bus_req_i.addr(2) = '0') then -- control register
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.clr_rx <= bus_req_i.data(ctrl_clr_rx_c);
Expand All @@ -164,10 +165,9 @@ begin
ctrl.irq_rx_half <= bus_req_i.data(ctrl_irq_rx_half_c);
ctrl.irq_rx_full <= bus_req_i.data(ctrl_irq_rx_full_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);
end if;

-- read access --
else
else -- read access
if (bus_req_i.addr(2) = '0') then -- control register
bus_rsp_o.data(ctrl_en_c) <= ctrl.enable;
--
Expand All @@ -177,17 +177,18 @@ begin
bus_rsp_o.data(ctrl_irq_rx_half_c) <= ctrl.irq_rx_half;
bus_rsp_o.data(ctrl_irq_rx_full_c) <= ctrl.irq_rx_full;
bus_rsp_o.data(ctrl_irq_tx_empty_c) <= ctrl.irq_tx_empty;
bus_rsp_o.data(ctrl_irq_tx_nhalf_c) <= ctrl.irq_tx_nhalf;
--
bus_rsp_o.data(ctrl_rx_avail_c) <= rx_fifo.avail;
bus_rsp_o.data(ctrl_rx_half_c) <= rx_fifo.half;
bus_rsp_o.data(ctrl_rx_full_c) <= not rx_fifo.free;
bus_rsp_o.data(ctrl_tx_empty_c) <= not tx_fifo.avail;
bus_rsp_o.data(ctrl_tx_nhalf_c) <= not tx_fifo.half;
bus_rsp_o.data(ctrl_tx_full_c) <= not tx_fifo.free;
else -- data register
bus_rsp_o.data(7 downto 0) <= rx_fifo.rdata;
end if;
end if;

end if;
end if;
end process bus_access;
Expand Down Expand Up @@ -270,10 +271,11 @@ begin
irq_o <= '0';
elsif rising_edge(clk_i) then
irq_o <= ctrl.enable and (
(ctrl.irq_rx_avail and rx_fifo.avail) or -- RX FIFO not empty
(ctrl.irq_rx_half and rx_fifo.half) or -- RX FIFO at least half full
(ctrl.irq_rx_full and (not rx_fifo.free)) or -- RX FIFO full
(ctrl.irq_tx_empty and (not tx_fifo.avail))); -- TX FIFO empty
(ctrl.irq_rx_avail and rx_fifo.avail) or -- RX FIFO not empty
(ctrl.irq_rx_half and rx_fifo.half) or -- RX FIFO at least half full
(ctrl.irq_rx_full and (not rx_fifo.free)) or -- RX FIFO full
(ctrl.irq_tx_empty and (not tx_fifo.avail)) or -- TX FIFO empty
(ctrl.irq_tx_nhalf and (not tx_fifo.half))); -- TX FIFO not at least half full
end if;
end process irq_generator;

Expand Down
29 changes: 16 additions & 13 deletions rtl/core/neorv32_spi.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ architecture neorv32_spi_rtl of neorv32_spi is
constant ctrl_irq_rx_avail_c : natural := 20; -- r/w: fire irq if rx fifo data available (fifo not empty)
constant ctrl_irq_tx_empty_c : natural := 21; -- r/w: fire irq if tx fifo empty
constant ctrl_irq_tx_nhalf_c : natural := 22; -- r/w: fire irq if tx fifo not at least half full
constant ctrl_fifo_size0_c : natural := 23; -- r/-: log2(fifo size), bit 0 (lsb)
constant ctrl_fifo_size1_c : natural := 24; -- r/-: log2(fifo size), bit 1
constant ctrl_fifo_size2_c : natural := 25; -- r/-: log2(fifo size), bit 2
constant ctrl_fifo_size3_c : natural := 26; -- r/-: log2(fifo size), bit 3 (msb)
constant ctrl_irq_idle_c : natural := 23; -- r/w: fire irq if tx fifo is empty and serial engine is idle
constant ctrl_fifo_size0_c : natural := 24; -- r/-: log2(fifo size), bit 0 (lsb)
constant ctrl_fifo_size1_c : natural := 25; -- r/-: log2(fifo size), bit 1
constant ctrl_fifo_size2_c : natural := 26; -- r/-: log2(fifo size), bit 2
constant ctrl_fifo_size3_c : natural := 27; -- r/-: log2(fifo size), bit 3 (msb)
--
constant ctrl_busy_c : natural := 31; -- r/-: spi phy busy or tx fifo not empty yet

Expand All @@ -103,14 +104,15 @@ architecture neorv32_spi_rtl of neorv32_spi is
irq_rx_avail : std_ulogic;
irq_tx_empty : std_ulogic;
irq_tx_nhalf : std_ulogic;
irq_idle : std_ulogic;
end record;
signal ctrl : ctrl_t;

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

-- spi transceiver --
-- SPI engine --
type rtx_engine_t is record
state : std_ulogic_vector(2 downto 0);
busy : std_ulogic;
Expand Down Expand Up @@ -156,15 +158,16 @@ begin
ctrl.irq_rx_avail <= '0';
ctrl.irq_tx_empty <= '0';
ctrl.irq_tx_nhalf <= '0';
ctrl.irq_idle <= '0';
elsif rising_edge(clk_i) then
-- bus handshake --
bus_rsp_o.ack <= bus_req_i.stb;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= (others => '0');
if (bus_req_i.stb = '1') then

-- write access --
if (bus_req_i.rw = '1') then
-- read/write access --
if (bus_req_i.stb = '1') then
if (bus_req_i.rw = '1') then -- write access
if (bus_req_i.addr(2) = '0') then -- control register
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.cpha <= bus_req_i.data(ctrl_cpha_c);
Expand All @@ -177,10 +180,9 @@ begin
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);
ctrl.irq_idle <= bus_req_i.data(ctrl_irq_idle_c);
end if;

-- read access --
else
else -- read access
if (bus_req_i.addr(2) = '0') then -- control register
bus_rsp_o.data(ctrl_en_c) <= ctrl.enable;
bus_rsp_o.data(ctrl_cpha_c) <= ctrl.cpha;
Expand All @@ -198,6 +200,7 @@ begin
bus_rsp_o.data(ctrl_irq_rx_avail_c) <= ctrl.irq_rx_avail;
bus_rsp_o.data(ctrl_irq_tx_empty_c) <= ctrl.irq_tx_empty;
bus_rsp_o.data(ctrl_irq_tx_nhalf_c) <= ctrl.irq_tx_nhalf;
bus_rsp_o.data(ctrl_irq_idle_c) <= ctrl.irq_idle;
--
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(IO_SPI_FIFO), 4));
--
Expand All @@ -206,7 +209,6 @@ begin
bus_rsp_o.data(7 downto 0) <= rx_fifo.rdata;
end if;
end if;

end if;
end if;
end process bus_access;
Expand Down Expand Up @@ -299,7 +301,8 @@ begin
irq_o <= ctrl.enable and (
(ctrl.irq_rx_avail and rx_fifo.avail) or -- IRQ if RX FIFO is not empty
(ctrl.irq_tx_empty and (not tx_fifo.avail)) or -- IRQ if TX FIFO is empty
(ctrl.irq_tx_nhalf and (not tx_fifo.half))); -- IRQ if TX buffer is not half full
(ctrl.irq_tx_nhalf and (not tx_fifo.half)) or -- IRQ if TX buffer is not half full
(ctrl.irq_idle and (not tx_fifo.avail) and (not rtx_engine.busy))); -- IRQ if TX FIFO is empty and serial engine is idle
end if;
end process irq_generator;

Expand Down
Loading

0 comments on commit 77492f6

Please sign in to comment.