mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-24 06:07:52 -04:00
⚠️✨ replace Zalrsc ISA extension by Zaamo ISA extension (#1141)
This commit is contained in:
commit
651732de84
35 changed files with 323 additions and 1255 deletions
|
@ -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) |
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 |
Binary file not shown.
Before Width: | Height: | Size: 355 KiB After Width: | Height: | Size: 359 KiB |
|
@ -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']
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
-- ****************************************************************************************************************************
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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} }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
1
sw/example/demo_dual_core/run_check.sh
Normal file
1
sw/example/demo_dual_core/run_check.sh
Normal file
|
@ -0,0 +1 @@
|
|||
make USER_FLAGS+="-DUART0_SIM_MODE -DUART1_SIM_MODE" clean_all asm install sim
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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/-)*/
|
||||
|
|
|
@ -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 "); }
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue