⚠️ replace Zalrsc ISA extension by Zaamo ISA extension (#1141)
Some checks failed
Documentation / SW Framework (push) Has been cancelled
Documentation / Datasheet (push) Has been cancelled
Processor / processor simulation (push) Has been cancelled
Documentation / Deploy to Releases and Pages (push) Has been cancelled

This commit is contained in:
stnolting 2025-01-04 06:22:44 +01:00 committed by GitHub
commit 651732de84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 323 additions and 1255 deletions

View file

@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 03.01.2025 | 1.10.8.7 | :warning: :sparkles: replace `Zalrsc` ISA extensions (reservation-set operations) by `Zaamo` ISA extension (atomic read-modify-write operations) | [#1141](https://github.com/stnolting/neorv32/pull/1141) |
| 01.01.2025 | 1.10.8.6 | :sparkles: :test_tube: add smp dual-core option | [#1135](https://github.com/stnolting/neorv32/pull/1135) |
| 29.12.2024 | 1.10.8.5 | :test_tube: add multi-hart support to debug module | [#1132](https://github.com/stnolting/neorv32/pull/1132) |
| 29.12.2024 | 1.10.8.4 | :warning: rename `SYSINFO.MEM -> SYSINFO.MISC`; add new `SYSINFO.MISC` entry for number of CPU cores (hardwired to one) | [#1134](https://github.com/stnolting/neorv32/pull/1134) |

View file

@ -106,7 +106,7 @@ setup according to your needs. Note that all of the following SoC modules are en
[[`B`](https://stnolting.github.io/neorv32/#_b_isa_extension)]
[[`U`](https://stnolting.github.io/neorv32/#_u_isa_extension)]
[[`X`](https://stnolting.github.io/neorv32/#_x_isa_extension)]
[[`Zalrsc`](https://stnolting.github.io/neorv32/#_zalrsc_isa_extension)]
[[`Zaamo`](https://stnolting.github.io/neorv32/#_zaamo_isa_extension)]
[[`Zba`](https://stnolting.github.io/neorv32/#_zba_isa_extension)]
[[`Zbb`](https://stnolting.github.io/neorv32/#_zbb_isa_extension)]
[[`Zbkb`](https://stnolting.github.io/neorv32/#_zbkb_isa_extension)]

View file

@ -415,7 +415,8 @@ always valid when set.
| `rw` | 1 | Access direction (`0` = read, `1` = write)
| `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>>)
| `amo` | 1 | Set if current access is an atomic memory operation (<<_atomic_memory_access>>)
| `amoop` | 4 | Type of atomic memory operation (<<_atomic_memory_access>>)
3+^| **Out-Of-Band Signals**
| `fence` | 1 | Data/instruction fence request; single-shot
| `sleep` | 1 | Set if ALL upstream devices are in <<_sleep_mode>>
@ -463,36 +464,31 @@ additional latency). However, _all_ bus signals (request and response) need to b
:sectnums:
==== Atomic Accesses
==== Atomic Memory Access
The load-reservate (`lr.w`) and store-conditional (`sc.w`) instructions from the <<_zalrsc_isa_extension>> execute as standard
load/store bus transactions but with the `rvso` ("reservation set operation") signal being set. It is the task of the
<<_reservation_set_controller>> to handle these LR/SC bus transactions accordingly. Note that these reservation set operations
are intended for processor-internal usage only (i.e. the reservation state is not available for processor-external modules yet).
The <<_zaamo_isa_extension>> adds atomic read-modify-write memory operations. Since the <<_bus_interface_protocol>>
only supports read-or-write operations, the atomic memory requests are handled by a dedicated module of the bus
infrastructure - the <<_atomic_memory_operations_controller>>.
.Reservation Set Controller
[NOTE]
See section <<_address_space>> / <<_reservation_set_controller>> for more information.
For the CPU, the atomic memory accesses are handled as plain "load" operation but with the `amo` signal set
and also providing write data (see <<_bus_interface>>). The `amoop` signal defines the actual atomic processing
operation:
The figure below shows three exemplary bus accesses (1 to 3 from left to right). The `req` signal record represents
the CPU-side of the bus interface. For easier understanding the current state of the reservation set is added as `rvs_valid` signal.
[start=1]
. A load-reservate (LR) instruction using `addr` as address. This instruction returns the loaded data `rdata` via `rsp.data`
and also registers a reservation for the address `addr` (`rvs_valid` becomes set).
. A store-conditional (SC) instruction attempts to write `wdata1` to address `addr`. This SC operation **succeeds**, so
`wdata1` is actually written to address `addr`. The successful operation is indicated by a **0** being returned via
`rsp.data` together with `ack`. As the LR/SC is completed the registered reservation is invalidated (`rvs_valid` becomes cleared).
. Another store-conditional (SC) instruction attempts to write `wdata2` to address `addr`. As the reservation set is already
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 (showing only in-band signals)
image::bus_interface_atomic.png[700]
.Store-Conditional Status
[NOTE]
The "normal" load data mechanism is used to return success/failure of the `sc.w` instruction to the CPU (via the LSB of `rsp.data`).
.AMO Operation Type Encoding
[cols="<1,<4"]
[options="header",grid="rows"]
|=======================
| `bus_req_t.amoop` | Description
| `-000` | swap
| `-001` | unsigned add
| `-010` | logical xor
| `-011` | logical and
| `-100` | logical or
| `0110` | unsigned minimum
| `0111` | unsigned maximum
| `1110` | signed minimum
| `1111` | signed maximum
|=======================
.Cache Coherency
[IMPORTANT]
@ -521,7 +517,7 @@ This chapter gives a brief overview of all available ISA extensions.
| <<_m_isa_extension,`M`>> | Integer multiplication and division instructions | <<_processor_top_entity_generics, `RISCV_ISA_M`>>
| <<_u_isa_extension,`U`>> | Less-privileged _user_ mode extension | <<_processor_top_entity_generics, `RISCV_ISA_U`>>
| <<_x_isa_extension,`X`>> | Platform-specific / NEORV32-specific extension | Always enabled
| <<_zalrsc_isa_extension,`Zalrsc`>> | Atomic reservation-set instructions | <<_processor_top_entity_generics, `RISCV_ISA_Zalrsc`>>
| <<_zaamo_isa_extension,`Zaamo`>> | Atomic memory operations | <<_processor_top_entity_generics, `RISCV_ISA_Zaamo`>>
| <<_zba_isa_extension,`Zba`>> | Shifted-add bit manipulation instructions | <<_processor_top_entity_generics, `RISCV_ISA_Zba`>>
| <<_zbb_isa_extension,`Zbb`>> | Basic bit manipulation instructions | <<_processor_top_entity_generics, `RISCV_ISA_Zbb`>>
| <<_zbkb_isa_extension,`Zbkb`>> | Scalar cryptographic bit manipulation instructions | <<_processor_top_entity_generics, `RISCV_ISA_Zbkb`>>
@ -689,37 +685,23 @@ RISC-V specs. Also, custom trap codes for <<_mcause>> are implemented.
* There are <<_neorv32_specific_csrs>>.
==== `Zalrsc` ISA Extension
==== `Zaamo` ISA Extension
The `Zalrsc` ISA extension is a sub-extension of the RISC-V _atomic memory access_ (`A`) ISA extension and includes
instructions for reservation-set operations (load-reservate `lr` and store-conditional `sc`) only.
It is enabled by the top's <<_processor_top_entity_generics, `RISCV_ISA_Zalrsc`>> generic.
.AMO / `A` Emulation
[NOTE]
The atomic memory access / read-modify-write operations of the `A` ISA extension can be emulated using the
LR and SC operations (quote from the RISC-V spec.: "_Any AMO can be emulated by an LR/SC pair._").
The NEORV32 <<_core_libraries>> provide an emulation wrapper for emulating AMO/read-modify-write instructions that is
based on LR/SC pairs. A demo/program can be found in `sw/example/atomic_test`.
The `Zaamo` ISA extension is a sub-extension of the RISC-V `A` ISA extension and compromises instructions for read-modify-write
<<_atomic_memory_access>> operations. It is enabled by the top's <<_processor_top_entity_generics, `RISCV_ISA_Zaamo`>> generic.
.Instructions and Timing
[cols="<2,<4,<3"]
[cols="<2,<4,<1"]
[options="header", grid="rows"]
|=======================
| Class | Instructions | Execution cycles
| Load-reservate word | `lr.w` | 5
| Store-conditional word | `sc.w` | 5
| Atomic memory operations | `amoswap.w` `amoadd.w` `amoand.w` `amoor.w` `amoxor.w` `amomax[u].w` `amomin[u].w` | 5 + 2 * _memory_latency_
|=======================
.`aq` and `rl` Bits
[NOTE]
The instruction word's `aq` and `lr` memory ordering bits are not evaluated by the hardware at all.
.Atomic Memory Access on Hardware Level
[NOTE]
More information regarding the atomic memory accesses and the according reservation
sets can be found in section <<_reservation_set_controller>>.
==== `Zifencei` ISA Extension

View file

@ -435,10 +435,10 @@ However, any write-access will be ignored and will not cause an exception to mai
[options="header",grid="rows"]
|=======================
| Bit | Name [C] | R/W | Function
| 3 | `CSR_MIP_MSIP` | r/- | **MSIP**: Machine _software_ interrupt pending, triggered by `msi_i` top port (see <<_cpu_top_entity_signals>>); _cleared by source-specific mechanism_
| 7 | `CSR_MIP_MTIP` | r/- | **MTIP**: Machine _timer_ interrupt pending, triggered by `mei_i` top port (see <<_cpu_top_entity_signals>>)or by the processor-internal <<(from <<_core_local_interruptor_clint>>)>>; _cleared by source-specific mechanism_
| 11 | `CSR_MIP_MEIP` | r/- | **MEIP**: Machine _external_ interrupt pending, triggered by `mti_i` top port (see <<_cpu_top_entity_signals>>) or by the processor-internal <<(from <<_core_local_interruptor_clint>>)>>; _cleared by source-specific mechanism_
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/- | **FIRQxP**: Fast interrupt channel 15..0 pending, see <<_neorv32_specific_fast_interrupt_requests>>; _cleared by source-specific mechanism_
| 3 | `CSR_MIP_MSIP` | r/- | **MSIP**: Machine _software_ interrupt pending, triggered by `msi_i` top port (see <<_cpu_top_entity_signals>>); cleared by source-specific mechanism
| 7 | `CSR_MIP_MTIP` | r/- | **MTIP**: Machine _timer_ interrupt pending, triggered by `mei_i` top port (see <<_cpu_top_entity_signals>>) or by the processor-internal <<_core_local_interruptor_clint>>; cleared by source-specific mechanism
| 11 | `CSR_MIP_MEIP` | r/- | **MEIP**: Machine _external_ interrupt pending, triggered by `mti_i` top port (see <<_cpu_top_entity_signals>>) or by the processor-internal <<_core_local_interruptor_clint>>; cleared by source-specific mechanism
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/- | **FIRQxP**: Fast interrupt channel 15..0 pending, see <<_neorv32_specific_fast_interrupt_requests>>; cleared by source-specific mechanism
|=======================
.FIRQ Channel Mapping
@ -770,8 +770,8 @@ caused by a fence instruction, a control flow transfer or a instruction fetch bu
| 5 | `HPMCNT_EVENT_WAIT_ALU` | r/w | any delay/wait cycle caused by a _multi-cycle_ <<_cpu_arithmetic_logic_unit>> operation
| 6 | `HPMCNT_EVENT_BRANCH` | r/w | any executed branch instruction (unconditional, conditional-taken or conditional-not-taken)
| 7 | `HPMCNT_EVENT_BRANCHED` | r/w | any control transfer operation (unconditional jump, taken conditional branch or trap entry/exit)
| 8 | `HPMCNT_EVENT_LOAD` | r/w | any executed load operation (including atomic memory operations, <<_zalrsc_isa_extension>>)
| 9 | `HPMCNT_EVENT_STORE` | r/w | any executed store operation (including atomic memory operations, <<_zalrsc_isa_extension>>)
| 8 | `HPMCNT_EVENT_LOAD` | r/w | any executed load operation (including any atomic memory operations)
| 9 | `HPMCNT_EVENT_STORE` | r/w | any executed store operation (including any atomic memory operations)
| 10 | `HPMCNT_EVENT_WAIT_LSU` | r/w | any memory/bus/cache/etc. delay/wait cycle while executing any load or store operation (caused by a data bus wait cycle))
| 11 | `HPMCNT_EVENT_TRAP` | r/w | starting processing of any trap (<<_traps_exceptions_and_interrupts>>)
|=======================
@ -979,7 +979,7 @@ discover ISA sub-extensions and CPU configuration options
| 22 | `CSR_MXISA_ZBA` | r/- | <<_zba_isa_extension>> available
| 23 | `CSR_MXISA_ZBB` | r/- | <<_zbb_isa_extension>> available
| 24 | `CSR_MXISA_ZBS` | r/- | <<_zbs_isa_extension>> available
| 25 | `CSR_MXISA_ZALRSC` | r/- | <<_zalrsc_isa_extension>> available
| 25 | `CSR_MXISA_ZAAMO` | r/- | <<_zaamo_isa_extension>> available
| 28:26 | - | r/- | _reserved_, hardwired to zero
| 27 | `CSR_MXISA_CLKGATE` | r/- | sleep-mode clock gating implemented when set (`CPU_CLOCK_GATING_EN`), see <<_cpu_tuning_options>>
| 28 | `CSR_MXISA_RFHWRST` | r/- | full hardware reset of register file available when set (`CPU_RF_HW_RST_EN`), see <<_cpu_tuning_options>>

View file

@ -226,7 +226,7 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
| `RISCV_ISA_E` | boolean | false | Enable <<_e_isa_extension>> (reduced register file size).
| `RISCV_ISA_M` | boolean | false | Enable <<_m_isa_extension>> (hardware-based integer multiplication and division).
| `RISCV_ISA_U` | boolean | false | Enable <<_u_isa_extension>> (less-privileged user mode).
| `RISCV_ISA_Zalrsc` | boolean | false | Enable <<_zalrsc_isa_extension>> (atomic reservation-set operations).
| `RISCV_ISA_Zaamo` | boolean | false | Enable <<_zaamo_isa_extension>> (atomic memory operations).
| `RISCV_ISA_Zba` | boolean | false | Enable <<_zba_isa_extension>> (shifted-add bit-manipulation instructions).
| `RISCV_ISA_Zbb` | boolean | false | Enable <<_zbb_isa_extension>> (basic bit-manipulation instructions).
| `RISCV_ISA_Zbkb` | boolean | false | Enable <<_zbkb_isa_extension>> (scalar cryptography bit manipulation instructions).
@ -576,67 +576,41 @@ explicit specific processor generic. See section <<_processor_external_bus_inter
:sectnums:
==== Reservation Set Controller
==== Atomic Memory Operations Controller
The reservation set controller is responsible for handling the load-reservate and store-conditional bus transaction that
are triggered by the `lr.w` (LR) and `sc.w` (SC) instructions from the CPU's <<_zalrsc_isa_extension>>.
The atomic memory operations (AMO) controller is responsible for handling the read-modify-write operations issued by the
CPU's <<_zaamo_isa_extension>>. For each AMO request, the controller executes an atomic set of three operations:
A "reservation" defines an address or address range that provides a guarding mechanism to support atomic accesses. A new
reservation is registered by the LR instruction. The address provided by this instruction defines the memory location
that is now monitored for atomic accesses. The according SC instruction evaluates the state of this reservation. If
the reservation is still valid the write access triggered by the SC instruction is finally executed and the instruction
return a "success" state (`rd` = 0). If the reservation has been invalidated the SC instruction will not write to memory
and will return a "failed" state (`rd` = 1).
.Simplified AMO Controller Operation
[cols="^1,<3,<6"]
[options="header",grid="rows"]
|=======================
| Step | Pseudo Code | Description
| 1 | `tmp1 <= MEM[address];` | Perform a read operation accessing the addressed memory
cell and store the loaded data into an internal buffer (`tmp1`).
| 2 | `tmp2 <= tmp1 OP cpu_wdata` | The buffered data from the first step is processed
using the write data provide by the CPU. The result is stored to another internal buffer (`tmp2`).
| 3 | `MEM[address] <= tmp2;` `cpu_rdata <= tmp1;` | The data from the second buffer (`tmp2`) is
written to the addressed memory cell. In parallel, the data from the first buffer (`tmp1` = original
content of the addresses memory cell) is sent back to the requesting CPU.
|=======================
.Reservation Set(s) and Granule
[NOTE]
The reservation set controller supports only **a single** global reservation set with a **word-aligned 4-byte granule**.
The controller performs two bus transactions: a read operations and a write operation. Only the acknowledge/error
handshake of the last transaction is sent back to the CPU.
The reservation is invalidated if...
* an SC instruction is executed that accesses an address **outside** of the reservation set of the previous LR instruction.
This SC instruction will **fail** (not writing to memory).
* an SC instruction is executed that accesses an address **inside** of the reservation set of the previous LR instruction.
This SC instruction will **succeed** (finally writing to memory).
* a normal store operation accesses an address **inside** of the current reservation set (by the CPU or by the DMA).
* a hardware reset is triggered.
.Consecutive LR Instructions
[NOTE]
If an LR instruction is followed by another LR instruction the reservation set of the former one is overridden
by the reservation set of the latter one.
.Bus Access Errors
[IMPORTANT]
If the LR operation causes a bus access error (raising a load access exception) the reservation **is registered anyway**.
If the SC operation causes a bus access error (raising a store access exception) an already registered reservation set
**is invalidated anyway**.
.Strong Semantic
[IMPORTANT]
The LR/SC mechanism follows the _strong semantic_ approach: the LR/SC instruction pair fails only if there is a write
access to the referenced memory location between the LR and SC instructions (by the CPU itself or by the DMA).
Context changes, interrupts, traps, etc. do not effect nor invalidate the reservation state at all.
As the AMO controller is the memory-nearest instance (see <<_bus_system>>) the previously described set of operations
cannot be interrupted. Hence, they execute in an atomic way.
.Physical Memory Attributes
[NOTE]
The reservation set can be set for _any_ address (only constrained by the configured granularity). This also
includes cached memory, memory-mapped IO devices and processor-external address spaces.
Bus transactions triggered by the LR instruction register a new reservation set and are delegated to the adressed
memory/device. Bus transactions triggered by the SC remove a reservation set and are forwarded to the adressed
memory/device only if the SC operations succeeds. Otherwise, the access request is not forwarded and a local ACK is
generated to terminate the bus transaction.
.LR/SC Bus Protocol
[NOTE]
More information regarding the LR/SC bus transactions and the the according protocol can be found in section
<<_bus_interface>> / <<_atomic_accesses>>.
Atomic memory operations can be executed for _any_ address. This also includes
cached memory, memory-mapped IO devices and processor-external address spaces.
.Cache Coherency
[IMPORTANT]
Atomic operations **always bypass** the cache using direct/uncached accesses. Care must be taken
to maintain data cache coherency (e.g. by using the `fence` instruction).
Atomic operations **always bypass** the CPU's <<_processor_internal_data_cache_dcache, data cache>>
using direct/uncached accesses. Care must be taken to maintain data cache coherency when accessing
cached memory (e.g. by using the `fence` instruction).
:sectnums:

View file

@ -19,7 +19,7 @@
**Overview**
The processor features an optional data cache to improve performance when using memories with high
access latencies. The cache is connected directly to the CPU's data access interface and provides
access latency. The cache is connected directly to the CPU's data access interface and provides
full-transparent accesses. The cache is direct-mapped and uses "write-allocate" and "write-back" strategies.
.Cached/Uncached Accesses
@ -28,8 +28,8 @@ The data cache provides direct accesses (= uncached) to memory in order to acces
processor-internal IO/peripheral modules). All accesses that target the address range from `0xF0000000` to `0xFFFFFFFF`
will not be cached at all (see section <<_address_space>>). Direct/uncached accesses have **lower** priority than
cache block operations to allow continuous burst transfer and also to maintain logical instruction forward
progress / data coherency. Furthermore, atomic load-reservate and store-conditional instructions (<<_zalrsc_isa_extension>>)
will always **bypass** the cache.
progress / data coherency. Furthermore, the atomic memory operations of the <<_zaamo_isa_extension>> will
always **bypass** the cache.
.Caching Internal Memories
[NOTE]

View file

@ -19,7 +19,7 @@
**Overview**
The processor features an optional instruction cache to improve performance when using memories with high
access latencies. The cache is connected directly to the CPU's instruction fetch interface and provides
access latency. The cache is connected directly to the CPU's instruction fetch interface and provides
full-transparent accesses. The cache is direct-mapped and read-only.
.Cached/Uncached Accesses
@ -28,8 +28,8 @@ The data cache provides direct accesses (= uncached) to memory in order to acces
processor-internal IO/peripheral modules). All accesses that target the address range from `0xF0000000` to `0xFFFFFFFF`
will not be cached at all (see section <<_address_space>>). Direct/uncached accesses have **lower** priority than
cache block operations to allow continuous burst transfer and also to maintain logical instruction forward
progress / data coherency. Furthermore, atomic load-reservate and store-conditional instructions (<<_zalrsc_isa_extension>>)
will always **bypass** the cache.
progress / data coherency. Furthermore, the atomic memory operations of the <<_zaamo_isa_extension>> will
always **bypass** the cache.
.Caching Internal Memories
[NOTE]

View file

@ -140,5 +140,5 @@ The data cache provides direct accesses (= uncached) to memory in order to acces
All accesses that target the address range from `0xF0000000` to `0xFFFFFFFF`
will not be cached at all (see section <<_address_space>>). Direct/uncached accesses have **lower** priority than
cache block operations to allow continuous burst transfer and also to maintain logical instruction forward
progress / data coherency. Furthermore, atomic load-reservate and store-conditional instructions (<<_zalrsc_isa_extension>>)
will always **bypass** the cache.
progress / data coherency. Furthermore, the atomic memory operations of the <<_zaamo_isa_extension>> will
always **bypass** the cache.

View file

@ -67,7 +67,6 @@ The NEORV32 HAL consists of the following files.
| `neorv32_cfs.c` | `neorv32_cfs.h` | <<_custom_functions_subsystem_cfs>> HAL
| `neorv32_clint.c` | `neorv32_clint.h` | <<_core_local_interruptor_clint>> HAL
| `neorv32_cpu.c` | `neorv32_cpu.h` | <<_neorv32_central_processing_unit_cpu>> HAL
| `neorv32_cpu_amo.c` | `neorv32_cpu_amo.h` | Emulation functions for the read-modify-write <<_zalrsc_isa_extension>> / `A` instructions
| | `neorv32_cpu_csr.h` | <<_control_and_status_registers_csrs>> definitions
| `neorv32_cpu_cfu.c` | `neorv32_cpu_cfu.h` | <<_custom_functions_unit_cfu>> HAL
| `neorv32_crc.c` | `neorv32_crc.h` | <<_cyclic_redundancy_check_crc>> HAL

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 KiB

After

Width:  |  Height:  |  Size: 359 KiB

Before After
Before After

View file

@ -1,25 +0,0 @@
{signal: [
{name: 'clk', wave: 'p....|.....|.....'},
[
"request",
{name: 'addr', wave: 'x3..x|.4..x|.5..x', data: ['addr', 'addr', 'addr']},
{name: 'data', wave: 'x....|.4..x|.5..x', data: ['wdata1', 'wdata2']},
{name: 'ben', wave: 'xx..x|.4..x|.5..x', data: ['0b1111', '0b1111']},
{name: 'stb', wave: '010..|.10..|.10..', node: '.a.....d......'},
{name: 'rw', wave: '0....|.1...|.....', node: '..............'},
{name: 'src', wave: '0....|.....|.....'},
{name: 'priv', wave: '0....|.....|.....'},
{name: 'rvso', wave: '01..0|.1..0|.1..0', node: '.b.......e....'},
],
{},
[
"response",
{name: 'data', wave: '2..32|...42|...52', data: ['0', 'rdata', '0', '0', '0', '1']},
{name: 'ack', wave: '0..10|...10|...10', node: '.........f....'},
{name: 'err', wave: '0....|.....|.....'},
],
{},
{name: 'rvs_valid', wave: '0.1..|....0|.....', node: '..c.......g...'}
],
edge: ['a~>c', 'b~>c', 'd~>g', 'e~>g', 'f~>g']
}

View file

@ -3,7 +3,7 @@
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -172,7 +172,8 @@ begin
-- Request Switch -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
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.amo <= a_req_i.amo when (sel = '0') else b_req_i.amo;
x_req_o.amoop <= a_req_i.amoop when (sel = '0') else b_req_i.amoop;
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;
@ -738,11 +739,11 @@ end neorv32_bus_io_switch_rtl;
-- ================================================================================ --
-- NEORV32 SoC - Processor Bus Infrastructure: Reservation Set Control --
-- NEORV32 SoC - Processor Bus Infrastructure: Atomic Memory Operations Controller --
-- -------------------------------------------------------------------------------- --
-- Reservation set controller for the A (atomic) ISA extension's LR.W --
-- (load-reservate) and SC.W (store-conditional) instructions. Only a single --
-- reservation set (granularity = 4 bytes) is supported. T --
-- Read-modify-write controller for the RISC-V A/Zaamp ISA extension. --
-- [WARNING] Load-reservate and store-conditional operations (Zalrsc ISA extension) --
-- are NOT supported! --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -753,136 +754,150 @@ end neorv32_bus_io_switch_rtl;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_bus_reservation_set is
entity neorv32_bus_amo_ctrl is
port (
-- global control --
clk_i : in std_ulogic; -- global clock, rising edge
rstn_i : in std_ulogic; -- global reset, low-active, async
-- external status and control --
rvs_addr_o : out std_ulogic_vector(31 downto 0);
rvs_valid_o : out std_ulogic;
rvs_clear_i : in std_ulogic;
clk_i : in std_ulogic; -- global clock, rising edge
rstn_i : in std_ulogic; -- global reset, low-active, async
-- core port --
core_req_i : in bus_req_t;
core_rsp_o : out bus_rsp_t;
core_req_i : in bus_req_t;
core_rsp_o : out bus_rsp_t;
-- system port --
sys_req_o : out bus_req_t;
sys_rsp_i : in bus_rsp_t
sys_req_o : out bus_req_t;
sys_rsp_i : in bus_rsp_t
);
end neorv32_bus_reservation_set;
end neorv32_bus_amo_ctrl;
architecture neorv32_bus_reservation_set_rtl of neorv32_bus_reservation_set is
architecture neorv32_bus_amo_ctrl_rtl of neorv32_bus_amo_ctrl is
-- reservation set --
type rsvs_t is record
state : std_ulogic_vector(1 downto 0);
addr : std_ulogic_vector(31 downto 2); -- reservated address; 4-byte granularity
valid : std_ulogic;
match : std_ulogic;
-- arbiter --
type state_t is (S_IDLE, S_READ_WAIT, S_EXECUTE, S_WRITE, S_WRITE_WAIT);
type arbiter_t is record
state : state_t;
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 rsvs : rsvs_t;
signal arbiter, arbiter_nxt : arbiter_t;
-- ACK override for failed SC.W --
signal ack_local : std_ulogic;
-- internal data ALU --
signal alu_res : std_ulogic_vector(31 downto 0);
-- comparator --
signal cmp_opa : std_ulogic_vector(32 downto 0);
signal cmp_opb : std_ulogic_vector(32 downto 0);
signal cmp_less : std_ulogic;
signal cmp_res : std_ulogic_vector(31 downto 0);
begin
-- Reservation Set Control ----------------------------------------------------------------
-- Arbiter Sync ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
rvs_control: process(rstn_i, clk_i)
arbiter_sync: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
rsvs.state <= "00";
rsvs.addr <= (others => '0');
arbiter.state <= S_IDLE;
arbiter.cmd <= (others => '0');
arbiter.rdata <= (others => '0');
arbiter.wdata <= (others => '0');
elsif rising_edge(clk_i) then
case rsvs.state is
arbiter <= arbiter_nxt;
end if;
end process arbiter_sync;
when "10" => -- active reservation: wait for condition to invalidate reservation
-- --------------------------------------------------------------------
if (core_req_i.stb = '1') and (core_req_i.rw = '0') and (core_req_i.rvso = '1') then -- another LR instruction overriding the current reservation
rsvs.addr <= core_req_i.addr(31 downto 2);
end if;
--
if (rvs_clear_i = '1') then -- external clear request (highest priority)
rsvs.state <= "00"; -- invalidate reservation
elsif (core_req_i.stb = '1') and (core_req_i.rw = '1') then -- write access
if (core_req_i.rvso = '1') then -- this is a SC operation
if (rsvs.match = '1') then -- SC to reservated address
rsvs.state <= "11"; -- execute SC instruction (reservation still valid)
else -- SC to any other address
rsvs.state <= "00"; -- invalidate reservation
end if;
-- Arbiter Comb ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
arbiter_comb: process(arbiter, core_req_i, sys_rsp_i)
begin
arbiter_nxt <= arbiter; -- defaults
case arbiter.state is
elsif (rsvs.match = '1') then -- normal write to reservated address
rsvs.state <= "00"; -- invalidate reservation
end if;
when S_IDLE => -- wait for AMO request; pass-through current request
-- ------------------------------------------------------------
if (core_req_i.stb = '1') and (core_req_i.amo = '1') then
arbiter_nxt.cmd <= core_req_i.amoop;
arbiter_nxt.wdata <= core_req_i.data;
arbiter_nxt.state <= S_READ_WAIT;
end if;
end if;
when S_READ_WAIT => -- wait for device read-access to complete
-- ------------------------------------------------------------
arbiter_nxt.rdata <= sys_rsp_i.data;
if (sys_rsp_i.ack = '1') or (sys_rsp_i.err = '1') then
arbiter_nxt.state <= S_EXECUTE;
end if;
when "11" => -- active reservation: invalidate reservation at the end of bus access
-- --------------------------------------------------------------------
if (sys_rsp_i.ack = '1') or (sys_rsp_i.err = '1') then
rsvs.state <= "00";
end if;
when S_EXECUTE => -- execute atomic data operation
-- ------------------------------------------------------------
arbiter_nxt.state <= S_WRITE;
when others => -- "0-" no active reservation: wait for new registration request
-- --------------------------------------------------------------------
if (core_req_i.stb = '1') and (core_req_i.rw = '0') and (core_req_i.rvso = '1') then -- load-reservate instruction
rsvs.addr <= core_req_i.addr(31 downto 2);
rsvs.state <= "10";
end if;
when S_WRITE => -- wait operation result to device
-- ------------------------------------------------------------
arbiter_nxt.state <= S_WRITE_WAIT;
when S_WRITE_WAIT => -- wait for device write-access to complete
-- ------------------------------------------------------------
if (sys_rsp_i.ack = '1') or (sys_rsp_i.err = '1') then
arbiter_nxt.state <= S_IDLE;
end if;
when others => -- undefined
-- ------------------------------------------------------------
arbiter_nxt.state <= S_IDLE;
end case;
end process arbiter_comb;
-- request switch --
sys_req_o.addr <= core_req_i.addr;
sys_req_o.data <= alu_res when (arbiter.state = S_WRITE) or (arbiter.state = S_WRITE_WAIT) else core_req_i.data;
sys_req_o.ben <= core_req_i.ben;
sys_req_o.stb <= '1' when (arbiter.state = S_WRITE) else core_req_i.stb;
sys_req_o.rw <= '1' when (arbiter.state = S_WRITE) or (arbiter.state = S_WRITE_WAIT) else core_req_i.rw;
sys_req_o.src <= core_req_i.src;
sys_req_o.priv <= core_req_i.priv;
sys_req_o.amo <= core_req_i.amo;
sys_req_o.amoop <= (others => '0'); -- the specific AMO type should not matter after this point
sys_req_o.fence <= core_req_i.fence;
sys_req_o.sleep <= core_req_i.sleep;
sys_req_o.debug <= core_req_i.debug;
-- 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.ack <= sys_rsp_i.ack when (arbiter.state = S_IDLE) or (arbiter.state = S_WRITE_WAIT) else '0';
-- Arbiter Sync ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
amo_alu: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
alu_res <= (others => '0');
elsif rising_edge(clk_i) then
case arbiter.cmd(2 downto 0) is
when "000" => alu_res <= arbiter.wdata; -- AMOSWAP
when "001" => alu_res <= std_ulogic_vector(unsigned(arbiter.rdata) + unsigned(arbiter.wdata)); -- AMOADD
when "010" => alu_res <= arbiter.rdata xor arbiter.wdata; -- AMOXOR
when "011" => alu_res <= arbiter.rdata and arbiter.wdata; -- AMOAND
when "100" => alu_res <= arbiter.rdata or arbiter.wdata; -- AMOOR
when others => alu_res <= cmp_res; -- AMOMIN[U] / AMOMAX[U]
end case;
end if;
end process rvs_control;
end process amo_alu;
-- address match? --
rsvs.match <= '1' when (core_req_i.addr(31 downto 2) = rsvs.addr) else '0';
-- reservation valid? --
rsvs.valid <= rsvs.state(1);
-- status for external system --
rvs_valid_o <= rsvs.valid;
rvs_addr_o <= rsvs.addr & "00";
-- comparator logic (min/max and signed/unsigned) --
cmp_opa <= (arbiter.rdata(arbiter.rdata'left) and arbiter.cmd(3)) & arbiter.rdata; -- sign-extend if signed operation
cmp_opb <= (arbiter.wdata(arbiter.wdata'left) and arbiter.cmd(3)) & arbiter.wdata; -- sign-extend if signed operation
cmp_less <= '1' when (signed(cmp_opa) < signed(cmp_opb)) else '0';
cmp_res <= cmp_opa(31 downto 0) when ((cmp_less xor arbiter.cmd(0)) = '1') else cmp_opb(31 downto 0);
-- System Bus Interface -------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- gated request --
bus_request: process(core_req_i, rsvs.valid)
begin
sys_req_o <= core_req_i;
if (core_req_i.rvso = '1') and (core_req_i.rw = '1') then -- SC operation
sys_req_o.stb <= core_req_i.stb and rsvs.valid; -- write allowed if reservation still valid
else -- normal memory request or LR
sys_req_o.stb <= core_req_i.stb;
end if;
end process bus_request;
-- if a SC.W instruction fails there will be no write-request being send to the bus system
-- so we need to provide a local ACK to complete the bus access
ack_override: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
ack_local <= '0';
elsif rising_edge(clk_i) then
ack_local <= core_req_i.rvso and core_req_i.stb and core_req_i.rw and (not rsvs.valid);
end if;
end process ack_override;
-- response --
core_rsp_o.err <= sys_rsp_i.err;
core_rsp_o.ack <= sys_rsp_i.ack or ack_local; -- generate local ACK if SC fails
-- inject 1 into read data's LSB if SC fails --
core_rsp_o.data(31 downto 1) <= sys_rsp_i.data(31 downto 1);
core_rsp_o.data(0) <= sys_rsp_i.data(0) or (core_req_i.rvso and core_req_i.rw and (not rsvs.valid));
end neorv32_bus_reservation_set_rtl;
end neorv32_bus_amo_ctrl_rtl;

View file

@ -26,7 +26,7 @@
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -178,7 +178,7 @@ begin
-- -------------------------------------------------------------------------------------------
dir_acc_d <= '1' when UC_ENABLE and -- direct accesses implemented
((unsigned(host_req_i.addr(31 downto 28)) >= unsigned(UC_BEGIN)) or -- uncached memory page
(host_req_i.rvso = '1')) else '0'; -- atomic (reservation set) operation
(host_req_i.amo = '1')) else '0'; -- atomic memory operation
-- request splitter: cached or direct access --
req_splitter: process(host_req_i, dir_acc_d)
@ -378,7 +378,7 @@ end neorv32_cache_rtl;
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -538,7 +538,7 @@ end neorv32_cache_host_rtl;
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -725,7 +725,7 @@ end neorv32_cache_memory_rtl;
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -851,7 +851,8 @@ begin
bus_req_o.ben <= (others => '1'); -- full-word writes only
bus_req_o.src <= '0'; -- cache accesses are always data accesses
bus_req_o.priv <= '0'; -- cache accesses are always "unprivileged" accesses
bus_req_o.rvso <= '0'; -- cache accesses can never be a reservation set operation
bus_req_o.amo <= '0'; -- cache accesses can never be an atomic memory operation set operation
bus_req_o.amoop <= (others => '0'); -- cache accesses can never be an atomic memory operation set operation
bus_req_o.debug <= host_req_i.debug;
if (state = S_IDLE) then
bus_req_o.sleep <= host_req_i.sleep;

View file

@ -8,7 +8,7 @@
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -32,7 +32,7 @@ entity neorv32_cpu is
RISCV_ISA_E : boolean; -- implement embedded RF extension
RISCV_ISA_M : boolean; -- implement mul/div extension
RISCV_ISA_U : boolean; -- implement user mode extension
RISCV_ISA_Zalrsc : boolean; -- implement atomic reservation-set extension
RISCV_ISA_Zaamo : boolean; -- implement atomic memory operations extension
RISCV_ISA_Zba : boolean; -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb : boolean; -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb : boolean; -- implement bit-manipulation instructions for cryptography
@ -138,7 +138,7 @@ begin
cond_sel_string_f(RISCV_ISA_M, "m", "" ) &
cond_sel_string_f(RISCV_ISA_U, "u", "" ) &
cond_sel_string_f(true, "x", "" ) & -- always enabled
cond_sel_string_f(RISCV_ISA_Zalrsc, "_zalrsc", "" ) &
cond_sel_string_f(RISCV_ISA_Zaamo, "_zaamo", "" ) &
cond_sel_string_f(RISCV_ISA_Zba, "_zba", "" ) &
cond_sel_string_f(RISCV_ISA_Zbb, "_zbb", "" ) &
cond_sel_string_f(RISCV_ISA_Zbkb, "_zbkb", "" ) &
@ -213,7 +213,7 @@ begin
RISCV_ISA_E => RISCV_ISA_E, -- implement embedded RF extension
RISCV_ISA_M => RISCV_ISA_M, -- implement mul/div extension
RISCV_ISA_U => RISCV_ISA_U, -- implement user mode extension
RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc, -- implement atomic reservation-set extension
RISCV_ISA_Zaamo => RISCV_ISA_Zaamo, -- implement atomic memory operations extension
RISCV_ISA_Zba => RISCV_ISA_Zba, -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb => RISCV_ISA_Zbb, -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb, -- implement bit-manipulation instructions for cryptography
@ -269,6 +269,7 @@ begin
csr_rdata_o => csr_rdata, -- CSR read data
-- external CSR interface --
xcsr_we_o => xcsr_we, -- global write enable
xcsr_re_o => open, -- global read enable
xcsr_addr_o => xcsr_addr, -- address
xcsr_wdata_o => xcsr_wdata, -- write data
xcsr_rdata_i => xcsr_rdata_res, -- read data
@ -367,24 +368,24 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_cpu_lsu_inst: entity neorv32.neorv32_cpu_lsu
generic map (
AMO_LRSC_ENABLE => RISCV_ISA_Zalrsc -- enable atomic LR/SC operations
AMO_EN => RISCV_ISA_Zaamo -- enable atomic memory operations
)
port map (
-- global control --
clk_i => clk_gated, -- global clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_i => ctrl, -- main control bus
clk_i => clk_gated, -- global clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_i => ctrl, -- main control bus
-- cpu data access interface --
addr_i => alu_add, -- access address
wdata_i => rs2, -- write data
rdata_o => lsu_rdata, -- read data
mar_o => lsu_mar, -- memory address register
wait_o => lsu_wait, -- wait for access to complete
err_o => lsu_err, -- alignment/access errors
pmp_fault_i => pmp_fault, -- PMP read/write access fault
addr_i => alu_add, -- access address
wdata_i => rs2, -- write data
rdata_o => lsu_rdata, -- read data
mar_o => lsu_mar, -- memory address register
wait_o => lsu_wait, -- wait for access to complete
err_o => lsu_err, -- alignment/access errors
pmp_fault_i => pmp_fault, -- PMP read/write access fault
-- data bus --
dbus_req_o => dbus_req_o, -- request
dbus_rsp_i => dbus_rsp_i -- response
dbus_req_o => dbus_req_o, -- request
dbus_rsp_i => dbus_rsp_i -- response
);

View file

@ -14,7 +14,7 @@
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -40,7 +40,7 @@ entity neorv32_cpu_control is
RISCV_ISA_E : boolean; -- implement embedded-class register file extension
RISCV_ISA_M : boolean; -- implement mul/div extension
RISCV_ISA_U : boolean; -- implement user mode extension
RISCV_ISA_Zalrsc : boolean; -- implement atomic reservation-set extension
RISCV_ISA_Zaamo : boolean; -- implement atomic memory operations extension
RISCV_ISA_Zba : boolean; -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb : boolean; -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb : boolean; -- implement bit-manipulation instructions for cryptography
@ -96,6 +96,7 @@ entity neorv32_cpu_control is
csr_rdata_o : out std_ulogic_vector(XLEN-1 downto 0); -- CSR read data
-- external CSR interface --
xcsr_we_o : out std_ulogic; -- global write enable
xcsr_re_o : out std_ulogic; -- global read enable
xcsr_addr_o : out std_ulogic_vector(11 downto 0); -- address
xcsr_wdata_o : out std_ulogic_vector(XLEN-1 downto 0); -- write data
xcsr_rdata_i : in std_ulogic_vector(XLEN-1 downto 0); -- read data
@ -368,7 +369,8 @@ begin
ibus_req_o.ben <= (others => '0'); -- read-only
ibus_req_o.rw <= '0'; -- read-only
ibus_req_o.src <= '1'; -- source = instruction fetch
ibus_req_o.rvso <= '0'; -- cannot be a reservation set operation
ibus_req_o.amo <= '0'; -- cannot be an atomic memory operation
ibus_req_o.amoop <= (others => '0'); -- cannot be an atomic memory operation
ibus_req_o.fence <= ctrl.lsu_fence; -- fence operation, valid without STB being set
ibus_req_o.sleep <= sleep_mode; -- sleep mode, valid without STB being set
ibus_req_o.debug <= debug_ctrl.run; -- debug mode, valid without STB being set
@ -384,7 +386,7 @@ begin
FIFO_WIDTH => ipb.wdata(i)'length, -- size of data elements in FIFO
FIFO_RSYNC => false, -- we NEED to read data asynchronously
FIFO_SAFE => false, -- no safe access required (ensured by FIFO-external logic)
FULL_RESET => true -- map to FFs and add a dedicated reset
FULL_RESET => false -- no need for a full hardware reset
)
port map (
-- control --
@ -622,8 +624,8 @@ begin
end case;
-- memory read/write access --
if RISCV_ISA_Zalrsc and (opcode(2) = opcode_amo_c(2)) then -- atomic lr/sc
ctrl_nxt.lsu_rw <= exe_engine.ir(instr_funct7_lsb_c+2);
if RISCV_ISA_Zaamo and (opcode(2) = opcode_amo_c(2)) then -- atomic memory operation (executed as single load for the CPU)
ctrl_nxt.lsu_rw <= '0';
else -- normal load/store
ctrl_nxt.lsu_rw <= exe_engine.ir(5);
end if;
@ -806,7 +808,7 @@ begin
(trap_ctrl.exc_buf(exc_saccess_c) = '1') or (trap_ctrl.exc_buf(exc_laccess_c) = '1') or -- access exception
(trap_ctrl.exc_buf(exc_salign_c) = '1') or (trap_ctrl.exc_buf(exc_lalign_c) = '1') or -- alignment exception
(trap_ctrl.exc_buf(exc_illegal_c) = '1') then -- illegal instruction exception
if (RISCV_ISA_Zalrsc and (opcode(2) = opcode_amo_c(2))) or (opcode(5) = '0') then -- atomic operation / normal load
if (RISCV_ISA_Zaamo and (opcode(2) = opcode_amo_c(2))) or (opcode(5) = '0') then -- atomic operation / normal load
ctrl_nxt.rf_wb_en <= '1'; -- allow write-back to register file (won't happen in case of exception)
end if;
exe_engine_nxt.state <= EX_DISPATCH;
@ -1033,10 +1035,12 @@ begin
when others => illegal_cmd <= '1';
end case;
when opcode_amo_c => -- atomic memory operation (LR/SC)
if (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") and RISCV_ISA_Zalrsc and
(exe_engine.ir(instr_funct7_lsb_c+6 downto instr_funct7_lsb_c+3) = "0001") then -- LR.W/SC.W
illegal_cmd <= '0';
when opcode_amo_c => -- atomic memory operation
if RISCV_ISA_Zaamo and (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") then
case exe_engine.ir(instr_funct5_msb_c downto instr_funct5_lsb_c) is
when "00001" | "00000" | "00100" | "01100" | "01000" | "10000" | "10100" | "11000" | "11100" => illegal_cmd <= '0';
when others => illegal_cmd <= '1';
end case;
end if;
when opcode_alu_c | opcode_alui_c | opcode_fop_c | opcode_cust0_c | opcode_cust1_c => -- ALU[I] / FPU / custom operations
@ -1329,6 +1333,7 @@ begin
-- External CSR Interface -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
xcsr_we_o <= csr.we;
xcsr_re_o <= '1' when (exe_engine.state = EX_SYSTEM) else '0';
xcsr_addr_o <= csr.addr;
xcsr_wdata_o <= csr.wdata;
@ -1852,9 +1857,9 @@ begin
csr.rdata(20) <= bool_to_ulogic_f(RISCV_ISA_Zksed); -- Zksed: ShangMi block cyphers
csr.rdata(21) <= bool_to_ulogic_f(RISCV_ISA_Zks); -- Zks: ShangMi algorithm suite
csr.rdata(22) <= bool_to_ulogic_f(RISCV_ISA_Zba); -- Zba: shifted-add bit-manipulation
csr.rdata(23) <= bool_to_ulogic_f(RISCV_ISA_Zbb); -- Zbb: basic bit-manipulation extension
csr.rdata(24) <= bool_to_ulogic_f(RISCV_ISA_Zbs); -- Zbs: single-bit bit-manipulation extension
csr.rdata(25) <= bool_to_ulogic_f(RISCV_ISA_Zalrsc); -- Zalrsc: reservation set extension
csr.rdata(23) <= bool_to_ulogic_f(RISCV_ISA_Zbb); -- Zbb: basic bit-manipulation
csr.rdata(24) <= bool_to_ulogic_f(RISCV_ISA_Zbs); -- Zbs: single-bit bit-manipulation
csr.rdata(25) <= bool_to_ulogic_f(RISCV_ISA_Zaamo); -- Zaamo: atomic memory operations
csr.rdata(26) <= '0'; -- reserved
csr.rdata(27) <= '0'; -- reserved
-- tuning options --
@ -2009,20 +2014,20 @@ begin
end process counter_event;
-- RISC-V-compliant counter events --
cnt_event(hpmcnt_event_cy_c) <= '1' when (sleep_mode = '0') else '0'; -- cycle: active cycle
cnt_event(hpmcnt_event_tm_c) <= '0'; -- time: not available
cnt_event(hpmcnt_event_cy_c) <= '1' when (sleep_mode = '0') else '0'; -- cycle: active cycle
cnt_event(hpmcnt_event_tm_c) <= '0'; -- time: not available
cnt_event(hpmcnt_event_ir_c) <= '1' when (exe_engine.state = EX_EXECUTE) else '0'; -- instret: retired (==executed!) instruction
-- NEORV32-specific counter events --
cnt_event(hpmcnt_event_compr_c) <= '1' when (exe_engine.state = EX_EXECUTE) and (exe_engine.ci = '1') else '0'; -- executed compressed instruction
cnt_event(hpmcnt_event_wait_dis_c) <= '1' when (exe_engine.state = EX_DISPATCH) and (issue_engine.valid = "00") else '0'; -- instruction dispatch wait cycle
cnt_event(hpmcnt_event_wait_alu_c) <= '1' when (exe_engine.state = EX_ALU_WAIT) else '0'; -- multi-cycle ALU wait cycle
cnt_event(hpmcnt_event_branch_c) <= '1' when (exe_engine.state = EX_BRANCH) else '0'; -- executed branch instruction
cnt_event(hpmcnt_event_branched_c) <= '1' when (exe_engine.state = EX_BRANCHED) else '0'; -- control flow transfer
cnt_event(hpmcnt_event_load_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '0') else '0'; -- executed load operation
cnt_event(hpmcnt_event_store_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '1') else '0'; -- executed store operation
cnt_event(hpmcnt_event_wait_lsu_c) <= '1' when (ctrl.lsu_req = '0') and (exe_engine.state = EX_MEM_RSP) else '0'; -- load/store memory wait cycle
cnt_event(hpmcnt_event_trap_c) <= '1' when (trap_ctrl.env_enter = '1') else '0'; -- entered trap
cnt_event(hpmcnt_event_compr_c) <= '1' when (exe_engine.state = EX_EXECUTE) and (exe_engine.ci = '1') else '0'; -- executed compressed instruction
cnt_event(hpmcnt_event_wait_dis_c) <= '1' when (exe_engine.state = EX_DISPATCH) and (issue_engine.valid = "00") else '0'; -- instruction dispatch wait cycle
cnt_event(hpmcnt_event_wait_alu_c) <= '1' when (exe_engine.state = EX_ALU_WAIT) else '0'; -- multi-cycle ALU wait cycle
cnt_event(hpmcnt_event_branch_c) <= '1' when (exe_engine.state = EX_BRANCH) else '0'; -- executed branch instruction
cnt_event(hpmcnt_event_branched_c) <= '1' when (exe_engine.state = EX_BRANCHED) else '0'; -- control flow transfer
cnt_event(hpmcnt_event_load_c) <= '1' when (ctrl.lsu_req = '1') and ((opcode(5) = '0') or (opcode(2) = '1')) else '0'; -- executed load operation
cnt_event(hpmcnt_event_store_c) <= '1' when (ctrl.lsu_req = '1') and ((opcode(5) = '1') or (opcode(2) = '1')) else '0'; -- executed store operation
cnt_event(hpmcnt_event_wait_lsu_c) <= '1' when (ctrl.lsu_req = '0') and (exe_engine.state = EX_MEM_RSP) else '0'; -- load/store memory wait cycle
cnt_event(hpmcnt_event_trap_c) <= '1' when (trap_ctrl.env_enter = '1') else '0'; -- entered trap
-- ****************************************************************************************************************************

View file

@ -16,7 +16,7 @@ use neorv32.neorv32_package.all;
entity neorv32_cpu_lsu is
generic (
AMO_LRSC_ENABLE : boolean -- enable atomic LR/SC operations
AMO_EN : boolean -- enable atomic memory operations
);
port (
-- global control --
@ -43,6 +43,7 @@ architecture neorv32_cpu_lsu_rtl of neorv32_cpu_lsu is
signal misaligned : std_ulogic; -- misaligned address
signal arbiter_req : std_ulogic; -- pending bus request
signal arbiter_err : std_ulogic; -- access error
signal amo_cmd : std_ulogic_vector(3 downto 0); -- atomic memory operation type
begin
@ -75,17 +76,19 @@ begin
mem_do_reg: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
dbus_req_o.rw <= '0';
dbus_req_o.priv <= '0';
dbus_req_o.rvso <= '0';
dbus_req_o.data <= (others => '0');
dbus_req_o.ben <= (others => '0');
dbus_req_o.rw <= '0';
dbus_req_o.priv <= '0';
dbus_req_o.amo <= '0';
dbus_req_o.amoop <= (others => '0');
dbus_req_o.data <= (others => '0');
dbus_req_o.ben <= (others => '0');
elsif rising_edge(clk_i) then
if (ctrl_i.lsu_mo_we = '1') then
-- type identifiers --
dbus_req_o.rw <= ctrl_i.lsu_rw; -- read/write
dbus_req_o.priv <= ctrl_i.lsu_priv; -- privilege level
dbus_req_o.rvso <= bool_to_ulogic_f(AMO_LRSC_ENABLE) and ctrl_i.ir_opcode(2); -- reservation set operation
dbus_req_o.rw <= ctrl_i.lsu_rw; -- read/write
dbus_req_o.priv <= ctrl_i.lsu_priv; -- privilege level
dbus_req_o.amo <= bool_to_ulogic_f(AMO_EN) and ctrl_i.ir_opcode(2); -- atomic memory operation
dbus_req_o.amoop <= amo_cmd;
-- data alignment + byte-enable --
case ctrl_i.ir_funct3(1 downto 0) is
when "00" => -- byte
@ -111,6 +114,27 @@ begin
dbus_req_o.debug <= ctrl_i.cpu_debug; -- out-of-band: this is valid without STB being set
-- atomic memory access operation encoding --
amo_encode: process(ctrl_i.ir_funct12)
begin
if AMO_EN then
case ctrl_i.ir_funct12(11 downto 7) is
when "00000" => amo_cmd <= "0001"; -- ADD
when "00100" => amo_cmd <= "0010"; -- XOR
when "01100" => amo_cmd <= "0011"; -- AND
when "01000" => amo_cmd <= "0100"; -- OR
when "10000" => amo_cmd <= "1110"; -- MIN
when "10100" => amo_cmd <= "1111"; -- MAX
when "11000" => amo_cmd <= "0110"; -- MINU
when "11100" => amo_cmd <= "0111"; -- MAXU
when others => amo_cmd <= "0000"; -- SWAP
end case;
else
amo_cmd <= (others => '0');
end if;
end process;
-- Data Input: Alignment and Sign-Extension -----------------------------------------------
-- -------------------------------------------------------------------------------------------
mem_di_reg: process(rstn_i, clk_i)

View file

@ -3,7 +3,7 @@
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -308,7 +308,8 @@ begin
dma_req_o.priv <= priv_mode_m_c; -- DMA accesses are always privileged
dma_req_o.src <= '0'; -- source = data access
dma_req_o.addr <= engine.src_addr when (engine.state = S_READ) else engine.dst_addr;
dma_req_o.rvso <= '0'; -- no reservation set operation possible
dma_req_o.amo <= '0'; -- no atomic memory operation possible
dma_req_o.amoop <= (others => '0'); -- no atomic memory operation possible
dma_req_o.fence <= cfg.enable and cfg.fence and engine.done; -- issue FENCE operation when transfer is done
dma_req_o.sleep <= '1' when (engine.state = S_IDLE) else '0'; -- idle = sleep mode
dma_req_o.debug <= '0'; -- can never ever be in debug mode

View file

@ -29,7 +29,7 @@ package neorv32_package is
-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100806"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100807"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width
@ -128,7 +128,8 @@ package neorv32_package is
rw : std_ulogic; -- 0=read, 1=write
src : std_ulogic; -- access source (1=instruction fetch, 0=data access)
priv : std_ulogic; -- set if privileged (machine-mode) access
rvso : std_ulogic; -- set if reservation set operation (atomic LR/SC)
amo : std_ulogic; -- set if atomic memory operation
amoop : std_ulogic_vector(3 downto 0); -- type of atomic memory operation
-- out-of-band signals --
fence : std_ulogic; -- set if fence(.i) request by upstream device, single-shot
sleep : std_ulogic; -- set if ALL upstream sources are in sleep mode
@ -151,7 +152,8 @@ package neorv32_package is
rw => '0',
src => '0',
priv => '0',
rvso => '0',
amo => '0',
amoop => (others => '0'),
fence => '0',
sleep => '1',
debug => '0'
@ -736,7 +738,7 @@ package neorv32_package is
RISCV_ISA_E : boolean := false;
RISCV_ISA_M : boolean := false;
RISCV_ISA_U : boolean := false;
RISCV_ISA_Zalrsc : boolean := false;
RISCV_ISA_Zaamo : boolean := false;
RISCV_ISA_Zba : boolean := false;
RISCV_ISA_Zbb : boolean := false;
RISCV_ISA_Zbkb : boolean := false;

View file

@ -43,7 +43,7 @@ entity neorv32_top is
RISCV_ISA_E : boolean := false; -- implement embedded RF extension
RISCV_ISA_M : boolean := false; -- implement mul/div extension
RISCV_ISA_U : boolean := false; -- implement user mode extension
RISCV_ISA_Zalrsc : boolean := false; -- implement atomic reservation-set extension
RISCV_ISA_Zaamo : boolean := false; -- implement atomic memory operations extension
RISCV_ISA_Zba : boolean := false; -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb : boolean := false; -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb : boolean := false; -- implement bit-manipulation instructions for cryptography
@ -508,7 +508,7 @@ begin
RISCV_ISA_E => RISCV_ISA_E,
RISCV_ISA_M => RISCV_ISA_M,
RISCV_ISA_U => RISCV_ISA_U,
RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc,
RISCV_ISA_Zaamo => RISCV_ISA_Zaamo,
RISCV_ISA_Zba => RISCV_ISA_Zba,
RISCV_ISA_Zbb => RISCV_ISA_Zbb,
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb,
@ -727,27 +727,24 @@ begin
-- **************************************************************************************************************************
-- Reservation Set Controller (for atomic LR/SC accesses)
-- Read-Modify-Write Controller for Atomic Memory Operations
-- **************************************************************************************************************************
neorv32_bus_reservation_set_true:
if RISCV_ISA_Zalrsc generate
neorv32_bus_reservation_set_inst: entity neorv32.neorv32_bus_reservation_set
neorv32_bus_amo_ctrl_true:
if RISCV_ISA_Zaamo generate
neorv32_bus_amo_ctrl_inst: entity neorv32.neorv32_bus_amo_ctrl
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
rvs_addr_o => open, -- yet unused
rvs_valid_o => open, -- yet unused
rvs_clear_i => '0', -- yet unused
core_req_i => main_req,
core_rsp_o => main_rsp,
sys_req_o => main2_req,
sys_rsp_i => main2_rsp
clk_i => clk_i,
rstn_i => rstn_sys,
core_req_i => main_req,
core_rsp_o => main_rsp,
sys_req_o => main2_req,
sys_rsp_i => main2_rsp
);
end generate;
neorv32_bus_reservation_set_false:
if not RISCV_ISA_Zalrsc generate
neorv32_bus_amo_ctrl_false:
if not RISCV_ISA_Zaamo generate
main2_req <= main_req;
main_rsp <= main2_rsp;
end generate;

View file

@ -6,7 +6,7 @@
# -- -------------------------------------------------------------------------------- --
# -- 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 --
# -- ================================================================================ --
@ -214,7 +214,7 @@ proc setup_ip_gui {} {
{ RISCV_ISA_E {E Extension} {Reduced register file size (16 registers only)} }
{ RISCV_ISA_M {M Extension} {Integer multiplication and division hardware} }
{ RISCV_ISA_U {U Extension} {Less-privileged user-mode} }
{ RISCV_ISA_Zalrsc {Zalrsc Extension} {Atomic reservation-set instructions} }
{ RISCV_ISA_Zaamo {Zaamo Extension} {Atomic memory operations instructions} }
{ RISCV_ISA_Zba {Zba Extension} {Shifted-add bit-manipulation instructions} }
{ RISCV_ISA_Zbb {Zbb Extension} {Basic bit-manipulation instructions} }
{ RISCV_ISA_Zbkb {Zbkb Extension} {Bit manipulation instructions for cryptography} }

View file

@ -8,7 +8,7 @@
-- -------------------------------------------------------------------------------- --
-- 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 --
-- ================================================================================ --
@ -40,7 +40,7 @@ entity neorv32_vivado_ip is
RISCV_ISA_E : boolean := false;
RISCV_ISA_M : boolean := false;
RISCV_ISA_U : boolean := false;
RISCV_ISA_Zalrsc : boolean := false;
RISCV_ISA_Zaamo : boolean := false;
RISCV_ISA_Zba : boolean := false;
RISCV_ISA_Zbb : boolean := false;
RISCV_ISA_Zbkb : boolean := false;
@ -366,7 +366,7 @@ begin
RISCV_ISA_E => RISCV_ISA_E,
RISCV_ISA_M => RISCV_ISA_M,
RISCV_ISA_U => RISCV_ISA_U,
RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc,
RISCV_ISA_Zaamo => RISCV_ISA_Zaamo,
RISCV_ISA_Zba => RISCV_ISA_Zba,
RISCV_ISA_Zbb => RISCV_ISA_Zbb,
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb,

View file

@ -27,7 +27,7 @@ entity neorv32_tb is
RISCV_ISA_E : boolean := false; -- implement embedded RF extension
RISCV_ISA_M : boolean := true; -- implement mul/div extension
RISCV_ISA_U : boolean := true; -- implement user mode extension
RISCV_ISA_Zalrsc : boolean := true; -- implement atomic reservation-set extension
RISCV_ISA_Zaamo : boolean := true; -- implement atomic memory operations extension
RISCV_ISA_Zba : boolean := true; -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb : boolean := true; -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb : boolean := true; -- implement bit-manipulation instructions for cryptography
@ -126,7 +126,7 @@ begin
RISCV_ISA_E => RISCV_ISA_E,
RISCV_ISA_M => RISCV_ISA_M,
RISCV_ISA_U => RISCV_ISA_U,
RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc,
RISCV_ISA_Zaamo => RISCV_ISA_Zaamo,
RISCV_ISA_Zba => RISCV_ISA_Zba,
RISCV_ISA_Zbb => RISCV_ISA_Zbb,
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb,

View file

@ -1,363 +0,0 @@
// ================================================================================ //
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
// Copyright (c) NEORV32 contributors. //
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
// SPDX-License-Identifier: BSD-3-Clause //
// ================================================================================ //
/**********************************************************************//**
* @file atomic_test/main.c
* @author Stephan Nolting
* @brief Test program for the NEORV32 'A' ISA extension - check the emulation
* of the AMO (read-modify-write) operations.
**************************************************************************/
#include <neorv32.h>
/**********************************************************************//**
* @name User configuration
**************************************************************************/
/**@{*/
/** UART BAUD rate */
#define BAUD_RATE (19200)
//** Number of test cases for each instruction */
#define NUM_TEST_CASES (1000)
//** Silent mode (only show actual errors when != 0) */
#define SILENT_MODE (1)
// Prototypes
uint32_t check_result(uint32_t num, uint32_t amo_var_old, uint32_t amo_var_pre, uint32_t amo_var_new, uint32_t amo_var);
void print_report(int num_err, int num_tests);
// Global variable for atomic accesses
volatile uint32_t amo_var;
/**********************************************************************//**
* Emulate atomic memory operation.
*
* @note This is a RTE "second-level" trap handler.
**************************************************************************/
void trap_handler_emulate_amo(void) {
uint32_t inst = neorv32_cpu_csr_read(CSR_MTINST);
// decompose I-type instruction
uint32_t opcode = (inst >> 0) & 0x07f;
uint32_t rd_addr = (inst >> 7) & 0x01f;
uint32_t funct3 = (inst >> 12) & 0x003;
uint32_t rs1_addr = (inst >> 15) & 0x01f;
uint32_t rs2_addr = (inst >> 20) & 0x01f;
uint32_t funct5 = (inst >> 27) & 0x01f;
// set opcode bit 1 as the instruction word might be transformed (de-compressed)
opcode |= 1 << 1;
#if 0
neorv32_uart0_printf("\n<< EMULATING >>\n");
neorv32_uart0_printf(" opcode: 0x%x\n", opcode);
neorv32_uart0_printf(" rd_addr: %u\n", rd_addr);
neorv32_uart0_printf(" funct3: %u\n", funct3);
neorv32_uart0_printf(" rs1_addr: %u\n", rs1_addr);
neorv32_uart0_printf(" rs2_addr: %u\n", rs2_addr);
neorv32_uart0_printf(" funct5: 0x%x\n", funct5);
neorv32_uart0_printf("<< /EMULATING >>\n\n");
#endif
// emulate if valid A operation and A ISA extension is available
if ((opcode == 0b0101111) && (funct3 == 0b010) && (neorv32_cpu_csr_read(CSR_MISA) & (1 << 0))) {
// get operands from main's context
uint32_t rs1 = neorv32_rte_context_get(rs1_addr);
uint32_t rs2 = neorv32_rte_context_get(rs2_addr);
uint32_t rd = 0, valid = 0;
// emulated functions
switch (funct5) {
case 0b00001: rd = neorv32_cpu_amoswapw(rs1, rs2); valid = 1; break; // amoswap.w
case 0b00000: rd = neorv32_cpu_amoaddw( rs1, rs2); valid = 1; break; // amoadd.w
case 0b00100: rd = neorv32_cpu_amoxorw( rs1, rs2); valid = 1; break; // amoxor.w
case 0b01100: rd = neorv32_cpu_amoandw( rs1, rs2); valid = 1; break; // amoand.w
case 0b01000: rd = neorv32_cpu_amoorw( rs1, rs2); valid = 1; break; // amoor.w
case 0b10000: rd = neorv32_cpu_amominw( rs1, rs2); valid = 1; break; // amomin.w
case 0b10100: rd = neorv32_cpu_amomaxw( rs1, rs2); valid = 1; break; // amomax.w
case 0b11000: rd = neorv32_cpu_amominuw(rs1, rs2); valid = 1; break; // amominu.w
case 0b11100: rd = neorv32_cpu_amomaxuw(rs1, rs2); valid = 1; break; // amomaxu.w
default: neorv32_rte_debug_handler(); break; // use the RTE debug handler for any other misaligned load exception
}
if (valid) {
// write result back to main's context
neorv32_rte_context_put(rd_addr, rd);
}
}
else {
neorv32_rte_debug_handler();
}
}
/**********************************************************************//**
* Main function; test all provided AMO emulation functions.
*
* @note This program requires the RISC-V A CPU extension.
*
* @return Irrelevant.
**************************************************************************/
int main() {
const uint32_t num_tests = (uint32_t)NUM_TEST_CASES;
// capture all exceptions and give debug info via UART
neorv32_rte_setup();
// install trap handler for "unaligned load address" exception
neorv32_rte_handler_install(RTE_TRAP_I_ILLEGAL, trap_handler_emulate_amo);
// setup UART0 at default baud rate, no interrupts
neorv32_uart0_setup(BAUD_RATE, 0);
// intro
neorv32_uart0_printf("<<< NEORV32 AMO Operations (atomic read-modify-write) Emulation Test >>>\n\n");
// check if A extension is implemented at all
if ((neorv32_cpu_csr_read(CSR_MXISA) & (1<<CSR_MXISA_ZALRSC)) == 0) {
neorv32_uart0_printf("Error! A ISA extension not implemented!\n");
return 1;
}
#if (SILENT_MODE != 0)
neorv32_uart0_printf("SILENT_MODE enabled (only showing actual errors)\n");
#endif
neorv32_uart0_printf("Starting tests (%u test case(s) per instruction)...\n\n", num_tests);
#if defined __riscv_atomic
uint32_t amo_addr;
uint32_t amo_var_old, amo_var_new, amo_var_update, amo_var_pre;
uint32_t i = 0, err_cnt = 0;
amo_addr = (uint32_t)&amo_var;
// AMOSWAP.W
neorv32_uart0_printf("\namoswap.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amoswap.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = amo_var_update;
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOADD.W
neorv32_uart0_printf("\namoadd.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amoadd.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = amo_var_old + amo_var_update;
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOAND.W
neorv32_uart0_printf("\namoand.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amoand.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = amo_var_old & amo_var_update;
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOOR.W
neorv32_uart0_printf("\namoor.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amoor.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = amo_var_old | amo_var_update;
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOXOR.W
neorv32_uart0_printf("\namoxor.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amoxor.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = amo_var_old ^ amo_var_update;
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOMAX.W
neorv32_uart0_printf("\namomax.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amomax.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = (uint32_t)neorv32_aux_max((int32_t)amo_var_old, (int32_t)amo_var_update);
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOMAXU.W
neorv32_uart0_printf("\namomaxu.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amomaxu.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = neorv32_aux_max(amo_var_old, amo_var_update);
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOMIN.W
neorv32_uart0_printf("\namomin.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amomin.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = (uint32_t)neorv32_aux_min((int32_t)amo_var_old, (int32_t)amo_var_update);
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
// AMOMINU.W
neorv32_uart0_printf("\namominu.w:\n");
err_cnt = 0;
for (i=0; i<num_tests; i++) {
amo_var_old = neorv32_aux_xorshift32();
amo_var_update = neorv32_aux_xorshift32();
amo_var = amo_var_old;
asm volatile ("fence");
asm volatile ("amominu.w %[dest], %[data], 0(%[addr])" : [dest] "=r" (amo_var_pre) : [data] "r" (amo_var_update), [addr] "r" (amo_addr));
asm volatile ("fence");
amo_var_new = neorv32_aux_min(amo_var_old, amo_var_update);
err_cnt += check_result(i, amo_var_old, amo_var_pre, amo_var_new, amo_var);
}
print_report(err_cnt, num_tests);
#else
#warning Program HAS NOT BEEN COMPILED since RISC-V 'A' ISA extension is not enabled!
neorv32_uart0_printf("\nProgram HAS NOT BEEN COMPILED since RISC-V 'A' ISA extension is not enabled!\n");
#endif
neorv32_uart0_printf("\n\nTests completed.\n");
return 0;
}
/**********************************************************************//**
* Check results (reference (SW) vs actual hardware).
*
* @param[in] num Test case number
* @param[in] amo_var_old Initial value of atomic variable.
* @param[in] amo_var_pre Value of atomic variable read from memory (before operation).
* @param[in] amo_var_new Expected new value of atomic variable.
* @param[in] amo_var Actual new value of atomic variable.
* @return zero if results are correct.
**************************************************************************/
uint32_t check_result(uint32_t num, uint32_t amo_var_old, uint32_t amo_var_pre, uint32_t amo_var_new, uint32_t amo_var) {
#if (SILENT_MODE == 0)
neorv32_uart0_printf("%u: MEM_INITIAL[addr] = 0x%x vs. MEM_PRE[addr] = 0x%x & MEM_NEW_ref[addr] = 0x%x vs. MEM_NEW[addr] = 0x%x, ", num, amo_var_old, amo_var_pre, amo_var_new, amo_var);
#endif
if ((amo_var_old != amo_var_pre) || (amo_var_new != amo_var)) {
#if (SILENT_MODE != 0)
neorv32_uart0_printf("%u: MEM_INITIAL[addr] = 0x%x vs. MEM_PRE[addr] = 0x%x & MEM_NEW_ref[addr] = 0x%x vs. MEM_NEW[addr] = 0x%x, ", num, amo_var_old, amo_var_pre, amo_var_new, amo_var);
#endif
neorv32_uart0_printf("%c[1m[FAILED]%c[0m\n", 27, 27);
return 1;
}
else {
#if (SILENT_MODE == 0)
neorv32_uart0_printf("%c[1m[ok]%c[0m\n", 27, 27);
#endif
return 0;
}
}
/**********************************************************************//**
* Print test report.
*
* @param[in] num_err Number or errors in this test.
* @param[in] num_tests Total number of conducted tests.
**************************************************************************/
void print_report(int num_err, int num_tests) {
neorv32_uart0_printf("Errors: %i/%i ", num_err, num_tests);
if (num_err == 0) {
neorv32_uart0_printf("%c[1m[ok]%c[0m\n", 27, 27);
}
else {
neorv32_uart0_printf("%c[1m[FAILED]%c[0m\n", 27, 27);
}
}

View file

@ -1,33 +0,0 @@
# Application makefile.
# Use this makefile to configure all relevant CPU / compiler options.
# Override the default CPU ISA
MARCH = rv32ia_zicsr_zifencei
# Override the default RISC-V GCC prefix
#RISCV_PREFIX ?= riscv-none-elf-
# Override default optimization goal
EFFORT = -Os
# Add extended debug symbols
USER_FLAGS += -ggdb -gdwarf-3
# Adjust processor IMEM size
USER_FLAGS += -Wl,--defsym,__neorv32_rom_size=16k
# Adjust processor DMEM size
USER_FLAGS += -Wl,--defsym,__neorv32_ram_size=8k
# Adjust maximum heap size
#USER_FLAGS += -Wl,--defsym,__neorv32_heap_size=1k
# Additional sources
#APP_SRC += $(wildcard ./*.c)
#APP_INC += -I .
# Set path to NEORV32 root directory
NEORV32_HOME ?= ../../..
# Include the main NEORV32 makefile
include $(NEORV32_HOME)/sw/common/common.mk

View file

@ -30,7 +30,7 @@ volatile uint8_t __attribute__ ((aligned (16))) core1_stack[2048]; // stack memo
* Main function for core 0 (primary core).
*
* @attention This program requires the dual-core configuration, the CLINT, UART0
* and the Zalrsc ISa extension.
* and the Zaamo ISA extension.
*
* @return Irrelevant (but can be inspected by the debugger).
**************************************************************************/
@ -57,8 +57,8 @@ int main(void) {
neorv32_uart0_printf("[ERROR] CLINT module not available!\n");
return -1;
}
if ((neorv32_cpu_csr_read(CSR_MXISA) & (1<<CSR_MXISA_ZALRSC)) == 0) { // atomic lr/sc operations available?
neorv32_uart0_printf("[ERROR] 'Zalrsc' ISA extension not available!\n");
if ((neorv32_cpu_csr_read(CSR_MXISA) & (1<<CSR_MXISA_ZAAMO)) == 0) { // atomic memory operations available?
neorv32_uart0_printf("[ERROR] 'Zaamo' ISA extension not available!\n");
return -1;
}
#ifndef __riscv_atomic

View file

@ -0,0 +1 @@
make USER_FLAGS+="-DUART0_SIM_MODE -DUART1_SIM_MODE" clean_all asm install sim

View file

@ -1,6 +1,6 @@
/**
* @file spinlock.c
* @brief Single simple spin-lock based on atomic lr/sc operations.
* @brief Single simple spin-lock based on atomic memory operations.
*/
#include <neorv32.h>
@ -18,7 +18,7 @@ static volatile uint32_t __spin_locked = 0;
**************************************************************************/
void spin_lock(void) {
while (neorv32_cpu_amoswapw((uint32_t)&__spin_locked, 1) != 0);
while(__sync_lock_test_and_set(&__spin_locked, -1)); // -> amoswap.w
}
@ -27,5 +27,6 @@ void spin_lock(void) {
**************************************************************************/
void spin_unlock(void) {
neorv32_cpu_amoswapw((uint32_t)&__spin_locked, 0);
//__sync_lock_release(&__spin_locked); // uses fence that is not required here
__sync_lock_test_and_set(&__spin_locked, 0); // -> amoswap.w
}

View file

@ -85,7 +85,6 @@ volatile uint32_t constr_test = 0; // for constructor test
volatile uint32_t dma_src; // dma source & destination data
volatile uint32_t store_access_addr[2]; // variable to test store accesses
volatile uint32_t amo_var; // variable for testing atomic memory accesses
volatile uint32_t __attribute__((aligned(4))) pmp_access[2]; // variable to test pmp
volatile uint32_t trap_cnt; // number of triggered traps
volatile uint32_t pmp_num_regions; // number of implemented pmp regions
@ -1976,88 +1975,6 @@ int main() {
}
// ----------------------------------------------------------
// Test atomic lr/sc memory access - failing access
// ----------------------------------------------------------
#if defined __riscv_atomic
neorv32_cpu_csr_write(CSR_MCAUSE, mcause_never_c);
PRINT_STANDARD("[%i] AMO LR/SC (", cnt_test);
PRINT_STANDARD("failing) ");
if (neorv32_cpu_csr_read(CSR_MXISA) & (1 << CSR_MXISA_ZALRSC)) {
cnt_test++;
// [NOTE] LR/SC operations bypass the data cache so we need to flush/reload
// it before/after making "normal" load/store operations
amo_var = 0x00cafe00; // initialize
asm volatile ("fence"); // flush/reload d-cache
tmp_a = neorv32_cpu_amolr((uint32_t)&amo_var);
amo_var = 0x10cafe00; // break reservation
asm volatile ("fence"); // flush/reload d-cache
tmp_b = neorv32_cpu_amosc((uint32_t)&amo_var, 0xaaaaaaaa);
tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)&amo_var, 0xcccccccc); // another SC: must fail
tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)ADDR_UNREACHABLE, 0); // another SC: must fail; no bus exception!
asm volatile ("fence"); // flush/reload d-cache
if ((tmp_a == 0x00cafe00) && // correct LR.W result
(amo_var == 0x10cafe00) && // atomic variable NOT updates by SC.W
(tmp_b == 0x00000007) && // SC.W[2] failed, SC.W[1] failed, SC.W[0] failed
(neorv32_cpu_csr_read(CSR_MCAUSE) == mcause_never_c)) { // no exception
test_ok();
}
else {
test_fail();
}
}
else {
PRINT_STANDARD("[n.a.]\n");
}
#endif
// ----------------------------------------------------------
// Test atomic lr/sc memory access - succeeding access
// ----------------------------------------------------------
#if defined __riscv_atomic
neorv32_cpu_csr_write(CSR_MCAUSE, mcause_never_c);
PRINT_STANDARD("[%i] AMO LR/SC (", cnt_test);
PRINT_STANDARD("succeed) ");
if (neorv32_cpu_csr_read(CSR_MXISA) & (1 << CSR_MXISA_ZALRSC)) {
cnt_test++;
// [NOTE] LR/SC operations bypass the data cache so we need to flush/reload
// it before/after making "normal" load/store operations
amo_var = 0x00abba00; // initialize
asm volatile ("fence"); // flush/reload d-cache
tmp_a = neorv32_cpu_amolr((uint32_t)&amo_var);
asm volatile ("fence"); // flush/reload d-cache
neorv32_cpu_load_unsigned_word((uint32_t)&amo_var); // dummy read, must not alter reservation set state
tmp_b = neorv32_cpu_amosc((uint32_t)&amo_var, 0xcccccccc);
tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)&amo_var, 0xcccccccc); // another SC: must fail
tmp_b = (tmp_b << 1) | neorv32_cpu_amosc((uint32_t)ADDR_UNREACHABLE, 0); // another SC: must fail; no bus exception!
asm volatile ("fence"); // flush/reload d-cache
if ((tmp_a == 0x00abba00) && // correct LR.W result
(amo_var == 0xcccccccc) && // atomic variable WAS updates by SC.W
(tmp_b == 0x00000003) && // SC.W[2] succeeded, SC.W[1] failed, SC.W[0] failed
(neorv32_cpu_csr_read(CSR_MCAUSE) == mcause_never_c)) { // no exception
test_ok();
}
else {
test_fail();
}
}
else {
PRINT_STANDARD("[n.a.]\n");
}
#endif
// ----------------------------------------------------------
// Test physical memory protection
// ----------------------------------------------------------

View file

@ -257,7 +257,6 @@ typedef union {
// CPU core
#include "neorv32_cpu.h"
#include "neorv32_cpu_amo.h"
#include "neorv32_cpu_csr.h"
#include "neorv32_cpu_cfu.h"

View file

@ -1,93 +0,0 @@
// ================================================================================ //
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
// Copyright (c) NEORV32 contributors. //
// 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 //
// ================================================================================ //
/**
* @file neorv32_cpu_amo.h
* @brief Atomic memory access (read-modify-write) emulation functions using LR/SC pairs - header file.
*
* @see https://stnolting.github.io/neorv32/sw/files.html
*/
#ifndef neorv32_cpu_amo_h
#define neorv32_cpu_amo_h
#include <stdint.h>
/**********************************************************************//**
* Atomic memory access: load-reservate word.
*
* @note The address has to be word-aligned - otherwise an alignment exception will be raised.
* @warning This function requires the A/Zalrsc ISA extension.
*
* @param[in] addr Address (32-bit).
* @return Read data word (32-bit).
**************************************************************************/
inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_amolr(uint32_t addr) {
#if defined __riscv_atomic
uint32_t amo_addr = addr;
uint32_t amo_rdata;
asm volatile ("lr.w %[dst], 0(%[addr])" : [dst] "=r" (amo_rdata) : [addr] "r" (amo_addr));
return amo_rdata;
#else
(void)addr;
return 0;
#endif
}
/**********************************************************************//**
* Atomic memory access: store-conditional word.
*
* @note The address has to be word-aligned - otherwise an alignment exception will be raised.
* @warning This function requires the A/Zalrsc ISA extension.
*
* @param[in] addr Address (32-bit).
* @param[in] wdata Data word to-be-written conditionally (32-bit).
* @return Status: 0 = ok, 1 = failed (32-bit).
**************************************************************************/
inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_amosc(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t amo_addr = addr;
uint32_t amo_wdata = wdata;
uint32_t amo_status;
asm volatile ("sc.w %[dst], %[src], (%[addr])" : [dst] "=r" (amo_status) : [src] "r" (amo_wdata), [addr] "r" (amo_addr));
return amo_status;
#else
(void)addr;
(void)wdata;
return 1; // always fail
#endif
}
/**********************************************************************//**
* @name Prototypes
**************************************************************************/
/**@{*/
uint32_t neorv32_cpu_amoswapw(uint32_t addr, uint32_t wdata);
uint32_t neorv32_cpu_amoaddw(uint32_t addr, uint32_t wdata);
uint32_t neorv32_cpu_amoandw(uint32_t addr, uint32_t wdata);
uint32_t neorv32_cpu_amoorw(uint32_t addr, uint32_t wdata);
uint32_t neorv32_cpu_amoxorw(uint32_t addr, uint32_t wdata);
int32_t neorv32_cpu_amomaxw(uint32_t addr, int32_t wdata);
uint32_t neorv32_cpu_amomaxuw(uint32_t addr, uint32_t wdata);
int32_t neorv32_cpu_amominw(uint32_t addr, int32_t wdata);
uint32_t neorv32_cpu_amominuw(uint32_t addr, uint32_t wdata);
/**@}*/
#endif // neorv32_cpu_amo_h

View file

@ -329,7 +329,7 @@ enum NEORV32_CSR_XISA_enum {
CSR_MXISA_ZBA = 22, /**< CPU mxisa CSR (22): shifted-add bit-manipulation operations (r/-)*/
CSR_MXISA_ZBB = 23, /**< CPU mxisa CSR (23): basic bit-manipulation operations (r/-)*/
CSR_MXISA_ZBS = 24, /**< CPU mxisa CSR (24): single-bit bit-manipulation operations (r/-)*/
CSR_MXISA_ZALRSC = 25, /**< CPU mxisa CSR (25): atomic reservation-set operations (r/-)*/
CSR_MXISA_ZAAMO = 25, /**< CPU mxisa CSR (25): atomic memory operations (r/-)*/
// Tuning options
CSR_MXISA_CLKGATE = 27, /**< CPU mxisa CSR (27): clock gating enabled (r/-)*/
CSR_MXISA_RFHWRST = 28, /**< CPU mxisa CSR (28): register file has full hardware reset (r/-)*/

View file

@ -331,7 +331,7 @@ void neorv32_aux_print_hw_config(void) {
if (tmp & (1<<CSR_MXISA_SDEXT)) { neorv32_uart0_printf("Sdext "); }
if (tmp & (1<<CSR_MXISA_SDTRIG)) { neorv32_uart0_printf("Sdtrig "); }
if (tmp & (1<<CSR_MXISA_SMPMP)) { neorv32_uart0_printf("Smpmp "); }
if (tmp & (1<<CSR_MXISA_ZALRSC)) { neorv32_uart0_printf("Zalrsc "); }
if (tmp & (1<<CSR_MXISA_ZAAMO)) { neorv32_uart0_printf("Zaamo "); }
if (tmp & (1<<CSR_MXISA_ZBA)) { neorv32_uart0_printf("Zba "); }
if (tmp & (1<<CSR_MXISA_ZBB)) { neorv32_uart0_printf("Zbb "); }
if (tmp & (1<<CSR_MXISA_ZBKB)) { neorv32_uart0_printf("Zbkb "); }

View file

@ -1,338 +0,0 @@
// ================================================================================ //
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
// Copyright (c) NEORV32 contributors. //
// 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 //
// ================================================================================ //
/**
* @file neorv32_cpu_amo.c
* @brief Atomic memory access (read-modify-write) emulation functions using LR/SC pairs - source file.
*
* @see https://stnolting.github.io/neorv32/sw/files.html
*/
#include <neorv32.h>
/**********************************************************************//**
* Atomic SWAP (AMOSWAP.W).
* return <= MEM[addr]; MEM[addr] <= wdata
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically stored to address (32-bit).
* @return Pre-operation data loaded from address (32-bit)
**************************************************************************/
uint32_t neorv32_cpu_amoswapw(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t rdata;
uint32_t status;
while(1) {
rdata = neorv32_cpu_amolr(addr);
status = neorv32_cpu_amosc(addr, wdata);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic ADD (AMOADD.W).
* return <= MEM[addr]; MEM[addr] <= MEM[addr] + wdata
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically added to original data at address (32-bit).
* @return Pre-operation data loaded from address (32-bit)
**************************************************************************/
uint32_t neorv32_cpu_amoaddw(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t rdata;
uint32_t tmp;
uint32_t status;
while(1) {
rdata = neorv32_cpu_amolr(addr);
tmp = rdata + wdata;
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic AND (AMOAND.W).
* return <= MEM[addr]; MEM[addr] <= MEM[addr] and wdata
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically AND-ed with original data at address (32-bit).
* @return Pre-operation data loaded from address (32-bit)
**************************************************************************/
uint32_t neorv32_cpu_amoandw(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t rdata;
uint32_t tmp;
uint32_t status;
while(1) {
rdata = neorv32_cpu_amolr(addr);
tmp = rdata & wdata;
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic OR (AMOOR.W).
* return <= MEM[addr]; MEM[addr] <= MEM[addr] or wdata
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically OR-ed with original data at address (32-bit).
* @return Pre-operation data loaded from address (32-bit)
**************************************************************************/
uint32_t neorv32_cpu_amoorw(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t rdata;
uint32_t tmp;
uint32_t status;
while(1) {
rdata = neorv32_cpu_amolr(addr);
tmp = rdata | wdata;
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic XOR (AMOXOR.W).
* return <= MEM[addr]; MEM[addr] <= MEM[addr] xor wdata
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically XOR-ed with original data at address (32-bit).
* @return Pre-operation data loaded from address (32-bit)
**************************************************************************/
uint32_t neorv32_cpu_amoxorw(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t rdata;
uint32_t tmp;
uint32_t status;
while(1) {
rdata = neorv32_cpu_amolr(addr);
tmp = rdata ^ wdata;
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic signed MAX (AMOMAX.W).
* return <= MEM[addr]; MEM[addr] <= maximum_signed(MEM[addr], wdata)
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically MAX-ed with original data at address (signed 32-bit).
* @return Pre-operation data loaded from address (signed 32-bit)
**************************************************************************/
int32_t neorv32_cpu_amomaxw(uint32_t addr, int32_t wdata) {
#if defined __riscv_atomic
int32_t rdata;
int32_t tmp;
uint32_t status;
while(1) {
rdata = (int32_t)neorv32_cpu_amolr(addr);
tmp = neorv32_aux_max(rdata, wdata);
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic unsigned MAX (AMOMAXU.W).
* return <= MEM[addr]; MEM[addr] <= maximum_unsigned(MEM[addr], wdata)
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically MAX-ed with original data at address (unsigned 32-bit).
* @return Pre-operation data loaded from address (unsigned 32-bit)
**************************************************************************/
uint32_t neorv32_cpu_amomaxuw(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t rdata;
uint32_t tmp;
uint32_t status;
while(1) {
rdata = (uint32_t)neorv32_cpu_amolr(addr);
tmp = neorv32_aux_max(rdata, wdata);
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic signed MIN (AMOMIN.W).
* return <= MEM[addr]; MEM[addr] <= minimum_signed(MEM[addr], wdata)
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically MIN-ed with original data at address (signed 32-bit).
* @return Pre-operation data loaded from address (signed 32-bit)
**************************************************************************/
int32_t neorv32_cpu_amominw(uint32_t addr, int32_t wdata) {
#if defined __riscv_atomic
int32_t rdata;
int32_t tmp;
uint32_t status;
while(1) {
rdata = (int32_t)neorv32_cpu_amolr(addr);
tmp = neorv32_aux_min(rdata, wdata);
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}
/**********************************************************************//**
* Atomic unsigned MIN (AMOMINU.W).
* return <= MEM[addr]; MEM[addr] <= minimum_unsigned(MEM[addr], wdata)
*
* @note This function requires the CPU A/Zalrsc ISA extension.
*
* @param[in] addr 32-bit memory address, word-aligned.
* @param[in] wdata Data word to be atomically MIN-ed with original data at address (unsigned 32-bit).
* @return Pre-operation data loaded from address (unsigned 32-bit)
**************************************************************************/
uint32_t neorv32_cpu_amominuw(uint32_t addr, uint32_t wdata) {
#if defined __riscv_atomic
uint32_t rdata;
uint32_t tmp;
uint32_t status;
while(1) {
rdata = (uint32_t)neorv32_cpu_amolr(addr);
tmp = neorv32_aux_min(rdata, wdata);
status = neorv32_cpu_amosc(addr, tmp);
if (status == 0) {
break;
}
}
return rdata;
#else
(void)addr;
(void)wdata;
return 0;
#endif
}