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

Add "out-of-band" signals to internal bus interface #1131

Merged
merged 7 commits into from
Dec 28, 2024
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
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 | Ticket |
|:----:|:-------:|:--------|:------:|
| 27.12.2024 | 1.10.8.2 | add out-of-band signals to internal request bus | [#1131](https://github.com/stnolting/neorv32/pull/1131) |
| 27.12.2024 | 1.10.8.1 | :warning: replace MTIME by CLINT; :warning: remove `HART_ID` generic | [#1130](https://github.com/stnolting/neorv32/pull/1130) |
| 26.12.2024 | [**:rocket:1.10.8**](https://github.com/stnolting/neorv32/releases/tag/v1.10.8) | **New release** | |
| 23.12.2024 | 1.10.7.9 | :warning: rework IO/peripheral address space; :sparkles: increase device size from 256 bytes to 64kB | [#1126](https://github.com/stnolting/neorv32/pull/1126) |
Expand Down
26 changes: 17 additions & 9 deletions docs/datasheet/cpu.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ direction as seen from the CPU.
4+^| **Global Signals**
| `clk_i` | 1 | in | Global clock line, all registers triggering on rising edge.
| `rstn_i` | 1 | in | Global reset, low-active.
| `sleep_o` | 1 | out | CPU is in <<_sleep_mode>> when set.
| `debug_o` | 1 | out | CPU is in <<_cpu_debug_mode,debug mode>> when set.
4+^| **Interrupts (<<_traps_exceptions_and_interrupts>>)**
| `msi_i` | 1 | in | RISC-V machine software interrupt.
| `mei_i` | 1 | in | RISC-V machine external interrupt.
Expand Down Expand Up @@ -342,7 +340,7 @@ The `wfi` instruction will raise an illegal instruction exception when executed
if `TW` in <<_mstatus>> is set. When executed in debug-mode or during single-stepping `wfi` will behave as
simple `nop` without entering sleep mode.

After executing the `wfi` instruction the CPU's `sleep_o` signal (<<_cpu_top_entity_signals>>) will become set
After executing the `wfi` instruction the `sleep` signal of the CPU's request buses (<<_bus_interface>> will become set
as soon as the CPU has fully halted:

[start=1]
Expand Down Expand Up @@ -393,15 +391,22 @@ the instruction fetch interface (`i_bus_*` signals) is used for fetching instruc
(`d_bus_*` signals) is used to access data via load and store operations. Each of these interfaces can access an address
space of up to 2^32^ bytes (4GB).

The bus interface uses two custom interface types: `bus_req_t` is used to propagate the bus access **requests**. These
signals are driven by the _accessing_ device (i.e. the CPU core). `bus_rsp_t` is used to return the bus **response** and
is driven by the _accessed_ device or bus system (i.e. a processor-internal memory or IO device).
The bus interface uses two custom interface types: `bus_req_t` is used to propagate the bus access requests downstream
from a host to a device. These signals are driven by the request-issuing device (i.e. the CPU core). Vice versa, `bus_rsp_t`
is used to return the bus response upstream from a device back to the host and is driven by the accessed device or bus system
(i.e. a processor-internal memory or IO device).

The signals of the request bus are split in to two categories: _in-band_ signals and _out-of-band_ signals. In-band
signals always belong to a certain bus transaction and are only valid between `stb` being set and the according response
(`err` or `ack`). being set. In contrast, the out-of-band signals are not associated with any bus transaction and are
always valid when set.

.Bus Interface - Request Bus (`bus_req_t`)
[cols="^1,^1,<6"]
[options="header",grid="rows"]
|=======================
| Signal | Width | Description
3+^| **In-Band Signals**
| `addr` | 32 | Access address (byte addressing)
| `data` | 32 | Write data
| `ben` | 4 | Byte-enable for each byte in `data`
Expand All @@ -410,7 +415,10 @@ is driven by the _accessed_ device or bus system (i.e. a processor-internal memo
| `src` | 1 | Access source (`0` = instruction fetch, `1` = load/store)
| `priv` | 1 | Set if privileged (M-mode) access
| `rvso` | 1 | Set if current access is a reservation-set operation (`lr` or `sc` instruction, <<_zalrsc_isa_extension>>)
| `fence` | 1 | Data/instruction fence operation; valid without `stb` being set
3+^| **Out-Of-Band Signals**
| `fence` | 1 | Data/instruction fence request; single-shot
| `sleep` | 1 | Set if ALL upstream devices are in <<_sleep_mode>>
| `debug` | 1 | Set if the upstream device is in debug-mode
|=======================

.Bus Interface - Response Bus (`bus_rsp_t`)
Expand Down Expand Up @@ -444,7 +452,7 @@ The figure below shows three exemplary bus accesses:
. A write access to address `B_addr` writing `wdata` (fastest response; `ACK` arrives right in the next cycle).
. A failing read access to address `C_addr` (slow response; `ERR` arrives after several cycles).

.Three Exemplary Bus Transactions
.Three Exemplary Bus Transactions (showing only in-band signals)
image::bus_interface.png[700]

.Adding Register Stages
Expand Down Expand Up @@ -478,7 +486,7 @@ and also registers a reservation for the address `addr` (`rvs_valid` becomes set
invalidated (`rvs_valid` is `0`) the store access fails, so `wdata2` is **not** written to address `addr` at all. The failed
operation is indicated by a **1** being returned via `rsp.data` together with `ack`.

.Three Exemplary LR/SC Bus Transactions
.Three Exemplary LR/SC Bus Transactions (showing only in-band signals)
image::bus_interface_atomic.png[700]

.Store-Conditional Status
Expand Down
Binary file modified docs/figures/bus_interface.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/figures/bus_interface_atomic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion docs/sources/bus_interface.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
{name: 'src', wave: 'x0.|.x0.x..|..'},
{name: 'priv', wave: 'x0.|.x0.x..|..'},
{name: 'rvso', wave: 'x0.|.x0.x..|..'},
{name: 'fence', wave: '0....|.....|..'},
],
{},
[
Expand Down
1 change: 0 additions & 1 deletion docs/sources/bus_interface_atomic.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
{name: 'src', wave: '0....|.....|.....'},
{name: 'priv', wave: '0....|.....|.....'},
{name: 'rvso', wave: '01..0|.1..0|.1..0', node: '.b.......e....'},
{name: 'fence', wave: '0....|.....|.....'},
],
{},
[
Expand Down
216 changes: 141 additions & 75 deletions rtl/core/neorv32_bus.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ use neorv32.neorv32_package.all;

entity neorv32_bus_switch is
generic (
PORT_A_READ_ONLY : boolean; -- set if port A is read-only
PORT_B_READ_ONLY : boolean -- set if port B is read-only
ROUND_ROBIN_EN : boolean := false; -- enable round-robing scheduling
PORT_A_READ_ONLY : boolean := false; -- set if port A is read-only
PORT_B_READ_ONLY : boolean := false -- set if port B is read-only
);
port (
clk_i : in std_ulogic; -- global clock, rising edge
rstn_i : in std_ulogic; -- global reset, low-active, async
a_lock_i : in std_ulogic; -- exclusive access for port A while set
a_req_i : in bus_req_t; -- host port A request bus (PRIORITIZED)
a_req_i : in bus_req_t; -- host port A request bus
a_rsp_o : out bus_rsp_t; -- host port A response bus
b_req_i : in bus_req_t; -- host port B request bus
b_rsp_o : out bus_rsp_t; -- host port B response bus
Expand All @@ -38,17 +39,10 @@ end neorv32_bus_switch;
architecture neorv32_bus_switch_rtl of neorv32_bus_switch is

-- access arbiter --
type arbiter_t is record
state, state_nxt : std_ulogic_vector(1 downto 0);
a_req, b_req : std_ulogic;
sel, stb : std_ulogic;
end record;
signal arbiter : arbiter_t;

-- FSM states --
constant IDLE : std_ulogic_vector(1 downto 0) := "00";
constant BUSY_A : std_ulogic_vector(1 downto 0) := "01";
constant BUSY_B : std_ulogic_vector(1 downto 0) := "10";
type state_t is (S_CHECK_A, S_BUSY_A, S_CHECK_B, S_BUSY_B);
signal state, state_nxt : state_t;
signal a_req, b_req : std_ulogic;
signal sel, stb : std_ulogic;

begin

Expand All @@ -57,86 +51,158 @@ begin
arbiter_sync: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
arbiter.state <= IDLE;
arbiter.a_req <= '0';
arbiter.b_req <= '0';
state <= S_CHECK_A;
a_req <= '0';
b_req <= '0';
elsif rising_edge(clk_i) then
arbiter.state <= arbiter.state_nxt;
arbiter.a_req <= (arbiter.a_req or a_req_i.stb) and (not arbiter.state(0)); -- clear STB buffer in BUSY_A
arbiter.b_req <= (arbiter.b_req or b_req_i.stb) and (not arbiter.state(1)); -- clear STB buffer in BUSY_B
state <= state_nxt;
if (state = S_BUSY_A) then -- clear request
a_req <= '0';
else -- buffer request
a_req <= a_req or a_req_i.stb;
end if;
if (state = S_BUSY_B) then -- clear request
b_req <= '0';
else -- buffer request
b_req <= b_req or b_req_i.stb;
end if;
end if;
end process arbiter_sync;

-- fsm --
arbiter_comb: process(arbiter, a_lock_i, a_req_i, b_req_i, x_rsp_i)
begin
-- defaults --
arbiter.state_nxt <= arbiter.state;
arbiter.sel <= '0';
arbiter.stb <= '0';

-- state machine --
case arbiter.state is

when BUSY_A => -- port A access in progress
-- ------------------------------------------------------------
arbiter.sel <= '0';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
arbiter.state_nxt <= IDLE;
end if;

when BUSY_B => -- port B access in progress
-- ------------------------------------------------------------
arbiter.sel <= '1';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
arbiter.state_nxt <= IDLE;
end if;
-- Prioritizing Bus Switch ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
arbiter_prioritized:
if not ROUND_ROBIN_EN generate
arbiter_fsm: process(state, a_req, b_req, a_lock_i, a_req_i, b_req_i, x_rsp_i)
begin
-- defaults --
state_nxt <= state;
sel <= '0';
stb <= '0';

-- state machine --
case state is

when S_BUSY_A => -- port A access in progress
-- ------------------------------------------------------------
sel <= '0';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_A;
end if;

when others => -- IDLE: wait for requests
-- ------------------------------------------------------------
if (a_req_i.stb = '1') or (arbiter.a_req = '1') then -- request from port A (prioritized)?
arbiter.sel <= '0';
arbiter.stb <= '1';
arbiter.state_nxt <= BUSY_A;
elsif ((b_req_i.stb = '1') or (arbiter.b_req = '1')) and (a_lock_i = '0') then -- request from port B?
arbiter.sel <= '1';
arbiter.stb <= '1';
arbiter.state_nxt <= BUSY_B;
end if;
when S_BUSY_B => -- port B access in progress
-- ------------------------------------------------------------
sel <= '1';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_A;
end if;

end case;
end process arbiter_comb;
when others => -- wait for requests
-- ------------------------------------------------------------
if (a_req_i.stb = '1') or (a_req = '1') then -- request from port A (prioritized)?
sel <= '0';
stb <= '1';
state_nxt <= S_BUSY_A;
elsif ((b_req_i.stb = '1') or (b_req = '1')) and (a_lock_i = '0') then -- request from port B?
sel <= '1';
stb <= '1';
state_nxt <= S_BUSY_B;
end if;

end case;
end process arbiter_fsm;
end generate;


-- Round-Robin Arbiter --------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
arbiter_round_robin:
if ROUND_ROBIN_EN generate
arbiter_fsm: process(state, a_req, b_req, a_req_i, b_req_i, x_rsp_i)
begin
-- defaults --
state_nxt <= state;
sel <= '0';
stb <= '0';

-- state machine --
case state is

when S_CHECK_A => -- check if access from port A
-- ------------------------------------------------------------
sel <= '0';
if (a_req_i.stb = '1') or (a_req = '1') then
stb <= '1';
state_nxt <= S_BUSY_A;
else
state_nxt <= S_CHECK_B;
end if;

when S_BUSY_A => -- port B access in progress
-- ------------------------------------------------------------
sel <= '0';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_B;
end if;

when S_CHECK_B => -- check if access from port B
-- ------------------------------------------------------------
sel <= '1';
if (b_req_i.stb = '1') or (b_req = '1') then
stb <= '1';
state_nxt <= S_BUSY_B;
else
state_nxt <= S_CHECK_A;
end if;

when S_BUSY_B => -- port B access in progress
-- ------------------------------------------------------------
sel <= '1';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_A;
end if;

when others => -- undefined
-- ------------------------------------------------------------
state_nxt <= S_CHECK_A;

end case;
end process arbiter_fsm;
end generate;


-- Request Switch -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
x_req_o.addr <= a_req_i.addr when (arbiter.sel = '0') else b_req_i.addr;
x_req_o.rvso <= a_req_i.rvso when (arbiter.sel = '0') else b_req_i.rvso;
x_req_o.priv <= a_req_i.priv when (arbiter.sel = '0') else b_req_i.priv;
x_req_o.src <= a_req_i.src when (arbiter.sel = '0') else b_req_i.src;
x_req_o.rw <= a_req_i.rw when (arbiter.sel = '0') else b_req_i.rw;
x_req_o.fence <= a_req_i.fence or b_req_i.fence; -- propagate any fence operations
x_req_o.addr <= a_req_i.addr when (sel = '0') else b_req_i.addr;
x_req_o.rvso <= a_req_i.rvso when (sel = '0') else b_req_i.rvso;
x_req_o.priv <= a_req_i.priv when (sel = '0') else b_req_i.priv;
x_req_o.src <= a_req_i.src when (sel = '0') else b_req_i.src;
x_req_o.rw <= a_req_i.rw when (sel = '0') else b_req_i.rw;
x_req_o.fence <= a_req_i.fence or b_req_i.fence; -- propagate any fence request
x_req_o.sleep <= a_req_i.sleep and b_req_i.sleep; -- set if ALL upstream devices are in sleep mode
x_req_o.debug <= a_req_i.debug when (sel = '0') else b_req_i.debug;

x_req_o.data <= b_req_i.data when PORT_A_READ_ONLY else
a_req_i.data when PORT_B_READ_ONLY else
a_req_i.data when (arbiter.sel = '0') else b_req_i.data;
x_req_o.data <= b_req_i.data when PORT_A_READ_ONLY else
a_req_i.data when PORT_B_READ_ONLY else
a_req_i.data when (sel = '0') else b_req_i.data;

x_req_o.ben <= b_req_i.ben when PORT_A_READ_ONLY else
a_req_i.ben when PORT_B_READ_ONLY else
a_req_i.ben when (arbiter.sel = '0') else b_req_i.ben;
x_req_o.ben <= b_req_i.ben when PORT_A_READ_ONLY else
a_req_i.ben when PORT_B_READ_ONLY else
a_req_i.ben when (sel = '0') else b_req_i.ben;

x_req_o.stb <= arbiter.stb;
x_req_o.stb <= stb;


-- Response Switch ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
a_rsp_o.data <= x_rsp_i.data;
a_rsp_o.ack <= x_rsp_i.ack when (arbiter.sel = '0') else '0';
a_rsp_o.err <= x_rsp_i.err when (arbiter.sel = '0') else '0';
a_rsp_o.ack <= x_rsp_i.ack when (sel = '0') else '0';
a_rsp_o.err <= x_rsp_i.err when (sel = '0') else '0';

b_rsp_o.data <= x_rsp_i.data;
b_rsp_o.ack <= x_rsp_i.ack when (arbiter.sel = '1') else '0';
b_rsp_o.err <= x_rsp_i.err when (arbiter.sel = '1') else '0';
b_rsp_o.ack <= x_rsp_i.ack when (sel = '1') else '0';
b_rsp_o.err <= x_rsp_i.err when (sel = '1') else '0';


end neorv32_bus_switch_rtl;
Expand Down Expand Up @@ -703,10 +769,10 @@ entity neorv32_bus_reservation_set is
rvs_addr_o : out std_ulogic_vector(31 downto 0);
rvs_valid_o : out std_ulogic;
rvs_clear_i : in std_ulogic;
-- core/cpu port --
-- core port --
core_req_i : in bus_req_t;
core_rsp_o : out bus_rsp_t;
-- system ports --
-- system port --
sys_req_o : out bus_req_t;
sys_rsp_i : in bus_rsp_t
);
Expand Down
Loading