/* * Copyright (c) 2019 Nuclei Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CORE_FEATURE_ECLIC__ #define __CORE_FEATURE_ECLIC__ /*! * @file core_feature_eclic.h * @brief ECLIC feature API header file for Nuclei N/NX Core */ /* * ECLIC Feature Configuration Macro: * 1. __ECLIC_PRESENT: Define whether Enhanced Core Local Interrupt Controller (ECLIC) Unit is present or not * * 0: Not present * * 1: Present * 2. __ECLIC_BASEADDR: Base address of the ECLIC unit. * 3. __ECLIC_INTCTLBITS: Optional, if defined, it should set to the value of ECLIC_GetInfoCtlbits(), define the number of hardware bits are actually implemented in the clicintctl registers. * Valid number is 1 - 8. * 4. __ECLIC_INTNUM: Define the external interrupt number of ECLIC Unit * 5. __TEE_PRESENT: Define whether TEE feature present, if present, ECLIC will present with S-Mode ECLIC feature * * 0: Not present * * 1: Present * */ #ifdef __cplusplus extern "C" { #endif #include "core_feature_base.h" #if defined(__ECLIC_PRESENT) && (__ECLIC_PRESENT == 1) /** * \defgroup NMSIS_Core_ECLIC_Registers Register Define and Type Definitions Of ECLIC * \ingroup NMSIS_Core_Registers * \brief Type definitions and defines for eclic registers. * * @{ */ /** * \brief Union type to access CLICFG configure register. */ typedef union { struct { __IM uint8_t _reserved0:1; __IOM uint8_t nlbits:4; /*!< bit: 1..4 specified the bit-width of level and priority in the register clicintctl[i] */ __IM uint8_t nmbits:2; /*!< bit: 5..6 ties to 1 if supervisor-level interrupt supported, or else it's reserved */ __IM uint8_t _reserved1:1; } b; /*!< Structure used for bit access */ uint8_t w; /*!< Type used for byte access */ } CLICCFG_Type; /** * \brief Union type to access CLICINFO information register. */ typedef union { struct { __IM uint32_t numint:13; /*!< bit: 0..12 number of maximum interrupt inputs supported */ __IM uint32_t version:8; /*!< bit: 13..20 Hardware implementation version number. 1: version 1. 2: version 2, support hardware context saving and restoring. */ __IM uint32_t intctlbits:4; /*!< bit: 21..24 specifies how many hardware bits are actually implemented in the clicintctl registers */ __IM uint32_t shd_num:4; /*!< bit: 25..28 number of shadow register groups for single mode(M/S mode) */ __IM uint32_t _reserved0:3; /*!< bit: 29..31 Reserved */ } b; /*!< Structure used for bit access */ __IM uint32_t w; /*!< Type used for word access */ } CLICINFO_Type; /** * \brief Access to the machine mode register structure of INTIP, INTIE, INTATTR, INTCTL. */ typedef struct { __IOM uint8_t INTIP; /*!< Offset: 0x000 (R/W) Interrupt set pending register */ __IOM uint8_t INTIE; /*!< Offset: 0x001 (R/W) Interrupt set enable register */ __IOM uint8_t INTATTR; /*!< Offset: 0x002 (R/W) Interrupt set attributes register */ __IOM uint8_t INTCTRL; /*!< Offset: 0x003 (R/W) Interrupt configure register */ } CLIC_CTRL_Type; /** * \brief Access to the structure of ECLIC Memory Map, which is compatible with TEE. */ typedef struct { __IOM uint8_t CFG; /*!< Offset: 0x000 (R/W) CLIC configuration register */ __IM uint8_t RESERVED0[3]; __IM uint32_t INFO; /*!< Offset: 0x004 (R/ ) CLIC information register */ __IM uint8_t RESERVED1; #if defined(__TEE_PRESENT) && (__TEE_PRESENT == 1) __IOM uint8_t STH; /*!< Offset: 0x009 (R/W ) CLIC supervisor mode interrupt-level threshold */ #else __IM uint8_t RESERVED2; #endif __IM uint8_t RESERVED3; __IOM uint8_t MTH; /*!< Offset: 0x00B(R/W) CLIC machine mode interrupt-level threshold */ uint32_t RESERVED4[1021]; #if defined(__TEE_PRESENT) && (__TEE_PRESENT == 1) CLIC_CTRL_Type CTRL[1024]; /*!< Offset: 0x1000 (R/W) CLIC machine mode register structure for INTIP, INTIE, INTATTR, INTCTL */ __IM uint32_t RESERVED5[2]; __IM uint8_t RESERVED6; __IOM uint8_t SSTH; /*!< Offset: 0x2009 (R) CLIC supervisor mode threshold register, which is a mirror to mintthresh.sth */ __IM uint8_t RESERVED7; __IM uint8_t RESERVED8; __IM uint32_t RESERVED9[1021]; CLIC_CTRL_Type SCTRL[1024]; /*!< Offset: 0x3000 (R/W) CLIC supervisor mode register structure for INTIP, INTIE, INTATTR, INTCTL */ #else CLIC_CTRL_Type CTRL[4096]; /*!< Offset: 0x1000 (R/W) CLIC machine mode register structure for INTIP, INTIE, INTATTR, INTCTL */ #endif } CLIC_Type; #define CLIC_CLICCFG_NLBIT_Pos 1U /*!< CLIC CLICCFG: NLBIT Position */ #define CLIC_CLICCFG_NLBIT_Msk (0xFUL << CLIC_CLICCFG_NLBIT_Pos) /*!< CLIC CLICCFG: NLBIT Mask */ #define CLIC_CLICINFO_CTLBIT_Pos 21U /*!< CLIC INTINFO: CLICINTCTLBITS Position */ #define CLIC_CLICINFO_CTLBIT_Msk (0xFUL << CLIC_CLICINFO_CTLBIT_Pos) /*!< CLIC INTINFO: CLICINTCTLBITS Mask */ #define CLIC_CLICINFO_VER_Pos 13U /*!< CLIC CLICINFO: VERSION Position */ #define CLIC_CLICINFO_VER_Msk (0xFFUL << CLIC_CLICINFO_VER_Pos) /*!< CLIC CLICINFO: VERSION Mask */ #define CLIC_CLICINFO_NUM_Pos 0U /*!< CLIC CLICINFO: NUM_INTERRUPT Position */ #define CLIC_CLICINFO_NUM_Msk (0x1FFFUL << CLIC_CLICINFO_NUM_Pos) /*!< CLIC CLICINFO: NUM_INTERRUPT Mask */ #define CLIC_CLICINFO_SHD_NUM_Pos 25U /*!< CLIC CLICINFO: SHD_NUM Position */ #define CLIC_CLICINFO_SHD_NUM_Msk (0xFUL << CLIC_CLICINFO_SHD_NUM_Pos) /*!< CLIC CLICINFO: SHD_NUM Mask */ #define CLIC_INTIP_IP_Pos 0U /*!< CLIC INTIP: IP Position */ #define CLIC_INTIP_IP_Msk (0x1UL << CLIC_INTIP_IP_Pos) /*!< CLIC INTIP: IP Mask */ #define CLIC_INTIE_IE_Pos 0U /*!< CLIC INTIE: IE Position */ #define CLIC_INTIE_IE_Msk (0x1UL << CLIC_INTIE_IE_Pos) /*!< CLIC INTIE: IE Mask */ #if defined(__TEE_PRESENT) && (__TEE_PRESENT == 1) #define CLIC_INTATTR_MODE_Pos 6U /*!< CLIC INTATTA: Mode Position */ #define CLIC_INTATTR_MODE_Msk (0x3U << CLIC_INTATTR_MODE_Pos) /*!< CLIC INTATTA: Mode Mask */ #endif #define CLIC_INTATTR_TRIG_Pos 1U /*!< CLIC INTATTR: TRIG Position */ #define CLIC_INTATTR_TRIG_Msk (0x3UL << CLIC_INTATTR_TRIG_Pos) /*!< CLIC INTATTR: TRIG Mask */ #define CLIC_INTATTR_SHV_Pos 0U /*!< CLIC INTATTR: SHV Position */ #define CLIC_INTATTR_SHV_Msk (0x1UL << CLIC_INTATTR_SHV_Pos) /*!< CLIC INTATTR: SHV Mask */ #define ECLIC_MAX_NLBITS 8U /*!< Max nlbit of the CLICINTCTLBITS */ #define ECLIC_MODE_MTVEC_Msk 3U /*!< ECLIC Mode mask for MTVT CSR Register */ #define ECLIC_NON_VECTOR_INTERRUPT 0x0 /*!< Non-Vector Interrupt Mode of ECLIC */ #define ECLIC_VECTOR_INTERRUPT 0x1 /*!< Vector Interrupt Mode of ECLIC */ /**\brief ECLIC Trigger Enum for different Trigger Type */ typedef enum ECLIC_TRIGGER { ECLIC_LEVEL_TRIGGER = 0x0, /*!< Level Triggerred, trig[0] = 0 */ ECLIC_POSTIVE_EDGE_TRIGGER = 0x1, /*!< Postive/Rising Edge Triggered, trig[0] = 1, trig[1] = 0 */ ECLIC_NEGTIVE_EDGE_TRIGGER = 0x3, /*!< Negtive/Falling Edge Triggered, trig[0] = 1, trig[1] = 1 */ ECLIC_MAX_TRIGGER = 0x3 /*!< MAX Supported Trigger Mode */ } ECLIC_TRIGGER_Type; #ifndef __ECLIC_BASEADDR /* Base address of ECLIC(__ECLIC_BASEADDR) should be defined in */ #error "__ECLIC_BASEADDR is not defined, please check!" #endif #ifndef __ECLIC_INTCTLBITS /* Define __ECLIC_INTCTLBITS to get via ECLIC->INFO if not defined */ #define __ECLIC_INTCTLBITS (__ECLIC_GetInfoCtlbits()) #endif /* ECLIC Memory mapping of Device */ #define ECLIC_BASE __ECLIC_BASEADDR /*!< ECLIC Base Address */ #define ECLIC ((CLIC_Type *) ECLIC_BASE) /*!< CLIC configuration struct */ /** @} */ /* end of group NMSIS_Core_ECLIC_Registers */ /* ########################## ECLIC functions #################################### */ /** * \defgroup NMSIS_Core_IntExc Interrupts and Exceptions * \brief Functions that manage interrupts and exceptions via the ECLIC. * * @{ */ /** * \brief Definition of IRQn numbers * \details * The core interrupt enumeration names for IRQn values are defined in the file .h. * - Interrupt ID(IRQn) from 0 to 18 are reserved for core internal interrupts. * - Interrupt ID(IRQn) start from 19 represent device-specific external interrupts. * - The first device-specific interrupt has the IRQn value 19. * * The table below describes the core interrupt names and their availability in various Nuclei Cores. */ /* The following enum IRQn definition in this file * is only used for doxygen documentation generation, * The .h is the real file to define it by vendor */ #if defined(__ONLY_FOR_DOXYGEN_DOCUMENT_GENERATION__) typedef enum IRQn { /* ========= Nuclei N/NX Core Specific Interrupt Numbers =========== */ /* Core Internal Interrupt IRQn definitions */ Reserved0_IRQn = 0, /*!< Internal reserved */ Reserved1_IRQn = 1, /*!< Internal reserved */ Reserved2_IRQn = 2, /*!< Internal reserved */ SysTimerSW_IRQn = 3, /*!< System Timer SW interrupt */ Reserved3_IRQn = 4, /*!< Internal reserved */ Reserved4_IRQn = 5, /*!< Internal reserved */ Reserved5_IRQn = 6, /*!< Internal reserved */ SysTimer_IRQn = 7, /*!< System Timer Interrupt */ Reserved6_IRQn = 8, /*!< Internal reserved */ Reserved7_IRQn = 9, /*!< Internal reserved */ Reserved8_IRQn = 10, /*!< Internal reserved */ Reserved9_IRQn = 11, /*!< Internal reserved */ Reserved10_IRQn = 12, /*!< Internal reserved */ Reserved11_IRQn = 13, /*!< Internal reserved */ Reserved12_IRQn = 14, /*!< Internal reserved */ Reserved13_IRQn = 15, /*!< Internal reserved */ Reserved14_IRQn = 16, /*!< Internal reserved */ Reserved15_IRQn = 17, /*!< Internal reserved */ Reserved16_IRQn = 18, /*!< Internal reserved */ /* ========= Device Specific Interrupt Numbers =================== */ /* ToDo: add here your device specific external interrupt numbers. * 19~max(NUM_INTERRUPT, 1023) is reserved number for user. * Maxmum interrupt supported could get from clicinfo.NUM_INTERRUPT. * According the interrupt handlers defined in startup_Device.S * eg.: Interrupt for Timer#1 eclic_tim1_handler -> TIM1_IRQn */ FirstDeviceSpecificInterrupt_IRQn = 19, /*!< First Device Specific Interrupt */ SOC_INT_MAX, /*!< Number of total interrupts */ } IRQn_Type; #endif /* __ONLY_FOR_DOXYGEN_DOCUMENT_GENERATION__ */ #ifdef NMSIS_ECLIC_VIRTUAL #ifndef NMSIS_ECLIC_VIRTUAL_HEADER_FILE #define NMSIS_ECLIC_VIRTUAL_HEADER_FILE "nmsis_eclic_virtual.h" #endif #include NMSIS_ECLIC_VIRTUAL_HEADER_FILE #else #define ECLIC_SetCfgNlbits __ECLIC_SetCfgNlbits #define ECLIC_GetCfgNlbits __ECLIC_GetCfgNlbits #define ECLIC_GetInfoVer __ECLIC_GetInfoVer #define ECLIC_GetInfoCtlbits __ECLIC_GetInfoCtlbits #define ECLIC_GetInfoNum __ECLIC_GetInfoNum #define ECLIC_GetInfoShadowNum __ECLIC_GetInfoShadowNum #define ECLIC_SetMth __ECLIC_SetMth #define ECLIC_GetMth __ECLIC_GetMth #define ECLIC_EnableIRQ __ECLIC_EnableIRQ #define ECLIC_GetEnableIRQ __ECLIC_GetEnableIRQ #define ECLIC_DisableIRQ __ECLIC_DisableIRQ #define ECLIC_SetPendingIRQ __ECLIC_SetPendingIRQ #define ECLIC_GetPendingIRQ __ECLIC_GetPendingIRQ #define ECLIC_ClearPendingIRQ __ECLIC_ClearPendingIRQ #define ECLIC_SetTrigIRQ __ECLIC_SetTrigIRQ #define ECLIC_GetTrigIRQ __ECLIC_GetTrigIRQ #define ECLIC_SetShvIRQ __ECLIC_SetShvIRQ #define ECLIC_GetShvIRQ __ECLIC_GetShvIRQ #define ECLIC_SetCtrlIRQ __ECLIC_SetCtrlIRQ #define ECLIC_GetCtrlIRQ __ECLIC_GetCtrlIRQ #define ECLIC_SetLevelIRQ __ECLIC_SetLevelIRQ #define ECLIC_GetLevelIRQ __ECLIC_GetLevelIRQ #define ECLIC_SetPriorityIRQ __ECLIC_SetPriorityIRQ #define ECLIC_GetPriorityIRQ __ECLIC_GetPriorityIRQ #if __ECLIC_VER == 2 #define ECLIC_EnableShadow __ECLIC_EnableShadow #define ECLIC_DisableShadow __ECLIC_DisableShadow #define ECLIC_SetShadowLevel __ECLIC_SetShadowLevel #define ECLIC_GetShadowLevel __ECLIC_GetShadowLevel #define ECLIC_SetShadowLevelReg __ECLIC_SetShadowLevelReg #define ECLIC_GetShadowLevelReg __ECLIC_GetShadowLevelReg #endif /* For TEE */ #if defined(__TEE_PRESENT) && (__TEE_PRESENT == 1) #define ECLIC_SetModeIRQ __ECLIC_SetModeIRQ #define ECLIC_SetSth __ECLIC_SetSth #define ECLIC_GetSth __ECLIC_GetSth #define ECLIC_SetPendingIRQ_S __ECLIC_SetPendingIRQ_S #define ECLIC_GetPendingIRQ_S __ECLIC_GetPendingIRQ_S #define ECLIC_ClearPendingIRQ_S __ECLIC_ClearPendingIRQ_S #define ECLIC_SetTrigIRQ_S __ECLIC_SetTrigIRQ_S #define ECLIC_GetTrigIRQ_S __ECLIC_GetTrigIRQ_S #define ECLIC_SetShvIRQ_S __ECLIC_SetShvIRQ_S #define ECLIC_GetShvIRQ_S __ECLIC_GetShvIRQ_S #define ECLIC_SetCtrlIRQ_S __ECLIC_SetCtrlIRQ_S #define ECLIC_GetCtrlIRQ_S __ECLIC_GetCtrlIRQ_S #define ECLIC_SetLevelIRQ_S __ECLIC_SetLevelIRQ_S #define ECLIC_GetLevelIRQ_S __ECLIC_GetLevelIRQ_S #define ECLIC_SetPriorityIRQ_S __ECLIC_SetPriorityIRQ_S #define ECLIC_GetPriorityIRQ_S __ECLIC_GetPriorityIRQ_S #define ECLIC_EnableIRQ_S __ECLIC_EnableIRQ_S #define ECLIC_GetEnableIRQ_S __ECLIC_GetEnableIRQ_S #define ECLIC_DisableIRQ_S __ECLIC_DisableIRQ_S #if __ECLIC_VER == 2 #define ECLIC_EnableShadow_S __ECLIC_EnableShadow_S #define ECLIC_DisableShadow_S __ECLIC_DisableShadow_S #define ECLIC_SetShadowLevel_S __ECLIC_SetShadowLevel_S #define ECLIC_GetShadowLevel_S __ECLIC_GetShadowLevel_S #define ECLIC_SetShadowLevelReg_S __ECLIC_SetShadowLevelReg_S #define ECLIC_GetShadowLevelReg_S __ECLIC_GetShadowLevelReg_S #endif #endif #endif /* NMSIS_ECLIC_VIRTUAL */ #ifdef NMSIS_VECTAB_VIRTUAL #ifndef NMSIS_VECTAB_VIRTUAL_HEADER_FILE #define NMSIS_VECTAB_VIRTUAL_HEADER_FILE "nmsis_vectab_virtual.h" #endif #include NMSIS_VECTAB_VIRTUAL_HEADER_FILE #else #define ECLIC_SetVector __ECLIC_SetVector #define ECLIC_GetVector __ECLIC_GetVector #if defined(__TEE_PRESENT) && (__TEE_PRESENT == 1) #define ECLIC_SetVector_S __ECLIC_SetVector_S #define ECLIC_GetVector_S __ECLIC_GetVector_S #endif #endif /* (NMSIS_VECTAB_VIRTUAL) */ /** * \brief Set nlbits value * \details * This function set the nlbits value of CLICCFG register. * \param [in] nlbits nlbits value * \remarks * - nlbits is used to set the width of level in the CLICINTCTL[i]. * \sa * - \ref ECLIC_GetCfgNlbits */ __STATIC_FORCEINLINE void __ECLIC_SetCfgNlbits(uint32_t nlbits) { uint8_t temp = ECLIC->CFG; ECLIC->CFG = (temp & ~CLIC_CLICCFG_NLBIT_Msk) | \ ((uint8_t)((nlbits << CLIC_CLICCFG_NLBIT_Pos) & CLIC_CLICCFG_NLBIT_Msk)); } /** * \brief Get nlbits value * \details * This function get the nlbits value of CLICCFG register. * \return nlbits value of CLICCFG register * \remarks * - nlbits is used to set the width of level in the CLICINTCTL[i]. * \sa * - \ref ECLIC_SetCfgNlbits */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetCfgNlbits(void) { return ((uint32_t)((ECLIC->CFG & CLIC_CLICCFG_NLBIT_Msk) >> CLIC_CLICCFG_NLBIT_Pos)); } /** * \brief Get the ECLIC version number * \details * This function gets the hardware version information from CLICINFO register. * \return hardware version number in CLICINFO register. * \remarks * - This function gets harware version information from CLICINFO register. * - Bit 20:17 for architecture version, bit 16:13 for implementation version. * \sa * - \ref ECLIC_GetInfoNum */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetInfoVer(void) { return ((uint32_t)((ECLIC->INFO & CLIC_CLICINFO_VER_Msk) >> CLIC_CLICINFO_VER_Pos)); } /** * \brief Get CLICINTCTLBITS * \details * This function gets CLICINTCTLBITS from CLICINFO register. * \return CLICINTCTLBITS from CLICINFO register. * \remarks * - In the CLICINTCTL[i] registers, with 2 <= CLICINTCTLBITS <= 8. * - The implemented bits are kept left-justified in the most-significant bits of each 8-bit * CLICINTCTL[I] register, with the lower unimplemented bits hardwired to 1. * \sa * - \ref ECLIC_GetInfoNum */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetInfoCtlbits(void) { return ((uint32_t)((ECLIC->INFO & CLIC_CLICINFO_CTLBIT_Msk) >> CLIC_CLICINFO_CTLBIT_Pos)); } /** * \brief Get number of maximum interrupt inputs supported * \details * This function gets number of maximum interrupt inputs supported from CLICINFO register. * \return number of maximum interrupt inputs supported from CLICINFO register. * \remarks * - This function gets number of maximum interrupt inputs supported from CLICINFO register. * - The num_interrupt field specifies the actual number of maximum interrupt inputs supported in this implementation. * \sa * - \ref ECLIC_GetInfoCtlbits */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetInfoNum(void) { return ((uint32_t)((ECLIC->INFO & CLIC_CLICINFO_NUM_Msk) >> CLIC_CLICINFO_NUM_Pos)); } /** * \brief Get number of shadow register groups * \details * This function gets the number of shadow register groups from the CLICINFO register. * This includes both the first-come-first-served dedicated interrupt shadow registers * and the shadow registers designed for different interrupt levels. * This number represents the total count for M mode; if S Mode is present, the number is the same as M Mode. * Note that shadow register 0 is fixed for first-come-first-served m/s-mode interrupt and cannot be configured. * \return number of shadow register groups from the CLICINFO register. * \remarks * - This function is only valid for ECLICv2 * - This function gets the number of shadow register groups from the CLICINFO register. * \sa * - \ref ECLIC_GetInfoNum */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetInfoShadowNum(void) { return ((uint32_t)((ECLIC->INFO & (CLIC_CLICINFO_SHD_NUM_Msk)) >> CLIC_CLICINFO_SHD_NUM_Pos)); } /** * \brief Set Machine Mode Interrupt Level Threshold * \details * This function sets machine mode interrupt level threshold. * This threshold is not effective immediately, if you want to safely * change it, you need to disable all interrupts first, and then change it, * and do fence and then enable all interrupts. * \param [in] mth Interrupt Level Threshold. * \sa * - \ref ECLIC_GetMth */ __STATIC_FORCEINLINE void __ECLIC_SetMth(uint8_t mth) { ECLIC->MTH = mth; } /** * \brief Get Machine Mode Interrupt Level Threshold * \details * This function gets machine mode interrupt level threshold. * \return Interrupt Level Threshold. * \sa * - \ref ECLIC_SetMth */ __STATIC_FORCEINLINE uint8_t __ECLIC_GetMth(void) { return (ECLIC->MTH); } /** * \brief Enable a specific interrupt * \details * This function enables the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_DisableIRQ */ __STATIC_FORCEINLINE void __ECLIC_EnableIRQ(IRQn_Type IRQn) { ECLIC->CTRL[IRQn].INTIE |= CLIC_INTIE_IE_Msk; } /** * \brief Get a specific interrupt enable status * \details * This function returns the interrupt enable status for the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \returns * - 0 Interrupt is not enabled * - 1 Interrupt is pending * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_EnableIRQ * - \ref ECLIC_DisableIRQ */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetEnableIRQ(IRQn_Type IRQn) { return ((uint32_t) (ECLIC->CTRL[IRQn].INTIE) & CLIC_INTIE_IE_Msk); } /** * \brief Disable a specific interrupt * \details * This function disables the specific interrupt \em IRQn. * \param [in] IRQn Number of the external interrupt to disable * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_EnableIRQ */ __STATIC_FORCEINLINE void __ECLIC_DisableIRQ(IRQn_Type IRQn) { ECLIC->CTRL[IRQn].INTIE &= ~CLIC_INTIE_IE_Msk; } /** * \brief Get the pending specific interrupt * \details * This function returns the pending status of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \returns * - 0 Interrupt is not pending * - 1 Interrupt is pending * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetPendingIRQ * - \ref ECLIC_ClearPendingIRQ */ __STATIC_FORCEINLINE int32_t __ECLIC_GetPendingIRQ(IRQn_Type IRQn) { return ((uint32_t)(ECLIC->CTRL[IRQn].INTIP) & CLIC_INTIP_IP_Msk); } /** * \brief Set a specific interrupt to pending * \details * This function sets the pending bit for the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_GetPendingIRQ * - \ref ECLIC_ClearPendingIRQ */ __STATIC_FORCEINLINE void __ECLIC_SetPendingIRQ(IRQn_Type IRQn) { ECLIC->CTRL[IRQn].INTIP |= CLIC_INTIP_IP_Msk; } /** * \brief Clear a specific interrupt from pending * \details * This function removes the pending state of the specific interrupt \em IRQn. * \em IRQn cannot be a negative number. * \param [in] IRQn Interrupt number * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetPendingIRQ * - \ref ECLIC_GetPendingIRQ */ __STATIC_FORCEINLINE void __ECLIC_ClearPendingIRQ(IRQn_Type IRQn) { ECLIC->CTRL[IRQn].INTIP &= ~CLIC_INTIP_IP_Msk; } /** * \brief Set trigger mode and polarity for a specific interrupt * \details * This function set trigger mode and polarity of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] trig * - 00 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 01 positive edge trigger, \ref ECLIC_POSTIVE_EDGE_TRIGGER * - 02 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 03 negative edge trigger, \ref ECLIC_NEGTIVE_EDGE_TRIGGER * \remarks * - IRQn must not be negative. * * \sa * - \ref ECLIC_GetTrigIRQ */ __STATIC_FORCEINLINE void __ECLIC_SetTrigIRQ(IRQn_Type IRQn, uint32_t trig) { uint8_t temp = ECLIC->CTRL[IRQn].INTATTR; ECLIC->CTRL[IRQn].INTATTR = (temp & ~CLIC_INTATTR_TRIG_Msk) | \ ((uint8_t)(trig << CLIC_INTATTR_TRIG_Pos)); } /** * \brief Get trigger mode and polarity for a specific interrupt * \details * This function get trigger mode and polarity of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return * - 00 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 01 positive edge trigger, \ref ECLIC_POSTIVE_EDGE_TRIGGER * - 02 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 03 negative edge trigger, \ref ECLIC_NEGTIVE_EDGE_TRIGGER * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetTrigIRQ */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetTrigIRQ(IRQn_Type IRQn) { return ((uint32_t)(((ECLIC->CTRL[IRQn].INTATTR) & CLIC_INTATTR_TRIG_Msk) >> CLIC_INTATTR_TRIG_Pos)); } /** * \brief Set interrupt working mode for a specific interrupt * \details * This function set selective hardware vector or non-vector working mode of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] shv * - 0 non-vector mode, \ref ECLIC_NON_VECTOR_INTERRUPT * - 1 vector mode, \ref ECLIC_VECTOR_INTERRUPT * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_GetShvIRQ */ __STATIC_FORCEINLINE void __ECLIC_SetShvIRQ(IRQn_Type IRQn, uint32_t shv) { uint8_t temp = ECLIC->CTRL[IRQn].INTATTR; ECLIC->CTRL[IRQn].INTATTR = (temp & ~CLIC_INTATTR_SHV_Msk) | \ ((uint8_t)(shv << CLIC_INTATTR_SHV_Pos)); } /** * \brief Get interrupt working mode for a specific interrupt * \details * This function get selective hardware vector or non-vector working mode of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return shv * - 0 non-vector mode, \ref ECLIC_NON_VECTOR_INTERRUPT * - 1 vector mode, \ref ECLIC_VECTOR_INTERRUPT * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetShvIRQ */ __STATIC_FORCEINLINE uint32_t __ECLIC_GetShvIRQ(IRQn_Type IRQn) { return ((uint32_t)(((ECLIC->CTRL[IRQn].INTATTR) & CLIC_INTATTR_SHV_Msk) >> CLIC_INTATTR_SHV_Pos)); } /** * \brief Modify ECLIC Interrupt Input Control Register for a specific interrupt * \details * This function modify ECLIC Interrupt Input Control(CLICINTCTL[i]) register of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] intctrl Set value for CLICINTCTL[i] register * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_GetCtrlIRQ */ __STATIC_FORCEINLINE void __ECLIC_SetCtrlIRQ(IRQn_Type IRQn, uint8_t intctrl) { ECLIC->CTRL[IRQn].INTCTRL = intctrl; } /** * \brief Get ECLIC Interrupt Input Control Register value for a specific interrupt * \details * This function modify ECLIC Interrupt Input Control register of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return value of ECLIC Interrupt Input Control register * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetCtrlIRQ */ __STATIC_FORCEINLINE uint8_t __ECLIC_GetCtrlIRQ(IRQn_Type IRQn) { return (ECLIC->CTRL[IRQn].INTCTRL); } /** * \brief Set ECLIC Interrupt level of a specific interrupt * \details * This function set interrupt level of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] lvl_abs Interrupt level * \remarks * - IRQn must not be negative. * - If lvl_abs to be set is larger than the max level allowed, it will be force to be max level. * - When you set level value you need use clciinfo.nlbits to get the width of level. * Then we could know the maximum of level. CLICINTCTLBITS is how many total bits are * present in the CLICINTCTL register. * \sa * - \ref ECLIC_GetLevelIRQ */ __STATIC_INLINE void __ECLIC_SetLevelIRQ(IRQn_Type IRQn, uint8_t lvl_abs) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits == 0) { return; } if (nlbits > intctlbits) { nlbits = intctlbits; } uint8_t maxlvl = ((1UL << nlbits) - 1); if (lvl_abs > maxlvl) { lvl_abs = maxlvl; } uint8_t lvl = lvl_abs << (ECLIC_MAX_NLBITS - nlbits); uint8_t cur_ctrl = __ECLIC_GetCtrlIRQ(IRQn); cur_ctrl = cur_ctrl << nlbits; cur_ctrl = cur_ctrl >> nlbits; __ECLIC_SetCtrlIRQ(IRQn, (cur_ctrl | lvl)); } /** * \brief Get ECLIC Interrupt level of a specific interrupt * \details * This function get interrupt level of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return Interrupt level * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetLevelIRQ */ __STATIC_INLINE uint8_t __ECLIC_GetLevelIRQ(IRQn_Type IRQn) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits == 0) { return 0; } if (nlbits > intctlbits) { nlbits = intctlbits; } uint8_t intctrl = __ECLIC_GetCtrlIRQ(IRQn); uint8_t lvl_abs = intctrl >> (ECLIC_MAX_NLBITS - nlbits); return lvl_abs; } /** * \brief Get ECLIC Interrupt priority of a specific interrupt * \details * This function get interrupt priority of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] pri Interrupt priority * \remarks * - IRQn must not be negative. * - If pri to be set is larger than the max priority allowed, it will be force to be max priority. * - Priority width is CLICINTCTLBITS minus clciinfo.nlbits if clciinfo.nlbits * is less than CLICINTCTLBITS. Otherwise priority width is 0. * \sa * - \ref ECLIC_GetPriorityIRQ */ __STATIC_INLINE void __ECLIC_SetPriorityIRQ(IRQn_Type IRQn, uint8_t pri) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits < intctlbits) { uint8_t maxpri = ((1UL << (intctlbits - nlbits)) - 1); if (pri > maxpri) { pri = maxpri; } pri = pri << (ECLIC_MAX_NLBITS - intctlbits); uint8_t mask = ((uint8_t)(-1)) >> intctlbits; pri = pri | mask; uint8_t cur_ctrl = __ECLIC_GetCtrlIRQ(IRQn); cur_ctrl = cur_ctrl >> (ECLIC_MAX_NLBITS - nlbits); cur_ctrl = cur_ctrl << (ECLIC_MAX_NLBITS - nlbits); __ECLIC_SetCtrlIRQ(IRQn, (cur_ctrl | pri)); } } /** * \brief Get ECLIC Interrupt priority of a specific interrupt * \details * This function get interrupt priority of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return Interrupt priority * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetPriorityIRQ */ __STATIC_INLINE uint8_t __ECLIC_GetPriorityIRQ(IRQn_Type IRQn) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits < intctlbits) { uint8_t cur_ctrl = __ECLIC_GetCtrlIRQ(IRQn); uint8_t pri = cur_ctrl << nlbits; pri = pri >> nlbits; pri = pri >> (ECLIC_MAX_NLBITS - intctlbits); return pri; } else { return 0; } } #if __ECLIC_VER == 2 /** * \brief Enable ECLIC Shadow Register Function (Machine Mode) * \details * This function enables the shadow register function for ECLIC in Machine Mode. * It sets the MECLIC_CTL_SHADOW_EN bit in the CSR_MECLIC_CTL CSR. * This function is only valid for ECLIC version 2 and above. * \remarks * - API only available for ECLIC v2 * \sa * - \ref __ECLIC_DisableShadow */ __STATIC_FORCEINLINE void __ECLIC_EnableShadow(void) { __RV_CSR_SET(CSR_MECLIC_CTL, MECLIC_CTL_SHADOW_EN); } /** * \brief Disable ECLIC Shadow Register Function (Machine Mode) * \details * This function disables the shadow register function for ECLIC in Machine Mode. * It clears the MECLIC_CTL_SHADOW_EN bit in the CSR_MECLIC_CTL CSR. * This function is only valid for ECLIC version 2 and above. * \remarks * - API only available for ECLIC v2 * \sa * - \ref __ECLIC_EnableShadow */ __STATIC_FORCEINLINE void __ECLIC_DisableShadow(void) { __RV_CSR_CLEAR(CSR_MECLIC_CTL, MECLIC_CTL_SHADOW_EN); } /** * \brief Set Shadow Register Interrupt Level for a specific m-mode shadow register * \details * This function sets the interrupt level for a specific m-mode shadow register \em idx + 1. * It configures CSR_MSHADGPRLVL0 and CSR_MSHADGPRLVL1 registers. * \param [in] idx Shadow register index (0-7), corresponding to shadow registers 1-8 * (Note: shadow register 0 is fixed for first-come-first-served m-mode * interrupt and cannot be configured) * \param [in] level Interrupt level to set for the shadow register * \remarks * - API only available for ECLIC v2 * - idx = 0 means set SHAD1_CFG, which configures the shadow register 1 * - For RV64, all 8 shadow registers are configured in CSR_MSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_MSHADGPRLVL0, * and shadow registers 5-8 are in CSR_MSHADGPRLVL1 * \sa * - \ref ECLIC_GetShadowLevel */ __STATIC_INLINE void __ECLIC_SetShadowLevel(unsigned long idx, uint8_t level) { /* Check if idx is valid (0-7) */ if (idx > 7) { return; } uint8_t nlbits = (uint8_t)__ECLIC_GetCfgNlbits(); /* Limit the level value to the available number of bits */ uint8_t max_level = (1U << nlbits) - 1; if (level > max_level) { level = max_level; } /* Position the level value in the upper nlbits of the 8-bit field and set the low (8-nlbits) bits to 1 */ uint8_t level_shifted = (uint8_t)((level << (8 - nlbits)) | ((1U << (8 - nlbits)) - 1)); #if __RISCV_XLEN == 64 /* For RV64, all 8 shadow registers are in CSR_MSHADGPRLVL0 */ /* Calculate the bit position for the 8-bit field of the specified index */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ /* Create mask to clear the 8-bit field for the specified index */ uint64_t mask = (uint64_t)0xFFUL << bit_pos; /* Read, modify, and write the CSR register */ uint64_t current_val = __RV_CSR_READ(CSR_MSHADGPRLVL0); current_val = (current_val & ~mask) | (((uint64_t)level_shifted) << bit_pos); __RV_CSR_WRITE(CSR_MSHADGPRLVL0, current_val); #else /* For RV32, calculate bit position and select appropriate register */ if (idx < 4) { /* Shadow registers 1-4 are in CSR_MSHADGPRLVL0 */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ uint32_t mask = 0xFFUL << bit_pos; uint32_t current_val = __RV_CSR_READ(CSR_MSHADGPRLVL0); current_val = (current_val & ~mask) | (((uint32_t)level_shifted) << bit_pos); __RV_CSR_WRITE(CSR_MSHADGPRLVL0, current_val); } else { /* Shadow registers 5-8 are in CSR_MSHADGPRLVL1 */ uint32_t bit_pos = (idx - 4) << 3; /* (idx - 4) * 8 using bit shift */ uint32_t mask = 0xFFUL << bit_pos; uint32_t current_val = __RV_CSR_READ(CSR_MSHADGPRLVL1); current_val = (current_val & ~mask) | (((uint32_t)level_shifted) << bit_pos); __RV_CSR_WRITE(CSR_MSHADGPRLVL1, current_val); } #endif } /** * \brief Get Shadow Register Interrupt Level for a specific m-mode shadow register * \details * This function gets the interrupt level for a specific m-mode shadow register \em idx + 1. * It reads CSR_MSHADGPRLVL0 and CSR_MSHADGPRLVL1 registers. * \param [in] idx Shadow register index (0-7), corresponding to shadow registers 1-8 * (Note: shadow register 0 is fixed for first-come-first-served m-mode * interrupt and cannot be configured) * \return Interrupt level of the shadow register * \remarks * - API only available for ECLIC v2 * - idx = 0 means set SHAD1_CFG, which configures the shadow register 1 * - For RV64, all 8 shadow registers are configured in CSR_MSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_MSHADGPRLVL0, * and shadow registers 5-8 are in CSR_MSHADGPRLVL1 * \sa * - \ref ECLIC_SetShadowLevel */ __STATIC_INLINE uint8_t __ECLIC_GetShadowLevel(unsigned long idx) { /* Check if idx is valid (0-7) */ if (idx > 7) { return 0; } uint8_t nlbits = (uint8_t)__ECLIC_GetCfgNlbits(); #if __RISCV_XLEN == 64 /* For RV64, all 8 shadow registers are in CSR_MSHADGPRLVL0 */ /* Calculate the bit position for the 8-bit field of the specified index */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ /* Read the CSR register and extract the 8-bit field */ uint64_t current_val = __RV_CSR_READ(CSR_MSHADGPRLVL0); uint8_t extracted_val = (uint8_t)((current_val >> bit_pos) & 0xFF); /* Extract the level from the upper nlbits of the 8-bit field */ uint8_t level = (extracted_val >> (8 - nlbits)); return level; #else /* For RV32, calculate bit position and select appropriate register */ if (idx < 4) { /* Shadow registers 1-4 are in CSR_MSHADGPRLVL0 */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ /* Read the CSR register and extract the 8-bit field */ uint32_t current_val = __RV_CSR_READ(CSR_MSHADGPRLVL0); uint8_t extracted_val = (uint8_t)((current_val >> bit_pos) & 0xFF); /* Extract the level from the upper nlbits of the 8-bit field */ uint8_t level = (extracted_val >> (8 - nlbits)); return level; } else { /* Shadow registers 5-8 are in CSR_MSHADGPRLVL1 */ uint32_t bit_pos = (idx - 4) << 3; /* (idx - 4) * 8 using bit shift */ /* Read the CSR register and extract the 8-bit field */ uint32_t current_val = __RV_CSR_READ(CSR_MSHADGPRLVL1); uint8_t extracted_val = (uint8_t)((current_val >> bit_pos) & 0xFF); /* Extract the level from the upper nlbits of the 8-bit field */ uint8_t level = (extracted_val >> (8 - nlbits)); return level; } #endif } /** * \brief Set Shadow Register Level Register for m-mode * \details * This function sets the entire shadow register level register for m-mode. * It writes directly to CSR_MSHADGPRLVL0 register (and CSR_MSHADGPRLVL1 for RV32). * \param [in] value 64-bit value to set for the shadow register level register * \remarks * - API only available for ECLIC v2 * - For RV64, all 8 shadow registers are configured in CSR_MSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_MSHADGPRLVL0, * and shadow registers 5-8 are in CSR_MSHADGPRLVL1 * - Note: shadow register 0 is fixed for first-come-first-served m-mode interrupt and cannot be configured * \sa * - \ref ECLIC_GetShadowLevelReg */ __STATIC_INLINE void __ECLIC_SetShadowLevelReg(uint64_t value) { #if __RISCV_XLEN == 64 __RV_CSR_WRITE(CSR_MSHADGPRLVL0, value); #else __RV_CSR_WRITE(CSR_MSHADGPRLVL0, (uint32_t)value); __RV_CSR_WRITE(CSR_MSHADGPRLVL1, (uint32_t)(value >> 32)); #endif } /** * \brief Get Shadow Register Level Register for m-mode * \details * This function gets the entire shadow register level register for m-mode. * It reads from CSR_MSHADGPRLVL0 register (and CSR_MSHADGPRLVL1 for RV32) and combines them. * \return 64-bit value of the shadow register level register * \remarks * - API only available for ECLIC v2 * - For RV64, all 8 shadow registers are configured in CSR_MSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_MSHADGPRLVL0, * and shadow registers 5-8 are in CSR_MSHADGPRLVL1 * - Note: shadow register 0 is fixed for first-come-first-served m-mode interrupt and cannot be configured * \sa * - \ref ECLIC_SetShadowLevelReg */ __STATIC_INLINE uint64_t __ECLIC_GetShadowLevelReg(void) { #if __RISCV_XLEN == 64 return __RV_CSR_READ(CSR_MSHADGPRLVL0); #else uint64_t value = __RV_CSR_READ(CSR_MSHADGPRLVL1); value <<= 32; value |= __RV_CSR_READ(CSR_MSHADGPRLVL0); return value; #endif } #endif /** * \brief Set Interrupt Vector of a specific interrupt * \details * This function set interrupt handler address of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] vector Interrupt handler address * \remarks * - IRQn must not be negative. * - You can set the \ref CSR_CSR_MTVT to set interrupt vector table entry address. * - If your vector table is placed in readonly section, the vector for IRQn will not be modified. * For this case, you need to use the correct irq handler name defined in your vector table as * your irq handler function name. * - This function will only work correctly when the vector table is placed in an read-write enabled section. * \sa * - \ref ECLIC_GetVector */ __STATIC_INLINE void __ECLIC_SetVector(IRQn_Type IRQn, rv_csr_t vector) { unsigned long vec_base; vec_base = ((unsigned long)__RV_CSR_READ(CSR_MTVT)); vec_base += ((unsigned long)IRQn) * sizeof(unsigned long); (* (unsigned long *) vec_base) = vector; #if (defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1)) #if (defined(__CCM_PRESENT) && (__CCM_PRESENT == 1)) MFlushDCacheLine((unsigned long)vec_base); #endif #endif #if (defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1)) #if (defined(__CCM_PRESENT) && (__CCM_PRESENT == 1)) MInvalICacheLine((unsigned long)vec_base); #else __FENCE_I(); #endif #endif } /** * \brief Get Interrupt Vector of a specific interrupt * \details * This function get interrupt handler address of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return Interrupt handler address * \remarks * - IRQn must not be negative. * - You can read \ref CSR_CSR_MTVT to get interrupt vector table entry address. * \sa * - \ref ECLIC_SetVector */ __STATIC_FORCEINLINE rv_csr_t __ECLIC_GetVector(IRQn_Type IRQn) { #if __RISCV_XLEN == 32 return (*(uint32_t *)(__RV_CSR_READ(CSR_MTVT) + IRQn * 4)); #elif __RISCV_XLEN == 64 return (*(uint64_t *)(__RV_CSR_READ(CSR_MTVT) + IRQn * 8)); #else // TODO Need cover for XLEN=128 case in future return (*(uint64_t *)(__RV_CSR_READ(CSR_MTVT) + IRQn * 8)); #endif } #if defined(__TEE_PRESENT) && (__TEE_PRESENT == 1) /** * \brief Set privilege mode of a specific interrupt * \details * This function set in which privilege mode the interrupts \em IRQn should be taken. * \param [in] IRQn Interrupt number * \param [in] mode Privilege mode * \remarks * - IRQn must not be negative. * - mode must be 1(Supervisor Mode) or 3(Machine Mode), other values are ignored. * - M-mode can R/W this field, but S-mode can only read.And ECLIC with TEE does not * reply on CSR mideleg to delegate interrupts. * - Mode of S-mode ECLIC region's clicintattr can be omitted to set, which is mirror to M-mode ECLIC region's. * Only the low 6 bits of clicintattr [i] can be written via the S-mode memory region. */ __STATIC_FORCEINLINE void __ECLIC_SetModeIRQ(IRQn_Type IRQn, uint32_t mode) { /* * only 1 or 3 can be assigned to mode in one step.the default value of mode is 3, * which can't be clear to 0 firstly, then OR it to 1 */ ECLIC->CTRL[IRQn].INTATTR = (uint8_t)(mode << CLIC_INTATTR_MODE_Pos) + \ (ECLIC->SCTRL[IRQn].INTATTR & (~CLIC_INTATTR_MODE_Msk)); } /** * \brief Set supervisor-mode Interrupt Level Threshold in supervisor mode * \details * This function sets supervisor-mode interrupt level threshold. * \param [in] sth Interrupt Level Threshold. * \remarks * - S-mode ECLIC region sintthresh'sth is a mirror to M-mode ECLIC region's mintthresh.sth, * and will be updated synchronously, here operate on mintthresh.sth. * - Similiar to \ref ECLIC_SetMth, also recommended to disable interrupts before * setting interrupt level threshold. * \sa * - \ref ECLIC_GetSth */ __STATIC_FORCEINLINE void __ECLIC_SetSth(uint8_t sth) { ECLIC->STH = sth; } /** * \brief Get supervisor-mode Interrupt Level Threshold in supervisor mode * \details * This function gets supervisor mode interrupt level threshold. * \return Interrupt Level Threshold. * \remarks * - S-mode ECLIC region sintthresh'sth is a mirror to M-mode ECLIC region's mintthresh.sth, * and will be updated synchronously, here operate on mintthresh.sth. * \sa * - \ref ECLIC_SetSth */ __STATIC_FORCEINLINE uint8_t __ECLIC_GetSth(void) { return (ECLIC->STH); } /** * \brief Set a specific interrupt to pending in supervisor mode * \details * This function sets the pending bit for the specific interrupt \em IRQn in supervisor mode. * \param [in] IRQn Interrupt number * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_GetPendingIRQ_S * - \ref ECLIC_ClearPendingIRQ_S */ __STATIC_FORCEINLINE void __ECLIC_SetPendingIRQ_S(IRQn_Type IRQn) { ECLIC->SCTRL[IRQn].INTIP |= CLIC_INTIP_IP_Msk; } /** * \brief Get the pending specific interrupt in supervisor mode * \details * This function returns the pending status of the specific interrupt \em IRQn in supervisor mode. * \param [in] IRQn Interrupt number * \returns * - 0 Interrupt is not pending * - 1 Interrupt is pending * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetPendingIRQ_S * - \ref ECLIC_ClearPendingIRQ_S */ __STATIC_FORCEINLINE int32_t __ECLIC_GetPendingIRQ_S(IRQn_Type IRQn) { return ((uint32_t)(ECLIC->SCTRL[IRQn].INTIP) & CLIC_INTIP_IP_Msk); } /** * \brief Clear a specific interrupt from pending in supervisor mode * \details * This function removes the pending state of the specific interrupt \em IRQn in supervisor mode. * \em IRQn cannot be a negative number. * \param [in] IRQn Interrupt number * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetPendingIRQ_S * - \ref ECLIC_GetPendingIRQ_S */ __STATIC_FORCEINLINE void __ECLIC_ClearPendingIRQ_S(IRQn_Type IRQn) { ECLIC->SCTRL[IRQn].INTIP &= ~CLIC_INTIP_IP_Msk; } /** * \brief Set trigger mode and polarity for a specific interrupt in supervisor mode * \details * This function set trigger mode and polarity of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] trig * - 00 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 01 positive edge trigger, \ref ECLIC_POSTIVE_EDGE_TRIGGER * - 02 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 03 negative edge trigger, \ref ECLIC_NEGTIVE_EDGE_TRIGGER * \remarks * - IRQn must not be negative. * * \sa * - \ref ECLIC_GetTrigIRQ_S */ __STATIC_FORCEINLINE void __ECLIC_SetTrigIRQ_S(IRQn_Type IRQn, uint32_t trig) { uint8_t temp = ECLIC->SCTRL[IRQn].INTATTR; ECLIC->SCTRL[IRQn].INTATTR = (temp & ~CLIC_INTATTR_TRIG_Msk) | \ ((uint8_t)(trig << CLIC_INTATTR_TRIG_Pos)); } /** * \brief Get trigger mode and polarity for a specific interrupt in supervisor mode * \details * This function get trigger mode and polarity of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return * - 00 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 01 positive edge trigger, \ref ECLIC_POSTIVE_EDGE_TRIGGER * - 02 level trigger, \ref ECLIC_LEVEL_TRIGGER * - 03 negative edge trigger, \ref ECLIC_NEGTIVE_EDGE_TRIGGER * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetTrigIRQ_S */ __STATIC_FORCEINLINE uint8_t __ECLIC_GetTrigIRQ_S(IRQn_Type IRQn) { return ((uint8_t)(((ECLIC->SCTRL[IRQn].INTATTR) & CLIC_INTATTR_TRIG_Msk) >> CLIC_INTATTR_TRIG_Pos)); } /** * \brief Set interrupt working mode for a specific interrupt in supervisor mode * \details * This function set selective hardware vector or non-vector working mode of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] shv * - 0 non-vector mode, \ref ECLIC_NON_VECTOR_INTERRUPT * - 1 vector mode, \ref ECLIC_VECTOR_INTERRUPT * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_GetShvIRQ_S */ __STATIC_FORCEINLINE void __ECLIC_SetShvIRQ_S(IRQn_Type IRQn, uint32_t shv) { uint8_t temp = ECLIC->SCTRL[IRQn].INTATTR; ECLIC->SCTRL[IRQn].INTATTR = (temp & ~CLIC_INTATTR_SHV_Msk) | \ ((uint8_t)(shv << CLIC_INTATTR_SHV_Pos)); } /** * \brief Get interrupt working mode for a specific interrupt in supervisor mode * \details * This function get selective hardware vector or non-vector working mode of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return shv * - 0 non-vector mode, \ref ECLIC_NON_VECTOR_INTERRUPT * - 1 vector mode, \ref ECLIC_VECTOR_INTERRUPT * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SMODE_SetShvIRQ */ __STATIC_FORCEINLINE uint8_t __ECLIC_GetShvIRQ_S(IRQn_Type IRQn) { return ((uint8_t)(((ECLIC->SCTRL[IRQn].INTATTR) & CLIC_INTATTR_SHV_Msk) >> CLIC_INTATTR_SHV_Pos)); } /** * \brief Modify ECLIC Interrupt Input Control Register for a specific interrupt in supervisor mode * \details * This function modify ECLIC Interrupt Input Control(CLICINTCTL[i]) register of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] intctrl Set value for CLICINTCTL[i] register * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_GetCtrlIRQ_S */ __STATIC_FORCEINLINE void __ECLIC_SetCtrlIRQ_S(IRQn_Type IRQn, uint8_t intctrl) { ECLIC->SCTRL[IRQn].INTCTRL = intctrl; } /** * \brief Get ECLIC Interrupt Input Control Register value for a specific interrupt in supervisor mode * \details * This function modify ECLIC Interrupt Input Control register of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return value of ECLIC Interrupt Input Control register * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetCtrlIRQ_S */ __STATIC_FORCEINLINE uint8_t __ECLIC_GetCtrlIRQ_S(IRQn_Type IRQn) { return (ECLIC->SCTRL[IRQn].INTCTRL); } /** * \brief Set ECLIC Interrupt level of a specific interrupt in supervisor mode * \details * This function set interrupt level of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] lvl_abs Interrupt level * \remarks * - IRQn must not be negative. * - If lvl_abs to be set is larger than the max level allowed, it will be force to be max level. * - When you set level value you need use clciinfo.nlbits to get the width of level. * Then we could know the maximum of level. CLICINTCTLBITS is how many total bits are * present in the CLICINTCTL register. * \sa * - \ref ECLIC_GetLevelIRQ_S */ __STATIC_INLINE void __ECLIC_SetLevelIRQ_S(IRQn_Type IRQn, uint8_t lvl_abs) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits == 0) { return; } if (nlbits > intctlbits) { nlbits = intctlbits; } uint8_t maxlvl = ((1UL << nlbits) - 1); if (lvl_abs > maxlvl) { lvl_abs = maxlvl; } uint8_t lvl = lvl_abs << (ECLIC_MAX_NLBITS - nlbits); uint8_t cur_ctrl = __ECLIC_GetCtrlIRQ_S(IRQn); cur_ctrl = cur_ctrl << nlbits; cur_ctrl = cur_ctrl >> nlbits; __ECLIC_SetCtrlIRQ_S(IRQn, (cur_ctrl | lvl)); } /** * \brief Get ECLIC Interrupt level of a specific interrupt * \details * This function get interrupt level of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return Interrupt level * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetLevelIRQ_S */ __STATIC_INLINE uint8_t __ECLIC_GetLevelIRQ_S(IRQn_Type IRQn) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits == 0) { return 0; } if (nlbits > intctlbits) { nlbits = intctlbits; } uint8_t intctrl = __ECLIC_GetCtrlIRQ_S(IRQn); uint8_t lvl_abs = intctrl >> (ECLIC_MAX_NLBITS - nlbits); return lvl_abs; } /** * \brief Set ECLIC Interrupt priority of a specific interrupt in supervisor mode * \details * This function get interrupt priority of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] pri Interrupt priority * \remarks * - IRQn must not be negative. * - If pri to be set is larger than the max priority allowed, it will be force to be max priority. * - Priority width is CLICINTCTLBITS minus clciinfo.nlbits if clciinfo.nlbits * is less than CLICINTCTLBITS. Otherwise priority width is 0. * \sa * - \ref ECLIC_GetPriorityIRQ_S */ __STATIC_INLINE void __ECLIC_SetPriorityIRQ_S(IRQn_Type IRQn, uint8_t pri) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits < intctlbits) { uint8_t maxpri = ((1UL << (intctlbits - nlbits)) - 1); if (pri > maxpri) { pri = maxpri; } pri = pri << (ECLIC_MAX_NLBITS - intctlbits); uint8_t mask = ((uint8_t)(-1)) >> intctlbits; pri = pri | mask; uint8_t cur_ctrl = __ECLIC_GetCtrlIRQ_S(IRQn); cur_ctrl = cur_ctrl >> (ECLIC_MAX_NLBITS - nlbits); cur_ctrl = cur_ctrl << (ECLIC_MAX_NLBITS - nlbits); __ECLIC_SetCtrlIRQ_S(IRQn, (cur_ctrl | pri)); } } /** * \brief Get ECLIC Interrupt priority of a specific interrupt in supervisor mode * \details * This function get interrupt priority of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return Interrupt priority * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_SetPriorityIRQ_S */ __STATIC_INLINE uint8_t __ECLIC_GetPriorityIRQ_S(IRQn_Type IRQn) { uint8_t nlbits = __ECLIC_GetCfgNlbits(); uint8_t intctlbits = (uint8_t)__ECLIC_INTCTLBITS; if (nlbits < intctlbits) { uint8_t cur_ctrl = __ECLIC_GetCtrlIRQ_S(IRQn); uint8_t pri = cur_ctrl << nlbits; pri = pri >> nlbits; pri = pri >> (ECLIC_MAX_NLBITS - intctlbits); return pri; } else { return 0; } } /** * \brief Enable a specific interrupt in supervisor mode * \details * This function enables the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_DisableIRQ */ __STATIC_FORCEINLINE void __ECLIC_EnableIRQ_S(IRQn_Type IRQn) { ECLIC->SCTRL[IRQn].INTIE |= CLIC_INTIE_IE_Msk; } /** * \brief Get a specific interrupt enable status in supervisor mode * \details * This function returns the interrupt enable status for the specific interrupt \em IRQn in S MODE. * \param [in] IRQn Interrupt number * \returns * - 0 Interrupt is not masked * - 1 Interrupt is enabled * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_EnableIRQ_S * - \ref ECLIC_DisableIRQ_S */ __STATIC_FORCEINLINE uint8_t __ECLIC_GetEnableIRQ_S(IRQn_Type IRQn) { return ((uint8_t) (ECLIC->SCTRL[IRQn].INTIE) & CLIC_INTIE_IE_Msk); } /** * \brief Disable a specific interrupt in supervisor mode * \details * This function disables the specific interrupt \em IRQn. * \param [in] IRQn Number of the external interrupt to disable * \remarks * - IRQn must not be negative. * \sa * - \ref ECLIC_EnableIRQ */ __STATIC_FORCEINLINE void __ECLIC_DisableIRQ_S(IRQn_Type IRQn) { ECLIC->SCTRL[IRQn].INTIE &= ~CLIC_INTIE_IE_Msk; } /** * \brief Set Interrupt Vector of a specific interrupt in supervisor mode * \details * This function set interrupt handler address of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \param [in] vector Interrupt handler address * \remarks * - IRQn must not be negative. * - You can set the \ref CSR_CSR_MTVT to set interrupt vector table entry address. * - If your vector table is placed in readonly section, the vector for IRQn will not be modified. * For this case, you need to use the correct irq handler name defined in your vector table as * your irq handler function name. * - This function will only work correctly when the vector table is placed in an read-write enabled section. * \sa * - \ref ECLIC_GetVector_S */ __STATIC_INLINE void __ECLIC_SetVector_S(IRQn_Type IRQn, rv_csr_t vector) { volatile unsigned long vec_base; vec_base = ((unsigned long)__RV_CSR_READ(CSR_STVT)); vec_base += ((unsigned long)IRQn) * sizeof(unsigned long); (* (unsigned long *) vec_base) = vector; #if (defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1)) #if (defined(__CCM_PRESENT) && (__CCM_PRESENT == 1)) SFlushDCacheLine((unsigned long)vec_base); #endif #endif #if (defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1)) #if (defined(__CCM_PRESENT) && (__CCM_PRESENT == 1)) SInvalICacheLine((unsigned long)vec_base); #else __FENCE_I(); #endif #endif } /** * \brief Get Interrupt Vector of a specific interrupt in supervisor mode * \details * This function get interrupt handler address of the specific interrupt \em IRQn. * \param [in] IRQn Interrupt number * \return Interrupt handler address * \remarks * - IRQn must not be negative. * - You can read \ref CSR_CSR_MTVT to get interrupt vector table entry address. * \sa * - \ref ECLIC_SMODE_SetVector */ __STATIC_FORCEINLINE rv_csr_t __ECLIC_GetVector_S(IRQn_Type IRQn) { #if __RISCV_XLEN == 32 return (*(uint32_t *)(__RV_CSR_READ(CSR_STVT) + IRQn * 4)); #elif __RISCV_XLEN == 64 return (*(uint64_t *)(__RV_CSR_READ(CSR_STVT) + IRQn * 8)); #else // TODO Need cover for XLEN=128 case in future return (*(uint64_t *)(__RV_CSR_READ(CSR_STVT) + IRQn * 8)); #endif } #if __ECLIC_VER == 2 /** * \brief Enable ECLIC Shadow Register Function (Supervisor Mode) * \details * This function enables the shadow register function for ECLIC in Supervisor Mode. * It sets the SECLIC_CTL_SHADOW_EN bit in the CSR_SECLIC_CTL CSR. * This function is only valid for ECLIC version 2 and above in TEE environments. * \remarks * - API only available for ECLIC v2 * \sa * - \ref __ECLIC_DisableShadow_S */ __STATIC_FORCEINLINE void __ECLIC_EnableShadow_S(void) { __RV_CSR_SET(CSR_SECLIC_CTL, SECLIC_CTL_SHADOW_EN); } /** * \brief Disable ECLIC Shadow Register Function (Supervisor Mode) * \details * This function disables the shadow register function for ECLIC in Supervisor Mode. * It clears the SECLIC_CTL_SHADOW_EN bit in the CSR_SECLIC_CTL CSR. * This function is only valid for ECLIC version 2 and above in TEE environments. * \remarks * - API only available for ECLIC v2 * \sa * - \ref __ECLIC_EnableShadow_S */ __STATIC_FORCEINLINE void __ECLIC_DisableShadow_S(void) { __RV_CSR_CLEAR(CSR_SECLIC_CTL, SECLIC_CTL_SHADOW_EN); } /** * \brief Set Shadow Register Interrupt Level for a specific s-mode shadow register * \details * This function sets the interrupt level for a specific s-mode shadow register \em idx + 1. * It configures CSR_SSHADGPRLVL0 and CSR_SSHADGPRLVL1 registers. * \param [in] idx Shadow register index (0-7), corresponding to shadow registers 1-8 * (Note: shadow register 0 is fixed for first-come-first-served s-mode * interrupt and cannot be configured) * \param [in] level Interrupt level to set for the shadow register * \remarks * - API only available for ECLIC v2 * - idx = 0 means set SHAD1_CFG, which configures the shadow register 1 * - For RV64, all 8 shadow registers are configured in CSR_SSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_SSHADGPRLVL0, * and shadow registers 5-8 are in CSR_SSHADGPRLVL1 * \sa * - \ref ECLIC_GetShadowLevel_S */ __STATIC_INLINE void __ECLIC_SetShadowLevel_S(unsigned long idx, uint8_t level) { /* Check if idx is valid (0-7) */ if (idx > 7) { return; } uint8_t nlbits = (uint8_t)__ECLIC_GetCfgNlbits(); /* Limit the level value to the available number of bits */ uint8_t max_level = (1U << nlbits) - 1; if (level > max_level) { level = max_level; } /* Position the level value in the upper nlbits of the 8-bit field and set the low (8-nlbits) bits to 1 */ uint8_t level_shifted = (uint8_t)((level << (8 - nlbits)) | ((1U << (8 - nlbits)) - 1)); #if __RISCV_XLEN == 64 /* For RV64, all 8 shadow registers are in CSR_SSHADGPRLVL0 */ /* Calculate the bit position for the 8-bit field of the specified index */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ /* Create mask to clear the 8-bit field for the specified index */ uint64_t mask = (uint64_t)0xFFUL << bit_pos; /* Read, modify, and write the CSR register */ uint64_t current_val = __RV_CSR_READ(CSR_SSHADGPRLVL0); current_val = (current_val & ~mask) | (((uint64_t)level_shifted) << bit_pos); __RV_CSR_WRITE(CSR_SSHADGPRLVL0, current_val); #else /* For RV32, calculate bit position and select appropriate register */ if (idx < 4) { /* Shadow registers 1-4 are in CSR_SSHADGPRLVL0 */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ uint32_t mask = 0xFFUL << bit_pos; uint32_t current_val = __RV_CSR_READ(CSR_SSHADGPRLVL0); current_val = (current_val & ~mask) | (((uint32_t)level_shifted) << bit_pos); __RV_CSR_WRITE(CSR_SSHADGPRLVL0, current_val); } else { /* Shadow registers 5-8 are in CSR_SSHADGPRLVL1 */ uint32_t bit_pos = (idx - 4) << 3; /* (idx - 4) * 8 using bit shift */ uint32_t mask = 0xFFUL << bit_pos; uint32_t current_val = __RV_CSR_READ(CSR_SSHADGPRLVL1); current_val = (current_val & ~mask) | (((uint32_t)level_shifted) << bit_pos); __RV_CSR_WRITE(CSR_SSHADGPRLVL1, current_val); } #endif } /** * \brief Get Shadow Register Interrupt Level for a specific s-mode shadow register * \details * This function gets the interrupt level for a specific s-mode shadow register \em idx + 1. * It reads CSR_SSHADGPRLVL0 and CSR_SSHADGPRLVL1 registers. * \param [in] idx Shadow register index (0-7), corresponding to shadow registers 1-8 * (Note: shadow register 0 is fixed for first-come-first-served s-mode * interrupt and cannot be configured) * \return Interrupt level of the shadow register * \remarks * - API only available for ECLIC v2 * - idx = 0 means set SHAD1_CFG, which configures the shadow register 1 * - For RV64, all 8 shadow registers are configured in CSR_SSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_SSHADGPRLVL0, * and shadow registers 5-8 are in CSR_SSHADGPRLVL1 * \sa * - \ref ECLIC_SetShadowLevel_S */ __STATIC_INLINE uint8_t __ECLIC_GetShadowLevel_S(unsigned long idx) { /* Check if idx is valid (0-7) */ if (idx > 7) { return 0; } uint8_t nlbits = (uint8_t)__ECLIC_GetCfgNlbits(); #if __RISCV_XLEN == 64 /* For RV64, all 8 shadow registers are in CSR_SSHADGPRLVL0 */ /* Calculate the bit position for the 8-bit field of the specified index */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ /* Read the CSR register and extract the 8-bit field */ uint64_t current_val = __RV_CSR_READ(CSR_SSHADGPRLVL0); uint8_t extracted_val = (uint8_t)((current_val >> bit_pos) & 0xFF); /* Extract the level from the upper nlbits of the 8-bit field */ uint8_t level = (extracted_val >> (8 - nlbits)); return level; #else /* For RV32, calculate bit position and select appropriate register */ if (idx < 4) { /* Shadow registers 1-4 are in CSR_SSHADGPRLVL0 */ uint32_t bit_pos = idx << 3; /* idx * 8 using bit shift */ /* Read the CSR register and extract the 8-bit field */ uint32_t current_val = __RV_CSR_READ(CSR_SSHADGPRLVL0); uint8_t extracted_val = (uint8_t)((current_val >> bit_pos) & 0xFF); /* Extract the level from the upper nlbits of the 8-bit field */ uint8_t level = (extracted_val >> (8 - nlbits)); return level; } else { /* Shadow registers 5-8 are in CSR_SSHADGPRLVL1 */ uint32_t bit_pos = (idx - 4) << 3; /* (idx - 4) * 8 using bit shift */ /* Read the CSR register and extract the 8-bit field */ uint32_t current_val = __RV_CSR_READ(CSR_SSHADGPRLVL1); uint8_t extracted_val = (uint8_t)((current_val >> bit_pos) & 0xFF); /* Extract the level from the upper nlbits of the 8-bit field */ uint8_t level = (extracted_val >> (8 - nlbits)); return level; } #endif } /** * \brief Set Shadow Register Level Register for s-mode * \details * This function sets the entire shadow register level register for s-mode. * It writes directly to CSR_SSHADGPRLVL0 register (and CSR_SSHADGPRLVL1 for RV32). * \param [in] value 64-bit value to set for the shadow register level register * \remarks * - API only available for ECLIC v2 * - For RV64, all 8 shadow registers are configured in CSR_SSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_SSHADGPRLVL0, * and shadow registers 5-8 are in CSR_SSHADGPRLVL1 * - Note: shadow register 0 is fixed for first-come-first-served s-mode interrupt and cannot be configured * \sa * - \ref ECLIC_GetShadowLevelReg_S */ __STATIC_INLINE void __ECLIC_SetShadowLevelReg_S(uint64_t value) { #if __RISCV_XLEN == 64 __RV_CSR_WRITE(CSR_SSHADGPRLVL0, value); #else __RV_CSR_WRITE(CSR_SSHADGPRLVL0, (uint32_t)value); __RV_CSR_WRITE(CSR_SSHADGPRLVL1, (uint32_t)(value >> 32)); #endif } /** * \brief Get Shadow Register Level Register for s-mode * \details * This function gets the entire shadow register level register for s-mode. * It reads from CSR_SSHADGPRLVL0 register (and CSR_SSHADGPRLVL1 for RV32) and combines them. * \return 64-bit value of the shadow register level register * \remarks * - API only available for ECLIC v2 * - For RV64, all 8 shadow registers are configured in CSR_SSHADGPRLVL0 * - For RV32, shadow registers 1-4 are in lower 32 bits of CSR_SSHADGPRLVL0, * and shadow registers 5-8 are in CSR_SSHADGPRLVL1 * - Note: shadow register 0 is fixed for first-come-first-served s-mode interrupt and cannot be configured * \sa * - \ref ECLIC_SetShadowLevelReg_S */ __STATIC_INLINE uint64_t __ECLIC_GetShadowLevelReg_S(void) { #if __RISCV_XLEN == 64 return __RV_CSR_READ(CSR_SSHADGPRLVL0); #else uint64_t value = __RV_CSR_READ(CSR_SSHADGPRLVL1); value <<= 32; value |= __RV_CSR_READ(CSR_SSHADGPRLVL0); return value; #endif } #endif #endif /* defined(__TEE_PRESENT) && (__TEE_PRESENT == 1) */ /** * \brief Set Exception entry address * \details * This function set exception handler address to 'CSR_MTVEC'. * \param [in] addr Exception handler address * \remarks * - This function use to set exception handler address to 'CSR_MTVEC'. * Address need to be aligned to 64 bytes. * \sa * - \ref __get_exc_entry */ __STATIC_FORCEINLINE void __set_exc_entry(rv_csr_t addr) { addr &= (rv_csr_t)(~0x3F); addr |= ECLIC_MODE_MTVEC_Msk; __RV_CSR_WRITE(CSR_MTVEC, addr); } /** * \brief Get Exception entry address * \details * This function get exception handler address from 'CSR_MTVEC'. * \return Exception handler address * \remarks * - This function use to get exception handler address from 'CSR_MTVEC'. * Address need to be aligned to 64 bytes. * \sa * - \ref __set_exc_entry */ __STATIC_FORCEINLINE rv_csr_t __get_exc_entry(void) { unsigned long addr = __RV_CSR_READ(CSR_MTVEC); return (addr & ~ECLIC_MODE_MTVEC_Msk); } /** * \brief Set Non-vector interrupt entry address * \details * This function set Non-vector interrupt address. * \param [in] addr Non-vector interrupt entry address * \remarks * - This function use to set non-vector interrupt entry address to 'CSR_MTVT2' if * - CSR_MTVT2 bit0 is 1. If 'CSR_MTVT2' bit0 is 0 then set address to 'CSR_MTVEC' * \sa * - \ref __get_nonvec_entry */ __STATIC_INLINE void __set_nonvec_entry(rv_csr_t addr) { if (__RV_CSR_READ(CSR_MTVT2) & 0x1) { __RV_CSR_WRITE(CSR_MTVT2, addr | 0x01); } else { addr &= (rv_csr_t)(~0x3F); addr |= ECLIC_MODE_MTVEC_Msk; __RV_CSR_WRITE(CSR_MTVEC, addr); } } /** * \brief Get Non-vector interrupt entry address * \details * This function get Non-vector interrupt address. * \return Non-vector interrupt handler address * \remarks * - This function use to get non-vector interrupt entry address from 'CSR_MTVT2' if * - CSR_MTVT2 bit0 is 1. If 'CSR_MTVT2' bit0 is 0 then get address from 'CSR_MTVEC'. * \sa * - \ref __set_nonvec_entry */ __STATIC_INLINE rv_csr_t __get_nonvec_entry(void) { if (__RV_CSR_READ(CSR_MTVT2) & 0x1) { return __RV_CSR_READ(CSR_MTVT2) & (~(rv_csr_t)(0x1)); } else { rv_csr_t addr = __RV_CSR_READ(CSR_MTVEC); return (addr & ~ECLIC_MODE_MTVEC_Msk); } } /** * \brief Get NMI interrupt entry from 'CSR_MNVEC' * \details * This function get NMI interrupt address from 'CSR_MNVEC'. * \return NMI interrupt handler address * \remarks * - This function use to get NMI interrupt handler address from 'CSR_MNVEC'. If CSR_MMISC_CTL[9] = 1 'CSR_MNVEC' * - will be equal as mtvec. If CSR_MMISC_CTL[9] = 0 'CSR_MNVEC' will be equal as reset vector. * - NMI entry is defined via \ref CSR_MMISC_CTL, writing to \ref CSR_MNVEC will be ignored. */ __STATIC_FORCEINLINE rv_csr_t __get_nmi_entry(void) { return __RV_CSR_READ(CSR_MNVEC); } /* NOTE: SSUBM CSR is introduced in ECLIC v2, without this the S_Mode vector interrupt nesting * and non-vector interrupt nesting will not work properly */ #if __ECLIC_VER == 2 #define SAVE_SSUBM_VAR() rv_csr_t __ssubm = __RV_CSR_READ(CSR_SSUBM); #define RESTORE_SSUBM_VAR() __RV_CSR_WRITE(CSR_SSUBM, __ssubm); #else #define SAVE_SSUBM_VAR() #define RESTORE_SSUBM_VAR() #endif /** * \brief Save necessary CSRs into variables for vector interrupt nesting * \details * This macro is used to declare variables which are used for saving * CSRs(MCAUSE, MEPC, MSUB), and it will read these CSR content into * these variables, it need to be used in a vector-interrupt if nesting * is required. * \remarks * - Interrupt will be enabled after this macro is called * - It need to be used together with \ref RESTORE_IRQ_CSR_CONTEXT * - Don't use variable names __mcause, __mpec, __msubm in your ISR code * - If you want to enable interrupt nesting feature for vector interrupt, * you can do it like this: * \code * // __INTERRUPT attribute will generates function entry and exit sequences suitable * // for use in an interrupt handler when this attribute is present * __INTERRUPT void eclic_mtip_handler(void) * { * // Must call this to save CSRs * SAVE_IRQ_CSR_CONTEXT(); * // !!!Interrupt is enabled here!!! * // !!!Higher priority interrupt could nest it!!! * * // put you own interrupt handling code here * * // Must call this to restore CSRs * RESTORE_IRQ_CSR_CONTEXT(); * } * \endcode */ #define SAVE_IRQ_CSR_CONTEXT() \ rv_csr_t __mcause = __RV_CSR_READ(CSR_MCAUSE); \ rv_csr_t __mepc = __RV_CSR_READ(CSR_MEPC); \ rv_csr_t __msubm = __RV_CSR_READ(CSR_MSUBM); \ __enable_irq(); /*! Save necessary CSRs into variables for vector interrupt nesting in supervisor mode */ #define SAVE_IRQ_CSR_CONTEXT_S() \ rv_csr_t __scause = __RV_CSR_READ(CSR_SCAUSE); \ rv_csr_t __sepc = __RV_CSR_READ(CSR_SEPC); \ SAVE_SSUBM_VAR(); \ __enable_irq_s(); /** * \brief Restore necessary CSRs from variables for vector interrupt nesting * \details * This macro is used restore CSRs(MCAUSE, MEPC, MSUB) from pre-defined variables * in \ref SAVE_IRQ_CSR_CONTEXT macro. * \remarks * - Interrupt will be disabled after this macro is called * - It need to be used together with \ref SAVE_IRQ_CSR_CONTEXT */ #define RESTORE_IRQ_CSR_CONTEXT() \ __disable_irq(); \ __RV_CSR_WRITE(CSR_MSUBM, __msubm); \ __RV_CSR_WRITE(CSR_MEPC, __mepc); \ __RV_CSR_WRITE(CSR_MCAUSE, __mcause); /*! Restore necessary CSRs from variables for vector interrupt nesting in supervisor mode */ #define RESTORE_IRQ_CSR_CONTEXT_S() \ __disable_irq_s(); \ RESTORE_SSUBM_VAR(); \ __RV_CSR_WRITE(CSR_SEPC, __sepc); \ __RV_CSR_WRITE(CSR_SCAUSE, __scause); /** @} */ /* End of Doxygen Group NMSIS_Core_IntExc */ #endif /* defined(__ECLIC_PRESENT) && (__ECLIC_PRESENT == 1) */ #ifdef __cplusplus } #endif #endif /* __CORE_FEATURE_ECLIC__ */