mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-24 14:17:51 -04:00
[sw] remove AMO emulation layer
no we have "real" AMOs
This commit is contained in:
parent
b167053295
commit
b677f49af7
6 changed files with 0 additions and 829 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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