Skip to content

Commit

Permalink
atomic memory access updates and improvements (#1163)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting authored Jan 19, 2025
2 parents 9726fb6 + 456684c commit ce8d64e
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 84 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 | Ticket |
|:----:|:-------:|:--------|:------:|
| 18.01.2025 | 1.10.9.9 | atomic memory access updates and improvements | [#1163](https://github.com/stnolting/neorv32/pull/1163) |
| 16.01.2025 | 1.10.9.8 | :bug: fix several TWD design flaws | [#1161](https://github.com/stnolting/neorv32/pull/1161) |
| 15.01.2025 | 1.10.9.7 | :sparkles: add GPIO interrupt(s); :warning: remove XIRQ controller, constrain GPIO input/output ports from 64-bit to 32-bit | [#1159](https://github.com/stnolting/neorv32/pull/1159) |
| 13.01.2025 | 1.10.9.6 | add WDT and OCD rest outputs to top module | [#1152](https://github.com/stnolting/neorv32/pull/1152) |
Expand Down
5 changes: 5 additions & 0 deletions docs/datasheet/soc_xbus.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ Native support for the "classic" Wishbone protocol has been deprecated.
However, classic mode can still be _emulated_ by connecting the processor's `xbus_cyc_o` directly to the
device's / bus system's `cyc` and `stb` signals (omitting the processor's `xbus_stb_o` signal).

.Atomic Memory Accesses
[NOTE]
<<_Atomic_Memory_Access>> keep the `cyc` signal active to perform a back-to-back bus access consisting of
two `stb` strobes (one for the load/read operation and another one for the store/write operation).

.Endianness
[NOTE]
Just like the processor itself the XBUS interface uses **little-endian** byte order.
Expand Down
7 changes: 4 additions & 3 deletions rtl/core/neorv32_bus.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,6 @@ architecture neorv32_bus_amo_ctrl_rtl of neorv32_bus_amo_ctrl is
cmd : std_ulogic_vector(3 downto 0);
rdata : std_ulogic_vector(31 downto 0);
wdata : std_ulogic_vector(31 downto 0);
ack : std_ulogic;
end record;
signal arbiter, arbiter_nxt : arbiter_t;

Expand Down Expand Up @@ -830,7 +829,9 @@ begin
when S_READ_WAIT => -- wait for read-access to complete
-- ------------------------------------------------------------
arbiter_nxt.rdata <= sys_rsp_i.data;
if (sys_rsp_i.ack = '1') or (sys_rsp_i.err = '1') then
if (sys_rsp_i.err = '1') then -- abort if error
arbiter_nxt.state <= S_IDLE;
elsif (sys_rsp_i.ack = '1') then
arbiter_nxt.state <= S_EXECUTE;
end if;

Expand Down Expand Up @@ -871,7 +872,7 @@ begin

-- response switch --
core_rsp_o.data <= sys_rsp_i.data when (arbiter.state = S_IDLE) else arbiter.rdata;
core_rsp_o.err <= sys_rsp_i.err when (arbiter.state = S_IDLE) or (arbiter.state = S_WRITE_WAIT) else '0';
core_rsp_o.err <= sys_rsp_i.err when (arbiter.state = S_IDLE) or (arbiter.state = S_WRITE_WAIT) or (arbiter.state = S_READ_WAIT) else '0';
core_rsp_o.ack <= sys_rsp_i.ack when (arbiter.state = S_IDLE) or (arbiter.state = S_WRITE_WAIT) else '0';


Expand Down
8 changes: 1 addition & 7 deletions rtl/core/neorv32_cache.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,7 @@ begin
dir_req_q <= req_terminate_c;
dir_rsp_q <= rsp_terminate_c;
elsif rising_edge(clk_i) then
-- is direct access? --
if (dir_acc_q = '0') and (host_req_i.stb = '1') and (dir_acc_d = '1') then
dir_acc_q <= '1';
elsif (dir_acc_q = '1') and ((dir_rsp_q.ack = '1') or (dir_rsp_q.err = '1')) then
dir_acc_q <= '0';
end if;
-- bus request buffer --
dir_acc_q <= dir_acc_d;
if READ_ONLY then -- do not propagate STB on write access, issue ERR instead
dir_req_q <= dir_req_d;
dir_req_q.stb <= dir_req_d.stb and (not dir_req_d.rw); -- read accesses only
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 @@ -29,7 +29,7 @@ package neorv32_package is

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

Expand Down
124 changes: 68 additions & 56 deletions rtl/core/neorv32_xbus.vhd
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
-- ================================================================================ --
-- NEORV32 SoC - External Bus Interface (XBUS) --
-- -------------------------------------------------------------------------------- --
-- Converts internal bus transactions into Wishbone b4-compatible bus transactions. --
-- Converts internal bus transactions into Wishbone b4-compatible bus accesses. --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
Expand Down Expand Up @@ -48,87 +48,99 @@ architecture neorv32_xbus_rtl of neorv32_xbus is
signal bus_rsp : bus_rsp_t;

-- bus arbiter --
signal pending : std_ulogic;
signal bus_rw : std_ulogic;
signal timeout : std_ulogic;
signal timeout_cnt : std_ulogic_vector(index_size_f(TIMEOUT_VAL) downto 0);
signal pending : std_ulogic_vector(1 downto 0);
signal timeout : std_ulogic;
signal timecnt : std_ulogic_vector(index_size_f(TIMEOUT_VAL) downto 0);

begin

-- Configuration Info ---------------------------------------------------------------------
-- Optional Register Stage ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
assert not (TIMEOUT_VAL = 0) report
"[NEORV32] External Bus Interface (XBUS): NO auto-timeout defined - can cause permanent CPU stall!" severity warning;


-- Register Stage -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
reg_stage_enable:
if REGSTAGE_EN generate
reg_stage: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
bus_req <= req_terminate_c;
bus_rsp_o <= rsp_terminate_c;
elsif rising_edge(clk_i) then
-- request --
if (bus_req_i.stb = '1') then -- keep all signals stable ...
bus_req <= bus_req_i;
end if;
bus_req.stb <= bus_req_i.stb; -- ... except for STB that is single-shot
-- response --
bus_rsp_o <= bus_rsp;
end if;
end process reg_stage;
end generate;

reg_state_disable:
if not REGSTAGE_EN generate
bus_req <= bus_req_i;
bus_rsp_o <= bus_rsp;
end generate;
reg_stage_inst: entity neorv32.neorv32_bus_reg
generic map (
REQ_REG_EN => REGSTAGE_EN,
RSP_REG_EN => REGSTAGE_EN
)
port map (
clk_i => clk_i,
rstn_i => rstn_i,
host_req_i => bus_req_i,
host_rsp_o => bus_rsp_o,
device_req_o => bus_req,
device_rsp_i => bus_rsp
);


-- Bus Arbiter ----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
arbiter: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
timeout_cnt <= (others => '0');
pending <= '0';
bus_rw <= '0';
timecnt <= (others => '0');
pending <= (others => '0');
timeout <= '0';
elsif rising_edge(clk_i) then
if (pending = '0') then -- idle, waiting for request
timeout_cnt <= std_ulogic_vector(to_unsigned(TIMEOUT_VAL, timeout_cnt'length));
pending <= bus_req.stb;
else -- busy, transfer in progress
timeout_cnt <= std_ulogic_vector(unsigned(timeout_cnt) - 1);
if (xbus_ack_i = '1') or (xbus_err_i = '1') or (timeout = '1') then
pending <= '0';
end if;

-- access control --
case pending is

when "10" => -- single access / atomic access (2nd access: store)
-- ------------------------------------------------------------
timecnt <= std_ulogic_vector(unsigned(timecnt) + 1);
if (xbus_ack_i = '1') or (xbus_err_i = '1') or (timeout = '1') then
pending <= "00";
end if;

when "11" => -- atomic access (1st access: load)
-- ------------------------------------------------------------
timecnt <= std_ulogic_vector(unsigned(timecnt) + 1);
if (xbus_err_i = '1') or (timeout = '1') then -- abort if error
pending <= "00";
elsif (xbus_ack_i = '1') then
pending <= "10";
end if;

when others => -- "0-": idle; waiting for request
-- ------------------------------------------------------------
timecnt <= (others => '0');
if (bus_req.stb = '1') then
if (bus_req.amo = '1') then
pending <= "11";
else
pending <= "10";
end if;
end if;

end case;

-- access timeout --
if (TIMEOUT_VAL /= 0) and (unsigned(timecnt) = TIMEOUT_VAL) then
timeout <= '1';
else
timeout <= '0';
end if;
bus_rw <= bus_req.rw;

end if;
end process arbiter;

-- bus timeout --
timeout <= '1' when (TIMEOUT_VAL /= 0) and (or_reduce_f(timeout_cnt) = '0') else '0';
-- no-timeout warning --
assert not (TIMEOUT_VAL = 0) report "[NEORV32] XBUS: NO auto-timeout configured!" severity warning;


-- Bus Access -----------------------------------------------------------------------------
-- XBUS ("Pipelined" Wishbone b4) ---------------------------------------------------------
-- -------------------------------------------------------------------------------------------
xbus_adr_o <= bus_req.addr;
xbus_dat_o <= bus_req.data;
xbus_we_o <= bus_req.rw;
xbus_sel_o <= bus_req.ben;
xbus_stb_o <= bus_req.stb;
xbus_cyc_o <= bus_req.stb or pending;
xbus_cyc_o <= bus_req.stb or pending(1);
xbus_tag_o <= bus_req.src & '0' & bus_req.priv; -- instr/data, secure, privileged/unprivileged

-- response gating --
bus_rsp.data <= xbus_dat_i when (pending = '1') and (bus_rw = '0') else (others => '0'); -- no read-back if WRITE operation
bus_rsp.ack <= xbus_ack_i when (pending = '1') else '0';
bus_rsp.err <= (xbus_err_i or timeout) when (pending = '1') else '0';
bus_rsp.data <= xbus_dat_i when (pending(1) = '1') else (others => '0');
bus_rsp.ack <= pending(1) and xbus_ack_i;
bus_rsp.err <= pending(1) and (xbus_err_i or timeout);


end neorv32_xbus_rtl;
2 changes: 1 addition & 1 deletion rtl/system_integration/neorv32_vivado_ip.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ proc setup_ip_gui {} {
add_params $group {
{ CPU_FAST_MUL_EN {DSP-Based Multiplier} }
{ CPU_FAST_SHIFT_EN {Barrel Shifter} }
{ CPU_RF_HW_RST_EN {Allow Full HW Reset for Register File} {Implement register file with FFs instead of BRAM to allow full hardware reset} }
{ CPU_RF_HW_RST_EN {Full HW Reset for Register File} {Implement register file with FFs instead of BRAM to allow full hardware reset} }
}


Expand Down
33 changes: 17 additions & 16 deletions rtl/system_integration/xbus2axi4lite_bridge.vhd
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
-- ================================================================================ --
-- NEORV32 SoC - XBUS to AXI4-Lite Bridge (single non-overlapping transfers only) --
-- NEORV32 SoC - XBUS to AXI4-Lite Bridge --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity xbus2axi4lite_bridge is
port (
Expand Down Expand Up @@ -57,51 +56,53 @@ end entity;

architecture xbus2axi4lite_bridge_rtl of xbus2axi4lite_bridge is

signal ready : std_ulogic_vector(2 downto 0);
signal xbus_rd_ack, xbus_rd_err, xbus_wr_ack, xbus_wr_err : std_ulogic;
signal arvalid, awvalid, wvalid, xbus_rd_ack, xbus_rd_err, xbus_wr_ack, xbus_wr_err : std_ulogic;

begin

-- channel handshake arbiter --
axi_handshake: process(resetn, clk)
begin
if (resetn = '0') then
ready <= (others => '0');
arvalid <= '0';
awvalid <= '0';
wvalid <= '0';
elsif rising_edge(clk) then
ready(0) <= xbus_cyc_i and (ready(0) or std_ulogic(m_axi_arready));
ready(1) <= xbus_cyc_i and (ready(1) or std_ulogic(m_axi_awready));
ready(2) <= xbus_cyc_i and (ready(2) or std_ulogic(m_axi_wready));
-- /------------- Set ------------\ /-------- Hold -------\ /---------- Clear ----------\
arvalid <= (xbus_stb_i and (not xbus_we_i)) or (arvalid and xbus_cyc_i and std_ulogic(not m_axi_arready));
awvalid <= (xbus_stb_i and ( xbus_we_i)) or (awvalid and xbus_cyc_i and std_ulogic(not m_axi_awready));
wvalid <= (xbus_stb_i and ( xbus_we_i)) or (wvalid and xbus_cyc_i and std_ulogic(not m_axi_wready));
end if;
end process axi_handshake;

-- AXI read address channel --
m_axi_araddr <= std_logic_vector(xbus_adr_i);
m_axi_arprot <= std_logic_vector(xbus_tag_i);
m_axi_arvalid <= std_logic(xbus_cyc_i and (not xbus_we_i) and (not ready(0)));
m_axi_arvalid <= std_logic(arvalid);

-- AXI read data channel --
m_axi_rready <= std_logic(xbus_cyc_i and (not xbus_we_i));
m_axi_rready <= '1';
xbus_dat_o <= std_ulogic_vector(m_axi_rdata);
xbus_rd_ack <= '1' when (m_axi_rvalid = '1') and (m_axi_rresp = "00") else '0';
xbus_rd_err <= '1' when (m_axi_rvalid = '1') and (m_axi_rresp /= "00") else '0';

-- AXI write address channel --
m_axi_awaddr <= std_logic_vector(xbus_adr_i);
m_axi_awprot <= std_logic_vector(xbus_tag_i);
m_axi_awvalid <= std_logic(xbus_cyc_i and xbus_we_i and (not ready(1)));
m_axi_awvalid <= std_logic(awvalid);

-- AXI write data channel --
m_axi_wdata <= std_logic_vector(xbus_dat_i);
m_axi_wstrb <= std_logic_vector(xbus_sel_i);
m_axi_wvalid <= std_logic(xbus_cyc_i and xbus_we_i and (not ready(2)));
m_axi_wvalid <= std_logic(wvalid);

-- AXI write response channel --
m_axi_bready <= std_logic(xbus_cyc_i and xbus_we_i);
m_axi_bready <= '1';
xbus_wr_ack <= '1' when (m_axi_bvalid = '1') and (m_axi_bresp = "00") else '0';
xbus_wr_err <= '1' when (m_axi_bvalid = '1') and (m_axi_bresp /= "00") else '0';

-- XBUS response --
xbus_ack_o <= xbus_rd_ack when (xbus_we_i = '0') else xbus_wr_ack;
xbus_err_o <= xbus_rd_err when (xbus_we_i = '0') else xbus_wr_err;
xbus_ack_o <= xbus_rd_ack or xbus_wr_ack;
xbus_err_o <= xbus_rd_err or xbus_wr_err;

end architecture xbus2axi4lite_bridge_rtl;

0 comments on commit ce8d64e

Please sign in to comment.