//*****************************************************************************
//
//! @file am_vos_isr.c
//!
//! @brief GPIO, PDM, AUDADC, UART ISR and FreeRTOS wrapper.
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2024, Ambiq Micro, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision ambiqvos_r4.5-fdfa8cf6a4 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "am_vos_sys_config.h"
#include "am_vos_board_setup.h"

#include "am_vos_utils.h"

#include "am_vos_task.h"
#include "am_vos_init.h"
#include "am_vos_spp.h"

#if configUSE_BLE
#include "am_vos_ble.h"
#endif // configUSE_BLE

#if configUSE_RTT_RECORDER
#include "am_vos_rtt_recorder.h"
#endif // configUSE_RTT_RECORDER

#if configUSE_AAD
#include "am_devices_t5838.h"
#endif // configUSE_AAD

//*****************************************************************************
//
// Interrupt handler for the CTIMER module.
//
//*****************************************************************************
void
am_ctimer_isr(void)
{
    uint32_t ui32Status;

    am_hal_timer_interrupt_status_get(false, &ui32Status);
    am_hal_timer_interrupt_clear(ui32Status);

    // we don't have this function in new hal
    //  am_hal_ctimer_int_service(ui32Status);
}

//*****************************************************************************
//
// Interrupt handler for the GPIO pins.
//
//*****************************************************************************
void
am_gpio0_001f_isr(void)
{
//    am_hal_gpio_mask_t IntStatus;
    uint32_t    ui32IntStatus;

//    am_hal_gpio_interrupt_status_get(AM_HAL_GPIO_INT_CHANNEL_0,
//                                     false,
//                                     &IntStatus);

    //
    // Clear the GPIO Interrupt (write to clear).
    //
    AM_CRITICAL_BEGIN
    //am_hal_gpio_interrupt_irq_status_get(GPIO0_001F_IRQn, true, &ui32IntStatus);
    am_hal_gpio_interrupt_irq_status_get(GPIO0_001F_IRQn, false, &ui32IntStatus);
    am_hal_gpio_interrupt_irq_clear(GPIO0_001F_IRQn, ui32IntStatus);
    AM_CRITICAL_END

    am_hal_gpio_interrupt_service(GPIO0_001F_IRQn, ui32IntStatus);

}

//*****************************************************************************
//
// PDM Interrupt Service Routine (ISR)
//
//*****************************************************************************
#if USE_DMIC_PDM
#if USE_DMIC_MB0 || USE_DMIC_MB0_T5838
void am_pdm0_isr(void)
#elif USE_DMIC_MB1
void am_pdm2_isr(void)
#elif USE_DMIC_MB2
void am_pdm1_isr(void)
#endif
{
    uint32_t ui32Status;
    //bool bQueueReValue;

#if configUSE_SYSVIEWER
    traceISR_ENTER();
#endif // configUSE_SYSVIEWER

    //
    // Read the interrupt status.
    //
    am_hal_pdm_interrupt_status_get(g_sVosBrd.pvPDMHandle, &ui32Status, true);
    am_hal_pdm_interrupt_clear(g_sVosBrd.pvPDMHandle, ui32Status);

    // Test code for PDM wakeup time measurement
    //am_hal_gpio_state_write(LED_SYSTEM, AM_HAL_GPIO_OUTPUT_TOGGLE);
    //
    // Once our DMA transaction completes, we will disable the PDM and send a
    // flag back down to the main routine. Disabling the PDM is only necessary
    // because this example only implemented a single buffer for storing FFT
    // data. More complex programs could use a system of multiple buffers to
    // allow the CPU to run the FFT in one buffer while the DMA pulls PCM data
    // into another buffer.
    //
    am_hal_pdm_interrupt_service(g_sVosBrd.pvPDMHandle, ui32Status, &(g_sVosBrd.sPdmTransfer));

    if (ui32Status & AM_HAL_PDM_INT_DCMP)
    {
        uint32_t* ui32PDMDatabuffer = (uint32_t*)am_hal_pdm_dma_get_buffer(g_sVosBrd.pvPDMHandle);

        //
        // Re-arrange data
        //
        uint8_t* temp1 = (uint8_t*)g_sVosBrd.pui32PcmBuf;
        for ( uint32_t i = 0; i < PCM_FRAME_SIZE_SAMPLES * USE_MIC_NUM; i++ )
        {
            temp1[2 * i] = (ui32PDMDatabuffer[i] & 0xFF00) >> 8U;
            temp1[2 * i + 1] = (ui32PDMDatabuffer[i] & 0xFF0000) >> 16U;
        }

#if configUSE_AAD
        if(g_sVosSys.ui8AadSkipFrameFlag)
        {
            g_sVosSys.ui8AadSkipFrameFlag = 0;
            memset(g_sVosBrd.pui32PcmBuf, 0,
                   (g_sVosSys.ui32AadDiscardTimeUS / 1000) * (USE_PCM_SAMPLE_RATE / 1000) * PCM_SAMPLE_BYTES * USE_MIC_NUM);
        }
#endif // configUSE_AAD

#if configUSE_RTT_RECORDER
        //
        // Record the raw PCM data and send over RTT
        //
        if(g_sVosSys.ui8RecordStartFlag == 1)
        {
#if configUSE_RECORD_RAW_PCM
            am_vos_rtt_record((void*)g_sVosBrd.pui32PcmBuf, PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM);
#endif // configUSE_RECORD_RAW_PCM
        }

#endif // configUSE_RTT_RECORDER

#if VOS_MEASURE_ISR_PCM_DATA
        g_sVosSys.ui32VosIsrCount++;
        g_sVosSys.ui32VosPcmSize += PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM;
#endif // VOS_MEASURE_ISR_PCM_DATA

#if configUSE_AMBIQ_SPP
        am_vos_task_send_fromISR(AM_VOS_ISR_PDM, AM_VOS_TASK_VAD,
                                 AM_VOS_MESSAGE_SHORT, EMPTY_MESSAGE, NULL);
#else
        am_audio_buffer_nested_push(AM_AUDIO_BUFFER_STEREO, g_sVosBrd.pui32PcmBuf,
                                    PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM);
        am_vos_task_send_fromISR(AM_VOS_ISR_PDM, AM_VOS_TASK_AUD_PROCESSING, AM_VOS_MESSAGE_LONG, 
                                 PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM,
                                 &(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_STEREO]));
#endif // configUSE_AMBIQ_SPP

    }
    else if(ui32Status & (AM_HAL_PDM_INT_UNDFL | AM_HAL_PDM_INT_OVF))
    {
        am_hal_pdm_fifo_flush(g_sVosBrd.pvPDMHandle);
        AM_VOS_LOG_DEBUG("am_hal_pdm_fifo_flush() %d\n", ui32Status);
    }

    //
    // When Threshold causes ISR, Grab samples from packed FIFO and make sure we don't underflow
    //
#if configUSE_SYSVIEWER
    traceISR_EXIT();	// Should be comment out when it is Apollo 3
#endif // configUSE_SYSVIEWER

}
#endif // USE_DMIC_PDM

#if USE_OUTPUT_BUFFER_UART1
//*****************************************************************************
//
// Interrupt handler for UART1
//
//*****************************************************************************
void
am_uart1_isr(void)
{
#if configUSE_SYSVIEWER
    traceISR_ENTER();
#endif // configUSE_SYSVIEWER
    BaseType_t xHigherPriorityTaskWoken;
    uint32_t ui32Status;

    //
    // Read and save the interrupt status, but clear out the status register.
    //
    ui32Status = AM_REGn(UART, 1, MIS);
    AM_REGn(UART, 1, IEC) = ui32Status;

    if (ui32Status & (AM_HAL_UART_INT_RX_TMOUT | AM_HAL_UART_INT_TX | AM_HAL_UART_INT_RX))
    {
        am_hal_uart_service_buffered_timeout_save(UART1_MODULE, ui32Status);
    }
    if (ui32Status & (AM_HAL_UART_INT_RX_TMOUT | AM_HAL_UART_INT_RX))
    {
        g_bUARTPacketReceived = true;
    }
    // set event flag to unblock tasks
//    xEventGroupSetBitsFromISR(xKWDEventHandle, 0x04, &xHigherPriorityTaskWoken);

#if configUSE_SYSVIEWER
    traceISR_EXIT();
#endif // configUSE_SYSVIEWER
}
#endif // USE_OUTPUT_BUFFER_UART1

#if USE_AMIC_AUDADC
am_hal_audadc_sample_t sLGSampleBuffer[PCM_FRAME_SIZE_SAMPLES * USE_MIC_NUM];
//am_hal_audadc_sample_t sHGSampleBuffer[PCM_FRAME_SIZE_SAMPLES * USE_MIC_NUM];

//*****************************************************************************
//
// Interrupt handler for the AUDADC.
//
//*****************************************************************************
void
am_audadc0_isr(void)
{
    uint32_t ui32IntMask;
    uint32_t ui32PcmSampleCnt = PCM_FRAME_SIZE_SAMPLES * USE_MIC_NUM;

    uint8_t indx= 0;
    //
    // Read the interrupt status.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_interrupt_status(g_sVosBrd.pvAUDADCHandle, &ui32IntMask, false))
    {
        am_util_stdio_printf("Error reading AUDADC interrupt status\n");
    }

    //
    // Clear the AUDADC interrupt.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_interrupt_clear(g_sVosBrd.pvAUDADCHandle, ui32IntMask))
    {
        am_util_stdio_printf("Error clearing AUDADC interrupt status\n");
    }

    //
    // If we got a DMA complete, set the flag.
    //
#if AUDADC_DCMP_EN
    if (ui32IntMask & AM_HAL_AUDADC_INT_DCMP)
#else
    if (ui32IntMask & AM_HAL_AUDADC_INT_FIFOOVR1)
#endif
    {
        if ( AUDADCn(0)->DMASTAT_b.DMACPL )
        {
#ifdef AM_PART_APOLLO4B
            // Invalidate DAXI to make sure CPU sees the new data when loaded.
            am_hal_daxi_control(AM_HAL_DAXI_CONTROL_INVALIDATE, NULL);
#endif

            am_hal_audadc_interrupt_service(g_sVosBrd.pvAUDADCHandle, &(g_sVosBrd.sAUDADCDMAConfig));
            uint32_t *puint32AUDADCSamplePtr = (uint32_t *)am_hal_audadc_dma_get_buffer(g_sVosBrd.pvAUDADCHandle);

            am_hal_audadc_samples_read(g_sVosBrd.pvAUDADCHandle,
                                       puint32AUDADCSamplePtr,
                                       &ui32PcmSampleCnt,
                                       true, &sLGSampleBuffer[0],
                                       false, NULL,
                                       &g_sVosBrd.sOffsetCalib);
#if USE_MIC_SINGLE
            int16_t *pi16PcmBuf = (int16_t *)g_sVosBrd.pui32PcmBuf;
            for(indx = 0; indx < ui32PcmSampleCnt; indx++)
            {
                pi16PcmBuf[indx] = sLGSampleBuffer[indx].int16Sample;        // Low gain samples (MIC0) data to left channel.
            }
#endif // USE_MIC_SINGLE

#if USE_MIC_DUAL
#if defined(AM_PART_APOLLO4P)
            uint32_t LeftChCount = 0;
            uint32_t RightChCount = 0;

//            memset((void *)(g_sVosBrd.pui32PcmBuf), 0, PCM_FRAME_SIZE * SAMPLE_32BIT);
            for(indx = 0; indx < ui32PcmSampleCnt; indx++)
            {
                if(sLGSampleBuffer[indx].ui16AudChannel == 0)
                    g_sVosBrd.pui32PcmBuf[LeftChCount++] = (sLGSampleBuffer[indx].int16Sample & 0x0000FFFF);    // left channel (MIC1) data
                else
                    g_sVosBrd.pui32PcmBuf[RightChCount++] |= ((sLGSampleBuffer[indx].int16Sample) << 16);        // right channel (MIC2, MIC3) data
            }
#endif
#endif // USE_MIC_DUAL

#if configUSE_RTT_RECORDER
            //
            // Record the raw PCM data and send over RTT
            //
            if(g_sVosSys.ui8RecordStartFlag == 1)
            {
#if configUSE_RECORD_RAW_PCM
                  am_vos_rtt_record((void*)g_sVosBrd.pui32PcmBuf,
                                    PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM);
#endif // configUSE_RECORD_RAW_PCM
            }
#endif // configUSE_RTT_RECORDER

#if VOS_MEASURE_ISR_PCM_DATA
        g_sVosSys.ui32VosIsrCount++;
        g_sVosSys.ui32VosPcmSize += PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM;
#endif // VOS_MEASURE_ISR_PCM_DATA

#if configUSE_AMBIQ_SPP
            am_vos_task_send_fromISR(AM_VOS_ISR_ADC, AM_VOS_TASK_VAD,
                                     AM_VOS_MESSAGE_SHORT, EMPTY_MESSAGE, NULL);
#else
            am_audio_buffer_nested_push(AM_AUDIO_BUFFER_STEREO, g_sVosBrd.pui32PcmBuf,
                                        PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM);
            am_vos_task_send_fromISR(AM_VOS_ISR_ADC, AM_VOS_TASK_AUD_PROCESSING, AM_VOS_MESSAGE_LONG, 
                                     PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM,
                                     &(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_STEREO]));
#endif // configUSE_AMBIQ_SPP
        }
    }

    //
    // If we got a DMA error, set the flag.
    //
    if ( ui32IntMask & AM_HAL_AUDADC_INT_DERR )
    {
        g_sVosBrd.bAUDADCDMAError = true;
    }
}
#endif // USE_AMIC_AUDADC

#if configUSE_LOG_UART0 || configUSE_PRINTF_UART0
//*****************************************************************************
//
// UART0 interrupt handler.
//
//*****************************************************************************
void am_uart_isr(void)
{
    //
    // Service the FIFOs as necessary, and clear the interrupts.
    //
    uint32_t ui32Status;

    am_hal_uart_interrupt_status_get(g_sVosBrd.pvUartHandle, &ui32Status, true);
    am_hal_uart_interrupt_clear(g_sVosBrd.pvUartHandle, ui32Status);
    am_hal_uart_interrupt_service(g_sVosBrd.pvUartHandle, ui32Status);
}
#endif // configUSE_LOG_UART0 || configUSE_PRINTF_UART0

//*****************************************************************************
//
// Sleep function called from FreeRTOS IDLE task.
// Do necessary application specific Power down operations here
// Return 0 if this function also incorporates the WFI, else return value same
// as idleTime
//
//*****************************************************************************
uint32_t am_freertos_sleep(uint32_t idleTime)
{
#if VOS_MEASURE_SYS_MIPS
#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)
    am_vos_bench_dwt_reset();
    am_vos_bench_dwt_start();
#else
    uint32_t TickBeforeSleep = am_vos_timer_benchmark_read();
#endif
#endif // VOS_MEASURE_SYS_MIPS

#if (!configUSE_RTT_RECORDER)
    am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
#endif // (!configUSE_RTT_RECORDER)

#if VOS_MEASURE_SYS_MIPS
#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)
    g_sVosSys.ui32VosSleepTimeTick += am_vos_bench_dwt_getcycle();
    am_vos_bench_dwt_stop();
#else
    if((am_vos_timer_benchmark_read() - TickBeforeSleep) > 0)
    {
        g_sVosSys.ui32VosSleepTimeTick += am_vos_timer_benchmark_read() - TickBeforeSleep;
        am_vos_timer_benchmark_clear_and_start();
    }
#endif
#endif // VOS_MEASURE_SYS_MIPS
    return 0;
}

//*****************************************************************************
//
// Recovery function called from FreeRTOS IDLE task, after waking up from Sleep
// Do necessary 'wakeup' operations here, e.g. to power up/enable peripherals etc.
//
//*****************************************************************************
void am_freertos_wakeup(uint32_t idleTime)
{
    return;
}


//*****************************************************************************
//
// FreeRTOS debugging functions.
//
//*****************************************************************************
void
vApplicationMallocFailedHook(void)
{
    //
    // Called if a call to pvPortMalloc() fails because there is insufficient
    // free memory available in the FreeRTOS heap.  pvPortMalloc() is called
    // internally by FreeRTOS API functions that create tasks, queues, software
    // timers, and semaphores.  The size of the FreeRTOS heap is set by the
    // configTOTAL_HEAP_SIZE configuration constant in FreeRTOSConfig.h.
    //
    while (1);
}

void
vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName)
{
    (void) pcTaskName;
    (void) pxTask;

    //
    // Run time stack overflow checking is performed if
    // configconfigCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  This hook
    // function is called if a stack overflow is detected.
    //
    while (1)
    {
        __asm("BKPT #0\n") ; // Break into the debugger
    }
}

