// #################################################################################################
|
// #################################################################################################
|
// # << NEORV32 - Processor Test Program >> #
|
// # << NEORV32 - Processor Test Program >> #
|
// # ********************************************************************************************* #
|
// # ********************************************************************************************* #
|
// # BSD 3-Clause License #
|
// # BSD 3-Clause License #
|
// # #
|
// # #
|
// # Copyright (c) 2021, Stephan Nolting. All rights reserved. #
|
// # Copyright (c) 2021, Stephan Nolting. All rights reserved. #
|
// # #
|
// # #
|
// # Redistribution and use in source and binary forms, with or without modification, are #
|
// # Redistribution and use in source and binary forms, with or without modification, are #
|
// # permitted provided that the following conditions are met: #
|
// # permitted provided that the following conditions are met: #
|
// # #
|
// # #
|
// # 1. Redistributions of source code must retain the above copyright notice, this list of #
|
// # 1. Redistributions of source code must retain the above copyright notice, this list of #
|
// # conditions and the following disclaimer. #
|
// # conditions and the following disclaimer. #
|
// # #
|
// # #
|
// # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
|
// # 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 #
|
// # conditions and the following disclaimer in the documentation and/or other materials #
|
// # provided with the distribution. #
|
// # provided with the distribution. #
|
// # #
|
// # #
|
// # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
|
// # 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 #
|
// # endorse or promote products derived from this software without specific prior written #
|
// # permission. #
|
// # permission. #
|
// # #
|
// # #
|
// # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
|
// # 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 #
|
// # 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 #
|
// # 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, #
|
// # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
|
// # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
|
// # 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 #
|
// # 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 #
|
// # 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 #
|
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
|
// # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
// # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
// # ********************************************************************************************* #
|
// # ********************************************************************************************* #
|
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
|
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
|
// #################################################################################################
|
// #################################################################################################
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* @file processor_check/main.c
|
* @file processor_check/main.c
|
* @author Stephan Nolting
|
* @author Stephan Nolting
|
* @brief CPU/Processor test program.
|
* @brief CPU/Processor test program.
|
**************************************************************************/
|
**************************************************************************/
|
|
|
#include <neorv32.h>
|
#include <neorv32.h>
|
#include <string.h>
|
#include <string.h>
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* @name User configuration
|
* @name User configuration
|
**************************************************************************/
|
**************************************************************************/
|
/**@{*/
|
/**@{*/
|
/** UART BAUD rate */
|
/** UART BAUD rate */
|
#define BAUD_RATE (19200)
|
#define BAUD_RATE (19200)
|
//** Reachable unaligned address */
|
//** Reachable unaligned address */
|
#define ADDR_UNALIGNED_1 (0x00000001)
|
#define ADDR_UNALIGNED_1 (0x00000001)
|
//** Reachable unaligned address */
|
//** Reachable unaligned address */
|
#define ADDR_UNALIGNED_2 (0x00000002)
|
#define ADDR_UNALIGNED_2 (0x00000002)
|
//** Unreachable word-aligned address */
|
//** Unreachable word-aligned address */
|
#define ADDR_UNREACHABLE (IO_BASE_ADDRESS-4)
|
#define ADDR_UNREACHABLE (IO_BASE_ADDRESS-4)
|
//** external memory base address */
|
//** external memory base address */
|
#define EXT_MEM_BASE (0xF0000000)
|
#define EXT_MEM_BASE (0xF0000000)
|
/**@}*/
|
/**@}*/
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* @name UART print macros
|
* @name UART print macros
|
**************************************************************************/
|
**************************************************************************/
|
/**@{*/
|
/**@{*/
|
//** for simulation only! */
|
//** for simulation only! */
|
#ifdef SUPPRESS_OPTIONAL_UART_PRINT
|
#ifdef SUPPRESS_OPTIONAL_UART_PRINT
|
//** print standard output to UART0 */
|
//** print standard output to UART0 */
|
#define PRINT_STANDARD(...)
|
#define PRINT_STANDARD(...)
|
//** print critical output to UART1 */
|
//** print critical output to UART1 */
|
#define PRINT_CRITICAL(...) neorv32_uart1_printf(__VA_ARGS__)
|
#define PRINT_CRITICAL(...) neorv32_uart1_printf(__VA_ARGS__)
|
#else
|
#else
|
//** print standard output to UART0 */
|
//** print standard output to UART0 */
|
#define PRINT_STANDARD(...) neorv32_uart0_printf(__VA_ARGS__)
|
#define PRINT_STANDARD(...) neorv32_uart0_printf(__VA_ARGS__)
|
//** print critical output to UART0 */
|
//** print critical output to UART0 */
|
#define PRINT_CRITICAL(...) neorv32_uart0_printf(__VA_ARGS__)
|
#define PRINT_CRITICAL(...) neorv32_uart0_printf(__VA_ARGS__)
|
#endif
|
#endif
|
/**@}*/
|
/**@}*/
|
|
|
|
|
// Prototypes
|
// Prototypes
|
void sim_irq_trigger(uint32_t sel);
|
void sim_irq_trigger(uint32_t sel);
|
void global_trap_handler(void);
|
void global_trap_handler(void);
|
void xirq_trap_handler0(void);
|
void xirq_trap_handler0(void);
|
void xirq_trap_handler1(void);
|
void xirq_trap_handler1(void);
|
void test_ok(void);
|
void test_ok(void);
|
void test_fail(void);
|
void test_fail(void);
|
|
|
// Global variables (also test initialization of global vars here)
|
// Global variables (also test initialization of global vars here)
|
/// Global counter for failing tests
|
/// Global counter for failing tests
|
int cnt_fail = 0;
|
int cnt_fail = 0;
|
/// Global counter for successful tests
|
/// Global counter for successful tests
|
int cnt_ok = 0;
|
int cnt_ok = 0;
|
/// Global counter for total number of tests
|
/// Global counter for total number of tests
|
int cnt_test = 0;
|
int cnt_test = 0;
|
/// Global number of available HPMs
|
/// Global number of available HPMs
|
uint32_t num_hpm_cnts_global = 0;
|
uint32_t num_hpm_cnts_global = 0;
|
/// XIRQ trap handler acknowledge
|
/// XIRQ trap handler acknowledge
|
uint32_t xirq_trap_handler_ack = 0;
|
uint32_t xirq_trap_handler_ack = 0;
|
|
|
/// Variable to test atomic accesses
|
/// Variable to test atomic accesses
|
uint32_t atomic_access_addr;
|
uint32_t atomic_access_addr;
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* High-level CPU/processor test program.
|
* High-level CPU/processor test program.
|
*
|
*
|
* @note Applications has to be compiler with <USER_FLAGS+=-DRUN_CPUTEST>
|
* @note Applications has to be compiler with <USER_FLAGS+=-DRUN_CPUTEST>
|
* @warning This test is intended for simulation only.
|
* @warning This test is intended for simulation only.
|
* @warning This test requires all optional extensions/modules to be enabled.
|
* @warning This test requires all optional extensions/modules to be enabled.
|
*
|
*
|
* @return 0 if execution was successful
|
* @return 0 if execution was successful
|
**************************************************************************/
|
**************************************************************************/
|
int main() {
|
int main() {
|
|
|
register uint32_t tmp_a, tmp_b;
|
register uint32_t tmp_a, tmp_b;
|
uint8_t id;
|
uint8_t id;
|
|
|
// disable global interrupts
|
// disable global interrupts
|
neorv32_cpu_dint();
|
neorv32_cpu_dint();
|
|
|
// init UARTs at default baud rate, no parity bits, no hw flow control
|
// init UARTs at default baud rate, no parity bits, no hw flow control
|
neorv32_uart0_setup(BAUD_RATE, PARITY_NONE, FLOW_CONTROL_NONE);
|
neorv32_uart0_setup(BAUD_RATE, PARITY_NONE, FLOW_CONTROL_NONE);
|
NEORV32_UART1.CTRL = NEORV32_UART0.CTRL; // copy configuration to initialize UART1
|
NEORV32_UART1.CTRL = NEORV32_UART0.CTRL; // copy configuration to initialize UART1
|
|
|
#ifdef SUPPRESS_OPTIONAL_UART_PRINT
|
#ifdef SUPPRESS_OPTIONAL_UART_PRINT
|
neorv32_uart0_disable(); // do not generate any UART0 output
|
neorv32_uart0_disable(); // do not generate any UART0 output
|
#endif
|
#endif
|
|
|
// Disable processor_check compilation by default
|
// Disable processor_check compilation by default
|
#ifndef RUN_CHECK
|
#ifndef RUN_CHECK
|
#warning processor_check HAS NOT BEEN COMPILED! Use >>make USER_FLAGS+=-DRUN_CHECK clean_all exe<< to compile it.
|
#warning processor_check HAS NOT BEEN COMPILED! Use >>make USER_FLAGS+=-DRUN_CHECK clean_all exe<< to compile it.
|
|
|
// inform the user if you are actually executing this
|
// inform the user if you are actually executing this
|
PRINT_CRITICAL("ERROR! processor_check has not been compiled. Use >>make USER_FLAGS+=-DRUN_CHECK clean_all exe<< to compile it.\n");
|
PRINT_CRITICAL("ERROR! processor_check has not been compiled. Use >>make USER_FLAGS+=-DRUN_CHECK clean_all exe<< to compile it.\n");
|
|
|
return 1;
|
return 1;
|
#endif
|
#endif
|
|
|
|
|
// ----------------------------------------------
|
// ----------------------------------------------
|
// setup RTE
|
// setup RTE
|
neorv32_rte_setup(); // this will install a full-detailed debug handler for ALL traps
|
neorv32_rte_setup(); // this will install a full-detailed debug handler for ALL traps
|
// ----------------------------------------------
|
// ----------------------------------------------
|
|
|
// check available hardware extensions and compare with compiler flags
|
// check available hardware extensions and compare with compiler flags
|
neorv32_rte_check_isa(0); // silent = 0 -> show message if isa mismatch
|
neorv32_rte_check_isa(0); // silent = 0 -> show message if isa mismatch
|
|
|
|
|
// intro
|
// intro
|
PRINT_STANDARD("\n<< PROCESSOR CHECK >>\n");
|
PRINT_STANDARD("\n<< PROCESSOR CHECK >>\n");
|
PRINT_STANDARD("build: "__DATE__" "__TIME__"\n");
|
PRINT_STANDARD("build: "__DATE__" "__TIME__"\n");
|
|
|
|
|
// reset performance counter
|
// reset performance counter
|
// neorv32_cpu_csr_write(CSR_MCYCLEH, 0); -> done in crt0.S
|
// neorv32_cpu_csr_write(CSR_MCYCLEH, 0); -> done in crt0.S
|
// neorv32_cpu_csr_write(CSR_MCYCLE, 0); -> done in crt0.S
|
// neorv32_cpu_csr_write(CSR_MCYCLE, 0); -> done in crt0.S
|
// neorv32_cpu_csr_write(CSR_MINSTRETH, 0); -> done in crt0.S
|
// neorv32_cpu_csr_write(CSR_MINSTRETH, 0); -> done in crt0.S
|
// neorv32_cpu_csr_write(CSR_MINSTRET, 0); -> done in crt0.S
|
// neorv32_cpu_csr_write(CSR_MINSTRET, 0); -> done in crt0.S
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, 0); // enable performance counter auto increment (ALL counters)
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, 0); // enable performance counter auto increment (ALL counters)
|
neorv32_cpu_csr_write(CSR_MCOUNTEREN, 7); // allow access from user-mode code to standard counters only
|
neorv32_cpu_csr_write(CSR_MCOUNTEREN, 7); // allow access from user-mode code to standard counters only
|
|
|
// set CMP of machine system timer MTIME to max to prevent an IRQ
|
// set CMP of machine system timer MTIME to max to prevent an IRQ
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_time(0);
|
neorv32_mtime_set_time(0);
|
|
|
|
|
// fancy intro
|
// fancy intro
|
// -----------------------------------------------
|
// -----------------------------------------------
|
// logo
|
// logo
|
neorv32_rte_print_logo();
|
neorv32_rte_print_logo();
|
|
|
// show project credits
|
// show project credits
|
neorv32_rte_print_credits();
|
neorv32_rte_print_credits();
|
|
|
// show full HW config report
|
// show full HW config report
|
neorv32_rte_print_hw_config();
|
neorv32_rte_print_hw_config();
|
|
|
|
|
// configure RTE
|
// configure RTE
|
// -----------------------------------------------
|
// -----------------------------------------------
|
PRINT_STANDARD("\n\nRTE setup... ");
|
PRINT_STANDARD("\n\nRTE setup... ");
|
|
|
int install_err = 0;
|
int install_err = 0;
|
// initialize ALL provided trap handler (overriding the default debug handlers)
|
// initialize ALL provided trap handler (overriding the default debug handlers)
|
for (id=0; id<NEORV32_RTE_NUM_TRAPS; id++) {
|
for (id=0; id<NEORV32_RTE_NUM_TRAPS; id++) {
|
install_err += neorv32_rte_exception_install(id, global_trap_handler);
|
install_err += neorv32_rte_exception_install(id, global_trap_handler);
|
}
|
}
|
if (install_err) {
|
if (install_err) {
|
PRINT_CRITICAL("ERROR!\n");
|
PRINT_CRITICAL("ERROR!\n");
|
return 1;
|
return 1;
|
}
|
}
|
|
|
// clear testbench IRQ triggers
|
// clear testbench IRQ triggers
|
sim_irq_trigger(0);
|
sim_irq_trigger(0);
|
|
|
// clear all interrupt enables, enable only where needed
|
// clear all interrupt enables, enable only where needed
|
neorv32_cpu_csr_write(CSR_MIE, 0);
|
neorv32_cpu_csr_write(CSR_MIE, 0);
|
|
|
// test intro
|
// test intro
|
PRINT_STANDARD("\nStarting tests.\n\n");
|
PRINT_STANDARD("\nStarting tests.\n\n");
|
|
|
|
// sync (test)
|
|
asm volatile ("fence.i");
|
|
|
// enable global interrupts
|
// enable global interrupts
|
neorv32_cpu_eint();
|
neorv32_cpu_eint();
|
|
|
|
|
// **********************************************************************************************
|
// **********************************************************************************************
|
// Run CPU and SoC tests
|
// Run CPU and SoC tests
|
// **********************************************************************************************
|
// **********************************************************************************************
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test performance counter: setup as many events and counter as feasible
|
// Test performance counter: setup as many events and counter as feasible
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Setup HPM events: ", cnt_test);
|
PRINT_STANDARD("[%i] Setup HPM events: ", cnt_test);
|
|
|
num_hpm_cnts_global = neorv32_cpu_hpm_get_counters();
|
num_hpm_cnts_global = neorv32_cpu_hpm_get_counters();
|
|
|
if (num_hpm_cnts_global != 0) {
|
if (num_hpm_cnts_global != 0) {
|
cnt_test++;
|
cnt_test++;
|
|
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER3, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT3, 1 << HPMCNT_EVENT_CIR);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER3, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT3, 1 << HPMCNT_EVENT_CIR);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER4, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT4, 1 << HPMCNT_EVENT_WAIT_IF);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER4, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT4, 1 << HPMCNT_EVENT_WAIT_IF);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER5, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT5, 1 << HPMCNT_EVENT_WAIT_II);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER5, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT5, 1 << HPMCNT_EVENT_WAIT_II);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER6, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT6, 1 << HPMCNT_EVENT_WAIT_MC);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER6, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT6, 1 << HPMCNT_EVENT_WAIT_MC);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER7, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT7, 1 << HPMCNT_EVENT_LOAD);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER7, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT7, 1 << HPMCNT_EVENT_LOAD);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER8, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT8, 1 << HPMCNT_EVENT_STORE);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER8, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT8, 1 << HPMCNT_EVENT_STORE);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER9, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT9, 1 << HPMCNT_EVENT_WAIT_LS);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER9, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT9, 1 << HPMCNT_EVENT_WAIT_LS);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER10, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT10, 1 << HPMCNT_EVENT_JUMP);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER10, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT10, 1 << HPMCNT_EVENT_JUMP);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER11, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT11, 1 << HPMCNT_EVENT_BRANCH);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER11, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT11, 1 << HPMCNT_EVENT_BRANCH);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER12, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT12, 1 << HPMCNT_EVENT_TBRANCH);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER12, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT12, 1 << HPMCNT_EVENT_TBRANCH);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER13, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT13, 1 << HPMCNT_EVENT_TRAP);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER13, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT13, 1 << HPMCNT_EVENT_TRAP);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER14, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT14, 1 << HPMCNT_EVENT_ILLEGAL);
|
neorv32_cpu_csr_write(CSR_MHPMCOUNTER14, 0); neorv32_cpu_csr_write(CSR_MHPMEVENT14, 1 << HPMCNT_EVENT_ILLEGAL);
|
|
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, 0); // enable all counters
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, 0); // enable all counters
|
|
|
// make sure there was no exception
|
// make sure there was no exception
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test standard RISC-V performance counter [m]cycle[h]
|
// Test standard RISC-V performance counter [m]cycle[h]
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] cycle counter: ", cnt_test);
|
PRINT_STANDARD("[%i] cycle counter: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// make sure counter is enabled
|
// make sure counter is enabled
|
asm volatile ("csrci %[addr], %[imm]" : : [addr] "i" (CSR_MCOUNTINHIBIT), [imm] "i" (1<<CSR_MCOUNTINHIBIT_CY));
|
asm volatile ("csrci %[addr], %[imm]" : : [addr] "i" (CSR_MCOUNTINHIBIT), [imm] "i" (1<<CSR_MCOUNTINHIBIT_CY));
|
|
|
// prepare overflow
|
// prepare overflow
|
neorv32_cpu_set_mcycle(0x00000000FFFFFFFFULL);
|
neorv32_cpu_set_mcycle(0x00000000FFFFFFFFULL);
|
|
|
// get current cycle counter HIGH
|
// get current cycle counter HIGH
|
tmp_a = neorv32_cpu_csr_read(CSR_MCYCLEH);
|
tmp_a = neorv32_cpu_csr_read(CSR_MCYCLEH);
|
|
|
// make sure cycle counter high has incremented and there was no exception during access
|
// make sure cycle counter high has incremented and there was no exception during access
|
if ((tmp_a == 1) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
if ((tmp_a == 1) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test standard RISC-V performance counter [m]instret[h]
|
// Test standard RISC-V performance counter [m]instret[h]
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] instret counter: ", cnt_test);
|
PRINT_STANDARD("[%i] instret counter: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// make sure counter is enabled
|
// make sure counter is enabled
|
asm volatile ("csrci %[addr], %[imm]" : : [addr] "i" (CSR_MCOUNTINHIBIT), [imm] "i" (1<<CSR_MCOUNTINHIBIT_IR));
|
asm volatile ("csrci %[addr], %[imm]" : : [addr] "i" (CSR_MCOUNTINHIBIT), [imm] "i" (1<<CSR_MCOUNTINHIBIT_IR));
|
|
|
// prepare overflow
|
// prepare overflow
|
neorv32_cpu_set_minstret(0x00000000FFFFFFFFULL);
|
neorv32_cpu_set_minstret(0x00000000FFFFFFFFULL);
|
|
|
// get instruction counter HIGH
|
// get instruction counter HIGH
|
tmp_a = neorv32_cpu_csr_read(CSR_INSTRETH);
|
tmp_a = neorv32_cpu_csr_read(CSR_INSTRETH);
|
|
|
// make sure instruction counter high has incremented and there was no exception during access
|
// make sure instruction counter high has incremented and there was no exception during access
|
if ((tmp_a == 1) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
if ((tmp_a == 1) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test mcountinhibt: inhibit auto-inc of [m]cycle
|
// Test mcountinhibt: inhibit auto-inc of [m]cycle
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] mcountinhibt.cy CSR: ", cnt_test);
|
PRINT_STANDARD("[%i] mcountinhibt.cy CSR: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// inhibit [m]cycle CSR
|
// inhibit [m]cycle CSR
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTINHIBIT);
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTINHIBIT);
|
tmp_a |= (1<<CSR_MCOUNTINHIBIT_CY); // inhibit cycle counter auto-increment
|
tmp_a |= (1<<CSR_MCOUNTINHIBIT_CY); // inhibit cycle counter auto-increment
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, tmp_a);
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, tmp_a);
|
|
|
// get current cycle counter
|
// get current cycle counter
|
tmp_a = neorv32_cpu_csr_read(CSR_CYCLE);
|
tmp_a = neorv32_cpu_csr_read(CSR_CYCLE);
|
|
|
// wait some time to have a nice "increment" (there should be NO increment at all!)
|
// wait some time to have a nice "increment" (there should be NO increment at all!)
|
asm volatile ("nop");
|
asm volatile ("nop");
|
asm volatile ("nop");
|
asm volatile ("nop");
|
|
|
tmp_b = neorv32_cpu_csr_read(CSR_CYCLE);
|
tmp_b = neorv32_cpu_csr_read(CSR_CYCLE);
|
|
|
// make sure instruction counter has NOT incremented and there was no exception during access
|
// make sure instruction counter has NOT incremented and there was no exception during access
|
if ((tmp_a == tmp_b) && (tmp_a != 0) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
if ((tmp_a == tmp_b) && (tmp_a != 0) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// re-enable [m]cycle CSR
|
// re-enable [m]cycle CSR
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTINHIBIT);
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTINHIBIT);
|
tmp_a &= ~(1<<CSR_MCOUNTINHIBIT_CY); // clear inhibit of cycle counter auto-increment
|
tmp_a &= ~(1<<CSR_MCOUNTINHIBIT_CY); // clear inhibit of cycle counter auto-increment
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, tmp_a);
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, tmp_a);
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test mcounteren: do not allow cycle[h] access from user-mode
|
// Test mcounteren: do not allow cycle[h] access from user-mode
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] mcounteren.cy CSR: ", cnt_test);
|
PRINT_STANDARD("[%i] mcounteren.cy CSR: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// do not allow user-level code to access cycle[h] CSRs
|
// do not allow user-level code to access cycle[h] CSRs
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTEREN);
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTEREN);
|
tmp_a &= ~(1<<CSR_MCOUNTEREN_CY); // clear access right
|
tmp_a &= ~(1<<CSR_MCOUNTEREN_CY); // clear access right
|
neorv32_cpu_csr_write(CSR_MCOUNTEREN, tmp_a);
|
neorv32_cpu_csr_write(CSR_MCOUNTEREN, tmp_a);
|
|
|
neorv32_cpu_csr_write(CSR_CYCLE, 1); // make sure CSR is != 0 for this test
|
neorv32_cpu_csr_write(CSR_CYCLE, 1); // make sure CSR is != 0 for this test
|
|
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
neorv32_cpu_goto_user_mode();
|
neorv32_cpu_goto_user_mode();
|
{
|
{
|
// access to cycle CSR is no longer allowed
|
// access to cycle CSR is no longer allowed
|
asm volatile (" mv %[result], zero \n" // initialize with zero
|
asm volatile (" mv %[result], zero \n" // initialize with zero
|
" rdcycle %[result] " // read CSR_CYCLE, is not allowed and should not alter [result]
|
" rdcycle %[result] " // read CSR_CYCLE, is not allowed and should not alter [result]
|
: [result] "=r" (tmp_a) : );
|
: [result] "=r" (tmp_a) : );
|
}
|
}
|
|
|
if (tmp_a != 0) {
|
if (tmp_a != 0) {
|
PRINT_CRITICAL("%c[1m<SECURITY FAILURE> %c[0m\n", 27, 27);
|
PRINT_CRITICAL("%c[1m<SECURITY FAILURE> %c[0m\n", 27, 27);
|
}
|
}
|
|
|
// make sure there was an illegal instruction trap
|
// make sure there was an illegal instruction trap
|
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) && (tmp_a == 0)) {
|
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) && (tmp_a == 0)) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// re-allow user-level code to access cycle[h] CSRs
|
// re-allow user-level code to access cycle[h] CSRs
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTEREN);
|
tmp_a = neorv32_cpu_csr_read(CSR_MCOUNTEREN);
|
tmp_a |= (1<<CSR_MCOUNTEREN_CY); // re-allow access right
|
tmp_a |= (1<<CSR_MCOUNTEREN_CY); // re-allow access right
|
neorv32_cpu_csr_write(CSR_MCOUNTEREN, tmp_a);
|
neorv32_cpu_csr_write(CSR_MCOUNTEREN, tmp_a);
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Execute MRET in U-mode (has to trap!)
|
// Execute MRET in U-mode (has to trap!)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] MRET in U-mode: ", cnt_test);
|
PRINT_STANDARD("[%i] MRET in U-mode: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
neorv32_cpu_goto_user_mode();
|
neorv32_cpu_goto_user_mode();
|
{
|
{
|
asm volatile ("mret");
|
asm volatile ("mret");
|
}
|
}
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// External memory interface test
|
// External memory interface test
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] External memory access (@ 0x%x): ", cnt_test, (uint32_t)EXT_MEM_BASE);
|
PRINT_STANDARD("[%i] External memory access (@ 0x%x): ", cnt_test, (uint32_t)EXT_MEM_BASE);
|
|
|
if (NEORV32_SYSINFO.SOC & (1 << SYSINFO_SOC_MEM_EXT)) {
|
if (NEORV32_SYSINFO.SOC & (1 << SYSINFO_SOC_MEM_EXT)) {
|
cnt_test++;
|
cnt_test++;
|
|
|
// create test program in RAM
|
// create test program in RAM
|
static const uint32_t dummy_ext_program[2] __attribute__((aligned(8))) = {
|
static const uint32_t dummy_ext_program[2] __attribute__((aligned(8))) = {
|
0x3407D073, // csrwi mscratch, 15
|
0x3407D073, // csrwi mscratch, 15
|
0x00008067 // ret (32-bit)
|
0x00008067 // ret (32-bit)
|
};
|
};
|
|
|
// copy to external memory
|
// copy to external memory
|
if (memcpy((void*)EXT_MEM_BASE, (void*)&dummy_ext_program, (size_t)sizeof(dummy_ext_program)) == NULL) {
|
if (memcpy((void*)EXT_MEM_BASE, (void*)&dummy_ext_program, (size_t)sizeof(dummy_ext_program)) == NULL) {
|
test_fail();
|
test_fail();
|
}
|
}
|
else {
|
else {
|
|
|
// execute program
|
// execute program
|
tmp_a = (uint32_t)EXT_MEM_BASE; // call the dummy sub program
|
tmp_a = (uint32_t)EXT_MEM_BASE; // call the dummy sub program
|
asm volatile ("jalr ra, %[input_i]" : : [input_i] "r" (tmp_a));
|
asm volatile ("jalr ra, %[input_i]" : : [input_i] "r" (tmp_a));
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) { // make sure there was no exception
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) { // make sure there was no exception
|
if (neorv32_cpu_csr_read(CSR_MSCRATCH) == 15) { // make sure the program was executed in the right way
|
if (neorv32_cpu_csr_read(CSR_MSCRATCH) == 15) { // make sure the program was executed in the right way
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test FENCE.I instruction (instruction buffer / i-cache clear & reload)
|
|
// if Zifencei is not implemented FENCE.I should execute as NOP
|
|
// ----------------------------------------------------------
|
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
|
PRINT_STANDARD("[%i] FENCE.I: ", cnt_test);
|
|
|
|
cnt_test++;
|
|
|
|
asm volatile ("fence.i");
|
|
|
|
// make sure there was no exception (and that the cpu did not crash...)
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) {
|
|
test_ok();
|
|
}
|
|
else {
|
|
test_fail();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------
|
|
// Illegal CSR access (CSR not implemented)
|
// Illegal CSR access (CSR not implemented)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Non-existent CSR access: ", cnt_test);
|
PRINT_STANDARD("[%i] Non-existent CSR access: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
tmp_a = neorv32_cpu_csr_read(0xfff); // CSR 0xfff not implemented
|
tmp_a = neorv32_cpu_csr_read(0xfff); // CSR 0xfff not implemented
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Write-access to read-only CSR
|
// Write-access to read-only CSR
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Read-only CSR write access: ", cnt_test);
|
PRINT_STANDARD("[%i] Read-only CSR write access: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
neorv32_cpu_csr_write(CSR_TIME, 0); // time CSR is read-only
|
neorv32_cpu_csr_write(CSR_TIME, 0); // time CSR is read-only
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// No "real" CSR write access (because rs1 = r0)
|
// No "real" CSR write access (because rs1 = r0)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Read-only CSR 'no-write' (rs1=0) access: ", cnt_test);
|
PRINT_STANDARD("[%i] Read-only CSR 'no-write' (rs1=0) access: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// time CSR is read-only, but no actual write is performed because rs1=r0
|
// time CSR is read-only, but no actual write is performed because rs1=r0
|
// -> should cause no exception
|
// -> should cause no exception
|
asm volatile("csrrs zero, time, zero");
|
asm volatile("csrrs zero, time, zero");
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Unaligned instruction address
|
// Unaligned instruction address
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] I_ALIGN (instr. alignment) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] I_ALIGN (instr. alignment) EXC: ", cnt_test);
|
|
|
// skip if C-mode is implemented
|
// skip if C-mode is implemented
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_C)) == 0) {
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_C)) == 0) {
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// call unaligned address
|
// call unaligned address
|
((void (*)(void))ADDR_UNALIGNED_2)();
|
((void (*)(void))ADDR_UNALIGNED_2)();
|
asm volatile("nop");
|
asm volatile("nop");
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_MISALIGNED) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_MISALIGNED) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (n.a. with C-ext)\n");
|
PRINT_STANDARD("skipped (n.a. with C-ext)\n");
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Instruction access fault
|
// Instruction access fault
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] I_ACC (instr. bus access) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] I_ACC (instr. bus access) EXC: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
|
|
// call unreachable aligned address
|
// call unreachable aligned address
|
((void (*)(void))ADDR_UNREACHABLE)();
|
((void (*)(void))ADDR_UNREACHABLE)();
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ACCESS) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ACCESS) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Illegal instruction
|
// Illegal instruction
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] I_ILLEG (illegal instr.) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] I_ILLEG (illegal instr.) EXC: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// illegal 32-bit instruction (malformed SUB)
|
// illegal 32-bit instruction (malformed SUB)
|
asm volatile (".align 4 \n"
|
asm volatile (".align 4 \n"
|
".word 0x80000033");
|
".word 0x80000033");
|
|
|
// make sure this has cause an illegal exception
|
// make sure this has cause an illegal exception
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
// make sure this is really the instruction that caused the exception
|
// make sure this is really the instruction that caused the exception
|
// -> for illegal instructions MTVAL contains the faulting instruction word
|
// -> for illegal instructions MTVAL contains the faulting instruction word
|
if (neorv32_cpu_csr_read(CSR_MTVAL) == 0x80000033) {
|
if (neorv32_cpu_csr_read(CSR_MTVAL) == 0x80000033) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Illegal compressed instruction
|
// Illegal compressed instruction
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] CI_ILLEG (illegal compr. instr.) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] CI_ILLEG (illegal compr. instr.) EXC: ", cnt_test);
|
|
|
// skip if C-mode is not implemented
|
// skip if C-mode is not implemented
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_C))) {
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_C))) {
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// illegal 16-bit instruction (official UNIMP instruction)
|
// illegal 16-bit instruction (official UNIMP instruction)
|
asm volatile (".align 2 \n"
|
asm volatile (".align 2 \n"
|
".half 0x0001 \n" // NOP
|
".half 0x0001 \n" // NOP
|
".half 0x0000"); // UNIMP
|
".half 0x0000"); // UNIMP
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (n.a. with C-ext)\n");
|
PRINT_STANDARD("skipped (n.a. with C-ext)\n");
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Breakpoint instruction
|
// Breakpoint instruction
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] BREAK (break instr.) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] BREAK (break instr.) EXC: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
|
|
asm volatile("EBREAK");
|
asm volatile("EBREAK");
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_BREAKPOINT) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_BREAKPOINT) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Unaligned load address
|
// Unaligned load address
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] L_ALIGN (load addr alignment) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] L_ALIGN (load addr alignment) EXC: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
|
|
// load from unaligned address
|
// load from unaligned address
|
neorv32_cpu_load_unsigned_word(ADDR_UNALIGNED_1);
|
neorv32_cpu_load_unsigned_word(ADDR_UNALIGNED_1);
|
|
|
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_L_MISALIGNED) &&
|
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_L_MISALIGNED) &&
|
(neorv32_cpu_csr_read(CSR_MTVAL) == ADDR_UNALIGNED_1)) {
|
(neorv32_cpu_csr_read(CSR_MTVAL) == ADDR_UNALIGNED_1)) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Load access fault
|
// Load access fault
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] L_ACC (load bus access) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] L_ACC (load bus access) EXC: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
|
|
// load from unreachable aligned address
|
// load from unreachable aligned address
|
neorv32_cpu_load_unsigned_word(ADDR_UNREACHABLE);
|
neorv32_cpu_load_unsigned_word(ADDR_UNREACHABLE);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_L_ACCESS) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_L_ACCESS) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Unaligned store address
|
// Unaligned store address
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] S_ALIGN (store addr alignment) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] S_ALIGN (store addr alignment) EXC: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
|
|
// store to unaligned address
|
// store to unaligned address
|
neorv32_cpu_store_unsigned_word(ADDR_UNALIGNED_2, 0);
|
neorv32_cpu_store_unsigned_word(ADDR_UNALIGNED_2, 0);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_S_MISALIGNED) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_S_MISALIGNED) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Store access fault
|
// Store access fault
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] S_ACC (store bus access) EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] S_ACC (store bus access) EXC: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
|
|
// store to unreachable aligned address
|
// store to unreachable aligned address
|
neorv32_cpu_store_unsigned_word(ADDR_UNREACHABLE, 0);
|
neorv32_cpu_store_unsigned_word(ADDR_UNREACHABLE, 0);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_S_ACCESS) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_S_ACCESS) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Environment call from M-mode
|
// Environment call from M-mode
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] ENVCALL (ecall instr.) from M-mode EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] ENVCALL (ecall instr.) from M-mode EXC: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
|
|
asm volatile("ECALL");
|
asm volatile("ECALL");
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MENV_CALL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MENV_CALL) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Environment call from U-mode
|
// Environment call from U-mode
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] ENVCALL (ecall instr.) from U-mode EXC: ", cnt_test);
|
PRINT_STANDARD("[%i] ENVCALL (ecall instr.) from U-mode EXC: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
neorv32_cpu_goto_user_mode();
|
neorv32_cpu_goto_user_mode();
|
{
|
{
|
asm volatile("ECALL");
|
asm volatile("ECALL");
|
}
|
}
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_UENV_CALL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_UENV_CALL) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Machine timer interrupt (MTIME)
|
// Machine timer interrupt (MTIME)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] MTI (MTIME): ", cnt_test);
|
PRINT_STANDARD("[%i] MTI (MTIME): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// configure MTIME IRQ (and check overflow from low word to high word)
|
// configure MTIME IRQ (and check overflow from low word to high word)
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_time(0);
|
neorv32_mtime_set_time(0);
|
|
|
// enable interrupt
|
// enable interrupt
|
neorv32_mtime_set_timecmp(0x0000000100000000ULL);
|
neorv32_mtime_set_timecmp(0x0000000100000000ULL);
|
neorv32_mtime_set_time( 0x00000000FFFFFFFEULL);
|
neorv32_mtime_set_time( 0x00000000FFFFFFFEULL);
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
|
|
// wait some time for the IRQ to trigger and arrive the CPU
|
// wait some time for the IRQ to trigger and arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
|
|
// disable interrupt
|
// disable interrupt
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MTI) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MTI) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// no more mtime interrupts
|
// no more mtime interrupts
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_timecmp(-1);
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Machine software interrupt (MSI) via testbench
|
// Machine software interrupt (MSI) via testbench
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] MSI (testbench): ", cnt_test);
|
PRINT_STANDARD("[%i] MSI (testbench): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// enable interrupt
|
// enable interrupt
|
neorv32_cpu_irq_enable(CSR_MIE_MSIE);
|
neorv32_cpu_irq_enable(CSR_MIE_MSIE);
|
|
|
// trigger IRQ
|
// trigger IRQ
|
sim_irq_trigger(1 << CSR_MIE_MSIE);
|
sim_irq_trigger(1 << CSR_MIE_MSIE);
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
|
|
// disable interrupt
|
// disable interrupt
|
neorv32_cpu_irq_disable(CSR_MIE_MSIE);
|
neorv32_cpu_irq_disable(CSR_MIE_MSIE);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MSI) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MSI) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Machine external interrupt (MEI) via testbench
|
// Machine external interrupt (MEI) via testbench
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] MEI (testbench): ", cnt_test);
|
PRINT_STANDARD("[%i] MEI (testbench): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// enable interrupt
|
// enable interrupt
|
neorv32_cpu_irq_enable(CSR_MIE_MEIE);
|
neorv32_cpu_irq_enable(CSR_MIE_MEIE);
|
|
|
// trigger IRQ
|
// trigger IRQ
|
sim_irq_trigger(1 << CSR_MIE_MEIE);
|
sim_irq_trigger(1 << CSR_MIE_MEIE);
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
|
|
// enable interrupt
|
// enable interrupt
|
neorv32_cpu_irq_disable(CSR_MIE_MEIE);
|
neorv32_cpu_irq_disable(CSR_MIE_MEIE);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MEI) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_MEI) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Permanent IRQ (make sure interrupted program advances)
|
// Permanent IRQ (make sure interrupted program advances)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Permanent IRQ (MTIME): ", cnt_test);
|
PRINT_STANDARD("[%i] Permanent IRQ (MTIME): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// fire MTIME IRQ
|
// fire MTIME IRQ
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
neorv32_mtime_set_time(1); // prepare overflow
|
neorv32_mtime_set_time(1); // prepare overflow
|
neorv32_mtime_set_timecmp(0); // IRQ on overflow
|
neorv32_mtime_set_timecmp(0); // IRQ on overflow
|
|
|
register int test_cnt = 0;
|
register int test_cnt = 0;
|
while(test_cnt < 2) {
|
while(test_cnt < 2) {
|
test_cnt++;
|
test_cnt++;
|
}
|
}
|
|
|
// end MTIME IRQ
|
// end MTIME IRQ
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_timecmp(-1);
|
|
|
if (test_cnt == 2) {
|
if (test_cnt == 2) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test pending interrupt
|
// Test pending interrupt
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Pending IRQ (MTIME): ", cnt_test);
|
PRINT_STANDARD("[%i] Pending IRQ (MTIME): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// enable interrupt
|
// enable interrupt
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
|
|
// disable global interrupts
|
// disable global interrupts
|
neorv32_cpu_dint();
|
neorv32_cpu_dint();
|
|
|
// fire MTIME IRQ
|
// fire MTIME IRQ
|
neorv32_mtime_set_time(1); // prepare overflow
|
neorv32_mtime_set_time(1); // prepare overflow
|
neorv32_mtime_set_timecmp(0); // IRQ on overflow
|
neorv32_mtime_set_timecmp(0); // IRQ on overflow
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
|
|
int was_pending = 0;
|
int was_pending = 0;
|
if (neorv32_cpu_csr_read(CSR_MIP) & (1 << CSR_MIP_MTIP)) { // should be pending now
|
if (neorv32_cpu_csr_read(CSR_MIP) & (1 << CSR_MIP_MTIP)) { // should be pending now
|
was_pending = 1;
|
was_pending = 1;
|
}
|
}
|
|
|
// clear pending MTI
|
// clear pending MTI
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_timecmp(-1);
|
|
|
int is_pending = 0;
|
int is_pending = 0;
|
if (neorv32_cpu_csr_read(CSR_MIP) & (1 << CSR_MIP_MTIP)) { // should NOT be pending anymore
|
if (neorv32_cpu_csr_read(CSR_MIP) & (1 << CSR_MIP_MTIP)) { // should NOT be pending anymore
|
is_pending = 1;
|
is_pending = 1;
|
}
|
}
|
|
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
|
|
if ((was_pending == 1) && (is_pending == 0)) {
|
if ((was_pending == 1) && (is_pending == 0)) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// re-enable global interrupts
|
// re-enable global interrupts
|
neorv32_cpu_eint();
|
neorv32_cpu_eint();
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 0 (WDT)
|
// Fast interrupt channel 0 (WDT)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_wdt_available()) {
|
if (neorv32_wdt_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ0 (WDT): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ0 (WDT): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// enable fast interrupt
|
// enable fast interrupt
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ0E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ0E);
|
|
|
// configure WDT
|
// configure WDT
|
neorv32_wdt_setup(CLK_PRSC_4096, 0, 1); // highest clock prescaler, trigger IRQ on timeout, lock access
|
neorv32_wdt_setup(CLK_PRSC_4096, 0, 1); // highest clock prescaler, trigger IRQ on timeout, lock access
|
NEORV32_WDT.CTRL = 0; // try to deactivate WDT (should fail as access is loced)
|
NEORV32_WDT.CTRL = 0; // try to deactivate WDT (should fail as access is loced)
|
neorv32_wdt_force(); // force watchdog into action
|
neorv32_wdt_force(); // force watchdog into action
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ0E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ0E);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_0) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_0) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// no more WDT interrupts
|
// no more WDT interrupts
|
neorv32_wdt_disable();
|
neorv32_wdt_disable();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 1 (CFS)
|
// Fast interrupt channel 1 (CFS)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
PRINT_STANDARD("[%i] FIRQ1 (CFS): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ1 (CFS): ", cnt_test);
|
PRINT_STANDARD("skipped \n");
|
PRINT_STANDARD("skipped \n");
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 2 (UART0.RX)
|
// Fast interrupt channel 2 (UART0.RX)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_uart1_available()) {
|
if (neorv32_uart1_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ2 (UART0.RX): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ2 (UART0.RX): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// wait for UART0 to finish transmitting
|
// wait for UART0 to finish transmitting
|
while(neorv32_uart0_tx_busy());
|
while(neorv32_uart0_tx_busy());
|
|
|
// backup current UART0 configuration
|
// backup current UART0 configuration
|
tmp_a = NEORV32_UART0.CTRL;
|
tmp_a = NEORV32_UART0.CTRL;
|
|
|
// make sure UART is enabled
|
// make sure UART is enabled
|
NEORV32_UART0.CTRL |= (1 << UART_CTRL_EN);
|
NEORV32_UART0.CTRL |= (1 << UART_CTRL_EN);
|
// make sure sim mode is disabled
|
// make sure sim mode is disabled
|
NEORV32_UART0.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
NEORV32_UART0.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
|
|
// trigger UART0 RX IRQ
|
// trigger UART0 RX IRQ
|
neorv32_uart0_putc(0);
|
neorv32_uart0_putc(0);
|
// wait for UART0 to finish transmitting
|
// wait for UART0 to finish transmitting
|
while(neorv32_uart0_tx_busy());
|
while(neorv32_uart0_tx_busy());
|
|
|
// enable fast interrupt
|
// enable fast interrupt
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ2E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ2E);
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ2E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ2E);
|
|
|
// restore original configuration
|
// restore original configuration
|
NEORV32_UART0.CTRL = tmp_a;
|
NEORV32_UART0.CTRL = tmp_a;
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_2) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_2) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 3 (UART0.TX)
|
// Fast interrupt channel 3 (UART0.TX)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_uart0_available()) {
|
if (neorv32_uart0_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ3 (UART0.TX): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ3 (UART0.TX): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// wait for UART0 to finish transmitting
|
// wait for UART0 to finish transmitting
|
while(neorv32_uart0_tx_busy());
|
while(neorv32_uart0_tx_busy());
|
|
|
// backup current UART0 configuration
|
// backup current UART0 configuration
|
tmp_a = NEORV32_UART0.CTRL;
|
tmp_a = NEORV32_UART0.CTRL;
|
|
|
// make sure UART is enabled
|
// make sure UART is enabled
|
NEORV32_UART0.CTRL |= (1 << UART_CTRL_EN);
|
NEORV32_UART0.CTRL |= (1 << UART_CTRL_EN);
|
// make sure sim mode is disabled
|
// make sure sim mode is disabled
|
NEORV32_UART0.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
NEORV32_UART0.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
|
|
// trigger UART0 TX IRQ
|
// trigger UART0 TX IRQ
|
neorv32_uart0_putc(0);
|
neorv32_uart0_putc(0);
|
// UART0 TX interrupt enable
|
// UART0 TX interrupt enable
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ3E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ3E);
|
// wait for UART to finish transmitting
|
// wait for UART to finish transmitting
|
while(neorv32_uart0_tx_busy());
|
while(neorv32_uart0_tx_busy());
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ3E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ3E);
|
|
|
// restore original configuration
|
// restore original configuration
|
NEORV32_UART0.CTRL = tmp_a;
|
NEORV32_UART0.CTRL = tmp_a;
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_3) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_3) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 4 (UART1.RX)
|
// Fast interrupt channel 4 (UART1.RX)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_uart1_available()) {
|
if (neorv32_uart1_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ4 (UART1.RX): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ4 (UART1.RX): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// backup current UART1 configuration
|
// backup current UART1 configuration
|
tmp_a = NEORV32_UART1.CTRL;
|
tmp_a = NEORV32_UART1.CTRL;
|
|
|
// make sure UART is enabled
|
// make sure UART is enabled
|
NEORV32_UART1.CTRL |= (1 << UART_CTRL_EN);
|
NEORV32_UART1.CTRL |= (1 << UART_CTRL_EN);
|
// make sure sim mode is disabled
|
// make sure sim mode is disabled
|
NEORV32_UART1.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
NEORV32_UART1.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
|
|
// trigger UART1 RX IRQ
|
// trigger UART1 RX IRQ
|
neorv32_uart1_putc(0);
|
neorv32_uart1_putc(0);
|
// wait for UART1 to finish transmitting
|
// wait for UART1 to finish transmitting
|
while(neorv32_uart1_tx_busy());
|
while(neorv32_uart1_tx_busy());
|
// UART1 RX interrupt enable
|
// UART1 RX interrupt enable
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ4E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ4E);
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ4E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ4E);
|
|
|
// restore original configuration
|
// restore original configuration
|
NEORV32_UART1.CTRL = tmp_a;
|
NEORV32_UART1.CTRL = tmp_a;
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_4) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_4) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 5 (UART1.TX)
|
// Fast interrupt channel 5 (UART1.TX)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_uart1_available()) {
|
if (neorv32_uart1_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ5 (UART1.TX): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ5 (UART1.TX): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// backup current UART1 configuration
|
// backup current UART1 configuration
|
tmp_a = NEORV32_UART1.CTRL;
|
tmp_a = NEORV32_UART1.CTRL;
|
|
|
// make sure UART is enabled
|
// make sure UART is enabled
|
NEORV32_UART1.CTRL |= (1 << UART_CTRL_EN);
|
NEORV32_UART1.CTRL |= (1 << UART_CTRL_EN);
|
// make sure sim mode is disabled
|
// make sure sim mode is disabled
|
NEORV32_UART1.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
NEORV32_UART1.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
|
|
// trigger UART1 TX IRQ
|
// trigger UART1 TX IRQ
|
neorv32_uart1_putc(0);
|
neorv32_uart1_putc(0);
|
// UART1 RX interrupt enable
|
// UART1 RX interrupt enable
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ5E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ5E);
|
// wait for UART1 to finish transmitting
|
// wait for UART1 to finish transmitting
|
while(neorv32_uart1_tx_busy());
|
while(neorv32_uart1_tx_busy());
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ5E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ5E);
|
|
|
// restore original configuration
|
// restore original configuration
|
NEORV32_UART1.CTRL = tmp_a;
|
NEORV32_UART1.CTRL = tmp_a;
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_5) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_5) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 6 (SPI)
|
// Fast interrupt channel 6 (SPI)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_spi_available()) {
|
if (neorv32_spi_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ6 (SPI): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ6 (SPI): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// configure SPI
|
// configure SPI
|
neorv32_spi_setup(CLK_PRSC_2, 0, 0, 0);
|
neorv32_spi_setup(CLK_PRSC_2, 0, 0, 0);
|
|
|
// trigger SPI IRQ
|
// trigger SPI IRQ
|
neorv32_spi_trans(0);
|
neorv32_spi_trans(0);
|
// enable fast interrupt
|
// enable fast interrupt
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ6E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ6E);
|
while(neorv32_spi_busy()); // wait for current transfer to finish
|
while(neorv32_spi_busy()); // wait for current transfer to finish
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ6E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ6E);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_6) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_6) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// disable SPI
|
// disable SPI
|
neorv32_spi_disable();
|
neorv32_spi_disable();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 7 (TWI)
|
// Fast interrupt channel 7 (TWI)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_twi_available()) {
|
if (neorv32_twi_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ7 (TWI): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ7 (TWI): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// configure TWI, fastest clock, no peripheral clock stretching
|
// configure TWI, fastest clock, no peripheral clock stretching
|
neorv32_twi_setup(CLK_PRSC_2, 0);
|
neorv32_twi_setup(CLK_PRSC_2, 0);
|
|
|
// trigger TWI IRQ
|
// trigger TWI IRQ
|
neorv32_twi_generate_start();
|
neorv32_twi_generate_start();
|
neorv32_twi_trans(0);
|
neorv32_twi_trans(0);
|
neorv32_twi_generate_stop();
|
neorv32_twi_generate_stop();
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ7E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ7E);
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ7E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ7E);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_7) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_7) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// disable TWI
|
// disable TWI
|
neorv32_twi_disable();
|
neorv32_twi_disable();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 8 (XIRQ)
|
// Fast interrupt channel 8 (XIRQ)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ8 (XIRQ): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ8 (XIRQ): ", cnt_test);
|
if (neorv32_xirq_available()) {
|
if (neorv32_xirq_available()) {
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
int xirq_err_cnt = 0;
|
int xirq_err_cnt = 0;
|
xirq_trap_handler_ack = 0;
|
xirq_trap_handler_ack = 0;
|
|
|
xirq_err_cnt += neorv32_xirq_setup(); // initialize XIRQ
|
xirq_err_cnt += neorv32_xirq_setup(); // initialize XIRQ
|
xirq_err_cnt += neorv32_xirq_install(0, xirq_trap_handler0); // install XIRQ IRQ handler channel 0
|
xirq_err_cnt += neorv32_xirq_install(0, xirq_trap_handler0); // install XIRQ IRQ handler channel 0
|
xirq_err_cnt += neorv32_xirq_install(1, xirq_trap_handler1); // install XIRQ IRQ handler channel 1
|
xirq_err_cnt += neorv32_xirq_install(1, xirq_trap_handler1); // install XIRQ IRQ handler channel 1
|
|
|
// enable XIRQ FIRQ
|
// enable XIRQ FIRQ
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ8E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ8E);
|
|
|
// trigger XIRQ channel 1 and 0
|
// trigger XIRQ channel 1 and 0
|
neorv32_gpio_port_set(3);
|
neorv32_gpio_port_set(3);
|
|
|
// wait for IRQs to arrive CPU
|
// wait for IRQs to arrive CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
asm volatile("nop");
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ8E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ8E);
|
|
|
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_8) && // FIRQ8 IRQ
|
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_8) && // FIRQ8 IRQ
|
(xirq_err_cnt == 0) && // no errors during XIRQ configuration
|
(xirq_err_cnt == 0) && // no errors during XIRQ configuration
|
(xirq_trap_handler_ack == 4)) { // XIRQ channel handler 0 executed before handler 1
|
(xirq_trap_handler_ack == 4)) { // XIRQ channel handler 0 executed before handler 1
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
NEORV32_XIRQ.IER = 0;
|
NEORV32_XIRQ.IER = 0;
|
NEORV32_XIRQ.IPR = -1;
|
NEORV32_XIRQ.IPR = -1;
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 9 (NEOLED)
|
// Fast interrupt channel 9 (NEOLED)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
PRINT_STANDARD("[%i] FIRQ9 (NEOLED): skipped\n", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ9 (NEOLED): skipped\n", cnt_test);
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Fast interrupt channel 10 & 11 (SLINK)
|
// Fast interrupt channel 10 & 11 (SLINK)
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
if (neorv32_slink_available()) {
|
if (neorv32_slink_available()) {
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] FIRQ10 & 11 (SLINK): ", cnt_test);
|
PRINT_STANDARD("[%i] FIRQ10 & 11 (SLINK): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// enable SLINK
|
// enable SLINK
|
neorv32_slink_enable();
|
neorv32_slink_enable();
|
|
|
// configure SLINK IRQs
|
// configure SLINK IRQs
|
neorv32_slink_tx_irq_config(0, SLINK_IRQ_ENABLE, SLINK_IRQ_TX_NOT_FULL);
|
neorv32_slink_tx_irq_config(0, SLINK_IRQ_ENABLE, SLINK_IRQ_TX_NOT_FULL);
|
neorv32_slink_rx_irq_config(0, SLINK_IRQ_ENABLE, SLINK_IRQ_RX_NOT_EMPTY);
|
neorv32_slink_rx_irq_config(0, SLINK_IRQ_ENABLE, SLINK_IRQ_RX_NOT_EMPTY);
|
|
|
// enable SLINK FIRQs
|
// enable SLINK FIRQs
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ10E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ10E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ11E);
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ11E);
|
|
|
tmp_a = 0; // error counter
|
tmp_a = 0; // error counter
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
|
|
// check if TX FIFO fires IRQ
|
// check if TX FIFO fires IRQ
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != TRAP_CODE_FIRQ_11) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != TRAP_CODE_FIRQ_11) {
|
tmp_a += 1;
|
tmp_a += 1;
|
}
|
}
|
neorv32_slink_tx_irq_config(0, SLINK_IRQ_DISABLE, SLINK_IRQ_TX_NOT_FULL);
|
neorv32_slink_tx_irq_config(0, SLINK_IRQ_DISABLE, SLINK_IRQ_TX_NOT_FULL);
|
|
|
// send single data word via link 0
|
// send single data word via link 0
|
if (neorv32_slink_tx0_nonblocking(0xA1B2C3D4)) {
|
if (neorv32_slink_tx0_nonblocking(0xA1B2C3D4)) {
|
tmp_a += 2; // sending failed
|
tmp_a += 2; // sending failed
|
}
|
}
|
|
|
// wait some time for the IRQ to arrive the CPU
|
// wait some time for the IRQ to arrive the CPU
|
asm volatile("nop");
|
asm volatile("nop");
|
|
|
// check if RX FIFO fires IRQ
|
// check if RX FIFO fires IRQ
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != TRAP_CODE_FIRQ_10) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != TRAP_CODE_FIRQ_10) {
|
tmp_a += 4;
|
tmp_a += 4;
|
}
|
}
|
neorv32_slink_rx_irq_config(0, SLINK_IRQ_DISABLE, SLINK_IRQ_RX_NOT_EMPTY);
|
neorv32_slink_rx_irq_config(0, SLINK_IRQ_DISABLE, SLINK_IRQ_RX_NOT_EMPTY);
|
|
|
// get single data word from link 0
|
// get single data word from link 0
|
uint32_t slink_rx_data;
|
uint32_t slink_rx_data;
|
if (neorv32_slink_rx0_nonblocking(&slink_rx_data)) {
|
if (neorv32_slink_rx0_nonblocking(&slink_rx_data)) {
|
tmp_a += 8; // receiving failed
|
tmp_a += 8; // receiving failed
|
}
|
}
|
|
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ10E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ10E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ11E);
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ11E);
|
|
|
if ((tmp_a == 0) && // local error counter = 0
|
if ((tmp_a == 0) && // local error counter = 0
|
(slink_rx_data == 0xA1B2C3D4)) { // correct data read-back
|
(slink_rx_data == 0xA1B2C3D4)) { // correct data read-back
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
// shutdown SLINK
|
// shutdown SLINK
|
neorv32_slink_disable();
|
neorv32_slink_disable();
|
}
|
}
|
|
|
|
|
|
// ----------------------------------------------------------
|
|
// Fast interrupt channel 12 (GPTMR)
|
|
// ----------------------------------------------------------
|
|
if (neorv32_slink_available()) {
|
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
|
PRINT_STANDARD("[%i] FIRQ12 (GPTMR): ", cnt_test);
|
|
|
|
cnt_test++;
|
|
|
|
// enable GPTMR FIRQ
|
|
neorv32_cpu_irq_enable(CSR_MIE_FIRQ12E);
|
|
|
|
// configure timer IRQ for one-shot mode after 2*4 clock cycles
|
|
neorv32_gptmr_setup(CLK_PRSC_2, 0, 4);
|
|
|
|
// wait some time for the IRQ to arrive the CPU
|
|
asm volatile("nop");
|
|
asm volatile("nop");
|
|
|
|
// disable GPTMR interrupt
|
|
neorv32_cpu_irq_disable(CSR_MIE_FIRQ12E);
|
|
|
|
// check if RX FIFO fires IRQ
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_12) {
|
|
test_ok();
|
|
}
|
|
else {
|
|
test_fail();
|
|
}
|
|
|
|
// disable GPTMR
|
|
neorv32_gptmr_disable();
|
|
}
|
|
|
|
|
//// ----------------------------------------------------------
|
//// ----------------------------------------------------------
|
//// Fast interrupt channel 12..15 (reserved)
|
//// Fast interrupt channel 13..15 (reserved)
|
//// ----------------------------------------------------------
|
//// ----------------------------------------------------------
|
//PRINT_STANDARD("[%i] FIRQ12..15: ", cnt_test);
|
//PRINT_STANDARD("[%i] FIRQ13..15: ", cnt_test);
|
//PRINT_STANDARD("skipped (n.a.)\n");
|
//PRINT_STANDARD("skipped (n.a.)\n");
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test WFI ("sleep") instructions, wakeup via MTIME
|
// Test WFI ("sleep") instructions, wakeup via MTIME
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] WFI (sleep instruction, wake-up via MTIME): ", cnt_test);
|
PRINT_STANDARD("[%i] WFI (sleep instruction, wake-up via MTIME): ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// program wake-up timer
|
// program wake-up timer
|
neorv32_mtime_set_timecmp(neorv32_mtime_get_time() + 500);
|
neorv32_mtime_set_timecmp(neorv32_mtime_get_time() + 500);
|
|
|
// enable mtime interrupt
|
// enable mtime interrupt
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_enable(CSR_MIE_MTIE);
|
|
|
// put CPU into sleep mode
|
// put CPU into sleep mode
|
asm volatile ("wfi");
|
asm volatile ("wfi");
|
|
|
// no more mtime interrupts
|
// no more mtime interrupts
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
neorv32_cpu_irq_disable(CSR_MIE_MTIE);
|
neorv32_mtime_set_timecmp(-1);
|
neorv32_mtime_set_timecmp(-1);
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != TRAP_CODE_MTI) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != TRAP_CODE_MTI) {
|
test_fail();
|
test_fail();
|
}
|
}
|
else {
|
else {
|
test_ok();
|
test_ok();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test invalid CSR access in user mode
|
// Test invalid CSR access in user mode
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Invalid CSR access (mstatus) from user mode: ", cnt_test);
|
PRINT_STANDARD("[%i] Invalid CSR access (mstatus) from user mode: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
neorv32_cpu_goto_user_mode();
|
neorv32_cpu_goto_user_mode();
|
{
|
{
|
// access to misa not allowed for user-level programs
|
// access to misa not allowed for user-level programs
|
tmp_a = neorv32_cpu_csr_read(CSR_MISA);
|
tmp_a = neorv32_cpu_csr_read(CSR_MISA);
|
}
|
}
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_I_ILLEGAL) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test RTE debug trap handler
|
// Test RTE debug trap handler
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] RTE debug trap handler: ", cnt_test);
|
PRINT_STANDARD("[%i] RTE debug trap handler: ", cnt_test);
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
// uninstall custom handler and use default RTE debug handler
|
// uninstall custom handler and use default RTE debug handler
|
neorv32_rte_exception_uninstall(RTE_TRAP_I_ILLEGAL);
|
neorv32_rte_exception_uninstall(RTE_TRAP_I_ILLEGAL);
|
|
|
// trigger illegal instruction exception
|
// trigger illegal instruction exception
|
neorv32_cpu_csr_read(0xfff); // CSR not available
|
neorv32_cpu_csr_read(0xfff); // CSR not available
|
|
|
PRINT_STANDARD(" ");
|
PRINT_STANDARD(" ");
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != 0) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) != 0) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
PRINT_STANDARD("answer: 0x%x", neorv32_cpu_csr_read(CSR_MCAUSE));
|
PRINT_STANDARD("answer: 0x%x", neorv32_cpu_csr_read(CSR_MCAUSE));
|
}
|
}
|
|
|
// restore original handler
|
// restore original handler
|
neorv32_rte_exception_install(RTE_TRAP_I_ILLEGAL, global_trap_handler);
|
neorv32_rte_exception_install(RTE_TRAP_I_ILLEGAL, global_trap_handler);
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test physical memory protection
|
// Test physical memory protection
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
PRINT_STANDARD("[%i] PMP - Physical memory protection: ", cnt_test);
|
PRINT_STANDARD("[%i] PMP - Physical memory protection: ", cnt_test);
|
|
|
// check if PMP is implemented
|
// check if PMP is implemented
|
if (neorv32_cpu_pmp_get_num_regions() != 0) {
|
if (neorv32_cpu_pmp_get_num_regions() != 0) {
|
|
|
// Create PMP protected region
|
// Create PMP protected region
|
// ---------------------------------------------
|
// ---------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
cnt_test++;
|
cnt_test++;
|
|
|
// find out minimal region size (granularity)
|
// find out minimal region size (granularity)
|
tmp_b = neorv32_cpu_pmp_get_granularity();
|
tmp_b = neorv32_cpu_pmp_get_granularity();
|
|
|
tmp_a = NEORV32_SYSINFO.DSPACE_BASE; // base address of protected region
|
tmp_a = NEORV32_SYSINFO.DSPACE_BASE; // base address of protected region
|
PRINT_STANDARD("Creating protected page (NAPOT, [!X,!W,!R], %u bytes) @ 0x%x: ", tmp_b, tmp_a);
|
PRINT_STANDARD("Creating protected page (NAPOT, [!X,!W,!R], %u bytes) @ 0x%x: ", tmp_b, tmp_a);
|
|
|
// configure
|
// configure
|
int pmp_return = neorv32_cpu_pmp_configure_region(0, tmp_a, tmp_b, 0b00011000); // NAPOT, NO read permission, NO write permission, and NO execute permissions
|
int pmp_return = neorv32_cpu_pmp_configure_region(0, tmp_a, tmp_b, 0b00011000); // NAPOT, NO read permission, NO write permission, and NO execute permissions
|
|
|
if ((pmp_return == 0) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
if ((pmp_return == 0) && (neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) {
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
if (neorv32_cpu_csr_read(CSR_PMPCFG0) & 0x80) {
|
if (neorv32_cpu_csr_read(CSR_PMPCFG0) & 0x80) {
|
PRINT_CRITICAL("%c[1m<Entry LOCKED!> %c[0m\n", 27, 27);
|
PRINT_CRITICAL("%c[1m<Entry LOCKED!> %c[0m\n", 27, 27);
|
}
|
}
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ------ EXECUTE: should fail ------
|
// ------ EXECUTE: should fail ------
|
PRINT_STANDARD("[%i] PMP: U-mode execute: ", cnt_test);
|
PRINT_STANDARD("[%i] PMP: U-mode execute: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
|
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
neorv32_cpu_goto_user_mode();
|
neorv32_cpu_goto_user_mode();
|
{
|
{
|
asm volatile ("jalr ra, %[input_i]" : : [input_i] "r" (tmp_a)); // call address to execute -> should fail
|
asm volatile ("jalr ra, %[input_i]" : : [input_i] "r" (tmp_a)); // call address to execute -> should fail
|
}
|
}
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) {
|
// switch back to machine mode (if not already)
|
// switch back to machine mode (if not already)
|
asm volatile ("ecall");
|
asm volatile ("ecall");
|
|
|
test_fail();
|
test_fail();
|
}
|
}
|
else {
|
else {
|
// switch back to machine mode (if not already)
|
// switch back to machine mode (if not already)
|
asm volatile ("ecall");
|
asm volatile ("ecall");
|
|
|
test_ok();
|
test_ok();
|
}
|
}
|
|
|
|
|
// ------ LOAD: should fail ------
|
// ------ LOAD: should fail ------
|
PRINT_STANDARD("[%i] PMP: U-mode read: ", cnt_test);
|
PRINT_STANDARD("[%i] PMP: U-mode read: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
|
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
neorv32_cpu_goto_user_mode();
|
neorv32_cpu_goto_user_mode();
|
tmp_b = 0;
|
tmp_b = 0;
|
{
|
{
|
tmp_b = neorv32_cpu_load_unsigned_word(tmp_a); // load access -> should fail
|
tmp_b = neorv32_cpu_load_unsigned_word(tmp_a); // load access -> should fail
|
}
|
}
|
|
|
if (tmp_b != 0) {
|
if (tmp_b != 0) {
|
PRINT_CRITICAL("%c[1m<SECURITY FAILURE> %c[0m\n", 27, 27);
|
PRINT_CRITICAL("%c[1m<SECURITY FAILURE> %c[0m\n", 27, 27);
|
}
|
}
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_L_ACCESS) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_L_ACCESS) {
|
// switch back to machine mode (if not already)
|
// switch back to machine mode (if not already)
|
asm volatile ("ecall");
|
asm volatile ("ecall");
|
|
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
// switch back to machine mode (if not already)
|
// switch back to machine mode (if not already)
|
asm volatile ("ecall");
|
asm volatile ("ecall");
|
|
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ------ STORE: should fail ------
|
// ------ STORE: should fail ------
|
PRINT_STANDARD("[%i] PMP: U-mode write: ", cnt_test);
|
PRINT_STANDARD("[%i] PMP: U-mode write: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
|
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
// switch to user mode (hart will be back in MACHINE mode when trap handler returns)
|
neorv32_cpu_goto_user_mode();
|
neorv32_cpu_goto_user_mode();
|
{
|
{
|
neorv32_cpu_store_unsigned_word(tmp_a, 0); // store access -> should fail
|
neorv32_cpu_store_unsigned_word(tmp_a, 0); // store access -> should fail
|
}
|
}
|
|
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_S_ACCESS) {
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_S_ACCESS) {
|
// switch back to machine mode (if not already)
|
// switch back to machine mode (if not already)
|
asm volatile ("ecall");
|
asm volatile ("ecall");
|
|
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
// switch back to machine mode (if not already)
|
// switch back to machine mode (if not already)
|
asm volatile ("ecall");
|
asm volatile ("ecall");
|
|
|
test_fail();
|
test_fail();
|
}
|
}
|
|
|
|
|
// ------ Lock test - pmpcfg0.0 / pmpaddr0 ------
|
// ------ Lock test - pmpcfg0.0 / pmpaddr0 ------
|
PRINT_STANDARD("[%i] PMP: Entry [mode=off] lock: ", cnt_test);
|
PRINT_STANDARD("[%i] PMP: Entry [mode=off] lock: ", cnt_test);
|
cnt_test++;
|
cnt_test++;
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
|
|
neorv32_cpu_csr_write(CSR_PMPCFG0, 0b10000001); // locked, but entry is deactivated (mode = off)
|
neorv32_cpu_csr_write(CSR_PMPCFG0, 0b10000001); // locked, but entry is deactivated (mode = off)
|
|
|
// make sure a locked cfg cannot be written
|
// make sure a locked cfg cannot be written
|
tmp_a = neorv32_cpu_csr_read(CSR_PMPCFG0);
|
tmp_a = neorv32_cpu_csr_read(CSR_PMPCFG0);
|
neorv32_cpu_csr_write(CSR_PMPCFG0, 0b00011001); // try to re-write CFG content
|
neorv32_cpu_csr_write(CSR_PMPCFG0, 0b00011001); // try to re-write CFG content
|
|
|
tmp_b = neorv32_cpu_csr_read(CSR_PMPADDR0);
|
tmp_b = neorv32_cpu_csr_read(CSR_PMPADDR0);
|
neorv32_cpu_csr_write(CSR_PMPADDR0, 0xABABCDCD); // try to re-write ADDR content
|
neorv32_cpu_csr_write(CSR_PMPADDR0, 0xABABCDCD); // try to re-write ADDR content
|
|
|
if ((tmp_a != neorv32_cpu_csr_read(CSR_PMPCFG0)) || (tmp_b != neorv32_cpu_csr_read(CSR_PMPADDR0)) || (neorv32_cpu_csr_read(CSR_MCAUSE) != 0)) {
|
if ((tmp_a != neorv32_cpu_csr_read(CSR_PMPCFG0)) || (tmp_b != neorv32_cpu_csr_read(CSR_PMPADDR0)) || (neorv32_cpu_csr_read(CSR_MCAUSE) != 0)) {
|
test_fail();
|
test_fail();
|
}
|
}
|
else {
|
else {
|
test_ok();
|
test_ok();
|
}
|
}
|
|
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
}
|
}
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test atomic LR/SC operation - should succeed
|
// Test atomic LR/SC operation - should succeed
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Atomic access (LR+SC succeeding access): ", cnt_test);
|
PRINT_STANDARD("[%i] Atomic access (LR+SC succeeding access): ", cnt_test);
|
|
|
#ifdef __riscv_atomic
|
#ifdef __riscv_atomic
|
// skip if A-mode is not implemented
|
// skip if A-mode is not implemented
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_A)) != 0) {
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_A)) != 0) {
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0x11223344);
|
neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0x11223344);
|
|
|
tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&atomic_access_addr); // make reservation
|
tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&atomic_access_addr); // make reservation
|
asm volatile ("nop");
|
asm volatile ("nop");
|
tmp_b = neorv32_cpu_store_conditional((uint32_t)&atomic_access_addr, 0x22446688);
|
tmp_b = neorv32_cpu_store_conditional((uint32_t)&atomic_access_addr, 0x22446688);
|
|
|
// atomic access
|
// atomic access
|
if ((tmp_b == 0) && // status: success
|
if ((tmp_b == 0) && // status: success
|
(tmp_a == 0x11223344) && // correct data read
|
(tmp_a == 0x11223344) && // correct data read
|
(neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr) == 0x22446688) && // correct data write
|
(neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr) == 0x22446688) && // correct data write
|
(neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) { // no exception triggered
|
(neorv32_cpu_csr_read(CSR_MCAUSE) == 0)) { // no exception triggered
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
}
|
}
|
#else
|
#else
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
#endif
|
#endif
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test atomic LR/SC operation - should fail
|
// Test atomic LR/SC operation - should fail
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Atomic access (LR+SC failing access 1): ", cnt_test);
|
PRINT_STANDARD("[%i] Atomic access (LR+SC failing access 1): ", cnt_test);
|
|
|
#ifdef __riscv_atomic
|
#ifdef __riscv_atomic
|
// skip if A-mode is not implemented
|
// skip if A-mode is not implemented
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_A)) != 0) {
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_A)) != 0) {
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0xAABBCCDD);
|
neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0xAABBCCDD);
|
|
|
// atomic access
|
// atomic access
|
tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&atomic_access_addr); // make reservation
|
tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&atomic_access_addr); // make reservation
|
// neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0xDEADDEAD); // destroy reservation
|
// neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0xDEADDEAD); // destroy reservation
|
neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr); // destroy reservation
|
neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr); // destroy reservation
|
tmp_b = neorv32_cpu_store_conditional((uint32_t)&atomic_access_addr, 0x22446688);
|
tmp_b = neorv32_cpu_store_conditional((uint32_t)&atomic_access_addr, 0x22446688);
|
|
|
if ((tmp_b != 0) && // status: fail
|
if ((tmp_b != 0) && // status: fail
|
(tmp_a == 0xAABBCCDD) && // correct data read
|
(tmp_a == 0xAABBCCDD) && // correct data read
|
(neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr) == 0xAABBCCDD)) { // correct data write
|
(neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr) == 0xAABBCCDD)) { // correct data write
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
}
|
}
|
#else
|
#else
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
#endif
|
#endif
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Test atomic LR/SC operation - should fail
|
// Test atomic LR/SC operation - should fail
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0);
|
PRINT_STANDARD("[%i] Atomic access (LR+SC failing access 2): ", cnt_test);
|
PRINT_STANDARD("[%i] Atomic access (LR+SC failing access 2): ", cnt_test);
|
|
|
#ifdef __riscv_atomic
|
#ifdef __riscv_atomic
|
// skip if A-mode is not implemented
|
// skip if A-mode is not implemented
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_A)) != 0) {
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_A)) != 0) {
|
|
|
cnt_test++;
|
cnt_test++;
|
|
|
neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0x12341234);
|
neorv32_cpu_store_unsigned_word((uint32_t)&atomic_access_addr, 0x12341234);
|
|
|
// atomic access
|
// atomic access
|
tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&atomic_access_addr); // make reservation
|
tmp_a = neorv32_cpu_load_reservate_word((uint32_t)&atomic_access_addr); // make reservation
|
asm volatile ("ecall"); // destroy reservation via trap (simulate a context switch)
|
asm volatile ("ecall"); // destroy reservation via trap (simulate a context switch)
|
tmp_b = neorv32_cpu_store_conditional((uint32_t)&atomic_access_addr, 0xDEADBEEF);
|
tmp_b = neorv32_cpu_store_conditional((uint32_t)&atomic_access_addr, 0xDEADBEEF);
|
|
|
if ((tmp_b != 0) && // status: fail
|
if ((tmp_b != 0) && // status: fail
|
(tmp_a == 0x12341234) && // correct data read
|
(tmp_a == 0x12341234) && // correct data read
|
(neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr) == 0x12341234)) { // correct data write
|
(neorv32_cpu_load_unsigned_word((uint32_t)&atomic_access_addr) == 0x12341234)) { // correct data write
|
test_ok();
|
test_ok();
|
}
|
}
|
else {
|
else {
|
test_fail();
|
test_fail();
|
}
|
}
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("skipped (on real HW)\n");
|
PRINT_STANDARD("skipped (on real HW)\n");
|
}
|
}
|
#else
|
#else
|
PRINT_STANDARD("skipped (n.a.)\n");
|
PRINT_STANDARD("skipped (n.a.)\n");
|
#endif
|
#endif
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// HPM reports
|
// HPM reports
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, -1); // stop all counters
|
neorv32_cpu_csr_write(CSR_MCOUNTINHIBIT, -1); // stop all counters
|
PRINT_STANDARD("\n\n-- HPM reports LOW (%u HPMs available) --\n", num_hpm_cnts_global);
|
PRINT_STANDARD("\n\n-- HPM reports LOW (%u HPMs available) --\n", num_hpm_cnts_global);
|
PRINT_STANDARD("#IR - Instr.: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_INSTRET)); // = "HPM_0"
|
PRINT_STANDARD("#IR - Instr.: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_INSTRET)); // = "HPM_0"
|
//PRINT_STANDARD("#TM - Time: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_TIME)); // = "HPM_1"
|
//PRINT_STANDARD("#TM - Time: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_TIME)); // = "HPM_1"
|
PRINT_STANDARD("#CY - CLKs: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_CYCLE)); // = "HPM_2"
|
PRINT_STANDARD("#CY - CLKs: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_CYCLE)); // = "HPM_2"
|
PRINT_STANDARD("#03 - Compr.: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER3));
|
PRINT_STANDARD("#03 - Compr.: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER3));
|
PRINT_STANDARD("#04 - IF wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER4));
|
PRINT_STANDARD("#04 - IF wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER4));
|
PRINT_STANDARD("#05 - II wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER5));
|
PRINT_STANDARD("#05 - II wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER5));
|
PRINT_STANDARD("#06 - ALU wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER6));
|
PRINT_STANDARD("#06 - ALU wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER6));
|
PRINT_STANDARD("#07 - Loads: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER7));
|
PRINT_STANDARD("#07 - Loads: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER7));
|
PRINT_STANDARD("#08 - Stores: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER8));
|
PRINT_STANDARD("#08 - Stores: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER8));
|
PRINT_STANDARD("#09 - MEM wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER9));
|
PRINT_STANDARD("#09 - MEM wait: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER9));
|
PRINT_STANDARD("#10 - Jumps: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER10));
|
PRINT_STANDARD("#10 - Jumps: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER10));
|
PRINT_STANDARD("#11 - Branches: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER11));
|
PRINT_STANDARD("#11 - Branches: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER11));
|
PRINT_STANDARD("#12 - Taken: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER12));
|
PRINT_STANDARD("#12 - Taken: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER12));
|
PRINT_STANDARD("#13 - Traps: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER13));
|
PRINT_STANDARD("#13 - Traps: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER13));
|
PRINT_STANDARD("#14 - Illegals: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER14));
|
PRINT_STANDARD("#14 - Illegals: %u\n", (uint32_t)neorv32_cpu_csr_read(CSR_MHPMCOUNTER14));
|
|
|
|
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
// Final test reports
|
// Final test reports
|
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
PRINT_CRITICAL("\n\nTest results:\nPASS: %i/%i\nFAIL: %i/%i\n\n", cnt_ok, cnt_test, cnt_fail, cnt_test);
|
PRINT_CRITICAL("\n\nTest results:\nPASS: %i/%i\nFAIL: %i/%i\n\n", cnt_ok, cnt_test, cnt_fail, cnt_test);
|
|
|
// final result
|
// final result
|
if (cnt_fail == 0) {
|
if (cnt_fail == 0) {
|
PRINT_STANDARD("%c[1m[CPU TEST COMPLETED SUCCESSFULLY!]%c[0m\n", 27, 27);
|
PRINT_STANDARD("%c[1m[CPU TEST COMPLETED SUCCESSFULLY!]%c[0m\n", 27, 27);
|
}
|
}
|
else {
|
else {
|
PRINT_STANDARD("%c[1m[CPU TEST FAILED!]%c[0m\n", 27, 27);
|
PRINT_STANDARD("%c[1m[CPU TEST FAILED!]%c[0m\n", 27, 27);
|
}
|
}
|
|
|
return (int)cnt_fail; // return error counter for after-main handler
|
return (int)cnt_fail; // return error counter for after-main handler
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* Simulation-based function to trigger CPU interrupts (MSI, MEI, FIRQ4..7).
|
* Simulation-based function to trigger CPU interrupts (MSI, MEI, FIRQ4..7).
|
*
|
*
|
* @param[in] sel IRQ select mask (bit positions according to #NEORV32_CSR_MIE_enum).
|
* @param[in] sel IRQ select mask (bit positions according to #NEORV32_CSR_MIE_enum).
|
**************************************************************************/
|
**************************************************************************/
|
void sim_irq_trigger(uint32_t sel) {
|
void sim_irq_trigger(uint32_t sel) {
|
|
|
*(IO_REG32 (0xFF000000)) = sel;
|
*(IO_REG32 (0xFF000000)) = sel;
|
asm volatile("nop"); // interrupt should kick in here (latest)
|
asm volatile("nop"); // interrupt should kick in here (latest)
|
*(IO_REG32 (0xFF000000)) = 0;
|
*(IO_REG32 (0xFF000000)) = 0;
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* Trap handler for ALL exceptions/interrupts.
|
* Trap handler for ALL exceptions/interrupts.
|
**************************************************************************/
|
**************************************************************************/
|
void global_trap_handler(void) {
|
void global_trap_handler(void) {
|
|
|
// hack: always come back in MACHINE MODE
|
// hack: always come back in MACHINE MODE
|
register uint32_t mask = (1<<CSR_MSTATUS_MPP_H) | (1<<CSR_MSTATUS_MPP_L);
|
register uint32_t mask = (1<<CSR_MSTATUS_MPP_H) | (1<<CSR_MSTATUS_MPP_L);
|
asm volatile ("csrrs zero, mstatus, %[input_j]" : : [input_j] "r" (mask));
|
asm volatile ("csrrs zero, mstatus, %[input_j]" : : [input_j] "r" (mask));
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* XIRQ handler channel 0.
|
* XIRQ handler channel 0.
|
**************************************************************************/
|
**************************************************************************/
|
void xirq_trap_handler0(void) {
|
void xirq_trap_handler0(void) {
|
|
|
xirq_trap_handler_ack += 2;
|
xirq_trap_handler_ack += 2;
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* XIRQ handler channel 1.
|
* XIRQ handler channel 1.
|
**************************************************************************/
|
**************************************************************************/
|
void xirq_trap_handler1(void) {
|
void xirq_trap_handler1(void) {
|
|
|
xirq_trap_handler_ack *= 2;
|
xirq_trap_handler_ack *= 2;
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* Test results helper function: Shows "[ok]" and increments global cnt_ok
|
* Test results helper function: Shows "[ok]" and increments global cnt_ok
|
**************************************************************************/
|
**************************************************************************/
|
void test_ok(void) {
|
void test_ok(void) {
|
|
|
PRINT_STANDARD("%c[1m[ok]%c[0m\n", 27, 27);
|
PRINT_STANDARD("%c[1m[ok]%c[0m\n", 27, 27);
|
cnt_ok++;
|
cnt_ok++;
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* Test results helper function: Shows "[FAIL]" and increments global cnt_fail
|
* Test results helper function: Shows "[FAIL]" and increments global cnt_fail
|
**************************************************************************/
|
**************************************************************************/
|
void test_fail(void) {
|
void test_fail(void) {
|
|
|
PRINT_CRITICAL("%c[1m[FAIL]%c[0m\n", 27, 27);
|
PRINT_CRITICAL("%c[1m[FAIL]%c[0m\n", 27, 27);
|
cnt_fail++;
|
cnt_fail++;
|
}
|
}
|
|
|
|
|
/**********************************************************************//**
|
/**********************************************************************//**
|
* "after-main" handler that is executed after the application's
|
* "after-main" handler that is executed after the application's
|
* main function returns (called by crt0.S start-up code): Output minimal
|
* main function returns (called by crt0.S start-up code): Output minimal
|
* test report to physical UART
|
* test report to physical UART
|
**************************************************************************/
|
**************************************************************************/
|
int __neorv32_crt0_after_main(int32_t return_code) {
|
int __neorv32_crt0_after_main(int32_t return_code) {
|
|
|
// make sure sim mode is disabled and UARTs are actually enabled
|
// make sure sim mode is disabled and UARTs are actually enabled
|
NEORV32_UART0.CTRL |= (1 << UART_CTRL_EN);
|
NEORV32_UART0.CTRL |= (1 << UART_CTRL_EN);
|
NEORV32_UART0.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
NEORV32_UART0.CTRL &= ~(1 << UART_CTRL_SIM_MODE);
|
NEORV32_UART1.CTRL = NEORV32_UART0.CTRL;
|
NEORV32_UART1.CTRL = NEORV32_UART0.CTRL;
|
|
|
// minimal result report
|
// minimal result report
|
PRINT_CRITICAL("%u/%u\n", (uint32_t)return_code, (uint32_t)cnt_test);
|
PRINT_CRITICAL("%u/%u\n", (uint32_t)return_code, (uint32_t)cnt_test);
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|