#include "../driverlib/adi.h"
#include "../driverlib/aes.h"
#include "../driverlib/aon_batmon.h"
#include "../driverlib/aon_batmon.h"
#include "../driverlib/aon_batmon.h"
#include "../driverlib/aon_event.h"
#include "../driverlib/aon_ioc.h"
#include "../driverlib/aon_rtc.h"
#include "../driverlib/aon_rtc.h"
#include "../driverlib/aux_adc.h"
#include "../driverlib/aux_ctrl.h"
#include "../driverlib/aux_tdc.h"
#include "../driverlib/chipinfo.h"
#include "../driverlib/cpu.h"
#include "../driverlib/cpu.h"
#include "../driverlib/crypto.h"
#include "../driverlib/ddi.h"
#include "../driverlib/ddi.h"
#include "../driverlib/event.h"
#include "../driverlib/flash.h"
#include "../driverlib/flash.h"
#include "../driverlib/gpio.h"
#include "../driverlib/i2c.h"
#include "../driverlib/i2s.h"
#include "../driverlib/interrupt.h"
#include "../driverlib/ioc.h"
#include "../driverlib/ioc.h"
#include "../driverlib/osc.h"
#include "../driverlib/osc.h"
#include "../driverlib/pka.h"
#include "../driverlib/prcm.h"
#include "../driverlib/pwr_ctrl.h"
#include "../driverlib/setup_rom.h"
#include "../driverlib/setup_rom.h"
#include "../driverlib/setup_rom.h"
#include "../driverlib/sha2.h"
#include "../driverlib/smph.h"
#include "../driverlib/ssi.h"
#include "../driverlib/sys_ctrl.h"
#include "../driverlib/sys_ctrl.h"
#include "../driverlib/timer.h"
#include "../driverlib/trng.h"
#include "../driverlib/uart.h"
#include "../driverlib/udma.h"
#include "../driverlib/vims.h"
#include "../inc/hw_adi.h"
#include "../inc/hw_adi_2_refsys.h"
#include "../inc/hw_adi_3_refsys.h"
#include "../inc/hw_adi_4_aux.h"
#include "../inc/hw_aon_batmon.h"
#include "../inc/hw_aux_sysif.h"
#include "../inc/hw_aux_sysif.h"
#include "../inc/hw_ccfg.h"
#include "../inc/hw_ccfg.h"
#include "../inc/hw_ccfg.h"
#include "../inc/hw_ccfg.h"
#include "../inc/hw_ddi_0_osc.h"
#include "../inc/hw_fcfg1.h"
#include "../inc/hw_fcfg1.h"
#include "../inc/hw_fcfg1.h"
#include "../inc/hw_fcfg1.h"
#include "../inc/hw_ioc.h"
#include "../inc/hw_memmap.h"
#include "../inc/hw_memmap.h"
#include "../inc/hw_types.h"
#include "../inc/hw_types.h"
#include "../inc/hw_types.h"
#include "../inc/hw_types.h"

//*****************************************************************************
//
// Disable all external interrupts
//
//*****************************************************************************
#if defined(DOXYGEN)
uint32_t
CPUcpsid(void)
{
    // This function is written in assembly. See cpu.c for compiler specific implementation.
}
#elif defined(__IAR_SYSTEMS_ICC__)
uint32_t
CPUcpsid(void)
{
    // Read PRIMASK and disable interrupts.
    __asm("    mrs     r0, PRIMASK\n"
          "    cpsid   i\n");

    // "Warning[Pe940]: missing return statement at end of non-void function"
    // is suppressed here to avoid putting a "bx lr" in the inline assembly
    // above and a superfluous return statement here.
#pragma diag_suppress=Pe940
}
#pragma diag_default=Pe940
#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
__asm uint32_t
CPUcpsid(void)
{
    // Read PRIMASK and disable interrupts.
    mrs     r0, PRIMASK;
    cpsid   i;
    bx      lr
}
#elif defined(__TI_COMPILER_VERSION__)
uint32_t
CPUcpsid(void)
{
    // Read PRIMASK and disable interrupts.
    __asm("    mrs     r0, PRIMASK\n"
          "    cpsid   i\n"
          "    bx      lr\n");

    // The following keeps the compiler happy, because it wants to see a
    // return value from this function.  It will generate code to return
    // a zero.  However, the real return is the "bx lr" above, so the
    // return(0) is never executed and the function returns with the value
    // you expect in R0.
    return(0);
}
#else
uint32_t __attribute__((naked))
CPUcpsid(void)
{
    uint32_t ui32Ret;

    // Read PRIMASK and disable interrupts
    __asm volatile ("    mrs     %0, PRIMASK\n"
                    "    cpsid   i\n"
                    "    bx      lr\n"
                    : "=r"(ui32Ret)
                   );

    // The return is handled in the inline assembly, but the compiler will
    // still complain if there is not an explicit return here (despite the fact
    // that this does not result in any code being produced because of the
    // naked attribute).
    return(ui32Ret);
}
#endif
//*****************************************************************************
//
// Enable all external interrupts
//
//*****************************************************************************
#if defined(DOXYGEN)
uint32_t
CPUcpsie(void)
{
    // This function is written in assembly. See cpu.c for compiler specific implementation.
}
#elif defined(__IAR_SYSTEMS_ICC__)
uint32_t
CPUcpsie(void)
{
    // Read PRIMASK and enable interrupts.
    __asm("    mrs     r0, PRIMASK\n"
          "    cpsie   i\n");

    // "Warning[Pe940]: missing return statement at end of non-void function"
    // is suppressed here to avoid putting a "bx lr" in the inline assembly
    // above and a superfluous return statement here.
#pragma diag_suppress=Pe940
}
#pragma diag_default=Pe940
#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
__asm uint32_t
CPUcpsie(void)
{
    // Read PRIMASK and enable interrupts.
    mrs     r0, PRIMASK;
    cpsie   i;
    bx      lr
}
#elif defined(__TI_COMPILER_VERSION__)
uint32_t
CPUcpsie(void)
{
    // Read PRIMASK and enable interrupts.
    __asm("    mrs     r0, PRIMASK\n"
          "    cpsie   i\n"
          "    bx      lr\n");

    // The following keeps the compiler happy, because it wants to see a
    // return value from this function.  It will generate code to return
    // a zero.  However, the real return is the "bx lr" above, so the
    // return(0) is never executed and the function returns with the value
    // you expect in R0.
    return(0);
}
#else
uint32_t __attribute__((naked))
CPUcpsie(void)
{
    uint32_t ui32Ret;

    // Read PRIMASK and enable interrupts.
    __asm volatile ("    mrs     %0, PRIMASK\n"
                    "    cpsie   i\n"
                    "    bx      lr\n"
                    : "=r"(ui32Ret)
                   );

    // The return is handled in the inline assembly, but the compiler will
    // still complain if there is not an explicit return here (despite the fact
    // that this does not result in any code being produced because of the
    // naked attribute).
    return(ui32Ret);
}
#endif
//*****************************************************************************
//
// Provide a small delay
//
//*****************************************************************************
#if defined(DOXYGEN)
void
CPUdelay(uint32_t ui32Count)
{
    // This function is written in assembly. See cpu.c for compiler specific implementation.
}
#elif defined(__IAR_SYSTEMS_ICC__)
void
CPUdelay(uint32_t ui32Count)
{
    // Loop the specified number of times
    __asm("CPUdelay:\n"
          "    subs    r0, #1\n"
          "    bne.n   CPUdelay\n"
          "    bx      lr");
#pragma diag_suppress=Pe940
}
#pragma diag_default=Pe940
#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
__asm void
CPUdelay(uint32_t ui32Count)
{
    // Delay the specified number of times (3 cycles pr. loop)
CPUdel
    subs    r0, #1;
    bne     CPUdel;
    bx      lr;
}
#elif defined(__TI_COMPILER_VERSION__)
    // For CCS implement this function in pure assembly. This prevents the TI
    // compiler from doing funny things with the optimizer.

    // Loop the specified number of times
__asm("    .sect \".text:CPUdelay\"\n"
      "    .clink\n"
      "    .thumbfunc CPUdelay\n"
      "    .thumb\n"
      "    .global CPUdelay\n"
      "CPUdelay:\n"
      "    subs r0, #1\n"
      "    bne.n CPUdelay\n"
      "    bx lr\n");
#else
// GCC
void __attribute__((naked))
CPUdelay(uint32_t ui32Count)
{
    // Loop the specified number of times
    __asm volatile ("%=:  subs  %0, #1\n"
                    "     bne   %=b\n"
                    "     bx    lr\n"
                    : /* No output */
                    : "r" (ui32Count)
                   );
}
#endif

void AONEventMcuWakeUpSet(uint32_t ui32MCUWUEvent, uint32_t ui32EventSrc) {
    uint32_t ui32Shift  ;
    uint32_t ui32Mask   ;
    uint32_t ui32RegAdr ;

    // Check the arguments.
    ASSERT(( ui32MCUWUEvent >= AON_EVENT_MCU_WU0 ) && ( ui32MCUWUEvent <= AON_EVENT_MCU_WU7 ))
    ASSERT( ui32EventSrc <= AON_EVENT_NONE );

    ui32Shift  = (( ui32MCUWUEvent & 3 ) << 3            );
    ui32Mask   = ( 0x3F << ui32Shift                     );
    ui32RegAdr = ( AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL );
    if ( ui32MCUWUEvent > 3 ) {
      ui32RegAdr += 4;
    }
    HWREG( ui32RegAdr ) = ( HWREG( ui32RegAdr ) & ( ~ui32Mask )) | ( ui32EventSrc << ui32Shift );
}

uint32_t AONEventMcuWakeUpGet(uint32_t ui32MCUWUEvent) {
    uint32_t ui32Shift  ;
    uint32_t ui32RegAdr ;

    // Check the arguments.
    ASSERT(( ui32MCUWUEvent >= AON_EVENT_MCU_WU0 ) && ( ui32MCUWUEvent <= AON_EVENT_MCU_WU7 ))

    ui32Shift  = (( ui32MCUWUEvent & 3 ) << 3            );
    ui32RegAdr = ( AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL );
    if ( ui32MCUWUEvent > 3 ) {
      ui32RegAdr += 4;
    }
    return (( HWREG( ui32RegAdr ) >> ui32Shift ) & 0x3F );
}

void AONEventMcuSet(uint32_t ui32MCUEvent, uint32_t ui32EventSrc) {
    uint32_t ui32Ctrl;

    // Check the arguments.
    ASSERT((ui32MCUEvent == AON_EVENT_MCU_EVENT0) ||
           (ui32MCUEvent == AON_EVENT_MCU_EVENT1) ||
           (ui32MCUEvent == AON_EVENT_MCU_EVENT2));
    ASSERT(ui32EventSrc <= AON_EVENT_NONE);

    ui32Ctrl = HWREG(AON_EVENT_BASE + AON_EVENT_O_EVTOMCUSEL);

    if(ui32MCUEvent == AON_EVENT_MCU_EVENT0)
    {
        ui32Ctrl &= ~(AON_EVENT_EVTOMCUSEL_AON_PROG0_EV_M);
        ui32Ctrl |= (ui32EventSrc & 0x3f) << AON_EVENT_EVTOMCUSEL_AON_PROG0_EV_S;
    }
    else if(ui32MCUEvent == AON_EVENT_MCU_EVENT1)
    {
        ui32Ctrl &= ~(AON_EVENT_EVTOMCUSEL_AON_PROG1_EV_M);
        ui32Ctrl |= (ui32EventSrc & 0x3f) << AON_EVENT_EVTOMCUSEL_AON_PROG1_EV_S;
    }
    else if(ui32MCUEvent == AON_EVENT_MCU_EVENT2)
    {
        ui32Ctrl &= ~(AON_EVENT_EVTOMCUSEL_AON_PROG2_EV_M);
        ui32Ctrl |= (ui32EventSrc & 0x3f) << AON_EVENT_EVTOMCUSEL_AON_PROG2_EV_S;
    }

    HWREG(AON_EVENT_BASE + AON_EVENT_O_EVTOMCUSEL) = ui32Ctrl;
}

uint32_t AONEventMcuGet(uint32_t ui32MCUEvent) {
    uint32_t ui32EventSrc;

    // Check the arguments.
    ASSERT((ui32MCUEvent == AON_EVENT_MCU_EVENT0) ||
           (ui32MCUEvent == AON_EVENT_MCU_EVENT1) ||
           (ui32MCUEvent == AON_EVENT_MCU_EVENT2));

    ui32EventSrc = HWREG(AON_EVENT_BASE + AON_EVENT_O_EVTOMCUSEL);

    if(ui32MCUEvent == AON_EVENT_MCU_EVENT0)
    {
        return((ui32EventSrc & AON_EVENT_EVTOMCUSEL_AON_PROG0_EV_M) >>
               AON_EVENT_EVTOMCUSEL_AON_PROG0_EV_S);
    }
    else if(ui32MCUEvent == AON_EVENT_MCU_EVENT1)
    {
        return((ui32EventSrc & AON_EVENT_EVTOMCUSEL_AON_PROG1_EV_M) >>
               AON_EVENT_EVTOMCUSEL_AON_PROG1_EV_S);
    }
    else if(ui32MCUEvent == AON_EVENT_MCU_EVENT2)
    {
        return((ui32EventSrc & AON_EVENT_EVTOMCUSEL_AON_PROG2_EV_M) >>
               AON_EVENT_EVTOMCUSEL_AON_PROG2_EV_S);
    }

    // Should never get to this statement, but suppress warning.
    ASSERT(0);
    return(0);
}

uint64_t AONRTCCurrent64BitValueGet( void ) {
    union {
        uint64_t  returnValue       ;
        uint32_t  secAndSubSec[ 2 ] ;
    } currentRtc                    ;
    uint32_t      ui32SecondSecRead ;

    // Reading SEC both before and after SUBSEC in order to detect if SEC incremented while reading SUBSEC
    // If SEC incremented, we can't be sure which SEC the SUBSEC belongs to, so repeating the sequence then.
    do {
        currentRtc.secAndSubSec[ 1 ] = HWREG( AON_RTC_BASE + AON_RTC_O_SEC    );
        currentRtc.secAndSubSec[ 0 ] = HWREG( AON_RTC_BASE + AON_RTC_O_SUBSEC );
        ui32SecondSecRead            = HWREG( AON_RTC_BASE + AON_RTC_O_SEC    );
    } while ( currentRtc.secAndSubSec[ 1 ] != ui32SecondSecRead );

    return ( currentRtc.returnValue );
}

void AUXCTRLImageLoad(uint16_t *pui16Image, uint32_t ui32StartAddr, uint32_t ui32Size) {
    uint16_t* pui16Src16;
    uint16_t* pui16Dst16;
    uint32_t ui32WordCnt;

    // Check the arguments.
    ASSERT(ui32StartAddr < 512);
    ASSERT(ui32Size <= 1024);
    ASSERT((ui32Size / 2 + ui32StartAddr) <= 512);

    // Copy image to AUX RAM.
    ui32WordCnt = (ui32Size >> 1);
    pui16Src16 = pui16Image;
    pui16Dst16 = (uint16_t*)(AUX_RAM_BASE + (ui32StartAddr << 1));

    while(ui32WordCnt--)
    {
        *pui16Dst16++ = *pui16Src16++;
    }
}

void AUXTDCConfigSet(uint32_t ui32Base, uint32_t ui32StartCondition, uint32_t ui32StopCondition) {
    // Check the arguments.
    ASSERT(AUXTDCBaseValid(ui32Base));

    // Make sure the AUX TDC is in the idle state before changing the
    // configuration.
    while(!((HWREG(ui32Base + AUX_TDC_O_STAT) & AUX_TDC_STAT_STATE_M) ==
            AUX_TDC_STAT_STATE_IDLE))
    {
    }

    // Clear previous results.
    HWREG(ui32Base + AUX_TDC_O_CTL) = 0x0;

    // Change the configuration.
    HWREG(ui32Base + AUX_TDC_O_TRIGSRC) = ui32StartCondition | ui32StopCondition;
}

uint32_t AUXTDCMeasurementDone(uint32_t ui32Base) {
    uint32_t ui32Reg;
    uint32_t ui32Status;

    // Check the arguments.
    ASSERT(AUXTDCBaseValid(ui32Base));

    // Check if the AUX TDC is done measuring.
    ui32Reg = HWREG(ui32Base + AUX_TDC_O_STAT);
    if(ui32Reg & AUX_TDC_STAT_DONE)
    {
        ui32Status = AUX_TDC_DONE;
    }
    else if(ui32Reg & AUX_TDC_STAT_SAT)
    {
        ui32Status = AUX_TDC_TIMEOUT;
    }
    else
    {
        ui32Status = AUX_TDC_BUSY;
    }

    // Return the status.
    return (ui32Status);
}

void DDI32RegWrite(uint32_t ui32Base, uint32_t ui32Reg, uint32_t ui32Val) {
    // Check the arguments.
    ASSERT(DDIBaseValid(ui32Base));
    ASSERT(ui32Reg < DDI_SLAVE_REGS);

    // Write the value to the register.
    HWREG(ui32Base + ui32Reg) = ui32Val;
}

void DDI16BitWrite(uint32_t ui32Base, uint32_t ui32Reg, uint32_t ui32Mask, uint32_t ui32WrData) {
    uint32_t ui32RegAddr;
    uint32_t ui32Data;

    // Check the arguments.
    ASSERT(DDIBaseValid(ui32Base));
    ASSERT(!((ui32Mask & 0xFFFF0000) ^ (ui32Mask & 0x0000FFFF)));
    ASSERT(!(ui32WrData & 0xFFFF0000));

    // DDI 16-bit target is on 32-bit boundary so double offset
    ui32RegAddr = ui32Base + (ui32Reg << 1) + DDI_O_MASK16B;

    // Adjust for target bit in high half of the word.
    if(ui32Mask & 0xFFFF0000)
    {
        ui32RegAddr += 4;
        ui32Mask >>= 16;
    }

    // Write mask if data is not zero (to set mask bit), else write '0'.
    ui32Data = ui32WrData ? ui32Mask : 0x0;

    // Update the register.
    HWREG(ui32RegAddr) = (ui32Mask << 16) | ui32Data;
}

void DDI16BitfieldWrite(uint32_t ui32Base, uint32_t ui32Reg, uint32_t ui32Mask, uint32_t ui32Shift, uint16_t ui32Data) {
    uint32_t ui32RegAddr;
    uint32_t ui32WrData;

    // Check the arguments.
    ASSERT(DDIBaseValid(ui32Base));

    // 16-bit target is on 32-bit boundary so double offset.
    ui32RegAddr = ui32Base + (ui32Reg << 1) + DDI_O_MASK16B;

    // Adjust for target bit in high half of the word.
    if(ui32Shift >= 16)
    {
        ui32Shift = ui32Shift - 16;
        ui32RegAddr += 4;
        ui32Mask = ui32Mask >> 16;
    }

    // Shift data in to position.
    ui32WrData = ui32Data << ui32Shift;

    // Write data.
    HWREG(ui32RegAddr) = (ui32Mask << 16) | ui32WrData;
}

uint16_t DDI16BitRead(uint32_t ui32Base, uint32_t ui32Reg, uint32_t ui32Mask) {
    uint32_t ui32RegAddr;
    uint16_t ui16Data;

    // Check the arguments.
    ASSERT(DDIBaseValid(ui32Base));

    // Calculate the address of the register.
    ui32RegAddr = ui32Base + ui32Reg + DDI_O_DIR;

    // Adjust for target bit in high half of the word.
    if(ui32Mask & 0xFFFF0000)
    {
        ui32RegAddr += 2;
        ui32Mask = ui32Mask >> 16;
    }

    // Read a halfword on the DDI interface.
    ui16Data = HWREGH(ui32RegAddr);

    // Mask data.
    ui16Data = ui16Data & ui32Mask;

    // Return masked data.
    return(ui16Data);
}

uint16_t DDI16BitfieldRead(uint32_t ui32Base, uint32_t ui32Reg, uint32_t ui32Mask, uint32_t ui32Shift) {
    uint32_t ui32RegAddr;
    uint16_t ui16Data;

    // Check the arguments.
    ASSERT(DDIBaseValid(ui32Base));

    // Calculate the register address.
    ui32RegAddr = ui32Base + ui32Reg + DDI_O_DIR;

    // Adjust for target bit in high half of the word.
    if(ui32Shift >= 16)
    {
        ui32Shift = ui32Shift - 16;
        ui32RegAddr += 2;
        ui32Mask = ui32Mask >> 16;
    }

    // Read the register.
    ui16Data = HWREGH(ui32RegAddr);

    // Mask data and shift into place.
    ui16Data &= ui32Mask;
    ui16Data >>= ui32Shift;

    // Return data.
    return(ui16Data);
}

//*****************************************************************************
//
// Defines for accesses to the security control in the customer configuration
// area in flash top sector.
//
//*****************************************************************************
#define CCFG_OFFSET_SECURITY   CCFG_O_BL_CONFIG
#define CCFG_OFFSET_SECT_PROT  CCFG_O_CCFG_PROT_31_0
#define CCFG_SIZE_SECURITY     0x00000014
#define CCFG_SIZE_SECT_PROT    0x00000004

//*****************************************************************************
//
// Default values for security control in customer configuration area in flash
// top sector.
//
//*****************************************************************************
const uint8_t g_pui8CcfgDefaultSec[] = {0xFF, 0xFF, 0xFF, 0xC5,
                                        0xFF, 0xFF, 0xFF, 0xFF,
                                        0xC5, 0xFF, 0xFF, 0xFF,
                                        0xC5, 0xC5, 0xC5, 0xFF,
                                        0xC5, 0xC5, 0xC5, 0xFF
                                       };

//*****************************************************************************
//
// Function prototypes for static functions
//
//*****************************************************************************
static void IssueFsmCommand(tFlashStateCommandsType eCommand);
static void EnableSectorsForWrite(void);
static uint32_t ScaleCycleValues(uint32_t ui32SpecifiedTiming,
                                 uint32_t ui32ScaleValue);
static void SetWriteMode(void);
static void TrimForWrite(void);
static void SetReadMode(void);

void FlashPowerModeSet(uint32_t ui32PowerMode, uint32_t ui32BankGracePeriod, uint32_t ui32PumpGracePeriod) {
    // Check the arguments.
    ASSERT(ui32PowerMode == FLASH_PWR_ACTIVE_MODE ||
           ui32PowerMode == FLASH_PWR_OFF_MODE    ||
           ui32PowerMode == FLASH_PWR_DEEP_STDBY_MODE);
    ASSERT(ui32BankGracePeriod <= 0xFF);
    ASSERT(ui32PumpGracePeriod <= 0xFFFF);

    switch(ui32PowerMode)
    {
    case FLASH_PWR_ACTIVE_MODE:
        // Set bank power mode to ACTIVE.
        HWREG(FLASH_BASE + FLASH_O_FBFALLBACK) =
            (HWREG(FLASH_BASE + FLASH_O_FBFALLBACK) &
             ~FLASH_FBFALLBACK_BANKPWR0_M) | FBFALLBACK_ACTIVE;

        // Set charge pump power mode to ACTIVE mode.
        HWREG(FLASH_BASE + FLASH_O_FPAC1) =
            (HWREG(FLASH_BASE + FLASH_O_FPAC1) & ~FLASH_FPAC1_PUMPPWR_M) | (1 << FLASH_FPAC1_PUMPPWR_S);
        break;

    case FLASH_PWR_OFF_MODE:
        // Set bank grace period.
        HWREG(FLASH_BASE + FLASH_O_FBAC) =
            (HWREG(FLASH_BASE + FLASH_O_FBAC) & (~FLASH_FBAC_BAGP_M)) |
            ((ui32BankGracePeriod << FLASH_FBAC_BAGP_S) & FLASH_FBAC_BAGP_M);

        // Set pump grace period.
        HWREG(FLASH_BASE + FLASH_O_FPAC2) =
            (HWREG(FLASH_BASE + FLASH_O_FPAC2) & (~FLASH_FPAC2_PAGP_M)) |
            ((ui32PumpGracePeriod << FLASH_FPAC2_PAGP_S) & FLASH_FPAC2_PAGP_M);

        // Set bank power mode to SLEEP.
        HWREG(FLASH_BASE + FLASH_O_FBFALLBACK) &= ~FLASH_FBFALLBACK_BANKPWR0_M;

        // Set charge pump power mode to SLEEP mode.
        HWREG(FLASH_BASE + FLASH_O_FPAC1) &= ~FLASH_FPAC1_PUMPPWR_M;
        break;

    case FLASH_PWR_DEEP_STDBY_MODE:
        // Set bank grace period.
        HWREG(FLASH_BASE + FLASH_O_FBAC) =
            (HWREG(FLASH_BASE + FLASH_O_FBAC) & (~FLASH_FBAC_BAGP_M)) |
            ((ui32BankGracePeriod << FLASH_FBAC_BAGP_S) & FLASH_FBAC_BAGP_M);

        // Set pump grace period.
        HWREG(FLASH_BASE + FLASH_O_FPAC2) =
            (HWREG(FLASH_BASE + FLASH_O_FPAC2) & (~FLASH_FPAC2_PAGP_M)) |
            ((ui32PumpGracePeriod << FLASH_FPAC2_PAGP_S) & FLASH_FPAC2_PAGP_M);

        // Set bank power mode to DEEP STANDBY mode.
        HWREG(FLASH_BASE + FLASH_O_FBFALLBACK) =
            (HWREG(FLASH_BASE + FLASH_O_FBFALLBACK) &
             ~FLASH_FBFALLBACK_BANKPWR0_M) | FBFALLBACK_DEEP_STDBY;

        // Set charge pump power mode to STANDBY mode.
        HWREG(FLASH_BASE + FLASH_O_FPAC1) |= FLASH_FPAC1_PUMPPWR_M;
        break;
    }
}

uint32_t FlashPowerModeGet(void) {
    uint32_t ui32PowerMode;
    uint32_t ui32BankPwrMode;

    ui32BankPwrMode = HWREG(FLASH_BASE + FLASH_O_FBFALLBACK) &
                      FLASH_FBFALLBACK_BANKPWR0_M;

    if(ui32BankPwrMode == FBFALLBACK_SLEEP)
    {
        ui32PowerMode = FLASH_PWR_OFF_MODE;
    }
    else if(ui32BankPwrMode == FBFALLBACK_DEEP_STDBY)
    {
        ui32PowerMode = FLASH_PWR_DEEP_STDBY_MODE;
    }
    else
    {
        ui32PowerMode = FLASH_PWR_ACTIVE_MODE;
    }

    // Return power mode.
    return(ui32PowerMode);
}

void FlashProtectionSet(uint32_t ui32SectorAddress, uint32_t ui32ProtectMode) {
    uint32_t ui32SectorNumber;

    // Check the arguments.
    ASSERT(ui32SectorAddress <= (FLASHMEM_BASE + FlashSizeGet() -
                                 FlashSectorSizeGet()));
    ASSERT((ui32SectorAddress & (FlashSectorSizeGet() - 1)) == 00);

    if(ui32ProtectMode == FLASH_WRITE_PROTECT)
    {
        ui32SectorNumber = (ui32SectorAddress - FLASHMEM_BASE) /
                           FlashSectorSizeGet();
        HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;

        if(ui32SectorNumber <= 31)
        {
            HWREG(FLASH_BASE + FLASH_O_FSM_BSLE0) |= (1 << ui32SectorNumber);
            HWREG(FLASH_BASE + FLASH_O_FSM_BSLP0) |= (1 << ui32SectorNumber);
        }
        else if(ui32SectorNumber <= 63)
        {
            HWREG(FLASH_BASE + FLASH_O_FSM_BSLE1) |=
                (1 << (ui32SectorNumber & 0x1F));
            HWREG(FLASH_BASE + FLASH_O_FSM_BSLP1) |=
                (1 << (ui32SectorNumber & 0x1F));
        }

        HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;
    }
}

uint32_t FlashProtectionGet(uint32_t ui32SectorAddress) {
    uint32_t ui32SectorProtect;
    uint32_t ui32SectorNumber;

    // Check the arguments.
    ASSERT(ui32SectorAddress <= (FLASHMEM_BASE + FlashSizeGet() -
                                 FlashSectorSizeGet()));
    ASSERT((ui32SectorAddress & (FlashSectorSizeGet() - 1)) == 00);

    ui32SectorProtect = FLASH_NO_PROTECT;
    ui32SectorNumber = (ui32SectorAddress - FLASHMEM_BASE) / FlashSectorSizeGet();

    if(ui32SectorNumber <= 31)
    {
        if((HWREG(FLASH_BASE + FLASH_O_FSM_BSLE0) & (1 << ui32SectorNumber)) &&
                (HWREG(FLASH_BASE + FLASH_O_FSM_BSLP0) & (1 << ui32SectorNumber)))
        {
            ui32SectorProtect = FLASH_WRITE_PROTECT;
        }
    }
    else if(ui32SectorNumber <= 63)
    {
        if((HWREG(FLASH_BASE + FLASH_O_FSM_BSLE1) &
                (1 << (ui32SectorNumber & 0x1F))) &&
                (HWREG(FLASH_BASE + FLASH_O_FSM_BSLP1) &
                 (1 << (ui32SectorNumber & 0x1F))))
        {
            ui32SectorProtect = FLASH_WRITE_PROTECT;
        }
    }

    return(ui32SectorProtect);
}

uint32_t FlashProtectionSave(uint32_t ui32SectorAddress) {
    uint32_t ui32ErrorReturn;
    uint32_t ui32SectorNumber;
    uint32_t ui32CcfgSectorAddr;
    uint32_t ui32ProgBuf;

    ui32ErrorReturn = FAPI_STATUS_SUCCESS;

    // Check the arguments.
    ASSERT(ui32SectorAddress <= (FLASHMEM_BASE + FlashSizeGet() -
                                 FlashSectorSizeGet()));
    ASSERT((ui32SectorAddress & (FlashSectorSizeGet() - 1)) == 00);

    if(FlashProtectionGet(ui32SectorAddress) == FLASH_WRITE_PROTECT)
    {
        // Find sector number for specified sector.
        ui32SectorNumber = (ui32SectorAddress - FLASHMEM_BASE) / FlashSectorSizeGet();
        ui32CcfgSectorAddr = FLASHMEM_BASE + FlashSizeGet() - FlashSectorSizeGet();

        // Adjust CCFG address to the 32-bit CCFG word holding the
        // protect-bit for the specified sector.
        ui32CcfgSectorAddr += (((ui32SectorNumber >> 5) * 4) + CCFG_OFFSET_SECT_PROT);

        // Find value to program by setting the protect-bit which
        // corresponds to specified sector number, to 0.
        // Leave other protect-bits unchanged.
        ui32ProgBuf = (~(1 << (ui32SectorNumber & 0x1F))) &
                                   *(uint32_t *)ui32CcfgSectorAddr;

        ui32ErrorReturn = FlashProgram((uint8_t*)&ui32ProgBuf, ui32CcfgSectorAddr,
                                       CCFG_SIZE_SECT_PROT);
    }

    // Return status.
    return(ui32ErrorReturn);
}

uint32_t FlashSectorErase(uint32_t ui32SectorAddress) {
    // The below code part constitutes the variant of the FlashSectorErase()
    // function that is located in ROM. The source code of this variant is not
    // visible in internal or external driverlib. The source code is
    // only compiled during a ROM build.
    // The two above code parts (seperated by compile switches) constitute wrapper
    // functions which both call this ROM variant of the function.
    // The ROM variant is called by referrencing it directly through the ROM API table.
    uint32_t ui32ErrorReturn;
    uint32_t ui32Error;
    uint32_t ui32SectorBit;
    uint32_t ui32SectorNumber;

    // Check the arguments.
    ASSERT(ui32SectorAddress <= (FLASHMEM_BASE + FlashSizeGet() -
                                 FlashSectorSizeGet()));
    ASSERT((ui32SectorAddress & (FlashSectorSizeGet() - 1)) == 00);

    // Enable all sectors for erase.
    EnableSectorsForWrite();

    // Check the arguments.
    if((ui32SectorAddress >
        (FLASHMEM_BASE + FlashSizeGet() - FlashSectorSizeGet())) ||
       ((ui32SectorAddress & (FlashSectorSizeGet() - 1)) != 00))
    {
        // Invalid arguments. Exit function!
        FlashDisableSectorsForWrite();
        return (FAPI_STATUS_INCORRECT_DATABUFFER_LENGTH);
    }

    // Clear the Status register.
    IssueFsmCommand(FAPI_CLEAR_STATUS);

    // Unprotect sector to be erased.
    ui32SectorNumber = (ui32SectorAddress - FLASHMEM_BASE) / FlashSectorSizeGet();
    ui32SectorBit = 1 << (ui32SectorNumber & 0x1F);
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    if(ui32SectorNumber < 0x20)
    {
        HWREG(FLASH_BASE + FLASH_O_FSM_SECTOR1) = ~ui32SectorBit;
    }
    else
    {
        HWREG(FLASH_BASE + FLASH_O_FSM_SECTOR2) = ~ui32SectorBit;
    }
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Write the address to the FSM.
    HWREG(FLASH_BASE + FLASH_O_FADDR) = ui32SectorAddress + ADDR_OFFSET;

    // Issue the sector erase command to the FSM.
    IssueFsmCommand(FAPI_ERASE_SECTOR);

    // Wait for erase to finish.
    while(FlashCheckFsmForReady() == FAPI_STATUS_FSM_BUSY)
    {
    }

    // Update status.
    ui32ErrorReturn = FlashCheckFsmForError();

    // Disable sectors for erase.
    FlashDisableSectorsForWrite();

    // Check if flash top sector was erased.
    if(ui32SectorAddress == (FLASHMEM_BASE + FlashSizeGet() -
                             FlashSectorSizeGet()))
    {
        // Program security data to default values in the customer configuration
        // area within the flash top sector.
        ui32Error = FlashProgram((uint8_t *)g_pui8CcfgDefaultSec,
                                 (ui32SectorAddress + CCFG_OFFSET_SECURITY),
                                 CCFG_SIZE_SECURITY);

        if((ui32Error != FAPI_STATUS_SUCCESS) &&
                (ui32ErrorReturn == FAPI_STATUS_SUCCESS))
        {
            ui32ErrorReturn = ui32Error;
        }
}

    // Return status of operation.
    return(ui32ErrorReturn);
}

uint32_t FlashBankErase(bool bForcePrecondition) {
    uint32_t ui32ErrorReturn;
    uint32_t ui32SectorAddress;
    uint32_t ui32RegVal;

    // Enable all sectors for erase.
    EnableSectorsForWrite();

    // Clear the Status register.
    IssueFsmCommand(FAPI_CLEAR_STATUS);

    // Enable erase of all sectors and enable precondition if required.
    ui32RegVal = HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE);
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_SECTOR1) = 0x00000000;
    HWREG(FLASH_BASE + FLASH_O_FSM_SECTOR2) = 0x00000000;
    if(bForcePrecondition)
    {
        HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) |=
            FLASH_FSM_ST_MACHINE_DO_PRECOND;
    }
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Issue the bank erase command to the FSM.
    IssueFsmCommand(FAPI_ERASE_BANK);

    // Wait for erase to finish.
    while(FlashCheckFsmForReady() == FAPI_STATUS_FSM_BUSY)
    {
    }

    // Update status.
    ui32ErrorReturn = FlashCheckFsmForError();

    // Disable sectors for erase.
    FlashDisableSectorsForWrite();

    // Set configured precondition mode since it may have been forced on.
    if(!(ui32RegVal & FLASH_FSM_ST_MACHINE_DO_PRECOND))
    {
        HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
        HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &=
            ~FLASH_FSM_ST_MACHINE_DO_PRECOND;
        HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;
    }

    // Program security data to default values in the customer configuration
    // area within the flash top sector if erase was successful.
    if(ui32ErrorReturn == FAPI_STATUS_SUCCESS)
    {
        ui32SectorAddress = FLASHMEM_BASE + FlashSizeGet() - FlashSectorSizeGet();
        ui32ErrorReturn = FlashProgram((uint8_t *)g_pui8CcfgDefaultSec,
                                       (ui32SectorAddress + CCFG_OFFSET_SECURITY),
                                       CCFG_SIZE_SECURITY);
    }

    // Return status of operation.
    return(ui32ErrorReturn);
}

uint32_t FlashhOtpEngrErase(void) {
    uint32_t ui32ErrorReturn;
    uint32_t ui32RegVal;

    // Enable all sectors for erase.
    EnableSectorsForWrite();

    // Clear the Status register.
    IssueFsmCommand(FAPI_CLEAR_STATUS);

    // Disable OTP protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;
    HWREG(FLASH_BASE + FLASH_O_FBAC) |= FLASH_FBAC_OTPPROTDIS;
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;

    // Enable test commands.
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
    HWREG(FLASH_BASE + FLASH_O_FTCTL) |= FLASH_FTCTL_TEST_EN;
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;

    // Set address to OTP.
    HWREG(FLASH_BASE + FLASH_O_FADDR) = 0xF0000000;

    // Enable for FSM test commands and erase precondition.
    ui32RegVal = HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE);
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) |=
        (FLASH_FSM_ST_MACHINE_CMD_EN | FLASH_FSM_ST_MACHINE_DO_PRECOND);
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Issue the erase command to the FSM.
    IssueFsmCommand(FAPI_ERASE_OTP);

    // Wait for erase to finish.
    while(FlashCheckFsmForReady() == FAPI_STATUS_FSM_BUSY)
    {
    }

    // Update status.
    ui32ErrorReturn = FlashCheckFsmForError();

    // Disable sectors for erase.
    FlashDisableSectorsForWrite();

    // Disable test commands.
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
    HWREG(FLASH_BASE + FLASH_O_FTCTL) &= ~FLASH_FTCTL_TEST_EN;
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;

    // Renable OTP protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;
    HWREG(FLASH_BASE + FLASH_O_FBAC) &= ~FLASH_FBAC_OTPPROTDIS;
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;

    // Disable FSM test command mode.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &= ~FLASH_FSM_ST_MACHINE_CMD_EN;

    // Set configured precondition mode since it may have been changed.
    if(!(ui32RegVal & FLASH_FSM_ST_MACHINE_DO_PRECOND))
    {
        HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &=
            ~FLASH_FSM_ST_MACHINE_DO_PRECOND;
    }
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Return status of operation.
    return(ui32ErrorReturn);
}

uint32_t FlashProgram(uint8_t *pui8DataBuffer, uint32_t ui32Address, uint32_t ui32Count) {
    // The below code part constitutes the variant of the FlashProgram() function
    // that is located in ROM. The source code of this variant is not visible in
    // internal or external driverlib. The source code is only compiled during a ROM
    // build.
    // The two above code parts (seperated by compile switches) constitute wrapper
    // functions which both call this ROM variant of the function.
    // The ROM variant is called by referrencing it directly through the ROM API table.
    uint32_t ui32StartIndex;
    uint32_t ui32StopIndex;
    uint32_t ui32Index;
    uint8_t ui8BankWidth;
    uint8_t ui8NoOfBytes;
    tFwpWriteByte *oFwpWriteByte;
    uint32_t ui32ErrorReturn;

    // Check the arguments.
    ASSERT((ui32Address + ui32Count) <= (FLASHMEM_BASE + FlashSizeGet()));

    // Enable sectors for programming.
    EnableSectorsForWrite();
    oFwpWriteByte = FWPWRITE_BYTE_ADDRESS;

    // Check the arguments.
    if((ui32Address + ui32Count) > (FLASHMEM_BASE + FlashSizeGet()))
    {
        // Invalid arguments. Exit function!
        FlashDisableSectorsForWrite();
        return (FAPI_STATUS_INCORRECT_DATABUFFER_LENGTH);
    }

    // Set the status to indicate success.
    ui32ErrorReturn = FAPI_STATUS_SUCCESS;

    // Find flash bank width in number of bytes.
    ui8BankWidth =
        (uint8_t)(((HWREG(FLASH_BASE + FLASH_O_FCFG_BANK) &
                    FLASH_FCFG_BANK_MAIN_BANK_WIDTH_M) >>
                   FLASH_FCFG_BANK_MAIN_BANK_WIDTH_S) >> 3);

    // Loop over the bytes to be programmed.
    while(ui32Count)
    {
        // Setup the start position within the write data registers.
        ui32StartIndex = ui32Address & (uint32_t)(ui8BankWidth - 1);

        // Setup number of bytes to program.
        ui8NoOfBytes = ui8BankWidth - ui32StartIndex;
        if(ui8NoOfBytes > ui32Count)
        {
            ui8NoOfBytes = ui32Count;
        }

        // Clear the Status register.
        IssueFsmCommand(FAPI_CLEAR_STATUS);

        // Write address to FADDR register.
        HWREG(FLASH_BASE + FLASH_O_FADDR) = ui32Address + ADDR_OFFSET;

        // Setup the stop position within the write data registers.
        ui32StopIndex = ui32StartIndex + (uint32_t)(ui8NoOfBytes - 1);

        // Write each byte to the FWPWrite registers.
        for(ui32Index = ui32StartIndex; ui32Index <= ui32StopIndex; ui32Index++)
        {
            oFwpWriteByte[ui32Index] = *(pui8DataBuffer++);
        }

        // Issue the Program command to the FSM.
        IssueFsmCommand(FAPI_PROGRAM_DATA);

        // Wait until the word has been programmed.
        while(FlashCheckFsmForReady() == FAPI_STATUS_FSM_BUSY)
        {
        }

        // Exit if an access violation occurred.
        ui32ErrorReturn = FlashCheckFsmForError();
        if(ui32ErrorReturn != FAPI_STATUS_SUCCESS)
        {
            break;
        }

        // Prepare for next data burst.
        ui32Count   -= ((ui32StopIndex - ui32StartIndex) + 1);
        ui32Address += ((ui32StopIndex - ui32StartIndex) + 1);
    }

    // Disable sectors for programming.
    FlashDisableSectorsForWrite();

    // Return status of operation.
    return(ui32ErrorReturn);
}

uint32_t FlashProgramNowait(uint32_t ui32StartAddress, uint8_t *pui8DataBuffer, uint8_t ui8NoOfBytes) {
    uint32_t     ui32StartIndex;
    uint32_t     ui32StopIndex;
    uint32_t     ui32Index;
    uint32_t     ui32BankWidth;
    uint32_t     ui32ErrorReturn;
    tFwpWriteByte    *oFwpWriteByte;

    // Check the arguments.
    ASSERT((ui32StartAddress + ui8NoOfBytes) <= (FLASHMEM_BASE + FlashSizeGet()));

    // Enable sectors for programming.
    EnableSectorsForWrite();
    oFwpWriteByte = FWPWRITE_BYTE_ADDRESS;

    // Check the arguments.
    if((ui32StartAddress + ui8NoOfBytes) > (FLASHMEM_BASE + FlashSizeGet()))
    {
        // Invalid arguments. Exit function!
        FlashDisableSectorsForWrite();
        return (FAPI_STATUS_INCORRECT_DATABUFFER_LENGTH);
    }

    // Set status to indicate success
    ui32ErrorReturn  = FAPI_STATUS_SUCCESS;

    // Find flash bank width in number of bytes.
    ui32BankWidth = (((HWREG(FLASH_BASE + FLASH_O_FCFG_BANK) &
                       FLASH_FCFG_BANK_MAIN_BANK_WIDTH_M) >>
                      FLASH_FCFG_BANK_MAIN_BANK_WIDTH_S) >> 3);

    // Setup the start position within the write data registers.
    ui32StartIndex = ui32StartAddress & (ui32BankWidth - 1);

    // Check to see if there is more data in the buffer than the register.
    // width.
    if((ui8NoOfBytes == 0) || ((ui32StartIndex + ui8NoOfBytes) > ui32BankWidth))
    {
        ui32ErrorReturn = FAPI_STATUS_INCORRECT_DATABUFFER_LENGTH;
    }

    if(ui32ErrorReturn == FAPI_STATUS_SUCCESS)
    {
        // Clear the Status register.
        IssueFsmCommand(FAPI_CLEAR_STATUS);

        // Write address to FADDR register.
        HWREG(FLASH_BASE + FLASH_O_FADDR) = ui32StartAddress + ADDR_OFFSET;

        // Setup the stop position within the write data registers.
        ui32StopIndex  = ui32StartIndex + (uint32_t)(ui8NoOfBytes - 1);

        // Write each byte to the FWPWrite registers.
        for(ui32Index = ui32StartIndex; ui32Index <= ui32StopIndex; ui32Index++)
        {
            oFwpWriteByte[ui32Index] = *(pui8DataBuffer++);
}

        // Issue the Program command to the FSM.
        IssueFsmCommand(FAPI_PROGRAM_DATA);
    }

    // Return the function status.
    return(ui32ErrorReturn);
}

bool FlashEfuseReadRow(uint32_t *pui32EfuseData, uint32_t ui32RowAddress) {
    bool bStatus;

    // Make sure the clock for the efuse is enabled
    HWREG(FLASH_BASE + FLASH_O_CFG) &= ~FLASH_CFG_DIS_EFUSECLK;

    // Set timing for EFUSE read operations.
    HWREG(FLASH_BASE + FLASH_O_EFUSEREAD) |= ((5 << FLASH_EFUSEREAD_READCLOCK_S) &
            FLASH_EFUSEREAD_READCLOCK_M);

    // Clear status register.
    HWREG(FLASH_BASE + FLASH_O_EFUSEERROR) = 0;

    // Select the FuseROM block 0.
    HWREG(FLASH_BASE + FLASH_O_EFUSEADDR) = 0x00000000;

    // Start the read operation.
    HWREG(FLASH_BASE + FLASH_O_EFUSE) =
        (DUMPWORD_INSTR << FLASH_EFUSE_INSTRUCTION_S) |
        (ui32RowAddress & FLASH_EFUSE_DUMPWORD_M);

    // Wait for operation to finish.
    while(!(HWREG(FLASH_BASE + FLASH_O_EFUSEERROR) & FLASH_EFUSEERROR_DONE))
    {
    }

    // Check if error reported.
    if(HWREG(FLASH_BASE + FLASH_O_EFUSEERROR) & FLASH_EFUSEERROR_CODE_M)
    {
        // Set error status.
        bStatus = 1;

        // Clear data.
        *pui32EfuseData = 0;
    }
    else
    {
        // Set ok status.
        bStatus = 0;

        // No error. Get data from data register.
        *pui32EfuseData = HWREG(FLASH_BASE + FLASH_O_DATALOWER);
    }

    // Disable the efuse clock to conserve power
    HWREG(FLASH_BASE + FLASH_O_CFG) |= FLASH_CFG_DIS_EFUSECLK;

    // Return the data.
    return(bStatus);
}

uint32_t FlashProgramPattern(uint32_t ui32SectorAddress, uint32_t ui32DataPattern, bool bInvertData) {
    uint8_t ui8Index;
    uint8_t ui8BankWidth;
    tFwpWriteByte *oFwpWriteByte;
    uint32_t ui32ErrorReturn;

    // Check the arguments.
    ASSERT(ui32SectorAddress <= (FLASHMEM_BASE + FlashSizeGet() -
                                 FlashSectorSizeGet()));
    ASSERT((ui32SectorAddress & (FlashSectorSizeGet() - 1)) == 00);

    // Enable sectors for programming.
    EnableSectorsForWrite();
    oFwpWriteByte = FWPWRITE_BYTE_ADDRESS;

    // Check the arguments.
    if((ui32SectorAddress >
       (FLASHMEM_BASE + FlashSizeGet() - FlashSectorSizeGet())) ||
       ((ui32SectorAddress & (FlashSectorSizeGet() - 1)) != 00))
    {
        // Invalid arguments. Exit function!
        FlashDisableSectorsForWrite();
        return (FAPI_STATUS_INCORRECT_DATABUFFER_LENGTH);
    }

    // Find flash bank width in number of bytes.
    ui8BankWidth =
        (uint8_t)(((HWREG(FLASH_BASE + FLASH_O_FCFG_BANK) &
                    FLASH_FCFG_BANK_MAIN_BANK_WIDTH_M) >>
                   FLASH_FCFG_BANK_MAIN_BANK_WIDTH_S) >> 3);

    // Clear the Status register.
    IssueFsmCommand(FAPI_CLEAR_STATUS);

    // Write address to FADDR register.
    HWREG(FLASH_BASE + FLASH_O_FADDR) = ui32SectorAddress + ADDR_OFFSET;

    // Write each byte of the pattern to the FWPWrite registers.
    for(ui8Index = 0; ui8Index < ui8BankWidth; ui8Index++)
    {
        oFwpWriteByte[ui8Index] = ui32DataPattern >> ((ui8Index * 8) &
                                  (PATTERN_BITS - 1));
    }

    // Enable for FSM test command and enable the Invert Data option if
    // required.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) |= FLASH_FSM_ST_MACHINE_CMD_EN;
    if(bInvertData)
    {
        HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) |= FLASH_FSM_ST_MACHINE_INV_DATA;
    }
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Issue the Program command to the FSM.
    IssueFsmCommand(FAPI_PROGRAM_SECTOR);

    // Wait until the sector has been programmed.
    while(FlashCheckFsmForReady() == FAPI_STATUS_FSM_BUSY)
    {
    }

    // Update status of the program operation.
    ui32ErrorReturn = FlashCheckFsmForError();

    // Disable sectors for programming.
    FlashDisableSectorsForWrite();

    // Disable FSM test command mode and the Invert Data option.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &= ~FLASH_FSM_ST_MACHINE_CMD_EN;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &= ~FLASH_FSM_ST_MACHINE_INV_DATA;
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Return status of operation.
    return(ui32ErrorReturn);
}

uint32_t FlashProgramEngr(uint8_t *pui8DataBuffer, uint32_t ui32AddressOffset, uint32_t ui32Count) {
    uint32_t ui32StartIndex;
    uint32_t ui32StopIndex;
    uint32_t ui32Index;
    uint8_t ui8BankWidth;
    uint8_t ui8NoOfBytes;
    tFwpWriteByte *oFwpWriteByte;
    uint32_t ui32ErrorReturn;

    // Check the arguments.
    ASSERT((ui32AddressOffset + ui32Count) <= 2048);
    // Enable sectors for programming.
    EnableSectorsForWrite();
    oFwpWriteByte = FWPWRITE_BYTE_ADDRESS;

    // Check the arguments.
    if((ui32AddressOffset + ui32Count) > 2048)
    {
        // Invalid arguments. Exit function!
        FlashDisableSectorsForWrite();
        return (FAPI_STATUS_INCORRECT_DATABUFFER_LENGTH);
    }

    // Set the status to indicate success.
    ui32ErrorReturn = FAPI_STATUS_SUCCESS;

    // Find flash bank width in number of bytes.
    ui8BankWidth =
        (uint8_t)(((HWREG(FLASH_BASE + FLASH_O_FCFG_BANK) &
                    FLASH_FCFG_BANK_MAIN_BANK_WIDTH_M) >>
                   FLASH_FCFG_BANK_MAIN_BANK_WIDTH_S) >> 3);

    // Disable OTP protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;
    HWREG(FLASH_BASE + FLASH_O_FBAC) |= FLASH_FBAC_OTPPROTDIS;
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;

    // Enable test commands.
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
    HWREG(FLASH_BASE + FLASH_O_FTCTL) |= FLASH_FTCTL_TEST_EN;
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;

    // Enable for FSM test command.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) |= FLASH_FSM_ST_MACHINE_CMD_EN;
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Loop over the bytes to be programmed.
    while(ui32Count)
    {
        // Setup the start position within the write data registers.
        ui32StartIndex = ui32AddressOffset & (uint32_t)(ui8BankWidth - 1);

        // Setup number of bytes to program.
        ui8NoOfBytes = ui8BankWidth - ui32StartIndex;
        if(ui8NoOfBytes > ui32Count)
        {
            ui8NoOfBytes = ui32Count;
        }

        // Clear the Status register.
        IssueFsmCommand(FAPI_CLEAR_STATUS);

        // Write address to FADDR register.
        HWREG(FLASH_BASE + FLASH_O_FADDR) = ui32AddressOffset + 0xF0080000;

        // Setup the stop position within the write data registers.
        ui32StopIndex = ui32StartIndex + (uint32_t)(ui8NoOfBytes - 1);

        // Write each byte to the FWPWrite registers.
        for(ui32Index = ui32StartIndex; ui32Index <= ui32StopIndex; ui32Index++)
        {
            oFwpWriteByte[ui32Index] = *(pui8DataBuffer++);
        }

        // Issue programming command.
        IssueFsmCommand(FAPI_PROGRAM_DATA);

        // Wait until the word has been programmed.
        while(FlashCheckFsmForReady() == FAPI_STATUS_FSM_BUSY)
        {
        }

        // Update error status and exit if an error occurred.
        ui32ErrorReturn = FlashCheckFsmForError();
        if(ui32ErrorReturn != FAPI_STATUS_SUCCESS)
        {
            break;
        }

        // Prepare for next data burst.
        ui32Count   -= ((ui32StopIndex - ui32StartIndex) + 1);
        ui32AddressOffset += ((ui32StopIndex - ui32StartIndex) + 1);
    }

    // Disable sectors for programming.
    FlashDisableSectorsForWrite();

    // Re-enable OTP protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;
    HWREG(FLASH_BASE + FLASH_O_FBAC) &= ~FLASH_FBAC_OTPPROTDIS;
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;

    // Disable test commands.
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
    HWREG(FLASH_BASE + FLASH_O_FTCTL) &= ~FLASH_FTCTL_TEST_EN;
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;

    // Disable FSM test command mode.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &= ~FLASH_FSM_ST_MACHINE_CMD_EN;
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;

    // Return status of operation.
    return(ui32ErrorReturn);
}

void FlashOtpProgramEraseSetup(void) {
    // Disable OTP protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;
    HWREG(FLASH_BASE + FLASH_O_FBAC) |= FLASH_FBAC_OTPPROTDIS;
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;

    // Enable test commands by performing the following steps:
    // - Enable SW Interface mode
    // - Enable for test commands
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x0000AAAA;
    HWREG(FLASH_BASE + FLASH_O_FTCTL) |= FLASH_FTCTL_TEST_EN;
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x000055AA;

    // Enable for FSM test commands.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) |= FLASH_FSM_ST_MACHINE_CMD_EN;
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;
}

void FlashOtpProgramEraseCleanup(void) {
    // Re-enable OTP protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;
    HWREG(FLASH_BASE + FLASH_O_FBAC) &= ~FLASH_FBAC_OTPPROTDIS;
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;

    // Disable test commands and turn off SW interface mode.
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x0000AAAA;
    HWREG(FLASH_BASE + FLASH_O_FTCTL) &= ~FLASH_FTCTL_TEST_EN;
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;

    // Disable FSM test command mode.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &= ~FLASH_FSM_ST_MACHINE_CMD_EN;
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;
}

void FlashDisableSectorsForWrite(void) {
    // Configure flash back to read mode
    SetReadMode();

    // Disable Level 1 Protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;

    // Disable all sectors for erase and programming.
    HWREG(FLASH_BASE + FLASH_O_FBSE) = 0x0000;

    // Enable Level 1 Protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;

    // Protect sectors from sector erase.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;
    HWREG(FLASH_BASE + FLASH_O_FSM_SECTOR1) = 0xFFFFFFFF;
    HWREG(FLASH_BASE + FLASH_O_FSM_SECTOR2) = 0xFFFFFFFF;
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;
}
//*****************************************************************************
//
//! \internal
//! Issues a command to the Flash State Machine.
//!
//! \param eCommand specifies the FSM command.
//!
//! Issues a command to the Flash State Machine.
//!
//! \return None
//
//*****************************************************************************
static void
IssueFsmCommand(tFlashStateCommandsType eCommand)
{
    // Check the arguments.
    ASSERT(
        eCommand == FAPI_ERASE_SECTOR    || eCommand == FAPI_ERASE_BANK ||
        eCommand == FAPI_VALIDATE_SECTOR || eCommand == FAPI_CLEAR_STATUS ||
        eCommand == FAPI_PROGRAM_RESUME  || eCommand == FAPI_ERASE_RESUME ||
        eCommand == FAPI_CLEAR_MORE      || eCommand == FAPI_PROGRAM_SECTOR ||
        eCommand == FAPI_PROGRAM_DATA    || eCommand == FAPI_ERASE_OTP);

    // Enable write to FSM register.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;

    // Issue FSM command.
    HWREG(FLASH_BASE + FLASH_O_FSM_CMD) = eCommand;

    // Start command execute.
    HWREG(FLASH_BASE + FLASH_O_FSM_EXECUTE) = FLASH_CMD_EXEC;

    // Disable write to FSM register.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;
}

//*****************************************************************************
//
//! \internal
//! Enables all sectors for erase and programming on the active bank.
//!
//! This function disables the idle reading power reduction mode, selects the
//! flash bank and enables all sectors for erase and programming on the active
//! bank.
//! Sectors may be protected from programming depending on the value of the
//! FLASH_O_FSM_BSLPx registers.
//! Sectors may be protected from erase depending on the value of the
//! FLASH_O_FSM_BSLEx registers. Additional sector erase protection is set by
//! the FLASH_O_FSM_SECTOR1 register.
//!
//! \return None
//
//*****************************************************************************
static void
EnableSectorsForWrite(void)
{
    // Trim flash module for program/erase operation.
    TrimForWrite();

    // Configure flash to write mode
    SetWriteMode();

    // Select flash bank.
    HWREG(FLASH_BASE + FLASH_O_FMAC) = 0x00;

    // Disable Level 1 Protection.
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = FLASH_FBPROT_PROTL1DIS;

    // Enable all sectors for erase and programming.
    HWREG(FLASH_BASE + FLASH_O_FBSE) = 0xFFFF;

    // Enable Level 1 Protection
    HWREG(FLASH_BASE + FLASH_O_FBPROT) = 0;
}

//*****************************************************************************
//
//! \internal
//! Trims the Flash Bank and Flash Pump for program/erase functionality
//!
//! This trimming will make it possible to perform erase and program operations
//! of the flash. Trim values are loaded from factory configuration area
//! (referred to as FCGF1). The trimming done by this function is valid until
//! reset of the flash module.
//!
//! Some registers shall be written with a value that is a number of FCLK
//! cycles. The trim values controlling these registers have a value of
//! number of half us. FCLK = SysClk / ((RWAIT+1) x 2).
//!
//! In order to calculate the register value for these registers the
//! following calculation must be done:
//!
//!                                        OtpValue                   SysClkMHz
//!                                        -------- us      OtpValue x ---------
//!                                           2                       (RWAIT+1)
//!     RegValue_in_no_of_clk_cycles = ----------------- = ---------------------
//!                                           1                     4
//!                                     --------------
//!                                       SysClkMHz
//!                                      ------------
//!                                      (RWAIT+1)x 2
//!
//! This is equivalent to:
//!
//!                                               16 x SysClkMHz
//!                                    OtpValue x ---------------
//!                                                  (RWAIT+1)
//!     RegValue_in_no_of_clk_cycles = ----------------------------
//!                                               64
//!
//! A scaling factor is set equal to:
//!
//!                     16 x SysClkMHz
//!     ui32FclkScale = --------------
//!                       (RWAIT+1)
//!
//! which gives:
//!
//!                                     OtpValue x ui32FclkScale
//!     RegValue_in_no_of_clk_cycles = ------------------------
//!                                              64
//!
//! \return None.
//
//*****************************************************************************
static void
TrimForWrite(void)
{
    uint32_t ui32Value;
    uint32_t ui32TempVal;
    uint32_t ui32FclkScale;
    uint32_t ui32RWait;

    // Return if flash is already trimmed for program/erase operations.
    if(HWREG(FLASH_BASE + FLASH_O_FWFLAG) & FW_WRT_TRIMMED)
    {
        return;
    }

    //***********************************************************************//
    //                                                                       //
    //                 Configure the FSM registers                           //
    //                                                                       //
    //***********************************************************************//

    // Enable access to the FSM registers.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_ENABLE;

    // Determine the scaling value to be used on timing related trim values.
    // The scaling value is based on the flash module clock frequency and RWAIT
    ui32RWait     = (HWREG(FLASH_BASE + FLASH_O_FRDCTL) &
                     FLASH_FRDCTL_RWAIT_M) >> FLASH_FRDCTL_RWAIT_S;
    ui32FclkScale = (16 * FLASH_MODULE_CLK_FREQ) / (ui32RWait + 1);

    // Configure Program pulse width bits 15:0.
    // (FCFG1 offset 0x188 bits 15:0).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_PROG_EP) &
         FCFG1_FLASH_PROG_EP_PROGRAM_PW_M) >>
         FCFG1_FLASH_PROG_EP_PROGRAM_PW_S;

    ui32Value = ScaleCycleValues(ui32Value, ui32FclkScale);

    HWREG(FLASH_BASE + FLASH_O_FSM_PRG_PW) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PRG_PW) &
         ~FLASH_FSM_PRG_PW_PROG_PUL_WIDTH_M) |
        ((ui32Value << FLASH_FSM_PRG_PW_PROG_PUL_WIDTH_S) &
         FLASH_FSM_PRG_PW_PROG_PUL_WIDTH_M);

    // Configure Erase pulse width bits 31:0.
    // (FCFG1 offset 0x18C bits 31:0).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_ERA_PW) &
         FCFG1_FLASH_ERA_PW_ERASE_PW_M) >>
         FCFG1_FLASH_ERA_PW_ERASE_PW_S;

    ui32Value = ScaleCycleValues(ui32Value, ui32FclkScale);

    HWREG(FLASH_BASE + FLASH_O_FSM_ERA_PW) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_ERA_PW) &
         ~FLASH_FSM_ERA_PW_FSM_ERA_PW_M) |
        ((ui32Value << FLASH_FSM_ERA_PW_FSM_ERA_PW_S) &
         FLASH_FSM_ERA_PW_FSM_ERA_PW_M);


    // Configure no of flash clock cycles from EXECUTEZ going low to the
    // verify data can be read in the program verify mode bits 7:0.
    // (FCFG1 offset 0x174 bits 23:16).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_C_E_P_R) &
         FCFG1_FLASH_C_E_P_R_PV_ACCESS_M) >>
         FCFG1_FLASH_C_E_P_R_PV_ACCESS_S;

    ui32Value = ScaleCycleValues(ui32Value, ui32FclkScale);

    HWREG(FLASH_BASE + FLASH_O_FSM_EX_VAL) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_EX_VAL) &
         ~FLASH_FSM_EX_VAL_EXE_VALD_M) |
        ((ui32Value << FLASH_FSM_EX_VAL_EXE_VALD_S) &
         FLASH_FSM_EX_VAL_EXE_VALD_M);

    // Configure the number of flash clocks from the start of the Read mode at
    // the end of the operations until the FSM clears the BUSY bit in FMSTAT.
    // (FCFG1 offset 0x178 bits 23:16).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_P_R_PV) &
         FCFG1_FLASH_P_R_PV_RH_M) >>
         FCFG1_FLASH_P_R_PV_RH_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_RD_H) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_RD_H) &
         ~FLASH_FSM_RD_H_RD_H_M) |
        ((ui32Value << FLASH_FSM_RD_H_RD_H_S) &
         FLASH_FSM_RD_H_RD_H_M);

    // Configure Program hold time
    // (FCFG1 offset 0x178 bits 31:24).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_P_R_PV) &
         FCFG1_FLASH_P_R_PV_PH_M) >>
         FCFG1_FLASH_P_R_PV_PH_S;

    ui32Value = ScaleCycleValues(ui32Value, ui32FclkScale);

    HWREG(FLASH_BASE + FLASH_O_FSM_P_OH) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_P_OH) &
         ~FLASH_FSM_P_OH_PGM_OH_M) |
        ((ui32Value << FLASH_FSM_P_OH_PGM_OH_S) &
         FLASH_FSM_P_OH_PGM_OH_M);

    // Configure Erase hold time
    // (FCFG1 offset 0x17C bits 31:24).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_EH_SEQ) &
         FCFG1_FLASH_EH_SEQ_EH_M) >>
         FCFG1_FLASH_EH_SEQ_EH_S;

    ui32Value = ScaleCycleValues(ui32Value, ui32FclkScale);

    HWREG(FLASH_BASE + FLASH_O_FSM_ERA_OH) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_ERA_OH) &
         ~FLASH_FSM_ERA_OH_ERA_OH_M) |
        ((ui32Value << FLASH_FSM_ERA_OH_ERA_OH_S) &
         FLASH_FSM_ERA_OH_ERA_OH_M);

    // Configure Program verify row switch time
    // (FCFG1 offset0x178 bits 15:8).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_P_R_PV) &
         FCFG1_FLASH_P_R_PV_PVH_M) >>
         FCFG1_FLASH_P_R_PV_PVH_S;

    ui32Value = ScaleCycleValues(ui32Value, ui32FclkScale);

    HWREG(FLASH_BASE + FLASH_O_FSM_PE_VH) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PE_VH) &
         ~FLASH_FSM_PE_VH_PGM_VH_M) |
        ((ui32Value << FLASH_FSM_PE_VH_PGM_VH_S) &
         FLASH_FSM_PE_VH_PGM_VH_M);

    // Configure Program Operation Setup time
    // (FCFG1 offset 0x170 bits 31:24).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_E_P) &
                 FCFG1_FLASH_E_P_PSU_M) >>
                 FCFG1_FLASH_E_P_PSU_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_PE_OSU) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PE_OSU) &
         ~FLASH_FSM_PE_OSU_PGM_OSU_M) |
        ((ui32Value << FLASH_FSM_PE_OSU_PGM_OSU_S) &
         FLASH_FSM_PE_OSU_PGM_OSU_M);

    // Configure Erase Operation Setup time
    // (FCGF1 offset 0x170 bits 23:16).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_E_P) &
                 FCFG1_FLASH_E_P_ESU_M) >>
                 FCFG1_FLASH_E_P_ESU_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_PE_OSU) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PE_OSU) &
         ~FLASH_FSM_PE_OSU_ERA_OSU_M) |
        ((ui32Value << FLASH_FSM_PE_OSU_ERA_OSU_S) &
         FLASH_FSM_PE_OSU_ERA_OSU_M);

    // Configure Program Verify Setup time
    // (FCFG1 offset 0x170 bits 15:8).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_E_P) &
                 FCFG1_FLASH_E_P_PVSU_M) >>
                 FCFG1_FLASH_E_P_PVSU_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_PE_VSU) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PE_VSU) &
         ~FLASH_FSM_PE_VSU_PGM_VSU_M) |
        ((ui32Value << FLASH_FSM_PE_VSU_PGM_VSU_S) &
         FLASH_FSM_PE_VSU_PGM_VSU_M);

    // Configure Erase Verify Setup time
    // (FCFG1 offset 0x170 bits 7:0).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_E_P) &
                 FCFG1_FLASH_E_P_EVSU_M) >>
                 FCFG1_FLASH_E_P_EVSU_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_PE_VSU) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PE_VSU) &
         ~FLASH_FSM_PE_VSU_ERA_VSU_M) |
        ((ui32Value << FLASH_FSM_PE_VSU_ERA_VSU_S) &
         FLASH_FSM_PE_VSU_ERA_VSU_M);

    // Configure Addr to EXECUTEZ low setup time
    // (FCFG1 offset 0x174 bits 15:12).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_C_E_P_R) &
         FCFG1_FLASH_C_E_P_R_A_EXEZ_SETUP_M) >>
         FCFG1_FLASH_C_E_P_R_A_EXEZ_SETUP_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_CMP_VSU) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_CMP_VSU) &
         ~FLASH_FSM_CMP_VSU_ADD_EXZ_M) |
        ((ui32Value << FLASH_FSM_CMP_VSU_ADD_EXZ_S) &
         FLASH_FSM_CMP_VSU_ADD_EXZ_M);

    // Configure Voltage Status Count
    // (FCFG1 offset 0x17C bits 15:12).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_EH_SEQ) &
         FCFG1_FLASH_EH_SEQ_VSTAT_M) >>
         FCFG1_FLASH_EH_SEQ_VSTAT_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_VSTAT) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_VSTAT) &
         ~FLASH_FSM_VSTAT_VSTAT_CNT_M) |
        ((ui32Value << FLASH_FSM_VSTAT_VSTAT_CNT_S) &
         FLASH_FSM_VSTAT_VSTAT_CNT_M);

    // Configure Repeat Verify action setup
    // (FCFG1 offset 0x174 bits 31:24).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_C_E_P_R) &
         FCFG1_FLASH_C_E_P_R_RVSU_M) >>
         FCFG1_FLASH_C_E_P_R_RVSU_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_EX_VAL) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_EX_VAL) &
         ~FLASH_FSM_EX_VAL_REP_VSU_M) |
        ((ui32Value << FLASH_FSM_EX_VAL_REP_VSU_S) &
         FLASH_FSM_EX_VAL_REP_VSU_M);

    // Configure Maximum Programming Pulses
    // (FCFG1 offset 0x184 bits 15:0).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_PP) &
                 FCFG1_FLASH_PP_MAX_PP_M) >>
                 FCFG1_FLASH_PP_MAX_PP_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_PRG_PUL) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PRG_PUL) &
         ~FLASH_FSM_PRG_PUL_MAX_PRG_PUL_M) |
        ((ui32Value << FLASH_FSM_PRG_PUL_MAX_PRG_PUL_S) &
         FLASH_FSM_PRG_PUL_MAX_PRG_PUL_M);

    // Configure Beginning level for VHVCT used during erase modes
    // (FCFG1 offset 0x180 bits 31:16).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_VHV_E) &
         FCFG1_FLASH_VHV_E_VHV_E_START_M) >>
         FCFG1_FLASH_VHV_E_VHV_E_START_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_PRG_PUL) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_PRG_PUL) &
         ~FLASH_FSM_PRG_PUL_BEG_EC_LEVEL_M) |
        ((ui32Value << FLASH_FSM_PRG_PUL_BEG_EC_LEVEL_S) &
         FLASH_FSM_PRG_PUL_BEG_EC_LEVEL_M);

    // Configure Maximum EC Level
    // (FCFG1 offset 0x2B0 bits 21:18).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA3) &
         FCFG1_FLASH_OTP_DATA3_MAX_EC_LEVEL_M) >>
         FCFG1_FLASH_OTP_DATA3_MAX_EC_LEVEL_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_ERA_PUL) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_ERA_PUL) &
         ~FLASH_FSM_ERA_PUL_MAX_EC_LEVEL_M) |
        ((ui32Value << FLASH_FSM_ERA_PUL_MAX_EC_LEVEL_S) &
         FLASH_FSM_ERA_PUL_MAX_EC_LEVEL_M);

    // Configure Maximum Erase Pulses
    // (FCFG1 offset 0x188 bits 31:16).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_PROG_EP) &
         FCFG1_FLASH_PROG_EP_MAX_EP_M) >>
         FCFG1_FLASH_PROG_EP_MAX_EP_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_ERA_PUL) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_ERA_PUL) &
         ~FLASH_FSM_ERA_PUL_MAX_ERA_PUL_M) |
        ((ui32Value << FLASH_FSM_ERA_PUL_MAX_ERA_PUL_S) &
         FLASH_FSM_ERA_PUL_MAX_ERA_PUL_M);

    // Configure the VHVCT Step Size. This is the number of erase pulses that
    // must be completed for each level before the FSM increments the
    // CUR_EC_LEVEL to the next higher level. Actual erase pulses per level
    // equals (EC_STEP_SIZE +1). The stepping is only needed for the VHVCT
    // voltage.
    // (FCFG1 offset 0x2B0 bits 31:23).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA3) &
         FCFG1_FLASH_OTP_DATA3_EC_STEP_SIZE_M) >>
         FCFG1_FLASH_OTP_DATA3_EC_STEP_SIZE_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_STEP_SIZE) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_STEP_SIZE) &
         ~FLASH_FSM_STEP_SIZE_EC_STEP_SIZE_M) |
        ((ui32Value << FLASH_FSM_STEP_SIZE_EC_STEP_SIZE_S) &
         FLASH_FSM_STEP_SIZE_EC_STEP_SIZE_M);

    // Configure the hight of each EC step. This is the number of counts that
    // the CUR_EC_LEVEL will increment when going to a new level. Actual count
    // size equals (EC_STEP_HEIGHT + 1). The stepping applies only to the VHVCT
    // voltage.
    // The read trim value is decremented by 1 before written to the register
    // since actual counts equals (register value + 1).
    // (FCFG1 offset 0x180 bits 15:0).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_VHV_E) &
         FCFG1_FLASH_VHV_E_VHV_E_STEP_HIGHT_M) >>
         FCFG1_FLASH_VHV_E_VHV_E_STEP_HIGHT_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_EC_STEP_HEIGHT) = ((ui32Value - 1) &
            FLASH_FSM_EC_STEP_HEIGHT_EC_STEP_HEIGHT_M);

    // Configure Precondition used in erase operations
    // (FCFG1 offset 0x2B0 bit 22).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA3) &
         FCFG1_FLASH_OTP_DATA3_DO_PRECOND_M) >>
         FCFG1_FLASH_OTP_DATA3_DO_PRECOND_S;

    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) =
        (HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) &
         ~FLASH_FSM_ST_MACHINE_DO_PRECOND_M) |
        ((ui32Value << FLASH_FSM_ST_MACHINE_DO_PRECOND_S) &
         FLASH_FSM_ST_MACHINE_DO_PRECOND_M);

    // Enable the recommended Good Time function.
    HWREG(FLASH_BASE + FLASH_O_FSM_ST_MACHINE) |=
        FLASH_FSM_ST_MACHINE_ONE_TIME_GOOD;

    // Disable write access to FSM registers.
    HWREG(FLASH_BASE + FLASH_O_FSM_WR_ENA) = FSM_REG_WRT_DISABLE;


    //***********************************************************************//
    //                                                                       //
    //                 Configure the voltage registers                       //
    //                                                                       //
    //***********************************************************************//

    // Unlock voltage registers (0x2080 - 0x2098).
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;

    // Configure voltage level for the specified pump voltage of high
    // voltage supply input during erase operation VHVCT_E and the TRIM13_E
    // (FCFG1 offset 0x190 bits[3:0] and bits[11:8]).
    ui32TempVal = HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_VHV);

    ui32Value  = ((ui32TempVal & FCFG1_FLASH_VHV_TRIM13_E_M)>>
                  FCFG1_FLASH_VHV_TRIM13_E_S) << FLASH_FVHVCT1_TRIM13_E_S;
    ui32Value |= ((ui32TempVal & FCFG1_FLASH_VHV_VHV_E_M)>>
                  FCFG1_FLASH_VHV_VHV_E_S) << FLASH_FVHVCT1_VHVCT_E_S;

    HWREG(FLASH_BASE + FLASH_O_FVHVCT1) = (HWREG(FLASH_BASE + FLASH_O_FVHVCT1) &
         ~(FLASH_FVHVCT1_TRIM13_E_M | FLASH_FVHVCT1_VHVCT_E_M)) | ui32Value;

    // Configure voltage level for the specified pump voltage of high voltage
    // supply input during program verify operation VHVCT_PV and the TRIM13_PV
    // (OTP offset 0x194 bits[19:16] and bits[27:24]).
    ui32TempVal =
             HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_VHV_PV);

    ui32Value  = ((ui32TempVal & FCFG1_FLASH_VHV_PV_TRIM13_PV_M)>>
                  FCFG1_FLASH_VHV_PV_TRIM13_PV_S) <<
                                                     FLASH_FVHVCT1_TRIM13_PV_S;
    ui32Value |= ((ui32TempVal & FCFG1_FLASH_VHV_PV_VHV_PV_M)>>
                  FCFG1_FLASH_VHV_PV_VHV_PV_S) <<
                                                     FLASH_FVHVCT1_VHVCT_PV_S;

    HWREG(FLASH_BASE + FLASH_O_FVHVCT1) = (HWREG(FLASH_BASE + FLASH_O_FVHVCT1) &
         ~(FLASH_FVHVCT1_TRIM13_PV_M | FLASH_FVHVCT1_VHVCT_PV_M)) | ui32Value;

    // Configure voltage level for the specified pump voltage of high voltage
    // supply input during program operation VHVCT_P and TRIM13_P
    // (FCFG1 offset 0x190 bits[19:16] and bits[27:24]).
    ui32TempVal =
                HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_VHV);

    ui32Value = ((ui32TempVal & FCFG1_FLASH_VHV_TRIM13_P_M)>>
                  FCFG1_FLASH_VHV_TRIM13_P_S) << FLASH_FVHVCT2_TRIM13_P_S;
    ui32Value |= ((ui32TempVal & FCFG1_FLASH_VHV_VHV_P_M)>>
                  FCFG1_FLASH_VHV_VHV_P_S) << FLASH_FVHVCT2_VHVCT_P_S;

    HWREG(FLASH_BASE + FLASH_O_FVHVCT2) =
        (HWREG(FLASH_BASE + FLASH_O_FVHVCT2) &
         ~(FLASH_FVHVCT2_TRIM13_P_M | FLASH_FVHVCT2_VHVCT_P_M)) | ui32Value;

    // Configure voltage level for the specified pump voltage of wordline power
    // supply for read mode
    // (FCFG1 offset 0x198 Bits 15:8).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_V) &
                 FCFG1_FLASH_V_V_READ_M) >>
                 FCFG1_FLASH_V_V_READ_S;

    HWREG(FLASH_BASE + FLASH_O_FVREADCT) =
        (HWREG(FLASH_BASE + FLASH_O_FVREADCT) &
         ~FLASH_FVREADCT_VREADCT_M) |
        ((ui32Value << FLASH_FVREADCT_VREADCT_S) &
         FLASH_FVREADCT_VREADCT_M);

    // Configure the voltage level for the VCG 2.5 CT pump voltage
    // (FCFG1 offset 0x194 bits 15:8).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_VHV_PV) &
         FCFG1_FLASH_VHV_PV_VCG2P5_M) >>
         FCFG1_FLASH_VHV_PV_VCG2P5_S;

    HWREG(FLASH_BASE + FLASH_O_FVNVCT) =
        (HWREG(FLASH_BASE + FLASH_O_FVNVCT) &
         ~FLASH_FVNVCT_VCG2P5CT_M) |
        ((ui32Value << FLASH_FVNVCT_VCG2P5CT_S) &
         FLASH_FVNVCT_VCG2P5CT_M);

    // Configure the voltage level for the specified pump voltage of high
    // current power input during program operation
    // (FCFG1 offset 0x198 bits 31:24).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_V) &
                 FCFG1_FLASH_V_VSL_P_M) >>
                 FCFG1_FLASH_V_VSL_P_S;

    HWREG(FLASH_BASE + FLASH_O_FVSLP) =
        (HWREG(FLASH_BASE + FLASH_O_FVSLP) &
         ~FLASH_FVSLP_VSL_P_M) |
        ((ui32Value << FLASH_FVSLP_VSL_P_S) &
         FLASH_FVSLP_VSL_P_M);

    // Configure the voltage level for the specified pump voltage of wordline
    // power supply during programming operations
    // (OTP offset 0x198 bits 23:16).
    ui32Value = (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_V) &
                 FCFG1_FLASH_V_VWL_P_M) >>
                 FCFG1_FLASH_V_VWL_P_S;

    HWREG(FLASH_BASE + FLASH_O_FVWLCT) =
        (HWREG(FLASH_BASE + FLASH_O_FVWLCT) &
         ~FLASH_FVWLCT_VWLCT_P_M) |
        ((ui32Value << FLASH_FVWLCT_VWLCT_P_S) &
         FLASH_FVWLCT_VWLCT_P_M);

    // Configure the pump's TRIM_1P7 port pins.
    // (FCFG1 offset 0x2B0 bits 17:16).
    ui32Value =
        (HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA3) &
         FCFG1_FLASH_OTP_DATA3_TRIM_1P7_M) >>
         FCFG1_FLASH_OTP_DATA3_TRIM_1P7_S;

    HWREG(FLASH_BASE + FLASH_O_FSEQPMP) =
        (HWREG(FLASH_BASE + FLASH_O_FSEQPMP) &
         ~FLASH_FSEQPMP_TRIM_1P7_M) |
        ((ui32Value << FLASH_FSEQPMP_TRIM_1P7_S) &
         FLASH_FSEQPMP_TRIM_1P7_M);

    // Lock the voltage registers.
    HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;

    // Set trimmed flag.
    HWREG(FLASH_BASE + FLASH_O_FWLOCK) = 5;
    HWREG(FLASH_BASE + FLASH_O_FWFLAG) |= FW_WRT_TRIMMED;
    HWREG(FLASH_BASE + FLASH_O_FWLOCK) = 0;
}

//*****************************************************************************
//
//! \internal
//! Used to scale the TI OTP values based on the FClk scaling value.
//!
//! \param ui32SpecifiedTiming
//! \param ui32ScaleValue
//!
//! Used to scale the TI OTP values based on the FClk scaling value.
//!
//! \return Returns the scaled value
//
//*****************************************************************************
static uint32_t
ScaleCycleValues(uint32_t ui32SpecifiedTiming, uint32_t ui32ScaleValue)
{
    return((ui32SpecifiedTiming * ui32ScaleValue) >> 6);
}

//*****************************************************************************
//
//! \internal
//! Used to set flash in read mode.
//!
//! Flash is configured with values loaded from OTP dependent on the current
//! regulator mode.
//!
//! \return None.
//
//*****************************************************************************
static void
SetReadMode(void)
{
    uint32_t ui32TrimValue;
    uint32_t ui32Value;

    // Configure the STANDBY_MODE_SEL, STANDBY_PW_SEL, DIS_STANDBY, DIS_IDLE,
    // VIN_AT_X and VIN_BY_PASS for read mode
    if(HWREG(AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL) &
       AON_PMCTL_PWRCTL_EXT_REG_MODE)
    {
        // Select trim values for external regulator mode:
        // Configure STANDBY_MODE_SEL (OTP offset 0x308 bit 7)
        // COnfigure STANDBY_PW_SEL   (OTP offset 0x308 bit 6:5)
        // Must be done while the register bit field CONFIG.DIS_STANDBY = 1
        HWREG(FLASH_BASE + FLASH_O_CFG) |= FLASH_CFG_DIS_STANDBY;

        ui32TrimValue =
           HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA4);

        ui32Value = ((ui32TrimValue &
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_EXT_RD_M) >>
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_EXT_RD_S) <<
                    FLASH_CFG_STANDBY_MODE_SEL_S;

        ui32Value |= ((ui32TrimValue &
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_EXT_RD_M) >>
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_EXT_RD_S) <<
                     FLASH_CFG_STANDBY_PW_SEL_S;

        // Configure DIS_STANDBY (OTP offset 0x308 bit 4).
        // Configure DIS_IDLE    (OTP offset 0x308 bit 3).
        ui32Value |= ((ui32TrimValue &
                       (FCFG1_FLASH_OTP_DATA4_DIS_STANDBY_EXT_RD_M |
                        FCFG1_FLASH_OTP_DATA4_DIS_IDLE_EXT_RD_M)) >>
                        FCFG1_FLASH_OTP_DATA4_DIS_IDLE_EXT_RD_S) <<
                        FLASH_CFG_DIS_IDLE_S;

        HWREG(FLASH_BASE + FLASH_O_CFG) = (HWREG(FLASH_BASE + FLASH_O_CFG) &
                                           ~(FLASH_CFG_STANDBY_MODE_SEL_M |
                                             FLASH_CFG_STANDBY_PW_SEL_M   |
                                             FLASH_CFG_DIS_STANDBY_M      |
                                             FLASH_CFG_DIS_IDLE_M)) | ui32Value;

        // Check if sample and hold functionality is disabled.
        if(HWREG(FLASH_BASE + FLASH_O_CFG) & FLASH_CFG_DIS_IDLE)
        {
            // Wait for disabled sample and hold functionality to be stable.
            while(!(HWREG(FLASH_BASE + FLASH_O_STAT) & FLASH_STAT_SAMHOLD_DIS))
            {
            }
        }

        // Configure VIN_AT_X (OTP offset 0x308 bits 2:0)
        ui32Value = ((ui32TrimValue &
                      FCFG1_FLASH_OTP_DATA4_VIN_AT_X_EXT_RD_M) >>
                      FCFG1_FLASH_OTP_DATA4_VIN_AT_X_EXT_RD_S) <<
                    FLASH_FSEQPMP_VIN_AT_X_S;

        // Configure VIN_BY_PASS which is dependent on the VIN_AT_X value.
        // If VIN_AT_X = 7 then VIN_BY_PASS should be 0 otherwise
        // VIN_BY_PASS should be 1
        if(((ui32Value & FLASH_FSEQPMP_VIN_AT_X_M) >>
            FLASH_FSEQPMP_VIN_AT_X_S) != 0x7)
        {
            ui32Value |= FLASH_FSEQPMP_VIN_BY_PASS;
        }

        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
        HWREG(FLASH_BASE + FLASH_O_FSEQPMP) =
                                    (HWREG(FLASH_BASE + FLASH_O_FSEQPMP) &
                                     ~(FLASH_FSEQPMP_VIN_BY_PASS_M |
                                       FLASH_FSEQPMP_VIN_AT_X_M))  | ui32Value;
        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;
    }
    else
    {
        // Select trim values for internal regulator mode:
        // Configure STANDBY_MODE_SEL (OTP offset 0x308 bit 15)
        // COnfigure STANDBY_PW_SEL   (OTP offset 0x308 bit 14:13)
        // Must be done while the register bit field CONFIG.DIS_STANDBY = 1
        HWREG(FLASH_BASE + FLASH_O_CFG) |= FLASH_CFG_DIS_STANDBY;

        ui32TrimValue =
           HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA4);

        ui32Value = ((ui32TrimValue &
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_INT_RD_M) >>
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_INT_RD_S) <<
                    FLASH_CFG_STANDBY_MODE_SEL_S;

        ui32Value |= ((ui32TrimValue &
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_INT_RD_M) >>
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_INT_RD_S) <<
                     FLASH_CFG_STANDBY_PW_SEL_S;

        // Configure DIS_STANDBY (OTP offset 0x308 bit 12).
        // Configure DIS_IDLE    (OTP offset 0x308 bit 11).
        ui32Value |= ((ui32TrimValue &
                       (FCFG1_FLASH_OTP_DATA4_DIS_STANDBY_INT_RD_M |
                        FCFG1_FLASH_OTP_DATA4_DIS_IDLE_INT_RD_M)) >>
                        FCFG1_FLASH_OTP_DATA4_DIS_IDLE_INT_RD_S) <<
                        FLASH_CFG_DIS_IDLE_S;

        HWREG(FLASH_BASE + FLASH_O_CFG) = (HWREG(FLASH_BASE + FLASH_O_CFG) &
                                           ~(FLASH_CFG_STANDBY_MODE_SEL_M |
                                             FLASH_CFG_STANDBY_PW_SEL_M   |
                                             FLASH_CFG_DIS_STANDBY_M      |
                                             FLASH_CFG_DIS_IDLE_M)) | ui32Value;

        // Check if sample and hold functionality is disabled.
        if(HWREG(FLASH_BASE + FLASH_O_CFG) & FLASH_CFG_DIS_IDLE)
        {
            // Wait for disabled sample and hold functionality to be stable.
            while(!(HWREG(FLASH_BASE + FLASH_O_STAT) & FLASH_STAT_SAMHOLD_DIS))
            {
            }
        }

        // Configure VIN_AT_X (OTP offset 0x308 bits 10:8)
        ui32Value = (((ui32TrimValue &
                       FCFG1_FLASH_OTP_DATA4_VIN_AT_X_INT_RD_M) >>
                       FCFG1_FLASH_OTP_DATA4_VIN_AT_X_INT_RD_S) <<
                       FLASH_FSEQPMP_VIN_AT_X_S);

        // Configure VIN_BY_PASS which is dependent on the VIN_AT_X value.
        // If VIN_AT_X = 7 then VIN_BY_PASS should be 0 otherwise
        // VIN_BY_PASS should be 1
        if(((ui32Value & FLASH_FSEQPMP_VIN_AT_X_M) >>
            FLASH_FSEQPMP_VIN_AT_X_S) != 0x7)
        {
            ui32Value |= FLASH_FSEQPMP_VIN_BY_PASS;
        }

        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
        HWREG(FLASH_BASE + FLASH_O_FSEQPMP) =
                                    (HWREG(FLASH_BASE + FLASH_O_FSEQPMP) &
                                     ~(FLASH_FSEQPMP_VIN_BY_PASS_M |
                                       FLASH_FSEQPMP_VIN_AT_X_M)) | ui32Value;
        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;
    }
}

//*****************************************************************************
//
//! \internal
//! Used to set flash in write mode.
//!
//! Flash is configured with values loaded from OTP dependent on the current
//! regulator mode.
//!
//! \return None.
//
//*****************************************************************************
static void
SetWriteMode(void)
{
    uint32_t ui32TrimValue;
    uint32_t ui32Value;

    // Configure the STANDBY_MODE_SEL, STANDBY_PW_SEL, DIS_STANDBY, DIS_IDLE,
    // VIN_AT_X and VIN_BY_PASS for program/erase mode
    if(HWREG(AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL) &
       AON_PMCTL_PWRCTL_EXT_REG_MODE)
    {
        // Select trim values for external regulator mode:
        // Configure STANDBY_MODE_SEL (OTP offset 0x308 bit 23)
        // COnfigure STANDBY_PW_SEL   (OTP offset 0x308 bit 22:21)
        // Must be done while the register bit field CONFIG.DIS_STANDBY = 1
        HWREG(FLASH_BASE + FLASH_O_CFG) |= FLASH_CFG_DIS_STANDBY;

        ui32TrimValue =
           HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA4);

        ui32Value = ((ui32TrimValue &
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_EXT_WRT_M) >>
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_EXT_WRT_S) <<
                      FLASH_CFG_STANDBY_MODE_SEL_S;

        ui32Value |= ((ui32TrimValue &
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_EXT_WRT_M) >>
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_EXT_WRT_S) <<
                       FLASH_CFG_STANDBY_PW_SEL_S;

        // Configure DIS_STANDBY (OTP offset 0x308 bit 20).
        // Configure DIS_IDLE    (OTP offset 0x308 bit 19).
        ui32Value |= ((ui32TrimValue &
                       (FCFG1_FLASH_OTP_DATA4_DIS_STANDBY_EXT_WRT_M |
                        FCFG1_FLASH_OTP_DATA4_DIS_IDLE_EXT_WRT_M)) >>
                        FCFG1_FLASH_OTP_DATA4_DIS_IDLE_EXT_WRT_S) <<
                        FLASH_CFG_DIS_IDLE_S;

        HWREG(FLASH_BASE + FLASH_O_CFG) = (HWREG(FLASH_BASE + FLASH_O_CFG) &
                                           ~(FLASH_CFG_STANDBY_MODE_SEL_M |
                                             FLASH_CFG_STANDBY_PW_SEL_M   |
                                             FLASH_CFG_DIS_STANDBY_M      |
                                             FLASH_CFG_DIS_IDLE_M)) | ui32Value;

        // Check if sample and hold functionality is disabled.
        if(HWREG(FLASH_BASE + FLASH_O_CFG) & FLASH_CFG_DIS_IDLE)
        {
            // Wait for disabled sample and hold functionality to be stable.
            while(!(HWREG(FLASH_BASE + FLASH_O_STAT) & FLASH_STAT_SAMHOLD_DIS))
            {
            }
        }

        // Configure VIN_AT_X (OTP offset 0x308 bits 18:16)
        ui32Value = ((ui32TrimValue &
                      FCFG1_FLASH_OTP_DATA4_VIN_AT_X_EXT_WRT_M) >>
                      FCFG1_FLASH_OTP_DATA4_VIN_AT_X_EXT_WRT_S) <<
                      FLASH_FSEQPMP_VIN_AT_X_S;

        // Configure VIN_BY_PASS which is dependent on the VIN_AT_X value.
        // If VIN_AT_X = 7 then VIN_BY_PASS should be 0 otherwise
        // VIN_BY_PASS should be 1
        if(((ui32Value & FLASH_FSEQPMP_VIN_AT_X_M) >>
            FLASH_FSEQPMP_VIN_AT_X_S) != 0x7)
        {
            ui32Value |= FLASH_FSEQPMP_VIN_BY_PASS;
        }

        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
        HWREG(FLASH_BASE + FLASH_O_FSEQPMP) =
                                    (HWREG(FLASH_BASE + FLASH_O_FSEQPMP) &
                                     ~(FLASH_FSEQPMP_VIN_BY_PASS_M |
                                       FLASH_FSEQPMP_VIN_AT_X_M))  | ui32Value;
        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;
    }
    else
    {
        // Select trim values for internal regulator mode:
        // Configure STANDBY_MODE_SEL (OTP offset 0x308 bit 31)
        // COnfigure STANDBY_PW_SEL   (OTP offset 0x308 bit 30:29)
        // Must be done while the register bit field CONFIG.DIS_STANDBY = 1
        HWREG(FLASH_BASE + FLASH_O_CFG) |= FLASH_CFG_DIS_STANDBY;

        ui32TrimValue =
           HWREG(FLASH_CFG_BASE + FCFG1_OFFSET + FCFG1_O_FLASH_OTP_DATA4);

        ui32Value = ((ui32TrimValue &
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_INT_WRT_M) >>
                      FCFG1_FLASH_OTP_DATA4_STANDBY_MODE_SEL_INT_WRT_S) <<
                      FLASH_CFG_STANDBY_MODE_SEL_S;

        ui32Value |= ((ui32TrimValue &
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_INT_WRT_M) >>
                       FCFG1_FLASH_OTP_DATA4_STANDBY_PW_SEL_INT_WRT_S) <<
                       FLASH_CFG_STANDBY_PW_SEL_S;

        // Configure DIS_STANDBY (OTP offset 0x308 bit 28).
        // Configure DIS_IDLE    (OTP offset 0x308 bit 27).
        ui32Value |= ((ui32TrimValue &
                      (FCFG1_FLASH_OTP_DATA4_DIS_STANDBY_INT_WRT_M |
                       FCFG1_FLASH_OTP_DATA4_DIS_IDLE_INT_WRT_M)) >>
                       FCFG1_FLASH_OTP_DATA4_DIS_IDLE_INT_WRT_S) <<
                       FLASH_CFG_DIS_IDLE_S;


        HWREG(FLASH_BASE + FLASH_O_CFG) = (HWREG(FLASH_BASE + FLASH_O_CFG) &
                                           ~(FLASH_CFG_STANDBY_MODE_SEL_M |
                                             FLASH_CFG_STANDBY_PW_SEL_M   |
                                             FLASH_CFG_DIS_STANDBY_M      |
                                             FLASH_CFG_DIS_IDLE_M)) | ui32Value;

        // Check if sample and hold functionality is disabled.
        if(HWREG(FLASH_BASE + FLASH_O_CFG) & FLASH_CFG_DIS_IDLE)
        {
            // Wait for disabled sample and hold functionality to be stable.
            while(!(HWREG(FLASH_BASE + FLASH_O_STAT) & FLASH_STAT_SAMHOLD_DIS))
            {
            }
        }

        // Configure VIN_AT_X (OTP offset 0x308 bits 26:24)
        ui32Value = ((ui32TrimValue &
                      FCFG1_FLASH_OTP_DATA4_VIN_AT_X_INT_WRT_M) >>
                      FCFG1_FLASH_OTP_DATA4_VIN_AT_X_INT_WRT_S) <<
                      FLASH_FSEQPMP_VIN_AT_X_S;

        // Configure VIN_BY_PASS which is dependent on the VIN_AT_X value.
        // If VIN_AT_X = 7 then VIN_BY_PASS should be 0 otherwise
        // VIN_BY_PASS should be 1
        if(((ui32Value & FLASH_FSEQPMP_VIN_AT_X_M) >>
            FLASH_FSEQPMP_VIN_AT_X_S) != 0x7)
        {
            ui32Value |= FLASH_FSEQPMP_VIN_BY_PASS;
        }

        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0xAAAA;
        HWREG(FLASH_BASE + FLASH_O_FSEQPMP) =
                                    (HWREG(FLASH_BASE + FLASH_O_FSEQPMP) &
                                     ~(FLASH_FSEQPMP_VIN_BY_PASS_M |
                                       FLASH_FSEQPMP_VIN_AT_X_M)) | ui32Value;
        HWREG(FLASH_BASE + FLASH_O_FLOCK) = 0x55AA;
    }
}

void I2CMasterInitExpClk(uint32_t ui32Base, uint32_t ui32I2CClk, bool bFast) {
    uint32_t ui32SCLFreq;
    uint32_t ui32TPR;

    // Check the arguments.
    ASSERT(I2CBaseValid(ui32Base));

    // Must enable the device before doing anything else.
    I2CMasterEnable(I2C0_BASE);

    // Get the desired SCL speed.
    if(bFast == true)
    {
        ui32SCLFreq = 400000;
    }
    else
    {
        ui32SCLFreq = 100000;
    }

    // Compute the clock divider that achieves the fastest speed less than or
    // equal to the desired speed. The numerator is biased to favor a larger
    // clock divider so that the resulting clock is always less than or equal
    // to the desired clock, never greater.
    ui32TPR = ((ui32I2CClk + (2 * 10 * ui32SCLFreq) - 1) / (2 * 10 * ui32SCLFreq)) - 1;
    HWREG(I2C0_BASE + I2C_O_MTPR) = ui32TPR;
}

uint32_t I2CMasterErr(uint32_t ui32Base) {
    uint32_t ui32Err;

    // Check the arguments.
    ASSERT(I2CBaseValid(ui32Base));

    // Get the raw error state.
    ui32Err = HWREG(I2C0_BASE + I2C_O_MSTAT);

    // If the I2C master is busy, then all the other status bits are invalid,
    // and there is no error to report.
    if(ui32Err & I2C_MSTAT_BUSY)
    {
        return(I2C_MASTER_ERR_NONE);
    }

    // Check for errors.
    if(ui32Err & (I2C_MSTAT_ERR | I2C_MSTAT_ARBLST))
    {
        return(ui32Err & (I2C_MSTAT_ARBLST | I2C_MSTAT_DATACK_N | I2C_MSTAT_ADRACK_N));
    }
    else
    {
        return(I2C_MASTER_ERR_NONE);
    }
}
//*****************************************************************************
//
//! This is a mapping between priority grouping encodings and the number of
//! preemption priority bits.
//
//*****************************************************************************
static const uint32_t g_pui32Priority[] =
{
    NVIC_APINT_PRIGROUP_0_8, NVIC_APINT_PRIGROUP_1_7, NVIC_APINT_PRIGROUP_2_6,
    NVIC_APINT_PRIGROUP_3_5, NVIC_APINT_PRIGROUP_4_4, NVIC_APINT_PRIGROUP_5_3,
    NVIC_APINT_PRIGROUP_6_2, NVIC_APINT_PRIGROUP_7_1
};

//*****************************************************************************
//
//! This is a mapping between interrupt number and the register that contains
//! the priority encoding for that interrupt.
//
//*****************************************************************************
static const uint32_t g_pui32Regs[] =
{
    0, NVIC_SYS_PRI1, NVIC_SYS_PRI2, NVIC_SYS_PRI3, NVIC_PRI0, NVIC_PRI1,
    NVIC_PRI2, NVIC_PRI3, NVIC_PRI4, NVIC_PRI5, NVIC_PRI6, NVIC_PRI7,
    NVIC_PRI8, NVIC_PRI9, NVIC_PRI10, NVIC_PRI11, NVIC_PRI12, NVIC_PRI13
};

void IntPriorityGroupingSet(uint32_t ui32Bits) {
    // Check the arguments.
    ASSERT(ui32Bits < NUM_PRIORITY);

    // Set the priority grouping.
    HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | g_pui32Priority[ui32Bits];
}

uint32_t IntPriorityGroupingGet(void) {
    uint32_t ui32Loop, ui32Value;

    // Read the priority grouping.
    ui32Value = HWREG(NVIC_APINT) & NVIC_APINT_PRIGROUP_M;

    // Loop through the priority grouping values.
    for(ui32Loop = 0; ui32Loop < NUM_PRIORITY; ui32Loop++)
    {
        // Stop looping if this value matches.
        if(ui32Value == g_pui32Priority[ui32Loop])
        {
            break;
        }
    }

    // Return the number of priority bits.
    return(ui32Loop);
}

void IntPrioritySet(uint32_t ui32Interrupt, uint8_t ui8Priority) {
    uint32_t ui32Temp;

    // Check the arguments.
    ASSERT((ui32Interrupt >= 4) && (ui32Interrupt < NUM_INTERRUPTS));
    ASSERT(ui8Priority <= INT_PRI_LEVEL7);

    // Set the interrupt priority.
    ui32Temp = HWREG(g_pui32Regs[ui32Interrupt >> 2]);
    ui32Temp &= ~(0xFF << (8 * (ui32Interrupt & 3)));
    ui32Temp |= ui8Priority << (8 * (ui32Interrupt & 3));
    HWREG(g_pui32Regs[ui32Interrupt >> 2]) = ui32Temp;
}

int32_t IntPriorityGet(uint32_t ui32Interrupt) {
    // Check the arguments.
    ASSERT((ui32Interrupt >= 4) && (ui32Interrupt < NUM_INTERRUPTS));

    // Return the interrupt priority.
    return((HWREG(g_pui32Regs[ui32Interrupt >> 2]) >> (8 * (ui32Interrupt & 3))) &
           0xFF);
}

void IntEnable(uint32_t ui32Interrupt) {
    // Check the arguments.
    ASSERT(ui32Interrupt < NUM_INTERRUPTS);

    // Determine the interrupt to enable.
    if(ui32Interrupt == INT_MEMMANAGE_FAULT)
    {
        // Enable the MemManage interrupt.
        HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_MEM;
    }
    else if(ui32Interrupt == INT_BUS_FAULT)
    {
        // Enable the bus fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_BUS;
    }
    else if(ui32Interrupt == INT_USAGE_FAULT)
    {
        // Enable the usage fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_USAGE;
    }
    else if(ui32Interrupt == INT_SYSTICK)
    {
        // Enable the System Tick interrupt.
        HWREG(NVIC_ST_CTRL) |= NVIC_ST_CTRL_INTEN;
    }
    else if((ui32Interrupt >= 16) && (ui32Interrupt <= 47))
    {
        // Enable the general interrupt.
        HWREG(NVIC_EN0) = 1 << (ui32Interrupt - 16);
    }
    else if(ui32Interrupt >= 48)
    {
        // Enable the general interrupt.
        HWREG(NVIC_EN1) = 1 << (ui32Interrupt - 48);
    }
}

void IntDisable(uint32_t ui32Interrupt) {
    // Check the arguments.
    ASSERT(ui32Interrupt < NUM_INTERRUPTS);

    // Determine the interrupt to disable.
    if(ui32Interrupt == INT_MEMMANAGE_FAULT)
    {
        // Disable the MemManage interrupt.
        HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_MEM);
    }
    else if(ui32Interrupt == INT_BUS_FAULT)
    {
        // Disable the bus fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_BUS);
    }
    else if(ui32Interrupt == INT_USAGE_FAULT)
    {
        // Disable the usage fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_USAGE);
    }
    else if(ui32Interrupt == INT_SYSTICK)
    {
        // Disable the System Tick interrupt.
        HWREG(NVIC_ST_CTRL) &= ~(NVIC_ST_CTRL_INTEN);
    }
    else if((ui32Interrupt >= 16) && (ui32Interrupt <= 47))
    {
        // Disable the general interrupt.
        HWREG(NVIC_DIS0) = 1 << (ui32Interrupt - 16);
    }
    else if(ui32Interrupt >= 48)
    {
        // Disable the general interrupt.
        HWREG(NVIC_DIS1) = 1 << (ui32Interrupt - 48);
    }
}

void IntPendSet(uint32_t ui32Interrupt) {
    // Check the arguments.
    ASSERT(ui32Interrupt < NUM_INTERRUPTS);

    // Determine the interrupt to pend.
    if(ui32Interrupt == INT_NMI_FAULT)
    {
        // Pend the NMI interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_NMI_SET;
    }
    else if(ui32Interrupt == INT_PENDSV)
    {
        // Pend the PendSV interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PEND_SV;
    }
    else if(ui32Interrupt == INT_SYSTICK)
    {
        // Pend the SysTick interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PENDSTSET;
    }
    else if((ui32Interrupt >= 16) && (ui32Interrupt <= 47))
    {
        // Pend the general interrupt.
        HWREG(NVIC_PEND0) = 1 << (ui32Interrupt - 16);
    }
    else if(ui32Interrupt >= 48)
    {
        // Pend the general interrupt.
        HWREG(NVIC_PEND1) = 1 << (ui32Interrupt - 48);
    }
}

bool IntPendGet(uint32_t ui32Interrupt) {
    uint32_t ui32IntPending;

    // Check the arguments.
    ASSERT(ui32Interrupt < NUM_INTERRUPTS);

    // Assume no interrupts are pending.
    ui32IntPending = 0;

    // The lower 16 IRQ vectors are unsupported by this function
    if (ui32Interrupt < 16)
    {

       return 0;
    }

    // Subtract lower 16 irq vectors
    ui32Interrupt -= 16;

    // Check if the interrupt is pending
    ui32IntPending = HWREG(NVIC_PEND0 + (ui32Interrupt / 32));
    ui32IntPending &= (1 << (ui32Interrupt & 31));

    return ui32IntPending ? true : false;
}

void IntPendClear(uint32_t ui32Interrupt) {
    // Check the arguments.
    ASSERT(ui32Interrupt < NUM_INTERRUPTS);

    // Determine the interrupt to unpend.
    if(ui32Interrupt == INT_PENDSV)
    {
        // Unpend the PendSV interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_UNPEND_SV;
    }
    else if(ui32Interrupt == INT_SYSTICK)
    {
        // Unpend the SysTick interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PENDSTCLR;
    }
    else if((ui32Interrupt >= 16) && (ui32Interrupt <= 47))
    {
        // Unpend the general interrupt.
        HWREG(NVIC_UNPEND0) = 1 << (ui32Interrupt - 16);
    }
    else if(ui32Interrupt >= 48)
    {
        // Unpend the general interrupt.
        HWREG(NVIC_UNPEND1) = 1 << (ui32Interrupt - 48);
    }
}

void IOCPortConfigureSet(uint32_t ui32IOId, uint32_t ui32PortId, uint32_t ui32IOConfig) {
    uint32_t ui32Reg;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT(ui32PortId <= IOC_PORT_RFC_GPI1);

    // Get the register address.
    ui32Reg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the port.
    HWREG(ui32Reg) = ui32IOConfig | ui32PortId;
}

uint32_t IOCPortConfigureGet(uint32_t ui32IOId) {
    uint32_t ui32Reg;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);

    // Get the register address.
    ui32Reg = IOC_BASE + ( ui32IOId << 2 );

    // Return the IO configuration.
    return HWREG(ui32Reg);
}

void IOCIOShutdownSet(uint32_t ui32IOId, uint32_t ui32IOShutdown) {
    uint32_t ui32Reg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32IOShutdown == IOC_NO_WAKE_UP) ||
           (ui32IOShutdown == IOC_WAKE_ON_LOW) ||
           (ui32IOShutdown == IOC_WAKE_ON_HIGH));

    // Get the register address.
    ui32Reg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32Reg);
    ui32Config &= ~IOC_IOCFG0_WU_CFG_M;
    HWREG(ui32Reg) = ui32Config | ui32IOShutdown;
}

void IOCIOJTagSet(uint32_t ui32IOId, uint32_t ui32IOJTag) {
    uint32_t ui32Reg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32IOJTag == IOC_JTAG_TDO_ENABLE) ||
           (ui32IOJTag == IOC_JTAG_TDI_ENABLE) ||
           (ui32IOJTag == IOC_JTAG_DISABLE));

    // Get the register address.
    ui32Reg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32Reg);
    ui32Config &= ~(IOC_IOCFG0_TDI | IOC_IOCFG0_TDO);
    HWREG(ui32Reg) = ui32Config | ui32IOJTag;
}

void IOCIOModeSet(uint32_t ui32IOId, uint32_t ui32IOMode) {
    uint32_t ui32Reg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32IOMode == IOC_IOMODE_NORMAL) ||
           (ui32IOMode == IOC_IOMODE_INV) ||
           (ui32IOMode == IOC_IOMODE_OPEN_DRAIN_NORMAL) ||
           (ui32IOMode == IOC_IOMODE_OPEN_DRAIN_INV) ||
           (ui32IOMode == IOC_IOMODE_OPEN_SRC_NORMAL) ||
           (ui32IOMode == IOC_IOMODE_OPEN_SRC_INV));

    // Get the register address.
    ui32Reg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32Reg);
    ui32Config &= ~IOC_IOCFG0_IOMODE_M;
    HWREG(ui32Reg) = ui32Config | ui32IOMode;
}

void IOCIOIntSet(uint32_t ui32IOId, uint32_t ui32Int, uint32_t ui32EdgeDet) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32Int == IOC_INT_ENABLE) ||
           (ui32Int == IOC_INT_DISABLE));
    ASSERT((ui32EdgeDet == IOC_NO_EDGE) ||
           (ui32EdgeDet == IOC_FALLING_EDGE) ||
           (ui32EdgeDet == IOC_RISING_EDGE) ||
           (ui32EdgeDet == IOC_BOTH_EDGES));

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &=  ~(IOC_IOCFG0_EDGE_IRQ_EN | IOC_IOCFG0_EDGE_DET_M);
    HWREG(ui32IOReg) = ui32Config | ((ui32Int ? IOC_IOCFG0_EDGE_IRQ_EN : 0) | ui32EdgeDet);
}

void IOCIOPortPullSet(uint32_t ui32IOId, uint32_t ui32Pull) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the argument.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32Pull == IOC_NO_IOPULL) ||
           (ui32Pull == IOC_IOPULL_UP) ||
           (ui32Pull == IOC_IOPULL_DOWN));

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &= ~IOC_IOCFG0_PULL_CTL_M;
    HWREG(ui32IOReg) = ui32Config | ui32Pull;
}

void IOCIOHystSet(uint32_t ui32IOId, uint32_t ui32Hysteresis) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32Hysteresis == IOC_HYST_ENABLE) ||
           (ui32Hysteresis == IOC_HYST_DISABLE));

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &= ~IOC_IOCFG0_HYST_EN;
    HWREG(ui32IOReg) = ui32Config | ui32Hysteresis;
}

void IOCIOInputSet(uint32_t ui32IOId, uint32_t ui32Input) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32Input == IOC_INPUT_ENABLE) ||
           (ui32Input == IOC_INPUT_DISABLE));

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &= ~IOC_IOCFG0_IE;
    HWREG(ui32IOReg) = ui32Config | ui32Input;
}

void IOCIOSlewCtrlSet(uint32_t ui32IOId, uint32_t ui32SlewEnable) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32SlewEnable == IOC_SLEW_ENABLE) ||
           (ui32SlewEnable == IOC_SLEW_DISABLE));

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &= ~IOC_IOCFG0_SLEW_RED;
    HWREG(ui32IOReg) = ui32Config | ui32SlewEnable;
}

void IOCIODrvStrengthSet(uint32_t ui32IOId, uint32_t ui32IOCurrent, uint32_t ui32DrvStrength) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT((ui32IOCurrent == IOC_CURRENT_2MA) ||
           (ui32IOCurrent == IOC_CURRENT_4MA) ||
           (ui32IOCurrent == IOC_CURRENT_8MA));
    ASSERT((ui32DrvStrength == IOC_STRENGTH_MIN) ||
           (ui32DrvStrength == IOC_STRENGTH_MAX) ||
           (ui32DrvStrength == IOC_STRENGTH_MED) ||
           (ui32DrvStrength == IOC_STRENGTH_AUTO));

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &= ~(IOC_IOCFG0_IOCURR_M | IOC_IOCFG0_IOSTR_M);
    HWREG(ui32IOReg) = ui32Config | (ui32IOCurrent | ui32DrvStrength);
}

void IOCIOPortIdSet(uint32_t ui32IOId, uint32_t ui32PortId) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);
    ASSERT(ui32PortId <= IOC_PORT_RFC_GPI1);

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Configure the IO.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &= ~IOC_IOCFG0_PORT_ID_M;
    HWREG(ui32IOReg) = ui32Config | ui32PortId;
}

void IOCIntEnable(uint32_t ui32IOId) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Enable the specified interrupt.
    ui32Config = HWREG(ui32IOReg);
    ui32Config |= IOC_IOCFG0_EDGE_IRQ_EN;
    HWREG(ui32IOReg) = ui32Config;
}

void IOCIntDisable(uint32_t ui32IOId) {
    uint32_t ui32IOReg;
    uint32_t ui32Config;

    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);

    // Get the register address.
    ui32IOReg = IOC_BASE + ( ui32IOId << 2 );

    // Disable the specified interrupt.
    ui32Config = HWREG(ui32IOReg);
    ui32Config &= ~IOC_IOCFG0_EDGE_IRQ_EN;
    HWREG(ui32IOReg) = ui32Config;
}

void IOCPinTypeGpioInput(uint32_t ui32IOId) {
    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);

    // Setup the IO for standard input.
    IOCPortConfigureSet(ui32IOId, IOC_PORT_GPIO, IOC_STD_INPUT);

    // Enable input mode in the GPIO module.
    GPIO_setOutputEnableDio(ui32IOId, GPIO_OUTPUT_DISABLE);
}

void IOCPinTypeGpioOutput(uint32_t ui32IOId) {
    // Check the arguments.
    ASSERT(ui32IOId <= IOID_31);

    // Setup the IO for standard input.
    IOCPortConfigureSet(ui32IOId, IOC_PORT_GPIO, IOC_STD_OUTPUT);

    // Enable output mode in the GPIO module.
    GPIO_setOutputEnableDio(ui32IOId, GPIO_OUTPUT_ENABLE);
}

void IOCPinTypeUart(uint32_t ui32Base, uint32_t ui32Rx, uint32_t ui32Tx, uint32_t ui32Cts, uint32_t ui32Rts) {
    // Check the arguments.
    ASSERT(ui32Base == UART0_BASE);
    ASSERT((ui32Rx <= IOID_31) || (ui32Rx == IOID_UNUSED));
    ASSERT((ui32Tx <= IOID_31) || (ui32Tx == IOID_UNUSED));
    ASSERT((ui32Cts <= IOID_31) || (ui32Cts == IOID_UNUSED));
    ASSERT((ui32Rts <= IOID_31) || (ui32Rts == IOID_UNUSED));

    // Setup the IOs in the desired configuration.
    if(ui32Rx != IOID_UNUSED)
    {
        IOCPortConfigureSet(ui32Rx, IOC_PORT_MCU_UART0_RX, IOC_STD_INPUT);
    }
    if(ui32Tx != IOID_UNUSED)
    {
        IOCPortConfigureSet(ui32Tx, IOC_PORT_MCU_UART0_TX, IOC_STD_OUTPUT);
    }
    if(ui32Cts != IOID_UNUSED)
    {
        IOCPortConfigureSet(ui32Cts, IOC_PORT_MCU_UART0_CTS, IOC_STD_INPUT);
    }
    if(ui32Rts != IOID_UNUSED)
    {
        IOCPortConfigureSet(ui32Rts, IOC_PORT_MCU_UART0_RTS, IOC_STD_OUTPUT);
    }
}

void IOCPinTypeSsiMaster(uint32_t ui32Base, uint32_t ui32Rx, uint32_t ui32Tx, uint32_t ui32Fss, uint32_t ui32Clk) {
    // Check the arguments.
    ASSERT((ui32Base == SSI0_BASE) || (ui32Base == SSI1_BASE));
    ASSERT((ui32Rx <= IOID_31) || (ui32Rx == IOID_UNUSED));
    ASSERT((ui32Tx <= IOID_31) || (ui32Tx == IOID_UNUSED));
    ASSERT((ui32Fss <= IOID_31) || (ui32Fss == IOID_UNUSED));
    ASSERT((ui32Clk <= IOID_31) || (ui32Clk == IOID_UNUSED));

    // Setup the IOs in the desired configuration.
    if(ui32Base == SSI0_BASE)
    {
        if(ui32Rx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Rx, IOC_PORT_MCU_SSI0_RX, IOC_STD_INPUT);
        }
        if(ui32Tx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Tx, IOC_PORT_MCU_SSI0_TX, IOC_STD_OUTPUT);
        }
        if(ui32Fss != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Fss, IOC_PORT_MCU_SSI0_FSS, IOC_STD_OUTPUT);
        }
        if(ui32Clk != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Clk, IOC_PORT_MCU_SSI0_CLK, IOC_STD_OUTPUT);
        }
    }
    else
    {
        if(ui32Rx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Rx, IOC_PORT_MCU_SSI1_RX, IOC_STD_INPUT);
        }
        if(ui32Tx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Tx, IOC_PORT_MCU_SSI1_TX, IOC_STD_OUTPUT);
        }
        if(ui32Fss != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Fss, IOC_PORT_MCU_SSI1_FSS, IOC_STD_OUTPUT);
        }
        if(ui32Clk != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Clk, IOC_PORT_MCU_SSI1_CLK, IOC_STD_OUTPUT);
        }
    }
}

void IOCPinTypeSsiSlave(uint32_t ui32Base, uint32_t ui32Rx, uint32_t ui32Tx, uint32_t ui32Fss, uint32_t ui32Clk) {
    // Check the arguments.
    ASSERT((ui32Base == SSI0_BASE) || (ui32Base == SSI1_BASE));
    ASSERT((ui32Rx <= IOID_31) || (ui32Rx == IOID_UNUSED));
    ASSERT((ui32Tx <= IOID_31) || (ui32Tx == IOID_UNUSED));
    ASSERT((ui32Fss <= IOID_31) || (ui32Fss == IOID_UNUSED));
    ASSERT((ui32Clk <= IOID_31) || (ui32Clk == IOID_UNUSED));

    // Setup the IOs in the desired configuration.
    if(ui32Base == SSI0_BASE)
    {
        if(ui32Rx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Rx, IOC_PORT_MCU_SSI0_RX,  IOC_STD_INPUT);
        }
        if(ui32Tx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Tx, IOC_PORT_MCU_SSI0_TX, IOC_STD_OUTPUT);
        }
        if(ui32Fss != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Fss, IOC_PORT_MCU_SSI0_FSS, IOC_STD_INPUT);
        }
        if(ui32Clk != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Clk, IOC_PORT_MCU_SSI0_CLK, IOC_STD_INPUT);
        }
    }
    else
    {
        if(ui32Rx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Rx, IOC_PORT_MCU_SSI1_RX, IOC_STD_INPUT);
        }
        if(ui32Tx != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Tx, IOC_PORT_MCU_SSI1_TX, IOC_STD_OUTPUT);
        }
        if(ui32Fss != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Fss, IOC_PORT_MCU_SSI1_FSS, IOC_STD_INPUT);
        }
        if(ui32Clk != IOID_UNUSED)
        {
            IOCPortConfigureSet(ui32Clk, IOC_PORT_MCU_SSI1_CLK, IOC_STD_INPUT);
        }
    }
}

void IOCPinTypeI2c(uint32_t ui32Base, uint32_t ui32Data, uint32_t ui32Clk) {
    uint32_t ui32IOConfig;

    // Check the arguments.
    ASSERT((ui32Data <= IOID_31) || (ui32Data == IOID_UNUSED));
    ASSERT((ui32Clk <= IOID_31) || (ui32Clk == IOID_UNUSED));

    // Define the IO configuration parameters.
    ui32IOConfig = IOC_CURRENT_2MA | IOC_STRENGTH_AUTO | IOC_IOPULL_UP |
                   IOC_SLEW_DISABLE | IOC_HYST_DISABLE | IOC_NO_EDGE |
                   IOC_INT_DISABLE | IOC_IOMODE_OPEN_DRAIN_NORMAL |
                   IOC_NO_WAKE_UP | IOC_INPUT_ENABLE;

    // Setup the IOs in the desired configuration.
    IOCPortConfigureSet(ui32Data, IOC_PORT_MCU_I2C_MSSDA, ui32IOConfig);
    IOCPortConfigureSet(ui32Clk, IOC_PORT_MCU_I2C_MSSCL, ui32IOConfig);
}

void IOCPinTypeAux(uint32_t ui32IOId) {
    // Check the arguments.
    ASSERT((ui32IOId <= IOID_31) || (ui32IOId == IOID_UNUSED));

    // Setup the IO.
    IOCPortConfigureSet(ui32IOId, IOC_PORT_AUX_IO, IOC_STD_INPUT);
}

//*****************************************************************************
//
// Arrays that maps the "peripheral set" number (which is stored in
// bits[11:8] of the PRCM_PERIPH_* defines) to the PRCM register that
// contains the relevant bit for that peripheral.
//
//*****************************************************************************

// Run mode registers
static const uint32_t g_pui32RCGCRegs[] =
{
    PRCM_O_GPTCLKGR     , // Index 0
    PRCM_O_SSICLKGR     , // Index 1
    PRCM_O_UARTCLKGR    , // Index 2
    PRCM_O_I2CCLKGR     , // Index 3
    PRCM_O_SECDMACLKGR  , // Index 4
    PRCM_O_GPIOCLKGR    , // Index 5
    PRCM_O_I2SCLKGR       // Index 6
};

// Sleep mode registers
static const uint32_t g_pui32SCGCRegs[] =
{
    PRCM_O_GPTCLKGS     , // Index 0
    PRCM_O_SSICLKGS     , // Index 1
    PRCM_O_UARTCLKGS    , // Index 2
    PRCM_O_I2CCLKGS     , // Index 3
    PRCM_O_SECDMACLKGS  , // Index 4
    PRCM_O_GPIOCLKGS    , // Index 5
    PRCM_O_I2SCLKGS       // Index 6
};

// Deep sleep mode registers
static const uint32_t g_pui32DCGCRegs[] =
{
    PRCM_O_GPTCLKGDS    , // Index 0
    PRCM_O_SSICLKGDS    , // Index 1
    PRCM_O_UARTCLKGDS   , // Index 2
    PRCM_O_I2CCLKGDS    , // Index 3
    PRCM_O_SECDMACLKGDS , // Index 4
    PRCM_O_GPIOCLKGDS   , // Index 5
    PRCM_O_I2SCLKGDS      // Index 6
};

//*****************************************************************************
//
// This macro extracts the array index out of the peripheral number
//
//*****************************************************************************
#define PRCM_PERIPH_INDEX(a)  (((a) >> 8) & 0xf)

//*****************************************************************************
//
// This macro extracts the peripheral instance number and generates bit mask
//
//*****************************************************************************
#define PRCM_PERIPH_MASKBIT(a) (0x00000001 << ((a) & 0x1f))


void PRCMInfClockConfigureSet(uint32_t ui32ClkDiv, uint32_t ui32PowerMode) {
    uint32_t ui32Divisor;

    // Check the arguments.
    ASSERT((ui32ClkDiv == PRCM_CLOCK_DIV_1) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_2) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_8) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_32));
    ASSERT((ui32PowerMode == PRCM_RUN_MODE) ||
           (ui32PowerMode == PRCM_SLEEP_MODE) ||
           (ui32PowerMode == PRCM_DEEP_SLEEP_MODE));

    ui32Divisor = 0;

    // Find the correct division factor.
    if(ui32ClkDiv == PRCM_CLOCK_DIV_1)
    {
        ui32Divisor = 0x0;
    }
    else if(ui32ClkDiv == PRCM_CLOCK_DIV_2)
    {
        ui32Divisor = 0x1;
    }
    else if(ui32ClkDiv == PRCM_CLOCK_DIV_8)
    {
        ui32Divisor = 0x2;
    }
    else if(ui32ClkDiv == PRCM_CLOCK_DIV_32)
    {
        ui32Divisor = 0x3;
    }

    // Determine the correct power mode set the division factor accordingly.
    if(ui32PowerMode == PRCM_RUN_MODE)
    {
        HWREG(PRCM_BASE + PRCM_O_INFRCLKDIVR) = ui32Divisor;
    }
    else if(ui32PowerMode == PRCM_SLEEP_MODE)
    {
        HWREG(PRCM_BASE + PRCM_O_INFRCLKDIVS) = ui32Divisor;
    }
    else if(ui32PowerMode == PRCM_DEEP_SLEEP_MODE)
    {
        HWREG(PRCM_BASE + PRCM_O_INFRCLKDIVDS) = ui32Divisor;
    }
}

uint32_t PRCMInfClockConfigureGet(uint32_t ui32PowerMode) {
    uint32_t ui32ClkDiv;
    uint32_t ui32Divisor;

    // Check the arguments.
    ASSERT((ui32PowerMode == PRCM_RUN_MODE) ||
           (ui32PowerMode == PRCM_SLEEP_MODE) ||
           (ui32PowerMode == PRCM_DEEP_SLEEP_MODE));

    ui32ClkDiv = 0;
    ui32Divisor = 0;

    // Determine the correct power mode.
    if(ui32PowerMode == PRCM_RUN_MODE)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_INFRCLKDIVR);
    }
    else if(ui32PowerMode == PRCM_SLEEP_MODE)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_INFRCLKDIVS);
    }
    else if(ui32PowerMode == PRCM_DEEP_SLEEP_MODE)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_INFRCLKDIVDS);
    }

    // Find the correct division factor.
    if(ui32ClkDiv == 0x0)
    {
        ui32Divisor = PRCM_CLOCK_DIV_1;
    }
    else if(ui32ClkDiv == 0x1)
    {
        ui32Divisor = PRCM_CLOCK_DIV_2;
    }
    else if(ui32ClkDiv == 0x2)
    {
        ui32Divisor = PRCM_CLOCK_DIV_8;
    }
    else if(ui32ClkDiv == 0x3)
    {
        ui32Divisor = PRCM_CLOCK_DIV_32;
    }

    // Return the clock division factor.
    return ui32Divisor;
}

void PRCMClockConfigureSet(uint32_t ui32Domains, uint32_t ui32ClkDiv) {
    uint32_t ui32Reg;

    // Check the arguments.
    ASSERT((ui32Domains & PRCM_DOMAIN_SYSBUS) ||
           (ui32Domains & PRCM_DOMAIN_CPU) ||
           (ui32Domains & PRCM_DOMAIN_PERIPH) ||
           (ui32Domains & PRCM_DOMAIN_TIMER) ||
           (ui32Domains & PRCM_DOMAIN_SERIAL));
    ASSERT((ui32ClkDiv == PRCM_CLOCK_DIV_1) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_2) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_4) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_8) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_16) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_32) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_64) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_128) ||
           (ui32ClkDiv == PRCM_CLOCK_DIV_256));

    // Configure the selected clock dividers.
    if(ui32Domains & PRCM_DOMAIN_SYSBUS)
    {
        ui32Reg = PRCM_O_SYSBUSCLKDIV;
        HWREG(PRCM_BASE + ui32Reg) = ui32ClkDiv;
    }
    if(ui32Domains & PRCM_DOMAIN_CPU)
    {
        ui32Reg = PRCM_O_CPUCLKDIV;
        HWREG(PRCM_BASE + ui32Reg) = ui32ClkDiv;
    }
    if(ui32Domains & PRCM_DOMAIN_PERIPH)
    {
        ui32Reg = PRCM_O_PERBUSCPUCLKDIV;
        HWREG(PRCM_BASE + ui32Reg) = ui32ClkDiv;
    }
    if(ui32Domains & PRCM_DOMAIN_SERIAL)
    {
        ui32Reg = PRCM_O_PERDMACLKDIV;
        HWREG(PRCM_BASE + ui32Reg) = ui32ClkDiv;
    }
    if(ui32Domains & PRCM_DOMAIN_TIMER)
    {
        ui32Reg = PRCM_O_GPTCLKDIV;
        HWREG(PRCM_BASE + ui32Reg) = ui32ClkDiv;
    }
}

uint32_t PRCMClockConfigureGet(uint32_t ui32Domain) {
    uint32_t ui32ClkDiv;

    // Check the arguments.
    ASSERT((ui32Domain == PRCM_DOMAIN_SYSBUS) ||
           (ui32Domain == PRCM_DOMAIN_CPU) ||
           (ui32Domain == PRCM_DOMAIN_PERIPH) ||
           (ui32Domain == PRCM_DOMAIN_TIMER) ||
           (ui32Domain == PRCM_DOMAIN_SERIAL));

    ui32ClkDiv = 0;

    // Find the correct sub system.
    if(ui32Domain == PRCM_DOMAIN_SYSBUS)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_SYSBUSCLKDIV);
    }
    else if(ui32Domain == PRCM_DOMAIN_CPU)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_CPUCLKDIV);
    }
    else if(ui32Domain == PRCM_DOMAIN_PERIPH)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_PERBUSCPUCLKDIV);
    }
    else if(ui32Domain == PRCM_DOMAIN_SERIAL)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_PERDMACLKDIV);
    }
    else if(ui32Domain == PRCM_DOMAIN_TIMER)
    {
        ui32ClkDiv = HWREG(PRCM_BASE + PRCM_O_GPTCLKDIV);
    }

    // Return the clock configuration.
    return(ui32ClkDiv);
}

void PRCMAudioClockConfigSet(uint32_t ui32ClkConfig, uint32_t ui32SampleRate) {
    uint32_t ui32Reg;
    uint32_t ui32MstDiv;
    uint32_t ui32BitDiv;
    uint32_t ui32WordDiv;

    // Check the arguments.
    ASSERT(!(ui32ClkConfig & (PRCM_I2SCLKCTL_WCLK_PHASE_M | PRCM_I2SCLKCTL_SMPL_ON_POSEDGE_M)));
    ASSERT((ui32SampleRate == I2S_SAMPLE_RATE_16K) ||
           (ui32SampleRate == I2S_SAMPLE_RATE_24K) ||
           (ui32SampleRate == I2S_SAMPLE_RATE_32K) ||
           (ui32SampleRate == I2S_SAMPLE_RATE_48K));

    ui32MstDiv = 0;
    ui32BitDiv = 0;
    ui32WordDiv = 0;

    // Make sure the audio clock generation is disabled before reconfiguring.
    PRCMAudioClockDisable();

    // Define the clock division factors for the audio interface.
    switch(ui32SampleRate)
    {
    case I2S_SAMPLE_RATE_16K :
        ui32MstDiv = 6;
        ui32BitDiv = 60;
        ui32WordDiv = 25;
        break;
    case I2S_SAMPLE_RATE_24K :
        ui32MstDiv = 4;
        ui32BitDiv = 40;
        ui32WordDiv = 25;
        break;
    case I2S_SAMPLE_RATE_32K :
        ui32MstDiv = 3;
        ui32BitDiv = 30;
        ui32WordDiv = 25;
        break;
    case I2S_SAMPLE_RATE_48K :
        ui32MstDiv = 2;
        ui32BitDiv = 20;
        ui32WordDiv = 25;
        break;
    }

    // Make sure to compensate the Frame clock division factor if using single
    // phase format.
    if((ui32ClkConfig & PRCM_I2SCLKCTL_WCLK_PHASE_M) == PRCM_WCLK_SINGLE_PHASE)
    {
        ui32WordDiv -= 1;
    }

    // Write the clock division factors.
    HWREG(PRCM_BASE + PRCM_O_I2SMCLKDIV) = ui32MstDiv;
    HWREG(PRCM_BASE + PRCM_O_I2SBCLKDIV) = ui32BitDiv;
    HWREG(PRCM_BASE + PRCM_O_I2SWCLKDIV) = ui32WordDiv;

    // Configure the Word clock format and polarity.
    ui32Reg = HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) & ~(PRCM_I2SCLKCTL_WCLK_PHASE_M |
              PRCM_I2SCLKCTL_SMPL_ON_POSEDGE_M);
    HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = ui32Reg | ui32ClkConfig;
}

void PRCMAudioClockConfigSetOverride(uint32_t ui32ClkConfig, uint32_t ui32MstDiv, uint32_t ui32BitDiv, uint32_t ui32WordDiv) {
    uint32_t ui32Reg;

    // Check the arguments.
    ASSERT(!(ui32ClkConfig & (PRCM_I2SCLKCTL_WCLK_PHASE_M | PRCM_I2SCLKCTL_SMPL_ON_POSEDGE_M)));

    // Make sure the audio clock generation is disabled before reconfiguring.
    PRCMAudioClockDisable();

    // Make sure to compensate the Frame clock division factor if using single
    // phase format.
    if((ui32ClkConfig & PRCM_I2SCLKCTL_WCLK_PHASE_M) == PRCM_WCLK_SINGLE_PHASE)
    {
        ui32WordDiv -= 1;
    }

    // Write the clock division factors.
    HWREG(PRCM_BASE + PRCM_O_I2SMCLKDIV) = ui32MstDiv;
    HWREG(PRCM_BASE + PRCM_O_I2SBCLKDIV) = ui32BitDiv;
    HWREG(PRCM_BASE + PRCM_O_I2SWCLKDIV) = ui32WordDiv;

    // Configure the Word clock format and polarity.
    ui32Reg = HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) & ~(PRCM_I2SCLKCTL_WCLK_PHASE_M |
              PRCM_I2SCLKCTL_SMPL_ON_POSEDGE_M);
    HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = ui32Reg | ui32ClkConfig;
}

void PRCMPowerDomainOn(uint32_t ui32Domains) {
    // Check the arguments.
    ASSERT((ui32Domains & PRCM_DOMAIN_RFCORE) ||
           (ui32Domains & PRCM_DOMAIN_SERIAL) ||
           (ui32Domains & PRCM_DOMAIN_PERIPH) ||
           (ui32Domains & PRCM_DOMAIN_CPU) ||
           (ui32Domains & PRCM_DOMAIN_VIMS));

    // Assert the request to power on the right domains.
    if(ui32Domains & PRCM_DOMAIN_RFCORE)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL0RFC   ) = 1;
    }
    if(ui32Domains & PRCM_DOMAIN_SERIAL)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL0SERIAL) = 1;
    }
    if(ui32Domains & PRCM_DOMAIN_PERIPH)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL0PERIPH) = 1;
    }
    if(ui32Domains & PRCM_DOMAIN_VIMS)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL1VIMS  ) = 1;
    }
    if(ui32Domains & PRCM_DOMAIN_CPU)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL1CPU   ) = 1;
    }
}

void PRCMPowerDomainOff(uint32_t ui32Domains) {
    // Check the arguments.
    ASSERT((ui32Domains & PRCM_DOMAIN_RFCORE) ||
           (ui32Domains & PRCM_DOMAIN_SERIAL) ||
           (ui32Domains & PRCM_DOMAIN_PERIPH) ||
           (ui32Domains & PRCM_DOMAIN_CPU) ||
           (ui32Domains & PRCM_DOMAIN_VIMS));

    // Assert the request to power off the right domains.
    if(ui32Domains & PRCM_DOMAIN_RFCORE)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL0RFC   ) = 0;
    }
    if(ui32Domains & PRCM_DOMAIN_SERIAL)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL0SERIAL) = 0;
    }
    if(ui32Domains & PRCM_DOMAIN_PERIPH)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL0PERIPH) = 0;
    }
    if(ui32Domains & PRCM_DOMAIN_VIMS)
    {
        // Write bits ui32Domains[17:16] to the VIMS_MODE alias register.
        // PRCM_DOMAIN_VIMS sets VIMS_MODE=0b00, PRCM_DOMAIN_VIMS_OFF_NO_WAKEUP sets VIMS_MODE=0b10.
        ASSERT(!(ui32Domains & 0x00010000));
        HWREG(PRCM_BASE + PRCM_O_PDCTL1VIMS  ) = ( ui32Domains >> 16 ) & 3;
    }
    if(ui32Domains & PRCM_DOMAIN_CPU)
    {
        HWREG(PRCM_BASE + PRCM_O_PDCTL1CPU   ) = 0;
    }
}

void PRCMPeripheralRunEnable(uint32_t ui32Peripheral) {
    // Check the arguments.
    ASSERT(PRCMPeripheralValid(ui32Peripheral));

    // Enable module in Run Mode.
    HWREG(PRCM_BASE + g_pui32RCGCRegs[PRCM_PERIPH_INDEX(ui32Peripheral)]) |=
        PRCM_PERIPH_MASKBIT(ui32Peripheral);
}

void PRCMPeripheralRunDisable(uint32_t ui32Peripheral) {
    // Check the arguments.
    ASSERT(PRCMPeripheralValid(ui32Peripheral));

    // Disable module in Run Mode.
    HWREG(PRCM_BASE + g_pui32RCGCRegs[PRCM_PERIPH_INDEX(ui32Peripheral)]) &=
        ~PRCM_PERIPH_MASKBIT(ui32Peripheral);
}

void PRCMPeripheralSleepEnable(uint32_t ui32Peripheral) {
    // Check the arguments.
    ASSERT(PRCMPeripheralValid(ui32Peripheral));

    // Enable this peripheral in sleep mode.
    HWREG(PRCM_BASE + g_pui32SCGCRegs[PRCM_PERIPH_INDEX(ui32Peripheral)]) |=
        PRCM_PERIPH_MASKBIT(ui32Peripheral);
}

void PRCMPeripheralSleepDisable(uint32_t ui32Peripheral) {
    // Check the arguments.
    ASSERT(PRCMPeripheralValid(ui32Peripheral));

    // Disable this peripheral in sleep mode
    HWREG(PRCM_BASE + g_pui32SCGCRegs[PRCM_PERIPH_INDEX(ui32Peripheral)]) &=
        ~PRCM_PERIPH_MASKBIT(ui32Peripheral);
}

void PRCMPeripheralDeepSleepEnable(uint32_t ui32Peripheral) {
    // Check the arguments.
    ASSERT(PRCMPeripheralValid(ui32Peripheral));

    // Enable this peripheral in deep-sleep mode.
    HWREG(PRCM_BASE + g_pui32DCGCRegs[PRCM_PERIPH_INDEX(ui32Peripheral)]) |=
        PRCM_PERIPH_MASKBIT(ui32Peripheral);
}

void PRCMPeripheralDeepSleepDisable(uint32_t ui32Peripheral) {
    // Check the arguments.
    ASSERT(PRCMPeripheralValid(ui32Peripheral));

    // Disable this peripheral in Deep Sleep mode.
    HWREG(PRCM_BASE + g_pui32DCGCRegs[PRCM_PERIPH_INDEX(ui32Peripheral)]) &=
        ~PRCM_PERIPH_MASKBIT(ui32Peripheral);
}

uint32_t PRCMPowerDomainStatus(uint32_t ui32Domains) {
    bool bStatus;
    uint32_t ui32StatusRegister0;
    uint32_t ui32StatusRegister1;

    // Check the arguments.
    ASSERT((ui32Domains & (PRCM_DOMAIN_RFCORE |
                           PRCM_DOMAIN_SERIAL |
                           PRCM_DOMAIN_PERIPH)));

    bStatus = true;
    ui32StatusRegister0 = HWREG(PRCM_BASE + PRCM_O_PDSTAT0);
    ui32StatusRegister1 = HWREG(PRCM_BASE + PRCM_O_PDSTAT1);

    // Return the correct power status.
    if(ui32Domains & PRCM_DOMAIN_RFCORE)
    {
       bStatus = bStatus &&
                 ((ui32StatusRegister0 & PRCM_PDSTAT0_RFC_ON) ||
                  (ui32StatusRegister1 & PRCM_PDSTAT1_RFC_ON));
    }
    if(ui32Domains & PRCM_DOMAIN_SERIAL)
    {
        bStatus = bStatus && (ui32StatusRegister0 & PRCM_PDSTAT0_SERIAL_ON);
    }
    if(ui32Domains & PRCM_DOMAIN_PERIPH)
    {
        bStatus = bStatus && (ui32StatusRegister0 & PRCM_PDSTAT0_PERIPH_ON);
    }

    // Return the status.
    return (bStatus ? PRCM_DOMAIN_POWER_ON : PRCM_DOMAIN_POWER_OFF);
}

void PRCMDeepSleep(void) {
    // Enable deep-sleep.
    HWREG(NVIC_SYS_CTRL) |= NVIC_SYS_CTRL_SLEEPDEEP;

    // Wait for an interrupt.
    CPUwfi();

    // Disable deep-sleep so that a future sleep will work correctly.
    HWREG(NVIC_SYS_CTRL) &= ~(NVIC_SYS_CTRL_SLEEPDEEP);
}

void PRCMRetentionEnable(uint32_t ui32PowerDomain) {
    uint32_t ui32Retention;

    // Check the arguments.
    ASSERT(PRCM_DOMAIN_CPU & ui32PowerDomain);

    // Get the current register values.
    ui32Retention = HWREG(PRCM_BASE + PRCM_O_RAMRETEN);

    // Enable retention on RF core SRAM.
    if(PRCM_DOMAIN_RFCORE & ui32PowerDomain)
    {
        ui32Retention |= PRCM_RAMRETEN_RFC_M;
    }

    // Enable retention on VIMS cache.
    if(PRCM_DOMAIN_VIMS & ui32PowerDomain)
    {
        ui32Retention |= PRCM_RAMRETEN_VIMS_M;
    }

    // Enable retention on RFC ULL SRAM.
    if(PRCM_DOMAIN_RFCULL_SRAM & ui32PowerDomain)
    {
        ui32Retention |= PRCM_RAMRETEN_RFCULL_M;
    }

    // Reconfigure retention.
    HWREG(PRCM_BASE + PRCM_O_RAMRETEN) = ui32Retention;
}

void PRCMRetentionDisable(uint32_t ui32PowerDomain) {
    uint32_t ui32Retention;

    // Check the arguments.
    ASSERT(PRCM_DOMAIN_CPU & ui32PowerDomain);

    // Get the current register values
    ui32Retention = HWREG(PRCM_BASE + PRCM_O_RAMRETEN);

    // Disable retention on RF core SRAM
    if(PRCM_DOMAIN_RFCORE & ui32PowerDomain)
    {
        ui32Retention &= ~PRCM_RAMRETEN_RFC_M;
    }

    // Disable retention on VIMS cache
    if(PRCM_DOMAIN_VIMS & ui32PowerDomain)
    {
        ui32Retention &= ~PRCM_RAMRETEN_VIMS_M;
    }

    // Disable retention on RFC ULL SRAM.
    if(PRCM_DOMAIN_RFCULL_SRAM & ui32PowerDomain)
    {
        ui32Retention &= ~PRCM_RAMRETEN_RFCULL_M;
    }

    // Reconfigure retention.
    HWREG(PRCM_BASE + PRCM_O_RAMRETEN) = ui32Retention;
}

void SMPHAcquire(uint32_t ui32Semaphore) {
    // Check the arguments.
    ASSERT((ui32Semaphore == SMPH_0) ||
           (ui32Semaphore == SMPH_1) ||
           (ui32Semaphore == SMPH_2) ||
           (ui32Semaphore == SMPH_3) ||
           (ui32Semaphore == SMPH_4) ||
           (ui32Semaphore == SMPH_5) ||
           (ui32Semaphore == SMPH_6) ||
           (ui32Semaphore == SMPH_7) ||
           (ui32Semaphore == SMPH_8) ||
           (ui32Semaphore == SMPH_9) ||
           (ui32Semaphore == SMPH_10) ||
           (ui32Semaphore == SMPH_11) ||
           (ui32Semaphore == SMPH_12) ||
           (ui32Semaphore == SMPH_13) ||
           (ui32Semaphore == SMPH_14) ||
           (ui32Semaphore == SMPH_15) ||
           (ui32Semaphore == SMPH_16) ||
           (ui32Semaphore == SMPH_17) ||
           (ui32Semaphore == SMPH_18) ||
           (ui32Semaphore == SMPH_19) ||
           (ui32Semaphore == SMPH_20) ||
           (ui32Semaphore == SMPH_21) ||
           (ui32Semaphore == SMPH_22) ||
           (ui32Semaphore == SMPH_23) ||
           (ui32Semaphore == SMPH_24) ||
           (ui32Semaphore == SMPH_25) ||
           (ui32Semaphore == SMPH_26) ||
           (ui32Semaphore == SMPH_27) ||
           (ui32Semaphore == SMPH_28) ||
           (ui32Semaphore == SMPH_29) ||
           (ui32Semaphore == SMPH_30) ||
           (ui32Semaphore == SMPH_31));

    // Wait for semaphore to be release such that it can be claimed
    // Semaphore register reads 1 when lock was acquired otherwise 0
    // (i.e. SMPH_CLAIMED).
    while(HWREG(SMPH_BASE + SMPH_O_SMPH0 + 4 * ui32Semaphore) ==
            SMPH_CLAIMED)
    {
    }
}

void SSIConfigSetExpClk(uint32_t ui32Base, uint32_t ui32SSIClk, uint32_t ui32Protocol, uint32_t ui32Mode, uint32_t ui32BitRate, uint32_t ui32DataWidth) {
    uint32_t ui32MaxBitRate;
    uint32_t ui32RegVal;
    uint32_t ui32PreDiv;
    uint32_t ui32SCR;
    uint32_t ui32SPH_SPO;

    // Check the arguments.
    ASSERT(SSIBaseValid(ui32Base));
    ASSERT((ui32Protocol == SSI_FRF_MOTO_MODE_0) ||
           (ui32Protocol == SSI_FRF_MOTO_MODE_1) ||
           (ui32Protocol == SSI_FRF_MOTO_MODE_2) ||
           (ui32Protocol == SSI_FRF_MOTO_MODE_3) ||
           (ui32Protocol == SSI_FRF_TI) ||
           (ui32Protocol == SSI_FRF_NMW));
    ASSERT((ui32Mode == SSI_MODE_MASTER) ||
           (ui32Mode == SSI_MODE_SLAVE) ||
           (ui32Mode == SSI_MODE_SLAVE_OD));
    ASSERT(((ui32Mode == SSI_MODE_MASTER) && (ui32BitRate <= (ui32SSIClk / 2))) ||
           ((ui32Mode != SSI_MODE_MASTER) && (ui32BitRate <= (ui32SSIClk / 12))));
    ASSERT((ui32SSIClk / ui32BitRate) <= (254 * 256));
    ASSERT((ui32DataWidth >= 4) && (ui32DataWidth <= 16));

    // Set the mode.
    ui32RegVal = (ui32Mode == SSI_MODE_SLAVE_OD) ? SSI_CR1_SOD : 0;
    ui32RegVal |= (ui32Mode == SSI_MODE_MASTER) ? 0 : SSI_CR1_MS;
    HWREG(ui32Base + SSI_O_CR1) = ui32RegVal;

    // Set the clock predivider.
    ui32MaxBitRate = ui32SSIClk / ui32BitRate;
    ui32PreDiv = 0;
    do
    {
        ui32PreDiv += 2;
        ui32SCR = (ui32MaxBitRate / ui32PreDiv) - 1;
    }
    while(ui32SCR > 255);
    HWREG(ui32Base + SSI_O_CPSR) = ui32PreDiv;

    // Set protocol and clock rate.
    ui32SPH_SPO = (ui32Protocol & 3) << 6;
    ui32Protocol &= SSI_CR0_FRF_M;
    ui32RegVal = (ui32SCR << 8) | ui32SPH_SPO | ui32Protocol | (ui32DataWidth - 1);
    HWREG(ui32Base + SSI_O_CR0) = ui32RegVal;
}

int32_t SSIDataPutNonBlocking(uint32_t ui32Base, uint32_t ui32Data) {
    // Check the arguments.
    ASSERT(SSIBaseValid(ui32Base));
    ASSERT((ui32Data & (0xfffffffe << (HWREG(ui32Base + SSI_O_CR0) &
                                       SSI_CR0_DSS_M))) == 0);

    // Check for space to write.
    if(HWREG(ui32Base + SSI_O_SR) & SSI_SR_TNF)
    {
        HWREG(ui32Base + SSI_O_DR) = ui32Data;
        return(1);
    }
    else
    {
        return(0);
    }
}

void SSIDataPut(uint32_t ui32Base, uint32_t ui32Data) {
    // Check the arguments.
    ASSERT(SSIBaseValid(ui32Base));
    ASSERT((ui32Data & (0xfffffffe << (HWREG(ui32Base + SSI_O_CR0) &
                                       SSI_CR0_DSS_M))) == 0);

    // Wait until there is space.
    while(!(HWREG(ui32Base + SSI_O_SR) & SSI_SR_TNF))
    {
    }

    // Write the data to the SSI.
    HWREG(ui32Base + SSI_O_DR) = ui32Data;
}

void SSIDataGet(uint32_t ui32Base, uint32_t *pui32Data) {
    // Check the arguments.
    ASSERT(SSIBaseValid(ui32Base));

    // Wait until there is data to be read.
    while(!(HWREG(ui32Base + SSI_O_SR) & SSI_SR_RNE))
    {
    }

    // Read data from SSI.
    *pui32Data = HWREG(ui32Base + SSI_O_DR);
}

int32_t SSIDataGetNonBlocking(uint32_t ui32Base, uint32_t *pui32Data) {
    // Check the arguments.
    ASSERT(SSIBaseValid(ui32Base));

    // Check for data to read.
    if(HWREG(ui32Base + SSI_O_SR) & SSI_SR_RNE)
    {
        *pui32Data = HWREG(ui32Base + SSI_O_DR);
        return(1);
    }
    else
    {
        return(0);
    }
}

void TimerConfigure(uint32_t ui32Base, uint32_t ui32Config) {
    // Check the arguments.
    ASSERT(TimerBaseValid(ui32Base));
    ASSERT((ui32Config == TIMER_CFG_ONE_SHOT) ||
           (ui32Config == TIMER_CFG_ONE_SHOT_UP) ||
           (ui32Config == TIMER_CFG_PERIODIC) ||
           (ui32Config == TIMER_CFG_PERIODIC_UP) ||
           ((ui32Config & 0xFF000000) == TIMER_CFG_SPLIT_PAIR));
    ASSERT(((ui32Config & 0xFF000000) != TIMER_CFG_SPLIT_PAIR) ||
           ((((ui32Config & 0x000000FF) == TIMER_CFG_A_ONE_SHOT) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_ONE_SHOT_UP) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_PERIODIC) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_PERIODIC_UP) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_CAP_COUNT) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_CAP_COUNT_UP) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_CAP_TIME) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_CAP_TIME_UP) ||
             ((ui32Config & 0x000000FF) == TIMER_CFG_A_PWM)) &&
            (((ui32Config & 0x0000FF00) == TIMER_CFG_B_ONE_SHOT) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_ONE_SHOT_UP) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_PERIODIC) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_PERIODIC_UP) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_CAP_COUNT) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_CAP_COUNT_UP) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_CAP_TIME) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_CAP_TIME_UP) ||
             ((ui32Config & 0x0000FF00) == TIMER_CFG_B_PWM))));

    // Disable the timers.
    HWREG(ui32Base + GPT_O_CTL) &= ~(GPT_CTL_TAEN | GPT_CTL_TBEN);

    // Set the global timer configuration.
    HWREG(ui32Base + GPT_O_CFG) = ui32Config >> 24;

    // Set the configuration of the A and B timers. Note that the B timer
    // configuration is ignored by the hardware in 32-bit modes.
    HWREG(ui32Base + GPT_O_TAMR) = (ui32Config & 0xFF) | GPT_TAMR_TAPWMIE;
    HWREG(ui32Base + GPT_O_TBMR) =
        ((ui32Config >> 8) & 0xFF) | GPT_TBMR_TBPWMIE;
}

void TimerLevelControl(uint32_t ui32Base, uint32_t ui32Timer, bool bInvert) {
    // Check the arguments.
    ASSERT(TimerBaseValid(ui32Base));
    ASSERT((ui32Timer == TIMER_A) || (ui32Timer == TIMER_B) ||
           (ui32Timer == TIMER_BOTH));

    // Set the output levels as requested.
    ui32Timer &= GPT_CTL_TAPWML | GPT_CTL_TBPWML;
    HWREG(ui32Base + GPT_O_CTL) = (bInvert ?
                                   (HWREG(ui32Base + GPT_O_CTL) | ui32Timer) :
                                   (HWREG(ui32Base + GPT_O_CTL) &
                                   ~(ui32Timer)));
}

void TimerStallControl(uint32_t ui32Base, uint32_t ui32Timer, bool bStall) {
    // Check the arguments.
    ASSERT(TimerBaseValid(ui32Base));
    ASSERT((ui32Timer == TIMER_A) || (ui32Timer == TIMER_B) ||
           (ui32Timer == TIMER_BOTH));

    // Set the stall mode.
    ui32Timer &= GPT_CTL_TASTALL | GPT_CTL_TBSTALL;
    HWREG(ui32Base + GPT_O_CTL) = (bStall ?
                                   (HWREG(ui32Base + GPT_O_CTL) | ui32Timer) :
                                   (HWREG(ui32Base + GPT_O_CTL) & ~(ui32Timer)));
}

void TimerWaitOnTriggerControl(uint32_t ui32Base, uint32_t ui32Timer, bool bWait) {
    // Check the arguments.
    ASSERT(TimerBaseValid(ui32Base));
    ASSERT((ui32Timer == TIMER_A) || (ui32Timer == TIMER_B) ||
           (ui32Timer == TIMER_BOTH));

    // Set the wait on trigger mode for timer A.
    if(ui32Timer & TIMER_A)
    {
        if(bWait)
        {
            HWREG(ui32Base + GPT_O_TAMR) |= GPT_TAMR_TAWOT;
        }
        else
        {
            HWREG(ui32Base + GPT_O_TAMR) &= ~(GPT_TAMR_TAWOT);
        }
    }

    // Set the wait on trigger mode for timer B.
    if(ui32Timer & TIMER_B)
    {
        if(bWait)
        {
            HWREG(ui32Base + GPT_O_TBMR) |= GPT_TBMR_TBWOT;
        }
        else
        {
            HWREG(ui32Base + GPT_O_TBMR) &= ~(GPT_TBMR_TBWOT);
        }
    }
}

void TimerMatchUpdateMode(uint32_t ui32Base, uint32_t ui32Timer, uint32_t ui32Mode) {
    // Check the arguments
    ASSERT(TimerBaseValid(ui32Base));
    ASSERT((ui32Timer == TIMER_A) || (ui32Timer == TIMER_B) || (ui32Timer == TIMER_BOTH));
    ASSERT((ui32Mode == TIMER_MATCHUPDATE_NEXTCYCLE) || (ui32Mode == TIMER_MATCHUPDATE_TIMEOUT));

    // Set mode for timer A
    if(ui32Timer & TIMER_A)
    {
        if(ui32Mode == TIMER_MATCHUPDATE_NEXTCYCLE)
        {
            HWREG(ui32Base + GPT_O_TAMR) &= ~(GPT_TAMR_TAMRSU);
        }
        else
        {
            HWREG(ui32Base + GPT_O_TAMR) |= GPT_TAMR_TAMRSU;
        }
    }

    // Set mode for timer B
    if(ui32Timer & TIMER_B)
    {
        if(ui32Mode == TIMER_MATCHUPDATE_NEXTCYCLE)
        {
            HWREG(ui32Base + GPT_O_TBMR) &= ~(GPT_TBMR_TBMRSU);
        }
        else
        {
            HWREG(ui32Base + GPT_O_TBMR) |= GPT_TBMR_TBMRSU;
        }
    }
}

void TimerIntervalLoadMode(uint32_t ui32Base, uint32_t ui32Timer, uint32_t ui32Mode) {
    // Check the arguments
    ASSERT(TimerBaseValid(ui32Base));
    ASSERT((ui32Timer == TIMER_A) || (ui32Timer == TIMER_B) || (ui32Timer == TIMER_BOTH));
    ASSERT((ui32Mode == TIMER_INTERVALLOAD_NEXTCYCLE) || (ui32Mode == TIMER_INTERVALLOAD_TIMEOUT));

    // Set mode for timer A
    if(ui32Timer & TIMER_A)
    {
        if(ui32Mode == TIMER_INTERVALLOAD_NEXTCYCLE)
        {
            HWREG(ui32Base + GPT_O_TAMR) &= ~(GPT_TAMR_TAILD);
        }
        else
        {
            HWREG(ui32Base + GPT_O_TAMR) |= GPT_TAMR_TAILD;
        }
    }

    // Set mode for timer B
    if(ui32Timer & TIMER_B)
    {
        if(ui32Mode == TIMER_INTERVALLOAD_NEXTCYCLE)
        {
            HWREG(ui32Base + GPT_O_TBMR) &= ~(GPT_TBMR_TBILD);
        }
        else
        {
            HWREG(ui32Base + GPT_O_TBMR) |= GPT_TBMR_TBILD;
        }
    }
}

void TRNGConfigure(uint32_t ui32MinSamplesPerCycle, uint32_t ui32MaxSamplesPerCycle, uint32_t ui32ClocksPerSample) {
    uint32_t ui32Val;

    // Make sure the TRNG is disabled.
    ui32Val = HWREG(TRNG_BASE + TRNG_O_CTL) & ~TRNG_CTL_TRNG_EN;
    HWREG(TRNG_BASE + TRNG_O_CTL) = ui32Val;

    // Configure the startup number of samples.
    ui32Val &= ~TRNG_CTL_STARTUP_CYCLES_M;
    ui32Val |= ((( ui32MaxSamplesPerCycle >> 8 ) << TRNG_CTL_STARTUP_CYCLES_S ) & TRNG_CTL_STARTUP_CYCLES_M );
    HWREG(TRNG_BASE + TRNG_O_CTL) = ui32Val;

    // Configure the minimum and maximum number of samples pr generated number
    // and the number of clocks per sample.
    HWREG(TRNG_BASE + TRNG_O_CFG0) = (
        ((( ui32MaxSamplesPerCycle >> 8 ) << TRNG_CFG0_MAX_REFILL_CYCLES_S ) & TRNG_CFG0_MAX_REFILL_CYCLES_M ) |
        ((( ui32ClocksPerSample         ) << TRNG_CFG0_SMPL_DIV_S          ) & TRNG_CFG0_SMPL_DIV_M          ) |
        ((( ui32MinSamplesPerCycle >> 6 ) << TRNG_CFG0_MIN_REFILL_CYCLES_S ) & TRNG_CFG0_MIN_REFILL_CYCLES_M )   );
}

uint32_t TRNGNumberGet(uint32_t ui32Word) {
    uint32_t ui32RandomNumber;

    // Check the arguments.
    ASSERT((ui32Word == TRNG_HI_WORD) ||
           (ui32Word == TRNG_LOW_WORD));

    // Return the right requested part of the generated number.
    if(ui32Word == TRNG_HI_WORD)
    {
        ui32RandomNumber = HWREG(TRNG_BASE + TRNG_O_OUT1);
    }
    else
    {
        ui32RandomNumber = HWREG(TRNG_BASE + TRNG_O_OUT0);
    }

    // Initiate generation of new number.
    HWREG(TRNG_BASE + TRNG_O_IRQFLAGCLR) = 0x1;

    // Return the random number.
    return ui32RandomNumber;
}

void UARTFIFOLevelGet(uint32_t ui32Base, uint32_t *pui32TxLevel, uint32_t *pui32RxLevel) {
    uint32_t ui32Temp;

    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));

    // Read the FIFO level register.
    ui32Temp = HWREG(ui32Base + UART_O_IFLS);

    // Extract the transmit and receive FIFO levels.
    *pui32TxLevel = ui32Temp & UART_IFLS_TXSEL_M;
    *pui32RxLevel = ui32Temp & UART_IFLS_RXSEL_M;
}

void UARTConfigSetExpClk(uint32_t ui32Base, uint32_t ui32UARTClk, uint32_t ui32Baud, uint32_t ui32Config) {
    uint32_t ui32Div;

    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));
    ASSERT(ui32Baud != 0);

    // Stop the UART.
    UARTDisable(ui32Base);

    // Compute the fractional baud rate divider.
    ui32Div = (((ui32UARTClk * 8) / ui32Baud) + 1) / 2;

    // Set the baud rate.
    HWREG(ui32Base + UART_O_IBRD) = ui32Div / 64;
    HWREG(ui32Base + UART_O_FBRD) = ui32Div % 64;

    // Set parity, data length, and number of stop bits.
    HWREG(ui32Base + UART_O_LCRH) = ui32Config;
}

void UARTConfigGetExpClk(uint32_t ui32Base, uint32_t ui32UARTClk, uint32_t *pui32Baud, uint32_t *pui32Config) {
    uint32_t ui32Int, ui32Frac;

    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));

    // Compute the baud rate.
    ui32Int = HWREG(ui32Base + UART_O_IBRD);
    ui32Frac = HWREG(ui32Base + UART_O_FBRD);
    *pui32Baud = (ui32UARTClk * 4) / ((64 * ui32Int) + ui32Frac);

    // Get the parity, data length, and number of stop bits.
    *pui32Config = (HWREG(ui32Base + UART_O_LCRH) &
                    (UART_LCRH_SPS | UART_LCRH_WLEN_M | UART_LCRH_STP2 |
                     UART_LCRH_EPS | UART_LCRH_PEN));
}

void UARTDisable(uint32_t ui32Base) {

    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));

    // Wait for end of TX.
    while(HWREG(ui32Base + UART_O_FR) & UART_FR_BUSY)
    {
    }

    // Disable the FIFO.
    HWREG(ui32Base + UART_O_LCRH) &= ~(UART_LCRH_FEN);

    // Disable the UART.
    HWREG(ui32Base + UART_O_CTL) &= ~(UART_CTL_UARTEN | UART_CTL_TXE |
                                      UART_CTL_RXE);
}

int32_t UARTCharGetNonBlocking(uint32_t ui32Base) {
    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));

    // See if there are any characters in the receive FIFO.
    if(!(HWREG(ui32Base + UART_O_FR) & UART_FR_RXFE))
    {
        // Read and return the next character.
        return(HWREG(ui32Base + UART_O_DR));
    }
    else
    {
        // There are no characters, so return a failure.
        return(-1);
    }
}

int32_t UARTCharGet(uint32_t ui32Base) {
    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));

    // Wait until a char is available.
    while(HWREG(ui32Base + UART_O_FR) & UART_FR_RXFE)
    {
    }

    // Now get the character.
    return(HWREG(ui32Base + UART_O_DR));
}

bool UARTCharPutNonBlocking(uint32_t ui32Base, uint8_t ui8Data) {
    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));

    // See if there is space in the transmit FIFO.
    if(!(HWREG(ui32Base + UART_O_FR) & UART_FR_TXFF))
    {
        // Write this character to the transmit FIFO.
        HWREG(ui32Base + UART_O_DR) = ui8Data;

        // Success.
        return(true);
    }
    else
    {
        // There is no space in the transmit FIFO, so return a failure.
        return(false);
    }
}

void UARTCharPut(uint32_t ui32Base, uint8_t ui8Data) {
    // Check the arguments.
    ASSERT(UARTBaseValid(ui32Base));

    // Wait until space is available.
    while(HWREG(ui32Base + UART_O_FR) & UART_FR_TXFF)
    {
    }

    // Send the char.
    HWREG(ui32Base + UART_O_DR) = ui8Data;
}

void uDMAChannelAttributeEnable(uint32_t ui32Base, uint32_t ui32ChannelNum, uint32_t ui32Attr) {
    // Check the arguments.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelNum < UDMA_NUM_CHANNELS);
    ASSERT((ui32Attr & ~(UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT |
                         UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK)) == 0);

    // Set the useburst bit for this channel if set in ui32Attr.
    if(ui32Attr & UDMA_ATTR_USEBURST)
    {
        HWREG(ui32Base + UDMA_O_SETBURST) = 1 << ui32ChannelNum;
    }

    // Set the alternate control select bit for this channel,
    // if set in ui32Attr.
    if(ui32Attr & UDMA_ATTR_ALTSELECT)
    {
        HWREG(ui32Base + UDMA_O_SETCHNLPRIALT) = 1 << ui32ChannelNum;
    }

    // Set the high priority bit for this channel, if set in ui32Attr.
    if(ui32Attr & UDMA_ATTR_HIGH_PRIORITY)
    {
        HWREG(ui32Base + UDMA_O_SETCHNLPRIORITY) = 1 << ui32ChannelNum;
    }

    // Set the request mask bit for this channel, if set in ui32Attr.
    if(ui32Attr & UDMA_ATTR_REQMASK)
    {
        HWREG(ui32Base + UDMA_O_SETREQMASK) = 1 << ui32ChannelNum;
    }
}

void uDMAChannelAttributeDisable(uint32_t ui32Base, uint32_t ui32ChannelNum, uint32_t ui32Attr) {
    // Check the arguments.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelNum < UDMA_NUM_CHANNELS);
    ASSERT((ui32Attr & ~(UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT |
                         UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK)) == 0);

    // Clear the useburst bit for this channel if set in ui32Attr.
    if(ui32Attr & UDMA_ATTR_USEBURST)
    {
        HWREG(ui32Base + UDMA_O_CLEARBURST) = 1 << ui32ChannelNum;
    }

    // Clear the alternate control select bit for this channel, if set in
    // ululAttr.
    if(ui32Attr & UDMA_ATTR_ALTSELECT)
    {
        HWREG(ui32Base + UDMA_O_CLEARCHNLPRIALT) = 1 << ui32ChannelNum;
    }

    // Clear the high priority bit for this channel, if set in ui32Attr.
    if(ui32Attr & UDMA_ATTR_HIGH_PRIORITY)
    {
        HWREG(ui32Base + UDMA_O_CLEARCHNLPRIORITY) = 1 << ui32ChannelNum;
    }

    // Clear the request mask bit for this channel, if set in ui32Attr.
    if(ui32Attr & UDMA_ATTR_REQMASK)
    {
        HWREG(ui32Base + UDMA_O_CLEARREQMASK) = 1 << ui32ChannelNum;
    }
}

uint32_t uDMAChannelAttributeGet(uint32_t ui32Base, uint32_t ui32ChannelNum) {
    uint32_t ui32Attr = 0;

    // Check the arguments.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelNum < UDMA_NUM_CHANNELS);

    // Check to see if useburst bit is set for this channel.
    if(HWREG(ui32Base + UDMA_O_SETBURST) & (1 << ui32ChannelNum))
    {
        ui32Attr |= UDMA_ATTR_USEBURST;
    }

    // Check to see if the alternate control bit is set for this channel.
    if(HWREG(ui32Base + UDMA_O_SETCHNLPRIALT) & (1 << ui32ChannelNum))
    {
        ui32Attr |= UDMA_ATTR_ALTSELECT;
    }

    // Check to see if the high priority bit is set for this channel.
    if(HWREG(ui32Base + UDMA_O_SETCHNLPRIORITY) & (1 << ui32ChannelNum))
    {
        ui32Attr |= UDMA_ATTR_HIGH_PRIORITY;
    }

    // Check to see if the request mask bit is set for this channel.
    if(HWREG(ui32Base + UDMA_O_SETREQMASK) & (1 << ui32ChannelNum))
    {
        ui32Attr |= UDMA_ATTR_REQMASK;
    }

    // Return the configuration flags.
    return(ui32Attr);
}

void uDMAChannelControlSet(uint32_t ui32Base, uint32_t ui32ChannelStructIndex, uint32_t ui32Control) {
    tDMAControlTable *pControlTable;

    // Check the arguments.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelStructIndex < (UDMA_NUM_CHANNELS * 2));
    ASSERT(HWREG(ui32Base + UDMA_O_CTRL) != 0);

    // Get the base address of the control table.
    pControlTable = (tDMAControlTable *)HWREG(ui32Base + UDMA_O_CTRL);

    // Get the current control word value and mask off the fields to be
    // changed, then OR in the new settings.
    pControlTable[ui32ChannelStructIndex].ui32Control =
        ((pControlTable[ui32ChannelStructIndex].ui32Control &
          ~(UDMA_DST_INC_M |
            UDMA_SRC_INC_M |
            UDMA_SIZE_M |
            UDMA_ARB_M |
            UDMA_NEXT_USEBURST)) |
         ui32Control);
}

void uDMAChannelTransferSet(uint32_t ui32Base, uint32_t ui32ChannelStructIndex, uint32_t ui32Mode, void *pvSrcAddr, void *pvDstAddr, uint32_t ui32TransferSize) {
    tDMAControlTable *pControlTable;
    uint32_t ui32Control;
    uint32_t ui32Inc;
    uint32_t ui32BufferBytes;

    // Check the arguments.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelStructIndex < (UDMA_NUM_CHANNELS * 2));
    ASSERT(HWREG(ui32Base + UDMA_O_CTRL) != 0);
    ASSERT(ui32Mode <= UDMA_MODE_PER_SCATTER_GATHER);
    ASSERT((uint32_t)pvSrcAddr >= SRAM_BASE);
    ASSERT((uint32_t)pvDstAddr >= SRAM_BASE);
    ASSERT((ui32TransferSize != 0) && (ui32TransferSize <= UDMA_XFER_SIZE_MAX));

    // Get the base address of the control table.
    pControlTable = (tDMAControlTable *)HWREG(ui32Base + UDMA_O_CTRL);

    // Get the current control word value and mask off the mode and size
    // fields.
    ui32Control = (pControlTable[ui32ChannelStructIndex].ui32Control &
                   ~(UDMA_XFER_SIZE_M | UDMA_MODE_M));

    // Adjust the mode if the alt control structure is selected.
    if(ui32ChannelStructIndex & UDMA_ALT_SELECT)
    {
        if((ui32Mode == UDMA_MODE_MEM_SCATTER_GATHER) ||
           (ui32Mode == UDMA_MODE_PER_SCATTER_GATHER))
        {
            ui32Mode |= UDMA_MODE_ALT_SELECT;
        }
    }

    // Set the transfer size and mode in the control word (but don't write the
    // control word yet as it could kick off a transfer).
    ui32Control |= ui32Mode | ((ui32TransferSize - 1) << UDMA_XFER_SIZE_S);

    // Get the address increment value for the source, from the control word.
    ui32Inc = (ui32Control & UDMA_SRC_INC_M);

    // Compute the ending source address of the transfer.  If the source
    // increment is set to none, then the ending address is the same as the
    // beginning.
    if(ui32Inc != UDMA_SRC_INC_NONE)
    {
        ui32Inc = ui32Inc >> UDMA_SRC_INC_S;
        ui32BufferBytes = ui32TransferSize << ui32Inc;
        pvSrcAddr = (void *)((uint32_t)pvSrcAddr + ui32BufferBytes - (1 << ui32Inc));
    }

    // Load the source ending address into the control block.
    pControlTable[ui32ChannelStructIndex].pvSrcEndAddr = pvSrcAddr;

    // Get the address increment value for the destination, from the control
    // word.
    ui32Inc = ui32Control & UDMA_DST_INC_M;

    // Compute the ending destination address of the transfer.  If the
    // destination increment is set to none, then the ending address is the
    // same as the beginning.
    if(ui32Inc != UDMA_DST_INC_NONE)
    {
        // There is a special case if this is setting up a scatter-gather
        // transfer.  The destination pointer needs to point to the end of
        // the alternate structure for this channel instead of calculating
        // the end of the buffer in the normal way.
        if((ui32Mode == UDMA_MODE_MEM_SCATTER_GATHER) ||
           (ui32Mode == UDMA_MODE_PER_SCATTER_GATHER))
        {
            pvDstAddr =
                (void *)&pControlTable[ui32ChannelStructIndex |
                                       UDMA_ALT_SELECT].ui32Spare;
        }
        // Not a scatter-gather transfer, calculate end pointer normally.
        else
        {
            ui32Inc = ui32Inc >> UDMA_DST_INC_S;
            ui32BufferBytes = ui32TransferSize << ui32Inc;
            pvDstAddr = (void *)((uint32_t)pvDstAddr + ui32BufferBytes - 1);
        }
    }

    // Load the destination ending address into the control block.
    pControlTable[ui32ChannelStructIndex].pvDstEndAddr = pvDstAddr;

    // Write the new control word value.
    pControlTable[ui32ChannelStructIndex].ui32Control = ui32Control;
}

void uDMAChannelScatterGatherSet(uint32_t ui32Base, uint32_t ui32ChannelNum, uint32_t ui32TaskCount, void *pvTaskList, uint32_t ui32IsPeriphSG) {
    tDMAControlTable *pControlTable;
    tDMAControlTable *pTaskTable;

    // Check the parameters.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelNum < UDMA_NUM_CHANNELS);
    ASSERT(HWREG(ui32Base + UDMA_O_CTRL) != 0);
    ASSERT(pvTaskList != 0);
    ASSERT(ui32TaskCount <= UDMA_XFER_SIZE_MAX);
    ASSERT(ui32TaskCount != 0);

    // Get the base address of the control table.
    pControlTable = (tDMAControlTable *)HWREG(ui32Base + UDMA_O_CTRL);

    // Get a handy pointer to the task list.
    pTaskTable = (tDMAControlTable *)pvTaskList;

    // Compute the ending address for the source pointer. This will be the
    // last element of the last task in the task table.
    pControlTable[ui32ChannelNum].pvSrcEndAddr =
        &pTaskTable[ui32TaskCount - 1].ui32Spare;

    // Compute the ending address for the destination pointer. This will be
    // the end of the alternate structure for this channel.
    pControlTable[ui32ChannelNum].pvDstEndAddr =
        &pControlTable[ui32ChannelNum | UDMA_ALT_SELECT].ui32Spare;

    // Compute the control word.  Most configurable items are fixed for
    // scatter-gather. Item and increment sizes are all 32-bit and arb
    // size must be 4. The count is the number of items in the task list
    // times 4 (4 words per task).
    pControlTable[ui32ChannelNum].ui32Control =
        (UDMA_DST_INC_32 | UDMA_SRC_INC_32 |
         UDMA_SIZE_32 | UDMA_ARB_4 |
         (((ui32TaskCount * 4) - 1) << UDMA_XFER_SIZE_S) |
         (ui32IsPeriphSG ? UDMA_MODE_PER_SCATTER_GATHER :
          UDMA_MODE_MEM_SCATTER_GATHER));

    // Scatter-gather operations can leave the alt bit set.  So if doing
    // back to back scatter-gather transfers, the second attempt may not
    // work correctly because the alt bit is set.  Therefore, clear the
    // alt bit here to ensure that it is always cleared before a new SG
    // transfer is started.
    HWREG(ui32Base + UDMA_O_CLEARCHNLPRIALT) = 1 << ui32ChannelNum;

}

uint32_t uDMAChannelSizeGet(uint32_t ui32Base, uint32_t ui32ChannelStructIndex) {
    tDMAControlTable *pControlTable;
    uint32_t ui32Control;

    // Check the arguments.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelStructIndex < (UDMA_NUM_CHANNELS * 2));
    ASSERT(HWREG(ui32Base + UDMA_O_CTRL) != 0);

    // Get the base address of the control table.
    pControlTable = (tDMAControlTable *)HWREG(ui32Base + UDMA_O_CTRL);

    // Get the current control word value and mask off all but the size field
    // and the mode field.
    ui32Control = (pControlTable[ui32ChannelStructIndex].ui32Control &
                   (UDMA_XFER_SIZE_M | UDMA_MODE_M));

    // If the size field and mode field are 0 then the transfer is finished
    // and there are no more items to transfer.
    if(ui32Control == 0)
    {
        return(0);
    }

    // Otherwise, if either the size field or more field is non-zero, then
    // not all the items have been transferred.
    else
    {
        // Shift the size field and add one, then return to user.
        return((ui32Control >> UDMA_XFER_SIZE_S) + 1);
    }
}

uint32_t uDMAChannelModeGet(uint32_t ui32Base, uint32_t ui32ChannelStructIndex) {
    tDMAControlTable *pControlTable;
    uint32_t ui32Control;

    // Check the arguments.
    ASSERT(uDMABaseValid(ui32Base));
    ASSERT(ui32ChannelStructIndex < (UDMA_NUM_CHANNELS * 2));
    ASSERT(HWREG(ui32Base + UDMA_O_CTRL) != 0);

    // Get the base address of the control table.
    pControlTable = (tDMAControlTable *)HWREG(ui32Base + UDMA_O_CTRL);

    // Get the current control word value and mask off all but the mode field.
    ui32Control = (pControlTable[ui32ChannelStructIndex].ui32Control &
                   UDMA_MODE_M);

    // Check if scatter/gather mode, and if so, mask off the alt bit.
    if(((ui32Control & ~UDMA_MODE_ALT_SELECT) == UDMA_MODE_MEM_SCATTER_GATHER) ||
       ((ui32Control & ~UDMA_MODE_ALT_SELECT) == UDMA_MODE_PER_SCATTER_GATHER))
    {
        ui32Control &= ~UDMA_MODE_ALT_SELECT;
    }

    // Return the mode to the caller.
    return(ui32Control);
}

void VIMSConfigure(uint32_t ui32Base, bool bRoundRobin, bool bPrefetch) {
    uint32_t ui32Reg;

    // Check the arguments.
    ASSERT(VIMSBaseValid(ui32Base));

    ui32Reg = HWREG(ui32Base + VIMS_O_CTL);
    ui32Reg &= ~(VIMS_CTL_PREF_EN | VIMS_CTL_ARB_CFG);
    if(bRoundRobin)
    {
        ui32Reg |= VIMS_CTL_ARB_CFG;
    }
    if(bPrefetch)
    {
        ui32Reg |= VIMS_CTL_PREF_EN;
    }

    // Set the Arbitration and prefetch mode.
    HWREG(ui32Base + VIMS_O_CTL) = ui32Reg;
}

void VIMSModeSet(uint32_t ui32Base, uint32_t ui32Mode) {
    uint32_t ui32Reg;

    // Check the arguments.
    ASSERT(VIMSBaseValid(ui32Base));

    ASSERT((ui32Mode == VIMS_MODE_DISABLED)   ||
           (ui32Mode == VIMS_MODE_ENABLED)    ||
           (ui32Mode == VIMS_MODE_OFF));

    // Set the mode.
    ui32Reg = HWREG(ui32Base + VIMS_O_CTL);
    ui32Reg &= ~VIMS_CTL_MODE_M;
    ui32Reg |= (ui32Mode & VIMS_CTL_MODE_M);

    HWREG(ui32Base + VIMS_O_CTL) = ui32Reg;
}

uint32_t VIMSModeGet(uint32_t ui32Base) {
    uint32_t ui32Reg;

    // Check the arguments.
    ASSERT(VIMSBaseValid(ui32Base));

    ui32Reg = HWREG(ui32Base + VIMS_O_STAT);
    if(ui32Reg & VIMS_STAT_MODE_CHANGING)
    {
        return (VIMS_MODE_CHANGING);
    }
    else
    {
        return (ui32Reg & VIMS_STAT_MODE_M);
    }
}

void VIMSModeSafeSet( uint32_t ui32Base, uint32_t ui32NewMode, bool blocking ) {
    uint32_t currentMode;

    // Check the arguments.
    ASSERT(VIMSBaseValid(ui32Base));
    ASSERT((ui32NewMode == VIMS_MODE_DISABLED)   ||
           (ui32NewMode == VIMS_MODE_ENABLED)    ||
           (ui32NewMode == VIMS_MODE_OFF));

    // Make sure that only the mode bits are set in the input parameter
    // (done just for security since it is critical to the code flow)
    ui32NewMode &= VIMS_CTL_MODE_M;

    // Wait for any pending change to complete and get current VIMS mode
    // (This is a blocking point but will typically only be a blocking point
    // only if mode is changed multiple times with blocking=0)
    do {
        currentMode = VIMSModeGet( ui32Base );
    } while ( currentMode == VIMS_MODE_CHANGING );

    // First check that it actually is a mode change request
    if ( ui32NewMode != currentMode ) {
        // Set new mode
        VIMSModeSet( ui32Base, ui32NewMode );

        // Wait for final mode change to complete - if blocking is requested
        if ( blocking ) {
            while ( HWREGBITW( VIMS_BASE + VIMS_O_STAT, VIMS_STAT_MODE_CHANGING_BITN )) {
                // Do nothing - wait for change to complete.
            }
        }
    }
}

void OSCClockSourceSet(uint32_t ui32SrcClk, uint32_t ui32Osc) {
    // Check the arguments.
    ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
           (ui32SrcClk & OSC_SRC_CLK_HF));
    ASSERT((ui32Osc == OSC_RCOSC_HF) ||
           (ui32Osc == OSC_RCOSC_LF) ||
           (ui32Osc == OSC_XOSC_HF) ||
           (ui32Osc == OSC_XOSC_LF));

    // Request the high frequency source clock (using 24 MHz XTAL)
    if(ui32SrcClk & OSC_SRC_CLK_HF)
    {
        // Enable the HF XTAL as HF clock source
        DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
                           DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_M,
                           DDI_0_OSC_CTL0_SCLK_HF_SRC_SEL_S,
                           ui32Osc);
    }

    // Configure the low frequency source clock.
    if(ui32SrcClk & OSC_SRC_CLK_LF)
    {
        // Change the clock source.
        DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0,
                           DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_M,
                           DDI_0_OSC_CTL0_SCLK_LF_SRC_SEL_S,
                           ui32Osc);
    }
}

uint32_t OSCClockSourceGet(uint32_t ui32SrcClk) {
    uint32_t ui32ClockSource;

    // Check the arguments.
    ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
           (ui32SrcClk & OSC_SRC_CLK_HF));

    // Return the source for the selected clock.
    if(ui32SrcClk == OSC_SRC_CLK_LF)
    {
        ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
                                            DDI_0_OSC_STAT0_SCLK_LF_SRC_M,
                                            DDI_0_OSC_STAT0_SCLK_LF_SRC_S);
    }
    else
    {
        ui32ClockSource = DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_STAT0,
                                            DDI_0_OSC_STAT0_SCLK_HF_SRC_M,
                                            DDI_0_OSC_STAT0_SCLK_HF_SRC_S);
    }
    return (ui32ClockSource);
}

int32_t OSC_HPOSCRelativeFrequencyOffsetGet( int32_t tempDegC ) {
   // Estimate HPOSC frequency, using temperature and curve fitting parameters
   uint32_t fitParams = HWREG(FCFG1_BASE + FCFG1_O_FREQ_OFFSET);
   // Extract the P0,P1,P2 params, and sign extend them via shifting up/down
   int32_t paramP0 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_S))
                                             >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_W));
   int32_t paramP1 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_S))
                                             >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_W));
   int32_t paramP2 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_S))
                                             >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_W));
   int32_t paramP3 = ((((int32_t) HWREG(FCFG1_BASE + FCFG1_O_MISC_CONF_2))
                                             << (32 - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_W - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_S))
                                             >> (32 - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_W));

   // Now we can find the HPOSC freq offset, given as a signed variable d, expressed by:
   //
   //    F_HPOSC = F_nom * (1 + d/(2^22))    , where: F_HPOSC = HPOSC frequency
   //                                                 F_nom = nominal clock source frequency (e.g. 48.000 MHz)
   //                                                 d = describes relative freq offset

   // We can estimate the d variable, using temperature compensation parameters:
   //
   //    d = P0 + P1*(t - T0) + P2*(t - T0)^2 + P3*(t - T0)^3, where: P0,P1,P2,P3 are curve fitting parameters from FCFG1
   //                                                 t = current temperature (from temp sensor) in deg C
   //                                                 T0 = 27 deg C (fixed temperature constant)
   int32_t tempDelta = (tempDegC - 27);
   int32_t tempDeltaX2 = tempDelta * tempDelta;
   int32_t d = paramP0 + ((tempDelta*paramP1)>>3) + ((tempDeltaX2*paramP2)>>10) + ((tempDeltaX2*tempDelta*paramP3)>>18);

   return ( d );
}

int16_t OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert( int32_t HPOSC_RelFreqOffset ) {
   // The input argument, hereby referred to simply as "d", describes the frequency offset
   // of the HPOSC relative to the nominal frequency in this way:
   //
   //    F_HPOSC = F_nom * (1 + d/(2^22))
   //
   // But for use by the radio, to compensate the frequency error, we need to find the
   // frequency offset "rfcFreqOffset" defined in the following format:
   //
   //    F_nom = F_HPOSC * (1 + rfCoreFreqOffset/(2^22))
   //
   // To derive "rfCoreFreqOffset" from "d" we combine the two above equations and get:
   //
   //    (1 + rfCoreFreqOffset/(2^22)) = (1 + d/(2^22))^-1
   //
   // Which can be rewritten into:
   //
   //    rfCoreFreqOffset = -d*(2^22) / ((2^22) + d)
   //
   //               = -d * [ 1 / (1 + d/(2^22)) ]
   //
   // To avoid doing a 64-bit division due to the (1 + d/(2^22))^-1 expression,
   // we can use Taylor series (Maclaurin series) to approximate it:
   //
   //       1 / (1 - x) ~= 1 + x + x^2 + x^3 + x^4 + ... etc      (Maclaurin series)
   //
   // In our case, we have x = - d/(2^22), and we only include up to the first
   // order term of the series, as the second order term ((d^2)/(2^44)) is very small:
   //
   //       freqError ~= -d + d^2/(2^22)   (+ small approximation error)
   //
   // The approximation error is negligible for our use.

   int32_t rfCoreFreqOffset = -HPOSC_RelFreqOffset + (( HPOSC_RelFreqOffset * HPOSC_RelFreqOffset ) >> 22 );

   return ( rfCoreFreqOffset );
}

void AUXADCDisable(void) {
    // Disable the ADC reference
    ADI8BitsClear(AUX_ADI4_BASE, ADI_4_AUX_O_ADCREF0, ADI_4_AUX_ADCREF0_EN_M | ADI_4_AUX_ADCREF0_REF_ON_IDLE_M | ADI_4_AUX_ADCREF0_SRC_M);

    // Assert reset and disable the ADC
    ADI8BitsClear(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M | ADI_4_AUX_ADC0_SMPL_MODE_M | ADI_4_AUX_ADC0_SMPL_CYCLE_EXP_M);

    // Ensure that scaling is enabled by default before next use of the ADC
    ADI8BitsClear(AUX_ADI4_BASE, ADI_4_AUX_O_ADC1, ADI_4_AUX_ADC1_SCALE_DIS_M);

    // Flush the FIFO before disabling the clocks
    HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 1; // CMD: EN(1) -> FLUSH(3)
    HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 0; // CMD: FLUSH(3) -> EN(1)

    // Disable the ADC clock (no need to wait since IOB_WUC_ADCCLKCTL_ACK goes low immediately)
    HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) = 0;

    // Disable the ADC data interface
    HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = 0;
}

void AUXADCEnableAsync(uint32_t refSource, uint32_t trigger) {
    // Enable the ADC reference, with the following options:
    // - SRC: Set when using relative reference
    // - REF_ON_IDLE: Always cleared since there is no idle state in asynchronous operation
    ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADCREF0, refSource | ADI_4_AUX_ADCREF0_EN_M);

    // Enable the ADC clock
    HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) = AUX_SYSIF_ADCCLKCTL_REQ_M;
    while (!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) & AUX_SYSIF_ADCCLKCTL_ACK_M));

    // Enable the ADC data interface
    if (trigger == AUXADC_TRIGGER_MANUAL) {
        // Manual trigger: No need to configure event routing from GPT
        HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_NO_EVENT | AUX_ANAIF_ADCCTL_CMD_EN;
    } else {
        // GPT trigger: Configure event routing via MCU_EV to the AUX domain
        HWREG(EVENT_BASE + EVENT_O_AUXSEL0) = trigger;
        HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_MCU_EV | AUX_ANAIF_ADCCTL_CMD_EN;
    }

    // Configure the ADC
    ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_SMPL_MODE_M);

    // Release reset and enable the ADC
    ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M);
}

void AUXADCEnableSync(uint32_t refSource, uint32_t sampleTime, uint32_t trigger) {
    // Enable the ADC reference, with the following options:
    // - SRC: Set when using relative reference
    // - REF_ON_IDLE: Set when using fixed reference and sample time < 21.3 us
    uint8_t adcref0 = refSource | ADI_4_AUX_ADCREF0_EN_M;
    if (!refSource && (sampleTime < AUXADC_SAMPLE_TIME_21P3_US)) {
        adcref0 |= ADI_4_AUX_ADCREF0_REF_ON_IDLE_M;
    }
    ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADCREF0, adcref0);

    // Enable the ADC clock
    HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) = AUX_SYSIF_ADCCLKCTL_REQ_M;
    while (!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) & AUX_SYSIF_ADCCLKCTL_ACK_M));

    // Enable the ADC data interface
    if (trigger == AUXADC_TRIGGER_MANUAL) {
        // Manual trigger: No need to configure event routing from GPT
        HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_NO_EVENT | AUX_ANAIF_ADCCTL_CMD_EN;
    } else {
        // GPT trigger: Configure event routing via MCU_EV to the AUX domain
        HWREG(EVENT_BASE + EVENT_O_AUXSEL0) = trigger;
        HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_MCU_EV | AUX_ANAIF_ADCCTL_CMD_EN;
    }

    // Configure the ADC
    ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, sampleTime << ADI_4_AUX_ADC0_SMPL_CYCLE_EXP_S);

    // Release reset and enable the ADC
    ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M);
}

void AUXADCDisableInputScaling(void) {
    ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC1, ADI_4_AUX_ADC1_SCALE_DIS_M);
}

void AUXADCFlushFifo(void) {
    HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 1; // CMD: EN(1) -> FLUSH(3)
    HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 0; // CMD: FLUSH(3) -> EN(1)
}

uint32_t AUXADCReadFifo(void) {

    // Wait until there is at least one sample in the FIFO
    while (HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFOSTAT) & AUX_ANAIF_ADCFIFOSTAT_EMPTY_M);

    // Return the first sample from the FIFO
    return HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFO);
}

uint32_t AUXADCPopFifo(void) {

    // Return the first sample from the FIFO. If the FIFO is empty, this
    // generates ADC FIFO underflow
    return HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFO);
}

int32_t AUXADCGetAdjustmentGain(uint32_t refSource) {
    int32_t gain;
    if (refSource == AUXADC_REF_FIXED) {
        // AUXADC_REF_FIXED ==> ABS_GAIN
        gain = (HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_ABS_GAIN) & FCFG1_SOC_ADC_ABS_GAIN_SOC_ADC_ABS_GAIN_TEMP1_M) >> FCFG1_SOC_ADC_ABS_GAIN_SOC_ADC_ABS_GAIN_TEMP1_S;
    } else {
      // AUXADC_REF_VDDS_REL ==> REL_GAIN
        gain = (HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_REL_GAIN) & FCFG1_SOC_ADC_REL_GAIN_SOC_ADC_REL_GAIN_TEMP1_M) >> FCFG1_SOC_ADC_REL_GAIN_SOC_ADC_REL_GAIN_TEMP1_S;
    }
    return gain;
}

int32_t AUXADCGetAdjustmentOffset(uint32_t refSource) {
    int8_t offset;
    if ( refSource == AUXADC_REF_FIXED ) {
        // AUXADC_REF_FIXED ==> ABS_OFFSET
        offset = HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_OFFSET_INT) >> FCFG1_SOC_ADC_OFFSET_INT_SOC_ADC_ABS_OFFSET_TEMP1_S;
    } else {
        // AUXADC_REF_VDDS_REL ==> REL_OFFSET
        offset = HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_OFFSET_INT) >> FCFG1_SOC_ADC_OFFSET_INT_SOC_ADC_REL_OFFSET_TEMP1_S;
    }
    return offset;
}

int32_t AUXADCValueToMicrovolts(int32_t fixedRefVoltage, int32_t adcValue) {
    // Chop off 4 bits during calculations to avoid 32-bit overflow
    fixedRefVoltage >>= 4;
    return (((adcValue * fixedRefVoltage) + 2047) / 4095) << 4;
}

int32_t AUXADCMicrovoltsToValue(int32_t fixedRefVoltage, int32_t microvolts) {
    // Chop off 4 bits during calculations to avoid 32-bit overflow
    fixedRefVoltage >>= 4;
    microvolts >>= 4;
    return ((microvolts * 4095) + (fixedRefVoltage / 2)) / fixedRefVoltage;
}

int32_t AUXADCAdjustValueForGainAndOffset(int32_t adcValue, int32_t gain, int32_t offset) {
    // Apply gain and offset adjustment
    adcValue = (((adcValue + offset) * gain) + 16384) / 32768;

    // Saturate
    if (adcValue < 0) {
        return 0;
    } else if (adcValue > 4095) {
        return 4095;
    } else {
        return adcValue;
    }
}

int32_t AUXADCUnadjustValueForGainAndOffset(int32_t adcValue, int32_t gain, int32_t offset) {
    // Apply inverse gain and offset adjustment
    adcValue = (((adcValue * 32768) + (gain / 2)) / gain) - offset;

    // Saturate
    if (adcValue < 0) {
        return 0;
    } else if (adcValue > 4095) {
        return 4095;
    } else {
        return adcValue;
    }
}

void SysCtrl_DCDC_VoltageConditionalControl( void ) {
   uint32_t batThreshold     ;  // Fractional format with 8 fractional bits.
   uint32_t aonBatmonBat     ;  // Fractional format with 8 fractional bits.
   uint32_t ccfg_ModeConfReg ;  // Holds a copy of the CCFG_O_MODE_CONF register.
   uint32_t aonPmctlPwrctl   ;  // Reflect whats read/written to the AON_PMCTL_O_PWRCTL register.

   // We could potentially call this function before any battery voltage measurement
   // is made/available. In that case we must make sure that we do not turn off the DCDC.
   // This can be done by doing nothing as long as the battery voltage is 0 (Since the
   // reset value of the battery voltage register is 0).
   aonBatmonBat = HWREG( AON_BATMON_BASE + AON_BATMON_O_BAT );
   if ( aonBatmonBat != 0 ) {
      // Check if Voltage Conditional Control is enabled
      // It is enabled if all the following are true:
      // - DCDC in use (either in active or recharge mode), (in use if one of the corresponding CCFG bits are zero).
      // - Alternative DCDC settings are enabled ( DIS_ALT_DCDC_SETTING == 0 )
      // - Not in external regulator mode ( EXT_REG_MODE == 0 )
      ccfg_ModeConfReg = HWREG( CCFG_BASE + CCFG_O_MODE_CONF );

      if (((( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_RECHARGE_M ) == 0                                            ) ||
           (( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_ACTIVE_M   ) == 0                                            )    ) &&
          (( HWREG( AON_PMCTL_BASE  + AON_PMCTL_O_PWRCTL  ) & AON_PMCTL_PWRCTL_EXT_REG_MODE  )               == 0      ) &&
          (( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_ALT_DCDC_SETTING ) == 0      )    )
      {
         aonPmctlPwrctl = HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL );
         batThreshold   = (((( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) &
            CCFG_MODE_CONF_1_ALT_DCDC_VMIN_M ) >>
            CCFG_MODE_CONF_1_ALT_DCDC_VMIN_S ) + 28 ) << 4 );

         if ( aonPmctlPwrctl & ( AON_PMCTL_PWRCTL_DCDC_EN_M | AON_PMCTL_PWRCTL_DCDC_ACTIVE_M )) {
            // DCDC is ON, check if it should be switched off
            if ( aonBatmonBat < batThreshold ) {
               aonPmctlPwrctl &= ~( AON_PMCTL_PWRCTL_DCDC_EN_M | AON_PMCTL_PWRCTL_DCDC_ACTIVE_M );

               HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL ) = aonPmctlPwrctl;
            }
         } else {
            // DCDC is OFF, check if it should be switched on
            if ( aonBatmonBat > batThreshold ) {
               if (( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_RECHARGE_M ) == 0 ) aonPmctlPwrctl |= AON_PMCTL_PWRCTL_DCDC_EN_M     ;
               if (( ccfg_ModeConfReg & CCFG_MODE_CONF_DCDC_ACTIVE_M   ) == 0 ) aonPmctlPwrctl |= AON_PMCTL_PWRCTL_DCDC_ACTIVE_M ;

               HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL ) = aonPmctlPwrctl;
            }
         }
      }
   }
}

uint32_t SysCtrlResetSourceGet( void ) {
   uint32_t aonPmctlResetCtl = HWREG( AON_PMCTL_BASE + AON_PMCTL_O_RESETCTL );

   if ( aonPmctlResetCtl & AON_PMCTL_RESETCTL_WU_FROM_SD_M ) {
      if ( aonPmctlResetCtl & AON_PMCTL_RESETCTL_GPIO_WU_FROM_SD_M ) {
         return ( RSTSRC_WAKEUP_FROM_SHUTDOWN );
      } else {
         return ( RSTSRC_WAKEUP_FROM_TCK_NOISE );
      }
   } else {
      return (( aonPmctlResetCtl & AON_PMCTL_RESETCTL_RESET_SRC_M ) >> AON_PMCTL_RESETCTL_RESET_SRC_S );
   }
}

int32_t AONBatMonTemperatureGetDegC( void ) {
   int32_t  signedTemp        ; // Signed extended temperature with 8 fractional bits
   int32_t  tempCorrection    ; // Voltage dependent temp correction with 8 fractional bits
   int8_t   voltageSlope      ; // Signed byte value representing the TEMP slope with battery voltage, in degrees C/V, with 4 fractional bits.

   // Shift left then right to sign extend the BATMON_TEMP field
   signedTemp = ((((int32_t)HWREG( AON_BATMON_BASE + AON_BATMON_O_TEMP ))
     << ( 32 - AON_BATMON_TEMP_INT_W - AON_BATMON_TEMP_INT_S ))
     >> ( 32 - AON_BATMON_TEMP_INT_W - AON_BATMON_TEMP_INT_S ));

   // Typecasting voltageSlope to int8_t prior to assignment in order to make sure sign extension works properly
   // Using byte read (HWREGB) in order to make more efficient code since voltageSlope is assigned to bits[7:0] of FCFG1_O_MISC_TRIM
   voltageSlope      = ((int8_t)HWREGB( FCFG1_BASE + FCFG1_O_MISC_TRIM ));
   tempCorrection    = (( voltageSlope * (((int32_t)HWREG( AON_BATMON_BASE + AON_BATMON_O_BAT )) - 0x300 )) >> 4 );

   return ((( signedTemp - tempCorrection ) + 0x80 ) >> 8 );
}

void SetupStepVddrTrimTo( uint32_t toCode ) {
    uint32_t    pmctlResetctl_reg   ;
    int32_t     targetTrim          ;
    int32_t     currentTrim         ;

    targetTrim  = SetupSignExtendVddrTrimValue( toCode & ( ADI_3_REFSYS_DCDCCTL0_VDDR_TRIM_M >> ADI_3_REFSYS_DCDCCTL0_VDDR_TRIM_S ));
    currentTrim = SetupSignExtendVddrTrimValue((
        HWREGB( ADI3_BASE + ADI_3_REFSYS_O_DCDCCTL0 ) &
        ADI_3_REFSYS_DCDCCTL0_VDDR_TRIM_M ) >>
        ADI_3_REFSYS_DCDCCTL0_VDDR_TRIM_S ) ;

    if ( targetTrim != currentTrim ) {
        pmctlResetctl_reg = ( HWREG( AON_PMCTL_BASE + AON_PMCTL_O_RESETCTL ) & ~AON_PMCTL_RESETCTL_MCU_WARM_RESET_M );
        if ( pmctlResetctl_reg & AON_PMCTL_RESETCTL_VDDR_LOSS_EN_M ) {
            HWREG( AON_PMCTL_BASE + AON_PMCTL_O_RESETCTL ) = ( pmctlResetctl_reg & ~AON_PMCTL_RESETCTL_VDDR_LOSS_EN_M );
            HWREG( AON_RTC_BASE + AON_RTC_O_SYNC );      // Wait for VDDR_LOSS_EN setting to propagate
        }

        while ( targetTrim != currentTrim ) {
            HWREG( AON_RTC_BASE + AON_RTC_O_SYNCLF );    // Wait for next edge on SCLK_LF (positive or negative)

            if ( targetTrim > currentTrim )  currentTrim++;
            else                             currentTrim--;

            HWREGB( ADI3_BASE + ADI_3_REFSYS_O_DCDCCTL0 ) = (
                ( HWREGB( ADI3_BASE + ADI_3_REFSYS_O_DCDCCTL0 ) & ~ADI_3_REFSYS_DCDCCTL0_VDDR_TRIM_M ) |
                ((((uint32_t)currentTrim) << ADI_3_REFSYS_DCDCCTL0_VDDR_TRIM_S ) &
                                             ADI_3_REFSYS_DCDCCTL0_VDDR_TRIM_M ) );
        }

        HWREG( AON_RTC_BASE + AON_RTC_O_SYNCLF );        // Wait for next edge on SCLK_LF (positive or negative)

        if ( pmctlResetctl_reg & AON_PMCTL_RESETCTL_VDDR_LOSS_EN_M ) {
            HWREG( AON_RTC_BASE + AON_RTC_O_SYNCLF );    // Wait for next edge on SCLK_LF (positive or negative)
            HWREG( AON_RTC_BASE + AON_RTC_O_SYNCLF );    // Wait for next edge on SCLK_LF (positive or negative)
            HWREG( AON_PMCTL_BASE + AON_PMCTL_O_RESETCTL ) = pmctlResetctl_reg;
            HWREG( AON_RTC_BASE + AON_RTC_O_SYNC );      // And finally wait for VDDR_LOSS_EN setting to propagate
        }
    }
}

void SetupAfterColdResetWakeupFromShutDownCfg1( uint32_t ccfg_ModeConfReg ) {
    // Check for CC1352 boost mode
    // The combination VDDR_EXT_LOAD=0 and VDDS_BOD_LEVEL=1 is defined to select boost mode
    if ((( ccfg_ModeConfReg & CCFG_MODE_CONF_VDDR_EXT_LOAD  ) == 0 ) &&
        (( ccfg_ModeConfReg & CCFG_MODE_CONF_VDDS_BOD_LEVEL ) != 0 )    )
    {
        // Set VDDS_BOD trim - using masked write {MASK8:DATA8}
        // - TRIM_VDDS_BOD is bits[7:3] of ADI3..REFSYSCTL1
        // - Needs a positive transition on BOD_BG_TRIM_EN (bit[7] of REFSYSCTL3) to
        //   latch new VDDS BOD. Set to 0 first to guarantee a positive transition.
        HWREGB( ADI3_BASE + ADI_O_CLR + ADI_3_REFSYS_O_REFSYSCTL3 ) = ADI_3_REFSYS_REFSYSCTL3_BOD_BG_TRIM_EN;
        //
        // VDDS_BOD_LEVEL = 1 means that boost mode is selected
        // - Max out the VDDS_BOD trim (=VDDS_BOD_POS_31)
        HWREGH( ADI3_BASE + ADI_O_MASK8B + ( ADI_3_REFSYS_O_REFSYSCTL1 * 2 )) =
            ( ADI_3_REFSYS_REFSYSCTL1_TRIM_VDDS_BOD_M << 8 ) |
            ( ADI_3_REFSYS_REFSYSCTL1_TRIM_VDDS_BOD_POS_31 ) ;
        HWREGB( ADI3_BASE + ADI_O_SET + ADI_3_REFSYS_O_REFSYSCTL3 ) = ADI_3_REFSYS_REFSYSCTL3_BOD_BG_TRIM_EN;

        SetupStepVddrTrimTo(( HWREG( FCFG1_BASE + FCFG1_O_VOLT_TRIM ) &
            FCFG1_VOLT_TRIM_VDDR_TRIM_HH_M ) >>
            FCFG1_VOLT_TRIM_VDDR_TRIM_HH_S ) ;
    }

    // 1.
    // Do not allow DCDC to be enabled if in external regulator mode.
    // Preventing this by setting both the RECHARGE and the ACTIVE bits bit in the CCFG_MODE_CONF copy register (ccfg_ModeConfReg).
    //
    // 2.
    // Adjusted battery monitor low limit in internal regulator mode.
    // This is done by setting AON_BATMON_FLASHPUMPP0_LOWLIM=0 in internal regulator mode.
    if ( HWREG( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL ) & AON_PMCTL_PWRCTL_EXT_REG_MODE ) {
        ccfg_ModeConfReg |= ( CCFG_MODE_CONF_DCDC_RECHARGE_M | CCFG_MODE_CONF_DCDC_ACTIVE_M );
    } else {
        HWREGBITW( AON_BATMON_BASE + AON_BATMON_O_FLASHPUMPP0, AON_BATMON_FLASHPUMPP0_LOWLIM_BITN ) = 0;
    }

    // set the RECHARGE source based upon CCFG:MODE_CONF:DCDC_RECHARGE
    // Note: Inverse polarity
    HWREGBITW( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL, AON_PMCTL_PWRCTL_DCDC_EN_BITN ) =
        ((( ccfg_ModeConfReg >> CCFG_MODE_CONF_DCDC_RECHARGE_S ) & 1 ) ^ 1 );

    // set the ACTIVE source based upon CCFG:MODE_CONF:DCDC_ACTIVE
    // Note: Inverse polarity
    HWREGBITW( AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL, AON_PMCTL_PWRCTL_DCDC_ACTIVE_BITN ) =
        ((( ccfg_ModeConfReg >> CCFG_MODE_CONF_DCDC_ACTIVE_S ) & 1 ) ^ 1 );
}

void SetupAfterColdResetWakeupFromShutDownCfg2( uint32_t ui32Fcfg1Revision, uint32_t ccfg_ModeConfReg ) {
    uint32_t   ui32Trim;

    // Following sequence is required for using XOSCHF, if not included
    // devices crashes when trying to switch to XOSCHF.
    //
    // Trim CAP settings. Get and set trim value for the ANABYPASS_VALUE1
    // register
    ui32Trim = SetupGetTrimForAnabypassValue1( ccfg_ModeConfReg );
    DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ANABYPASSVAL1, ui32Trim);

    // Trim RCOSC_LF. Get and set trim values for the RCOSCLF_RTUNE_TRIM and
    // RCOSCLF_CTUNE_TRIM fields in the XOSCLF_RCOSCLF_CTRL register.
    ui32Trim = SetupGetTrimForRcOscLfRtuneCtuneTrim();
    DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_LFOSCCTL,
                       (DDI_0_OSC_LFOSCCTL_RCOSCLF_CTUNE_TRIM_M |
                        DDI_0_OSC_LFOSCCTL_RCOSCLF_RTUNE_TRIM_M),
                       DDI_0_OSC_LFOSCCTL_RCOSCLF_CTUNE_TRIM_S,
                       ui32Trim);

    // Trim XOSCHF IBIAS THERM. Get and set trim value for the
    // XOSCHF IBIAS THERM bit field in the ANABYPASS_VALUE2 register. Other
    // register bit fields are set to 0.
    ui32Trim = SetupGetTrimForXoscHfIbiastherm();
    DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ANABYPASSVAL2,
                  ui32Trim<<DDI_0_OSC_ANABYPASSVAL2_XOSC_HF_IBIASTHERM_S);

    // Trim AMPCOMP settings required before switch to XOSCHF
    ui32Trim = SetupGetTrimForAmpcompTh2();
    DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_AMPCOMPTH2, ui32Trim);
    ui32Trim = SetupGetTrimForAmpcompTh1();
    DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_AMPCOMPTH1, ui32Trim);
#if ( CCFG_BASE == CCFG_BASE_DEFAULT )
    ui32Trim = SetupGetTrimForAmpcompCtrl( ui32Fcfg1Revision );
#else
    ui32Trim = NOROM_SetupGetTrimForAmpcompCtrl( ui32Fcfg1Revision );
#endif
    DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_AMPCOMPCTL, ui32Trim);

    // Set trim for DDI_0_OSC_ADCDOUBLERNANOAMPCTL_ADC_SH_MODE_EN in accordance to FCFG1 setting
    // This is bit[5] in the DDI_0_OSC_O_ADCDOUBLERNANOAMPCTL register
    // Using MASK4 write + 1 => writing to bits[7:4]
    ui32Trim = SetupGetTrimForAdcShModeEn( ui32Fcfg1Revision );
    HWREGB( AUX_DDI0_OSC_BASE + DDI_O_MASK4B + ( DDI_0_OSC_O_ADCDOUBLERNANOAMPCTL * 2 ) + 1 ) =
      ( 0x20 | ( ui32Trim << 1 ));

    // Set trim for DDI_0_OSC_ADCDOUBLERNANOAMPCTL_ADC_SH_VBUF_EN in accordance to FCFG1 setting
    // This is bit[4] in the DDI_0_OSC_O_ADCDOUBLERNANOAMPCTL register
    // Using MASK4 write + 1 => writing to bits[7:4]
    ui32Trim = SetupGetTrimForAdcShVbufEn( ui32Fcfg1Revision );
    HWREGB( AUX_DDI0_OSC_BASE + DDI_O_MASK4B + ( DDI_0_OSC_O_ADCDOUBLERNANOAMPCTL * 2 ) + 1 ) =
      ( 0x10 | ( ui32Trim ));

    // Set trim for the PEAK_DET_ITRIM, HP_BUF_ITRIM and LP_BUF_ITRIM bit fields
    // in the DDI0_OSC_O_XOSCHFCTL register in accordance to FCFG1 setting.
    // Remaining register bit fields are set to their reset values of 0.
    ui32Trim = SetupGetTrimForXoscHfCtl(ui32Fcfg1Revision);
    DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_XOSCHFCTL, ui32Trim);

    // Set trim for DBLR_LOOP_FILTER_RESET_VOLTAGE in accordance to FCFG1 setting
    // (This is bits [18:17] in DDI_0_OSC_O_ADCDOUBLERNANOAMPCTL)
    // (Using MASK4 write + 4 => writing to bits[19:16] => (4*4))
    // (Assuming: DDI_0_OSC_ADCDOUBLERNANOAMPCTL_DBLR_LOOP_FILTER_RESET_VOLTAGE_S = 17 and
    //  that DDI_0_OSC_ADCDOUBLERNANOAMPCTL_DBLR_LOOP_FILTER_RESET_VOLTAGE_M = 0x00060000)
    ui32Trim = SetupGetTrimForDblrLoopFilterResetVoltage( ui32Fcfg1Revision );
    HWREGB( AUX_DDI0_OSC_BASE + DDI_O_MASK4B + ( DDI_0_OSC_O_ADCDOUBLERNANOAMPCTL * 2 ) + 4 ) =
      ( 0x60 | ( ui32Trim << 1 ));

    // Update DDI_0_OSC_ATESTCTL_ATESTLF_RCOSCLF_IBIAS_TRIM with data from
    // FCFG1_OSC_CONF_ATESTLF_RCOSCLF_IBIAS_TRIM
    // This is DDI_0_OSC_O_ATESTCTL bit[7]
    // ( DDI_0_OSC_O_ATESTCTL is currently hidden (but=0x00000020))
    // Using MASK4 write + 1 => writing to bits[7:4]
    ui32Trim = SetupGetTrimForRcOscLfIBiasTrim( ui32Fcfg1Revision );
    HWREGB( AUX_DDI0_OSC_BASE + DDI_O_MASK4B + ( 0x00000020 * 2 ) + 1 ) =
      ( 0x80 | ( ui32Trim << 3 ));

    // Update DDI_0_OSC_LFOSCCTL_XOSCLF_REGULATOR_TRIM and
    //        DDI_0_OSC_LFOSCCTL_XOSCLF_CMIRRWR_RATIO in one write
    // This can be simplified since the registers are packed together in the same
    // order both in FCFG1 and in the HW register.
    // This spans DDI_0_OSC_O_LFOSCCTL bits[23:18]
    // Using MASK8 write + 4 => writing to bits[23:16]
    ui32Trim = SetupGetTrimForXoscLfRegulatorAndCmirrwrRatio( ui32Fcfg1Revision );
    HWREGH( AUX_DDI0_OSC_BASE + DDI_O_MASK8B + ( DDI_0_OSC_O_LFOSCCTL * 2 ) + 4 ) =
      ( 0xFC00 | ( ui32Trim << 2 ));

    // Set trim the HPM_IBIAS_WAIT_CNT, LPM_IBIAS_WAIT_CNT and IDAC_STEP bit
    // fields in the DDI0_OSC_O_RADCEXTCFG register in accordance to FCFG1 setting.
    // Remaining register bit fields are set to their reset values of 0.
    ui32Trim = SetupGetTrimForRadcExtCfg(ui32Fcfg1Revision);
    DDI32RegWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RADCEXTCFG, ui32Trim);

}

void SetupAfterColdResetWakeupFromShutDownCfg3( uint32_t ccfg_ModeConfReg ) {
    uint32_t   fcfg1OscConf;
    uint32_t   ui32Trim;
#if ( ! defined( SKIP_SCLK_LF_SRC_CHANGE ))
    uint32_t   currentHfClock;
    uint32_t   ccfgExtLfClk;
#endif

    // Examine the XOSC_FREQ field to select 0x1=HPOSC, 0x2=48MHz XOSC, 0x3=24MHz XOSC
    switch (( ccfg_ModeConfReg & CCFG_MODE_CONF_XOSC_FREQ_M ) >> CCFG_MODE_CONF_XOSC_FREQ_S ) {
    case 2 :
        // XOSC source is a 48 MHz crystal
        // Do nothing (since this is the reset setting)
        break;
    case 1 :
        // XOSC source is HPOSC (trim the HPOSC if this is a chip with HPOSC, otherwise skip trimming and default to 24 MHz XOSC)

        fcfg1OscConf = HWREG( FCFG1_BASE + FCFG1_O_OSC_CONF );

        if (( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_OPTION ) == 0 ) {
            // This is a HPOSC chip, apply HPOSC settings
            // Set bit DDI_0_OSC_CTL0_HPOSC_MODE_EN (this is bit 14 in DDI_0_OSC_O_CTL0)
            HWREG( AUX_DDI0_OSC_BASE + DDI_O_SET + DDI_0_OSC_O_CTL0 ) = DDI_0_OSC_CTL0_HPOSC_MODE_EN;

            // ADI_2_REFSYS_HPOSCCTL2_BIAS_HOLD_MODE_EN = FCFG1_OSC_CONF_HPOSC_BIAS_HOLD_MODE_EN   (1 bit)
            // ADI_2_REFSYS_HPOSCCTL2_CURRMIRR_RATIO    = FCFG1_OSC_CONF_HPOSC_CURRMIRR_RATIO      (4 bits)
            // ADI_2_REFSYS_HPOSCCTL1_BIAS_RES_SET      = FCFG1_OSC_CONF_HPOSC_BIAS_RES_SET        (4 bits)
            // ADI_2_REFSYS_HPOSCCTL0_FILTER_EN         = FCFG1_OSC_CONF_HPOSC_FILTER_EN           (1 bit)
            // ADI_2_REFSYS_HPOSCCTL0_BIAS_RECHARGE_DLY = FCFG1_OSC_CONF_HPOSC_BIAS_RECHARGE_DELAY (2 bits)
            // ADI_2_REFSYS_HPOSCCTL0_SERIES_CAP        = FCFG1_OSC_CONF_HPOSC_SERIES_CAP          (2 bits)
            // ADI_2_REFSYS_HPOSCCTL0_DIV3_BYPASS       = FCFG1_OSC_CONF_HPOSC_DIV3_BYPASS         (1 bit)

            HWREG( ADI2_BASE + ADI_2_REFSYS_O_HPOSCCTL2 ) = (( HWREG( ADI2_BASE + ADI_2_REFSYS_O_HPOSCCTL2 ) &
                  ~( ADI_2_REFSYS_HPOSCCTL2_BIAS_HOLD_MODE_EN_M | ADI_2_REFSYS_HPOSCCTL2_CURRMIRR_RATIO_M  )                                                                       ) |
                   ((( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_BIAS_HOLD_MODE_EN_M   ) >> FCFG1_OSC_CONF_HPOSC_BIAS_HOLD_MODE_EN_S   ) << ADI_2_REFSYS_HPOSCCTL2_BIAS_HOLD_MODE_EN_S   ) |
                   ((( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_CURRMIRR_RATIO_M      ) >> FCFG1_OSC_CONF_HPOSC_CURRMIRR_RATIO_S      ) << ADI_2_REFSYS_HPOSCCTL2_CURRMIRR_RATIO_S      )   );
            HWREG( ADI2_BASE + ADI_2_REFSYS_O_HPOSCCTL1 ) = (( HWREG( ADI2_BASE + ADI_2_REFSYS_O_HPOSCCTL1 ) & ~( ADI_2_REFSYS_HPOSCCTL1_BIAS_RES_SET_M )                          ) |
                   ((( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_BIAS_RES_SET_M        ) >> FCFG1_OSC_CONF_HPOSC_BIAS_RES_SET_S        ) << ADI_2_REFSYS_HPOSCCTL1_BIAS_RES_SET_S        )   );
            HWREG( ADI2_BASE + ADI_2_REFSYS_O_HPOSCCTL0 ) = (( HWREG( ADI2_BASE + ADI_2_REFSYS_O_HPOSCCTL0 ) &
                  ~( ADI_2_REFSYS_HPOSCCTL0_FILTER_EN_M | ADI_2_REFSYS_HPOSCCTL0_BIAS_RECHARGE_DLY_M | ADI_2_REFSYS_HPOSCCTL0_SERIES_CAP_M | ADI_2_REFSYS_HPOSCCTL0_DIV3_BYPASS_M )) |
                   ((( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_FILTER_EN_M           ) >> FCFG1_OSC_CONF_HPOSC_FILTER_EN_S           ) << ADI_2_REFSYS_HPOSCCTL0_FILTER_EN_S           ) |
                   ((( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_BIAS_RECHARGE_DELAY_M ) >> FCFG1_OSC_CONF_HPOSC_BIAS_RECHARGE_DELAY_S ) << ADI_2_REFSYS_HPOSCCTL0_BIAS_RECHARGE_DLY_S   ) |
                   ((( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_SERIES_CAP_M          ) >> FCFG1_OSC_CONF_HPOSC_SERIES_CAP_S          ) << ADI_2_REFSYS_HPOSCCTL0_SERIES_CAP_S          ) |
                   ((( fcfg1OscConf & FCFG1_OSC_CONF_HPOSC_DIV3_BYPASS_M         ) >> FCFG1_OSC_CONF_HPOSC_DIV3_BYPASS_S         ) << ADI_2_REFSYS_HPOSCCTL0_DIV3_BYPASS_S         )   );
            break;
        }
        // Not a HPOSC chip - fall through to default
    default :
        // XOSC source is a 24 MHz crystal (default)
        // Set bit DDI_0_OSC_CTL0_XTAL_IS_24M (this is bit 31 in DDI_0_OSC_O_CTL0)
        HWREG( AUX_DDI0_OSC_BASE + DDI_O_SET + DDI_0_OSC_O_CTL0 ) = DDI_0_OSC_CTL0_XTAL_IS_24M;
        break;
    }

    // Set XOSC_HF in bypass mode if CCFG is configured for external TCXO
    // Please note that it is up to the customer to make sure that the external clock source is up and running before XOSC_HF can be used.
    if (( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_TCXO ) == 0 ) {
        HWREG( AUX_DDI0_OSC_BASE + DDI_O_SET + DDI_0_OSC_O_XOSCHFCTL ) = DDI_0_OSC_XOSCHFCTL_BYPASS;
    }

    // Clear DDI_0_OSC_CTL0_CLK_LOSS_EN (ClockLossEventEnable()). This is bit 9 in DDI_0_OSC_O_CTL0.
    // This is typically already 0 except on Lizard where it is set in ROM-boot
    HWREG( AUX_DDI0_OSC_BASE + DDI_O_CLR + DDI_0_OSC_O_CTL0 ) = DDI_0_OSC_CTL0_CLK_LOSS_EN;

    // Setting DDI_0_OSC_CTL1_XOSC_HF_FAST_START according to value found in FCFG1
    ui32Trim = SetupGetTrimForXoscHfFastStart();
    HWREGB( AUX_DDI0_OSC_BASE + DDI_O_MASK4B + ( DDI_0_OSC_O_CTL1 * 2 )) = ( 0x30 | ui32Trim );

#if ( defined( SKIP_SCLK_LF_SRC_CHANGE ))
    // If SKIP_SCLK_LF_SRC_CHANGE is defined we will skip the switch-statement below, and
    // remain on RCOSCHFDLF as LF source (since RCOSCHFDLF is the reset setting).
    // (RCOSCHFDLF = Low frequency clock derived from RCOSC_HF).
    // Note1: Function SetupAfterColdResetWakeupFromShutDownCfg3() is a ROM function in some devices (R2 and Agama)
    //        It must be removed from rom.h on these devices in order to avoid that the ROM version is called.
    // Note2: It's not possible to enter STANDBY when running the LF clock from RCOSCHFDLF, however IDLE will work just fine.
    // RCOSCHFDLF (RCOSC_HF/1536) -> SCLK_LF (=31250 Hz)
    SetupSetAonRtcSubSecInc( 0x8637BD ); // RTC_INCREMENT = 2^38 / frequency
#else
    // setup the LF clock based upon CCFG:MODE_CONF:SCLK_LF_OPTION
    switch (( ccfg_ModeConfReg & CCFG_MODE_CONF_SCLK_LF_OPTION_M ) >> CCFG_MODE_CONF_SCLK_LF_OPTION_S ) {
    case 0 : // XOSC_HF_DLF (XOSCHF/1536) -> SCLK_LF (=31250 Hz)
        OSCClockSourceSet( OSC_SRC_CLK_LF, OSC_XOSC_HF );
        SetupSetAonRtcSubSecInc( 0x8637BD ); // RTC_INCREMENT = 2^38 / frequency
        break;
    case 1 : // EXTERNAL signal -> SCLK_LF (frequency=2^38/CCFG_EXT_LF_CLK_RTC_INCREMENT)
        // Set SCLK_LF to use the same source as SCLK_HF
        // Can be simplified a bit since possible return values for HF matches LF settings
        currentHfClock = OSCClockSourceGet( OSC_SRC_CLK_HF );
        OSCClockSourceSet( OSC_SRC_CLK_LF, currentHfClock );
        while( OSCClockSourceGet( OSC_SRC_CLK_LF ) != currentHfClock ) {
            // Wait until switched
        }
        ccfgExtLfClk = HWREG( CCFG_BASE + CCFG_O_EXT_LF_CLK );
        SetupSetAonRtcSubSecInc(( ccfgExtLfClk & CCFG_EXT_LF_CLK_RTC_INCREMENT_M ) >> CCFG_EXT_LF_CLK_RTC_INCREMENT_S );
        IOCPortConfigureSet(( ccfgExtLfClk & CCFG_EXT_LF_CLK_DIO_M ) >> CCFG_EXT_LF_CLK_DIO_S,
                              IOC_PORT_AON_CLK32K,
                              IOC_STD_INPUT | IOC_HYST_ENABLE );   // Route external clock to AON IOC w/hysteresis
                                                                   // Set XOSC_LF in bypass mode to allow external 32 kHz clock
        HWREG( AUX_DDI0_OSC_BASE + DDI_O_SET + DDI_0_OSC_O_CTL0 ) = DDI_0_OSC_CTL0_XOSC_LF_DIG_BYPASS;
        // Fall through to set XOSC_LF as SCLK_LF source
    case 2 : // XOSC_LF -> SLCK_LF (32768 Hz)
        OSCClockSourceSet( OSC_SRC_CLK_LF, OSC_XOSC_LF );
        break;
    default : // (=3) RCOSC_LF
        OSCClockSourceSet( OSC_SRC_CLK_LF, OSC_RCOSC_LF );
        break;
    }
#endif // ( ! defined( SKIP_SCLK_LF_SRC_CHANGE ))

    // Update ADI_4_AUX_ADCREF1_VTRIM with value from FCFG1
    HWREGB( AUX_ADI4_BASE + ADI_4_AUX_O_ADCREF1 ) =
      ((( HWREG( FCFG1_BASE + FCFG1_O_SOC_ADC_REF_TRIM_AND_OFFSET_EXT ) >>
      FCFG1_SOC_ADC_REF_TRIM_AND_OFFSET_EXT_SOC_ADC_REF_VOLTAGE_TRIM_TEMP1_S ) <<
      ADI_4_AUX_ADCREF1_VTRIM_S ) &
      ADI_4_AUX_ADCREF1_VTRIM_M );

    // Sync with AON
    SysCtrlAonSync();
}

uint32_t SetupGetTrimForAnabypassValue1( uint32_t ccfg_ModeConfReg ) {
    uint32_t ui32Fcfg1Value            ;
    uint32_t ui32XoscHfRow             ;
    uint32_t ui32XoscHfCol             ;
    uint32_t ui32TrimValue             ;

    // Use device specific trim values located in factory configuration
    // area for the XOSC_HF_COLUMN_Q12 and XOSC_HF_ROW_Q12 bit fields in
    // the ANABYPASS_VALUE1 register. Value for the other bit fields
    // are set to 0.

    ui32Fcfg1Value = HWREG(FCFG1_BASE + FCFG1_O_CONFIG_OSC_TOP);
    ui32XoscHfRow = (( ui32Fcfg1Value &
        FCFG1_CONFIG_OSC_TOP_XOSC_HF_ROW_Q12_M ) >>
        FCFG1_CONFIG_OSC_TOP_XOSC_HF_ROW_Q12_S );
    ui32XoscHfCol = (( ui32Fcfg1Value &
        FCFG1_CONFIG_OSC_TOP_XOSC_HF_COLUMN_Q12_M ) >>
        FCFG1_CONFIG_OSC_TOP_XOSC_HF_COLUMN_Q12_S );

    if (( ccfg_ModeConfReg & CCFG_MODE_CONF_XOSC_CAP_MOD ) == 0 ) {
        // XOSC_CAP_MOD = 0 means: CAP_ARRAY_DELTA is in use -> Apply compensation
        // XOSC_CAPARRAY_DELTA is located in bit[15:8] of ccfg_ModeConfReg
        // Note: HW_REV_DEPENDENT_IMPLEMENTATION. Field width is not given by
        // a define and sign extension must therefore be hard coded.
        // ( A small test program is created verifying the code lines below:
        //   Ref.: ..\test\small_standalone_test_programs\CapArrayDeltaAdjust_test.c)
        int32_t i32CustomerDeltaAdjust =
            (((int32_t)( ccfg_ModeConfReg << ( 32 - CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_W - CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_S )))
                                          >> ( 32 - CCFG_MODE_CONF_XOSC_CAPARRAY_DELTA_W ));

        while ( i32CustomerDeltaAdjust < 0 ) {
            ui32XoscHfCol >>= 1;                              // COL 1 step down
            if ( ui32XoscHfCol == 0 ) {                       // if COL below minimum
                ui32XoscHfCol = 0xFFFF;                       //   Set COL to maximum
                ui32XoscHfRow >>= 1;                          //   ROW 1 step down
                if ( ui32XoscHfRow == 0 ) {                   // if ROW below minimum
                   ui32XoscHfRow = 1;                         //   Set both ROW and COL
                   ui32XoscHfCol = 1;                         //   to minimum
                }
            }
            i32CustomerDeltaAdjust++;
        }
        while ( i32CustomerDeltaAdjust > 0 ) {
            ui32XoscHfCol = ( ui32XoscHfCol << 1 ) | 1;       // COL 1 step up
            if ( ui32XoscHfCol > 0xFFFF ) {                   // if COL above maximum
                ui32XoscHfCol = 1;                            //   Set COL to minimum
                ui32XoscHfRow = ( ui32XoscHfRow << 1 ) | 1;   //   ROW 1 step up
                if ( ui32XoscHfRow > 0xF ) {                  // if ROW above maximum
                   ui32XoscHfRow = 0xF;                       //   Set both ROW and COL
                   ui32XoscHfCol = 0xFFFF;                    //   to maximum
                }
            }
            i32CustomerDeltaAdjust--;
        }
    }

    ui32TrimValue = (( ui32XoscHfRow << DDI_0_OSC_ANABYPASSVAL1_XOSC_HF_ROW_Q12_S    ) |
                     ( ui32XoscHfCol << DDI_0_OSC_ANABYPASSVAL1_XOSC_HF_COLUMN_Q12_S )   );

    return (ui32TrimValue);
}

uint32_t SetupGetTrimForRcOscLfRtuneCtuneTrim( void ) {
    uint32_t ui32TrimValue;

    // Use device specific trim values located in factory configuration
    // area
    ui32TrimValue =
        ((HWREG(FCFG1_BASE + FCFG1_O_CONFIG_OSC_TOP) &
          FCFG1_CONFIG_OSC_TOP_RCOSCLF_CTUNE_TRIM_M)>>
          FCFG1_CONFIG_OSC_TOP_RCOSCLF_CTUNE_TRIM_S)<<
            DDI_0_OSC_LFOSCCTL_RCOSCLF_CTUNE_TRIM_S;

    ui32TrimValue |=
        ((HWREG(FCFG1_BASE + FCFG1_O_CONFIG_OSC_TOP) &
          FCFG1_CONFIG_OSC_TOP_RCOSCLF_RTUNE_TRIM_M)>>
          FCFG1_CONFIG_OSC_TOP_RCOSCLF_RTUNE_TRIM_S)<<
            DDI_0_OSC_LFOSCCTL_RCOSCLF_RTUNE_TRIM_S;

    return(ui32TrimValue);
}

uint32_t SetupGetTrimForXoscHfIbiastherm( void ) {
    uint32_t ui32TrimValue;

    // Use device specific trim value located in factory configuration
    // area
    ui32TrimValue =
        (HWREG(FCFG1_BASE + FCFG1_O_ANABYPASS_VALUE2) &
         FCFG1_ANABYPASS_VALUE2_XOSC_HF_IBIASTHERM_M)>>
         FCFG1_ANABYPASS_VALUE2_XOSC_HF_IBIASTHERM_S;

    return(ui32TrimValue);
}

uint32_t SetupGetTrimForAmpcompTh2( void ) {
    uint32_t ui32TrimValue;
    uint32_t ui32Fcfg1Value;

    // Use device specific trim value located in factory configuration
    // area. All defined register bit fields have corresponding trim
    // value in the factory configuration area
    ui32Fcfg1Value = HWREG(FCFG1_BASE + FCFG1_O_AMPCOMP_TH2);
    ui32TrimValue = ((ui32Fcfg1Value &
                      FCFG1_AMPCOMP_TH2_LPMUPDATE_LTH_M)>>
                      FCFG1_AMPCOMP_TH2_LPMUPDATE_LTH_S)<<
                   DDI_0_OSC_AMPCOMPTH2_LPMUPDATE_LTH_S;
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_TH2_LPMUPDATE_HTM_M)>>
                        FCFG1_AMPCOMP_TH2_LPMUPDATE_HTM_S)<<
                     DDI_0_OSC_AMPCOMPTH2_LPMUPDATE_HTH_S);
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_TH2_ADC_COMP_AMPTH_LPM_M)>>
                        FCFG1_AMPCOMP_TH2_ADC_COMP_AMPTH_LPM_S)<<
                     DDI_0_OSC_AMPCOMPTH2_ADC_COMP_AMPTH_LPM_S);
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_TH2_ADC_COMP_AMPTH_HPM_M)>>
                        FCFG1_AMPCOMP_TH2_ADC_COMP_AMPTH_HPM_S)<<
                     DDI_0_OSC_AMPCOMPTH2_ADC_COMP_AMPTH_HPM_S);

    return(ui32TrimValue);
}

uint32_t SetupGetTrimForAmpcompTh1( void ) {
    uint32_t ui32TrimValue;
    uint32_t ui32Fcfg1Value;

    // Use device specific trim values located in factory configuration
    // area. All defined register bit fields have a corresponding trim
    // value in the factory configuration area
    ui32Fcfg1Value = HWREG(FCFG1_BASE + FCFG1_O_AMPCOMP_TH1);
    ui32TrimValue = (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_TH1_HPMRAMP3_LTH_M)>>
                        FCFG1_AMPCOMP_TH1_HPMRAMP3_LTH_S)<<
                     DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_LTH_S);
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_TH1_HPMRAMP3_HTH_M)>>
                        FCFG1_AMPCOMP_TH1_HPMRAMP3_HTH_S)<<
                     DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_HTH_S);
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_TH1_IBIASCAP_LPTOHP_OL_CNT_M)>>
                        FCFG1_AMPCOMP_TH1_IBIASCAP_LPTOHP_OL_CNT_S)<<
                     DDI_0_OSC_AMPCOMPTH1_IBIASCAP_LPTOHP_OL_CNT_S);
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_TH1_HPMRAMP1_TH_M)>>
                        FCFG1_AMPCOMP_TH1_HPMRAMP1_TH_S)<<
                     DDI_0_OSC_AMPCOMPTH1_HPMRAMP1_TH_S);

    return(ui32TrimValue);
}

uint32_t SetupGetTrimForAmpcompCtrl( uint32_t ui32Fcfg1Revision ) {
    uint32_t ui32TrimValue    ;
    uint32_t ui32Fcfg1Value   ;
    uint32_t ibiasOffset      ;
    uint32_t ibiasInit        ;
    uint32_t modeConf1        ;
    int32_t  deltaAdjust      ;

    // Use device specific trim values located in factory configuration
    // area. Register bit fields without trim values in the factory
    // configuration area will be set to the value of 0.
    ui32Fcfg1Value = HWREG( FCFG1_BASE + FCFG1_O_AMPCOMP_CTRL1 );

    ibiasOffset    = ( ui32Fcfg1Value &
                       FCFG1_AMPCOMP_CTRL1_IBIAS_OFFSET_M ) >>
                       FCFG1_AMPCOMP_CTRL1_IBIAS_OFFSET_S ;
    ibiasInit      = ( ui32Fcfg1Value &
                       FCFG1_AMPCOMP_CTRL1_IBIAS_INIT_M ) >>
                       FCFG1_AMPCOMP_CTRL1_IBIAS_INIT_S ;

    if (( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_XOSC_OVR_M ) == 0 ) {
        // Adjust with DELTA_IBIAS_OFFSET and DELTA_IBIAS_INIT from CCFG
        modeConf1   = HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 );

        // Both fields are signed 4-bit values. This is an assumption when doing the sign extension.
        deltaAdjust =
            (((int32_t)( modeConf1 << ( 32 - CCFG_MODE_CONF_1_DELTA_IBIAS_OFFSET_W - CCFG_MODE_CONF_1_DELTA_IBIAS_OFFSET_S )))
                                   >> ( 32 - CCFG_MODE_CONF_1_DELTA_IBIAS_OFFSET_W ));
        deltaAdjust += (int32_t)ibiasOffset;
        if ( deltaAdjust < 0 ) {
            deltaAdjust  = 0;
        }
        if ( deltaAdjust > ( DDI_0_OSC_AMPCOMPCTL_IBIAS_OFFSET_M >> DDI_0_OSC_AMPCOMPCTL_IBIAS_OFFSET_S )) {
            deltaAdjust  = ( DDI_0_OSC_AMPCOMPCTL_IBIAS_OFFSET_M >> DDI_0_OSC_AMPCOMPCTL_IBIAS_OFFSET_S );
        }
        ibiasOffset = (uint32_t)deltaAdjust;

        deltaAdjust =
            (((int32_t)( modeConf1 << ( 32 - CCFG_MODE_CONF_1_DELTA_IBIAS_INIT_W - CCFG_MODE_CONF_1_DELTA_IBIAS_INIT_S )))
                                   >> ( 32 - CCFG_MODE_CONF_1_DELTA_IBIAS_INIT_W ));
        deltaAdjust += (int32_t)ibiasInit;
        if ( deltaAdjust < 0 ) {
            deltaAdjust  = 0;
        }
        if ( deltaAdjust > ( DDI_0_OSC_AMPCOMPCTL_IBIAS_INIT_M >> DDI_0_OSC_AMPCOMPCTL_IBIAS_INIT_S )) {
            deltaAdjust  = ( DDI_0_OSC_AMPCOMPCTL_IBIAS_INIT_M >> DDI_0_OSC_AMPCOMPCTL_IBIAS_INIT_S );
        }
        ibiasInit = (uint32_t)deltaAdjust;
    }
    ui32TrimValue = ( ibiasOffset << DDI_0_OSC_AMPCOMPCTL_IBIAS_OFFSET_S ) |
                    ( ibiasInit   << DDI_0_OSC_AMPCOMPCTL_IBIAS_INIT_S   ) ;

    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_CTRL1_LPM_IBIAS_WAIT_CNT_FINAL_M)>>
                        FCFG1_AMPCOMP_CTRL1_LPM_IBIAS_WAIT_CNT_FINAL_S)<<
                       DDI_0_OSC_AMPCOMPCTL_LPM_IBIAS_WAIT_CNT_FINAL_S);
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_CTRL1_CAP_STEP_M)>>
                        FCFG1_AMPCOMP_CTRL1_CAP_STEP_S)<<
                       DDI_0_OSC_AMPCOMPCTL_CAP_STEP_S);
    ui32TrimValue |= (((ui32Fcfg1Value &
                        FCFG1_AMPCOMP_CTRL1_IBIASCAP_HPTOLP_OL_CNT_M)>>
                        FCFG1_AMPCOMP_CTRL1_IBIASCAP_HPTOLP_OL_CNT_S)<<
                       DDI_0_OSC_AMPCOMPCTL_IBIASCAP_HPTOLP_OL_CNT_S);

    if ( ui32Fcfg1Revision >= 0x00000022 ) {
        ui32TrimValue |= ((( ui32Fcfg1Value &
            FCFG1_AMPCOMP_CTRL1_AMPCOMP_REQ_MODE_M ) >>
            FCFG1_AMPCOMP_CTRL1_AMPCOMP_REQ_MODE_S ) <<
           DDI_0_OSC_AMPCOMPCTL_AMPCOMP_REQ_MODE_S );
    }

    return(ui32TrimValue);
}

uint32_t SetupGetTrimForDblrLoopFilterResetVoltage( uint32_t ui32Fcfg1Revision ) {
   uint32_t dblrLoopFilterResetVoltageValue = 0; // Reset value

   if ( ui32Fcfg1Revision >= 0x00000020 ) {
      dblrLoopFilterResetVoltageValue = ( HWREG( FCFG1_BASE + FCFG1_O_MISC_OTP_DATA_1 ) &
         FCFG1_MISC_OTP_DATA_1_DBLR_LOOP_FILTER_RESET_VOLTAGE_M ) >>
         FCFG1_MISC_OTP_DATA_1_DBLR_LOOP_FILTER_RESET_VOLTAGE_S;
   }

   return ( dblrLoopFilterResetVoltageValue );
}

uint32_t SetupGetTrimForAdcShModeEn( uint32_t ui32Fcfg1Revision ) {
   uint32_t getTrimForAdcShModeEnValue = 1; // Recommended default setting

   if ( ui32Fcfg1Revision >= 0x00000022 ) {
      getTrimForAdcShModeEnValue = ( HWREG( FCFG1_BASE + FCFG1_O_OSC_CONF ) &
         FCFG1_OSC_CONF_ADC_SH_MODE_EN_M ) >>
         FCFG1_OSC_CONF_ADC_SH_MODE_EN_S;
   }

   return ( getTrimForAdcShModeEnValue );
}

uint32_t SetupGetTrimForAdcShVbufEn( uint32_t ui32Fcfg1Revision ) {
   uint32_t getTrimForAdcShVbufEnValue = 1; // Recommended default setting

   if ( ui32Fcfg1Revision >= 0x00000022 ) {
      getTrimForAdcShVbufEnValue = ( HWREG( FCFG1_BASE + FCFG1_O_OSC_CONF ) &
         FCFG1_OSC_CONF_ADC_SH_VBUF_EN_M ) >>
         FCFG1_OSC_CONF_ADC_SH_VBUF_EN_S;
   }

   return ( getTrimForAdcShVbufEnValue );
}

uint32_t SetupGetTrimForXoscHfCtl( uint32_t ui32Fcfg1Revision ) {
   uint32_t getTrimForXoschfCtlValue = 0; // Recommended default setting
   uint32_t fcfg1Data;

   if ( ui32Fcfg1Revision >= 0x00000020 ) {
      fcfg1Data = HWREG( FCFG1_BASE + FCFG1_O_MISC_OTP_DATA_1 );
      getTrimForXoschfCtlValue =
         ( ( ( fcfg1Data & FCFG1_MISC_OTP_DATA_1_PEAK_DET_ITRIM_M ) >>
             FCFG1_MISC_OTP_DATA_1_PEAK_DET_ITRIM_S ) <<
           DDI_0_OSC_XOSCHFCTL_PEAK_DET_ITRIM_S);

      getTrimForXoschfCtlValue |=
         ( ( ( fcfg1Data & FCFG1_MISC_OTP_DATA_1_HP_BUF_ITRIM_M ) >>
             FCFG1_MISC_OTP_DATA_1_HP_BUF_ITRIM_S ) <<
           DDI_0_OSC_XOSCHFCTL_HP_BUF_ITRIM_S);

      getTrimForXoschfCtlValue |=
         ( ( ( fcfg1Data & FCFG1_MISC_OTP_DATA_1_LP_BUF_ITRIM_M ) >>
             FCFG1_MISC_OTP_DATA_1_LP_BUF_ITRIM_S ) <<
           DDI_0_OSC_XOSCHFCTL_LP_BUF_ITRIM_S);
   }

   return ( getTrimForXoschfCtlValue );
}

uint32_t SetupGetTrimForXoscHfFastStart( void ) {
   uint32_t ui32XoscHfFastStartValue   ;

   // Get value from FCFG1
   ui32XoscHfFastStartValue = ( HWREG( FCFG1_BASE + FCFG1_O_OSC_CONF ) &
      FCFG1_OSC_CONF_XOSC_HF_FAST_START_M ) >>
      FCFG1_OSC_CONF_XOSC_HF_FAST_START_S;

   return ( ui32XoscHfFastStartValue );
}

uint32_t SetupGetTrimForRadcExtCfg( uint32_t ui32Fcfg1Revision ) {
   uint32_t getTrimForRadcExtCfgValue = 0x403F8000; // Recommended default setting
   uint32_t fcfg1Data;

   if ( ui32Fcfg1Revision >= 0x00000020 ) {
      fcfg1Data = HWREG( FCFG1_BASE + FCFG1_O_MISC_OTP_DATA_1 );
      getTrimForRadcExtCfgValue =
         ( ( ( fcfg1Data & FCFG1_MISC_OTP_DATA_1_HPM_IBIAS_WAIT_CNT_M ) >>
             FCFG1_MISC_OTP_DATA_1_HPM_IBIAS_WAIT_CNT_S ) <<
           DDI_0_OSC_RADCEXTCFG_HPM_IBIAS_WAIT_CNT_S);

      getTrimForRadcExtCfgValue |=
         ( ( ( fcfg1Data & FCFG1_MISC_OTP_DATA_1_LPM_IBIAS_WAIT_CNT_M ) >>
             FCFG1_MISC_OTP_DATA_1_LPM_IBIAS_WAIT_CNT_S ) <<
           DDI_0_OSC_RADCEXTCFG_LPM_IBIAS_WAIT_CNT_S);

      getTrimForRadcExtCfgValue |=
         ( ( ( fcfg1Data & FCFG1_MISC_OTP_DATA_1_IDAC_STEP_M ) >>
             FCFG1_MISC_OTP_DATA_1_IDAC_STEP_S ) <<
           DDI_0_OSC_RADCEXTCFG_IDAC_STEP_S);
   }

   return ( getTrimForRadcExtCfgValue );
}

uint32_t SetupGetTrimForRcOscLfIBiasTrim( uint32_t ui32Fcfg1Revision ) {
   uint32_t trimForRcOscLfIBiasTrimValue = 0; // Default value

   if ( ui32Fcfg1Revision >= 0x00000022 ) {
      trimForRcOscLfIBiasTrimValue = ( HWREG( FCFG1_BASE + FCFG1_O_OSC_CONF ) &
         FCFG1_OSC_CONF_ATESTLF_RCOSCLF_IBIAS_TRIM_M ) >>
         FCFG1_OSC_CONF_ATESTLF_RCOSCLF_IBIAS_TRIM_S ;
   }

   return ( trimForRcOscLfIBiasTrimValue );
}

uint32_t SetupGetTrimForXoscLfRegulatorAndCmirrwrRatio( uint32_t ui32Fcfg1Revision ) {
   uint32_t trimForXoscLfRegulatorAndCmirrwrRatioValue = 0; // Default value for both fields

   if ( ui32Fcfg1Revision >= 0x00000022 ) {
      trimForXoscLfRegulatorAndCmirrwrRatioValue = ( HWREG( FCFG1_BASE + FCFG1_O_OSC_CONF ) &
         ( FCFG1_OSC_CONF_XOSCLF_REGULATOR_TRIM_M |
           FCFG1_OSC_CONF_XOSCLF_CMIRRWR_RATIO_M  )) >>
           FCFG1_OSC_CONF_XOSCLF_CMIRRWR_RATIO_S  ;
   }

   return ( trimForXoscLfRegulatorAndCmirrwrRatioValue );
}

void SetupSetCacheModeAccordingToCcfgSetting( void ) {
    // - Make sure to enable aggressive VIMS clock gating for power optimization
    //   Only for PG2 devices.
    // - Enable cache prefetch enable as default setting
    //   (Slightly higher power consumption, but higher CPU performance)
    // - IF ( CCFG_..._DIS_GPRAM == 1 )
    //   then: Enable cache (set cache mode = 1), even if set by ROM boot code
    //         (This is done because it's not set by boot code when running inside
    //         a debugger supporting the Halt In Boot (HIB) functionality).
    //   else: Set MODE_GPRAM if not already set (see inline comments as well)
    uint32_t vimsCtlMode0 ;

    while ( HWREGBITW( VIMS_BASE + VIMS_O_STAT, VIMS_STAT_MODE_CHANGING_BITN )) {
        // Do nothing - wait for an eventual ongoing mode change to complete.
        // (There should typically be no wait time here, but need to be sure)
    }

    // Note that Mode=0 is equal to MODE_GPRAM
    vimsCtlMode0 = (( HWREG( VIMS_BASE + VIMS_O_CTL ) & ~VIMS_CTL_MODE_M ) | VIMS_CTL_DYN_CG_EN_M | VIMS_CTL_PREF_EN_M );

#if ( defined( DO_NOT_ENABLE_CACHE_IN_TRIM_DEVICE ))
 #if ( defined( CODE_IN_FLASH ))
    // Enable cache (and hence disable GPRAM)
    HWREG( VIMS_BASE + VIMS_O_CTL ) = ( vimsCtlMode0 | VIMS_CTL_MODE_CACHE );
 #else
    HWREG( VIMS_BASE + VIMS_O_CTL ) = vimsCtlMode0;
 #endif
#else

    if ( HWREG( CCFG_BASE + CCFG_O_SIZE_AND_DIS_FLAGS ) & CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM ) {
        // Enable cache (and hence disable GPRAM)
        HWREG( VIMS_BASE + VIMS_O_CTL ) = ( vimsCtlMode0 | VIMS_CTL_MODE_CACHE );
    } else if (( HWREG( VIMS_BASE + VIMS_O_STAT ) & VIMS_STAT_MODE_M ) != VIMS_STAT_MODE_GPRAM ) {
        // GPRAM is enabled in CCFG but not selected
        // Note: It is recommended to go via MODE_OFF when switching to MODE_GPRAM
        HWREG( VIMS_BASE + VIMS_O_CTL ) = ( vimsCtlMode0 | VIMS_CTL_MODE_OFF );
        while (( HWREG( VIMS_BASE + VIMS_O_STAT ) & VIMS_STAT_MODE_M ) != VIMS_STAT_MODE_OFF ) {
            // Do nothing - wait for an eventual mode change to complete (This goes fast).
        }
        HWREG( VIMS_BASE + VIMS_O_CTL ) = vimsCtlMode0;
    } else {
        // Correct mode, but make sure PREF_EN and DYN_CG_EN always are set
        HWREG( VIMS_BASE + VIMS_O_CTL ) = vimsCtlMode0;
    }
#endif
}

void SetupSetAonRtcSubSecInc( uint32_t subSecInc ) {
   // Loading a new RTCSUBSECINC value is done in 5 steps:
   // 1. Write bit[15:0] of new SUBSECINC value to AUX_SYSIF_O_RTCSUBSECINC0
   // 2. Write bit[23:16] of new SUBSECINC value to AUX_SYSIF_O_RTCSUBSECINC1
   // 3. Set AUX_SYSIF_RTCSUBSECINCCTL_UPD_REQ
   // 4. Wait for AUX_SYSIF_RTCSUBSECINCCTL_UPD_ACK
   // 5. Clear AUX_SYSIF_RTCSUBSECINCCTL_UPD_REQ
   HWREG( AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINC0 ) = (( subSecInc       ) & AUX_SYSIF_RTCSUBSECINC0_INC15_0_M  );
   HWREG( AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINC1 ) = (( subSecInc >> 16 ) & AUX_SYSIF_RTCSUBSECINC1_INC23_16_M );

   HWREG( AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINCCTL ) = AUX_SYSIF_RTCSUBSECINCCTL_UPD_REQ;
   while( ! ( HWREGBITW( AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINCCTL, AUX_SYSIF_RTCSUBSECINCCTL_UPD_ACK_BITN )));
   HWREG( AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINCCTL ) = 0;
}

void I2SPointerSet(uint32_t ui32Base, bool bInput, void * pNextPointer) {
    // Check the arguments.
    ASSERT(I2SBaseValid(ui32Base));

    // Update the next input/output pointer with the correct address.
    if(bInput == true)
    {
        HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)pNextPointer;
    }
    else
    {
        HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = (uint32_t)pNextPointer;
    }
}

uint32_t I2SSampleStampGet(uint32_t ui32Base, uint32_t ui32Channel) {
    uint32_t ui32FrameClkCnt;
    uint32_t ui32SysClkCnt;
    uint32_t ui32PeriodSysClkCnt;
    uint32_t ui32SampleStamp;

    // Get the number of Frame clock counts since last stamp.
    ui32FrameClkCnt = HWREG(I2S0_BASE + I2S_O_STMPWCNTCAPT0);

    // Get the number of system clock ticks since last frame clock edge.
    ui32SysClkCnt = HWREG(I2S0_BASE + I2S_O_STMPXCNTCAPT0);

    // Get the number system clock ticks in the last frame clock period.
    ui32PeriodSysClkCnt = HWREG(I2S0_BASE + I2S_O_STMPXPER);

    // Calculate the sample stamp.
    ui32SampleStamp = (ui32SysClkCnt << 16) / ui32PeriodSysClkCnt;
    ui32SampleStamp = (ui32SampleStamp > I2S_STMP_SATURATION) ?
                      I2S_STMP_SATURATION : ui32SampleStamp;
    ui32SampleStamp |= (ui32FrameClkCnt << 16);

    return (ui32SampleStamp);
}

void PowerCtrlSourceSet(uint32_t ui32PowerConfig) {
    // Check the arguments.
    ASSERT((ui32PowerConfig == PWRCTRL_PWRSRC_DCDC) ||
           (ui32PowerConfig == PWRCTRL_PWRSRC_GLDO) ||
           (ui32PowerConfig == PWRCTRL_PWRSRC_ULDO));

    // Configure the power.
    if(ui32PowerConfig == PWRCTRL_PWRSRC_DCDC) {
        HWREG(AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL) |=
            (AON_PMCTL_PWRCTL_DCDC_EN | AON_PMCTL_PWRCTL_DCDC_ACTIVE);
    }
    else if (ui32PowerConfig == PWRCTRL_PWRSRC_GLDO)
    {
        HWREG(AON_PMCTL_BASE + AON_PMCTL_O_PWRCTL) &=
            ~(AON_PMCTL_PWRCTL_DCDC_EN | AON_PMCTL_PWRCTL_DCDC_ACTIVE);
    }
    else
    {
        PRCMMcuUldoConfigure(true);
    }
}

void AESSetInitializationVector(const uint32_t *initializationVector) {
    // Write initialization vector to the aes registers
    HWREG(CRYPTO_BASE + CRYPTO_O_AESIV0) = initializationVector[0];
    HWREG(CRYPTO_BASE + CRYPTO_O_AESIV1) = initializationVector[1];
    HWREG(CRYPTO_BASE + CRYPTO_O_AESIV2) = initializationVector[2];
    HWREG(CRYPTO_BASE + CRYPTO_O_AESIV3) = initializationVector[3];
}

void AESStartDMAOperation(const uint8_t *channel0Addr, uint32_t channel0Length,  uint8_t *channel1Addr, uint32_t channel1Length) {
    if (channel0Length && channel0Addr) {
        // We actually want to perform an operation. Clear any outstanding events.
        HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = CRYPTO_IRQCLR_RESULT_AVAIL_M | CRYPTO_IRQEN_DMA_IN_DONE_M; // This might need AES_IRQEN_DMA_IN_DONE as well

        while(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & (CRYPTO_IRQSTAT_DMA_IN_DONE_M | CRYPTO_IRQSTAT_RESULT_AVAIL_M));

        // Configure the DMA controller - enable both DMA channels.
        HWREGBITW(CRYPTO_BASE + CRYPTO_O_DMACH0CTL, CRYPTO_DMACH0CTL_EN_BITN) = 1;

        // Base address of the payload data in ext. memory.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0EXTADDR) = (uint32_t)channel0Addr;

        // Payload data length in bytes, equal to the cipher text length.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0LEN) = channel0Length;
    }

    if (channel1Length && channel1Addr) {
        // Enable DMA channel 1.
        HWREGBITW(CRYPTO_BASE + CRYPTO_O_DMACH1CTL, CRYPTO_DMACH1CTL_EN_BITN) = 1;

        // Base address of the output data buffer.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH1EXTADDR) = (uint32_t)channel1Addr;

        // Output data length in bytes, equal to the cipher text length.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH1LEN) = channel1Length;
    }
}

uint32_t AESWaitForIRQFlags(uint32_t irqFlags) {
    uint32_t irqTrigger = 0;
    // Wait for the DMA operation to complete. Add a delay to make sure we are
    // not flooding the bus with requests too much.
    do {
        CPUdelay(1);
    }
    while(!(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & irqFlags & (CRYPTO_IRQSTAT_DMA_IN_DONE_M |
                                                                CRYPTO_IRQSTAT_RESULT_AVAIL_M |
                                                                CRYPTO_IRQSTAT_DMA_BUS_ERR_M |
                                                                CRYPTO_IRQSTAT_KEY_ST_WR_ERR_M)));

    // Save the IRQ trigger source
    irqTrigger = HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & irqFlags;

    // Clear IRQ flags
    HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = irqTrigger;

    return irqTrigger;
}

uint32_t AESWriteToKeyStore(const uint8_t *aesKey, uint32_t aesKeyLength, uint32_t keyStoreArea) {
    // Check the arguments.
    ASSERT((keyStoreArea == AES_KEY_AREA_0) ||
           (keyStoreArea == AES_KEY_AREA_1) ||
           (keyStoreArea == AES_KEY_AREA_2) ||
           (keyStoreArea == AES_KEY_AREA_3) ||
           (keyStoreArea == AES_KEY_AREA_4) ||
           (keyStoreArea == AES_KEY_AREA_5) ||
           (keyStoreArea == AES_KEY_AREA_6) ||
           (keyStoreArea == AES_KEY_AREA_7));

    ASSERT((aesKeyLength == AES_128_KEY_LENGTH_BYTES) ||
           (aesKeyLength == AES_192_KEY_LENGTH_BYTES) ||
           (aesKeyLength == AES_256_KEY_LENGTH_BYTES));

    uint32_t keySize = 0;

    switch (aesKeyLength) {
        case AES_128_KEY_LENGTH_BYTES:
            keySize = CRYPTO_KEYSIZE_SIZE_128_BIT;
            break;
        case AES_192_KEY_LENGTH_BYTES:
            keySize = CRYPTO_KEYSIZE_SIZE_192_BIT;
            break;
        case AES_256_KEY_LENGTH_BYTES:
            keySize = CRYPTO_KEYSIZE_SIZE_256_BIT;
            break;
    }

    // Clear any previously written key at the keyLocation
    AESInvalidateKey(keyStoreArea);

    // Disable the external interrupt to stop the interrupt form propagating
    // from the module to the System CPU.
    IntDisable(INT_CRYPTO_RESULT_AVAIL_IRQ);

    // Enable internal interrupts.
    HWREG(CRYPTO_BASE + CRYPTO_O_IRQTYPE) = CRYPTO_IRQTYPE_LEVEL_M;
    HWREG(CRYPTO_BASE + CRYPTO_O_IRQEN) = CRYPTO_IRQEN_DMA_IN_DONE_M | CRYPTO_IRQEN_RESULT_AVAIL_M;

    // Configure master control module.
    HWREG(CRYPTO_BASE + CRYPTO_O_ALGSEL) = CRYPTO_ALGSEL_KEY_STORE;

    // Clear any outstanding events.
    HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = (CRYPTO_IRQCLR_DMA_IN_DONE | CRYPTO_IRQCLR_RESULT_AVAIL);

    // Configure the size of keys contained within the key store
    // Do not write to the register if the correct key size is already set.
    // Writing to this register causes all current keys to be invalidated.
    uint32_t keyStoreKeySize = HWREG(CRYPTO_BASE + CRYPTO_O_KEYSIZE);
    if (keySize != keyStoreKeySize) {
        HWREG(CRYPTO_BASE + CRYPTO_O_KEYSIZE) = keySize;
    }

    // Enable key to write (e.g. Key 0).
    HWREG(CRYPTO_BASE + CRYPTO_O_KEYWRITEAREA) = 1 << keyStoreArea;

    // Total key length in bytes (16 for 1 x 128-bit key and 32 for 1 x 256-bit key).
    AESStartDMAOperation(aesKey, aesKeyLength, 0, 0);

    // Wait for the DMA operation to complete.
    uint32_t irqTrigger = AESWaitForIRQFlags(CRYPTO_IRQCLR_RESULT_AVAIL | CRYPTO_IRQCLR_DMA_IN_DONE | CRYPTO_IRQSTAT_DMA_BUS_ERR | CRYPTO_IRQSTAT_KEY_ST_WR_ERR);

    // Re-enable interrupts globally.
    IntPendClear(INT_CRYPTO_RESULT_AVAIL_IRQ);
    IntEnable(INT_CRYPTO_RESULT_AVAIL_IRQ);

    // If we had a bus error or the key is not in the CRYPTO_O_KEYWRITTENAREA, return an error.
    if ((irqTrigger & (CRYPTO_IRQSTAT_DMA_BUS_ERR_M | CRYPTO_IRQSTAT_KEY_ST_WR_ERR_M)) || !(HWREG(CRYPTO_BASE + CRYPTO_O_KEYWRITTENAREA) & (1 << keyStoreArea))) {
        // There was an error in writing to the keyStore.
        return AES_KEYSTORE_ERROR;
    }
    else {
        return AES_SUCCESS;
    }
}

uint32_t AESReadFromKeyStore(uint32_t keyStoreArea) {
    // Check the arguments.
    ASSERT((keyStoreArea == AES_KEY_AREA_0) ||
           (keyStoreArea == AES_KEY_AREA_1) ||
           (keyStoreArea == AES_KEY_AREA_2) ||
           (keyStoreArea == AES_KEY_AREA_3) ||
           (keyStoreArea == AES_KEY_AREA_4) ||
           (keyStoreArea == AES_KEY_AREA_5) ||
           (keyStoreArea == AES_KEY_AREA_6) ||
           (keyStoreArea == AES_KEY_AREA_7));

    // Check if there is a valid key in the specified keyStoreArea
    if (!(HWREG(CRYPTO_BASE + CRYPTO_O_KEYWRITTENAREA) & (1 << keyStoreArea))) {
        return AES_KEYSTORE_AREA_INVALID;
    }

    // Enable keys to read (e.g. Key 0).
    HWREG(CRYPTO_BASE + CRYPTO_O_KEYREADAREA) = keyStoreArea;

    // Wait until key is loaded to the AES module.
    // We cannot simply poll the IRQ status as only an error is communicated through
    // the IRQ status and not the completion of the transfer.
    do {
        CPUdelay(1);
    }
    while((HWREG(CRYPTO_BASE + CRYPTO_O_KEYREADAREA) & CRYPTO_KEYREADAREA_BUSY_M));

    // Check for keyStore read error.
    if((HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & CRYPTO_IRQSTAT_KEY_ST_RD_ERR_M)) {
        return AES_KEYSTORE_ERROR;
    }
    else {
        return AES_SUCCESS;
    }
}

uint32_t AESReadTag(uint8_t *tag, uint32_t tagLength) {
    // The intermediate array is used instead of a caller-provided one
    // to enable a simple API with no unintuitive alignment or size requirements.
    // This is a trade-off of stack-depth vs ease-of-use that came out on the
    // ease-of-use side.
    uint32_t computedTag[AES_BLOCK_SIZE / sizeof(uint32_t)];

    // Wait until the computed tag is ready.
    while (!(HWREG(CRYPTO_BASE + CRYPTO_O_AESCTL) & CRYPTO_AESCTL_SAVED_CONTEXT_RDY_M));

    // Read computed tag out from the HW registers
    // Need to read out all 128 bits in four reads to correctly clear CRYPTO_AESCTL_SAVED_CONTEXT_RDY flag
    computedTag[0] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT0);
    computedTag[1] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT1);
    computedTag[2] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT2);
    computedTag[3] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT3);

    memcpy(tag, computedTag, tagLength);

    return AES_SUCCESS;
}

uint32_t AESVerifyTag(const uint8_t *tag, uint32_t tagLength) {
    uint32_t resultStatus;
    // The intermediate array is allocated on the stack to ensure users do not
    // point the tag they provide and the one computed at the same location.
    // That would cause memcmp to compare an array against itself. We could add
    // a check that verifies that the arrays are not the same. If we did that and
    // modified AESReadTag to just copy all 128 bits into a provided array,
    // we could save 16 bytes of stack space while making the API much more
    // complicated.
    uint8_t computedTag[AES_BLOCK_SIZE];

    resultStatus = AESReadTag(computedTag, tagLength);

    if (resultStatus != AES_SUCCESS) {
        return resultStatus;
    }

    resultStatus = memcmp(computedTag, tag, tagLength);

    if (resultStatus != 0) {
        return AES_TAG_VERIFICATION_FAILED;
    }

    return AES_SUCCESS;
}

void AESConfigureCCMCtrl(uint32_t nonceLength, uint32_t macLength, bool encrypt) {
    uint32_t ctrlVal = 0;

    ctrlVal = ((15 - nonceLength - 1) << CRYPTO_AESCTL_CCM_L_S);
    if ( macLength >= 2 ) {
        ctrlVal |= ((( macLength - 2 ) >> 1 ) << CRYPTO_AESCTL_CCM_M_S );
    }
    ctrlVal |= CRYPTO_AESCTL_CCM |
               CRYPTO_AESCTL_CTR |
               CRYPTO_AESCTL_SAVE_CONTEXT |
               CRYPTO_AESCTL_CTR_WIDTH_128_BIT;
    ctrlVal |= encrypt ? CRYPTO_AESCTL_DIR : 0;

    AESSetCtrl(ctrlVal);
}

void AESWriteCCMInitializationVector(const uint8_t *nonce, uint32_t nonceLength) {
    union {
        uint32_t word[4];
        uint8_t  byte[16];
    } initializationVector = {{0}};

    initializationVector.byte[0] = 15 - nonceLength - 1;

    memcpy(&(initializationVector.byte[1]), nonce, nonceLength);

    AESSetInitializationVector(initializationVector.word);
}


#define MAX(x,y)            (((x) > (y)) ?  (x) : (y))
#define MIN(x,y)            (((x) < (y)) ?  (x) : (y))
#define INRANGE(x,y,z)      ((x) > (y) && (x) < (z))


//*****************************************************************************
//
// Define for the maximum curve size supported by the PKA module in 32 bit
// word.
// \note PKA hardware module can support up to 384 bit curve size due to the
//       2K of PKA RAM.
//
//*****************************************************************************
#define PKA_MAX_CURVE_SIZE_32_BIT_WORD  12

//*****************************************************************************
//
// Define for the maximum length of the big number supported by the PKA module
// in 32 bit word.
//
//*****************************************************************************
#define PKA_MAX_LEN_IN_32_BIT_WORD  PKA_MAX_CURVE_SIZE_32_BIT_WORD

//*****************************************************************************
//
// Used in PKAWritePkaParam() and PKAWritePkaParamExtraOffset() to specify that
// the base address of the parameter should not be written to a NPTR register.
//
//*****************************************************************************
#define PKA_NO_POINTER_REG 0xFF

//*****************************************************************************
//
// NIST P224 constants in little endian format. byte[0] is the least
// significant byte and byte[NISTP224_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint224 NISTP224_generator = {
    .x = {.byte = {0x21, 0x1D, 0x5C, 0x11, 0xD6, 0x80, 0x32, 0x34,
                   0x22, 0x11, 0xC2, 0x56, 0xD3, 0xC1, 0x03, 0x4A,
                   0xB9, 0x90, 0x13, 0x32, 0x7F, 0xBF, 0xB4, 0x6B,
                   0xBD, 0x0C, 0x0E, 0xB7, }},
    .y = {.byte = {0x34, 0x7E, 0x00, 0x85, 0x99, 0x81, 0xD5, 0x44,
                   0x64, 0x47, 0x07, 0x5A, 0xA0, 0x75, 0x43, 0xCD,
                   0xE6, 0xDF, 0x22, 0x4C, 0xFB, 0x23, 0xF7, 0xB5,
                   0x88, 0x63, 0x37, 0xBD, }},
};

const PKA_EccParam224 NISTP224_prime       = {.byte = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                       0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
                                                       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                                                       0xFF, 0xFF, 0xFF, 0xFF}};

const PKA_EccParam224 NISTP224_a           = {.byte = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                                                       0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF,
                                                       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                                                       0xFF, 0xFF, 0xFF, 0xFF}};

const PKA_EccParam224 NISTP224_b           = {.byte = {0xB4, 0xFF, 0x55, 0x23, 0x43, 0x39, 0x0B, 0x27,
                                                       0xBA, 0xD8, 0xBF, 0xD7, 0xB7, 0xB0, 0x44, 0x50,
                                                       0x56, 0x32, 0x41, 0xF5, 0xAB, 0xB3, 0x04, 0x0C,
                                                       0x85, 0x0A, 0x05, 0xB4}};

const PKA_EccParam224 NISTP224_order       = {.byte = {0x3D, 0x2A, 0x5C, 0x5C, 0x45, 0x29, 0xDD, 0x13,
                                                       0x3E, 0xF0, 0xB8, 0xE0, 0xA2, 0x16, 0xFF, 0xFF,
                                                       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                                                       0xFF, 0xFF, 0xFF, 0xFF}};

//*****************************************************************************
//
// NIST P256 constants in little endian format. byte[0] is the least
// significant byte and byte[NISTP256_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint256 NISTP256_generator = {
    .x = {.byte = {0x96, 0xc2, 0x98, 0xd8, 0x45, 0x39, 0xa1, 0xf4,
                   0xa0, 0x33, 0xeb, 0x2d, 0x81, 0x7d, 0x03, 0x77,
                   0xf2, 0x40, 0xa4, 0x63, 0xe5, 0xe6, 0xbc, 0xf8,
                   0x47, 0x42, 0x2c, 0xe1, 0xf2, 0xd1, 0x17, 0x6b}},
    .y = {.byte = {0xf5, 0x51, 0xbf, 0x37, 0x68, 0x40, 0xb6, 0xcb,
                   0xce, 0x5e, 0x31, 0x6b, 0x57, 0x33, 0xce, 0x2b,
                   0x16, 0x9e, 0x0f, 0x7c, 0x4a, 0xeb, 0xe7, 0x8e,
                   0x9b, 0x7f, 0x1a, 0xfe, 0xe2, 0x42, 0xe3, 0x4f}},
};

const PKA_EccParam256 NISTP256_prime       = {.byte = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                    0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
                                                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                    0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}};

const PKA_EccParam256 NISTP256_a           = {.byte = {0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                    0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
                                                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                    0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}};

const PKA_EccParam256 NISTP256_b           = {.byte = {0x4b, 0x60, 0xd2, 0x27, 0x3e, 0x3c, 0xce, 0x3b,
                                                    0xf6, 0xb0, 0x53, 0xcc, 0xb0, 0x06, 0x1d, 0x65,
                                                    0xbc, 0x86, 0x98, 0x76, 0x55, 0xbd, 0xeb, 0xb3,
                                                    0xe7, 0x93, 0x3a, 0xaa, 0xd8, 0x35, 0xc6, 0x5a}};

const PKA_EccParam256 NISTP256_order       = {.byte = {0x51, 0x25, 0x63, 0xfc, 0xc2, 0xca, 0xb9, 0xf3,
                                                    0x84, 0x9e, 0x17, 0xa7, 0xad, 0xfa, 0xe6, 0xbc,
                                                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}};

//*****************************************************************************
//
// NIST P384 constants in little endian format. byte[0] is the least
// significant byte and byte[NISTP384_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint384 NISTP384_generator = {
    .x = {.byte = {0xb7, 0x0a, 0x76, 0x72, 0x38, 0x5e, 0x54, 0x3a,
                   0x6c, 0x29, 0x55, 0xbf, 0x5d, 0xf2, 0x02, 0x55,
                   0x38, 0x2a, 0x54, 0x82, 0xe0, 0x41, 0xf7, 0x59,
                   0x98, 0x9b, 0xa7, 0x8b, 0x62, 0x3b, 0x1d, 0x6e,
                   0x74, 0xad, 0x20, 0xf3, 0x1e, 0xc7, 0xb1, 0x8e,
                   0x37, 0x05, 0x8b, 0xbe, 0x22, 0xca, 0x87, 0xaa}},
    .y = {.byte = {0x5f, 0x0e, 0xea, 0x90, 0x7c, 0x1d, 0x43, 0x7a,
                   0x9d, 0x81, 0x7e, 0x1d, 0xce, 0xb1, 0x60, 0x0a,
                   0xc0, 0xb8, 0xf0, 0xb5, 0x13, 0x31, 0xda, 0xe9,
                   0x7c, 0x14, 0x9a, 0x28, 0xbd, 0x1d, 0xf4, 0xf8,
                   0x29, 0xdc, 0x92, 0x92, 0xbf, 0x98, 0x9e, 0x5d,
                   0x6f, 0x2c, 0x26, 0x96, 0x4a, 0xde, 0x17, 0x36,}},
};

const PKA_EccParam384 NISTP384_prime       = {.byte = {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
                                                       0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
                                                       0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};

const PKA_EccParam384 NISTP384_a           = {.byte = {0xfc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
                                                       0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
                                                       0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};

const PKA_EccParam384 NISTP384_b           = {.byte = {0xef, 0x2a, 0xec, 0xd3, 0xed, 0xc8, 0x85, 0x2a,
                                                       0x9d, 0xd1, 0x2e, 0x8a, 0x8d, 0x39, 0x56, 0xc6,
                                                       0x5a, 0x87, 0x13, 0x50, 0x8f, 0x08, 0x14, 0x03,
                                                       0x12, 0x41, 0x81, 0xfe, 0x6e, 0x9c, 0x1d, 0x18,
                                                       0x19, 0x2d, 0xf8, 0xe3, 0x6b, 0x05, 0x8e, 0x98,
                                                       0xe4, 0xe7, 0x3e, 0xe2, 0xa7, 0x2f, 0x31, 0xb3}};

const PKA_EccParam384 NISTP384_order       = {.byte = {0x73, 0x29, 0xc5, 0xcc, 0x6a, 0x19, 0xec, 0xec,
                                                       0x7a, 0xa7, 0xb0, 0x48, 0xb2, 0x0d, 0x1a, 0x58,
                                                       0xdf, 0x2d, 0x37, 0xf4, 0x81, 0x4d, 0x63, 0xc7,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};


//*****************************************************************************
//
// NIST P521 constants in little endian format. byte[0] is the least
// significant byte and byte[NISTP521_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint521 NISTP521_generator = {
    .x = {.byte = {0x66, 0xbd, 0xe5, 0xc2, 0x31, 0x7e, 0x7e, 0xf9,
                   0x9b, 0x42, 0x6a, 0x85, 0xc1, 0xb3, 0x48, 0x33,
                   0xde, 0xa8, 0xff, 0xa2, 0x27, 0xc1, 0x1d, 0xfe,
                   0x28, 0x59, 0xe7, 0xef, 0x77, 0x5e, 0x4b, 0xa1,
                   0xba, 0x3d, 0x4d, 0x6b, 0x60, 0xaf, 0x28, 0xf8,
                   0x21, 0xb5, 0x3f, 0x05, 0x39, 0x81, 0x64, 0x9c,
                   0x42, 0xb4, 0x95, 0x23, 0x66, 0xcb, 0x3e, 0x9e,
                   0xcd, 0xe9, 0x04, 0x04, 0xb7, 0x06, 0x8e, 0x85,
                   0xc6, 0x00}},
    .y = {.byte = {0x50, 0x66, 0xd1, 0x9f, 0x76, 0x94, 0xbe, 0x88,
                   0x40, 0xc2, 0x72, 0xa2, 0x86, 0x70, 0x3c, 0x35,
                   0x61, 0x07, 0xad, 0x3f, 0x01, 0xb9, 0x50, 0xc5,
                   0x40, 0x26, 0xf4, 0x5e, 0x99, 0x72, 0xee, 0x97,
                   0x2c, 0x66, 0x3e, 0x27, 0x17, 0xbd, 0xaf, 0x17,
                   0x68, 0x44, 0x9b, 0x57, 0x49, 0x44, 0xf5, 0x98,
                   0xd9, 0x1b, 0x7d, 0x2c, 0xb4, 0x5f, 0x8a, 0x5c,
                   0x04, 0xc0, 0x3b, 0x9a, 0x78, 0x6a, 0x29, 0x39,
                   0x18, 0x01}},
};

const PKA_EccParam521 NISTP521_prime       = {.byte = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0x01}};

const PKA_EccParam521 NISTP521_a           = {.byte = {0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0x01}};

const PKA_EccParam521 NISTP521_b           = {.byte = {0x00, 0x3f, 0x50, 0x6b, 0xd4, 0x1f, 0x45, 0xef,
                                                       0xf1, 0x34, 0x2c, 0x3d, 0x88, 0xdf, 0x73, 0x35,
                                                       0x07, 0xbf, 0xb1, 0x3b, 0xbd, 0xc0, 0x52, 0x16,
                                                       0x7b, 0x93, 0x7e, 0xec, 0x51, 0x39, 0x19, 0x56,
                                                       0xe1, 0x09, 0xf1, 0x8e, 0x91, 0x89, 0xb4, 0xb8,
                                                       0xf3, 0x15, 0xb3, 0x99, 0x5b, 0x72, 0xda, 0xa2,
                                                       0xee, 0x40, 0x85, 0xb6, 0xa0, 0x21, 0x9a, 0x92,
                                                       0x1f, 0x9a, 0x1c, 0x8e, 0x61, 0xb9, 0x3e, 0x95,
                                                       0x51, 0x00}};

const PKA_EccParam521 NISTP521_order       = {.byte = {0x09, 0x64, 0x38, 0x91, 0x1e, 0xb7, 0x6f, 0xbb,
                                                       0xae, 0x47, 0x9c, 0x89, 0xb8, 0xc9, 0xb5, 0x3b,
                                                       0xd0, 0xa5, 0x09, 0xf7, 0x48, 0x01, 0xcc, 0x7f,
                                                       0x6b, 0x96, 0x2f, 0xbf, 0x83, 0x87, 0x86, 0x51,
                                                       0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                       0xff, 0x01}};


//*****************************************************************************
//
// Brainpool P256r1 constants in little endian format. byte[0] is the least
// significant byte and byte[BrainpoolP256R1_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint256 BrainpoolP256R1_generator = {
    .x = {.byte = {0x62, 0x32, 0xCE, 0x9A, 0xBD, 0x53, 0x44, 0x3A,
                   0xC2, 0x23, 0xBD, 0xE3, 0xE1, 0x27, 0xDE, 0xB9,
                   0xAF, 0xB7, 0x81, 0xFC, 0x2F, 0x48, 0x4B, 0x2C,
                   0xCB, 0x57, 0x7E, 0xCB, 0xB9, 0xAE, 0xD2, 0x8B}},
    .y = {.byte = {0x97, 0x69, 0x04, 0x2F, 0xC7, 0x54, 0x1D, 0x5C,
                   0x54, 0x8E, 0xED, 0x2D, 0x13, 0x45, 0x77, 0xC2,
                   0xC9, 0x1D, 0x61, 0x14, 0x1A, 0x46, 0xF8, 0x97,
                   0xFD, 0xC4, 0xDA, 0xC3, 0x35, 0xF8, 0x7E, 0x54}},
};

const PKA_EccParam256 BrainpoolP256R1_prime       = {.byte = {0x77, 0x53, 0x6E, 0x1F, 0x1D, 0x48, 0x13, 0x20,
                                                              0x28, 0x20, 0x26, 0xD5, 0x23, 0xF6, 0x3B, 0x6E,
                                                              0x72, 0x8D, 0x83, 0x9D, 0x90, 0x0A, 0x66, 0x3E,
                                                              0xBC, 0xA9, 0xEE, 0xA1, 0xDB, 0x57, 0xFB, 0xA9}};

const PKA_EccParam256 BrainpoolP256R1_a           = {.byte = {0xD9, 0xB5, 0x30, 0xF3, 0x44, 0x4B, 0x4A, 0xE9,
                                                              0x6C, 0x5C, 0xDC, 0x26, 0xC1, 0x55, 0x80, 0xFB,
                                                              0xE7, 0xFF, 0x7A, 0x41, 0x30, 0x75, 0xF6, 0xEE,
                                                              0x57, 0x30, 0x2C, 0xFC, 0x75, 0x09, 0x5A, 0x7D}};

const PKA_EccParam256 BrainpoolP256R1_b           = {.byte = {0xB6, 0x07, 0x8C, 0xFF, 0x18, 0xDC, 0xCC, 0x6B,
                                                              0xCE, 0xE1, 0xF7, 0x5C, 0x29, 0x16, 0x84, 0x95,
                                                              0xBF, 0x7C, 0xD7, 0xBB, 0xD9, 0xB5, 0x30, 0xF3,
                                                              0x44, 0x4B, 0x4A, 0xE9, 0x6C, 0x5C, 0xDC, 0x26,}};

const PKA_EccParam256 BrainpoolP256R1_order       = {.byte = {0xA7, 0x56, 0x48, 0x97, 0x82, 0x0E, 0x1E, 0x90,
                                                              0xF7, 0xA6, 0x61, 0xB5, 0xA3, 0x7A, 0x39, 0x8C,
                                                              0x71, 0x8D, 0x83, 0x9D, 0x90, 0x0A, 0x66, 0x3E,
                                                              0xBC, 0xA9, 0xEE, 0xA1, 0xDB, 0x57, 0xFB, 0xA9}};

//*****************************************************************************
//
// Brainpool P384r1 constants in little endian format. byte[0] is the least
// significant byte and byte[BrainpoolP384R1_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint384 BrainpoolP384R1_generator = {
    .x = {.byte = {0x1E, 0xAF, 0xD4, 0x47, 0xE2, 0xB2, 0x87, 0xEF,
                   0xAA, 0x46, 0xD6, 0x36, 0x34, 0xE0, 0x26, 0xE8,
                   0xE8, 0x10, 0xBD, 0x0C, 0xFE, 0xCA, 0x7F, 0xDB,
                   0xE3, 0x4F, 0xF1, 0x7E, 0xE7, 0xA3, 0x47, 0x88,
                   0x6B, 0x3F, 0xC1, 0xB7, 0x81, 0x3A, 0xA6, 0xA2,
                   0xFF, 0x45, 0xCF, 0x68, 0xF0, 0x64, 0x1C, 0x1D}},
    .y = {.byte = {0x15, 0x53, 0x3C, 0x26, 0x41, 0x03, 0x82, 0x42,
                   0x11, 0x81, 0x91, 0x77, 0x21, 0x46, 0x46, 0x0E,
                   0x28, 0x29, 0x91, 0xF9, 0x4F, 0x05, 0x9C, 0xE1,
                   0x64, 0x58, 0xEC, 0xFE, 0x29, 0x0B, 0xB7, 0x62,
                   0x52, 0xD5, 0xCF, 0x95, 0x8E, 0xEB, 0xB1, 0x5C,
                   0xA4, 0xC2, 0xF9, 0x20, 0x75, 0x1D, 0xBE, 0x8A}},
};

const PKA_EccParam384 BrainpoolP384R1_prime       = {.byte = {0x53, 0xEC, 0x07, 0x31, 0x13, 0x00, 0x47, 0x87,
                                                              0x71, 0x1A, 0x1D, 0x90, 0x29, 0xA7, 0xD3, 0xAC,
                                                              0x23, 0x11, 0xB7, 0x7F, 0x19, 0xDA, 0xB1, 0x12,
                                                              0xB4, 0x56, 0x54, 0xED, 0x09, 0x71, 0x2F, 0x15,
                                                              0xDF, 0x41, 0xE6, 0x50, 0x7E, 0x6F, 0x5D, 0x0F,
                                                              0x28, 0x6D, 0x38, 0xA3, 0x82, 0x1E, 0xB9, 0x8C}};

const PKA_EccParam384 BrainpoolP384R1_a           = {.byte = {0x26, 0x28, 0xCE, 0x22, 0xDD, 0xC7, 0xA8, 0x04,
                                                              0xEB, 0xD4, 0x3A, 0x50, 0x4A, 0x81, 0xA5, 0x8A,
                                                              0x0F, 0xF9, 0x91, 0xBA, 0xEF, 0x65, 0x91, 0x13,
                                                              0x87, 0x27, 0xB2, 0x4F, 0x8E, 0xA2, 0xBE, 0xC2,
                                                              0xA0, 0xAF, 0x05, 0xCE, 0x0A, 0x08, 0x72, 0x3C,
                                                              0x0C, 0x15, 0x8C, 0x3D, 0xC6, 0x82, 0xC3, 0x7B}};

const PKA_EccParam384 BrainpoolP384R1_b           = {.byte = {0x11, 0x4C, 0x50, 0xFA, 0x96, 0x86, 0xB7, 0x3A,
                                                              0x94, 0xC9, 0xDB, 0x95, 0x02, 0x39, 0xB4, 0x7C,
                                                              0xD5, 0x62, 0xEB, 0x3E, 0xA5, 0x0E, 0x88, 0x2E,
                                                              0xA6, 0xD2, 0xDC, 0x07, 0xE1, 0x7D, 0xB7, 0x2F,
                                                              0x7C, 0x44, 0xF0, 0x16, 0x54, 0xB5, 0x39, 0x8B,
                                                              0x26, 0x28, 0xCE, 0x22, 0xDD, 0xC7, 0xA8, 0x04}};

const PKA_EccParam384 BrainpoolP384R1_order       = {.byte = {0x65, 0x65, 0x04, 0xE9, 0x02, 0x32, 0x88, 0x3B,
                                                              0x10, 0xC3, 0x7F, 0x6B, 0xAF, 0xB6, 0x3A, 0xCF,
                                                              0xA7, 0x25, 0x04, 0xAC, 0x6C, 0x6E, 0x16, 0x1F,
                                                              0xB3, 0x56, 0x54, 0xED, 0x09, 0x71, 0x2F, 0x15,
                                                              0xDF, 0x41, 0xE6, 0x50, 0x7E, 0x6F, 0x5D, 0x0F,
                                                              0x28, 0x6D, 0x38, 0xA3, 0x82, 0x1E, 0xB9, 0x8C}};

//*****************************************************************************
//
// Brainpool P512r1 constants in little endian format. byte[0] is the least
// significant byte and byte[BrainpoolP512R1_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint512 BrainpoolP512R1_generator = {
    .x = {.byte = {0x22, 0xF8, 0xB9, 0xBC, 0x09, 0x22, 0x35, 0x8B,
                   0x68, 0x5E, 0x6A, 0x40, 0x47, 0x50, 0x6D, 0x7C,
                   0x5F, 0x7D, 0xB9, 0x93, 0x7B, 0x68, 0xD1, 0x50,
                   0x8D, 0xD4, 0xD0, 0xE2, 0x78, 0x1F, 0x3B, 0xFF,
                   0x8E, 0x09, 0xD0, 0xF4, 0xEE, 0x62, 0x3B, 0xB4,
                   0xC1, 0x16, 0xD9, 0xB5, 0x70, 0x9F, 0xED, 0x85,
                   0x93, 0x6A, 0x4C, 0x9C, 0x2E, 0x32, 0x21, 0x5A,
                   0x64, 0xD9, 0x2E, 0xD8, 0xBD, 0xE4, 0xAE, 0x81}},
    .y = {.byte = {0x92, 0x08, 0xD8, 0x3A, 0x0F, 0x1E, 0xCD, 0x78,
                   0x06, 0x54, 0xF0, 0xA8, 0x2F, 0x2B, 0xCA, 0xD1,
                   0xAE, 0x63, 0x27, 0x8A, 0xD8, 0x4B, 0xCA, 0x5B,
                   0x5E, 0x48, 0x5F, 0x4A, 0x49, 0xDE, 0xDC, 0xB2,
                   0x11, 0x81, 0x1F, 0x88, 0x5B, 0xC5, 0x00, 0xA0,
                   0x1A, 0x7B, 0xA5, 0x24, 0x00, 0xF7, 0x09, 0xF2,
                   0xFD, 0x22, 0x78, 0xCF, 0xA9, 0xBF, 0xEA, 0xC0,
                   0xEC, 0x32, 0x63, 0x56, 0x5D, 0x38, 0xDE, 0x7D}},
};

const PKA_EccParam512 BrainpoolP512R1_prime       = {.byte = {0xF3, 0x48, 0x3A, 0x58, 0x56, 0x60, 0xAA, 0x28,
                                                              0x85, 0xC6, 0x82, 0x2D, 0x2F, 0xFF, 0x81, 0x28,
                                                              0xE6, 0x80, 0xA3, 0xE6, 0x2A, 0xA1, 0xCD, 0xAE,
                                                              0x42, 0x68, 0xC6, 0x9B, 0x00, 0x9B, 0x4D, 0x7D,
                                                              0x71, 0x08, 0x33, 0x70, 0xCA, 0x9C, 0x63, 0xD6,
                                                              0x0E, 0xD2, 0xC9, 0xB3, 0xB3, 0x8D, 0x30, 0xCB,
                                                              0x07, 0xFC, 0xC9, 0x33, 0xAE, 0xE6, 0xD4, 0x3F,
                                                              0x8B, 0xC4, 0xE9, 0xDB, 0xB8, 0x9D, 0xDD, 0xAA}};

const PKA_EccParam512 BrainpoolP512R1_a           = {.byte = {0xCA, 0x94, 0xFC, 0x77, 0x4D, 0xAC, 0xC1, 0xE7,
                                                              0xB9, 0xC7, 0xF2, 0x2B, 0xA7, 0x17, 0x11, 0x7F,
                                                              0xB5, 0xC8, 0x9A, 0x8B, 0xC9, 0xF1, 0x2E, 0x0A,
                                                              0xA1, 0x3A, 0x25, 0xA8, 0x5A, 0x5D, 0xED, 0x2D,
                                                              0xBC, 0x63, 0x98, 0xEA, 0xCA, 0x41, 0x34, 0xA8,
                                                              0x10, 0x16, 0xF9, 0x3D, 0x8D, 0xDD, 0xCB, 0x94,
                                                              0xC5, 0x4C, 0x23, 0xAC, 0x45, 0x71, 0x32, 0xE2,
                                                              0x89, 0x3B, 0x60, 0x8B, 0x31, 0xA3, 0x30, 0x78}};

const PKA_EccParam512 BrainpoolP512R1_b           = {.byte = {0x23, 0xF7, 0x16, 0x80, 0x63, 0xBD, 0x09, 0x28,
                                                              0xDD, 0xE5, 0xBA, 0x5E, 0xB7, 0x50, 0x40, 0x98,
                                                              0x67, 0x3E, 0x08, 0xDC, 0xCA, 0x94, 0xFC, 0x77,
                                                              0x4D, 0xAC, 0xC1, 0xE7, 0xB9, 0xC7, 0xF2, 0x2B,
                                                              0xA7, 0x17, 0x11, 0x7F, 0xB5, 0xC8, 0x9A, 0x8B,
                                                              0xC9, 0xF1, 0x2E, 0x0A, 0xA1, 0x3A, 0x25, 0xA8,
                                                              0x5A, 0x5D, 0xED, 0x2D, 0xBC, 0x63, 0x98, 0xEA,
                                                              0xCA, 0x41, 0x34, 0xA8, 0x10, 0x16, 0xF9, 0x3D}};

const PKA_EccParam512 BrainpoolP512R1_order       = {.byte = {0x69, 0x00, 0xA9, 0x9C, 0x82, 0x96, 0x87, 0xB5,
                                                              0xDD, 0xDA, 0x5D, 0x08, 0x81, 0xD3, 0xB1, 0x1D,
                                                              0x47, 0x10, 0xAC, 0x7F, 0x19, 0x61, 0x86, 0x41,
                                                              0x19, 0x26, 0xA9, 0x4C, 0x41, 0x5C, 0x3E, 0x55,
                                                              0x70, 0x08, 0x33, 0x70, 0xCA, 0x9C, 0x63, 0xD6,
                                                              0x0E, 0xD2, 0xC9, 0xB3, 0xB3, 0x8D, 0x30, 0xCB,
                                                              0x07, 0xFC, 0xC9, 0x33, 0xAE, 0xE6, 0xD4, 0x3F,
                                                              0x8B, 0xC4, 0xE9, 0xDB, 0xB8, 0x9D, 0xDD, 0xAA}};

//*****************************************************************************
//
// Curve25519 constants in little endian format. byte[0] is the least
// significant byte and byte[Curve25519_PARAM_SIZE_BYTES - 1] is the most
// significant.
//
//*****************************************************************************
const PKA_EccPoint256 Curve25519_generator = {
    .x = {.byte = {0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}},
    .y = {.byte = {0xd9, 0xd3, 0xce, 0x7e, 0xa2, 0xc5, 0xe9, 0x29,
                   0xb2, 0x61, 0x7c, 0x6d, 0x7e, 0x4d, 0x3d, 0x92,
                   0x4c, 0xd1, 0x48, 0x77, 0x2c, 0xdd, 0x1e, 0xe0,
                   0xb4, 0x86, 0xa0, 0xb8, 0xa1, 0x19, 0xae, 0x20}},
};

const PKA_EccParam256 Curve25519_prime       = {.byte = {0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}};

const PKA_EccParam256 Curve25519_a           = {.byte = {0x06, 0x6d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}};

const PKA_EccParam256 Curve25519_b           = {.byte = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}};

const PKA_EccParam256 Curve25519_order       = {.byte = {0xb9, 0xdc, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
                                                         0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}};

//*****************************************************************************
//
// Write a PKA parameter to the PKA module, set required registers, and add an offset.
//
//*****************************************************************************
static uint32_t PKAWritePkaParam(const uint8_t *param, uint32_t paramLength, uint32_t paramOffset, uint32_t ptrRegOffset)
{
    uint32_t i;
    uint32_t *paramWordAlias = (uint32_t *)param;
    // Take the floor of paramLength in 32-bit words
    uint32_t paramLengthInWords = paramLength / sizeof(uint32_t);

    // Only copy data if it is specified. We may wish to simply allocate another buffer and get
    // the required offset.
    if (param) {
        // Load the number in PKA RAM
        for (i = 0; i < paramLengthInWords; i++) {
            HWREG(PKA_RAM_BASE + paramOffset + sizeof(uint32_t) * i) = paramWordAlias[i];
        }

        // If the length is not a word-multiple, fill up a temporary word and copy that in
        // to avoid a bus error. The extra zeros at the end should not matter, as the large
        // number is little-endian and thus has no effect.
        // We could have correctly calculated ceiling(paramLength / sizeof(uint32_t)) above.
        // However, we would not have been able to zero-out the extra few most significant
        // bytes of the most significant word. That would have resulted in doing maths operations
        // on whatever follows param in RAM.
        if (paramLength % sizeof(uint32_t)) {
            uint32_t temp = 0;
            uint8_t j;

            // Load the entire word line of the param remainder
            temp = paramWordAlias[i];

            // Zero-out all bytes beyond the end of the param
            for (j = paramLength % sizeof(uint32_t); j < sizeof(uint32_t); j++) {
                ((uint8_t *)&temp)[j] = 0;
            }

            HWREG(PKA_RAM_BASE + paramOffset + sizeof(uint32_t) * i) = temp;

            // Increment paramLengthInWords since we take the ceiling of length / sizeof(uint32_t)
            paramLengthInWords++;
        }
    }

    // Update the A, B, C, or D pointer with the offset address of the PKA RAM location
    // where the number will be stored.
    switch (ptrRegOffset) {
        case PKA_O_APTR:
            HWREG(PKA_BASE + PKA_O_APTR) = paramOffset >> 2;
            HWREG(PKA_BASE + PKA_O_ALENGTH) = paramLengthInWords;
            break;
        case PKA_O_BPTR:
            HWREG(PKA_BASE + PKA_O_BPTR) = paramOffset >> 2;
            HWREG(PKA_BASE + PKA_O_BLENGTH) = paramLengthInWords;
            break;
        case PKA_O_CPTR:
            HWREG(PKA_BASE + PKA_O_CPTR) = paramOffset >> 2;
            break;
        case PKA_O_DPTR:
            HWREG(PKA_BASE + PKA_O_DPTR) = paramOffset >> 2;
            break;
    }

    // Ensure 8-byte alignment of next parameter.
    // Returns the offset for the next parameter.
    return paramOffset + sizeof(uint32_t) * (paramLengthInWords + (paramLengthInWords % 2));
}

//*****************************************************************************
//
// Write a PKA parameter to the PKA module but return a larger offset.
//
//*****************************************************************************
static uint32_t PKAWritePkaParamExtraOffset(const uint8_t *param, uint32_t paramLength, uint32_t paramOffset, uint32_t ptrRegOffset)
{
    // Ensure 16-byte alignment.
    return  (sizeof(uint32_t) * 2) + PKAWritePkaParam(param, paramLength, paramOffset, ptrRegOffset);
}

//*****************************************************************************
//
// Writes the result of a large number arithmetic operation to a provided buffer.
//
//*****************************************************************************
static uint32_t PKAGetBigNumResult(uint8_t *resultBuf, uint32_t *resultLength, uint32_t resultPKAMemAddr)
{
    uint32_t mswOffset;
    uint32_t lswOffset;
    uint32_t lengthInWords;
    uint32_t i;
    uint32_t *resultWordAlias = (uint32_t *)resultBuf;

    // Check the arguments.
    ASSERT(resultBuf);
    ASSERT((resultPKAMemAddr > PKA_RAM_BASE) &&
           (resultPKAMemAddr < (PKA_RAM_BASE + PKA_RAM_TOT_BYTE_SIZE)));

    // Verify that the operation is complete.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    // Get the MSW register value.
    mswOffset = HWREG(PKA_BASE + PKA_O_MSW);

    // If the result vector is zero, write back one zero byte so the caller does not need
    // to handle a special error for the perhaps valid result of zero.
    // They will only get the error status if they do not provide a buffer
    if (mswOffset & PKA_MSW_RESULT_IS_ZERO_M) {
        if (*resultLength){
            if(resultBuf){
                resultBuf[0] = 0;
            }

            *resultLength = 1;

            return PKA_STATUS_SUCCESS;
        }
        else {
            return PKA_STATUS_BUF_UNDERFLOW;
        }
    }

    // Get the length of the result
    mswOffset = ((mswOffset & PKA_MSW_MSW_ADDRESS_M) + 1);
    lswOffset = ((resultPKAMemAddr - PKA_RAM_BASE) >> 2);

    if (mswOffset >= lswOffset) {
        lengthInWords = mswOffset - lswOffset;
    }
    else {
        return PKA_STATUS_RESULT_ADDRESS_INCORRECT;
    }

    // Check if the provided buffer length is adequate to store the result data.
    if (*resultLength < lengthInWords * sizeof(uint32_t)) {
        return PKA_STATUS_BUF_UNDERFLOW;
    }

    // Copy the resultant length.
    *resultLength = lengthInWords * sizeof(uint32_t);


    if (resultBuf) {
        // Copy the result into the resultBuf.
        for (i = 0; i < lengthInWords; i++) {
            resultWordAlias[i]= HWREG(resultPKAMemAddr + sizeof(uint32_t) * i);
        }
    }

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Retrieve the result of a modulo operation or the remainder of a division.
//
//*****************************************************************************
static uint32_t PKAGetBigNumResultRemainder(uint8_t *resultBuf, uint32_t *resultLength, uint32_t resultPKAMemAddr)
{
    uint32_t regMSWVal;
    uint32_t lengthInWords;
    uint32_t i;
    uint32_t *resultWordAlias = (uint32_t *)resultBuf;

    // Check the arguments.
    ASSERT(resultBuf);
    ASSERT((resultPKAMemAddr > PKA_RAM_BASE) &&
           (resultPKAMemAddr < (PKA_RAM_BASE + PKA_RAM_TOT_BYTE_SIZE)));

    // Verify that the operation is complete.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    // Get the MSW register value.
    regMSWVal = HWREG(PKA_BASE + PKA_O_DIVMSW);

    // If the result vector is zero, write back one zero byte so the caller does not need
    // to handle a special error for the perhaps valid result of zero.
    // They will only get the error status if they do not provide a buffer
    if (regMSWVal & PKA_DIVMSW_RESULT_IS_ZERO_M) {
        if (*resultLength){
            if(resultBuf){
                resultBuf[0] = 0;
            }

            *resultLength = 1;

            return PKA_STATUS_SUCCESS;
        }
        else {
            return PKA_STATUS_BUF_UNDERFLOW;
        }
    }

    // Get the length of the result
    lengthInWords = ((regMSWVal & PKA_DIVMSW_MSW_ADDRESS_M) + 1) - ((resultPKAMemAddr - PKA_RAM_BASE) >> 2);

    // Check if the provided buffer length is adequate to store the result data.
    if (*resultLength < lengthInWords * sizeof(uint32_t)) {
        return PKA_STATUS_BUF_UNDERFLOW;
    }

    // Copy the resultant length.
    *resultLength = lengthInWords * sizeof(uint32_t);

    if (resultBuf) {
        // Copy the result into the resultBuf.
        for (i = 0; i < lengthInWords; i++) {
            resultWordAlias[i] = HWREG(resultPKAMemAddr + sizeof(uint32_t) * i);
        }
    }

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Writes the resultant curve point of an ECC operation to the provided buffer.
//
//*****************************************************************************
static uint32_t PKAGetECCResult(uint8_t *curvePointX, uint8_t *curvePointY, uint32_t resultPKAMemAddr, uint32_t length)
{
    uint32_t i = 0;
    uint32_t *xWordAlias = (uint32_t *)curvePointX;
    uint32_t *yWordAlias = (uint32_t *)curvePointY;
    uint32_t lengthInWordsCeiling = 0;

    // Check for the arguments.
    ASSERT(curvePointX);
    ASSERT(curvePointY);
    ASSERT((resultPKAMemAddr > PKA_RAM_BASE) &&
           (resultPKAMemAddr < (PKA_RAM_BASE + PKA_RAM_TOT_BYTE_SIZE)));

    // Verify that the operation is completed.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    if (HWREG(PKA_BASE + PKA_O_SHIFT)) {
         return PKA_STATUS_FAILURE;
    }

    // Check to make sure that the result vector is not the point at infinity.
    if (HWREG(PKA_BASE + PKA_O_MSW) & PKA_MSW_RESULT_IS_ZERO) {
        return PKA_STATUS_POINT_AT_INFINITY;
    }

    if (curvePointX != NULL) {
        // Copy the x co-ordinate value of the result from vector D into
        // the curvePoint.
        for (i = 0; i < (length / sizeof(uint32_t)); i++) {
            xWordAlias[i] = HWREG(resultPKAMemAddr + sizeof(uint32_t) * i);
        }

        // If the length is not a word-multiple, fill up a temporary word and copy that in
        // to avoid a bus error.
        if (length % sizeof(uint32_t)) {
            uint32_t temp = 0;
            uint8_t j;

            // Load the entire word line of the coordinate remainder
            temp = HWREG(resultPKAMemAddr + sizeof(uint32_t) * i);

            // Write all remaining bytes to the coordinate
            for (j = 0; j < length % sizeof(uint32_t); j++) {
                curvePointX[i * sizeof(uint32_t) + j] = ((uint8_t *)&temp)[j];
            }

        }
    }

    lengthInWordsCeiling = (length % sizeof(uint32_t)) ? length / sizeof(uint32_t) + 1 : length / sizeof(uint32_t);

    resultPKAMemAddr += sizeof(uint32_t) * (2 + lengthInWordsCeiling + (lengthInWordsCeiling % 2));

    if (curvePointY != NULL) {
        // Copy the y co-ordinate value of the result from vector D into
        // the curvePoint.
        for (i = 0; i < (length / sizeof(uint32_t)); i++) {
            yWordAlias[i] = HWREG(resultPKAMemAddr + sizeof(uint32_t) * i);
        }

        // If the length is not a word-multiple, fill up a temporary word and copy that in
        // to avoid a bus error.
        if (length % sizeof(uint32_t)) {
            uint32_t temp = 0;
            uint8_t j;

            // Load the entire word line of the coordinate remainder
            temp = HWREG(resultPKAMemAddr + sizeof(uint32_t) * i);

            // Write all remaining bytes to the coordinate
            for (j = 0; j < length % sizeof(uint32_t); j++) {
                curvePointY[i * sizeof(uint32_t) + j] = ((uint8_t *)&temp)[j];
            }
        }
    }


    return PKA_STATUS_SUCCESS;
}


//*****************************************************************************
//
// Provides the PKA operation status.
//
//*****************************************************************************
uint32_t PKAGetOpsStatus(void)
{
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN_M) {
        return PKA_STATUS_OPERATION_BUSY;
    }
    else {
        return PKA_STATUS_OPERATION_RDY;
    }
}

//*****************************************************************************
//
// Check if an array consists only of zeros.
//
//*****************************************************************************
bool PKAArrayAllZeros(const uint8_t *array, uint32_t arrayLength)
{
    uint32_t i;
    uint8_t arrayBits = 0;

    // We could speed things up by comparing word-wise rather than byte-wise.
    // However, this extra overhead is inconsequential compared to running an
    // actual PKA operation. Especially ECC operations.
    for (i = 0; i < arrayLength; i++) {
        arrayBits |= array[i];
    }

    if (arrayBits) {
        return false;
    }
    else {
        return true;
    }

}

//*****************************************************************************
//
// Fill an array with zeros
//
//*****************************************************************************
void PKAZeroOutArray(const uint8_t *array, uint32_t arrayLength)
{
    uint32_t i;
    // Take the floor of paramLength in 32-bit words
    uint32_t arrayLengthInWords = arrayLength / sizeof(uint32_t);

    // Zero-out the array word-wise until i >= arrayLength
    for (i = 0; i < arrayLengthInWords * sizeof(uint32_t); i += 4) {
        HWREG(array + i) = 0;
    }

    // If i != arrayLength, there are some remaining bytes to zero-out
    if (arrayLength % sizeof(uint32_t)) {
        // Subtract 4 from i, since i has already overshot the array
        for (i -= 4; i < arrayLength; i++) {
            HWREGB(array + i * sizeof(uint32_t));
        }
    }
}

//*****************************************************************************
//
// Start the big number modulus operation.
//
//*****************************************************************************
uint32_t PKABigNumModStart(const uint8_t *bigNum, uint32_t bigNumLength, const uint8_t *modulus, uint32_t modulusLength, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check the arguments.
    ASSERT(bigNum);
    ASSERT(modulus);
    ASSERT(resultPKAMemAddr);

    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(bigNum, bigNumLength, offset, PKA_O_APTR);

    offset = PKAWritePkaParamExtraOffset(modulus, modulusLength, offset, PKA_O_BPTR);

    // Copy the result vector address location.
    *resultPKAMemAddr = PKA_RAM_BASE + offset;

    // Load C pointer with the result location in PKA RAM
    HWREG(PKA_BASE + PKA_O_CPTR) = offset >> 2;

    // Start the PKCP modulo operation by setting the PKA Function register.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = (PKA_FUNCTION_RUN | PKA_FUNCTION_MODULO);

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the result of the big number modulus operation.
//
//*****************************************************************************
uint32_t PKABigNumModGetResult(uint8_t *resultBuf, uint32_t length, uint32_t resultPKAMemAddr)
{
    // Zero-out array in case modulo result is shorter than length
    PKAZeroOutArray(resultBuf, length);

    return PKAGetBigNumResultRemainder(resultBuf, &length, resultPKAMemAddr);
}

//*****************************************************************************
//
// Start the big number divide operation.
//
//*****************************************************************************
uint32_t PKABigNumDivideStart(const uint8_t *dividend, uint32_t dividendLength, const uint8_t *divisor, uint32_t divisorLength, uint32_t *resultQuotientMemAddr, uint32_t *resultRemainderMemAddr)
{
    uint32_t offset = 0;

    // Check the arguments.
    ASSERT(dividend);
    ASSERT(divisor);
    ASSERT(resultQuotientMemAddr);
    ASSERT(resultRemainderMemAddr);

    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(dividend, dividendLength, offset, PKA_O_APTR);

    offset = PKAWritePkaParamExtraOffset(divisor, divisorLength, offset, PKA_O_BPTR);

    // Copy the remainder result vector address location.
    if (resultRemainderMemAddr) {
        *resultRemainderMemAddr = PKA_RAM_BASE + offset;
    }

    // The remainder cannot ever be larger than the divisor. It should fit inside
    // a buffer of that size.
    offset = PKAWritePkaParamExtraOffset(0, divisorLength, offset, PKA_O_CPTR);

    // Copy the remainder result vector address location.
    if (resultQuotientMemAddr) {
        *resultQuotientMemAddr = PKA_RAM_BASE + offset;
    }

    // Load D pointer with the quotient location in PKA RAM
    HWREG(PKA_BASE + PKA_O_DPTR) = offset >> 2;

    // Start the PKCP modulo operation by setting the PKA Function register.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = (PKA_FUNCTION_RUN | PKA_FUNCTION_DIVIDE);

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the quotient of the big number divide operation.
//
//*****************************************************************************
uint32_t PKABigNumDivideGetQuotient(uint8_t *resultBuf, uint32_t *length, uint32_t resultQuotientMemAddr)
{
    return PKAGetBigNumResult(resultBuf, length, resultQuotientMemAddr);
}

//*****************************************************************************
//
// Get the remainder of the big number divide operation.
//
//*****************************************************************************
uint32_t PKABigNumDivideGetRemainder(uint8_t *resultBuf, uint32_t *length, uint32_t resultQuotientMemAddr)
{
    return PKAGetBigNumResultRemainder(resultBuf, length, resultQuotientMemAddr);
}


//*****************************************************************************
//
// Start the comparison of two big numbers.
//
//*****************************************************************************
uint32_t PKABigNumCmpStart(const uint8_t *bigNum1, const uint8_t *bigNum2, uint32_t length)
{
    uint32_t offset = 0;

    // Check the arguments.
    ASSERT(bigNum1);
    ASSERT(bigNum2);

    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(bigNum1, length, offset, PKA_O_APTR);

    offset = PKAWritePkaParam(bigNum2, length, offset, PKA_O_BPTR);

    // Set the PKA Function register for the Compare operation
    // and start the operation.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = (PKA_FUNCTION_RUN | PKA_FUNCTION_COMPARE);

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the result of the comparison operation of two big numbers.
//
//*****************************************************************************
uint32_t PKABigNumCmpGetResult(void)
{
    uint32_t  status;

    // verify that the operation is complete.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    // Check the COMPARE register.
    switch(HWREG(PKA_BASE + PKA_O_COMPARE)) {
        case PKA_COMPARE_A_EQUALS_B:
            status = PKA_STATUS_EQUAL;
            break;

        case PKA_COMPARE_A_GREATER_THAN_B:
            status = PKA_STATUS_A_GREATER_THAN_B;
            break;

        case PKA_COMPARE_A_LESS_THAN_B:
            status = PKA_STATUS_A_LESS_THAN_B;
            break;

        default:
            status = PKA_STATUS_FAILURE;
            break;
    }

    return status;
}

//*****************************************************************************
//
// Start the big number inverse modulo operation.
//
//*****************************************************************************
uint32_t PKABigNumInvModStart(const uint8_t *bigNum, uint32_t bigNumLength, const uint8_t *modulus, uint32_t modulusLength, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check the arguments.
    ASSERT(bigNum);
    ASSERT(modulus);
    ASSERT(resultPKAMemAddr);

    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(bigNum, bigNumLength, offset, PKA_O_APTR);

    offset = PKAWritePkaParam(modulus, modulusLength, offset, PKA_O_BPTR);

    // Copy the result vector address location.
    *resultPKAMemAddr = PKA_RAM_BASE + offset;

    // Load D pointer with the result location in PKA RAM.
    HWREG(PKA_BASE + PKA_O_DPTR) = offset >> 2;

    // set the PKA function to InvMod operation and the start the operation.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = 0x0000F000;

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the result of the big number inverse modulo operation.
//
//*****************************************************************************
uint32_t PKABigNumInvModGetResult(uint8_t *resultBuf, uint32_t length, uint32_t resultPKAMemAddr)
{
    // Zero-out array in case modulo result is shorter than length
    PKAZeroOutArray(resultBuf, length);

    return PKAGetBigNumResult(resultBuf, &length, resultPKAMemAddr);
}

//*****************************************************************************
//
// Start the big number multiplication.
//
//*****************************************************************************
uint32_t PKABigNumMultiplyStart(const uint8_t *multiplicand, uint32_t multiplicandLength, const uint8_t *multiplier, uint32_t multiplierLength, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check for the arguments.
    ASSERT(multiplicand);
    ASSERT(multiplier);
    ASSERT(resultPKAMemAddr);

    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(multiplicand, multiplicandLength, offset, PKA_O_APTR);

    offset = PKAWritePkaParam(multiplier, multiplierLength, offset, PKA_O_BPTR);


    // Copy the result vector address location.
    *resultPKAMemAddr = PKA_RAM_BASE + offset;

    // Load C pointer with the result location in PKA RAM.
    HWREG(PKA_BASE + PKA_O_CPTR) = offset >> 2;

    // Set the PKA function to the multiplication and start it.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = (PKA_FUNCTION_RUN | PKA_FUNCTION_MULTIPLY);

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the results of the big number multiplication.
//
//*****************************************************************************
uint32_t PKABigNumMultGetResult(uint8_t *resultBuf, uint32_t *resultLength, uint32_t resultPKAMemAddr)
{
     return PKAGetBigNumResult(resultBuf, resultLength, resultPKAMemAddr);
}

//*****************************************************************************
//
// Start the addition of two big number.
//
//*****************************************************************************
uint32_t PKABigNumAddStart(const uint8_t *bigNum1, uint32_t bigNum1Length, const uint8_t *bigNum2, uint32_t bigNum2Length, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check for arguments.
    ASSERT(bigNum1);
    ASSERT(bigNum2);
    ASSERT(resultPKAMemAddr);

    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(bigNum1, bigNum1Length, offset, PKA_O_APTR);

    offset = PKAWritePkaParam(bigNum2, bigNum2Length, offset, PKA_O_BPTR);

    // Copy the result vector address location.
    *resultPKAMemAddr = PKA_RAM_BASE + offset;

    // Load C pointer with the result location in PKA RAM.
    HWREG(PKA_BASE + PKA_O_CPTR) = offset >> 2;

    // Set the function for the add operation and start the operation.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = (PKA_FUNCTION_RUN | PKA_FUNCTION_ADD);

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the result of the addition operation on two big number.
//
//*****************************************************************************
uint32_t PKABigNumSubGetResult(uint8_t *resultBuf, uint32_t *resultLength, uint32_t resultPKAMemAddr)
{
    return PKAGetBigNumResult(resultBuf, resultLength, resultPKAMemAddr);
}

//*****************************************************************************
//
// Start the addition of two big number.
//
//*****************************************************************************
uint32_t PKABigNumSubStart(const uint8_t *minuend, uint32_t minuendLength, const uint8_t *subtrahend, uint32_t subtrahendLength, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check for arguments.
    ASSERT(minuend);
    ASSERT(subtrahend);
    ASSERT(resultPKAMemAddr);


    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(minuend, minuendLength, offset, PKA_O_APTR);

    offset = PKAWritePkaParam(subtrahend, subtrahendLength, offset, PKA_O_BPTR);

    // Copy the result vector address location.
    *resultPKAMemAddr = PKA_RAM_BASE + offset;

    // Load C pointer with the result location in PKA RAM.
    HWREG(PKA_BASE + PKA_O_CPTR) = offset >> 2;

    // Set the function for the add operation and start the operation.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = (PKA_FUNCTION_RUN | PKA_FUNCTION_SUBTRACT);

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the result of the addition operation on two big number.
//
//*****************************************************************************
uint32_t PKABigNumAddGetResult(uint8_t *resultBuf, uint32_t *resultLength, uint32_t resultPKAMemAddr)
{
    return PKAGetBigNumResult(resultBuf, resultLength, resultPKAMemAddr);
}


//*****************************************************************************
//
// Start ECC Multiplication.
//
//*****************************************************************************
uint32_t PKAEccMultiplyStart(const uint8_t *scalar, const uint8_t *curvePointX, const uint8_t *curvePointY, const uint8_t *prime, const uint8_t *a, const uint8_t *b, uint32_t length, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check for the arguments.
    ASSERT(scalar);
    ASSERT(curvePointX);
    ASSERT(curvePointY);
    ASSERT(prime);
    ASSERT(a);
    ASSERT(b);
    ASSERT(length <= PKA_MAX_CURVE_SIZE_32_BIT_WORD * sizeof(uint32_t));
    ASSERT(resultPKAMemAddr);

    // Make sure no PKA operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(scalar, length, offset, PKA_O_APTR);

    offset = PKAWritePkaParamExtraOffset(prime, length, offset, PKA_O_BPTR);
    offset = PKAWritePkaParamExtraOffset(a, length, offset, PKA_NO_POINTER_REG);
    offset = PKAWritePkaParamExtraOffset(b, length, offset, PKA_NO_POINTER_REG);

    offset = PKAWritePkaParamExtraOffset(curvePointX, length, offset, PKA_O_CPTR);
    offset = PKAWritePkaParamExtraOffset(curvePointY, length, offset, PKA_NO_POINTER_REG);

    // Update the result location.
    // The resultPKAMemAddr may be 0 if we only want to check that we generated the point at infinity
    if (resultPKAMemAddr) {
        *resultPKAMemAddr =  PKA_RAM_BASE + offset;
    }

    // Load D pointer with the result location in PKA RAM.
    HWREG(PKA_BASE + PKA_O_DPTR) = offset >> 2;

    // Set the PKA function to ECC-MULT and start the operation.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = PKA_FUNCTION_RUN_M | (0x05 << PKA_FUNCTION_SEQUENCER_OPERATIONS_S);

    return PKA_STATUS_SUCCESS;
}


//*****************************************************************************
//
// Start ECC Montgomery Multiplication.
//
//*****************************************************************************
uint32_t PKAEccMontgomeryMultiplyStart(const uint8_t *scalar, const uint8_t *curvePointX, const uint8_t *prime, const uint8_t *a, uint32_t length, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check for the arguments.
    ASSERT(scalar);
    ASSERT(curvePointX);
    ASSERT(prime);
    ASSERT(a);
    ASSERT(length <= PKA_MAX_CURVE_SIZE_32_BIT_WORD * sizeof(uint32_t));
    ASSERT(resultPKAMemAddr);

    // Make sure no PKA operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParam(scalar, length, offset, PKA_O_APTR);

    offset = PKAWritePkaParamExtraOffset(prime, length, offset, PKA_O_BPTR);
    offset = PKAWritePkaParamExtraOffset(a, length, offset, PKA_NO_POINTER_REG);

    offset = PKAWritePkaParamExtraOffset(curvePointX, length, offset, PKA_O_CPTR);

    // Update the result location.
    // The resultPKAMemAddr may be 0 if we only want to check that we generated the point at infinity
    if (resultPKAMemAddr) {
        *resultPKAMemAddr =  PKA_RAM_BASE + offset;
    }

    // Load D pointer with the result location in PKA RAM.
    HWREG(PKA_BASE + PKA_O_DPTR) = offset >> 2;

    // Set the PKA function to Montgomery ECC-MULT and start the operation.
    HWREG(PKA_BASE + PKA_O_FUNCTION) = PKA_FUNCTION_RUN_M | (0x02 << PKA_FUNCTION_SEQUENCER_OPERATIONS_S);

    return PKA_STATUS_SUCCESS;
}


//*****************************************************************************
//
// Get the result of ECC Multiplication
//
//*****************************************************************************
uint32_t PKAEccMultiplyGetResult(uint8_t *curvePointX, uint8_t *curvePointY, uint32_t resultPKAMemAddr, uint32_t length)
{
    return PKAGetECCResult(curvePointX, curvePointY, resultPKAMemAddr, length);
}

//*****************************************************************************
//
// Start the ECC Addition.
//
//*****************************************************************************
uint32_t PKAEccAddStart(const uint8_t *curvePoint1X, const uint8_t *curvePoint1Y, const uint8_t *curvePoint2X, const uint8_t *curvePoint2Y, const uint8_t *prime, const uint8_t *a, uint32_t length, uint32_t *resultPKAMemAddr)
{
    uint32_t offset = 0;

    // Check for the arguments.
    ASSERT(curvePoint1X);
    ASSERT(curvePoint1Y);
    ASSERT(curvePoint2X);
    ASSERT(curvePoint2Y);
    ASSERT(prime);
    ASSERT(a);
    ASSERT(resultPKAMemAddr);

    // Make sure no operation is in progress.
    if (HWREG(PKA_BASE + PKA_O_FUNCTION) & PKA_FUNCTION_RUN) {
        return PKA_STATUS_OPERATION_BUSY;
    }

    offset = PKAWritePkaParamExtraOffset(curvePoint1X, length, offset, PKA_O_APTR);
    offset = PKAWritePkaParamExtraOffset(curvePoint1Y, length, offset, PKA_NO_POINTER_REG);


    offset = PKAWritePkaParamExtraOffset(prime, length, offset, PKA_O_BPTR);
    offset = PKAWritePkaParamExtraOffset(a, length, offset, PKA_NO_POINTER_REG);

    offset = PKAWritePkaParamExtraOffset(curvePoint2X, length, offset, PKA_O_CPTR);
    offset = PKAWritePkaParamExtraOffset(curvePoint2Y, length, offset, PKA_NO_POINTER_REG);

    // Copy the result vector location.
    *resultPKAMemAddr = PKA_RAM_BASE + offset;

    // Load D pointer with the result location in PKA RAM.
    HWREG(PKA_BASE + PKA_O_DPTR) = offset >> 2;

    // Set the PKA Function to ECC-ADD and start the operation.
    HWREG(PKA_BASE + PKA_O_FUNCTION ) = PKA_FUNCTION_RUN_M | (0x03 << PKA_FUNCTION_SEQUENCER_OPERATIONS_S);

    return PKA_STATUS_SUCCESS;
}

//*****************************************************************************
//
// Get the result of the ECC Addition
//
//*****************************************************************************
uint32_t PKAEccAddGetResult(uint8_t *curvePointX, uint8_t *curvePointY, uint32_t resultPKAMemAddr, uint32_t length)
{
    return PKAGetECCResult(curvePointX, curvePointY, resultPKAMemAddr, length);
}

//*****************************************************************************
//
// Verify a public key against the supplied elliptic curve equation
//
//*****************************************************************************
uint32_t PKAEccVerifyPublicKeyWeierstrassStart(const uint8_t *curvePointX, const uint8_t *curvePointY, const uint8_t *prime, const uint8_t *a, const uint8_t *b, const uint8_t *order, uint32_t length)
{
    uint32_t pkaResult;
    uint32_t resultAddress;
    uint32_t resultLength;
    uint8_t *scratchBuffer = (uint8_t *)(PKA_RAM_BASE + PKA_RAM_TOT_BYTE_SIZE / 2);
    uint8_t *scratchBuffer2 = scratchBuffer + 512;


    // Verify X in range [0, prime - 1]
    PKABigNumCmpStart(curvePointX,
                      prime,
                      length);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    pkaResult = PKABigNumCmpGetResult();

    if (pkaResult != PKA_STATUS_A_LESS_THAN_B) {
        return PKA_STATUS_X_LARGER_THAN_PRIME;
    }

    // Verify Y in range [0, prime - 1]
    PKABigNumCmpStart(curvePointY,
                      prime,
                      length);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    pkaResult = PKABigNumCmpGetResult();

    if (pkaResult != PKA_STATUS_A_LESS_THAN_B) {
        return PKA_STATUS_Y_LARGER_THAN_PRIME;
    }

    // Verify point on curve
    // Short-Weierstrass equation: Y ^ 2 = X ^3 + a * X + b mod P
    // Reduced: Y ^ 2 = X * (X ^ 2 + a) + b

    // tmp = X ^ 2
    PKABigNumMultiplyStart(curvePointX, length, curvePointX, length, &resultAddress);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    resultLength = 200;
    pkaResult = PKABigNumMultGetResult(scratchBuffer, &resultLength, resultAddress);

    if (pkaResult != PKA_STATUS_SUCCESS) {
        return PKA_STATUS_FAILURE;
    }

    // tmp += a
    PKABigNumAddStart(scratchBuffer, resultLength, a, length, &resultAddress);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    resultLength = 200;
    pkaResult = PKABigNumAddGetResult(scratchBuffer, &resultLength, resultAddress);

    if (pkaResult != PKA_STATUS_SUCCESS) {
        return PKA_STATUS_FAILURE;
    }

    // tmp *= x
    PKABigNumMultiplyStart(scratchBuffer, resultLength, curvePointX, length, &resultAddress);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    resultLength = 200;
    pkaResult = PKABigNumMultGetResult(scratchBuffer, &resultLength, resultAddress);

    if (pkaResult != PKA_STATUS_SUCCESS) {
        return PKA_STATUS_FAILURE;
    }

    // tmp += b
     PKABigNumAddStart(scratchBuffer, resultLength, b, length, &resultAddress);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    resultLength = 200;
    pkaResult = PKABigNumAddGetResult(scratchBuffer, &resultLength, resultAddress);

    if (pkaResult != PKA_STATUS_SUCCESS) {
        return PKA_STATUS_FAILURE;
    }


    // tmp2 = tmp % prime to ensure we have no fraction in the division.
    // The number will only shrink from here on out.
    PKABigNumModStart(scratchBuffer, resultLength, prime, length, &resultAddress);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    // If the result is not a multiple of the word-length, the PKA HW will round up
    // because it deals in words only. That means that using 'length' directly
    // would cause and underflow, since length refers to the actual length in bytes of
    // the curve parameters while the PKA HW reports that rounded up to the next
    // word boundary.
    // Use 200 as the resultLength instead since we are copying to the scratch buffer
    // anyway.
    // Practically, this only happens with curves such as NIST-P521 that are not word
    // multiples.
    resultLength = 200;
    pkaResult = PKABigNumModGetResult(scratchBuffer2, resultLength, resultAddress);

    if (pkaResult != PKA_STATUS_SUCCESS) {
        return PKA_STATUS_FAILURE;
    }

    // tmp = y^2
    PKABigNumMultiplyStart(curvePointY, length, curvePointY, length, &resultAddress);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    resultLength = 200;
    pkaResult = PKABigNumMultGetResult(scratchBuffer, &resultLength, resultAddress);

    if (pkaResult != PKA_STATUS_SUCCESS) {
        return PKA_STATUS_FAILURE;
    }

    // tmp %= prime
    PKABigNumModStart(scratchBuffer, resultLength, prime, length, &resultAddress);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    // If the result is not a multiple of the word-length, the PKA HW will round up
    // because it deals in words only. That means that using 'length' directly
    // would cause and underflow, since length refers to the actual length in bytes of
    // the curve parameters while the PKA HW reports that rounded up to the next
    // word boundary.
    // Use 200 as the resultLength instead since we are copying to the scratch buffer
    // anyway.
    // Practically, this only happens with curves such as NIST-P521 that are not word
    // multiples.
    resultLength = 200;
    pkaResult = PKABigNumModGetResult(scratchBuffer, resultLength, resultAddress);

    if (pkaResult != PKA_STATUS_SUCCESS) {
        return PKA_STATUS_FAILURE;
    }

    // tmp ?= tmp2
    PKABigNumCmpStart(scratchBuffer,
                      scratchBuffer2,
                      length);

    while(PKAGetOpsStatus() == PKA_STATUS_OPERATION_BUSY);

    pkaResult = PKABigNumCmpGetResult();

    if (pkaResult != PKA_STATUS_EQUAL) {
        return PKA_STATUS_POINT_NOT_ON_CURVE;
    }
    else {
        return PKA_STATUS_SUCCESS;
    }
}


static uint32_t SHA2ExecuteHash(const uint8_t *message, uint8_t *resultDigest, uint32_t *intermediateDigest, uint32_t totalMsgLength, uint32_t messageLength, uint32_t hashAlgorithm, bool initialHash, bool finalHash);


void SHA2StartDMAOperation(uint8_t *channel0Addr, uint32_t channel0Length,  uint8_t *channel1Addr, uint32_t channel1Length) {

    // Clear any outstanding events.
    HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = CRYPTO_IRQCLR_RESULT_AVAIL_M | CRYPTO_IRQEN_DMA_IN_DONE_M;

    while(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & (CRYPTO_IRQSTAT_DMA_IN_DONE_M | CRYPTO_IRQSTAT_RESULT_AVAIL_M));

    if (channel0Addr) {
        // Configure the DMA controller - enable both DMA channels.
        HWREGBITW(CRYPTO_BASE + CRYPTO_O_DMACH0CTL, CRYPTO_DMACH0CTL_EN_BITN) = 1;

        // Base address of the payload data in ext. memory.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0EXTADDR) = (uint32_t)channel0Addr;

        // Payload data length in bytes, equal to the cipher text length.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0LEN) = channel0Length;
    }

    if (channel1Addr) {
        // Enable DMA channel 1.
        HWREGBITW(CRYPTO_BASE + CRYPTO_O_DMACH1CTL, CRYPTO_DMACH1CTL_EN_BITN) = 1;

        // Base address of the output data buffer.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH1EXTADDR) = (uint32_t)channel1Addr;

        // Output data length in bytes, equal to the cipher text length.
        HWREG(CRYPTO_BASE + CRYPTO_O_DMACH1LEN) = channel1Length;
    }
}

uint32_t SHA2WaitForIRQFlags(uint32_t irqFlags) {
    uint32_t irqTrigger = 0;
    // Wait for the DMA operation to complete. Add a delay to make sure we are
    // not flooding the bus with requests too much.
    do {
        CPUdelay(1);
    }
    while(!(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & irqFlags & (CRYPTO_IRQSTAT_DMA_IN_DONE_M | CRYPTO_IRQSTAT_RESULT_AVAIL_M)));

    // Save the IRQ trigger source
    irqTrigger = HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT);

    // Clear IRQ flags
    HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = irqFlags;

    while(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & irqFlags & (CRYPTO_IRQSTAT_DMA_IN_DONE_M | CRYPTO_IRQSTAT_RESULT_AVAIL_M));

    return irqTrigger;
}

uint32_t SHA2ComputeInitialHash(const uint8_t *message, uint32_t *intermediateDigest, uint32_t hashAlgorithm, uint32_t initialMessageLength) {
    ASSERT(message);
    ASSERT((hashAlgorithm == SHA2_MODE_SELECT_SHA224) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA256) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA384) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA512));
    ASSERT(!(intermediateDigest == NULL) && !((uint32_t)intermediateDigest & 0x03));

    return SHA2ExecuteHash(message, (uint8_t *)intermediateDigest, intermediateDigest, initialMessageLength, initialMessageLength, hashAlgorithm, true, false);
}

uint32_t SHA2ComputeIntermediateHash(const uint8_t *message, uint32_t *intermediateDigest, uint32_t hashAlgorithm, uint32_t intermediateMessageLength) {
    ASSERT(message);
    ASSERT(!(intermediateDigest == NULL) && !((uint32_t)intermediateDigest & 0x03));
    ASSERT((hashAlgorithm == SHA2_MODE_SELECT_SHA224) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA256) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA384) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA512));

    return SHA2ExecuteHash(message, (uint8_t *)intermediateDigest, intermediateDigest, 0, intermediateMessageLength, hashAlgorithm, false, false);
}

uint32_t SHA2ComputeFinalHash(const uint8_t *message, uint8_t *resultDigest, uint32_t *intermediateDigest, uint32_t totalMsgLength, uint32_t messageLength, uint32_t hashAlgorithm) {
    ASSERT(message);
    ASSERT(totalMsgLength);
    ASSERT(!(intermediateDigest == NULL) && !((uint32_t)intermediateDigest & 0x03));
    ASSERT(resultDigest);
    ASSERT((hashAlgorithm == SHA2_MODE_SELECT_SHA224) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA256) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA384) ||
           (hashAlgorithm == SHA2_MODE_SELECT_SHA512));

    return SHA2ExecuteHash(message, resultDigest, intermediateDigest, totalMsgLength, messageLength, hashAlgorithm, false, true);
}

uint32_t SHA2ComputeHash(const uint8_t *message, uint8_t *resultDigest, uint32_t totalMsgLength, uint32_t hashAlgorithm) {
    ASSERT(message);
    ASSERT(totalMsgLength);
    ASSERT(resultDigest);
    ASSERT((hashAlgorithm == SHA2_MODE_SELECT_SHA224) ||
       (hashAlgorithm == SHA2_MODE_SELECT_SHA256) ||
       (hashAlgorithm == SHA2_MODE_SELECT_SHA384) ||
       (hashAlgorithm == SHA2_MODE_SELECT_SHA512));

    return SHA2ExecuteHash(message, resultDigest, 0, totalMsgLength, totalMsgLength, hashAlgorithm, true, true);
}

static uint32_t SHA2ExecuteHash(const uint8_t *message, uint8_t *resultDigest, uint32_t *intermediateDigest, uint32_t totalMsgLength, uint32_t messageLength, uint32_t hashAlgorithm, bool initialHash, bool finalHash) {
    uint8_t digestLength = 0;
    uint32_t dmaAlgorithmSelect = 0;

    SHA2ClearDigestAvailableFlag();

    switch (hashAlgorithm) {
        case SHA2_MODE_SELECT_SHA224:
            digestLength = SHA2_SHA224_DIGEST_LENGTH_BYTES;
            dmaAlgorithmSelect = SHA2_ALGSEL_SHA256;
            break;
        case SHA2_MODE_SELECT_SHA256:
            digestLength = SHA2_SHA256_DIGEST_LENGTH_BYTES;
            dmaAlgorithmSelect = SHA2_ALGSEL_SHA256;
            break;
        case SHA2_MODE_SELECT_SHA384:
            digestLength = SHA2_SHA384_DIGEST_LENGTH_BYTES;
            dmaAlgorithmSelect = SHA2_ALGSEL_SHA512;
            break;
        case SHA2_MODE_SELECT_SHA512:
            digestLength = SHA2_SHA512_DIGEST_LENGTH_BYTES;
            dmaAlgorithmSelect = SHA2_ALGSEL_SHA512;
            break;
        default:
            return SHA2_INVALID_ALGORITHM;
    }

    if (initialHash && finalHash) {
        // The empty string is a perfectly valid message. It obviously has a length of 0. The DMA cannot
        // handle running with a transfer length of 0. This workaround depends on the hash engine adding the
        // trailing 1 bit and 0-padding bits after the DMAtransfer is complete and not in the DMA itself.
        // totalMsgLength is purposefully not altered as it is appended to the end of the message during finalization
        // and determines how many padding-bytes are added.
        // Altering totalMsgLength would alter the final hash digest.
        // Because totalMsgLength specifies that the message is of length 0, the content of the byte loaded
        // through the DMA is irrelevant. It is overwritten internally in the hash engine.
        messageLength = messageLength ? messageLength : 1;
    }

    // Setting the incorrect number of bits here leads to the calculation of the correct result
    // but a failure to read them out.
    // The tag bit is set to read out the digest via DMA rather than through the slave interface.
    SHA2SelectAlgorithm(dmaAlgorithmSelect | (resultDigest ? SHA2_ALGSEL_TAG : 0));
    SHA2IntClear(SHA2_DMA_IN_DONE | SHA2_RESULT_RDY);
    SHA2IntEnable(SHA2_DMA_IN_DONE | SHA2_RESULT_RDY);

    HWREG(CRYPTO_BASE + CRYPTO_O_HASHMODE) = hashAlgorithm | (initialHash ? CRYPTO_HASHMODE_NEW_HASH_M : 0);

    // Only load the intermediate digest if requested.
    if (intermediateDigest && !initialHash) {
        SHA2SetDigest(intermediateDigest, digestLength);
    }

    // If this is the final hash, finalization is required. This means appending a 1 bit, padding the message until this section
    // is 448 bytes long, and adding the 64 bit total length of the message in bits. Thankfully, this is all done in hardware.
    if (finalHash) {
        // This specific length must be specified in bits not bytes.
        SHA2SetMessageLength(totalMsgLength * 8);
        HWREG(CRYPTO_BASE + CRYPTO_O_HASHIOBUFCTRL) = CRYPTO_HASHIOBUFCTRL_PAD_DMA_MESSAGE_M;

    }

    // The cast is fine in this case. SHA2StartDMAOperation channel one serves as input and no one does
    // hash operations in-place.
    SHA2StartDMAOperation((uint8_t *)message, messageLength,  resultDigest, digestLength);

    return SHA2_SUCCESS;
}
