/***************************************************************************
 *   Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO,             *
 *                                Niccolò Izzo IU2KIN                      *
 *                                Frederik Saraci IU2NRO                   *
 *                                Silvano Seva IU2KWO,                     *
 *                                Federico Terraneo                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
 ***************************************************************************/

/*
 * startup.cpp
 * STM32 C++ startup.
 * NOTE: for stm32f4 devices ONLY.
 * Supports interrupt handlers in C++ without extern "C"
 * Developed by Terraneo Federico, based on ST startup code.
 * Additionally modified to boot Miosix.
 */

#include "interfaces/arch_registers.h"
#include "kernel/stage_2_boot.h"
#include "core/interrupts.h" //For the unexpected interrupt call
#include <string.h>

/**
 * Called by Reset_Handler, performs initialization and calls main.
 * Never returns.
 */
void program_startup() __attribute__((noreturn));
void program_startup()
{
    //Cortex M3 core appears to get out of reset with interrupts already enabled
    __disable_irq();

    //SystemInit() is called *before* initializing .data and zeroing .bss
    //Despite all startup files provided by ST do the opposite, there are three
    //good reasons to do so:
    //First, the CMSIS specifications say that SystemInit() must not access
    //global variables, so it is actually possible to call it before
    //Second, when running Miosix with the xram linker scripts .data and .bss
    //are placed in the external RAM, so we *must* call SystemInit(), which
    //enables xram, before touching .data and .bss
    //Third, this is a performance improvement since the loops that initialize
    //.data and zeros .bss now run with the CPU at full speed instead of 8MHz
    SystemInit();

    // Initialise clock tree for full speed run, since SystemInit by NXP does not.
    //
    // Clock tree configuration:
    // - External clock input: 12.288MHz from HR_C6000 resonator
    // - PLL input divider: 4 -> PLL reference clock is 3.072MHz
    // - PLL multiplier: 39 -> PLL output clock is 119.808MHz
    //
    // - Core and system clock @ 119.808MHz
    // - Bus clock @ 59.904MHz
    // - FlexBus clock @ 29.952MHz
    // - Flash clock @ 23.962MHz

    SIM->CLKDIV1 = SIM_CLKDIV1_OUTDIV2(1)   // Bus clock divider = 2
                 | SIM_CLKDIV1_OUTDIV3(3)   // FlexBus clock divider = 4
                 | SIM_CLKDIV1_OUTDIV4(4);  // Flash clock divider = 5

    MCG->C2 |= MCG_C2_RANGE(2);             // Very high frequency range
    OSC->CR |= OSC_CR_ERCLKEN(1);           // Enable external reference clock

    MCG->C6 |= MCG_C6_VDIV0(15);            // PLL multiplier set to 39
    MCG->C5 |= MCG_C5_PRDIV0(3)             // Divide PLL ref clk by 4
            |  MCG_C5_PLLCLKEN0(1);         // Enable PLL clock

    SMC->PMPROT = SMC_PMPROT_AHSRUN(1);     // Allow HSRUN mode
    SMC->PMCTRL = SMC_PMCTRL_RUNM(3);       // Switch to HSRUN mode
    while((SMC->PMSTAT & 0x80) == 0) ;      // Wait till switch to HSRUN is effective

    MCG->C6 |= MCG_C6_PLLS(1);              // Connect PLL to MCG source

    //These are defined in the linker script
    extern unsigned char _etext asm("_etext");
    extern unsigned char _data asm("_data");
    extern unsigned char _edata asm("_edata");
    extern unsigned char _bss_start asm("_bss_start");
    extern unsigned char _bss_end asm("_bss_end");

    //Initialize .data section, clear .bss section
    unsigned char *etext=&_etext;
    unsigned char *data=&_data;
    unsigned char *edata=&_edata;
    unsigned char *bss_start=&_bss_start;
    unsigned char *bss_end=&_bss_end;
    memcpy(data, etext, edata-data);
    memset(bss_start, 0, bss_end-bss_start);

    //Move on to stage 2
    _init();

    //If main returns, reboot
    NVIC_SystemReset();
    for(;;) ;
}

/**
 * Reset handler, called by hardware immediately after reset
 */
void Reset_Handler() __attribute__((__interrupt__, noreturn));
void Reset_Handler()
{
    /*
     * Initialize process stack and switch to it.
     * This is required for booting Miosix, a small portion of the top of the
     * heap area will be used as stack until the first thread starts. After,
     * this stack will be abandoned and the process stack will point to the
     * current thread's stack.
     */
    asm volatile("ldr r0,  =_heap_end          \n\t"
                 "msr psp, r0                  \n\t"
                 "movw r0, #2                  \n\n" //Privileged, process stack
                 "msr control, r0              \n\t"
                 "isb                          \n\t":::"r0");

    program_startup();
}

/**
 * All unused interrupts call this function.
 */
extern "C" void Default_Handler()
{
    unexpectedInterrupt();
}

//System handlers
void /*__attribute__((weak))*/ Reset_Handler();     //These interrupts are not
void /*__attribute__((weak))*/ NMI_Handler();       //weak because they are
void /*__attribute__((weak))*/ HardFault_Handler(); //surely defined by Miosix
void /*__attribute__((weak))*/ MemManage_Handler();
void /*__attribute__((weak))*/ BusFault_Handler();
void /*__attribute__((weak))*/ UsageFault_Handler();
void /*__attribute__((weak))*/ SVC_Handler();
void /*__attribute__((weak))*/ DebugMon_Handler();
void /*__attribute__((weak))*/ PendSV_Handler();
void /*__attribute__((weak))*/ SysTick_Handler();

//Interrupt handlers
void __attribute__((weak)) DMA0_IRQHandler();
void __attribute__((weak)) DMA1_IRQHandler();
void __attribute__((weak)) DMA2_IRQHandler();
void __attribute__((weak)) DMA3_IRQHandler();
void __attribute__((weak)) DMA4_IRQHandler();
void __attribute__((weak)) DMA5_IRQHandler();
void __attribute__((weak)) DMA6_IRQHandler();
void __attribute__((weak)) DMA7_IRQHandler();
void __attribute__((weak)) DMA8_IRQHandler();
void __attribute__((weak)) DMA9_IRQHandler();
void __attribute__((weak)) DMA10_IRQHandler();
void __attribute__((weak)) DMA11_IRQHandler();
void __attribute__((weak)) DMA12_IRQHandler();
void __attribute__((weak)) DMA13_IRQHandler();
void __attribute__((weak)) DMA14_IRQHandler();
void __attribute__((weak)) DMA15_IRQHandler();
void __attribute__((weak)) DMA_Error_IRQHandler();
void __attribute__((weak)) MCM_IRQHandler();
void __attribute__((weak)) FTF_IRQHandler();
void __attribute__((weak)) Read_Collision_IRQHandler();
void __attribute__((weak)) LVD_LVW_IRQHandler();
void __attribute__((weak)) LLWU_IRQHandler();
void __attribute__((weak)) WDOG_EWM_IRQHandler();
void __attribute__((weak)) RNG_IRQHandler();
void __attribute__((weak)) I2C0_IRQHandler();
void __attribute__((weak)) I2C1_IRQHandler();
void __attribute__((weak)) SPI0_IRQHandler();
void __attribute__((weak)) SPI1_IRQHandler();
void __attribute__((weak)) I2S0_Tx_IRQHandler();
void __attribute__((weak)) I2S0_Rx_IRQHandler();
void __attribute__((weak)) LPUART0_IRQHandler();
void __attribute__((weak)) UART0_RX_TX_IRQHandler();
void __attribute__((weak)) UART0_ERR_IRQHandler();
void __attribute__((weak)) UART1_RX_TX_IRQHandler();
void __attribute__((weak)) UART1_ERR_IRQHandler();
void __attribute__((weak)) UART2_RX_TX_IRQHandler();
void __attribute__((weak)) UART2_ERR_IRQHandler();
void __attribute__((weak)) Reserved53_IRQHandler();
void __attribute__((weak)) Reserved54_IRQHandler();
void __attribute__((weak)) ADC0_IRQHandler();
void __attribute__((weak)) CMP0_IRQHandler();
void __attribute__((weak)) CMP1_IRQHandler();
void __attribute__((weak)) FTM0_IRQHandler();
void __attribute__((weak)) FTM1_IRQHandler();
void __attribute__((weak)) FTM2_IRQHandler();
void __attribute__((weak)) Reserved61_IRQHandler();
void __attribute__((weak)) RTC_IRQHandler();
void __attribute__((weak)) RTC_Seconds_IRQHandler();
void __attribute__((weak)) PIT0_IRQHandler();
void __attribute__((weak)) PIT1_IRQHandler();
void __attribute__((weak)) PIT2_IRQHandler();
void __attribute__((weak)) PIT3_IRQHandler();
void __attribute__((weak)) PDB0_IRQHandler();
void __attribute__((weak)) USB0_IRQHandler();
void __attribute__((weak)) Reserved70_IRQHandler();
void __attribute__((weak)) Reserved71_IRQHandler();
void __attribute__((weak)) DAC0_IRQHandler();
void __attribute__((weak)) MCG_IRQHandler();
void __attribute__((weak)) LPTMR0_IRQHandler();
void __attribute__((weak)) PORTA_IRQHandler();
void __attribute__((weak)) PORTB_IRQHandler();
void __attribute__((weak)) PORTC_IRQHandler();
void __attribute__((weak)) PORTD_IRQHandler();
void __attribute__((weak)) PORTE_IRQHandler();
void __attribute__((weak)) SWI_IRQHandler();
void __attribute__((weak)) Reserved81_IRQHandler();
void __attribute__((weak)) Reserved82_IRQHandler();
void __attribute__((weak)) Reserved83_IRQHandler();
void __attribute__((weak)) Reserved84_IRQHandler();
void __attribute__((weak)) Reserved85_IRQHandler();
void __attribute__((weak)) Reserved86_IRQHandler();
void __attribute__((weak)) FTM3_IRQHandler();
void __attribute__((weak)) DAC1_IRQHandler();
void __attribute__((weak)) ADC1_IRQHandler();

//Stack top, defined in the linker script
extern char _main_stack_top asm("_main_stack_top");

//Interrupt vectors, must be placed @ address 0x00000000
//The extern declaration is required otherwise g++ optimizes it out
extern void (* const __Vectors[])();
void (* const __Vectors[])() __attribute__ ((section(".isr_vector"))) =
{
    reinterpret_cast<void (*)()>(&_main_stack_top),/* Stack pointer*/
    Reset_Handler,
    NMI_Handler,
    HardFault_Handler,
    MemManage_Handler,
    BusFault_Handler,
    UsageFault_Handler,
    0,
    0,
    0,
    0,
    SVC_Handler,
    DebugMon_Handler,
    0,
    PendSV_Handler,
    SysTick_Handler,

    DMA0_IRQHandler,            /* DMA Channel 0 Transfer Complete*/
    DMA1_IRQHandler,            /* DMA Channel 1 Transfer Complete*/
    DMA2_IRQHandler,            /* DMA Channel 2 Transfer Complete*/
    DMA3_IRQHandler,            /* DMA Channel 3 Transfer Complete*/
    DMA4_IRQHandler,            /* DMA Channel 4 Transfer Complete*/
    DMA5_IRQHandler,            /* DMA Channel 5 Transfer Complete*/
    DMA6_IRQHandler,            /* DMA Channel 6 Transfer Complete*/
    DMA7_IRQHandler,            /* DMA Channel 7 Transfer Complete*/
    DMA8_IRQHandler,            /* DMA Channel 8 Transfer Complete*/
    DMA9_IRQHandler,            /* DMA Channel 9 Transfer Complete*/
    DMA10_IRQHandler,           /* DMA Channel 10 Transfer Complete*/
    DMA11_IRQHandler,           /* DMA Channel 11 Transfer Complete*/
    DMA12_IRQHandler,           /* DMA Channel 12 Transfer Complete*/
    DMA13_IRQHandler,           /* DMA Channel 13 Transfer Complete*/
    DMA14_IRQHandler,           /* DMA Channel 14 Transfer Complete*/
    DMA15_IRQHandler,           /* DMA Channel 15 Transfer Complete*/
    DMA_Error_IRQHandler,       /* DMA Error Interrupt*/
    MCM_IRQHandler,             /* Normal Interrupt*/
    FTF_IRQHandler,             /* FTFA Command complete interrupt*/
    Read_Collision_IRQHandler,  /* Read Collision Interrupt*/
    LVD_LVW_IRQHandler,         /* Low Voltage Detect, Low Voltage Warning*/
    LLWU_IRQHandler,            /* Low Leakage Wakeup Unit*/
    WDOG_EWM_IRQHandler,        /* WDOG Interrupt*/
    RNG_IRQHandler,             /* RNG Interrupt*/
    I2C0_IRQHandler,            /* I2C0 interrupt*/
    I2C1_IRQHandler,            /* I2C1 interrupt*/
    SPI0_IRQHandler,            /* SPI0 Interrupt*/
    SPI1_IRQHandler,            /* SPI1 Interrupt*/
    I2S0_Tx_IRQHandler,         /* I2S0 transmit interrupt*/
    I2S0_Rx_IRQHandler,         /* I2S0 receive interrupt*/
    LPUART0_IRQHandler,         /* LPUART0 status/error interrupt*/
    UART0_RX_TX_IRQHandler,     /* UART0 Receive/Transmit interrupt*/
    UART0_ERR_IRQHandler,       /* UART0 Error interrupt*/
    UART1_RX_TX_IRQHandler,     /* UART1 Receive/Transmit interrupt*/
    UART1_ERR_IRQHandler,       /* UART1 Error interrupt*/
    UART2_RX_TX_IRQHandler,     /* UART2 Receive/Transmit interrupt*/
    UART2_ERR_IRQHandler,       /* UART2 Error interrupt*/
    Reserved53_IRQHandler,      /* Reserved interrupt 53*/
    Reserved54_IRQHandler,      /* Reserved interrupt 54*/
    ADC0_IRQHandler,            /* ADC0 interrupt*/
    CMP0_IRQHandler,            /* CMP0 interrupt*/
    CMP1_IRQHandler,            /* CMP1 interrupt*/
    FTM0_IRQHandler,            /* FTM0 fault, overflow and channels interrupt*/
    FTM1_IRQHandler,            /* FTM1 fault, overflow and channels interrupt*/
    FTM2_IRQHandler,            /* FTM2 fault, overflow and channels interrupt*/
    Reserved61_IRQHandler,      /* Reserved interrupt 61*/
    RTC_IRQHandler,             /* RTC interrupt*/
    RTC_Seconds_IRQHandler,     /* RTC seconds interrupt*/
    PIT0_IRQHandler,            /* PIT timer channel 0 interrupt*/
    PIT1_IRQHandler,            /* PIT timer channel 1 interrupt*/
    PIT2_IRQHandler,            /* PIT timer channel 2 interrupt*/
    PIT3_IRQHandler,            /* PIT timer channel 3 interrupt*/
    PDB0_IRQHandler,            /* PDB0 Interrupt*/
    USB0_IRQHandler,            /* USB0 interrupt*/
    Reserved70_IRQHandler,      /* Reserved interrupt 70*/
    Reserved71_IRQHandler,      /* Reserved interrupt 71*/
    DAC0_IRQHandler,            /* DAC0 interrupt*/
    MCG_IRQHandler,             /* MCG Interrupt*/
    LPTMR0_IRQHandler,          /* LPTimer interrupt*/
    PORTA_IRQHandler,           /* Port A interrupt*/
    PORTB_IRQHandler,           /* Port B interrupt*/
    PORTC_IRQHandler,           /* Port C interrupt*/
    PORTD_IRQHandler,           /* Port D interrupt*/
    PORTE_IRQHandler,           /* Port E interrupt*/
    SWI_IRQHandler,             /* Software interrupt*/
    Reserved81_IRQHandler,      /* Reserved interrupt 81*/
    Reserved82_IRQHandler,      /* Reserved interrupt 82*/
    Reserved83_IRQHandler,      /* Reserved interrupt 83*/
    Reserved84_IRQHandler,      /* Reserved interrupt 84*/
    Reserved85_IRQHandler,      /* Reserved interrupt 85*/
    Reserved86_IRQHandler,      /* Reserved interrupt 86*/
    FTM3_IRQHandler,            /* FTM3 fault, overflow and channels interrupt*/
    DAC1_IRQHandler,            /* DAC1 interrupt*/
    ADC1_IRQHandler             /* ADC1 interrupt*/
};

#pragma weak DMA0_IRQHandler = Default_Handler
#pragma weak DMA1_IRQHandler = Default_Handler
#pragma weak DMA2_IRQHandler = Default_Handler
#pragma weak DMA3_IRQHandler = Default_Handler
#pragma weak DMA4_IRQHandler = Default_Handler
#pragma weak DMA5_IRQHandler = Default_Handler
#pragma weak DMA6_IRQHandler = Default_Handler
#pragma weak DMA7_IRQHandler = Default_Handler
#pragma weak DMA8_IRQHandler = Default_Handler
#pragma weak DMA9_IRQHandler = Default_Handler
#pragma weak DMA10_IRQHandler = Default_Handler
#pragma weak DMA11_IRQHandler = Default_Handler
#pragma weak DMA12_IRQHandler = Default_Handler
#pragma weak DMA13_IRQHandler = Default_Handler
#pragma weak DMA14_IRQHandler = Default_Handler
#pragma weak DMA15_IRQHandler = Default_Handler
#pragma weak DMA_Error_IRQHandler = Default_Handler
#pragma weak MCM_IRQHandler = Default_Handler
#pragma weak FTF_IRQHandler = Default_Handler
#pragma weak Read_Collision_IRQHandler = Default_Handler
#pragma weak LVD_LVW_IRQHandler = Default_Handler
#pragma weak LLWU_IRQHandler = Default_Handler
#pragma weak WDOG_EWM_IRQHandler = Default_Handler
#pragma weak RNG_IRQHandler = Default_Handler
#pragma weak I2C0_IRQHandler = Default_Handler
#pragma weak I2C1_IRQHandler = Default_Handler
#pragma weak SPI0_IRQHandler = Default_Handler
#pragma weak SPI1_IRQHandler = Default_Handler
#pragma weak I2S0_Tx_IRQHandler = Default_Handler
#pragma weak I2S0_Rx_IRQHandler = Default_Handler
#pragma weak LPUART0_IRQHandler = Default_Handler
#pragma weak UART0_RX_TX_IRQHandler = Default_Handler
#pragma weak UART0_ERR_IRQHandler = Default_Handler
#pragma weak UART1_RX_TX_IRQHandler = Default_Handler
#pragma weak UART1_ERR_IRQHandler = Default_Handler
#pragma weak UART2_RX_TX_IRQHandler = Default_Handler
#pragma weak UART2_ERR_IRQHandler = Default_Handler
#pragma weak Reserved53_IRQHandler = Default_Handler
#pragma weak Reserved54_IRQHandler = Default_Handler
#pragma weak ADC0_IRQHandler = Default_Handler
#pragma weak CMP0_IRQHandler = Default_Handler
#pragma weak CMP1_IRQHandler = Default_Handler
#pragma weak FTM0_IRQHandler = Default_Handler
#pragma weak FTM1_IRQHandler = Default_Handler
#pragma weak FTM2_IRQHandler = Default_Handler
#pragma weak Reserved61_IRQHandler = Default_Handler
#pragma weak RTC_IRQHandler = Default_Handler
#pragma weak RTC_Seconds_IRQHandler = Default_Handler
#pragma weak PIT0_IRQHandler = Default_Handler
#pragma weak PIT1_IRQHandler = Default_Handler
#pragma weak PIT2_IRQHandler = Default_Handler
#pragma weak PIT3_IRQHandler = Default_Handler
#pragma weak PDB0_IRQHandler = Default_Handler
#pragma weak USB0_IRQHandler = Default_Handler
#pragma weak Reserved70_IRQHandler = Default_Handler
#pragma weak Reserved71_IRQHandler = Default_Handler
#pragma weak DAC0_IRQHandler = Default_Handler
#pragma weak MCG_IRQHandler = Default_Handler
#pragma weak LPTMR0_IRQHandler = Default_Handler
#pragma weak PORTA_IRQHandler = Default_Handler
#pragma weak PORTB_IRQHandler = Default_Handler
#pragma weak PORTC_IRQHandler = Default_Handler
#pragma weak PORTD_IRQHandler = Default_Handler
#pragma weak PORTE_IRQHandler = Default_Handler
#pragma weak SWI_IRQHandler = Default_Handler
#pragma weak Reserved81_IRQHandler = Default_Handler
#pragma weak Reserved82_IRQHandler = Default_Handler
#pragma weak Reserved83_IRQHandler = Default_Handler
#pragma weak Reserved84_IRQHandler = Default_Handler
#pragma weak Reserved85_IRQHandler = Default_Handler
#pragma weak Reserved86_IRQHandler = Default_Handler
#pragma weak FTM3_IRQHandler = Default_Handler
#pragma weak DAC1_IRQHandler = Default_Handler
#pragma weak ADC1_IRQHandler = Default_Handler
