/*******************************************************************************
* Freescale Semiconductor Inc.
* (c) Copyright 2011 Freescale Semiconductor, Inc.
* ALL RIGHTS RESERVED.
********************************************************************************
Services performed by FREESCALE in this matter are performed AS IS and without
any warranty. CUSTOMER retains the final decision relative to the total design
and functionality of the end product. FREESCALE neither guarantees nor will be
held liable by CUSTOMER for the success of this project.
FREESCALE DISCLAIMS ALL WARRANTIES, EXPRESSED, IMPLIED OR STATUTORY INCLUDING,
BUT NOT LIMITED TO, IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
A PARTICULAR PURPOSE ON ANY HARDWARE, SOFTWARE ORE ADVISE SUPPLIED 
TO THE PROJECT BY FREESCALE, AND OR NAY PRODUCT RESULTING FROM FREESCALE 
SERVICES. IN NO EVENT SHALL FREESCALE BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL 
DAMAGES ARISING OUT OF THIS AGREEMENT.
CUSTOMER agrees to hold FREESCALE harmless against any and all claims demands 
or actions by anyone on account of any damage, or injury, whether commercial,
contractual, or tortuous, rising directly or indirectly as a result 
of the advise or assistance supplied CUSTOMER in connection with product, 
services or goods supplied under this Agreement.
********************************************************************************
* File:             main.c
* Owner:            b05111
* Version:          0.1
* Date:             Aug-21-2014
* Classification:   General Business Information
* Brief:            MPC5643L 2b RAM / 2b FLASH ECC error injection example
********************************************************************************
* Detailed Description: 
* Purpose of the example is to show how to generate Multi bit ECC error in
* internal SRAM or FLASH (user must choose it in the option at the end of main
* function) and how to handle this error with respect to constraints given by 
* MPC5643L architecture (ECSM/RGM/FCCU relation and ECC error handling through 
* reset). The example is only possible to run in internal_FLASH target. Power-
* -on-reset is required after downloading the code into MCU's flash. The example
* displays notices in the terminal window (setting specified below). No other 
* external connection is required. 
* ------------------------------------------------------------------------------
* Test HW:         xPC564xLKIT, PPC5643L Cut3 silicon
* Target :         internal_FLASH
* Fsys:            120 MHz PLL0
* Debugger:        Lauterbach Trace32
*                  PeMicro USB-ML-PPCNEXUS
* Terminal:        19200-8-no parity-1 stop bit-no flow control via LINFlex0
* EVB connection:  default 
*
********************************************************************************
Revision History:
0.0     Aug-21-2014  b05111  Initial Version
0.1     Sep-22-2014  b05111  IVOR1_handler changed, minor edits
0.2     Sep-25-2014  b05111  Added e200z4_SPR_read_write_macros.h
*******************************************************************************/
#include <stdio.h>
#include "MPC5643L.h"
#include "e200z4_SPR_read_write_macros.h"
#include "uart.h"
#include "IntcInterrupts.h"
#include "Exceptions.h"

/*******************************************************************************
* External objects
*******************************************************************************/

/*******************************************************************************
* Global variables
*******************************************************************************/

// debugging variables
vint32_t critical_fault_count = 0;
vint32_t noncritical_fault_count = 0;
vint32_t safe_mode_count = 0;
vint32_t ecc_2b_handler_pass_count = 0;
vint32_t ecc_1b_handler_pass_count = 0;
vint32_t NMI_handler_pass_count = 0;
vint32_t IVOR1_handler_pass_count = 0;

// RESET reasons
vuint16_t RGM_FES_reg = 0;
vuint16_t RGM_DES_reg = 0;
    
// ECC Addresses
vuint32_t PFEAR_reg = 0;
vuint32_t PREAR_reg = 0;
    
// counter
vuint32_t i;
 
// dummy variable
vuint32_t dummy;

/*******************************************************************************
* Constants and macros
*******************************************************************************/

/* FCCU keys */
#define FCCU_NCFK_KEY 0xAB3498FE
#define FCCU_CFK_KEY  0x618B7A50

/* CTRLK register OP-keys for FCCU configuration */
#define  KEY_OP1 0x913756AF
#define  KEY_OP2 0x825A132B
#define KEY_OP16 0x7ACB32F0
#define KEY_OP31 0x29AF8752

/* PCR numbers */
#define PD4_pin 52
#define PD5_pin 53  
#define PD6_pin 54
#define PB6_pin 22
#define LED1_pin    PD4_pin
#define LED2_pin    PD5_pin
#define LED3_pin    PD6_pin
#define CLKOUT_pin  PB6_pin

/* ECSM */
#define ECSM_ECR_EFNCR_MASK     0x01
#define ECSM_ECR_ERNCR_MASK     0x02
#define ECSM_ECR_EF1BR_MASK		0x10
#define ECSM_ECR_ER1BR_MASK     0x20

// Force RAM Continuous Noncorrectable Data Inversions
#define ECSM_EEGR_FRCNCI_MASK   0x0200
// Force RAM One Noncorrectable Data Inversions
#define ECSM_EEGR_FR1NCI_MASK   0x0100
// Force RAM Continuous 1-Bit Data Inversions
#define ECSM_EEGR_FRC1BI_MASK   0x2000
// Force RAM One 1-bit Data Inversion
#define ECSM_EEGR_FR11BI_MASK   0x1000

// remame ECSM module to MCM (Miscellaneous Control Module)
#define ECSM SPP_MCM


/*******************************************************************************
* Local types
*******************************************************************************/

/*******************************************************************************
* Local function prototypes
*******************************************************************************/
static void HW_init(void);
static void SysClk_init(void);
static void ModesAndClks_init(void);
static void PeriClkGen_init(void);
static void DisableWatchdog(void);
static void ClearNCF(void);
static void ClearCF(void);
static void ClearFails(void);
static void ECSM_init(void);
static void FCCU_Alarm_Interrupt(void);
static void ME_Safe_Mode_Interrupt(void);
void IVOR1_handler(void);
static asm void Increment_MCSRR0_to_next_instruction(void);
static asm void ClearMCSR(void);
static void NMI_handler(void);
static asm void ClearMSR_EE(void);
static asm void SetMSR_ME(void);
static void Generate_noncorrectable_RAM_ECC_error(void);

// load to RAM if FLASH target chosen
#ifdef ROM_VERSION
    #pragma push
    #pragma section code_type ".my_ram" ".my_ram" code_mode=far_abs
#endif
    static void Generate_noncorrectable_FLASH_ECC_error(void);
    static void Erase_Flash_Block_H1(void);
#ifdef ROM_VERSION
    #pragma pop
#endif

// function called from relocated function defined with far absolute mode
// this is workaround for CW2.10 where option 'tune relocation' does not work
#ifdef ROM_VERSION
    #pragma push
    #pragma section code_type ".text_vle" data_mode=far_abs code_mode=far_abs
#endif    
    static void display_notice(void);
#ifdef ROM_VERSION
    #pragma pop
#endif


/*******************************************************************************
* Local variables
*******************************************************************************/

/* align this to a 0-modulo-8 address (aligned to 8 bytes) */
static vuint32_t RAM_test[2] __attribute__ ((aligned (8))) = 
{
    0xBABADEDA,
    0xCAFEBEEF
};

static vuint32_t RAM_test_read = 0;


#pragma push // save the current state
#pragma section const_type ".my_flash"
/* align this to a 0-modulo-8 address (aligned to 8 bytes) */
static const vuint32_t FLASH_test[] __attribute__ ((aligned (8))) = 
{
    0xAAAAAAAA,
    0xBBBBBBBB,
    0xCCCCCCCC,
    0xDDDDDDDD
};
#pragma pop
// Note: 
// at default constant smaller then 8 bytes are placed into .sdata and .sdata2
// bigger data are placed into .rodata (that is flash memory)

const vuint32_t *p_FLASH_test = &FLASH_test[0];
vuint32_t FLASH_test_read;

static vuint32_t data_to_be_written[] = 
{
    0x00450000, 0x00000000,
    0x00580000, 0x00000000   
};

static vuint32_t size_of_FLASH_test = (sizeof(FLASH_test)); // how many bytes is needed 


/*******************************************************************************
Function Name : HW_init
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : initialization of the hw for the purposes of this example
Issues        : 
*******************************************************************************/
static void HW_init(void)
{
    DisableWatchdog();   
    ClearFails();
    SysClk_init();       
}


/*******************************************************************************
Function Name : SysClk_init
Engineer      : b08110
Date          : Feb-25-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void SysClk_init(void)
{
    ModesAndClks_init();
    PeriClkGen_init();
}


/*******************************************************************************
Function Name : ModesAndClks_init
Engineer      : b08110
Date          : Aug-26-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : - DRUN->RUN1->RUN0->RUN1
                - enable CLKOUT
Issues        : 
*******************************************************************************/
static void ModesAndClks_init(void) 
{
    int32_t cnt = 0;
    
    /* Enable DRUN, RUN0, RUN1 SAFE, RESET modes */
    ME.MER.R = 0x0000003D;
    
    /* Mode Transition to enter RUN1 mode: */
        
    // RUN1 cfg: 16MHzIRCON,OSC0ON,PLL0OFF,PLL1OFF,syclk=16MIRC
    ME.RUN[1].R = 0x001F0030;
    // Enter RUN1 Mode & Key
    ME.MCTL.R = 0x50005AF0;
    // Enter RUN1 Mode & Inverted Key
    ME.MCTL.R = 0x5000A50F;
    // Wait for mode entry to complete
    while(0 == ME.GS.B.S_XOSC) {};
    // Wait for mode transition to complete
    while(1 == ME.GS.B.S_MTRANS) {}
    // Check RUN1 mode has been entered
    while(5 != ME.GS.B.S_CURRENT_MODE) {};
    
    
    /* Initialize PLL before turning it on:
       fsys = fcrystal*ndiv/idf/odf
       fvco must be from 256MHz to 512MHz
       we want fsys = 120MHz. fvco = fsys*odf = 120MHz * 4 = 480MHz
       fsys = 40*72/6/4 = 120MHz */

    CGM.AC3SC.R = 0x01000000; // Select Xosc as PLL0 source clock
    CGM.AC4SC.R = 0x01000000; // Select Xosc as PLL1 source clock

#if 0
    /* 80 MHz */
    CGM.FMPLL[0].CR.B.IDF = 0x7; // FMPLL0 IDF=5 --> divide by 8
    CGM.FMPLL[0].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[0].CR.B.NDIV = 64; // FMPLL0 NDIV=72 --> divide by 64
    
    /* 120 MHz */
    CGM.FMPLL[1].CR.B.IDF = 0x5; // FMPLL0 IDF=5 --> divide by 8
    CGM.FMPLL[1].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[1].CR.B.NDIV = 72; // FMPLL0 NDIV=72 --> divide by 64
#else
    /* 120 MHz */
    CGM.FMPLL[0].CR.B.IDF = 0x5; // FMPLL0 IDF=5 --> divide by 6
    CGM.FMPLL[0].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[0].CR.B.NDIV = 72; // FMPLL0 NDIV=72 --> divide by 72
    
    /* 80 MHz */
    CGM.FMPLL[1].CR.B.IDF = 0x7; // FMPLL0 IDF=5 --> divide by 8
    CGM.FMPLL[1].CR.B.ODF = 0x1; // FMPLL0 ODF=1 --> divide by 4
    CGM.FMPLL[1].CR.B.NDIV = 64; // FMPLL0 NDIV=72 --> divide by 64
#endif

    CGM.FMPLL[0].CR.B.EN_PLL_SW = 1; // enable progressive clock switching
    CGM.FMPLL[1].CR.B.EN_PLL_SW = 1; // enable progressive clock switching
    
    ME.RUNPC[0].R = 0x000000FE; // enable peripherals run in all modes
    ME.LPPC[0].R = 0x00000000;  // disable peripherals run in LP modes

#if 0     
    /* Mode Transition to enter RUN0 mode: */
    
    // RUN0 cfg: 16MHzIRCON,OSC0ON,syclk=OSC
    ME.RUN[0].R = 0x001F0032;       
    // Enter RUN0 Mode & Key
    ME.MCTL.R = 0x40005AF0;         
    // Enter RUN0 Mode & Inverted Key
    ME.MCTL.R = 0x4000A50F;         
    // Wait for mode transition to complete 
    while (ME.IS.B.I_MTC != 1) {}     
    // Clear Transition flag  
    ME.IS.R = 0x00000001;           
#endif
    
    /* Mode Transition to enter RUN0 mode: */
    
    // RUN0 cfg: 16MHzIRCON,OSC0ON,PLL0ON,PLL1ON,syclk=16M IRC
    ME.RUN[0].R = 0x001F00F0;
    // Enter RUN0 Mode & Key
    ME.MCTL.R = 0x40005AF0;
    // Enter RUN0 Mode & Inverted Key
    ME.MCTL.R = 0x4000A50F;
    // Wait for mode transition to complete
    while (1 == ME.GS.B.S_MTRANS) {}
    // Check RUN0 mode has been entered
    while(4 != ME.GS.B.S_CURRENT_MODE) {};
    
    /* Mode Transition to enter RUN1 mode: */
    
    // RUN1 cfg: 16MHzIRCON,OSC0ON,PLL0ON,PLL1ON,syclk=PLL0
    ME.RUN[1].R = 0x001F00F4;
    // Enter RUN1 Mode & Key
    ME.MCTL.R = 0x50005AF0;
    // Enter RUN1 Mode & Inverted Key
    ME.MCTL.R = 0x5000A50F;
    // Wait for mode transition to complete
    while(1 == ME.GS.B.S_MTRANS) {}
    // Check RUN1 mode has been entered
    while(5 != ME.GS.B.S_CURRENT_MODE) {};
    
    /* enable CLKOUT on PB6 */
    SIU.PCR[CLKOUT_pin].R = 0x0600; // ALT1 - PCR[22] - PA = 0b01
    
    /* set CLKOUT divider of 8 */
    CGM.OCDS_SC.R = 0x32000000; // div by 8, system FMPLL, cut2
    CGM.OCEN.B.EN = 1; 			// enable CLKOUT signal
}


/*******************************************************************************
Function Name : PeriClkGen_init
Engineer      : b08110
Date          : Apr-14-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : - Enable auxiliary clock 0-3. from PLL0
Issues        : 
*******************************************************************************/
static void PeriClkGen_init(void)
{
    CGM.AC0SC.R = 0x04000000;  /* MPC56xxP: Select PLL0 for aux clk 0  */
    CGM.AC0DC.R = 0x80800000;  /* MPC56xxP: Enable aux clk 0 div by 1 */
    CGM.AC1SC.R = 0x04000000;  /* MPC56xxP: Select PLL0 for aux clk 1  */
    CGM.AC1DC.R = 0x80000000;  /* MPC56xxP: Enable aux clk 1 div by 1 */
    CGM.AC2SC.R = 0x04000000;  /* MPC56xxP: Select PLL0 for aux clk 2  */
    CGM.AC2DC.R = 0x80000000;  /* MPC56xxP: Enable aux clk 2 div by 1 */
}


/*******************************************************************************
Function Name : DisableWatchdog
Engineer      : b08110
Date          : Jun-23-2010
Parameters    : 
Modifies      : 
Returns       : 
Notes         : - disable SWT functionality
Issues        : 
*******************************************************************************/
static void DisableWatchdog(void)
{
    SWT.SR.R = 0x0000c520;     /* Write keys to clear soft lock bit */
    SWT.SR.R = 0x0000d928; 
    SWT.CR.R = 0x8000010A;     /* Clear watchdog enable (WEN) */
    
    /* e200 Core Watchdog Timer */
    asm
    {
        li  r3, 0
        mtspr   340, r3
    }
}


/*******************************************************************************
Function Name : ClearNCF
Engineer      : b08110
Date          : Apr-08-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void ClearNCF(void)
{
    uint32_t i,b[4];
    for(i=0;i<4;i++)
    {
        FCCU.NCFK.R = FCCU_NCFK_KEY;
        FCCU.NCF_S[i].R = 0xFFFFFFFF;
        while(FCCU.CTRL.B.OPS != 0x3)
        {
        
        };              /* wait for the completion of the operation */
        b[i]=FCCU.NCF_S[i].R;
    }
}


/*******************************************************************************
Function Name : ClearCF
Engineer      : b08110
Date          : Apr-08-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void ClearCF(void)
{
    uint32_t i,a[4];
    for(i=0;i<4;i++)
    {
        FCCU.CFK.R = FCCU_CFK_KEY;
   
        FCCU.CF_S[i].R = 0xFFFFFFFF;
  
        while(FCCU.CTRL.B.OPS != 0x3)
        {
            
        };      /* wait for the completion of the operation */

        a[i]=FCCU.CF_S[i].R;
    }
}


/*******************************************************************************
Function Name : ClearFails
Engineer      : b08110
Date          : Oct-26-2011
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void ClearFails(void)
{
    uint16_t reset_register;
    
    if(RGM.FES.B.F_FCCU_SAFE /*|| RGM.FES.B.F_FCCU_HARD*/)
    {
        reset_register = RGM.FES.R;
        ME.IMTS.R = 0x00000001;
        ClearCF();
        ClearNCF();
        
        RGM.FES.R = 0xFFFF;
        RGM.DES.R = 0xFFFF;
        
        /* re-enter DRUN */
        ME.MCTL.R = 0x30005AF0; /* Enter DRUN Mode & Key */        
        ME.MCTL.R = 0x3000A50F; /* Enter DRUN Mode & Inverted Key */
    }
}


/*******************************************************************************
Function Name : ECSM_init
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : initialization of the ECSM module
Issues        : 
*******************************************************************************/
static void ECSM_init(void)
{
 
    /* enable RAM ECC error reporting */
    ECSM.ECR.R = (uint8_t) ECSM_ECR_EFNCR_MASK | // non-correctable FLASH error
                           ECSM_ECR_ERNCR_MASK ; // non-correctable RAM error                          

}


/*******************************************************************************
Function Name : FCCU_Alarm_Interrupt
Engineer      : b05111
Date          : Apr-12-2012
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void FCCU_Alarm_Interrupt(void)
{
   noncritical_fault_count++;
   ClearNCF();    
}


/*******************************************************************************
Function Name : ME_Safe_Mode_Interrupt
Engineer      : b05111
Date          : Apr-12-2012
Parameters    : 
Modifies      : 
Returns       : 
Notes         : 
Issues        : 
*******************************************************************************/
static void ME_Safe_Mode_Interrupt(void)
{
    #define FES_F_FCCU_SAFE 0x0080
    #define ME_IS_I_SAFE 0x00000002
    
    safe_mode_count++;  
    
    /* clear functional event in RGM (safe mode) */
    RGM.FES.R = FES_F_FCCU_SAFE;
  
    /* clear interrupt flag in ME */
    ME.IS.R = ME_IS_I_SAFE;

    /* configure DRUN same way as RUN1 */
    ME.DRUN_MC.R = 0x001F00F4;
    
    /* select DRUN */          
    ME.MCTL.R = 0x30005AF0;         
    ME.MCTL.R = 0x3000A50F;               
    
    while (1 == ME.GS.B.S_MTRANS) {} // Wait for mode transition to complete
    while (3 != ME.GS.B.S_CURRENT_MODE) {};    
    
    /* Mode Transition to enter RUN1 mode: */
    // Enter RUN1 Mode & Key
    ME.MCTL.R = 0x50005AF0;
    // Enter RUN1 Mode & Inverted Key
    ME.MCTL.R = 0x5000A50F;
    // Wait for mode transition to complete
    while (1 == ME.GS.B.S_MTRANS) {}
    // Check RUN1 mode has been entered
    while (5 != ME.GS.B.S_CURRENT_MODE) {};
  
  
    // get FCCU back to normal state
	#define  KEY_OP2 0x825A132B
    FCCU.CTRLK.R = KEY_OP2;
    FCCU.CTRL.R = 2;         
}


/*******************************************************************************
Function Name : IVOR1_handler
Engineer      : b05111
Date          : 
Parameters    : 
Modifies      : 
Returns       : 
Notes         : Function handles IVOR1 exception.        
Issues        : For z4 and z7:
                -  __declspec(interrupt machine)
                - mcsrr0 register increment
                For z0, z1, z3, z6:
                - __declspec(interrupt critical)
                - csrr0 register increment
*******************************************************************************/
#pragma push /* Save the current state */
#pragma force_active on
#pragma function_align 16 /* We use 16 bytes alignment for Exception handlers */
__declspec(interrupt machine)
void IVOR1_handler(void)
{

    NMI_handler();
    
    Increment_MCSRR0_to_next_instruction();
    
    ClearMCSR();
    
    IVOR1_handler_pass_count++;

}
#pragma force_active off
#pragma pop


/*******************************************************************************
Function Name : Increment_MCSRR0_to_next_instruction
Engineer      : b05111
Date          : Sep-19-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : - based on algorithm described in AN4648
                - applicable for IVOR1 (stores address to MCSRR0)                              
Issues        : 
*******************************************************************************/
#if VLE_IS_ON // for VLE code 
static asm void Increment_MCSRR0_to_next_instruction(void)
{
    /* prolog */
    fralloc
    
    /* MCSRR0->r5 */
    mfmcsrr0 r5
    
    /* determine opcode @ MCSRR0 */
    se_lhz r4,0(r5)
    
    /* check bit 31,28 only*/
    e_andi. r3,r4,0x9000
    e_cmpli 0x0,r3,0x1000 
    
    e_bne __machine_check_adjust_for_16bit_opcode
    
        /* 0xx1 => 32 bit*/
        se_addi r5,2
    
    __machine_check_adjust_for_16bit_opcode:
    
        /* all others just 16 bit long*/
        se_addi r5,2 
        
        /* save adjusted return address*/
        mtmcsrr0 r5 
    
    
    /* epilog */
    frfree
    blr   
}
#else //BookE
static asm void Increment_MCSRR0_to_next_instruction(void)
{
    /* prolog */
    fralloc
    
    /* MCSRR0->r5 */
    mfmcsrr0 r5
    
    /* for BookE, all instructions are 32 bit long */
    se_addi r5,4
        
    /* save adjusted return address*/
    mtmcsrr0 r5 
    
    /* epilog */
    frfree
    blr 
}
#endif


/*******************************************************************************
Function Name : ClearMCSR
Engineer      : b05111
Date          : Sep-19-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : When MSR[ME] is set, error flags must be cleared to prevent 
                machine check exception to recall.                                 
Issues        : 
*******************************************************************************/
static asm void ClearMCSR(void)
{
    /* prolog */
    fralloc
    
    /* load mask */   
    lis r4, 0xFFFF
    addi r4, r4, 0xFFFF
    
    /* Clear MCSR */
    mtmcsr r4
    
    /* epilog */
    frfree
    blr   
}

/*******************************************************************************
Function Name : NMI_handler
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : Function handles IVOR1 exception.        
Issues        : 
*******************************************************************************/
static void NMI_handler(void)
{
    // must be static local or global variable
    static vuint32_t address_capture;    // MCAR
    static vuint32_t syndrome_register;  // MCSR
    
    READ_FROM_SPR(SPR_MCAR,address_capture);
    READ_FROM_SPR(SPR_MCSR,syndrome_register);
      
    printf("Multibit ECC error found on address 0x%X\r\n", address_capture);
    
    NMI_handler_pass_count++;
    critical_fault_count++;
    ClearCF();
}

/*******************************************************************************
Function Name : ClearMSR_EE
Engineer      : b05111
Date          : Sep-19-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : Negates the MSR[EE].                                   
Issues        : 
*******************************************************************************/
static asm void ClearMSR_EE(void)
{
    /* prolog */
    fralloc
    
    /* read spr MSR */
    mfmsr r3  
    
    /* load mask to negate the EE bit */   
    lis r4, 0xFFFF
    addi r4, r4, 0x7FFF
    
    /* clear EE bit */
    and r3, r3, r4
    
    /* write back to MSR */
    mtmsr r3
    
    /* epilog */
    frfree
    blr   
}


/*******************************************************************************
Function Name : SetMSR_ME
Engineer      : b05111
Date          : Sep-19-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : Asserts the MSR[ME].                                   
Issues        : 
*******************************************************************************/
static asm void SetMSR_ME(void)
{
    /* prolog */
    fralloc
    
    /* read spr MSR */
    mfmsr r3
    
    /* load mask to assert the ME bit */   
    lis r4, 0x0000
    addi r4, r4, 0x1000
    
    /* set ME bit */
    or r3, r3, r4
    
    /* write back to MSR */
    mtmsr r3
    
    /* epilog */
    frfree
    blr   
}


/*******************************************************************************
Function Name : Generate_noncorrectable_RAM_ECC_error
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         : generates 2-bit ECC error in RAM using the ECSM_EEGR register
Issues        : ECC is being calculated above 64-bit word. RAM[0] of the odd 
                bank means bit 0, RAM[0] of the even bank means bit 32
*******************************************************************************/
static void Generate_noncorrectable_RAM_ECC_error(void)
{
       
    /* Force internal SRAM one non-correctable data error */
    ECSM.EEGR.R = (uint16_t)(ECSM_EEGR_FR1NCI_MASK | 32);
    
    /* write to internal SRAM - this generates the wrong data */
    RAM_test[0] = 0xCAFEBEEF;

    printf("SRAM 2b ECC error injected\r\n");
    printf("Now check read of affected area\r\n");
    printf("\r\n");
        
    /* now here the ECC is checked and non-correctable error found */
    RAM_test_read = RAM_test[0];  // error caused by read
    RAM_test_read = RAM_test[1];
}


/*******************************************************************************
Function Name : Generate_noncorrectable_FLASH_ECC_error
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         :                          
Issues        : - section my_flash is defined in linker file with range
                  0x000C0000-0x000FFFFF thus it fits to block H1
                - need to be executed be executed from RAM  
*******************************************************************************/
#ifdef ROM_VERSION
#pragma push
#pragma section code_type ".my_ram" ".my_ram" code_mode=far_abs
#endif
static void Generate_noncorrectable_FLASH_ECC_error(void)
{
    #define FLASH_HLR_PASSWORD      0xB2B22222  
    
    /* enable high address space */	
    CFLASH.HBL.R = FLASH_HLR_PASSWORD; // unlock register
    CFLASH.HBL.R = 0x00000000;         // unlock all high blocks

    /* step1. erase H1 (0xC0000-0xFFFFF) */
    CFLASH.MCR.B.ERS = 1;
    CFLASH.HBS.R = 0x00000002;               // select H1 block
    *((unsigned int*) p_FLASH_test) = 0xFFFFFFFF;  // interlock write 
    CFLASH.MCR.B.EHV = 1;                    // start the erase operation
    while(CFLASH.MCR.B.DONE == 0){};         // wait for DONE
    CFLASH.MCR.B.EHV = 0;
    CFLASH.MCR.B.ERS = 0;

    /* step2. program data */
    CFLASH.MCR.B.PGM = 1;              
    *((unsigned int*) p_FLASH_test++) = data_to_be_written[0]; // first write
    *((unsigned int*) p_FLASH_test++) = data_to_be_written[1]; // additional write
    CFLASH.MCR.B.EHV = 1;                                //start the erase operation
    while(CFLASH.MCR.B.DONE == 0){}; //wait for DONE
    CFLASH.MCR.B.EHV = 0;
    CFLASH.MCR.B.PGM = 0;
    
    /* reinitialize pointer */
    p_FLASH_test = &FLASH_test[0];
    
    /* step3. over-program data - this generates ECC error */
    CFLASH.MCR.B.PGM = 1;              
    *((unsigned int*) p_FLASH_test++) = data_to_be_written[2]; // first write
    *((unsigned int*) p_FLASH_test++) = data_to_be_written[3]; // additional write
    CFLASH.MCR.B.EHV = 1;                                //start the erase operation
    while(CFLASH.MCR.B.DONE == 0){}; //wait for DONE
    CFLASH.MCR.B.EHV = 0;
    CFLASH.MCR.B.PGM = 0;
    
    display_notice();

    /* now here the ECC is checked and non-correctable error found */
    FLASH_test_read = FLASH_test[0];  // error caused by read
    FLASH_test_read = FLASH_test[1];
}


/*******************************************************************************
Function Name : Erase_Flash_Block_H1
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         :                          
Issues        : need to be executed be executed from RAM 
*******************************************************************************/
static void Erase_Flash_Block_H1(void)
{
    #define FLASH_HLR_PASSWORD      0xB2B22222  
    
    /* enable high address space */	
    CFLASH.HBL.R = FLASH_HLR_PASSWORD; // unlock register
    CFLASH.HBL.R = 0x00000000;         // unlock all high blocks

    /* step1. erase H1 (0xC0000-0xFFFFF) */
    CFLASH.MCR.B.ERS = 1;
    CFLASH.HBS.R = 0x00000002;               // select H1 block
    *((unsigned int*) p_FLASH_test) = 0xFFFFFFFF;  // interlock write 
    CFLASH.MCR.B.EHV = 1;                    // start the erase operation
    while(CFLASH.MCR.B.DONE == 0){};         // wait for DONE
    CFLASH.MCR.B.EHV = 0;
    CFLASH.MCR.B.ERS = 0;
}

#ifdef ROM_VERSION
#pragma pop
#endif


/*******************************************************************************
Function Name : display_notice
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         :                          
Issues        : - This function are not supposed to be relocated to SRAM, but 
                  if called from SRAM, far absolute addresing mode must be used.
                  This would be solved by adding of branch island 
                  (Target Settings/EPPC Target/check 'Tune relocations')
                - CW210 errata: 'Tune relocations' option does not work properly
                  workaround is absolute addressing pragma (marked below) 
*******************************************************************************/
#ifdef ROM_VERSION
    #pragma push
    // workaround isntead of 'Tune relocations'
    #pragma section code_type ".text_vle" data_mode=far_abs code_mode=far_abs
#endif    
    static void display_notice(void)
    {
        printf("FLASH 2b ECC error injected\r\n");
        printf("Now check read of affected area\r\n");
        printf("\r\n");
    }
#ifdef ROM_VERSION
    #pragma pop
#endif


/*******************************************************************************
* Global functions
*******************************************************************************/

/*******************************************************************************
Function Name : main
Engineer      : b05111
Date          : Aug-21-2014
Parameters    : 
Modifies      : 
Returns       : 
Notes         :                          
Issues        : 
*******************************************************************************/
int32_t main(void) 
{
    #define RGM_FES_F_FL_ECC_RCC 0x0400
    
    #define FLASH_start  0x00000000
    #define FLASH_end    0x000FFFFF   
    
    /* Save RGM status register before initialization for subsequent usage */
    // Note: Multi-bit ECC also cause FCCU CF16 and CF17
    RGM_FES_reg = RGM.FES.R;
    RGM_DES_reg = RGM.DES.R;
    
    /* Initialize HW */
    HW_init(); 
    
    /* check ECC error */
    if(RGM_FES_reg & RGM_FES_F_FL_ECC_RCC) 
    {
        printf("Functional RESET due to Multi-Bit ECC error\r\n");  
        printf("Start flash scan...\r\n");
        
        /* Scan the content of flash memory */
        // Possible fault will lead in IVOR1
        // Shadow block omitted
        for (i=FLASH_start; i<FLASH_end; i=i+4)
        {
            dummy = (*(uint32_t *) i);
        }
        
        // check whether there was IVOR0 exception during scan
        if (NMI_handler_pass_count == 0)
        {
            printf("No FLASH ECC error has been found => \r\n");
            printf("ECC error was in SRAM and has already been "); 
            printf("corrected during SRAM init sequence\r\n");
            printf("The application can continue\r\n");
            printf("Have a nice day\r\n");
            
            // Loop forever
            for (;;) 
            {
                asm (nop);
            }
            
        }
        else
        {
            printf("Number of FLASH ECC error founded: ");
            printf("%d\r\n", critical_fault_count/2);
            printf("The application should be re-flashed\r\n");
            printf("\r\n");
            
            printf("Now erase block H1 as example of data correction\r\n");
            Erase_Flash_Block_H1();
            printf("Done...\r\n");
            printf("Have a nice day\r\n");
            
            // Loop forever
            for (;;) 
            {
                asm (nop);
            }
        }
         
    }
        
    /* unmask safe mode interrupt */
    ME.IM.R = 0x00000002; 
    
    /* set ME bit */
    SetMSR_ME();
    
    /* Install ISRs into the interrupt vector table */
    INTC_InstallINTCInterruptHandler(FCCU_Alarm_Interrupt, 250, 8);
    INTC_InstallINTCInterruptHandler(ME_Safe_Mode_Interrupt, 51, 7);
    
    
    /* lower current INTC priority to start INTC interrupts */
    INTC.CPR_PRC0.R = 0;
    
    /* Enable non-correctable RAM ECC error reporting*/
    ECSM_init();
    
    printf("ECC error injection example\r\n");
    printf("\r\n");
    printf("POR destructive RESET\r\n");
    printf("Application will inject 2b ECC error\r\n");
    printf("\r\n");
    
    
    #if 1
    /* I. configure time-out */
    FCCU.CFG_TO.B.TO = 0x7; // CONFIG timeout=8.192 ms

    /* II. enter config state */
    FCCU.CTRLK.R = KEY_OP1;
    FCCU.CTRL.R = 1; // OP1
    while(0b11 != FCCU.CTRL.B.OPS){}; // wait for operation to finish 
	
	/* Long functional reset for all sources */
    FCCU.CFS_CFG0.R=0xAAAAAAAA;
    FCCU.CFS_CFG1.R=0xAAAAAAAA;
    FCCU.CFS_CFG2.R=0xAAAAAAAA;
    FCCU.CFS_CFG3.R=0xAAAAAAAA;
    
    /* III. enter normal state */
    FCCU.CTRLK.R = KEY_OP2;
    FCCU.CTRL.R = 2; // OP2  
    
    #endif
       
    /**************************************************************************/
    /*                                                                        */
    /* Choose which error is supposed to be injected !                        */
    /*                                                                        */
    /**************************************************************************/
   
    #if 1
        Generate_noncorrectable_FLASH_ECC_error();
    #else
        Generate_noncorrectable_RAM_ECC_error();
    #endif
    
      
    /* Loop forever */
    for (;;) 
    {
        asm (nop);
    }

}



