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

[SLINK] split interrupt into two FIRQs #868

Merged
merged 11 commits into from
Apr 4, 2024
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 |
|:----:|:-------:|:--------|:----:|
| 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) |
| 26.03.2024 | 1.9.7.6 | :warning: rework TWI module; add optional & configurable command/data FIFO | [#865](https://github.com/stnolting/neorv32/pull/865) |
| 24.03.2024 | 1.9.7.5 | :warning: **interrupt system rework**: rework CPU's FIRQ system; `mip` CSR is now read-only ; :bug: fix DMA fence configuration flag | [#864](https://github.com/stnolting/neorv32/pull/864) |
Expand Down
4 changes: 2 additions & 2 deletions docs/datasheet/soc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,8 @@ table (the channel number also corresponds to the according FIRQ priority: 0 = h
| 11 | <<_serial_data_interface_controller_sdi,SDI>> | SDI FIFO level interrupt
| 12 | <<_general_purpose_timer_gptmr,GPTMR>> | General purpose timer interrupt
| 13 | <<_one_wire_serial_interface_controller_onewire,ONEWIRE>> | 1-wire idle interrupt
| 14 | <<_stream_link_interface_slink,SLINK>> | SLINK FIFO level interrupt
| 15 | - | _reserved_
| 14 | <<_stream_link_interface_slink,SLINK>> | SLINK RX FIFO level interrupt
| 15 | <<_stream_link_interface_slink,SLINK>> | SLINK TX FIFO level interrupt
|=======================


Expand Down
87 changes: 47 additions & 40 deletions docs/datasheet/soc_slink.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,32 @@
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_slink.vhd |
| Software driver file(s): | neorv32_slink.c |
| | neorv32_slink.h |
| Top entity port(s): | `slink_rx_dat_i` | RX link data (32-bit)
| | `slink_rx_val_i` | RX link data valid (1-bit)
| | `slink_rx_lst_i` | RX link last element of stream (1-bit)
| | `slink_rx_rdy_o` | RX link ready to receive (1-bit)
| | `slink_tx_dat_o` | TX link data (32-bit)
| | `slink_tx_val_o` | TX link data valid (1-bit)
| | `slink_tx_lst_o` | TX link last element of stream (1-bit)
| | `slink_tx_rdy_i` | TX link allowed to send (1-bit)
| Configuration generics: | `IO_SLINK_EN` | implement SLINK when _true_
| | `IO_SLINK_RX_FIFO` | RX FIFO depth (1..32k), has to be a power of two
| | `IO_SLINK_TX_FIFO` | TX FIFO depth (1..32k), has to be a power of two
| CPU interrupt: | fast IRQ channel 14 | SLINK IRQ (see <<_processor_interrupts>>)
| Hardware source files: | neorv32_slink.vhd |
| Software driver files: | neorv32_slink.c |
| | neorv32_slink.h |
| Top entity ports: | `slink_rx_dat_i` | RX link data (32-bit)
| | `slink_rx_val_i` | RX link data valid (1-bit)
| | `slink_rx_lst_i` | RX link last element of stream (1-bit)
| | `slink_rx_rdy_o` | RX link ready to receive (1-bit)
| | `slink_tx_dat_o` | TX link data (32-bit)
| | `slink_tx_val_o` | TX link data valid (1-bit)
| | `slink_tx_lst_o` | TX link last element of stream (1-bit)
| | `slink_tx_rdy_i` | TX link allowed to send (1-bit)
| Configuration generics: | `IO_SLINK_EN` | implement SLINK when _true_
| | `IO_SLINK_RX_FIFO` | RX FIFO depth (1..32k), has to be a power of two, min 1
| | `IO_SLINK_TX_FIFO` | TX FIFO depth (1..32k), has to be a power of two, min 1
| CPU interrupts: | fast IRQ channel 14 | RX SLINK IRQ (see <<_processor_interrupts>>)
| | fast IRQ channel 15 | TX SLINK IRQ (see <<_processor_interrupts>>)
|=======================


**Overview**

The stream link interface provides independent RX and TX channels for sending for sending and receiving
stream data. Each channel features an internal FIFO with configurable depth to buffer stream data
The stream link interface provides independent RX and TX channels for sending and receiving
stream data. Each channel features a configurable internal FIFO to buffer stream data
(`IO_SLINK_RX_FIFO` for the RX FIFO, `IO_SLINK_TX_FIFO` for the TX FIFO). The SLINK interface provides higher
bandwidth and less latency than the external bus interface making it ideally suited for coupling custom
stream processing units or streaming peripherals.
stream processors or streaming peripherals.

.Example Program
[TIP]
Expand All @@ -38,51 +39,57 @@ An example program for the SLINK module is available in `sw/example/demo_slink`.

**Interface & Protocol**

The SLINK interface consists of four signals per channel:
The SLINK interface consists of four signals for each channel:

* `dat` contains the actual data word
* `val` marks the current transmission cycle as valid
* `lst` makes the current transmission cycle as the last element of a stream
* `rdy` indicates that the receiving part is ready to receive
* `lst` marks the current transmission cycle as the last element of a stream
* `rdy` indicates that the receiver is ready to receive

.AXI4-Stream Compatibility
[NOTE]
The interface names and the underlying protocol is compatible to the AXI4-Stream standard.
The interface names and the underlying protocol is compatible to the AXI4-Stream protocol standard.


**Theory of Operation**

The SLINK provides four interface registers. The control register (`CTRL`) is used to configure
the module and to check its status. Three individual data registers (`RX_DATA`, `TX_DATA`, `TX_DATA_LAST`)
are used to send and received the link's actual data stream.
are used to send and receive the link's actual data stream.

The `RX_DATA` register provides direct access to the RX link FIFO buffer. After reading data from this the register
the control register's `SLINK_CTRL_RX_LAST` can be checked to determine if the according data word has been marked
The `RX_DATA` register provides direct access to the RX link FIFO buffer. After reading data from this register
the control register's `SLINK_CTRL_RX_LAST` flag can be checked to determine if the according data word has been marked
as "end of stream" via the `slink_rx_lst_i` signal (this signal is also buffered by the link's FIFO).

Writing to the `TX_DATA` or `TX_DATA_LAST` register will immediately write to the TX link FIFO buffer.
When writing to the `TX_DATA_LAST` the according data word will be marked as "end of stream" via the
When writing to the `TX_DATA_LAST` the according data word will also be marked as "end of stream" via the
`slink_tx_lst_o` signal (this signal is also buffered by the link's FIFO).

The configured FIFO sizes can be retrieved by software via the control register's `SLINK_CTRL_RX_FIFO_*` and
`SLINK_CTRL_TX_FIFO_*` bits.

The SLINK is globally activated by setting the control register's enable bit `SLINK_CTRL_EN`. Clearing this bit will
reset all internal logic and will also clear both FIFOs. The FIFOs can also be cleared manually at all time by
setting the `SLINK_CTRL_RX_CLR` and `SLINK_CTRL_TX_CLR` bits (these bits will auto-clear).
reset all internal logic and will also clear both FIFOs. The FIFOs can also be cleared manually at any time by
setting the `SLINK_CTRL_RX_CLR` and/or `SLINK_CTRL_TX_CLR` bits (these bits will auto-clear).

.FIFO Overflow
[NOTE]
Writing to the TX channel's FIFO while it is _full_ will have no effect. Reading from the RX channel's FIFO while it
is _empty_ will also have no effect and will return the last received data word.
is _empty_ will also have no effect and will return the last received data word. There is no overflow indicator
implemented yet.

The current status of the RX and TX FIFOs can be determined via the `SLINK_CTRL_RX_*` and `SLINK_CTRL_TX_*` flags.
The current status of the RX and TX FIFOs can be determined via the control register's `SLINK_CTRL_RX_*` and
`SLINK_CTRL_TX_*` flags.


**Interrupt**
**Interrupts**

A single global interrupt can be programmed based on these FIFO status flags via the control register's `SLINK_CTRL_IRQ_*`
bits. Note that all enabled interrupt conditions are logically OR-ed. If any enable interrupt conditions becomes true the
interrupt will become pending until the interrupt-causing condition is resolved (e.g. by reading from the RX FIFO).
The SLINK module provides two independent interrupt channels: one for RX events and one for TX events.
The interrupt conditions are based on the according link's FIFO status flags and are configured via the control
register's `SLINK_CTRL_IRQ_*` flags. The according interrupt will fire when the module is enabled (`SLINK_CTRL_EN`)
and the selected interrupt conditions are met. Note that all enabled interrupt conditions are logically OR-ed per
channel. If any enable interrupt conditions becomes active the interrupt will become pending until the
interrupt-causing condition is resolved (e.g. by reading from the RX FIFO).


**Register Map**
Expand All @@ -105,12 +112,12 @@ interrupt will become pending until the interrupt-causing condition is resolved
<| `12` `SLINK_CTRL_TX_HALF` ^| r/- <| TX FIFO at least half full
<| `13` `SLINK_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<| `15:14` _reserved_ ^| r/- <| _reserved_, read as zero
<| `16` `SLINK_CTRL_IRQ_RX_NEMPTY` ^| r/w <| IRQ if RX FIFO not empty
<| `17` `SLINK_CTRL_IRQ_RX_HALF` ^| r/w <| IRQ if RX FIFO at least half full
<| `18` `SLINK_CTRL_IRQ_RX_FULL` ^| r/w <| IRQ if RX FIFO full
<| `19` `SLINK_CTRL_IRQ_TX_EMPTY` ^| r/w <| IRQ if TX FIFO empty
<| `20` `SLINK_CTRL_IRQ_TX_NHALF` ^| r/w <| IRQ if TX FIFO not at least half full
<| `21` `SLINK_CTRL_IRQ_TX_NFULL` ^| r/w <| IRQ if TX FIFO not full
<| `16` `SLINK_CTRL_IRQ_RX_NEMPTY` ^| r/w <| RX interrupt if RX FIFO not empty
<| `17` `SLINK_CTRL_IRQ_RX_HALF` ^| r/w <| RX interrupt if RX FIFO at least half full
<| `18` `SLINK_CTRL_IRQ_RX_FULL` ^| r/w <| RX interrupt if RX FIFO full
<| `19` `SLINK_CTRL_IRQ_TX_EMPTY` ^| r/w <| TX interrupt if TX FIFO empty
<| `20` `SLINK_CTRL_IRQ_TX_NHALF` ^| r/w <| TX interrupt if TX FIFO not at least half full
<| `21` `SLINK_CTRL_IRQ_TX_NFULL` ^| r/w <| TX interrupt if TX FIFO not full
<| `23:22` _reserved_ ^| r/- <| _reserved_, read as zero
<| `27:24` `SLINK_CTRL_RX_FIFO_MSB : SLINK_CTRL_RX_FIFO_LSB` ^| r/- <| log2(RX FIFO size)
<| `31:28` `SLINK_CTRL_TX_FIFO_MSB : SLINK_CTRL_TX_FIFO_LSB` ^| r/- <| log2(TX FIFO size)
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 @@ -52,7 +52,7 @@ package neorv32_package is

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

Expand Down
73 changes: 39 additions & 34 deletions rtl/core/neorv32_slink.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
-- # << NEORV32 - Stream Link Interface (SLINK) >> #
-- # ********************************************************************************************* #
-- # Two independent stream links for RX and TX each equipped with a configurable FIFO and #
-- # providing programmable interrupt conditions. #
-- # individually programmable interrupt conditions (based on the FIFO status flags). #
-- # ********************************************************************************************* #
-- # BSD 3-Clause License #
-- # #
Expand Down Expand Up @@ -43,16 +43,17 @@ use neorv32.neorv32_package.all;

entity neorv32_slink is
generic (
SLINK_RX_FIFO : natural range 1 to 2**15; -- RX fifo depth, has to be a power of two
SLINK_TX_FIFO : natural range 1 to 2**15 -- TX fifo depth, has to be a power of two
SLINK_RX_FIFO : natural range 1 to 2**15; -- RX fifo depth, has to be a power of two, min 1
SLINK_TX_FIFO : natural range 1 to 2**15 -- TX fifo depth, has to be a power of two, min 1
);
port (
-- Host access --
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
irq_o : out std_ulogic; -- CPU interrupt
rx_irq_o : out std_ulogic; -- RX interrupt
tx_irq_o : out std_ulogic; -- TX interrupt
-- RX stream interface --
slink_rx_data_i : in std_ulogic_vector(31 downto 0); -- input data
slink_rx_valid_i : in std_ulogic; -- valid input
Expand All @@ -72,7 +73,7 @@ architecture neorv32_slink_rtl of neorv32_slink is
constant addr_ctrl_c : std_ulogic_vector(1 downto 0) := "00"; -- control register
constant addr_rx_c : std_ulogic_vector(1 downto 0) := "01"; -- RX data
constant addr_tx_c : std_ulogic_vector(1 downto 0) := "10"; -- TX data
constant addr_tx_last_c : std_ulogic_vector(1 downto 0) := "11"; -- TY data + last-delimiter
constant addr_tx_last_c : std_ulogic_vector(1 downto 0) := "11"; -- TX data + last-delimiter

-- control register --
constant ctrl_en_c : natural := 0; -- r/w: Global module enable
Expand All @@ -88,12 +89,12 @@ architecture neorv32_slink_rtl of neorv32_slink is
constant ctrl_tx_half_c : natural := 12; -- r/-: TX FIFO at least half full
constant ctrl_tx_full_c : natural := 13; -- r/-: TX FIFO full
--
constant ctrl_irq_rx_nempty_c : natural := 16; -- r/w: IRQ if RX FIFO not empty
constant ctrl_irq_rx_half_c : natural := 17; -- r/w: IRQ if RX FIFO at least half full
constant ctrl_irq_rx_full_c : natural := 18; -- r/w: IRQ if RX FIFO full
constant ctrl_irq_tx_empty_c : natural := 19; -- r/w: IRQ if TX FIFO empty
constant ctrl_irq_tx_nhalf_c : natural := 20; -- r/w: IRQ if TX FIFO not at least half full
constant ctrl_irq_tx_nfull_c : natural := 21; -- r/w: IRQ if TX FIFO not full
constant ctrl_irq_rx_nempty_c : natural := 16; -- r/w: RX interrupt if RX FIFO not empty
constant ctrl_irq_rx_half_c : natural := 17; -- r/w: RX interrupt if RX FIFO at least half full
constant ctrl_irq_rx_full_c : natural := 18; -- r/w: RX interrupt if RX FIFO full
constant ctrl_irq_tx_empty_c : natural := 19; -- r/w: TX interrupt if TX FIFO empty
constant ctrl_irq_tx_nhalf_c : natural := 20; -- r/w: TX interrupt if TX FIFO not at least half full
constant ctrl_irq_tx_nfull_c : natural := 21; -- r/w: TX interrupt if TX FIFO not full
--
constant ctrl_rx_fifo_size0_c : natural := 24; -- r/-: log2(RX fifo size), bit 0 (lsb)
constant ctrl_rx_fifo_size1_c : natural := 25; -- r/-: log2(RX fifo size), bit 1
Expand All @@ -106,19 +107,15 @@ architecture neorv32_slink_rtl of neorv32_slink is

-- control register --
type ctrl_t is record
enable : std_ulogic;
rx_clr : std_ulogic;
tx_clr : std_ulogic;
irq_rx_nempty : std_ulogic;
irq_rx_half : std_ulogic;
irq_rx_full : std_ulogic;
irq_tx_empty : std_ulogic;
irq_tx_nhalf : std_ulogic;
irq_tx_nfull : std_ulogic;
enable : std_ulogic;
rx_clr, tx_clr : std_ulogic;
irq_rx_nempty, irq_tx_empty : std_ulogic;
irq_rx_half, irq_tx_nhalf : std_ulogic;
irq_rx_full, irq_tx_nfull : std_ulogic;
end record;
signal ctrl : ctrl_t;

-- RX last indicator --
-- RX end-of-stream indicator --
signal rx_last : std_ulogic;

-- FIFO interface --
Expand Down Expand Up @@ -258,6 +255,19 @@ begin
end if;
end process rx_last_flag;

-- interrupt generator --
rx_interrupt: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
rx_irq_o <= '0';
elsif rising_edge(clk_i) then
rx_irq_o <= ctrl.enable and ( -- IRQ if enabled and ...
(ctrl.irq_rx_nempty and ( rx_fifo.avail)) or -- RX FIFO is not empty
(ctrl.irq_rx_half and ( rx_fifo.half)) or -- RX FIFO is at least half full
(ctrl.irq_rx_full and (not rx_fifo.free))); -- RX FIFO is full
end if;
end process rx_interrupt;


-- TX Data FIFO ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -294,23 +304,18 @@ begin
slink_tx_last_o <= tx_fifo.rdata(32) and tx_fifo.avail;
slink_tx_valid_o <= tx_fifo.avail;


-- Interrupt Generator --------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
irq_generator: process(rstn_i, clk_i)
-- interrupt generator --
tx_interrupt: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_o <= '0';
tx_irq_o <= '0';
elsif rising_edge(clk_i) then
irq_o <= ctrl.enable and ( -- IRQ if enabled and ...
(ctrl.irq_tx_empty and (not tx_fifo.avail)) or -- TX FIFO is empty
(ctrl.irq_tx_nhalf and (not tx_fifo.half)) or -- TX FIFO is not at least half full
(ctrl.irq_tx_nfull and ( tx_fifo.free)) or -- TX FIFO is not full
(ctrl.irq_rx_nempty and ( rx_fifo.avail)) or -- RX FIFO is not empty
(ctrl.irq_rx_half and ( rx_fifo.half)) or -- RX FIFO is at least half full
(ctrl.irq_rx_full and (not rx_fifo.free))); -- RX FIFO is full
tx_irq_o <= ctrl.enable and ( -- IRQ if enabled and ...
(ctrl.irq_tx_empty and (not tx_fifo.avail)) or -- TX FIFO is empty
(ctrl.irq_tx_nhalf and (not tx_fifo.half)) or -- TX FIFO is not at least half full
(ctrl.irq_tx_nfull and ( tx_fifo.free))); -- TX FIFO is not full
end if;
end process irq_generator;
end process tx_interrupt;


end neorv32_slink_rtl;
Loading