|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | #ifndef OPENTITAN_SW_DEVICE_LIB_RUNTIME_IBEX_H_ | 
|  | #define OPENTITAN_SW_DEVICE_LIB_RUNTIME_IBEX_H_ | 
|  |  | 
|  | #include <stdbool.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include "sw/device/lib/arch/device.h" | 
|  | #include "sw/device/lib/base/math.h" | 
|  | #include "sw/device/lib/base/stdasm.h" | 
|  |  | 
|  | // IBEX_SPIN_FOR needs a dependency on check.h, but the build fails if a | 
|  | // dependency on sw_lib_testing_check is added. | 
|  |  | 
|  | /** | 
|  | * @file | 
|  | * @brief This header provides Ibex-specific functions and enums, such as | 
|  | * cycle-accurate busy loops. | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * An Ibex exception type. | 
|  | * | 
|  | * This enum is used to decode RISC-V exception causes generated by Ibex. | 
|  | */ | 
|  | typedef enum ibex_exc { | 
|  | kIbexExcInstrMisaligned = 0, | 
|  | kIbexExcInstrAccessFault = 1, | 
|  | kIbexExcIllegalInstrFault = 2, | 
|  | kIbexExcBreakpoint = 3, | 
|  | kIbexExcLoadAccessFault = 5, | 
|  | kIbexExcStoreAccessFault = 7, | 
|  | kIbexExcUserECall = 8, | 
|  | kIbexExcSupervisorECall = 9, | 
|  | kIbexExcMachineECall = 11, | 
|  | kIbexExcMax = 31 | 
|  | } ibex_exc_t; | 
|  |  | 
|  | /** | 
|  | * An Ibex internal IRQ type. | 
|  | * | 
|  | * This enum is used to decode RISC-V internal IRQs generated by Ibex. | 
|  | */ | 
|  | typedef enum ibex_internal_irq { | 
|  | kIbexInternalIrqLoadInteg = 0xffffffe0, | 
|  | kIbexInternalIrqNmi = 0x8000001f | 
|  | } ibex_internal_irq_t; | 
|  |  | 
|  | /** | 
|  | * A spinwait timeout type. | 
|  | */ | 
|  | typedef struct ibex_timeout { | 
|  | /** | 
|  | * The number of cycles to timeout. | 
|  | */ | 
|  | uint64_t cycles; | 
|  | /** | 
|  | * The initial cycle count. | 
|  | */ | 
|  | uint64_t start; | 
|  | } ibex_timeout_t; | 
|  |  | 
|  | /** | 
|  | * Read the cycle counter. | 
|  | * | 
|  | * The value of the counter is stored across two 32-bit registers: `mcycle` and | 
|  | * `mcycleh`. This function is guaranteed to return a valid 64-bit cycle | 
|  | * counter value, even if `mcycle` overflows before reading `mcycleh`. | 
|  | * | 
|  | * Adapted from: The RISC-V Instruction Set Manual, Volume I: Unprivileged ISA | 
|  | * V20191213, pp. 61. | 
|  | */ | 
|  | inline uint64_t ibex_mcycle_read(void) { | 
|  | uint32_t cycle_low = 0; | 
|  | uint32_t cycle_high = 0; | 
|  | uint32_t cycle_high_2 = 0; | 
|  | asm volatile( | 
|  | "1:" | 
|  | "  csrr %0, mcycleh;"  // Read `mcycleh`. | 
|  | "  csrr %1, mcycle;"   // Read `mcycle`. | 
|  | "  csrr %2, mcycleh;"  // Read `mcycleh` again. | 
|  | "  bne  %0, %2, 1b;"   // Try again if `mcycle` overflowed before | 
|  | // reading `mcycleh`. | 
|  | : "=r"(cycle_high), "=r"(cycle_low), "=r"(cycle_high_2) | 
|  | :); | 
|  | return (uint64_t)cycle_high << 32 | cycle_low; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reads the mcause register. | 
|  | * | 
|  | * When an exception is encountered, the corresponding exception code is stored | 
|  | * in mcause register. | 
|  | * | 
|  | * A list of the exception codes can be found at: | 
|  | * https://ibex-core.readthedocs.io/en/latest/03_reference/ | 
|  | * exception_interrupts.html#exceptions | 
|  | */ | 
|  | uint32_t ibex_mcause_read(void); | 
|  |  | 
|  | /** | 
|  | * Reads the mtval register. | 
|  | * | 
|  | * When an exception is encountered, the Machine Trap Value (mtval) register | 
|  | * can holds exception-specific information to assist software in handling the | 
|  | * trap. | 
|  | * | 
|  | * From the Ibex documentation (found at | 
|  | * https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html) | 
|  | * - In the case of errors in the load-store unit mtval holds the address of | 
|  | * the transaction causing the error. | 
|  | * | 
|  | * - If a transaction is misaligned, mtval holds the address of the missing | 
|  | *   transaction part. | 
|  | * | 
|  | * - In the case of illegal instruction exceptions, mtval holds the actual | 
|  | * faulting instruction. | 
|  | * | 
|  | * - For all other exceptions, mtval is 0. | 
|  | */ | 
|  | uint32_t ibex_mtval_read(void); | 
|  |  | 
|  | /** | 
|  | * Reads the mepc register. | 
|  | * | 
|  | * When an exception is encountered, the current program counter is saved in | 
|  | * mepc, and the core jumps to the exception address. When an MRET instruction | 
|  | * is executed, the value from mepc replaces the current program counter. | 
|  | * | 
|  | * From the Ibex documentation (found at | 
|  | * https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html) | 
|  | * | 
|  | * Please note that in case of a fault, mepc must be modified to hold the | 
|  | * address of the next instruction, which can be at the 2byte (16bit) or 4byte | 
|  | * (32bit) offset, dependent on the fault cause instruction type (standard or | 
|  | * compressed). | 
|  | * | 
|  | * @return The mepc register value. | 
|  | */ | 
|  | uint32_t ibex_mepc_read(void); | 
|  |  | 
|  | /** | 
|  | * Writes the mepc register. | 
|  | * | 
|  | * When an exception is encountered, the current program counter is saved in | 
|  | * mepc, and the core jumps to the exception address. When an MRET instruction | 
|  | * is executed, the value from mepc replaces the current program counter. | 
|  | * | 
|  | * From the Ibex documentation (found at | 
|  | * https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html) | 
|  | * | 
|  | * Please note that in case of a fault, mepc must be modified to hold the | 
|  | * address of the next instruction, which can be at the 2byte (16bit) or 4byte | 
|  | * (32bit) offset, dependent on the fault cause instruction type (standard or | 
|  | * compressed). | 
|  | * | 
|  | * @param The new value to be written to the mepc register. | 
|  | */ | 
|  | void ibex_mepc_write(uint32_t mepc); | 
|  |  | 
|  | /** | 
|  | * Initializes the ibex timeout based on current mcycle count. | 
|  | * | 
|  | * @param timeout_usec Timeout in microseconds. | 
|  | * @return The initialized timeout value. | 
|  | */ | 
|  | inline ibex_timeout_t ibex_timeout_init(uint32_t timeout_usec) { | 
|  | return (ibex_timeout_t){ | 
|  | .cycles = udiv64_slow(kClockFreqCpuHz * timeout_usec, 1000000, NULL), | 
|  | .start = ibex_mcycle_read(), | 
|  | }; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Check whether the timeout has expired. | 
|  | * | 
|  | * @param timeout Holds the counter start value. | 
|  | * @return True if the timeout has expired and false otherwise. | 
|  | */ | 
|  | inline bool ibex_timeout_check(const ibex_timeout_t *timeout) { | 
|  | return ibex_mcycle_read() - timeout->start > timeout->cycles; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the time elapsed in microseconds since `ibex_timeout_init` was | 
|  | * called. | 
|  | * | 
|  | * @param timeout Holds the counter start value.. | 
|  | * @return Time elapsed in microseconds. | 
|  | */ | 
|  | inline uint64_t ibex_timeout_elapsed(const ibex_timeout_t *timeout) { | 
|  | return udiv64_slow((ibex_mcycle_read() - timeout->start) * 1000000, | 
|  | kClockFreqCpuHz, NULL); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Convenience macro to spin with timeout in microseconds. | 
|  | * | 
|  | * @param expr An expression that is evaluated multiple times until true. | 
|  | * @param timeout_usec Timeout in microseconds. | 
|  | */ | 
|  | #define IBEX_SPIN_FOR(expr, timeout_usec)                                 \ | 
|  | do {                                                                    \ | 
|  | const ibex_timeout_t timeout_ = ibex_timeout_init(timeout_usec);      \ | 
|  | while (!(expr)) {                                                     \ | 
|  | CHECK(!ibex_timeout_check(&timeout_),                               \ | 
|  | "Timed out after %d usec (%d CPU cycles) waiting for " #expr, \ | 
|  | timeout_usec, (uint32_t)timeout_.cycles);                     \ | 
|  | }                                                                     \ | 
|  | } while (0) | 
|  |  | 
|  | #endif  // OPENTITAN_SW_DEVICE_LIB_RUNTIME_IBEX_H_ |