Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update RTE to support easy emulation of instructions #673

Merged
merged 5 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date (*dd.mm.yyyy*) | Version | Comment |
|:-------------------:|:-------:|:--------|
| 19.08.2023 | 1.8.8.1 | update RTE to support easy emulation of instructions; add example program to showcase how to emulate unaligned memory accesses; [#673](https://github.com/stnolting/neorv32/pull/673) |
| 18.08.2023 | [**:rocket:1.8.8**](https://github.com/stnolting/neorv32/releases/tag/v1.8.8) | **New release** |
| 17.08.2023 | 1.8.7.9 | minor rtl edits and cleanups; [#672](https://github.com/stnolting/neorv32/pull/672) |
| 13.08.2023 | 1.8.7.8 | :warning: constrain/optimize `mtval` and `mcounteren` CSRs; [#671](https://github.com/stnolting/neorv32/pull/671) |
Expand Down
5 changes: 4 additions & 1 deletion docs/datasheet/cpu.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ recommended that the trap handler software provides a means of accessing the pla
[IMPORTANT]
The CPU does not support resolving unaligned memory access by the hardware (this is not a
RISC-V-incompatibility issue but an important thing to know!). Any kind of unaligned memory access
will raise an exception to allow a _software-based_ emulation provided by the application.
will raise an exception to allow a _software-based_ emulation provided by the application. However, unaligned memory
access can be **emulated** using the NEORV32 runtime environment. See section <<_application_context_handling>>
for more information.

.No Atomic Read-Modify-Write Operations
[IMPORTANT]
Expand Down Expand Up @@ -128,6 +130,7 @@ effect maximal operation frequency.
[WARNING]
The CPU does not support a hardware-based handling of unaligned memory accesses! Any unaligned access will raise a bus load/store unaligned
address exception. The exception handler can be used to _emulate_ unaligned memory accesses in software.
See the NEORV32 Runtime Environment's <<_application_context_handling>> section for more information.


:sectnums:
Expand Down
32 changes: 32 additions & 0 deletions docs/datasheet/software_rte.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,35 @@ obtained from the <<_mcause>> CSR (see <<_neorv32_trap_listing>>). A full list o
| "Fast IRQ 0x0000000f" | `0x8000001f`
| "Unknown trap cause" | undefined
|=======================


==== Application Context Handling

Upon trap entry the RTE backups the _entire_ application context (i.e. all `x` general purpose registers)
to the stack. The context is restored automatically after trap completion. The base address of the according
stack frame is copied to the <<_mscratch>> CSR. By having this information available, the RTE provides dedicated
functions for accessing and _altering_ the application context:

.Context Access Functions
[source,c]
----
// Prototypes
uint32_t neorv32_rte_context_get(int x); // read register x
void neorv32_rte_context_put(int x, uint32_t data); write data to register x

// Examples
uint32_t tmp = neorv32_rte_context_get(9); // read register 'x9'
neorv32_rte_context_put(28, tmp); // write 'tmp' to register 'x28'
----

.RISC-V `E` Extension
[NOTE]
Registers `x16..x31` are not available if the RISC-V <<_e_isa_extension>> is enabled.

The context access functions can be used by application-specific trap handlers to emulate unsupported
CPU / SoC features like unimplemented IO modules, unsupported instructions and even unaligned memory accesses.

.Demo Program: Emulate Unaligned Memory Access
[TIP]
A demo program, which showcases how to emulate unaligned memory accesses using the NEORV32 runtime environment
can be found in `sw/example/demo_emulate_unaligned`.
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ package neorv32_package is

-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01080800"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01080801"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width, do not change!

Expand Down
196 changes: 196 additions & 0 deletions sw/example/demo_emulate_unaligned/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// #################################################################################################
// # << NEORV32 - Demo program for emulating unaligned memory accesses using the NEORV32 RTE >> #
// # ********************************************************************************************* #
// # BSD 3-Clause License #
// # #
// # Copyright (c) 2023, Stephan Nolting. All rights reserved. #
// # #
// # Redistribution and use in source and binary forms, with or without modification, are #
// # permitted provided that the following conditions are met: #
// # #
// # 1. Redistributions of source code must retain the above copyright notice, this list of #
// # conditions and the following disclaimer. #
// # #
// # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
// # conditions and the following disclaimer in the documentation and/or other materials #
// # provided with the distribution. #
// # #
// # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
// # endorse or promote products derived from this software without specific prior written #
// # permission. #
// # #
// # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
// # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
// # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
// # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
// # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
// # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
// # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
// # OF THE POSSIBILITY OF SUCH DAMAGE. #
// # ********************************************************************************************* #
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
// #################################################################################################


/**********************************************************************//**
* @file demo_emulate_unaligned/main.c
* @author Stephan Nolting
* @brief Demo program for emulating unaligned memory accesses using the NEORV32
* run-time environment (RTE).
**************************************************************************/

#include <neorv32.h>


/**********************************************************************//**
* @name User configuration
**************************************************************************/
/**@{*/
/** UART BAUD rate */
#define BAUD_RATE 19200
/**@}*/


/**********************************************************************//**
* @name Global variables
**************************************************************************/
volatile uint32_t data_block[2];


/**********************************************************************//**
* Emulate unaligned load-word operation
*
* @note This is a RTE "second-level" trap handler.
*
* @warning Compressed load instructions are not supported here!
**************************************************************************/
void trap_handler_emulate_unaligned_lw(void) {

uint32_t mepc = neorv32_cpu_csr_read(CSR_MEPC);

// this function assumes that the exception is raised by an UNCOMPRESSED load operation
uint32_t inst = neorv32_cpu_load_unsigned_word(mepc);

// decompose I-type instruction
uint32_t opcode = (inst >> 0) & 0x007;
uint32_t funct3 = (inst >> 12) & 0x003;
uint32_t rs1_addr = (inst >> 15) & 0x01f;
uint32_t rd_addr = (inst >> 7) & 0x01f;
uint32_t imm12 = (inst >> 20) & 0xfff;

// check if the trap-causing instruction is 'lw' instruction
if ((opcode == 0b0000011) && (funct3 == 0b010)) {

// neorv32_uart0_printf("\n<< emulating 'lw x%u, %i(x%u)' >>\n", rd_addr, imm12, rs1_addr);

// get operands from main's context
uint32_t rs1 = neorv32_rte_context_get(rs1_addr);

// emulated function
uint32_t addr = rs1 + imm12;
uint32_t b0 = (uint32_t)neorv32_cpu_load_unsigned_byte(addr + 0);
uint32_t b1 = (uint32_t)neorv32_cpu_load_unsigned_byte(addr + 1);
uint32_t b2 = (uint32_t)neorv32_cpu_load_unsigned_byte(addr + 2);
uint32_t b3 = (uint32_t)neorv32_cpu_load_unsigned_byte(addr + 3);
uint32_t rd = (b3 << 24) | (b2 << 16) | (b1 << 8) | (b0 << 0);

// write result back to main's context
neorv32_rte_context_put(rd_addr, rd);

}
}


/**********************************************************************//**
* Load 32-bit data from memory. This wrapper function is used to ensure the emitted
* load instruction is UNCOMPRESSED.
*
* @param[in] addr Address (32-bit).
* @return Read data word (32-bit).
**************************************************************************/
uint32_t lw32(uint32_t addr) {

uint32_t reg_addr = addr;
uint32_t reg_data;

asm volatile (
".option push \n"
".option norvc \n" // make sure this emits uncompressed code
"lw %[da], 0(%[ad]) \n"
".option pop \n"
: [da] "=r" (reg_data) : [ad] "r" (reg_addr)
);

return reg_data;
}


/**********************************************************************//**
* Demo program to showcase RTE-based emulation of unaligned memory accesses.
*
* @return Irrelevant.
**************************************************************************/
int main() {

uint32_t addr, data;

// setup NEORV32 runtime environment
neorv32_rte_setup();

// setup UART at default baud rate, no interrupts
neorv32_uart0_setup(BAUD_RATE, 0);

// intro
neorv32_uart0_printf("\n<<< Demo: Emulation of Unaligned Memory Accesses >>>\n");

// show source data block
data_block[0] = 0x00112233;
data_block[1] = 0x44556677;
neorv32_uart0_printf("\nSource data:\n");
neorv32_uart0_printf("MEM[0x%x] = 0x%x\n", (uint32_t)&data_block[0], data_block[0]);
neorv32_uart0_printf("MEM[0x%x] = 0x%x\n", (uint32_t)&data_block[1], data_block[1]);


// ------------------------------------------
// Without emulation: RTE debug handler will show an error
// ------------------------------------------
neorv32_uart0_printf("\nUnaligned load without emulation:\n");

addr = ((uint32_t)&data_block[0]) + 1; // = unaligned address
neorv32_uart0_printf("MEM[0x%x] = ", addr);

data = lw32(addr); // this will raise an exception

if (data == 0x77001122) {
neorv32_uart0_printf("0x%x [ok]\n", data);
}
else {
neorv32_uart0_printf("[FAILED]\n");
}


// ------------------------------------------
// With emulation: operation is handled by trap_handler_emulate_unaligned_lw
// ------------------------------------------
neorv32_uart0_printf("\nUnaligned load with emulation:\n");

// install trap handler for "unaligned load address" exception
neorv32_rte_handler_install(RTE_TRAP_L_MISALIGNED, trap_handler_emulate_unaligned_lw);

addr = ((uint32_t)&data_block[0]) + 1; // = unaligned address
neorv32_uart0_printf("MEM[0x%x] = ", addr);

data = lw32(addr); // this will raise an exception

if (data == 0x77001122) {
neorv32_uart0_printf("0x%x [ok]\n", data);
}
else {
neorv32_uart0_printf("[FAILED]\n");
}


neorv32_uart0_printf("\nProgram completed.\n");
return 0;
}
4 changes: 4 additions & 0 deletions sw/example/demo_emulate_unaligned/makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Modify this variable to fit your NEORV32 setup (neorv32 home folder)
NEORV32_HOME ?= ../../..

include $(NEORV32_HOME)/sw/common/common.mk
8 changes: 5 additions & 3 deletions sw/lib/include/neorv32_rte.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ enum NEORV32_RTE_TRAP_enum {
* @name Prototypes
**************************************************************************/
/**@{*/
void neorv32_rte_setup(void);
int neorv32_rte_handler_install(int id, void (*handler)(void));
int neorv32_rte_handler_uninstall(int id);
void neorv32_rte_setup(void);
int neorv32_rte_handler_install(int id, void (*handler)(void));
int neorv32_rte_handler_uninstall(int id);
uint32_t neorv32_rte_context_get(int x);
void neorv32_rte_context_put(int x, uint32_t data);

void neorv32_rte_print_hw_config(void);
void neorv32_rte_print_hw_version(void);
Expand Down
Loading