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

🐛 fix atomic write/clear/set accesses of clear-only CSR bits #829

Merged
merged 6 commits into from
Feb 24, 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 @@ -30,6 +30,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Link |
|:----:|:-------:|:--------|:----:|
| 23.02.2024 | 1.9.5.9 | :bug: fix atomic write/clear/set accesses of clear-only CSR bits (re-fix of v1.9.5.6) | [#829](https://github.com/stnolting/neorv32/pull/829) |
| 23.02.2024 | 1.9.5.8 | optimize FIFO component to improve technology mapping (inferring blockRAM for "async read" configuration); :bug: fix SLINK status flag delay | [#828](https://github.com/stnolting/neorv32/pull/828) |
| 23.02.2024 | 1.9.5.7 | fix FIFO synthesis issue (Vivado cannot infer block RAM nor LUT-RAM) | [#827](https://github.com/stnolting/neorv32/pull/827) |
| 20.02.2024 | 1.9.5.6 | :bug: fix bug in `mip.firq` CSR access; `mip.firq` bits are now read-write - software can trigger FIRQs by writing `1` to the according CSR bit | [#821](https://github.com/stnolting/neorv32/pull/821) |
Expand Down
3 changes: 3 additions & 0 deletions docs/datasheet/cpu.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,9 @@ As a custom extension, the NEORV32 CPU features 16 fast interrupt request (FIRQ)
entity signals. These interrupts have custom configuration and status flags in the <<_mie>> and <<_mip>> CSRs and also
provide custom trap codes in <<_mcause>>. These FIRQs are reserved for NEORV32 processor-internal usage only.

Once triggered, the according FIRQ bits in <<_mip>> has to be cleared _manually_ by the FIRQ interrupt handler
to clear/acknowledge the fast interrupt request.


:sectnums:
===== NEORV32 Trap Listing
Expand Down
5 changes: 1 addition & 4 deletions docs/datasheet/cpu_csr.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -443,17 +443,14 @@ However, any write-access will be ignored and will not cause an exception to mai
| 3 | `CSR_MIP_MSIP` | r/- | **MSIP**: Machine _software_ interrupt pending; _cleared by platform-defined mechanism_
| 7 | `CSR_MIP_MTIP` | r/- | **MTIP**: Machine _timer_ interrupt pending; _cleared by platform-defined mechanism_
| 11 | `CSR_MIP_MEIP` | r/- | **MEIP**: Machine _external_ interrupt pending; _cleared by platform-defined mechanism_
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/w | **FIRQxP**: Fast interrupt channel 15..0 pending; has to be cleared manually by writing zero
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/c | **FIRQxP**: Fast interrupt channel 15..0 pending; has to be cleared manually by writing zero; writing `1` has no effect
|=======================

.FIRQ Channel Mapping
[TIP]
See section <<_neorv32_specific_fast_interrupt_requests>> for the mapping of the FIRQ channels and the according
interrupt-triggering processor module.

[NOTE]
The FIRQ channels can be triggered manually by software by writing `1` to the according `mip` bit.


{empty} +
[discrete]
Expand Down
2 changes: 1 addition & 1 deletion docs/datasheet/on_chip_debugger.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ if it uses the trigger module for implementing a "hardware breakpoint"
| 25 | `hit1` | r/- | `0` - hardwired to zero, only `hit0` is used
| 24 | `vs` | r/- | `0` - VS-mode not supported
| 23 | `vu` | r/- | `0` - VU-mode not supported
| 22 | `hit0` | r/c | set when trigger has fired (**BEFORE** executing the triggering address); must be explicitly cleared by writing zero
| 22 | `hit0` | r/c | set when trigger has fired (**BEFORE** executing the triggering address); must be explicitly cleared by writing zero; writing 1 has no effect
| 21 | `select` | r/- | `0` - only address matching is supported
| 20:19 | reserved | r/- | `00` - hardwired to zero
| 18:16 | `size` | r/- | `000` - match accesses of any size
Expand Down
71 changes: 39 additions & 32 deletions rtl/core/neorv32_cpu_control.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,11 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
type csr_t is record
addr : std_ulogic_vector(11 downto 0); -- csr address
raddr : std_ulogic_vector(11 downto 0); -- simplified csr read address
cmd : std_ulogic_vector(2 downto 0); -- CSR access command
we, we_nxt : std_ulogic; -- csr write enable
re, re_nxt : std_ulogic; -- csr read enable
wdata, rdata : std_ulogic_vector(XLEN-1 downto 0); -- csr write/read data
wmask : std_ulogic_vector(XLEN-1 downto 0); -- csr write data (mask)
--
mstatus_mie : std_ulogic; -- machine-mode IRQ enable
mstatus_mpie : std_ulogic; -- previous machine-mode IRQ enable
Expand All @@ -255,7 +257,6 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
mie_mei : std_ulogic; -- machine external interrupt enable
mie_mti : std_ulogic; -- machine timer interrupt enable
mie_firq : std_ulogic_vector(15 downto 0); -- fast interrupt enable
mip_firq_wdata : std_ulogic_vector(15 downto 0); -- fast interrupt pending write data
mip_firq_we : std_ulogic; -- fast interrupt pending write enable
--
privilege : std_ulogic; -- current privilege mode
Expand All @@ -279,7 +280,7 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
dpc : std_ulogic_vector(XLEN-1 downto 0); -- mode program counter
dscratch0 : std_ulogic_vector(XLEN-1 downto 0); -- debug mode scratch register 0
--
tdata1_hit_clr : std_ulogic; -- set to manually clear mcontrol6.hit0
tdata1_hit_we : std_ulogic; -- mcontrol6.hit0 write access
tdata1_execute : std_ulogic; -- enable instruction address match trigger
tdata1_action : std_ulogic; -- enter debug mode / ebreak exception when trigger fires
tdata1_dmode : std_ulogic; -- set to ignore tdata* CSR access from machine-mode
Expand Down Expand Up @@ -1453,10 +1454,10 @@ begin

-- NEORV32-specific fast interrupts --
for i in 0 to 15 loop
if (csr.mip_firq_we = '1') then -- write access to MIP(.FIRQ) CSR
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= firq_i(i) or csr.mip_firq_wdata(i); -- keep buffering incoming FIRQs
else
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= firq_i(i) or trap_ctrl.irq_pnd(irq_firq_0_c+i); -- keep pending FIRQs alive
if (firq_i(i) = '1') then -- new incoming FIRQs have priority
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= '1';
elsif (csr.mip_firq_we = '1') and (csr.wmask(16+i) = '0') then -- clear-only
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= '0';
end if;
end loop;

Expand Down Expand Up @@ -1592,23 +1593,32 @@ begin

-- CSR Write Data -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
csr_write_data: process(execute_engine.ir, csr.rdata, rs1_i)
variable tmp_v : std_ulogic_vector(XLEN-1 downto 0);
csr.cmd <= execute_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c); -- access type

-- compute the write-data update mask --
csr_write_mask: process(csr.cmd, execute_engine.ir, rs1_i)
variable src_v : std_ulogic_vector(XLEN-1 downto 0);
begin
-- immediate/register operand --
if (execute_engine.ir(instr_funct3_msb_c) = '1') then
tmp_v := (others => '0');
tmp_v(4 downto 0) := execute_engine.ir(19 downto 15); -- uimm5
-- CSR operand --
if (csr.cmd(2) = '1') then -- immediate source
src_v := (others => '0');
src_v(4 downto 0) := execute_engine.ir(19 downto 15); -- uimm5
else -- register source
src_v := rs1_i;
end if;
-- actual write mask --
if (csr.cmd(1 downto 0) = "11") then -- csrrc[i]: clear
csr.wmask <= not src_v;
else
tmp_v := rs1_i;
csr.wmask <= src_v;
end if;
-- tiny ALU to compute CSR write data --
case execute_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) is
when "10" => csr.wdata <= csr.rdata or tmp_v; -- set
when "11" => csr.wdata <= csr.rdata and (not tmp_v); -- clear
when others => csr.wdata <= tmp_v; -- write
end case;
end process csr_write_data;
end process csr_write_mask;

-- tiny ALU to compute CSR write data --
with csr.cmd(1 downto 0) select csr.wdata <=
csr.rdata or csr.wmask when "10", -- csrrs[i]: set
csr.rdata and csr.wmask when "11", -- csrrc[i]: clear
csr.wmask when others; -- csrrw[i]: write


-- External CSR Interface -----------------------------------------------------------------
Expand Down Expand Up @@ -1643,26 +1653,21 @@ begin
csr.mtinst <= (others => '0');
csr.mcounteren <= '0';
csr.mcountinhibit <= (others => '0');
csr.mip_firq_wdata <= (others => '0');
csr.mip_firq_we <= '0';
csr.dcsr_ebreakm <= '0';
csr.dcsr_ebreaku <= '0';
csr.dcsr_step <= '0';
csr.dcsr_prv <= priv_mode_m_c;
csr.dcsr_cause <= (others => '0');
csr.dpc <= CPU_BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit aligned boot address
csr.dscratch0 <= (others => '0');
csr.tdata1_hit_clr <= '0';
csr.tdata1_execute <= '0';
csr.tdata1_action <= '0';
csr.tdata1_dmode <= '0';
csr.tdata2 <= (others => '0');
elsif rising_edge(clk_i) then

-- defaults --
csr.we <= csr.we_nxt and (not trap_ctrl.exc_buf(exc_illegal_c)); -- write if not an illegal instruction
csr.mip_firq_we <= '0'; -- no write to MIP.FIRQ by default
csr.tdata1_hit_clr <= '0';
-- write if not an illegal instruction --
csr.we <= csr.we_nxt and (not trap_ctrl.exc_buf(exc_illegal_c));

-- ********************************************************************************
-- CSR access by application software
Expand Down Expand Up @@ -1721,9 +1726,8 @@ begin
when csr_mcause_c => -- machine trap cause
csr.mcause <= csr.wdata(31) & csr.wdata(4 downto 0); -- type (exception/interrupt) & identifier

when csr_mip_c => -- machine interrupt pending
csr.mip_firq_wdata <= csr.wdata(31 downto 16);
csr.mip_firq_we <= '1'; -- trigger MIP.FIRQ write
-- when csr_mip_c => -- machine interrupt pending
-- only the FIRQs are writable (clear-only); the actual mip.firq write logic is in <interrupt_buffer>

-- --------------------------------------------------------------------
-- machine counter setup --
Expand Down Expand Up @@ -1771,7 +1775,6 @@ begin
if (csr.tdata1_dmode = '0') or (debug_ctrl.running = '1') then -- write access from debug-mode only?
csr.tdata1_execute <= csr.wdata(2);
csr.tdata1_action <= csr.wdata(12);
csr.tdata1_hit_clr <= not csr.wdata(22);
end if;
if (debug_ctrl.running = '1') then -- writable from debug-mode only
csr.tdata1_dmode <= csr.wdata(27);
Expand Down Expand Up @@ -1917,6 +1920,10 @@ begin
end if;
end process csr_write_access;

-- out-of-process CSR write access --
csr.mip_firq_we <= '1' when (csr.we = '1') and (csr.cmd(0) = '1') and (csr.addr = csr_mip_c) else '0'; -- mip.firq: write/clear only
csr.tdata1_hit_we <= '1' when (csr.we = '1') and (csr.cmd(0) = '1') and (csr.addr = csr_tdata1_c) else '0'; -- tdata1.hit: write/clear only

-- effective privilege mode is MACHINE when in debug mode --
csr.privilege_eff <= priv_mode_m_c when (debug_ctrl.running = '1') else csr.privilege;

Expand Down Expand Up @@ -2413,7 +2420,7 @@ begin
elsif rising_edge(clk_i) then
if (hw_trigger_match = '1') and (trap_ctrl.exc_buf(exc_ebreak_c) = '1') then -- trigger has fired and breakpoint exception is pending
hw_trigger_fired <= '1';
elsif (csr.tdata1_hit_clr = '1') then
elsif (csr.tdata1_hit_we = '1') and (csr.wmask(22) = '0') then -- clear-only
hw_trigger_fired <= '0';
end if;
end if;
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 @@ -53,7 +53,7 @@ package neorv32_package is

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

Expand Down
2 changes: 1 addition & 1 deletion sim/neorv32_tb.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ begin
if ci_mode then
-- No need to send the full expectation in one big chunk
check_uart(net, uart1_rx_handle, nul & nul);
check_uart(net, uart1_rx_handle, "0/57" & cr & lf);
check_uart(net, uart1_rx_handle, "0/58" & cr & lf);
end if;

-- Wait until all expected data has been received
Expand Down
54 changes: 53 additions & 1 deletion sw/example/processor_check/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ int main() {
// No "real" CSR write access (because rs1 = r0)
// ----------------------------------------------------------
neorv32_cpu_csr_write(CSR_MCAUSE, mcause_never_c);
PRINT_STANDARD("[%i] Read-only CSR 'no-write' ", cnt_test);
PRINT_STANDARD("[%i] Read-only CSR (no-write) ", cnt_test);

cnt_test++;

Expand All @@ -530,6 +530,58 @@ int main() {
}


// ----------------------------------------------------------
// Test MIP.FIRQ clear-only bits
// ----------------------------------------------------------
neorv32_cpu_csr_write(CSR_MCAUSE, mcause_never_c);
PRINT_STANDARD("[%i] Clear-only CSR (mip.FIRQ) ", cnt_test);

if ((NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_IO_SPI)) && (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_IO_GPTMR))) {
cnt_test++;

tmp_a = 0; // error counter

// disable all IRQ sources
neorv32_cpu_csr_write(CSR_MIE, 0);

// trigger two FIRQs
neorv32_gptmr_setup(CLK_PRSC_2, 1, -1); // fire GPTMR FIRQ
neorv32_spi_setup(CLK_PRSC_2, 0, 0, 0, -1); // fire SPI FIRQ
neorv32_gptmr_disable();
neorv32_spi_disable();

// both FIRQs should be pending now
if ((neorv32_cpu_csr_read(CSR_MIP) & 0xffff0000) != ((1 << SPI_FIRQ_PENDING) + (1 << GPTMR_FIRQ_PENDING))) {
tmp_a++;
}

// test write/set/clear access
neorv32_cpu_csr_write(CSR_MIP, 0xffffffff); // should have no effect at all
neorv32_cpu_csr_set(CSR_MIP, 0xffffffff); // should have no effect at all
neorv32_cpu_csr_clr(CSR_MIP, 1 << SPI_FIRQ_PENDING); // clear SPI FIRQ only

if ((neorv32_cpu_csr_read(CSR_MIP) & 0xffff0000) != (1 << GPTMR_FIRQ_PENDING)) {
tmp_a++;
}

// clear all mip.FIRQ bits
neorv32_cpu_csr_write(CSR_MIP, 0);
if ((neorv32_cpu_csr_read(CSR_MIP) & 0xffff0000) != 0) {
tmp_a++;
}

if (tmp_a == 0) {
test_ok();
}
else {
test_fail();
}
}
else {
PRINT_STANDARD("[n.a.]\n");
}


// ----------------------------------------------------------
// Unaligned instruction address
// ----------------------------------------------------------
Expand Down
32 changes: 16 additions & 16 deletions sw/lib/include/neorv32_cpu_csr.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,22 +304,22 @@ enum NEORV32_CSR_MIP_enum {
CSR_MIP_MEIP = 11, /**< CPU mip CSR (11): MEIP - Machine external interrupt pending (r/-) */

/* NEORV32-specific extension: Fast Interrupt Requests (FIRQ) */
CSR_MIP_FIRQ0P = 16, /**< CPU mip CSR (16): FIRQ0P - Fast interrupt channel 0 pending (r/w) */
CSR_MIP_FIRQ1P = 17, /**< CPU mip CSR (17): FIRQ1P - Fast interrupt channel 1 pending (r/w) */
CSR_MIP_FIRQ2P = 18, /**< CPU mip CSR (18): FIRQ2P - Fast interrupt channel 2 pending (r/w) */
CSR_MIP_FIRQ3P = 19, /**< CPU mip CSR (19): FIRQ3P - Fast interrupt channel 3 pending (r/w) */
CSR_MIP_FIRQ4P = 20, /**< CPU mip CSR (20): FIRQ4P - Fast interrupt channel 4 pending (r/w) */
CSR_MIP_FIRQ5P = 21, /**< CPU mip CSR (21): FIRQ5P - Fast interrupt channel 5 pending (r/w) */
CSR_MIP_FIRQ6P = 22, /**< CPU mip CSR (22): FIRQ6P - Fast interrupt channel 6 pending (r/w) */
CSR_MIP_FIRQ7P = 23, /**< CPU mip CSR (23): FIRQ7P - Fast interrupt channel 7 pending (r/w) */
CSR_MIP_FIRQ8P = 24, /**< CPU mip CSR (24): FIRQ8P - Fast interrupt channel 8 pending (r/w) */
CSR_MIP_FIRQ9P = 25, /**< CPU mip CSR (25): FIRQ9P - Fast interrupt channel 9 pending (r/w) */
CSR_MIP_FIRQ10P = 26, /**< CPU mip CSR (26): FIRQ10P - Fast interrupt channel 10 pending (r/w) */
CSR_MIP_FIRQ11P = 27, /**< CPU mip CSR (27): FIRQ11P - Fast interrupt channel 11 pending (r/w) */
CSR_MIP_FIRQ12P = 28, /**< CPU mip CSR (28): FIRQ12P - Fast interrupt channel 12 pending (r/w) */
CSR_MIP_FIRQ13P = 29, /**< CPU mip CSR (29): FIRQ13P - Fast interrupt channel 13 pending (r/w) */
CSR_MIP_FIRQ14P = 30, /**< CPU mip CSR (30): FIRQ14P - Fast interrupt channel 14 pending (r/w) */
CSR_MIP_FIRQ15P = 31 /**< CPU mip CSR (31): FIRQ15P - Fast interrupt channel 15 pending (r/w) */
CSR_MIP_FIRQ0P = 16, /**< CPU mip CSR (16): FIRQ0P - Fast interrupt channel 0 pending (r/c) */
CSR_MIP_FIRQ1P = 17, /**< CPU mip CSR (17): FIRQ1P - Fast interrupt channel 1 pending (r/c) */
CSR_MIP_FIRQ2P = 18, /**< CPU mip CSR (18): FIRQ2P - Fast interrupt channel 2 pending (r/c) */
CSR_MIP_FIRQ3P = 19, /**< CPU mip CSR (19): FIRQ3P - Fast interrupt channel 3 pending (r/c) */
CSR_MIP_FIRQ4P = 20, /**< CPU mip CSR (20): FIRQ4P - Fast interrupt channel 4 pending (r/c) */
CSR_MIP_FIRQ5P = 21, /**< CPU mip CSR (21): FIRQ5P - Fast interrupt channel 5 pending (r/c) */
CSR_MIP_FIRQ6P = 22, /**< CPU mip CSR (22): FIRQ6P - Fast interrupt channel 6 pending (r/c) */
CSR_MIP_FIRQ7P = 23, /**< CPU mip CSR (23): FIRQ7P - Fast interrupt channel 7 pending (r/c) */
CSR_MIP_FIRQ8P = 24, /**< CPU mip CSR (24): FIRQ8P - Fast interrupt channel 8 pending (r/c) */
CSR_MIP_FIRQ9P = 25, /**< CPU mip CSR (25): FIRQ9P - Fast interrupt channel 9 pending (r/c) */
CSR_MIP_FIRQ10P = 26, /**< CPU mip CSR (26): FIRQ10P - Fast interrupt channel 10 pending (r/c) */
CSR_MIP_FIRQ11P = 27, /**< CPU mip CSR (27): FIRQ11P - Fast interrupt channel 11 pending (r/c) */
CSR_MIP_FIRQ12P = 28, /**< CPU mip CSR (28): FIRQ12P - Fast interrupt channel 12 pending (r/c) */
CSR_MIP_FIRQ13P = 29, /**< CPU mip CSR (29): FIRQ13P - Fast interrupt channel 13 pending (r/c) */
CSR_MIP_FIRQ14P = 30, /**< CPU mip CSR (30): FIRQ14P - Fast interrupt channel 14 pending (r/c) */
CSR_MIP_FIRQ15P = 31 /**< CPU mip CSR (31): FIRQ15P - Fast interrupt channel 15 pending (r/c) */
};


Expand Down