/**
*   @copyright       © 2026 Microchip Technology Inc.
    Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
    PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
    BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
    FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
    ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
    THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS. 
*
*************************************************************************/

/**
*  @file        diag_cpu_self_test.c
*  @version     1.0
*  @brief       This file has the implementation for the GPIO
                Input practice diagnostic. @see diag_cpu.h
*
*************************************************************************/
/*Function headers*/
#include "diag_cpu.h"
#include "diag_cpu_shared.h"
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
uint32_t (*testSubset[DIAG_CPU_NUM_TEST_SUBSETS])(void) = {    
                                                          DIAG_CPU_SelfTest_Subset1,
                                                          DIAG_CPU_SelfTest_Subset2,
                                                          DIAG_CPU_SelfTest_Subset3,
                                                          DIAG_CPU_SelfTest_Subset4,
                                                          DIAG_CPU_SelfTest_Subset5,
                                                          DIAG_CPU_SelfTest_Subset6,
                                                          DIAG_CPU_SelfTest_Subset7,
                                                          DIAG_CPU_SelfTest_Subset8                                                          
                                                          };
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((address(0x004800))) DIAG_CPU_TestRam;
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((address(0x0048A0))) DIAG_CPU_TestRam1;
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((address(0x004A00))) DIAG_CPU_TestRam2;
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((address(0x004BA0))) DIAG_CPU_TestRam3;
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((address(0x004B00))) DIAG_CPU_TestRam4;
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((address(0x006200))) DIAG_CPU_TestRamY1;
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((address(0x006400))) DIAG_CPU_TestRamY2;
/*LDRA_EXCLUDE 27 D <justification start> variable used in assembly in another file, violation ignored <justification end> */
volatile uint32_t __attribute__((near)) DIAG_CPU_tempArray[12];

volatile uint32_t DIAG_CPU_TestResult;
static volatile uint32_t CPU_SelfTest_SubsetStatus;
static volatile uint16_t CPUSelfTestExecuteStatus;

/*LDRA_EXCLUDE 76 D <justification start> Function called from DIAG_CPU_SelfTest, violation ignored <justification end> */
/*LDRA_EXCLUDE 61 D <justification start> Static helper intentionally not exposed, violation ignored <justification end> */
/*LDRA_EXCLUDE 106 D <justification start> The function is already defined static, violation ignored <justification end> */
/*LDRA_EXCLUDE 553 S <justification start> The function is already defined static and hence no declaration, violation ignored <justification end> */
static uint32_t __attribute__((noinline, optimize(0))) DIAG_CPU_CallSubset(uint32_t index)
{
    return (*(testSubset + index))();
}
                                
/*LDRA_EXCLUDE 88 S <justification start> functionally implemented to test assembly instruction behavior, hence violation ignored <justification end> */    
uint32_t DIAG_CPU_SelfTest(DIAG_CPU_SELF_TEST_SUBSET executionMode)
{
    volatile uint32_t result;
    volatile uint32_t savedINTCON2 = 0x0000U; 
    volatile uint32_t SelfTest_PassFailStatus;  
    volatile uint32_t testCount = 0U;
    volatile uint32_t savedINTCON1 = 0x0000U;
    /* Save INTCON2 register and temporarily disable all interrupts */
    savedINTCON2 = INTCON2;
    savedINTCON1 = INTCON1;
    CPU_SelfTest_SubsetStatus = 0U;
    _GIE = 0;
     CPUSelfTestExecuteStatus = DIAG_CPU_TEST_EXECUTION_IN_PROGRESS;
    __asm__ volatile( "push w1\n\t"
                      "push w2\n\t"
                      "mov.l #_DIAG_CPU_tempArray, w1\n\t"
                      "mov.l #_DIAG_CPU_TestRam, w2\n\t"
                      "repeat #3\n\t"
                      "mov.l [w2++], [w1++]\n\t"
                      "mov.l #_DIAG_CPU_TestRam1, w2\n\t"
                      "mov.l [w2],[w1++]\n\t"
                      "mov.l #_DIAG_CPU_TestRam2, w2\n\t"
                      "mov.l [w2], [w1++]\n\t"
                      "mov.l #_DIAG_CPU_TestRam3, w2\n\t"
                      "mov.l [w2], [w1++]\n\t"
                      "mov.l #_DIAG_CPU_TestRam4, w2\n\t"
                      "mov.l [w2], [w1++]\n\t"
                      "mov.l  #_DIAG_CPU_TestRamY1, w2\n\t"
                      "mov.l  [w2], [w1++]\n\t"
                      "mov.l  #_DIAG_CPU_TestRamY2, w2\n\t" 
                      "repeat #2\n\t"
                      "mov.l  [w2++], [w1++]\n\t"
                      "pop w2\n\t"
                      "pop w1\n\t"
                      : /* no outputs */
                      : /* no inputs */ );
    
    DIAG_CPU_SetRetValue(DIAG_FAIL);
    if(executionMode == DIAG_CPU_SELF_TEST_ALL)
    {
        while(testCount < DIAG_CPU_NUM_TEST_SUBSETS) 
        {   
            SelfTest_PassFailStatus = DIAG_CPU_CallSubset(testCount);
            if(SelfTest_PassFailStatus == DIAG_FAIL)
            {
                /*If there is a failure only then the corresponding bit position
                 is updated to 1, else it is retained to 0*/
                CPU_SelfTest_SubsetStatus |= 1U << testCount;
            }
            testCount += 1U;
        }
        if(CPU_SelfTest_SubsetStatus == 0U)
        {
            DIAG_CPU_SetRetValue(DIAG_PASS);
        }
    }
    else if(executionMode == DIAG_CPU_SELF_TEST_SUBSET_INVALID)
    {
        DIAG_CPU_SetRetValue(DIAG_INVALID_PARAM);
    }
    else
    {
        SelfTest_PassFailStatus = DIAG_CPU_CallSubset((uint32_t)executionMode);
        DIAG_CPU_SetRetValue(SelfTest_PassFailStatus);
    }
    
    result = DIAG_CPU_GetRetValue();
    /*Restore*/
    __asm__ volatile ("push w1\n\t"
                      "push w2\n\t"
                      "mov.l #_DIAG_CPU_TestRam,w1\n\t"
                      "mov.l #_DIAG_CPU_tempArray,w2\n\t"
                      "repeat #3\n\t"
                      "mov.l [w2++],[w1++]\n\t"
                      "mov.l #_DIAG_CPU_TestRam1,w1\n\t"
                      "mov.l [w2++],[w1]\n\t"
                      "mov.l #_DIAG_CPU_TestRam2,w1\n\t"
                      "mov.l [w2++],[w1]\n\t"
                      "mov.l #_DIAG_CPU_TestRam3,w1\n\t"
                      "mov.l [w2++],[w1]\n\t"
                      "mov.l #_DIAG_CPU_TestRam4,w1\n\t"
                      "mov.l [w2++],[w1]\n\t"
                      "mov.l #_DIAG_CPU_TestRamY1,w1\n\t"
                      "mov.l [w2++],[w1]\n\t"
                      "mov.l #_DIAG_CPU_TestRamY2,w1\n\t"
                      "repeat #2\n\t"
                      "mov.l [w2++],[w1++]\n\t"  
                      "pop w2\n\t"
                      "pop w1\n\t"
                      : /* no outputs */
                      : /* no inputs */ );
    
    CPUSelfTestExecuteStatus = DIAG_CPU_TEST_EXECUTION_COMPLETE;
    INTCON1 = savedINTCON1;
    INTCON2 = savedINTCON2;
    return result;
/*LDRA_EXCLUDE 88 S <justification start> functionally implemented to test assembly instruction behavior, hence violation ignored <justification end> */    
}

uint32_t DIAG_CPU_SelfTest_GetStatus(void)
{
    /* Indicates the most recent pass/fail status of the specified test subset */
    return CPU_SelfTest_SubsetStatus; 
    /* For a particular test subset, a '1' indicates Fail and a '0' indicates a Pass */     
}

/* LDRA_EXCLUDE 65 D <justification start>Wrapper function used to wrap the assembly instructions. Violation Ignored.<justification end> */
void  __attribute__((optimize(0))) DIAG_CPU_CheckFlagMathError(void (*userMathError)())
{
    /*The userMathError function is an user defined function that can be called
     if the math error is caused by an application error. Since the handler is the
     same, there is no method to get the coverage for LDRA for this function*/
    /*LDRA_NOANALYSIS*/
    if(CPUSelfTestExecuteStatus == DIAG_CPU_TEST_EXECUTION_IN_PROGRESS)
    {
      DIAG_CPU_MathError();
    }
    else
    {
        (void)userMathError();
    }
    /*LDRA_ANALYSIS*/
 }
