/*
 * -------------------------------------------
 *    MSP432 DriverLib - v3_21_00_05 
 * -------------------------------------------
 *
 * --COPYRIGHT--,BSD,BSD
 * Copyright (c) 2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
/* Standard Includes */
#include <stdint.h>

/* DriverLib Includes */
#include <flash.h>
#include <debug.h>
#include <interrupt.h>
#include <msp.h>
#include <cpu.h>
#include <rom.h>
#include <sysctl.h>
#include <hw_memmap.h>

static const uint32_t MAX_ERASE_NO_TLV = 50;
static const uint32_t MAX_PROGRAM_NO_TLV = 5;

static volatile uint32_t* __getBurstProgramRegs[16] =
{ &FLCTL->PRGBRST_DATA0_0, &FLCTL->PRGBRST_DATA0_1,
&FLCTL->PRGBRST_DATA0_2, &FLCTL->PRGBRST_DATA0_3,
&FLCTL->PRGBRST_DATA1_0, &FLCTL->PRGBRST_DATA1_1,
&FLCTL->PRGBRST_DATA1_2, &FLCTL->PRGBRST_DATA1_3,
&FLCTL->PRGBRST_DATA2_0, &FLCTL->PRGBRST_DATA2_1,
&FLCTL->PRGBRST_DATA2_2, &FLCTL->PRGBRST_DATA2_3,
&FLCTL->PRGBRST_DATA3_0, &FLCTL->PRGBRST_DATA3_1,
&FLCTL->PRGBRST_DATA3_2, &FLCTL->PRGBRST_DATA3_3 };

static uint32_t getUserFlashSector(uint32_t addr)
{
    if (addr > 0x1ffff)
    {
        addr = addr - 0x20000;
    }

    switch (addr)
    {
    case 0:
        return FLASH_SECTOR0;
    case 0x1000:
        return FLASH_SECTOR1;
    case 0x2000:
        return FLASH_SECTOR2;
    case 0x3000:
        return FLASH_SECTOR3;
    case 0x4000:
        return FLASH_SECTOR4;
    case 0x5000:
        return FLASH_SECTOR5;
    case 0x6000:
        return FLASH_SECTOR6;
    case 0x7000:
        return FLASH_SECTOR7;
    case 0x8000:
        return FLASH_SECTOR8;
    case 0x9000:
        return FLASH_SECTOR9;
    case 0xA000:
        return FLASH_SECTOR10;
    case 0xB000:
        return FLASH_SECTOR11;
    case 0xC000:
        return FLASH_SECTOR12;
    case 0xD000:
        return FLASH_SECTOR13;
    case 0xE000:
        return FLASH_SECTOR14;
    case 0xF000:
        return FLASH_SECTOR15;
    case 0x10000:
        return FLASH_SECTOR16;
    case 0x11000:
        return FLASH_SECTOR17;
    case 0x12000:
        return FLASH_SECTOR18;
    case 0x13000:
        return FLASH_SECTOR19;
    case 0x14000:
        return FLASH_SECTOR20;
    case 0x15000:
        return FLASH_SECTOR21;
    case 0x16000:
        return FLASH_SECTOR22;
    case 0x17000:
        return FLASH_SECTOR23;
    case 0x18000:
        return FLASH_SECTOR24;
    case 0x19000:
        return FLASH_SECTOR25;
    case 0x1A000:
        return FLASH_SECTOR26;
    case 0x1B000:
        return FLASH_SECTOR27;
    case 0x1C000:
        return FLASH_SECTOR28;
    case 0x1D000:
        return FLASH_SECTOR29;
    case 0x1E000:
        return FLASH_SECTOR30;
    case 0x1F000:
        return FLASH_SECTOR31;
    default:
        ASSERT(false);
        return 0;
    }
}

void FlashCtl_getMemoryInfo(uint32_t addr, uint32_t *sectorNum,
        uint32_t *bankNum)
{
    uint32_t bankLimit;

    bankLimit = SysCtl_getFlashSize() / 2;

    if (addr > bankLimit)
    {
        *(sectorNum) = FLASH_BANK1;
        addr = (addr - bankLimit);
    } else
    {
        *(sectorNum) = FLASH_BANK0;
    }

    *(bankNum) = (addr - __MAIN_MEMORY_START__) / 4096;
}

static bool _FlashCtl_Program8(uint32_t src, uint32_t dest, uint32_t mTries)
{
    uint32_t ii;
    uint8_t data;

    /* Enabling the correct verification settings  */
    FlashCtl_setProgramVerification(FLASH_REGPRE | FLASH_REGPOST);
    FlashCtl_clearProgramVerification(FLASH_BURSTPOST | FLASH_BURSTPRE);

    data = HWREG8(src);

    for (ii = 0; ii < mTries; ii++)
    {
        /* Clearing flags */
        FLCTL->CLRIFG |= (FLASH_PROGRAM_ERROR | FLASH_POSTVERIFY_FAILED
                | FLASH_PREVERIFY_FAILED | FLASH_WRDPRGM_COMPLETE);

        HWREG8(dest) = data;

        while (!(FlashCtl_getInterruptStatus() & FLASH_WRDPRGM_COMPLETE))
        {
            __no_operation();
        }

        /* Pre-Verify */
        if ((BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_VER_PRE_OFS)
                && BITBAND_PERI(FLCTL->IFG, FLCTL_IFG_AVPRE_OFS)))
        {
            data = __FlashCtl_remaskData8Pre(data, dest);

            if (data != 0xFF)
            {
                FlashCtl_clearProgramVerification(FLASH_REGPRE);
                continue;
            }

        }

        /* Post Verify */
        if ((BITBAND_PERI(FLCTL->IFG, FLCTL_IFG_AVPST_OFS)))
        {
            data = __FlashCtl_remaskData8Post(data, dest);

            /* Seeing if we actually need to do another pulse */
            if (data == 0xFF)
                return true;

            FlashCtl_setProgramVerification(FLASH_REGPRE | FLASH_REGPOST);
            continue;
        }

        /* If we got this far, return true */
        return true;

    }

    return false;

}

static bool _FlashCtl_Program32(uint32_t src, uint32_t dest, uint32_t mTries)
{
    uint32_t ii;
    uint32_t data;

    /* Enabling the correct verification settings  */
    FlashCtl_setProgramVerification(FLASH_REGPRE | FLASH_REGPOST);
    FlashCtl_clearProgramVerification(FLASH_BURSTPOST | FLASH_BURSTPRE);

    data = HWREG32(src);

    for (ii = 0; ii < mTries; ii++)
    {
        /* Clearing flags */
        FLCTL->CLRIFG |= (FLASH_PROGRAM_ERROR | FLASH_POSTVERIFY_FAILED
                | FLASH_PREVERIFY_FAILED | FLASH_WRDPRGM_COMPLETE);

        HWREG32(dest) = data;

        while (!(FlashCtl_getInterruptStatus() & FLASH_WRDPRGM_COMPLETE))
        {
            __no_operation();
        }

        /* Pre-Verify */
        if ((BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_VER_PRE_OFS)
                && BITBAND_PERI(FLCTL->IFG, FLCTL_IFG_AVPRE_OFS)))
        {
            data = __FlashCtl_remaskData32Pre(data, dest);

            if (data != 0xFFFFFFFF)
            {

                FlashCtl_clearProgramVerification(FLASH_REGPRE);
                continue;
            }

        }

        /* Post Verify */
        if ((BITBAND_PERI(FLCTL->IFG, FLCTL_IFG_AVPST_OFS)))
        {
            data = __FlashCtl_remaskData32Post(data, dest);

            /* Seeing if we actually need to do another pulse */
            if (data == 0xFFFFFFFF)
                return true;

            FlashCtl_setProgramVerification(FLASH_REGPRE | FLASH_REGPOST);
            continue;
        }

        /* If we got this far, return true */
        return true;

    }

    return false;

}

static bool _FlashCtl_ProgramBurst(uint32_t src, uint32_t dest, uint32_t length,
        uint32_t mTries)
{
    uint32_t bCalc, otpOffset, ii, jj;
    bool res;

    /* Setting verification */
    FlashCtl_clearProgramVerification(FLASH_REGPRE | FLASH_REGPOST);
    FlashCtl_setProgramVerification(FLASH_BURSTPOST | FLASH_BURSTPRE);

    /* Assume Failure */
    res = false;

    /* Waiting for idle status */
    while ((FLCTL->PRGBRST_CTLSTAT & FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_MASK)
            != FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_0)
    {
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_CLR_STAT_OFS) = 1;
    }

    /* Setting/clearing INFO flash flags as appropriate */
    if (dest > __MAIN_MEMORY_END__)
    {
        FLCTL->PRGBRST_CTLSTAT = (FLCTL->PRGBRST_CTLSTAT
                & ~FLCTL_PRGBRST_CTLSTAT_TYPE_MASK) | FLCTL_PRGBRST_CTLSTAT_TYPE_1;
        otpOffset = __INFO_FLASH_TECH_START__;
    } else
    {
        FLCTL->PRGBRST_CTLSTAT = (FLCTL->PRGBRST_CTLSTAT
                & ~FLCTL_PRGBRST_CTLSTAT_TYPE_MASK) | FLCTL_PRGBRST_CTLSTAT_TYPE_0;
        otpOffset = __MAIN_MEMORY_START__;
    }

    bCalc = 0;
    FLCTL->PRGBRST_STARTADDR = (dest - otpOffset);

    /* Initially populating the burst registers */
    while (bCalc < 16 && length != 0)
    {
        HWREG32(__getBurstProgramRegs[bCalc]) = HWREG32(src);
        bCalc++;
        length -= 4;
        src += 4;
    }

    for (ii = 0; ii < mTries; ii++)
    {
        /* Clearing Flags */
        FLCTL->CLRIFG |= (FLASH_BRSTPRGM_COMPLETE | FLASH_POSTVERIFY_FAILED
                | FLASH_PREVERIFY_FAILED);

        /* Waiting for idle status */
        while ((FLCTL->PRGBRST_CTLSTAT & FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_MASK)
                != FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_0)
        {
            BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                    FLCTL_PRGBRST_CTLSTAT_CLR_STAT_OFS) = 1;
        }

        /* Start the burst program */
        FLCTL->PRGBRST_CTLSTAT = (FLCTL->PRGBRST_CTLSTAT
                & ~(FLCTL_PRGBRST_CTLSTAT_LEN_MASK))
                | ((bCalc / 4) << FLASH_BURST_PRG_BIT)
                | FLCTL_PRGBRST_CTLSTAT_START;

        /* Waiting for the burst to complete */
        while ((FLCTL->PRGBRST_CTLSTAT &
        FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_MASK)
                != FLASH_PRGBRSTCTLSTAT_BURSTSTATUS_COMPLETE)
        {
            __no_operation();
        }

        /* Checking for errors and clearing/masking */

        /* Address Error */
        if (BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_ADDR_ERR_OFS))
        {
            goto BurstCleanUp;
        }

        /* Pre-Verify Error */
        if (BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_AUTO_PRE_OFS) && BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                        FLCTL_PRGBRST_CTLSTAT_PRE_ERR_OFS))
        {
            __FlashCtl_remaskBurstDataPre(dest, bCalc * 4);

            for (jj = 0; jj < bCalc; jj++)
            {
                if (HWREG32(__getBurstProgramRegs[jj])
                        != 0xFFFFFFFF)
                {
                    FlashCtl_clearProgramVerification(FLASH_BURSTPRE);
                    break;
                }
            }

            if (jj != bCalc)
                continue;
        }

        /* Post-Verify Error */
        if (BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_PST_ERR_OFS))
        {
            __FlashCtl_remaskBurstDataPost(dest, bCalc * 4);

            for (jj = 0; jj < bCalc; jj++)
            {
                if ((HWREG32(__getBurstProgramRegs[jj]))
                        != 0xFFFFFFFF)
                {
                    FlashCtl_setProgramVerification(
                            FLASH_BURSTPOST | FLASH_BURSTPRE);
                    break;
                }
            }

            if (jj != bCalc)
                continue;

        }

        /* If we got this far, the program happened */
        res = true;
        goto BurstCleanUp;
    }

    BurstCleanUp:
    /* Waiting for idle status */
    while ((FLCTL->PRGBRST_CTLSTAT & FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_MASK)
            != FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_0)
    {
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_CLR_STAT_OFS) = 1;
    }
    return res;
}

void FlashCtl_enableReadBuffering(uint_fast8_t memoryBank,
        uint_fast8_t accessMethod)
{
    if (memoryBank == FLASH_BANK0 && accessMethod == FLASH_DATA_READ)
        BITBAND_PERI(FLCTL->BANK0_RDCTL, FLCTL_BANK0_RDCTL_BUFD_OFS) = 1;
    else if (memoryBank == FLASH_BANK1 && accessMethod == FLASH_DATA_READ)
        BITBAND_PERI(FLCTL->BANK1_RDCTL, FLCTL_BANK1_RDCTL_BUFD_OFS) = 1;
    else if (memoryBank == FLASH_BANK0
            && accessMethod == FLASH_INSTRUCTION_FETCH)
        BITBAND_PERI(FLCTL->BANK0_RDCTL, FLCTL_BANK0_RDCTL_BUFI_OFS) = 1;
    else if (memoryBank == FLASH_BANK1
            && accessMethod == FLASH_INSTRUCTION_FETCH)
        BITBAND_PERI(FLCTL->BANK1_RDCTL, FLCTL_BANK1_RDCTL_BUFI_OFS) = 1;
    else
        ASSERT(false);
}

void FlashCtl_disableReadBuffering(uint_fast8_t memoryBank,
        uint_fast8_t accessMethod)
{
    if (memoryBank == FLASH_BANK0 && accessMethod == FLASH_DATA_READ)
        BITBAND_PERI(FLCTL->BANK0_RDCTL, FLCTL_BANK0_RDCTL_BUFD_OFS) = 0;
    else if (memoryBank == FLASH_BANK1 && accessMethod == FLASH_DATA_READ)
        BITBAND_PERI(FLCTL->BANK1_RDCTL, FLCTL_BANK1_RDCTL_BUFD_OFS) = 0;
    else if (memoryBank == FLASH_BANK0
            && accessMethod == FLASH_INSTRUCTION_FETCH)
        BITBAND_PERI(FLCTL->BANK0_RDCTL, FLCTL_BANK0_RDCTL_BUFI_OFS) = 0;
    else if (memoryBank == FLASH_BANK1
            && accessMethod == FLASH_INSTRUCTION_FETCH)
        BITBAND_PERI(FLCTL->BANK1_RDCTL, FLCTL_BANK1_RDCTL_BUFI_OFS) = 0;
    else
        ASSERT(false);
}

bool FlashCtl_unprotectSector(uint_fast8_t memorySpace, uint32_t sectorMask)
{
    switch (memorySpace)
    {
    case FLASH_MAIN_MEMORY_SPACE_BANK0:
        FLCTL->BANK0_MAIN_WEPROT &= ~sectorMask;
        break;
    case FLASH_MAIN_MEMORY_SPACE_BANK1:
        FLCTL->BANK1_MAIN_WEPROT &= ~sectorMask;
        break;
    case FLASH_INFO_MEMORY_SPACE_BANK0:
        ASSERT(sectorMask <= 0x04);
        FLCTL->BANK0_INFO_WEPROT &= ~sectorMask;
        break;
    case FLASH_INFO_MEMORY_SPACE_BANK1:
        ASSERT(sectorMask <= 0x04);
        FLCTL->BANK1_INFO_WEPROT &= ~sectorMask;
        break;

    default:
        ASSERT(false);

    }

    return !FlashCtl_isSectorProtected(memorySpace, sectorMask);
}

bool FlashCtl_protectSector(uint_fast8_t memorySpace, uint32_t sectorMask)
{
    switch (memorySpace)
    {
    case FLASH_MAIN_MEMORY_SPACE_BANK0:
        FLCTL->BANK0_MAIN_WEPROT |= sectorMask;
        break;
    case FLASH_MAIN_MEMORY_SPACE_BANK1:
        FLCTL->BANK1_MAIN_WEPROT |= sectorMask;
        break;
    case FLASH_INFO_MEMORY_SPACE_BANK0:
        ASSERT(sectorMask <= 0x04);
        FLCTL->BANK0_INFO_WEPROT |= sectorMask;
        break;
    case FLASH_INFO_MEMORY_SPACE_BANK1:
        ASSERT(sectorMask <= 0x04);
        FLCTL->BANK1_INFO_WEPROT |= sectorMask;
        break;

    default:
        ASSERT(false);

    }

    return FlashCtl_isSectorProtected(memorySpace, sectorMask);
}

bool FlashCtl_isSectorProtected(uint_fast8_t memorySpace, uint32_t sector)
{
    switch (memorySpace)
    {
    case FLASH_MAIN_MEMORY_SPACE_BANK0:
        return FLCTL->BANK0_MAIN_WEPROT & sector;
    case FLASH_MAIN_MEMORY_SPACE_BANK1:
        return FLCTL->BANK1_MAIN_WEPROT & sector;
    case FLASH_INFO_MEMORY_SPACE_BANK0:
        ASSERT(sector <= 0x04);
        return FLCTL->BANK0_INFO_WEPROT & sector;
    case FLASH_INFO_MEMORY_SPACE_BANK1:
        ASSERT(sector <= 0x04);
        return FLCTL->BANK1_INFO_WEPROT & sector;
    default:
        return false;
    }
}

bool FlashCtl_verifyMemory(void* verifyAddr, uint32_t length,
        uint_fast8_t pattern)
{
    uint32_t memoryPattern, addr, otpOffset;
    uint32_t b0WaitState, b1WaitState, intStatus;
    uint32_t bankOneStart, startBank, endBank;
    uint_fast8_t b0readMode, b1readMode;
    uint_fast8_t memoryType;
    bool res;

    ASSERT(pattern == FLASH_0_PATTERN || pattern == FLASH_1_PATTERN);

    /* Saving interrupt context and disabling interrupts for program
     * operation
     */
    intStatus = CPU_primask();
    Interrupt_disableMaster();

    /* Casting and determining the memory that we need to use */
    addr = (uint32_t) verifyAddr;
    memoryType =
            (addr > __MAIN_MEMORY_END__) ? FLASH_INFO_SPACE : FLASH_MAIN_SPACE;

    /* Assuming Failure */
    res = false;

    /* Finding out which bank we are in */
    if(addr >  SysCtl_getFlashSize())
    {
        bankOneStart = __INFO_FLASH_TECH_MIDDLE__;
    }
    else
    {
        bankOneStart = SysCtl_getFlashSize() / 2;
    }
    startBank = addr < (bankOneStart) ? FLASH_BANK0 : FLASH_BANK1;
    endBank = (addr + length) < (bankOneStart) ? FLASH_BANK0 : FLASH_BANK1;

    /* Saving context and changing read modes */
    b0WaitState = FlashCtl_getWaitState(startBank);
    b0readMode = FlashCtl_getReadMode(startBank);

    /* Setting the wait state to account for the mode */
    FlashCtl_setWaitState(startBank, (2 * b0WaitState) + 1);

    if(startBank != endBank)
    {
        b1WaitState = FlashCtl_getWaitState(endBank);
        b1readMode = FlashCtl_getReadMode(endBank);
        FlashCtl_setWaitState(endBank, (2 * b1WaitState) + 1);
    }

    /* Changing to the relevant VERIFY mode */
    if (pattern == FLASH_1_PATTERN)
    {
        FlashCtl_setReadMode(startBank, FLASH_ERASE_VERIFY_READ_MODE);

        if(startBank != endBank)
        {
            FlashCtl_setReadMode(endBank, FLASH_ERASE_VERIFY_READ_MODE);
        }

        memoryPattern = 0xFFFFFFFF;
    } else
    {
        FlashCtl_setReadMode(startBank, FLASH_PROGRAM_VERIFY_READ_MODE);

        if(startBank != endBank)
        {
            FlashCtl_setReadMode(endBank, FLASH_PROGRAM_VERIFY_READ_MODE);
        }

        memoryPattern = 0;
    }

    /* Taking care of byte accesses */
    while ((addr & 0x03) && (length > 0))
    {
        if (HWREG8(addr++) != ((uint8_t) memoryPattern))
            goto FlashVerifyCleanup;
        length--;
    }

    /* Making sure we are aligned by 128-bit address */
    while (((addr & 0x0F)) && (length > 3))
    {
        if (HWREG32(addr) != memoryPattern)
            goto FlashVerifyCleanup;

        addr = addr + 4;
        length = length - 4;
    }

    /* Burst Verify */
    if (length > 63)
    {
        /* Setting/clearing INFO flash flags as appropriate */
        if (addr > __MAIN_MEMORY_END__)
        {
            FLCTL->RDBRST_CTLSTAT = (FLCTL->RDBRST_CTLSTAT
                    & ~FLCTL_RDBRST_CTLSTAT_MEM_TYPE_MASK)
                    | FLCTL_RDBRST_CTLSTAT_MEM_TYPE_1;
            otpOffset = __INFO_FLASH_TECH_START__;
        } else
        {
            FLCTL->RDBRST_CTLSTAT = (FLCTL->RDBRST_CTLSTAT
                    & ~FLCTL_RDBRST_CTLSTAT_MEM_TYPE_MASK)
                    | FLCTL_RDBRST_CTLSTAT_MEM_TYPE_0;
            otpOffset = __MAIN_MEMORY_START__;
        }

        /* Clearing any lingering fault flags  and preparing burst verify*/
        BITBAND_PERI(FLCTL->RDBRST_CTLSTAT,
                FLCTL_RDBRST_CTLSTAT_CLR_STAT_OFS) = 1;
        FLCTL->RDBRST_FAILCNT = 0;
        FLCTL->RDBRST_STARTADDR = addr - otpOffset;
        FLCTL->RDBRST_LEN = (length & 0xFFFFFFF0);
        addr += FLCTL->RDBRST_LEN;
        length = length & 0xF;

        /* Starting Burst Verify */
        FLCTL->RDBRST_CTLSTAT = (FLCTL_RDBRST_CTLSTAT_STOP_FAIL | pattern
                | memoryType | FLCTL_RDBRST_CTLSTAT_START);

        /* While the burst read hasn't finished */
        while ((FLCTL->RDBRST_CTLSTAT & FLCTL_RDBRST_CTLSTAT_BRST_STAT_MASK)
                != FLCTL_RDBRST_CTLSTAT_BRST_STAT_3)
        {
            __no_operation();
        }

        /* Checking  for a verification/access error/failure */
        if (BITBAND_PERI(FLCTL->RDBRST_CTLSTAT,
                FLCTL_RDBRST_CTLSTAT_CMP_ERR_OFS)
                || BITBAND_PERI(FLCTL->RDBRST_CTLSTAT,
                        FLCTL_RDBRST_CTLSTAT_ADDR_ERR_OFS)
                || FLCTL->RDBRST_FAILCNT)
        {
            goto FlashVerifyCleanup;
        }
    }

    /* Remaining Words */
    while (length > 3)
    {
        if (HWREG32(addr) != memoryPattern)
            goto FlashVerifyCleanup;

        addr = addr + 4;
        length = length - 4;
    }

    /* Remaining Bytes */
    while (length > 0)
    {
        if (HWREG8(addr++) != ((uint8_t) memoryPattern))
            goto FlashVerifyCleanup;
        length--;
    }

    /* If we got this far, that means it no failure happened */
    res = true;

    FlashVerifyCleanup:

    /* Clearing the Read Burst flag and returning */
    BITBAND_PERI(FLCTL->RDBRST_CTLSTAT,
            FLCTL_RDBRST_CTLSTAT_CLR_STAT_OFS) = 1;

    FlashCtl_setReadMode(startBank, b0readMode);
    FlashCtl_setWaitState(startBank, b0WaitState);

    if(startBank != endBank)
    {
        FlashCtl_setReadMode(endBank, b1readMode);
        FlashCtl_setWaitState(endBank, b1WaitState);
    }

    if(intStatus == 0)
        Interrupt_enableMaster();

    return res;
}

bool FlashCtl_setReadMode(uint32_t flashBank, uint32_t readMode)
{

    if (FLCTL->POWER_STAT & FLCTL_POWER_STAT_RD_2T)
        return false;

    if (flashBank == FLASH_BANK0)
    {
        FLCTL->BANK0_RDCTL = (FLCTL->BANK0_RDCTL
                & ~FLCTL_BANK0_RDCTL_RD_MODE_MASK) | readMode;
        while ((FLCTL->BANK0_RDCTL & FLCTL_BANK0_RDCTL_RD_MODE_MASK)
                != readMode)
            ;
    } else if (flashBank == FLASH_BANK1)
    {
        FLCTL->BANK1_RDCTL = (FLCTL->BANK1_RDCTL
                & ~FLCTL_BANK1_RDCTL_RD_MODE_MASK) | readMode;
        while ((FLCTL->BANK1_RDCTL & FLCTL_BANK1_RDCTL_RD_MODE_MASK)
                != readMode)
            ;
    } else
    {
        ASSERT(false);
        return false;
    }

    return true;
}

uint32_t FlashCtl_getReadMode(uint32_t flashBank)
{
    if (flashBank == FLASH_BANK0)
    {
        return (FLCTL->BANK0_RDCTL & FLCTL_BANK0_RDCTL_RD_MODE_MASK);
    } else if (flashBank == FLASH_BANK1)
    {
        return (FLCTL->BANK1_RDCTL & FLCTL_BANK1_RDCTL_RD_MODE_MASK);
    } else
    {
        ASSERT(false);
        return 0;
    }
}

void FlashCtl_initiateMassErase(void)
{
    /* Clearing old mass erase flags */
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_CLR_STAT_OFS) = 1;

    /* Performing the mass erase */
    FLCTL->ERASE_CTLSTAT |= (FLCTL_ERASE_CTLSTAT_MODE
            | FLCTL_ERASE_CTLSTAT_START);
}

bool FlashCtl_performMassErase(void)
{
    uint32_t userFlash, ii, sector, intStatus;
    bool res;

    /* Saving interrupt context and disabling interrupts for program
     * operation
     */
    intStatus = CPU_primask();
    Interrupt_disableMaster();

    /* Assume Failure */
    res = false;

    /* Clearing old mass erase flags */
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_CLR_STAT_OFS) = 1;

    /* Performing the mass erase */
    FLCTL->ERASE_CTLSTAT |= (FLCTL_ERASE_CTLSTAT_MODE
            | FLCTL_ERASE_CTLSTAT_START);

    while ((FLCTL->ERASE_CTLSTAT & FLCTL_ERASE_CTLSTAT_STATUS_MASK)
            == FLCTL_ERASE_CTLSTAT_STATUS_1
            || (FLCTL->ERASE_CTLSTAT & FLCTL_ERASE_CTLSTAT_STATUS_MASK)
                    == FLCTL_ERASE_CTLSTAT_STATUS_2)
    {
        __no_operation();
    }

    /* Return false if an address error */
    if (BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_ADDR_ERR_OFS))
        goto MassEraseCleanup;

    /* Changing to erase verify */
    userFlash = SysCtl_getFlashSize() / 2;

    for (ii = __MAIN_MEMORY_START__; ii < userFlash; ii += 4096)
    {
        sector = getUserFlashSector(ii);

        if (!((FLCTL->BANK0_MAIN_WEPROT) & sector))
        {
            if (!FlashCtl_verifyMemory((void*) ii, 4096, FLASH_1_PATTERN))
            {
                if (!FlashCtl_eraseSector(ii))
                    goto MassEraseCleanup;
            }
        }

        if (!(FLCTL->BANK1_MAIN_WEPROT & sector))
        {
            if (!FlashCtl_verifyMemory((void*) (ii + userFlash), 4096,
            FLASH_1_PATTERN))
            {
                if (!FlashCtl_eraseSector(ii + userFlash))
                    goto MassEraseCleanup;
            }
        }

        if (sector < FLCTL_BANK0_MAIN_WEPROT_PROT2)
        {
            if (!(FLCTL->BANK0_INFO_WEPROT & sector))
            {
                if (!FlashCtl_verifyMemory(
                        (void*) (ii + __INFO_FLASH_TECH_START__), 4096,
                        FLASH_1_PATTERN))
                {
                    if (!FlashCtl_eraseSector(ii + __INFO_FLASH_TECH_START__))
                        goto MassEraseCleanup;
                }
            }

            if (!(FLCTL->BANK1_INFO_WEPROT & sector))
            {
                if (!FlashCtl_verifyMemory((void*) (ii + (0x202000)), 4096,
                FLASH_1_PATTERN))
                {
                    if (!FlashCtl_eraseSector(ii + (0x202000)))
                        goto MassEraseCleanup;
                }
            }

        }
    }

    /* If we got this far, the mass erase happened */
    res = true;

    MassEraseCleanup:
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_CLR_STAT_OFS) = 1;

    if(intStatus == 0)
        Interrupt_enableMaster();

    return res;
}

bool FlashCtl_eraseSector(uint32_t addr)
{
    uint_fast8_t memoryType, ii;
    uint32_t otpOffset = 0;
    uint32_t intStatus;
    uint_fast8_t mTries, tlvLength;
    SysCtl_FlashTLV_Info *flInfo;
    bool res;

    /* Saving interrupt context and disabling interrupts for program
     * operation
     */
    intStatus = CPU_primask();
    Interrupt_disableMaster();
    
    /* Assuming Failure */
    res = false;

    memoryType =
            addr > __MAIN_MEMORY_END__ ? FLASH_INFO_SPACE : FLASH_MAIN_SPACE;

    /* Parsing the TLV and getting the maximum erase pulses */
    SysCtl_getTLVInfo(TLV_TAG_FLASHCTL, 0, &tlvLength, (uint32_t**) &flInfo);

    if (tlvLength == 0 || flInfo->maxErasePulses == 0)
    {
        mTries = MAX_ERASE_NO_TLV;
    } else
    {
        mTries = flInfo->maxErasePulses;
    }

    /* We can only erase on 4KB boundaries */
    while (addr & 0xFFF)
    {
        addr--;
    }

    /* Clearing the status */
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_CLR_STAT_OFS) = 1;

    if (memoryType == FLASH_INFO_SPACE)
    {
        otpOffset = __INFO_FLASH_TECH_START__;
        FLCTL->ERASE_CTLSTAT = (FLCTL->ERASE_CTLSTAT
                & ~(FLCTL_ERASE_CTLSTAT_TYPE_MASK)) | FLCTL_ERASE_CTLSTAT_TYPE_1;

    } else
    {
        otpOffset = __MAIN_MEMORY_START__;
        FLCTL->ERASE_CTLSTAT = (FLCTL->ERASE_CTLSTAT
                & ~(FLCTL_ERASE_CTLSTAT_TYPE_MASK)) | FLCTL_ERASE_CTLSTAT_TYPE_0;
    }

    /* Clearing old flags  and setting up the erase */
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_MODE_OFS) = 0;
    FLCTL->ERASE_SECTADDR = addr - otpOffset;

    for (ii = 0; ii < mTries; ii++)
    {
        /* Clearing the status */
        BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_CLR_STAT_OFS) =
                1;

        /* Starting the erase */
        BITBAND_PERI(FLCTL->ERASE_CTLSTAT,
                FLCTL_ERASE_CTLSTAT_START_OFS) = 1;

        while ((FLCTL->ERASE_CTLSTAT & FLCTL_ERASE_CTLSTAT_STATUS_MASK)
                == FLCTL_ERASE_CTLSTAT_STATUS_1
                || (FLCTL->ERASE_CTLSTAT & FLCTL_ERASE_CTLSTAT_STATUS_MASK)
                        == FLCTL_ERASE_CTLSTAT_STATUS_2)
        {
            __no_operation();
        }

        /* Return false if an address error */
        if (BITBAND_PERI(FLCTL->ERASE_CTLSTAT,
                FLCTL_ERASE_CTLSTAT_ADDR_ERR_OFS))
        {
            goto SectorEraseCleanup;
        }
        /* Erase verifying */
        if (FlashCtl_verifyMemory((void*) addr, 4096, FLASH_1_PATTERN))
        {
            res = true;
            goto SectorEraseCleanup;
        }

    }

SectorEraseCleanup:

    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_CLR_STAT_OFS) = 1;
    
    if(intStatus == 0)
        Interrupt_enableMaster();

    return res;
}

void FlashCtl_initiateSectorErase(uint32_t addr)
{
    uint_fast8_t memoryType;
    uint32_t otpOffset = 0;

    memoryType =
            addr > __MAIN_MEMORY_END__ ? FLASH_INFO_SPACE : FLASH_MAIN_SPACE;

    /* We can only erase on 4KB boundaries */
    while (addr & 0xFFF)
    {
        addr--;
    }

    /* Clearing the status */
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_CLR_STAT_OFS) = 1;

    if (memoryType == FLASH_INFO_SPACE)
    {
        otpOffset = __INFO_FLASH_TECH_START__;
        FLCTL->ERASE_CTLSTAT = (FLCTL->ERASE_CTLSTAT
                & ~(FLCTL_ERASE_CTLSTAT_TYPE_MASK)) | FLCTL_ERASE_CTLSTAT_TYPE_1;

    } else
    {
        otpOffset = __MAIN_MEMORY_START__;
        FLCTL->ERASE_CTLSTAT = (FLCTL->ERASE_CTLSTAT
                & ~(FLCTL_ERASE_CTLSTAT_TYPE_MASK)) | FLCTL_ERASE_CTLSTAT_TYPE_0;
    }

    /* Clearing old flags  and setting up the erase */
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT, FLCTL_ERASE_CTLSTAT_MODE_OFS) = 0;
    FLCTL->ERASE_SECTADDR = addr - otpOffset;

    /* Starting the erase */
    BITBAND_PERI(FLCTL->ERASE_CTLSTAT,
            FLCTL_ERASE_CTLSTAT_START_OFS) = 1;

}

bool FlashCtl_programMemory(void* src, void* dest, uint32_t length)
{
    uint32_t destAddr, srcAddr, burstLength, intStatus;
    bool res;
    uint_fast8_t mTries, tlvLength;
    SysCtl_FlashTLV_Info *flInfo;

    /* Saving interrupt context and disabling interrupts for program
     * operation
     */
    intStatus = CPU_primask();
    Interrupt_disableMaster();

    /* Parsing the TLV and getting the maximum erase pulses */
    SysCtl_getTLVInfo(TLV_TAG_FLASHCTL, 0, &tlvLength, (uint32_t**) &flInfo);

    if (tlvLength == 0 || flInfo->maxProgramPulses == 0)
    {
        mTries = MAX_PROGRAM_NO_TLV;
    } else
    {
        mTries = flInfo->maxProgramPulses;
    }

    /* Casting to integers */
    srcAddr = (uint32_t) src;
    destAddr = (uint32_t) dest;

    /* Enabling word programming */
    FlashCtl_enableWordProgramming(FLASH_IMMEDIATE_WRITE_MODE);

    /* Assume failure */
    res = false;

    /* Taking care of byte accesses */
    while ((destAddr & 0x03) && length > 0)
    {
        if (!_FlashCtl_Program8(srcAddr, destAddr, mTries))
        {
            goto FlashProgramCleanUp;
        } else
        {
            srcAddr++;
            destAddr++;
            length--;
        }
    }

    /* Taking care of word accesses */
    while ((destAddr & 0x0F) && (length > 3))
    {
        if (!_FlashCtl_Program32(srcAddr, destAddr, mTries))
        {
            goto FlashProgramCleanUp;
        } else
        {
            srcAddr += 4;
            destAddr += 4;
            length -= 4;
        }
    }

    /* Taking care of burst programs */
    while (length > 16)
    {
        burstLength = length > 63 ? 64 : length & 0xFFFFFFF0;

        if (!_FlashCtl_ProgramBurst(srcAddr, destAddr, burstLength, mTries))
        {
            goto FlashProgramCleanUp;
        } else
        {
            srcAddr += burstLength;
            destAddr += burstLength;
            length -= burstLength;
        }
    }

    /* Remaining word accesses */
    while (length > 3)
    {
        if (!_FlashCtl_Program32(srcAddr, destAddr, mTries))
        {
            goto FlashProgramCleanUp;
        } else
        {
            srcAddr+=4;
            destAddr+=4;
            length-=4;
        }
    }

    /* Remaining byte accesses */
    while (length > 0)
    {
        if (!_FlashCtl_Program8(srcAddr, destAddr, mTries))
        {
            goto FlashProgramCleanUp;
        } else
        {
            srcAddr++;
            destAddr++;
            length--;
        }
    }

    /* If we got this far that means that we succeeded  */
    res = true;

    FlashProgramCleanUp:

    if(intStatus == 0)
        Interrupt_enableMaster();

    FlashCtl_disableWordProgramming();
    return res;

}
void FlashCtl_setProgramVerification(uint32_t verificationSetting)
{
    if ((verificationSetting & FLASH_BURSTPOST))
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_AUTO_PST_OFS) = 1;

    if ((verificationSetting & FLASH_BURSTPRE))
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_AUTO_PRE_OFS) = 1;

    if ((verificationSetting & FLASH_REGPRE))
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_VER_PRE_OFS) = 1;

    if ((verificationSetting & FLASH_REGPOST))
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_VER_PST_OFS) = 1;
}

void FlashCtl_clearProgramVerification(uint32_t verificationSetting)
{
    if ((verificationSetting & FLASH_BURSTPOST))
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_AUTO_PST_OFS) = 0;

    if ((verificationSetting & FLASH_BURSTPRE))
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_AUTO_PRE_OFS) = 0;

    if ((verificationSetting & FLASH_REGPRE))
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_VER_PRE_OFS) = 0;

    if ((verificationSetting & FLASH_REGPOST))
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_VER_PST_OFS) = 0;

}

void FlashCtl_enableWordProgramming(uint32_t mode)
{
    if (mode == FLASH_IMMEDIATE_WRITE_MODE)
    {
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_ENABLE_OFS) = 1;
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_MODE_OFS) = 0;

    } else if (mode == FLASH_COLLATED_WRITE_MODE)
    {
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_ENABLE_OFS) = 1;
        BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_MODE_OFS) = 1;
    }
}

void FlashCtl_disableWordProgramming(void)
{
    BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_ENABLE_OFS) = 0;
}

uint32_t FlashCtl_isWordProgrammingEnabled(void)
{
    if (!BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_ENABLE_OFS))
    {
        return 0;
    } else if (BITBAND_PERI(FLCTL->PRG_CTLSTAT, FLCTL_PRG_CTLSTAT_MODE_OFS))
        return FLASH_COLLATED_WRITE_MODE;
    else
        return FLASH_IMMEDIATE_WRITE_MODE;
}

void FlashCtl_setWaitState(uint32_t flashBank, uint32_t waitState)
{
    if (flashBank == FLASH_BANK0)
    {
        FLCTL->BANK0_RDCTL = (FLCTL->BANK0_RDCTL
                & ~FLCTL_BANK0_RDCTL_WAIT_MASK) | (waitState << FLCTL_BANK0_RDCTL_WAIT_OFS);
    } else if (flashBank == FLASH_BANK1)
    {
        FLCTL->BANK1_RDCTL = (FLCTL->BANK1_RDCTL
                & ~FLCTL_BANK1_RDCTL_WAIT_MASK) | (waitState << FLCTL_BANK1_RDCTL_WAIT_OFS);
    } else
    {
        ASSERT(false);
    }
}

uint32_t FlashCtl_getWaitState(uint32_t flashBank)
{
    if (flashBank == FLASH_BANK0)
    {
        return (FLCTL->BANK0_RDCTL & FLCTL_BANK0_RDCTL_WAIT_MASK) >> FLCTL_BANK0_RDCTL_WAIT_OFS;
    } else if (flashBank == FLASH_BANK1)
    {
        return (FLCTL->BANK1_RDCTL & FLCTL_BANK1_RDCTL_WAIT_MASK) >> FLCTL_BANK1_RDCTL_WAIT_OFS;
    } else
    {
        ASSERT(false);
        return 0;
    }
}

void FlashCtl_enableInterrupt(uint32_t flags)
{
    FLCTL->IE |= flags;
}

void FlashCtl_disableInterrupt(uint32_t flags)
{
    FLCTL->IE &= ~flags;
}

uint32_t FlashCtl_getInterruptStatus(void)
{
    return FLCTL->IFG;
}

uint32_t FlashCtl_getEnabledInterruptStatus(void)
{
    return FlashCtl_getInterruptStatus() & FLCTL->IE;
}

void FlashCtl_clearInterruptFlag(uint32_t flags)
{
    FLCTL->CLRIFG |= flags;
}

void FlashCtl_registerInterrupt(void (*intHandler)(void))
{
    //
    // Register the interrupt handler, returning an error if an error occurs.
    //
    Interrupt_registerInterrupt(INT_FLCTL, intHandler);

    //
    // Enable the system control interrupt.
    //
    Interrupt_enableInterrupt(INT_FLCTL);
}

void FlashCtl_unregisterInterrupt(void)
{
    //
    // Disable the interrupt.
    //
    Interrupt_disableInterrupt(INT_FLCTL);

    //
    // Unregister the interrupt handler.
    //
    Interrupt_unregisterInterrupt(INT_FLCTL);
}

uint8_t __FlashCtl_remaskData8Post(uint8_t data, uint32_t addr)
{
    uint32_t readMode, waitState, bankProgram, bankOneStart;

    /* Changing the waitstate and read mode of whichever bank we are in */
    /* Finding out which bank we are in */
    if(addr >  SysCtl_getFlashSize())
    {
        bankOneStart = __INFO_FLASH_TECH_MIDDLE__;
    }
    else
    {
        bankOneStart = SysCtl_getFlashSize() / 2;
    }

    bankProgram =
            addr < (bankOneStart) ? FLASH_BANK0 : FLASH_BANK1;

    /* Saving the current wait states and read mode */
    waitState = FlashCtl_getWaitState(bankProgram);
    readMode = FlashCtl_getReadMode(bankProgram);

    /* Setting the wait state to account for the mode */
    FlashCtl_setWaitState(bankProgram, (2 * waitState) + 1);

    /* Changing to PROGRAM VERIFY mode */
    FlashCtl_setReadMode(bankProgram, FLASH_PROGRAM_VERIFY_READ_MODE);

    data = ~(~(data) & HWREG8(addr));

    /* Setting the wait state to account for the mode */
    FlashCtl_setReadMode(bankProgram, readMode);
    FlashCtl_setWaitState(bankProgram, waitState);

    return data;
}

uint8_t __FlashCtl_remaskData8Pre(uint8_t data, uint32_t addr)
{
    uint32_t readMode, waitState, bankProgram, bankOneStart;

    /* Changing the waitstate and read mode of whichever bank we are in */
    /* Finding out which bank we are in */
    if(addr >  SysCtl_getFlashSize())
    {
        bankOneStart = __INFO_FLASH_TECH_MIDDLE__;
    }
    else
    {
        bankOneStart = SysCtl_getFlashSize() / 2;
    }

    bankProgram =
            addr < (bankOneStart) ? FLASH_BANK0 : FLASH_BANK1;

    /* Saving the current wait states and read mode */
    waitState = FlashCtl_getWaitState(bankProgram);
    readMode = FlashCtl_getReadMode(bankProgram);

    /* Setting the wait state to account for the mode */
    FlashCtl_setWaitState(bankProgram, (2 * waitState) + 1);

    /* Changing to PROGRAM VERIFY mode */
    FlashCtl_setReadMode(bankProgram, FLASH_PROGRAM_VERIFY_READ_MODE);

    data |= ~(HWREG8(addr) | data);

    /* Setting the wait state to account for the mode */
    FlashCtl_setReadMode(bankProgram, readMode);
    FlashCtl_setWaitState(bankProgram, waitState);

    return data;
}

uint32_t __FlashCtl_remaskData32Post(uint32_t data, uint32_t addr)
{
    uint32_t bankProgramStart, bankProgramEnd, bank1Start;
    uint32_t b0WaitState, b0ReadMode, b1WaitState, b1ReadMode;

    /* Changing the waitstate and read mode of whichever bank we are in */
    /* Finding out which bank we are in */
    if(addr >  SysCtl_getFlashSize())
    {
        bank1Start = __INFO_FLASH_TECH_MIDDLE__;
    }
    else
    {
        bank1Start = SysCtl_getFlashSize() / 2;
    }

    bankProgramStart = addr < bank1Start ? FLASH_BANK0 : FLASH_BANK1;
    bankProgramEnd = (addr + 4) < bank1Start ? FLASH_BANK0 : FLASH_BANK1;

    /* Saving the current wait states and read mode */
    b0WaitState = FlashCtl_getWaitState(bankProgramStart);
    b0ReadMode = FlashCtl_getReadMode(bankProgramStart);
    FlashCtl_setWaitState(bankProgramStart, (2 * b0WaitState) + 1);
    FlashCtl_setReadMode(bankProgramStart, FLASH_PROGRAM_VERIFY_READ_MODE);

    if (bankProgramStart != bankProgramEnd)
    {
        b1WaitState = FlashCtl_getWaitState(bankProgramEnd);
        b1ReadMode = FlashCtl_getReadMode(bankProgramEnd);
        FlashCtl_setWaitState(bankProgramEnd, (2 * b1WaitState) + 1);
        FlashCtl_setReadMode(bankProgramEnd, FLASH_PROGRAM_VERIFY_READ_MODE);
    }

    data = ~(~(data) & HWREG32(addr));

    /* Setting the wait state to account for the mode */
    FlashCtl_setReadMode(bankProgramStart, b0ReadMode);
    FlashCtl_setWaitState(bankProgramStart, b0WaitState);

    if (bankProgramStart != bankProgramEnd)
    {
        FlashCtl_setReadMode(bankProgramEnd, b1ReadMode);
        FlashCtl_setWaitState(bankProgramEnd, b1WaitState);
    }

    return data;
}

uint32_t __FlashCtl_remaskData32Pre(uint32_t data, uint32_t addr)
{
    uint32_t bankProgramStart, bankProgramEnd, bank1Start;
    uint32_t b0WaitState, b0ReadMode, b1WaitState, b1ReadMode;

    /* Changing the waitstate and read mode of whichever bank we are in */
    /* Finding out which bank we are in */
    if(addr >  SysCtl_getFlashSize())
    {
        bank1Start = __INFO_FLASH_TECH_MIDDLE__;
    }
    else
    {
        bank1Start = SysCtl_getFlashSize() / 2;
    }

    bankProgramStart = addr < bank1Start ? FLASH_BANK0 : FLASH_BANK1;
    bankProgramEnd = (addr + 4) < bank1Start ? FLASH_BANK0 : FLASH_BANK1;

    /* Saving the current wait states and read mode */
    b0WaitState = FlashCtl_getWaitState(bankProgramStart);
    b0ReadMode = FlashCtl_getReadMode(bankProgramStart);
    FlashCtl_setWaitState(bankProgramStart, (2 * b0WaitState) + 1);
    FlashCtl_setReadMode(bankProgramStart, FLASH_PROGRAM_VERIFY_READ_MODE);

    if (bankProgramStart != bankProgramEnd)
    {
        b1WaitState = FlashCtl_getWaitState(bankProgramEnd);
        b1ReadMode = FlashCtl_getReadMode(bankProgramEnd);
        FlashCtl_setWaitState(bankProgramEnd, (2 * b1WaitState) + 1);
        FlashCtl_setReadMode(bankProgramEnd, FLASH_PROGRAM_VERIFY_READ_MODE);
    }

    data |= ~(HWREG32(addr) | data);

    /* Setting the wait state to account for the mode */
    FlashCtl_setReadMode(bankProgramStart, b0ReadMode);
    FlashCtl_setWaitState(bankProgramStart, b0WaitState);

    if (bankProgramStart != bankProgramEnd)
    {
        FlashCtl_setReadMode(bankProgramEnd, b1ReadMode);
        FlashCtl_setWaitState(bankProgramEnd, b1WaitState);
    }

    return data;
}

void __FlashCtl_remaskBurstDataPre(uint32_t addr, uint32_t size)
{

    uint32_t bankProgramStart, bankProgramEnd, bank1Start, ii;
    uint32_t b0WaitState, b0ReadMode, b1WaitState, b1ReadMode;

    /* Waiting for idle status */
    while ((FLCTL->PRGBRST_CTLSTAT & FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_MASK)
            != FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_0)
    {
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_CLR_STAT_OFS) = 1;
    }

    /* Changing the waitstate and read mode of whichever bank we are in */
    /* Finding out which bank we are in */
    if(addr >  SysCtl_getFlashSize())
    {
        bank1Start = __INFO_FLASH_TECH_MIDDLE__;
    }
    else
    {
        bank1Start = SysCtl_getFlashSize() / 2;
    }

    bankProgramStart = addr < bank1Start ? FLASH_BANK0 : FLASH_BANK1;
    bankProgramEnd = (addr + size) < bank1Start ? FLASH_BANK0 : FLASH_BANK1;

    /* Saving the current wait states and read mode */
    b0WaitState = FlashCtl_getWaitState(bankProgramStart);
    b0ReadMode = FlashCtl_getReadMode(bankProgramStart);
    FlashCtl_setWaitState(bankProgramStart, (2 * b0WaitState) + 1);
    FlashCtl_setReadMode(bankProgramStart, FLASH_PROGRAM_VERIFY_READ_MODE);

    if (bankProgramStart != bankProgramEnd)
    {
        b1WaitState = FlashCtl_getWaitState(bankProgramEnd);
        b1ReadMode = FlashCtl_getReadMode(bankProgramEnd);
        FlashCtl_setWaitState(bankProgramEnd, (2 * b1WaitState) + 1);
        FlashCtl_setReadMode(bankProgramEnd, FLASH_PROGRAM_VERIFY_READ_MODE);
    }

    /* Going through each BURST program register and masking out for pre
     * verifcation
     */
    size = (size / 4);
    for (ii = 0; ii < size; ii++)
    {
        HWREG32(__getBurstProgramRegs[ii]) |=
                ~(HWREG32(__getBurstProgramRegs[ii])
                        | HWREG32(addr));
        addr += 4;
    }

    /* Setting the wait state to account for the mode */
    FlashCtl_setReadMode(bankProgramStart, b0ReadMode);
    FlashCtl_setWaitState(bankProgramStart, b0WaitState);

    if (bankProgramStart != bankProgramEnd)
    {
        FlashCtl_setReadMode(bankProgramEnd, b1ReadMode);
        FlashCtl_setWaitState(bankProgramEnd, b1WaitState);
    }

}
void __FlashCtl_remaskBurstDataPost(uint32_t addr, uint32_t size)
{
    uint32_t bankProgramStart, bankProgramEnd, bank1Start, ii;
    uint32_t b0WaitState, b0ReadMode, b1WaitState, b1ReadMode;

    /* Waiting for idle status */
    while ((FLCTL->PRGBRST_CTLSTAT & FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_MASK)
            != FLCTL_PRGBRST_CTLSTAT_BURST_STATUS_0)
    {
        BITBAND_PERI(FLCTL->PRGBRST_CTLSTAT,
                FLCTL_PRGBRST_CTLSTAT_CLR_STAT_OFS) = 1;
    }

    /* Changing the waitstate and read mode of whichever bank we are in */
    /* Finding out which bank we are in */
    if(addr >  SysCtl_getFlashSize())
    {
        bank1Start = __INFO_FLASH_TECH_MIDDLE__;
    }
    else
    {
        bank1Start = SysCtl_getFlashSize() / 2;
    }

    bankProgramStart = addr < bank1Start ? FLASH_BANK0 : FLASH_BANK1;
    bankProgramEnd = (addr + size) < bank1Start ? FLASH_BANK0 : FLASH_BANK1;

    /* Saving the current wait states and read mode */
    b0WaitState = FlashCtl_getWaitState(bankProgramStart);
    b0ReadMode = FlashCtl_getReadMode(bankProgramStart);
    FlashCtl_setWaitState(bankProgramStart, (2 * b0WaitState) + 1);
    FlashCtl_setReadMode(bankProgramStart, FLASH_PROGRAM_VERIFY_READ_MODE);

    if (bankProgramStart != bankProgramEnd)
    {
        b1WaitState = FlashCtl_getWaitState(bankProgramEnd);
        b1ReadMode = FlashCtl_getReadMode(bankProgramEnd);
        FlashCtl_setWaitState(bankProgramEnd, (2 * b1WaitState) + 1);
        FlashCtl_setReadMode(bankProgramEnd, FLASH_PROGRAM_VERIFY_READ_MODE);
    }

    /* Going through each BURST program register and masking out for post
     * verifcation if needed
     */
    size = (size / 4);
    for (ii = 0; ii < size; ii++)
    {
        HWREG32(__getBurstProgramRegs[ii]) = ~(~(HWREG32(
                __getBurstProgramRegs[ii])) & HWREG32(addr));

        addr += 4;
    }

    /* Setting the wait state to account for the mode */
    FlashCtl_setReadMode(bankProgramStart, b0ReadMode);
    FlashCtl_setWaitState(bankProgramStart, b0WaitState);

    if (bankProgramStart != bankProgramEnd)
    {
        FlashCtl_setReadMode(bankProgramEnd, b1ReadMode);
        FlashCtl_setWaitState(bankProgramEnd, b1WaitState);
    }
}
