/**
*   @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_adc_boundary_monitor_test.c
 *  @version    1.0
 *  @brief      This file has the implementation for the ADC Boundary 
 *		        monitor test diagnostics. @see diag_adc.h
 *
 *************************************************************************/

#include <stddef.h>
#include <stdint.h>
#include "diag.h"
#include "diag_adc.h"
#include "diag_adc_shared.h"

#define DIAG_ADC_BOUNDARY_MODE_SET_VALUE 0x00020000U
#define DIAG_ADC_BOUNDARY_MODE_SET_MASK 0xFFF8FFFFU


static void DIAG_ADC_SetBoundaryValues(DIAG_ADC_CHANNELS adcChannelNumber, 
        uint32_t boundaryValueLow, uint32_t boundaryValueHigh)
{
    volatile uint32_t *ptrBoundaryLow;
    volatile uint32_t *ptrBoundaryHigh;
    volatile uint32_t *ptrBoundaryModeSet;
    uint32_t instance = DIAG_ADC_GetADCInstance(adcChannelNumber);
    uint32_t channelNumber = DIAG_ADC_GetADCChannel(adcChannelNumber);
    
    //Assign comparator low value
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrBoundaryLow = (uint32_t *)((baseAddress.chLowCompReg + nextADCInstanceAddress [instance])
        +  ((channelNumber) * ((uint32_t)DIAG_ADC_NEXT_CHANNEL_ADDRESS)));
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrBoundaryLow = boundaryValueLow;

    //Assign comparator high value
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrBoundaryHigh = (uint32_t *)((baseAddress.chHighCompReg + nextADCInstanceAddress [instance])
        +  ((channelNumber) * ((uint32_t)DIAG_ADC_NEXT_CHANNEL_ADDRESS)));
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrBoundaryHigh = boundaryValueHigh;
    
    //Set comparator mode to within bounds
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrBoundaryModeSet = (uint32_t *)((baseAddress.chControl1Reg + nextADCInstanceAddress [instance])
        +  ((channelNumber) * ((uint32_t)DIAG_ADC_NEXT_CHANNEL_ADDRESS)));
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    *ptrBoundaryModeSet = (*ptrBoundaryModeSet & DIAG_ADC_BOUNDARY_MODE_SET_MASK);
    *ptrBoundaryModeSet = (*ptrBoundaryModeSet | DIAG_ADC_BOUNDARY_MODE_SET_VALUE);
}

static uint32_t DIAG_ADC_IsOutofBounds(DIAG_ADC_CHANNELS adcChannelNumber)
{
    volatile uint32_t *ptrOutOfBound;
    uint32_t result;
    uint32_t instance = DIAG_ADC_GetADCInstance(adcChannelNumber);
    uint32_t channelNumber = DIAG_ADC_GetADCChannel(adcChannelNumber);
    
    //Get out of bounds results
    /*LDRA_EXCLUDE 440 S <justification start> Accessing SFR address through pointers, hence violation ignored <justification end> */
    ptrOutOfBound = (uint32_t *)(baseAddress.compStatReg + nextADCInstanceAddress [instance]);
    /*LDRA_EXCLUDE 45 D <justification start> Pointer to sfr register, address assigned from lookup table and not NULL. Violation ignored <justification end>*/
    result = *ptrOutOfBound;
    result = ((result) & ((uint32_t)(0x00000001U) << channelNumber));

    return result;
}
    
uint32_t DIAG_ADC_BoundaryMonitorTest(DIAG_ADC_CHANNELS adcChannelNumber, 
        uint32_t boundaryValueLow, uint32_t boundaryValueHigh)
{
    uint32_t status;
    uint32_t outOfBound;
    
    if(DIAG_ADC_IsChannelValid(adcChannelNumber) == DIAG_ADC_VALID_CHANNEL)
	{            
        DIAG_ADC_StoreRegisters(adcChannelNumber);
        
        DIAG_ADC_SetBoundaryValues(adcChannelNumber, 
                        boundaryValueLow, boundaryValueHigh);

        //Initiate ADC conversion
        (void)DIAG_ADC_TriggerConversionAndGetResult(adcChannelNumber);
        
        //Get out of bounds status
        outOfBound = DIAG_ADC_IsOutofBounds(adcChannelNumber);
        
        DIAG_ADC_RestoreRegisters(adcChannelNumber);

        if(outOfBound > 0U)
        {
            //Indicates the results were within the bounds
            status = DIAG_PASS;
        }
        else
        {
            status = DIAG_FAIL;
        }
    }
    else
    {
        status = DIAG_INVALID_PARAM;
    }
    return status;
}
