blob: c34f876d9d8c9d02e708036332018257730520ab [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include <stdbool.h>
#include <stdint.h>
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/dif/dif_sram_ctrl.h"
#include "sw/device/lib/handler.h"
#include "sw/device/lib/runtime/ibex.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/check.h"
#include "sw/device/lib/testing/sram_ctrl_testutils.h"
#include "sw/device/lib/testing/test_framework/test_main.h"
#include "sw/device/lib/testing/test_framework/test_status.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
const test_config_t kTestConfig;
static dif_sram_ctrl_t sram_ctrl;
/**
* This flag is used to verify that the instruction access fault has occurred,
* and has been successfully serviced. Declared as volatile, because it is
* referenced in the fault handler, as well as the main test flow.
*/
static volatile bool instruction_access_fault;
static const uint32_t kRetRamStartAddr =
TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR;
static const uint32_t kRetRamEndAddr =
TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_BASE_ADDR +
TOP_EARLGREY_SRAM_CTRL_RET_AON_RAM_SIZE_BYTES - 1;
/**
* Handles an exception.
*
* This exception handler only processes the faults that are relevant. It falls
* into an infinite `wait_for_interrupt` routine for the rest.
*
* The controlled fault originates in the retention SRAM, which means that
* normally the return address would be calculated relative to the offending
* instruction. However, due to execution from retention SRAM being permanently
* disabled, this approach would not work.
*
* Instead the control needs to be returned to the caller. In other words
* sram_execution_test -> retention_sram -> exception_handler
* -> sram_execution_test.
*
* Before the jump into the exception handler, the register set is saved on
* stack, which means that the return address can be loaded from there.
*/
void handler_exception(void) {
uintptr_t ret_addr = (uintptr_t)OT_RETURN_ADDR();
LOG_INFO("Fault address: mepc = 0x%x, return address = 0x%x",
ibex_mepc_read(), ret_addr);
exc_id_t exception_id = ibex_mcause_read();
switch (exception_id) {
case kInstAccFault:
LOG_INFO("Instruction access fault handler");
instruction_access_fault = true;
ibex_mepc_write(ret_addr);
break;
case kInstIllegalFault:
LOG_INFO("Instruction illegal fault handler");
instruction_access_fault = false;
ibex_mepc_write(ret_addr);
break;
default:
LOG_FATAL("Unexpected exception id = 0x%x", exception_id);
while (true) {
wait_for_interrupt();
}
}
}
/**
* Used to execute instructions in Retention SRAM.
*/
typedef void (*sram_ctrl_instruction_fault_function_t)(void);
/**
* Performs SRAM execution test.
*
* `sram_ctrl_instruction_fault_function_t` is "mapped" onto the
* block of memory in Retention SRAM starting at `kRetRamStartAddr` and the
* size of `SRAM_CTRL_DATA_NUM_BYTES` that holds `unimp` instructions.
*
* Please see the following document for more context:
* https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md
*/
static void sram_execution_test(void) {
memset((void *)kRetRamStartAddr, 0, SRAM_CTRL_DATA_NUM_BYTES);
// Map the function pointer onto the instruction buffer.
sram_ctrl_instruction_fault_function_t instruction_access_fault_func =
(sram_ctrl_instruction_fault_function_t)kRetRamStartAddr;
uintptr_t func_address = (uintptr_t)instruction_access_fault_func;
CHECK(func_address >= kRetRamStartAddr && func_address <= kRetRamEndAddr,
"Test code resides outside of the Ret SRAM: function address = %x",
func_address);
instruction_access_fault = false;
instruction_access_fault_func();
CHECK(instruction_access_fault, "Expected instruction access fault");
}
bool test_main(void) {
CHECK_DIF_OK(dif_sram_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_SRAM_CTRL_RET_AON_REGS_BASE_ADDR),
&sram_ctrl));
sram_execution_test();
return true;
}