/**
\defgroup mpu8_functions  MPU Functions for Armv8-M
\brief Functions that relate to the Memory Protection Unit.
\details
The following functions support the optional Memory Protection Unit (MPU) that is available on the Cortex-M23, M33, M35P processor.

The MPU is used to prevent from illegal memory accesses that are typically caused by errors in an application software.

<b>Example:</b>
\code
int main() 
{
  // Set Region 0 using Attr 0
  ARM_MPU_SetMemAttr(0UL, ARM_MPU_ATTR( /* Normal memory */
    MPU_ATTR_NORMAL_OUTER_WT_TR_RA_WA,  /* Outer Write-Back transient with read and write allocate */
    MPU_ATTR_NORMAL_INNER_WT_TR_RA_WA   /* Inner Write-Through transient with read and write allocate */
  ));
  
  ARM_MPU_SetRegion(0UL,
    ARM_MPU_RBAR(0x08000000UL, ARM_MPU_SH_NON, ARM_MPU_AP_RW, ARM_MPU_AP_NP, ARM_MPU_XN),  /* Non-shareable, read/write, non-privileged, execute-never */
    ARM_MPU_RLAR(0x080FFFFFUL, MAIR_ATTR(0))                        /* 1MB memory block using Attr 0 */
  );
  
  ARM_MPU_Enable(0);
  
  // Execute application code that is access protected by the MPU
  
  ARM_MPU_Disable();
}
\endcode

@{
*/

/** \brief Attribute for device memory (outer only) */
#define ARM_MPU_ATTR_DEVICE                           ( 0U )

/** \brief Attribute for non-cacheable, normal memory */
#define ARM_MPU_ATTR_NON_CACHEABLE                    ( 4U )

/** \brief Attribute for Normal memory, Outer and Inner cacheability.
* \param NT Non-Transient: Set to 1 for Non-transient data. Set to 0 for Transient data.
* \param WB Write-Back: Set to 1 to use a Write-Back policy. Set to 0 to use a Write-Through policy.
* \param RA Read Allocation: Set to 1 to enable cache allocation on read miss. Set to 0 to disable cache allocation on read miss.
* \param WA Write Allocation: Set to 1 to enable cache allocation on write miss. Set to 0 to disable cache allocation on write miss.
*/
#define ARM_MPU_ATTR_MEMORY_(NT, WB, RA, WA)

/** \brief Device memory type non Gathering, non Re-ordering, non Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_nGnRnE 

/** \brief Device memory type non Gathering, non Re-ordering, Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_nGnRE

/** \brief Device memory type non Gathering, Re-ordering, Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_nGRE

/** \brief Device memory type Gathering, Re-ordering, Early Write Acknowledgement */
#define ARM_MPU_ATTR_DEVICE_GRE

/** \brief Normal memory outer-cacheable and inner-cacheable attributes
* WT = Write Through, WB = Write Back, TR = Transient, RA = Read-Allocate, WA = Write Allocate
*/
#define MPU_ATTR_NORMAL_OUTER_NON_CACHEABLE
#define MPU_ATTR_NORMAL_OUTER_WT_TR_RA
#define MPU_ATTR_NORMAL_OUTER_WT_TR_WA
#define MPU_ATTR_NORMAL_OUTER_WT_TR_RA_WA
#define MPU_ATTR_NORMAL_OUTER_WT_RA
#define MPU_ATTR_NORMAL_OUTER_WT_WA
#define MPU_ATTR_NORMAL_OUTER_WT_RA_WA
#define MPU_ATTR_NORMAL_OUTER_WB_TR_RA
#define MPU_ATTR_NORMAL_OUTER_WB_TR_WA
#define MPU_ATTR_NORMAL_OUTER_WB_TR_RA_WA
#define MPU_ATTR_NORMAL_OUTER_WB_RA
#define MPU_ATTR_NORMAL_OUTER_WB_WA
#define MPU_ATTR_NORMAL_OUTER_WB_RA_WA
#define MPU_ATTR_NORMAL_INNER_NON_CACHEABLE
#define MPU_ATTR_NORMAL_INNER_WT_TR_RA
#define MPU_ATTR_NORMAL_INNER_WT_TR_WA
#define MPU_ATTR_NORMAL_INNER_WT_TR_RA_WA
#define MPU_ATTR_NORMAL_INNER_WT_RA
#define MPU_ATTR_NORMAL_INNER_WT_WA
#define MPU_ATTR_NORMAL_INNER_WT_RA_WA
#define MPU_ATTR_NORMAL_INNER_WB_TR_RA
#define MPU_ATTR_NORMAL_INNER_WB_TR_WA
#define MPU_ATTR_NORMAL_INNER_WB_TR_RA_WA
#define MPU_ATTR_NORMAL_INNER_WB_RA
#define MPU_ATTR_NORMAL_INNER_WB_WA
#define MPU_ATTR_NORMAL_INNER_WB_RA_WA

/** \brief Memory Attribute
* \param O Outer memory attributes
* \param I O == ARM_MPU_ATTR_DEVICE: Device memory attributes, else: Inner memory attributes
*/
#define ARM_MPU_ATTR(O, I)

/**
 * Shareability
 */
/** \brief Normal memory non-shareable  */
#define ARM_MPU_SH_NON

/** \brief Normal memory outer shareable  */
#define ARM_MPU_SH_OUTER

/** \brief Normal memory inner shareable  */
#define ARM_MPU_SH_INNER

/**
 * Access permissions
 * AP = Access permission, RO = Read-only, RW = Read/Write, NP = Any privilege, PO = Privileged code only
 */
/** \brief Normal memory, read/write */
#define ARM_MPU_AP_RW

/** \brief Normal memory, read-only */
#define ARM_MPU_AP_RO

/** \brief Normal memory, any privilege level */
#define ARM_MPU_AP_NP

/** \brief Normal memory, privileged access only */
#define ARM_MPU_AP_PO

/*
 * Execute-never
 * XN = Execute-never, EX = Executable
 */
/** \brief Normal memory, Execution only permitted if read permitted */
#define ARM_MPU_XN

/** \brief Normal memory, Execution only permitted if read permitted */
#define ARM_MPU_EX

/** \brief Memory access permissions
* \param RO Read-Only: Set to 1 for read-only memory.
* \param NP Non-Privileged: Set to 1 for non-privileged memory.
*/
#define ARM_MPU_AP_(RO, NP)

/** \brief Region Base Address Register value
* \param BASE The base address bits [31:5] of a memory region. The value is zero extended. Effective address gets 32 byte aligned.
* \param SH Defines the Shareability domain for this memory region.
* \param RO Read-Only: Set to 1 for a read-only memory region. Set to 0 for a read/write memory region.
* \param NP Non-Privileged: Set to 1 for a non-privileged memory region. Set to 0 for privileged memory region.
* \param XN eXecute Never: Set to 1 for a non-executable memory region. Set to 0 for an executable memory region.
*/
#define ARM_MPU_RBAR(BASE, SH, RO, NP, XN)

/** \brief Region Limit Address Register value
* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended.
* \param IDX The attribute index to be associated with this memory region.
*/
#define ARM_MPU_RLAR(LIMIT, IDX)

/** \brief Region Limit Address Register with PXN value
* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended.
* \param PXN Privileged execute never. Defines whether code can be executed from this privileged region.
* \param IDX The attribute index to be associated with this memory region.
*/
#define ARM_MPU_RLAR_PXN(LIMIT, PXN, IDX)

/**
* Struct for a single MPU Region
*/
typedef struct {
  uint32_t RBAR;                   /*!< Region Base Address Register value */
  uint32_t RLAR;                   /*!< Region Limit Address Register value */
} ARM_MPU_Region_t;

/**
  \brief  Read MPU Type Register
  \return Number of MPU regions
*/
__STATIC_INLINE uint32_t ARM_MPU_TYPE()

/** Enable the MPU.
* \param MPU_Control Default access permissions for unconfigured regions.
*/
__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control);

/** Disable the MPU.
*/
__STATIC_INLINE void ARM_MPU_Disable(void);

/** Enable the Non-secure MPU.
* \param MPU_Control Default access permissions for unconfigured regions.
*/
__STATIC_INLINE ARM_MPU_Enable_NS(uint32_t MPU_Control);

/** Disable the Non-secure MPU.
*/
__STATIC_INLINE void ARM_MPU_Disable_NS(void);

/** Set the memory attribute encoding to the given MPU.
* \param mpu Pointer to the MPU to be configured.
* \param idx The attribute index to be set [0-7]
* \param attr The attribute value to be set.
*/
__STATIC_INLINE void ARM_MPU_SetMemAttrEx(MPU_Type* mpu, uint8_t idx, uint8_t attr);

/** Set the memory attribute encoding.
* \param idx The attribute index to be set [0-7]
* \param attr The attribute value to be set.
*/
__STATIC_INLINE void ARM_MPU_SetMemAttr(uint8_t idx, uint8_t attr);

/** Set the memory attribute encoding to the Non-secure MPU.
* \param idx The attribute index to be set [0-7]
* \param attr The attribute value to be set.
*/
__STATIC_INLINE void ARM_MPU_SetMemAttr_NS(uint8_t idx, uint8_t attr);

/** Clear and disable the given MPU region of the given MPU.
* \param mpu Pointer to MPU to be used.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegionEx(MPU_Type* mpu, uint32_t rnr);

/** Clear and disable the given MPU region.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr);

/** Clear and disable the given Non-secure MPU region.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegion_NS(uint32_t rnr);

/** Configure the given MPU region of the given MPU.
* \param mpu Pointer to MPU to be used.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rlar Value for RLAR register.
*/   
__STATIC_INLINE void ARM_MPU_SetRegionEx(MPU_Type* mpu, uint32_t rnr, uint32_t rbar, uint32_t rlar);

/** Configure the given MPU region.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rlar Value for RLAR register.
*/   
__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rnr, uint32_t rbar, uint32_t rlar);

/** Configure the given Non-secure MPU region.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rlar Value for RLAR register.
*/   
__STATIC_INLINE void ARM_MPU_SetRegion_NS(uint32_t rnr, uint32_t rbar, uint32_t rlar);

/** Memcpy with strictly ordered memory access, e.g. used by code in \ref ARM_MPU_LoadEx.
* \param dst Destination data is copied to.
* \param src Source data is copied from.
* \param len Amount of data words to be copied.
*/
__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len);

/** Load the given number of MPU regions from a table to the given MPU.
* \param mpu Pointer to the MPU registers to be used.
* \param rnr First region number to be configured.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*/
__STATIC_INLINE void ARM_MPU_LoadEx(MPU_Type* mpu, uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt);

/** Load the given number of MPU regions from a table.
* \param rnr First region number to be configured.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*
* <b>Example:</b>
* \code
* const ARM_MPU_Region_t mpuTable[1][4] = {
*   {
*     //                     BASE          SH              RO             NP             XN                                LIMIT         ATTR 
*     { .RBAR = ARM_MPU_RBAR(0x08000000UL, ARM_MPU_SH_NON, ARM_MPU_AP_RO, ARM_MPU_AP_NP, ARM_MPU_XN), .RLAR = ARM_MPU_RLAR(0x080FFFFFUL, MAIR_ATTR(0)) },
*     { .RBAR = ARM_MPU_RBAR(0x20000000UL, ARM_MPU_SH_NON, ARM_MPU_AP_RO, ARM_MPU_AP_NP, ARM_MPU_XN), .RLAR = ARM_MPU_RLAR(0x20007FFFUL, MAIR_ATTR(0)) },
*     { .RBAR = ARM_MPU_RBAR(0x40020000UL, ARM_MPU_SH_NON, ARM_MPU_AP_RO, ARM_MPU_AP_NP, ARM_MPU_XN), .RLAR = ARM_MPU_RLAR(0x40021FFFUL, MAIR_ATTR(1)) },
*     { .RBAR = ARM_MPU_RBAR(0x40022000UL, ARM_MPU_SH_NON, ARM_MPU_AP_RO, ARM_MPU_AP_NP, ARM_MPU_XN), .RLAR = ARM_MPU_RLAR(0x40022FFFUL, MAIR_ATTR(1)) }
*   }
* };
*  
* void UpdateMpu(uint32_t idx)
* {
*    ARM_MPU_Load(0, mpuTable[idx], 4);
* }
* \endcode
*/
__STATIC_INLINE void ARM_MPU_Load(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt);

/** Load the given number of MPU regions from a table to the Non-secure MPU.
* \param rnr First region number to be configured.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*/
__STATIC_INLINE void ARM_MPU_Load_NS(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt);

/** @} */

