/**************************************************************************//**
 * @file     FlashPrg.c
 * @brief    Flash Programming Functions adapted for RA8P1
 * @version  V1.0.0
 * @date     31 Oct 2024
 ******************************************************************************/
/*
 * Copyright (c) 2010-2018 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
 /**********************************************************************************************************************
 * DISCLAIMER
 * This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No
 * other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all
 * applicable laws, including copyright laws.
 * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
 * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM
 * EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES
 * SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO
 * THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of
 * this software. By using this software, you agree to the additional terms and conditions found by accessing the
 * following link:
 * http://www.renesas.com/disclaimer
 *
 * Copyright (C) 2024 Renesas Electronics Corporation. All rights reserved.
 *********************************************************************************************************************/
 /**********************************************************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 31.10.2024 1.0.0    First Release
 *********************************************************************************************************************/
 
 /*
    FlashOS Structures
 */
#include "../FlashOS.h"

/* 
   Mandatory Flash Programming Functions (Called by FlashOS):
                int Init        (unsigned long adr,   // Initialize Flash
                                 unsigned long clk,
                                 unsigned long fnc);
                int UnInit      (unsigned long fnc);  // De-initialize Flash
                int EraseSector (unsigned long adr);  // Erase Sector Function
                int ProgramPage (unsigned long adr,   // Program Page Function
                                 unsigned long sz,
                                 unsigned char *buf);

   Optional  Flash Programming Functions (Called by FlashOS):
                int BlankCheck  (unsigned long adr,   // Blank Check
                                 unsigned long sz,
                                 unsigned char pat);
                int EraseChip   (void);               // Erase complete Device
       unsigned long Verify     (unsigned long adr,   // Verify Function
                                 unsigned long sz,
                                 unsigned char *buf);

       - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space
       - Verify       is necessary if Flash space is not mapped into CPU memory space
       - if EraseChip is not provided than EraseSector for all sectors is called
*/

/*
   Definition to access a specific address with any size
*/
#define M8(adr)  (*((volatile unsigned char  *) (adr)))
#define M16(adr) (*((volatile unsigned short *) (adr)))
#define M32(adr) (*((volatile unsigned long  *) (adr)))
/*
   Clock related registers adrress and bit definition
*/
#define MCU_SYSTEM_BASE									(0x4001E000)
#define MCU_SCKDIVCR_ADDR               (MCU_SYSTEM_BASE+0x20)
    #define SCKDIVCR_MRPCK_1DEV_MASK    (0x0FFFFFFF)
    #define SCKDIVCR_ICLK_1DEV_MASK     (0xF0FFFFFF)
		#define SCKDIVCR_MRPCK_MASK         (0xF0000000)
    #define SCKDIVCR_ICK_MASK           (0x0F000000)
#define MCU_SCKDIVCR2_ADDR              (MCU_SYSTEM_BASE+0x20)
    #define SCKDIVCR2_MRICK_1DEV_MASK   (0x0FFF)
		#define SCKDIVCR2_MRICK_MASK        (0xF000)
#define MCU_SCKSCR_ADDR									(MCU_SYSTEM_BASE + 0x026)
		#define SCKSCR_CKSEL_HOCO	          (0x00)
		#define SCKSCR_CKSEL_MOCO	          (0x01)
		#define SCKSCR_CKSEL_MOSC	          (0x03)
		#define SCKSCR_CKSEL_SOSC	          (0x04)
		#define SCKSCR_CKSEL_PLL1P          (0x05) 
		#define SCKSCR_CKSEL_MASK	          (0x07)
#define MCU_PLLCCR_ADDR                 (MCU_SYSTEM_BASE+0xAC)
    #define PLLCCR_MUL                  (0x0001FF00)
    #define PLLCCR_PLSRCSEL_HOCO        (0x00000010)
    #define PLLCCR_PLSRCSEL_MAIN        (0x00000000)
    #define PLLCCR_PLSRCSEL_MASK        (0x00000010)
    #define PLLCCR_PLIDIV_MASK          (0x00000003)
    #define PLLCCR_PLIDIV_1DEV          (0x00000000)
    #define PLLCCR_PLIDIV_2DEV          (0x00000001)
    #define PLLCCR_PLIDIV_3DEV          (0x00000002)
		
		#define PLLCCR_PLLMULNF_MASK       	(0x000000C0)
		#define PLLCCR_PLLMULNF_0ADD_MUL  	(0x00000000)
		#define PLLCCR_PLLMULNF_1_3ADD_MUL  (0x00000040) 
		#define PLLCCR_PLLMULNF_2_3ADD_MUL  (0x00000080) 
		#define PLLCCR_PLLMULNF_1_2ADD_MUL  (0x000000C0)
		
		#define PLLCCR_PLLMULNF_0VALUE    	(0)
		#define PLLCCR_PLLMULNF_1_3VALUE    (1/3)
		#define PLLCCR_PLLMULNF_2_3VALUE    (2/3)
		#define PLLCCR_PLLMULNF_1_2VALUE    (1/2)	
#define MCU_PLLCCR2_ADDR                (MCU_SYSTEM_BASE+0x4C)
		#define PLLCCR2_PLODIVP_MASK        (0x000F)
    #define PLLCCR2_PLODIVP_2DEV        (0x0001)
		#define PLLCCR2_PLODIVP_3DEV        (0x0002)
    #define PLLCCR2_PLODIVP_4DEV        (0x0003)
    #define PLLCCR2_PLODIVP_6DEV        (0x0005)
		#define PLLCCR2_PLODIVP_8DEV        (0x0007)
		#define PLLCCR2_PLODIVP_16DEV       (0x000F)
#define MCU_PRCR_ADDR         	       	(0x4001E3FA)
		#define KEYCODE_PRCR                (0xA500)
		#define PRCR_PRC1                   (0x0002)
		#define PRCR_PRC0                   (0x0001)
		#define PRCR_PRC3 									(0x0008)
		#define PRCR_PRC4                   (0x0010)	
		#define PRCR_PRC5 									(0x0020)
#define MCU_OPCCR_ADDR                  (MCU_SYSTEM_BASE+0x0A0)
    #define OPCCR_OPCCR                 (0x03)
    #define OPCCR_OPCCR_HIGH            (0x00)
    #define OPCCR_OPCMTSF               (0x10)
    #define OPCCR_OPCMTSF_COMPLETE      (0x00)
    #define OPCCR_OPCMTSF_TRANS         (0x10)
#define MCU_OFS1_ADDR										(0x02C9F0C0)
		#define OFS1_HOCOFRQ0_16MHZ					(0x00000000)
		#define OFS1_HOCOFRQ0_18MHZ					(0x00000200)
		#define OFS1_HOCOFRQ0_20MHZ					(0x00000400)		
		#define OFS1_HOCOFRQ0_32MHZ					(0x00000800)
		#define OFS1_HOCOFRQ0_48MHZ					(0x00000E00)
		#define OFS1_HOCOFRQ0_MASK				  (0x00000E00)
		#define OFS1_HOCOEN                 (0x00000100)
#define MCU_MOCOCR_ADDR									(MCU_SYSTEM_BASE + 0x038)
		#define MOCOCR_STOP									(0x01)
		#define MOCOCR_ON										(0x00)
#define MCU_HOCOCR_ADDR									(MCU_SYSTEM_BASE + 0x036)
		#define HOCOCR_STOP									(0x01)
		#define HOCOCR_ON										(0x00)
#define MCU_OSCSF_ADDR									(MCU_SYSTEM_BASE + 0x03C)
		#define OSCSF_HOCOSF                (0x01)
    #define OSCSF_MOSCSF                (0x08)
    #define OSCSF_PLLSF                 (0x20)
		#define OSCSF_PLL2SF                (0x40)
/*
   Flash related registers adrress and bit definition
*/

#define MCU_MRAM_BASE                 	(0x40130000)
#define MCU_MACI_CMD_AREA_ADDR          (0x40120000)
#define MCU_MASTAT_ADDR                 (MCU_MRAM_BASE + 0xE010)
		#define MASTAT_MREAE                (0x08)
    #define MASTAT_CMDLK                (0x10)
#define MCU_MPAEINT_ADDR                (MCU_MRAM_BASE + 0xE014)
#define MCU_MRDYIE_ADDR                 (MCU_MRAM_BASE + 0xE018)
#define MCU_MSADDR_ADDR                 (MCU_MRAM_BASE + 0xE030)
#define MCU_MSTATR_ADDR                 (MCU_MRAM_BASE + 0xE080)
    #define MSTATR_MRDY                 (0x00008000)
    #define MSTATR_ILGLERR              (0x00004000)
    #define MSTATR_PRGERR               (0x00001000)
#define MCU_MENTRYR_ADDR                (MCU_MRAM_BASE + 0xE084)
    #define MENTRYR_READ_MODE           (0xAA00)
    #define MENTRYR_EMRAM_PROGRAM       (0xAA80)
    #define MENTRYR_CHK_EMRAM           (0x0080)
    #define MENTRYR_CHK_READ            (0x0000)
#define MCU_MSUINITR_ADDR               (MCU_MRAM_BASE + 0xE08C)
#define MCU_MCMDR_ADDR                  (MCU_MRAM_BASE + 0xE0A0)

#define MCU_MRCPFB_ADDR									(MCU_MRAM_BASE + 0xC000)
		#define MRCPFB_MPFBEN_EN						(0x01)
		#define MRCPFB_MPFBEN_DIS						(0x00)
#define MCU_MRCFREQ_ADDR								(MCU_MRAM_BASE + 0xC004)
		#define MRCFREQ_KEY									(0x1E000000)
#define MCU_MREFREQ_ADDR								(MCU_MRAM_BASE + 0xC008)
		#define MREFREQ_KEY									(0xE1000000)
#define MCU_MRCPS_ADDR								  (MCU_MRAM_BASE + 0xF010)
		#define MRCPS_PRGERRC               (0x01)
		#define MRCPS_ECCERRC               (0x02)
		#define MRCPS_ABUFEMP               (0x20)
		#define MRCPS_ABUFFULL              (0x40)
		#define MRCPS_PRGBSYC               (0x80)
#define MCU_MRCFLR_ADDR									(MCU_MRAM_BASE + 0xF030)
		#define	MRCFLR_KEY									(0xC300)
		#define MRCFLR_MRCFL_EXECUTE				(0x01)
#define MCU_MRCPC0_ADDR 								(0x4013F000)
		#define MRCPC0_KEY									(0x8600)
		#define MRCPC0_MRCPSEN_ENABLE				(0x0001)
#define MCU_MRCPC1_ADDR									(0x4013F004) 
		#define MRCPC1_KEY									(0x6800)
		#define MRCPC1_MRCPSEN_ENABLE	  		(0x0001)
#define MCU_MRCBPROT0_ADDR							(0x4013F008)
		#define MRCBPROT0_KEY								(0x07800)
		#define MRCBPROT0_BPCN0_ENABLE			(0x0000)
		#define MRCBPROT0_BPCN0_DISABLE			(0x0001)
#define MCU_MRCBPROT1_ADDR							(0x4013F00C)
		#define MRCBPROT1_KEY								(0xB100)
		#define MRCBPROT1_BPCN1_ENABLE			(0x0000)
		#define MRCBPROT1_BPCN1_DISABLE			(0x0001)		

/*
   Other parameters
*/
#define MACI_FORCED_QUIT_CMD    				(0xB3)
#define MACI_STATUS_CLR_CMD     				(0x50)
#define MACI_INCREAMENT_COUNTER     		(0x35)
#define MACI_READ_COUNTER     					(0x39)
#define MACI_PROG_CMD										(0xE8)
#define MACI_PROG_NUM										(0x08)
#define MACI_CONF_EXTRA_CMD							(0x40)
#define MACI_CONF_EXTRA_NUM							(0x08)
#define MACI_OTP_EXTRA_CMD							(0xE8)
#define MACI_OTP_EXTRA_NUM							(0x08)
#define MACI_END_CMD										(0xD0)
#define EMRAM_MASK_ADDR									(0x0FFFFFFF)

#define MCU_CODEMRAM_ADDR								(0x02000000)
#define MCU_CONF_EXTRAMRAM_ADDR					(0x02C9F040)
#define MCU_OTP_EXTRAMRAM_ADDR   				(0x02E07600)

#define MCU_CODEMRAM_BLOCK_SIZE					(0x8000)
#define CODEMRAM_32K_BLOCK_NUM					(0x00)

#ifdef RA8P1_1M
		#undef  CODEMRAM_32K_BLOCK_NUM			// Undef macro first
		#define CODEMRAM_32K_BLOCK_NUM     	(0x20)
#endif

#ifdef RA8P1_512K
		#undef  CODEMRAM_32K_BLOCK_NUM			// Undef macro first
		#define CODEMRAM_32K_BLOCK_NUM     	(0x10)
#endif

#ifdef RA8P1_CONF //The Configuration area does not support Erase, the default number for blocks 32KB is 0. 

#endif

#ifdef RA8P1_OTP //The OTP area does not support Erase, the default number for blocks 32KB is 0. 

#endif

/** Type declaration */
typedef unsigned char  ubyte_t;
typedef unsigned short uword_t;
typedef unsigned long  udword_t;

/** Index of internal data buffer */
typedef enum reg_data{
    PRCR = 0,
    SCKSCR,
    SCKDIVCR,
		SCKDIVCR2,
    PLLCCR,
		PLLCCR2,
    HOCOCR,
    MOCOCR,
    OPCCR,
    OFS1,
		MRCFREQ,
		MREFREQ,
		MRCPFB,
		MRCPC1,
		MRCBPROT1,
    REG_MAX,
}reg_data;

/** Register save structure */
static udword_t    s_udwClockUserVal[REG_MAX];
/*
   Current ICLK frequency
*/
static udword_t dwICLKValue;
/*
   MRCMHZ/MREMHZ Change Flag
	 0: low to high change
	 1: high to low change
*/
static udword_t dwMRCFREQChgFlg;
static udword_t dwMREFREQChgFlg;

/** Internal function declaration */
static udword_t change_clock_and_mode(udword_t *udwp_clk, udword_t *udwp_cmram_clk, udword_t *udwp_emram_clk);
static udword_t check_clock_and_mode(udword_t *udwp_clk, udword_t *udwp_cmram_clk, udword_t *udwp_emram_clk);
static udword_t restore_clock_and_mode(void);
static ubyte_t saveModeAndClock(void);
static udword_t pe_mode_entry(udword_t dwAddr, udword_t *adrRet);
static udword_t ilgl_chk(void);
static void set_mrcfreq(udword_t *udwp_clk);
static void set_mrefreq(udword_t *udwp_clk);
static void Wait_us(udword_t us);
static void read_page(udword_t adr, udword_t sz, ubyte_t *buf);

/**************************************************************************//**
* @details Initialize Flash Programming Functions
* @param[in] adr:  Device Base Address
* @param[in] clk:  Clock Frequency (Hz)
* @param[in] fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
* @retval    0 - OK,  1 - Failed
******************************************************************************/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
{

    ubyte_t ub_ret_val;
		udword_t udwMRPCKValue;
		udword_t udwMRICKValue;
	
		// Initialize the maximum value of ICLK
    dwICLKValue = 250000000;
		
		// Initialize the maximum value of MRPCK
    udwMRPCKValue = 125000000;
	
		// Initialize the maximum value of MRICK
    udwMRICKValue = 250000000;
		
		// Initialize MRCMHZ Change Flag
		dwMRCFREQChgFlg = 0;
	
		// Initialize MREMHZ Change Flag
		dwMREFREQChgFlg = 0;
	
		// Save current settings
		saveModeAndClock();
		ub_ret_val = check_clock_and_mode(&clk, &udwMRICKValue, &udwMRPCKValue);
		/* 
		Check if flash rewritable clock and power control mode 
		and save current frequency for dwICLKValue 
		*/
	
#ifdef CHG_CLK_AND_MOD_ENA
    /** If the clock and mode cannot rewrite the flash, switch to a possible state */
    if (ub_ret_val)
    {
        ub_ret_val = change_clock_and_mode(&clk, &udwMRICKValue,  &udwMRPCKValue);
			
				// Save ICLK frequency for wait processing
				dwICLKValue = clk;
    }
#endif

    if (ub_ret_val)
    {
        /** Failure if the flash cannot be rewritten */
        return (1); // Failed
    }
		
    return ilgl_chk(); // Check for command locked and illegal status	
}

/**************************************************************************//**
* @details   De-Initialize Flash Programming Functions
* @param[in] fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
* @retval    0 - OK,  1 - Failed
******************************************************************************/
int UnInit (unsigned long fnc)
{
		M16(MCU_MENTRYR_ADDR) = MENTRYR_READ_MODE; // Transition to read mode
		while (M16(MCU_MENTRYR_ADDR) != MENTRYR_CHK_READ) // Wait until switching to read mode
		{
				;
		}
		
		/** Restore clock and power control mode to the state before flash rewriting */
    if (restore_clock_and_mode())
    {
        return (1); // Failed
    }
    return (0); // OK
}

/**************************************************************************//**
* @details   Program Page in Flash Memory
* @param[in] adr:  Block start Address
* @param[in] sz:   Block size in byte
* @param[in] pat:  Pattern to compare
* @retval    0 - Memory is blank,  1 - Memory is not blank
******************************************************************************/
int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat)
{
#if (defined RA8P1_CONF) || (defined RA8P1_OTP) 
		/** Configuration area and OTP area not support BlankCheck */
		return (0); // OK
#endif
	
		ubyte_t ub_rd_buf[128];
		unsigned long adr_tmp;
    uword_t i, j ,k, m;
		adr_tmp = adr;
    m = 0;
	
    M16(MCU_MENTRYR_ADDR) = MENTRYR_READ_MODE; // Transition to read mode
		while (M16(MCU_MENTRYR_ADDR) != MENTRYR_CHK_READ) // Wait until switching to read mode
		{
				;
		}
	
    for (i = 0; i < sz ; i += 128)
    {
				read_page(adr_tmp, 128, &ub_rd_buf[0]);
			
        /** Determine size to compare */
        if ((sz - i) >= 128)
        {
            k = 128;
        } else
        {
            k = (sz - i);     
        }			
				
        /** Check up to 128 bytes if equal to pattern "pat" */
        for (j = 0; j < k; j++)
        {
            if (ub_rd_buf[j] != pat)
            {
                return (1); // Memory is not blank	
            }
            m++;
        }	
				adr_tmp = adr + m;
    }
		
	  return (0); // Memory is blank
}

/**************************************************************************//**
* @details   Erase complete Flash Memory
* @param     None
* @retval    0 - OK,  1 - Failed
******************************************************************************/
int EraseChip (void)
{
#if (defined RA8P1_CONF) || (defined RA8P1_OTP) 
		/** Configuration area and OTP area not support EraseChip */
		return (0); // OK
#endif

		udword_t adr;
	
		for (adr = MCU_CODEMRAM_ADDR; adr < (MCU_CODEMRAM_ADDR + CODEMRAM_32K_BLOCK_NUM * MCU_CODEMRAM_BLOCK_SIZE); adr += MCU_CODEMRAM_BLOCK_SIZE)
		{
				if (EraseSector(adr))
				{
					return 1;
				}
		}
		
		return (0); // OK
}

/**************************************************************************//**
* @details   Erase Sector in Flash Memory
* @param[in] adr:  Sector Address
* @retval    0 - OK,  1 - Failed
******************************************************************************/
int EraseSector (unsigned long adr)
{
#if (defined RA8P1_CONF) || (defined RA8P1_OTP) 
		/** Configuration area and OTP area not support EraseSector */
		return (0); // OK
#endif
	
    uword_t uw_wrt_cnt;
    uword_t uw_wrt_byte;
    uword_t i;
    udword_t udw_adr_tmp;
    ubyte_t uby_ilgl_chk;
	
		unsigned long sz = MCU_CODEMRAM_BLOCK_SIZE;
		udword_t pat = 0xFFFFFFFF;
		
		pe_mode_entry(adr,&udw_adr_tmp); // Check for command locked and illegal status
				
		M16(MCU_MRCPC1_ADDR) = MRCPC1_KEY | MRCPC1_MRCPSEN_ENABLE;
		M16(MCU_MRCBPROT1_ADDR) = MRCBPROT1_KEY | MRCBPROT1_BPCN1_DISABLE;
		
		while (sz)
		{
				while ((M8(MCU_MRCPS_ADDR) & MRCPS_PRGBSYC) == MRCPS_PRGBSYC) // Code MRAM is not in program status.
				{
					;
				}
				
				while ((M8(MCU_MRCPS_ADDR) & MRCPS_ABUFFULL) == MRCPS_ABUFFULL) //  Address buffer is not full, and code MRAM write transaction is acceptable
				{
					;
				}

				uw_wrt_cnt = 8;
				uw_wrt_byte = uw_wrt_cnt * 4;
			
				for(i = 0; i < uw_wrt_cnt; i++)
				{
						/** Store 4 bytes each for programming data */
						M32(adr + (i * 4)) = pat;
						
						__asm volatile ("dmb");
						M16(MCU_MRCFLR_ADDR) = MRCFLR_KEY | MRCFLR_MRCFL_EXECUTE;
					
						while ((M8(MCU_MRCPS_ADDR) & MRCPS_ABUFEMP) == 0) // Address buffer is empty, and code MRAM write data cannot flush
						{
							;
						}
						
						while ((M8(MCU_MRCPS_ADDR) & MRCPS_PRGBSYC) == MRCPS_PRGBSYC) // Code MRAM is not in program status.
						{
							;
						}
						
						if (M8(MCU_MRCPS_ADDR) & (MRCPS_PRGERRC | MRCPS_ECCERRC)) 
						{
								return (1);
						}
				}
									
				adr += uw_wrt_byte;
				sz -= uw_wrt_byte; // Update remaining size	
		}		

			
		/** Check for command locked and illegal status */
    uby_ilgl_chk = ilgl_chk();
    if (uby_ilgl_chk)
    {
        return (1); // Failed
    }
		
		return (0); // OK
}

/**************************************************************************//**
* @details   Program Page in Flash Memory
* @param[in] adr:  Page Start Address
* @param[in] sz:   Page Size
* @param[in] buf:  Page Data
* @retval    0 - OK,  1 - Failed
******************************************************************************/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
{

    uword_t uw_start_data = 0;
    uword_t uw_wrt_cnt;
    uword_t uw_wrt_byte;
    uword_t i;
    udword_t udw_adr_tmp;
		ubyte_t uby_area_flg = 0;
    ubyte_t uby_ilgl_chk;

		pe_mode_entry(adr,&udw_adr_tmp); // Check for command locked and illegal status
		M16(MCU_MRCPC1_ADDR) = MRCPC1_KEY | MRCPC1_MRCPSEN_ENABLE;
		M16(MCU_MRCBPROT1_ADDR) = MRCBPROT1_KEY | MRCBPROT1_BPCN1_DISABLE;
		
		while (sz)
		{
	
				while ((M8(MCU_MRCPS_ADDR) & MRCPS_PRGBSYC) == MRCPS_PRGBSYC) // Code MRAM is not in program status.
				{
					;
				}
				
				while ((M8(MCU_MRCPS_ADDR) & MRCPS_ABUFFULL) == MRCPS_ABUFFULL) //  Address buffer is not full, and code MRAM write transaction is acceptable
				{
					;
				}
				
				if (adr < MCU_CONF_EXTRAMRAM_ADDR)
				{
					uby_area_flg = 0; // Code MRAM area
					
					if (sz < 32)
					{
							for (i = sz; i < 32; i++)
							{
									buf[uw_start_data + i] = 0xFF;
							}
							sz = 32;
					}
				} else if (adr < MCU_OTP_EXTRAMRAM_ADDR)
				{
					uby_area_flg = 1; // Configuration setting area
					
					if (sz < 16)
					{
							for (i = sz; i < 16; i++)
							{
									buf[uw_start_data + i] = 0xFF;
							}
							sz = 16;
					}
				} else 
				{
					uby_area_flg = 2; // OTP area
					
					if (sz < 16)
					{
							for (i = sz; i < 16; i++)
							{
									buf[uw_start_data + i] = 0xFF;
							}
							sz = 16;
					}
				}
				
				
				if (uby_area_flg == 0) 
				{
						uw_wrt_cnt = 8;
						uw_wrt_byte = uw_wrt_cnt * 4;
					
						for(i = 0; i < uw_wrt_cnt; i++)
						{
								/** Store 4 bytes each for programming data */
								M32(adr + (i * 4)) = (udword_t)(buf[uw_start_data] | (buf[uw_start_data+1] << 8) | (buf[uw_start_data+2] << 16) | (buf[uw_start_data+3] << 24));
								uw_start_data +=4;
							
								__asm volatile ("dmb");
							
								M16(MCU_MRCFLR_ADDR) = MRCFLR_KEY | MRCFLR_MRCFL_EXECUTE;
							
								while ((M8(MCU_MRCPS_ADDR) & MRCPS_ABUFEMP) == 0) // Address buffer is empty, and code MRAM write data cannot flush
								{
									;
								}
						
								while ((M8(MCU_MRCPS_ADDR) & MRCPS_PRGBSYC) == MRCPS_PRGBSYC) // Code MRAM is not in program status.
								{
									;
								}
								
								if (M8(MCU_MRCPS_ADDR) & (MRCPS_PRGERRC | MRCPS_ECCERRC)) 
								{
										return 1;
								}
								
						}
						
				} else 
				{
						M32(MCU_MSADDR_ADDR) = adr; // Set the program start address
						if (uby_area_flg == 2)      // OTP area
						{
								M8(MCU_MACI_CMD_AREA_ADDR) = MACI_OTP_EXTRA_CMD; // Execution of program command
								M8(MCU_MACI_CMD_AREA_ADDR) = MACI_OTP_EXTRA_NUM; // Set the number of write data access
								uw_wrt_cnt = MACI_OTP_EXTRA_NUM;
								uw_wrt_byte = MACI_OTP_EXTRA_NUM * 2; // Data size to write with one command
						} else											// Configuration setting area
						{
								M8(MCU_MACI_CMD_AREA_ADDR) = MACI_CONF_EXTRA_CMD; // Execution of program command
								M8(MCU_MACI_CMD_AREA_ADDR) = MACI_CONF_EXTRA_NUM; // Set the number of write data access
								uw_wrt_cnt = MACI_CONF_EXTRA_NUM;
								uw_wrt_byte = MACI_CONF_EXTRA_NUM * 2; // Data size to write with one command
						}
						
						for (i = 0; i < uw_wrt_cnt; i++)
						{
								/** Store 2 bytes each for programming data */
								M16(MCU_MACI_CMD_AREA_ADDR) = (uword_t)(buf[uw_start_data] | (buf[uw_start_data+1] << 8));
								uw_start_data +=2;
						}
						
						M8(MCU_MACI_CMD_AREA_ADDR) = MACI_END_CMD; // Execution of termination command
						
						while (!(M32(MCU_MSTATR_ADDR) & MSTATR_MRDY))
						{
								; // Waiting for command completion
						}
			
						if (M32(MCU_MSTATR_ADDR) & MSTATR_PRGERR)
						{
								/** Programming error */
								return (1); // Failed
						}
				}
				
				adr += uw_wrt_byte;
				sz -= uw_wrt_byte; // Update remaining size	
		}		
			
		/** Check for command locked and illegal status */
    uby_ilgl_chk = ilgl_chk();
    if (uby_ilgl_chk)
    {
        return (1); // Failed
    }
		
		return (0); // OK
}

/**************************************************************************//**
* @details   Verify the size from the specified address
* @param[in] adr:  Start Address
* @param[in] sz:   Page Size
* @param[in] buf:  Page Data
* @retval    (adr + sz) - OK,  (adr + m) - Failed (Represent the failing address)
******************************************************************************/
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
{

    ubyte_t ub_rd_buf[128];
		unsigned long adr_tmp;
    uword_t i, j ,k, m;
		adr_tmp = adr;
    m = 0;
	
    M16(MCU_MENTRYR_ADDR) = MENTRYR_READ_MODE; // Transition to read mode
		while (M16(MCU_MENTRYR_ADDR) != MENTRYR_CHK_READ) // Wait until switching to read mode
		{
				;
		}
	
    for (i = 0; i < sz ; i += 128)
    {
				read_page(adr_tmp, 128, &ub_rd_buf[0]);
			
        /** Determine size to compare */
        if ((sz - i) >= 128)
        {
            k = 128;
        } else
        {
            k = (sz - i);     
        }			
				
        /** Check up to 128 bytes if equal to read data */
        for (j = 0; j < k; j++)
        {
            if (ub_rd_buf[j] != buf[m])
            {
                return (adr + m); // Address of Memory is not blank
            }
            m++;
        }	
				adr_tmp = adr + m;
    }
		
	  return (adr + sz); // Verify is success, in this line: sz = m
}

/**************************************************************************//**
* @details   Check if flash rewritable clock and power control mode
* @param[in] udwp_clk:  Clock Frequency (Hz)
* @retval    0 - OK,  1 - Failed
******************************************************************************/
static udword_t check_clock_and_mode(udword_t *udwp_clk, udword_t *udwp_cmram_clk, udword_t *udwp_emram_clk)
{

    udword_t udw_sckdivcr;
    udword_t udw_sckdivcr_val;
    udword_t udw_clk_tmp, udw_clk_tmp_PLLMUL;
    uword_t uw_bai;
    uword_t uw_plli_div;
		uword_t uw_sckdivcr2;

    udw_sckdivcr = M32(MCU_SCKDIVCR_ADDR);
		uw_sckdivcr2 = M16(MCU_SCKDIVCR2_ADDR);

    /** Calculate FCLK */
    switch (s_udwClockUserVal[SCKSCR])
    {
        case SCKSCR_CKSEL_PLL1P:
            if (s_udwClockUserVal[PLLCCR] & PLLCCR_PLSRCSEL_HOCO)
            { // Input clock source is HOCO
                if (OFS1_HOCOFRQ0_16MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
                {
                    udw_clk_tmp = 16000000;
                } else if (OFS1_HOCOFRQ0_18MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
                {
                    udw_clk_tmp = 18000000;
                } else if (OFS1_HOCOFRQ0_20MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
                {
                    udw_clk_tmp = 20000000;
                } else if (OFS1_HOCOFRQ0_32MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
                {
                    udw_clk_tmp = 32000000;
                } else if (OFS1_HOCOFRQ0_48MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
                {
                    udw_clk_tmp = 48000000;
                } else
                {
                    return (1); // Failed - This case cannot happen
                }
            } else
            {
                udw_clk_tmp = *udwp_clk; // Input clock source is External clock
            }

            uw_plli_div = (s_udwClockUserVal[PLLCCR] & PLLCCR_PLIDIV_MASK) + 1; // PLL1 input frequency division
            uw_bai = (((s_udwClockUserVal[PLLCCR] & PLLCCR_MUL) >> 8) + 1) / 2; // PLL1 frequency multiplication factor (PLLMUL)
            udw_clk_tmp_PLLMUL = (udw_clk_tmp / (udword_t)uw_plli_div) * (udword_t)uw_bai; // Part PLLMUL of Frequency output PLL1
						
						/* Frequency output as PLL1 = Part PLLMUL of Frequency output PLL1 + Part PLLMULNF of Frequency output PLL1 */
						if (PLLCCR_PLLMULNF_0ADD_MUL == (s_udwClockUserVal[PLLCCR] & PLLCCR_PLLMULNF_MASK))
						{
							udw_clk_tmp = udw_clk_tmp_PLLMUL;
						} 
						else if (PLLCCR_PLLMULNF_1_3ADD_MUL == (s_udwClockUserVal[PLLCCR] & PLLCCR_PLLMULNF_MASK))
						{
							udw_clk_tmp = udw_clk_tmp_PLLMUL + ((udw_clk_tmp / (udword_t)uw_plli_div) * PLLCCR_PLLMULNF_1_3VALUE);  
						} 
						else if (PLLCCR_PLLMULNF_2_3ADD_MUL == (s_udwClockUserVal[PLLCCR] & PLLCCR_PLLMULNF_MASK))
						{
							udw_clk_tmp = udw_clk_tmp_PLLMUL + ((udw_clk_tmp / (udword_t)uw_plli_div) * PLLCCR_PLLMULNF_2_3VALUE);  
						} 
						else // (PLLCCR_PLLMULNF_1_2ADD_MUL == (s_udwClockUserVal[PLLCCR] & PLLCCR_PLLMULNF_MASK))
						{
							udw_clk_tmp = udw_clk_tmp_PLLMUL + ((udw_clk_tmp / (udword_t)uw_plli_div) * PLLCCR_PLLMULNF_1_2VALUE);  
						}
						
						/* Frequency output as PLL1P */
						uw_plli_div = (s_udwClockUserVal[PLLCCR2] & PLLCCR2_PLODIVP_MASK) + 1; // PLL1 Output Frequency Division Ratio Select for output clock P (PLL1P)
						udw_clk_tmp = udw_clk_tmp / (udword_t)uw_plli_div; // Frequency output as PLL1P
																	
        break;
        case SCKSCR_CKSEL_MOSC:
            udw_clk_tmp = *udwp_clk; // Input clock source is External clock
        break;
        case SCKSCR_CKSEL_MOCO:
            udw_clk_tmp = 8000000; // MOCO frequency is 8MHz
        break;
        case SCKSCR_CKSEL_HOCO:
            if (OFS1_HOCOFRQ0_16MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
            {
                udw_clk_tmp = 16000000; // HOCO frequency is 16MHz
            } else if (OFS1_HOCOFRQ0_18MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
            {
                udw_clk_tmp = 18000000; // HOCO frequency is 18MHz
            } else if (OFS1_HOCOFRQ0_20MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
            {
                udw_clk_tmp = 20000000; // HOCO frequency is 20MHz
            } else if (OFS1_HOCOFRQ0_32MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
					  {
								udw_clk_tmp = 32000000; // HOCO frequency is 32MHz
            } else if (OFS1_HOCOFRQ0_48MHZ == (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK))
						{
								udw_clk_tmp = 48000000; // HOCO frequency is 48MHz
						}	else
            {
                return (1); // Failed - This case cannot happen
            }
        break;
        case SCKSCR_CKSEL_SOSC: // Failed because SOSC frequency is below 4MHz
						udw_clk_tmp = 32768;
				break;
        default:
            return (1); // Failed - This case cannot happen
    }

		udw_sckdivcr = (udw_sckdivcr & SCKDIVCR_MRPCK_MASK) >> 28;	
		if (udw_sckdivcr & 0x8) //case udw_sckdivcr = 8 (1/3), 9 (1/6) ,or 10 (1/12)
		{ 
				udw_sckdivcr_val = 3 * (1 << (udw_sckdivcr - 8)); // MRPCK division value
				*udwp_emram_clk = udw_clk_tmp / udw_sckdivcr_val; // Calculate MRPCK frequency
		}
		else
		{
				udw_sckdivcr_val = 1 << udw_sckdivcr; // MRPCK division value
				*udwp_emram_clk = udw_clk_tmp / udw_sckdivcr_val; // Calculate MRPCK frequency
		}
		
		udw_sckdivcr = M32(MCU_SCKDIVCR_ADDR);
		udw_sckdivcr = (udw_sckdivcr & SCKDIVCR_ICK_MASK) >> 24;
		if (udw_sckdivcr & 0x8) //case udw_sckdivcr = 8 (1/3), 9 (1/6) ,or 10 (1/12)
		{
				udw_sckdivcr_val = 3 * (1 << (udw_sckdivcr - 8)); // ICLK division value
				dwICLKValue = udw_clk_tmp / udw_sckdivcr_val; // Calculate ICLK frequency
		}
		else
		{		
				udw_sckdivcr_val = 1 << udw_sckdivcr; // ICLK division value
				dwICLKValue = udw_clk_tmp / udw_sckdivcr_val; // Calculate ICLK frequency
		}
		
		uw_sckdivcr2 = (uw_sckdivcr2 & SCKDIVCR2_MRICK_MASK) >> 12;	
		if (uw_sckdivcr2 & 0x8) //case udw_sckdivcr = 8 (1/3), 9 (1/6) ,or 10 (1/12)
		{ 
				udw_sckdivcr_val = 3 * (1 << (uw_sckdivcr2 - 8)); // MRICK division value
				*udwp_cmram_clk = udw_clk_tmp / udw_sckdivcr_val; // Calculate MRICK frequency
		}
		else
		{
				udw_sckdivcr_val = 1 << udw_sckdivcr; // MRICK division value
				*udwp_cmram_clk = udw_clk_tmp / udw_sckdivcr_val; // Calculate MRICK frequency
		}
		
    /** Failed if power control mode is Low-speed mode */
    if ((s_udwClockUserVal[OPCCR] & OPCCR_OPCCR) != OPCCR_OPCCR_HIGH)
    {
        return (1); // Failed
    }
		
		/** Failed because outside the allowable frequency(0-250MHz) of ICLK */
    if (((dwICLKValue) <= 0) || (250000000 < (dwICLKValue)))
    {
        return (1); // Failed
    }
		
		/** Failed because outside the allowable frequency(0-250MHz) of MRICK */
    if (((*udwp_cmram_clk) <= 0) || (250000000 < (*udwp_cmram_clk)))
    {
        return (1); // Failed
    }
		
		/** Failed because outside the allowable frequency(0-125MHz) of MRPCLK */
    if (((*udwp_emram_clk) <= 0) || (125000000 < (*udwp_emram_clk)))
    {
        return (1); // Failed
    }
		
		set_mrcfreq(udwp_cmram_clk);
		
		set_mrefreq(udwp_emram_clk);
		
		
    return (0); // OK

}

/**************************************************************************//**
* @details   Change to flash rewritable clock and power control mode
* @param[in] udwp_clk:  Clock Frequency (Hz)
* @retval    0 - OK,  1 - Failed
******************************************************************************/
static udword_t change_clock_and_mode(udword_t *udwp_clk, udword_t *udwp_cmram_clk, udword_t *udwp_emram_clk)
{
		
		ubyte_t temp = 0;
    M16(MCU_PRCR_ADDR) = KEYCODE_PRCR | PRCR_PRC1 | PRCR_PRC0; // Unprotect some registers
	
		if ((*udwp_cmram_clk) <= 0) // MRICK low to high change
    {
        dwMRCFREQChgFlg = 0;
    }
		else if (250000000 < (*udwp_cmram_clk)) // MRICK high to low change
		{
				M8(MCU_MRCPFB_ADDR) = MRCPFB_MPFBEN_DIS;
				for (int i = 0; i < 3; i++)
				{
					temp = M8(MCU_MRCPFB_ADDR);
				}
			
				dwMRCFREQChgFlg = 1;
		}
		else
		{
				; // This process does not pass because there is no change in MRICK
		}
		
		if ((*udwp_emram_clk) <= 0) // MRPCLK low to high change
    {
        dwMREFREQChgFlg = 0;
    }
		else if (125000000 < (*udwp_emram_clk)) // MRPCLK high to low change
		{
				dwMREFREQChgFlg = 1;
		}
		else
		{
				; // This process does not pass because there is no change in MRPCLK
		}
		
    /** If it is Low-speed mode, set it to High-speed mode */
    if ((s_udwClockUserVal[OPCCR] & OPCCR_OPCCR) != OPCCR_OPCCR_HIGH)
    {
        M8(MCU_OPCCR_ADDR) = OPCCR_OPCCR_HIGH;
        while (M8(MCU_OPCCR_ADDR) & OPCCR_OPCMTSF)
        {
            ; // Wait till transition completed
        }
    }

    /** If OFS1 is in the initial state, set the clock source to MOCO */
    if ((s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK) != OFS1_HOCOFRQ0_16MHZ && (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK) != OFS1_HOCOFRQ0_18MHZ && (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK) != OFS1_HOCOFRQ0_20MHZ && (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK) != OFS1_HOCOFRQ0_32MHZ && (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK) != OFS1_HOCOFRQ0_48MHZ) 
    {
				*udwp_emram_clk = 8000000; // MRPCK frequency is 8MHz
				*udwp_cmram_clk = 8000000; // MRICK frequency is 8MHz
				*udwp_clk = 8000000; 			 // ICLK frequency is 8MHz
			
				if (dwMRCFREQChgFlg == 0)
				{
						set_mrcfreq(udwp_cmram_clk);
				}
				
				if (dwMREFREQChgFlg == 0)
				{
						set_mrefreq(udwp_emram_clk);
				}
			
        /** If the clock source is MOCO, set the clock source to MOCO */
        if (s_udwClockUserVal[SCKSCR] != SCKSCR_CKSEL_MOCO)
        {
            /** If MOCO oscillation is stopped, oscillate it */
            if (s_udwClockUserVal[MOCOCR] == MOCOCR_STOP)
            {
                M8(MCU_MOCOCR_ADDR) = MOCOCR_ON;
                Wait_us(15);// Wait for oscillation stabilization of MOCO 
            }
						
            M16(MCU_SCKSCR_ADDR) = SCKSCR_CKSEL_MOCO;// Set the clock source to MOCO
        }
        /** Change MRPCK and ICLK to divide by 1 */
        M32(MCU_SCKDIVCR_ADDR) &= (SCKDIVCR_MRPCK_1DEV_MASK & SCKDIVCR_ICLK_1DEV_MASK);
				/** Change MRICK to divide by 1 */
        M32(MCU_SCKDIVCR2_ADDR) &= SCKDIVCR2_MRICK_1DEV_MASK;
				
    } else
    {/** If OFS1 is not in the initial state, set the clock source to HOCO */
				// Check HOCO freqency
				switch (s_udwClockUserVal[OFS1] & OFS1_HOCOFRQ0_MASK)
        {
            case OFS1_HOCOFRQ0_16MHZ:
                *udwp_cmram_clk = 16000000; // MRICK frequency is 16MHz
								*udwp_emram_clk = 16000000; // MRPCK frequency is 16MHz
								*udwp_clk = 16000000; 		  // ICLK frequency is 16MHz
            break;
            case OFS1_HOCOFRQ0_18MHZ:
								*udwp_cmram_clk = 18000000; // MRICK frequency is 18MHz
								*udwp_emram_clk = 18000000; // MRPCK frequency is 18MHz
								*udwp_clk = 18000000; 		  // ICLK frequency is 18MHz
            break;
            case OFS1_HOCOFRQ0_20MHZ:
                *udwp_cmram_clk = 20000000; // MRICK frequency is 20MHz
								*udwp_emram_clk = 20000000; // MRPCK frequency is 20MHz
								*udwp_clk = 20000000; 		  // ICLK frequency is 20MHz
            break;
						case OFS1_HOCOFRQ0_32MHZ:
                *udwp_cmram_clk = 32000000; // MRICK frequency is 32MHz
								*udwp_emram_clk = 32000000; // MRPCK frequency is 32MHz
								*udwp_clk = 32000000; 		  // ICLK frequency is 32MHz
            break;
            case OFS1_HOCOFRQ0_48MHZ:
                *udwp_cmram_clk = 48000000; // MRICK frequency is 48MHz
								*udwp_emram_clk = 48000000; // MRPCK frequency is 48MHz
								*udwp_clk = 48000000; 		  // ICLK frequency is 48MHz
            break;
            default:
                return (1); // Failed
        }
				
				if (dwMRCFREQChgFlg == 0)
				{
						set_mrcfreq(udwp_cmram_clk);
				}
				
				if (dwMREFREQChgFlg == 0)
				{
						set_mrefreq(udwp_emram_clk);
				}
			
        if (s_udwClockUserVal[SCKSCR] != SCKSCR_CKSEL_HOCO)
        {

            /** If HOCO oscillation is stopped, oscillate it */
            if (s_udwClockUserVal[HOCOCR] == HOCOCR_STOP)
            {
                M8(MCU_HOCOCR_ADDR) = HOCOCR_ON;
                while (!(M8(MCU_OSCSF_ADDR) & OSCSF_HOCOSF))
                {
                    Wait_us(65);
                }
            }
						
            M16(MCU_SCKSCR_ADDR) = SCKSCR_CKSEL_HOCO;// Set the clock source to HOCO
        }

        /** Change MRPCK and ICLK to divide by 1 */
        M32(MCU_SCKDIVCR_ADDR) &= (SCKDIVCR_MRPCK_1DEV_MASK & SCKDIVCR_ICLK_1DEV_MASK);
				/** Change MRICK to divide by 1 */
        M32(MCU_SCKDIVCR2_ADDR) &= SCKDIVCR2_MRICK_1DEV_MASK;

    }		
		
		if (dwMRCFREQChgFlg == 1)
		{
				set_mrcfreq(udwp_cmram_clk);
		}
		
		if (dwMREFREQChgFlg == 1)
		{
				set_mrefreq(udwp_emram_clk);
		}
		
		if  ((*udwp_cmram_clk / 1000000) > 101) 
		{
			M8(MCU_MRCPFB_ADDR) = MRCPFB_MPFBEN_EN;
		}
		
    return (0); // OK
}

/*
 *  Save Mode & Clock before rewriting flash
 *    Parameter:      none
 *    Return Value:   0 - OK,  1 - Failed
 */
static ubyte_t saveModeAndClock(void)
{
		s_udwClockUserVal[PRCR] = (udword_t)M16(MCU_PRCR_ADDR);
		s_udwClockUserVal[SCKSCR] = (udword_t)M8(MCU_SCKSCR_ADDR);
		s_udwClockUserVal[SCKDIVCR] = M32(MCU_SCKDIVCR_ADDR);
		s_udwClockUserVal[SCKDIVCR2] = M32(MCU_SCKDIVCR2_ADDR);
		s_udwClockUserVal[PLLCCR] = (udword_t)M16(MCU_PLLCCR_ADDR);
		s_udwClockUserVal[PLLCCR2] = (udword_t)M16(MCU_PLLCCR2_ADDR);
		s_udwClockUserVal[HOCOCR] = (udword_t)M8(MCU_HOCOCR_ADDR);
		s_udwClockUserVal[MOCOCR] = (udword_t)M8(MCU_MOCOCR_ADDR);
		s_udwClockUserVal[OPCCR] = (udword_t)M8(MCU_OPCCR_ADDR);
		s_udwClockUserVal[OFS1] = M32(MCU_OFS1_ADDR);
		
		s_udwClockUserVal[MRCFREQ] = M32(MCU_MRCFREQ_ADDR);
		s_udwClockUserVal[MREFREQ] = M32(MCU_MREFREQ_ADDR);
		s_udwClockUserVal[MRCPFB] = (udword_t)M8(MCU_MRCPFB_ADDR);
		s_udwClockUserVal[MRCPC1] = (udword_t)M16(MCU_MRCPC1_ADDR);
		s_udwClockUserVal[MRCBPROT1] = (udword_t)M16(MCU_MRCBPROT1_ADDR);
		
		return (0); // Finished without Errors
}

/**************************************************************************//**
* @details   Restore clock and power control mode to the state before flash rewriting
* @param     None
* @retval    0 - OK,  1 - Failed
******************************************************************************/
static udword_t restore_clock_and_mode(void)
{

    /** There is no problem with the following cast conversions
     * because it is a type conversion from unsigned to unsigned */
	
		if (dwMRCFREQChgFlg == 1)
		{
				M32(MCU_MRCFREQ_ADDR) = MRCFREQ_KEY | s_udwClockUserVal[MRCFREQ];
		}
		
		if (dwMREFREQChgFlg == 1)
		{
				M32(MCU_MREFREQ_ADDR) = MREFREQ_KEY | s_udwClockUserVal[MREFREQ];
		}
	
		
		M16(MCU_PRCR_ADDR) = KEYCODE_PRCR + PRCR_PRC1 + PRCR_PRC0;
    M32(MCU_SCKDIVCR_ADDR) = s_udwClockUserVal[SCKDIVCR]; // Restore system clock division
		M32(MCU_SCKDIVCR2_ADDR) = s_udwClockUserVal[SCKDIVCR2]; // Restore system clock division
    M16(MCU_SCKSCR_ADDR) = (uword_t)s_udwClockUserVal[SCKSCR]; // Restore system clock source
    M8(MCU_HOCOCR_ADDR) = (ubyte_t)s_udwClockUserVal[HOCOCR]; // Restore the oscillation state of HOCO
    M8(MCU_MOCOCR_ADDR) = (ubyte_t)s_udwClockUserVal[MOCOCR]; // Restore the oscillation state of MOCO
	
    /** Restore power control mode */
    M8(MCU_OPCCR_ADDR) = (ubyte_t)s_udwClockUserVal[OPCCR];
		while (M8(MCU_OPCCR_ADDR) & OPCCR_OPCMTSF)
    {
        ; // Wait till transition completed
    }	
		
		if (dwMRCFREQChgFlg == 0)
		{
				M32(MCU_MRCFREQ_ADDR) = MRCFREQ_KEY | s_udwClockUserVal[MRCFREQ];
		}
		
		if (dwMREFREQChgFlg == 0)
		{
				M32(MCU_MREFREQ_ADDR) = MREFREQ_KEY | s_udwClockUserVal[MREFREQ];
		}
		
		M8(MCU_MRCPFB_ADDR) = s_udwClockUserVal[MRCPFB];
		M16(MCU_MRCPC1_ADDR) = s_udwClockUserVal[MRCPC1];
		M16(MCU_MRCBPROT1_ADDR) = s_udwClockUserVal[MRCBPROT1];
		M16(MCU_PRCR_ADDR) = KEYCODE_PRCR | (uword_t)s_udwClockUserVal[PRCR]; // Restore the protection status
		
    return (0); // OK
}

/**************************************************************************//**
* @details    Transition to P/E mode
* @param[in]  udw_addr: Write start address
* @param[out] udwp_adr_ret: Write start address after mask
* @retval    0 - OK,  1 - Failed
******************************************************************************/
static udword_t pe_mode_entry(udword_t udw_addr, udword_t *udwp_adr_ret)
{
    if (udw_addr < MCU_CONF_EXTRAMRAM_ADDR) // Code MRAM area
    {
				return (0); // OK
			
    } else // Extra MRAM area
    {
				M16(MCU_MENTRYR_ADDR) = MENTRYR_READ_MODE; // Transition to read mode
				while (M16(MCU_MENTRYR_ADDR) != MENTRYR_CHK_READ) // Wait until switching to read mode
				{
						;
				}
				
        M16(MCU_MENTRYR_ADDR) = MENTRYR_EMRAM_PROGRAM; // Transition to Extra MRAM Program mode
        *udwp_adr_ret = udw_addr & EMRAM_MASK_ADDR; // Set the write start address after mask
        while (M16(MCU_MENTRYR_ADDR) != MENTRYR_CHK_EMRAM) // Wait until switching to Extra MRAM Program mode
        {
            ;
        }
    }

    return (0); // OK
}


/**************************************************************************//**
* @details   Check for command locked and illegal status
* @param     None
* @retval    0 - OK,  1 - Failed
******************************************************************************/
static udword_t ilgl_chk(void)
{
    ubyte_t ubydata;
    udword_t udwdata;

    /** Check for command locked state */
    ubydata = M8(MCU_MASTAT_ADDR);
    if (ubydata & MASTAT_CMDLK)
    {
        return(1); // Failed
    }

    /** Check for illegal state */
    udwdata = M32(MCU_MSTATR_ADDR);
    if (udwdata & MSTATR_ILGLERR)
    {
        return(1); // Failed
    }

    return (0); // OK
}

/**************************************************************************//**
* @details   MRCFREQ register setting
* @param[in] udwp_clk:  Clock Frequency (Hz)
* @retval    None
******************************************************************************/
static void set_mrcfreq(udword_t *udwp_clk)
{
	
		ubyte_t ub_mhz_mrcfreq;
	
		ub_mhz_mrcfreq = (*udwp_clk) / 1000000; // Conver to MHz unit

    if ((*udwp_clk) % 1000000)
    {
        ub_mhz_mrcfreq += 1; // Round up the first decimal place in MHz
    }
		M32(MCU_MRCFREQ_ADDR) = MRCFREQ_KEY | ub_mhz_mrcfreq;
		
		while (M32(MCU_MRCFREQ_ADDR) != ub_mhz_mrcfreq);
}

/**************************************************************************//**
* @details   MREFREQ register setting
* @param[in] udwp_clk:  Clock Frequency (Hz)
* @retval    None
******************************************************************************/
static void set_mrefreq(udword_t *udwp_clk)
{
	
		ubyte_t ub_mhz_mrefreq;
	
		ub_mhz_mrefreq = (*udwp_clk) / 1000000; // Conver to MHz unit

    if ((*udwp_clk) % 1000000)
    {
        ub_mhz_mrefreq += 1; // Round up the first decimal place in MHz
    }
		M32(MCU_MREFREQ_ADDR) = MREFREQ_KEY | ub_mhz_mrefreq;
		
		while (M32(MCU_MREFREQ_ADDR) != ub_mhz_mrefreq);
}

/*
 *  Wait processing in us units
 *    Parameter:      us: wait time
 *    Return Value:   none
 *	  Note:			  Please note that the weight processing is less accurate.
 */
#pragma GCC push_options
#pragma GCC optimize ("O0")

static void Wait_us(udword_t us)
{
	udword_t dwCount;
	udword_t dwTotalNsPerCycle;

	dwTotalNsPerCycle = 1000000000 / dwICLKValue; // Calculate ns per cycle
	if (dwTotalNsPerCycle < 4)
	{
		// Fail-safe when exceeding 250Mhz
		dwTotalNsPerCycle = 4;	
	}
	// Calculates the number of cycles required to wait for the specified us unit time
	dwCount = (us * 1000) / dwTotalNsPerCycle;
	// Since it takes at least about 14 cycles to execute one for statement, the number of executions of it is calculated based on that.
	dwCount = dwCount / 14;

	if (dwCount == 0)
	{
		// Fail-safe if dwCount is below 14
		dwCount = 1;	
	}

	for ( ; dwCount > 0; dwCount--)
	{
		__asm("NOP");
  }
}
#pragma GCC pop_options

/**************************************************************************//**
* @details   Read the data written in the flash
* @param[in] udw_addr:  Read start address
* @param[in] udw_sz:   	Read size
* @param[in] ubp_buf:   Buffer to store read data
* @retval    None
******************************************************************************/
static void read_page(udword_t udw_addr, udword_t udw_sz, ubyte_t *ubp_buf)
{

    uword_t i;

    for (i = 0; i < udw_sz ; i++)
    {
        ubp_buf[i] = M8(udw_addr + i); // Store read data byte by byte
    }

}