/*
 * STM32G071KBT6.cpp
 *
 *  Created on: Jun 23, 2023
 *      Author: Carst
 */
#include <algorithm>
#include <cstdint>
#include <stm32g0xx_ll_bus.h>
#include <stm32g0xx_ll_cortex.h>
#include <stm32g0xx_ll_rcc.h>
#include <stm32g0xx_ll_system.h>
#include <stm32g0xx_ll_utils.h>

#include <stm32g0xx_ll_gpio.h>
#include <stm32g0xx_ll_tim.h>

extern "C" void Reset_Handler(void);
extern "C" void NMI_Handler(void);
extern "C" void HardFault_Handler(void);
extern "C" void SVC_Handler(void);
extern "C" void PendSV_Handler(void);
extern "C" void SysTick_Handler(void);
extern "C" void WWDG_IRQHandler(void);                   /* Window WatchDog              */
extern "C" void PVD_IRQHandler(void);                    /* PVD through EXTI Line detect */
extern "C" void RTC_TAMP_IRQHandler(void);               /* RTC through the EXTI line    */
extern "C" void FLASH_IRQHandler(void);                  /* FLASH                        */
extern "C" void RCC_IRQHandler(void);                    /* RCC                          */
extern "C" void EXTI0_1_IRQHandler(void);                /* EXTI Line 0 and 1            */
extern "C" void EXTI2_3_IRQHandler(void);                /* EXTI Line 2 and 3            */
extern "C" void EXTI4_15_IRQHandler(void);               /* EXTI Line 4 to 15            */
extern "C" void UCPD1_2_IRQHandler(void);                /* UCPD1, UCPD2                 */
extern "C" void DMA1_Channel1_IRQHandler(void);          /* DMA1 Channel 1               */
extern "C" void DMA1_Channel2_3_IRQHandler(void);        /* DMA1 Channel 2 and Channel 3 */
extern "C" void DMA1_Ch4_7_DMAMUX1_OVR_IRQHandler(void); /* DMA1 Channel 4 to Channel 7, DMAMUX1 overrun */
extern "C" void ADC1_COMP_IRQHandler(void);              /* ADC1, COMP1 and COMP2         */
extern "C" void TIM1_BRK_UP_TRG_COM_IRQHandler(void);    /* TIM1 Break, Update, Trigger and Commutation */
extern "C" void TIM1_CC_IRQHandler(void);                /* TIM1 Capture Compare         */
extern "C" void TIM2_IRQHandler(void);                   /* TIM2                         */
extern "C" void TIM3_IRQHandler(void);                   /* TIM3                         */
extern "C" void TIM6_DAC_LPTIM1_IRQHandler(void);        /* TIM6, DAC and LPTIM1         */
extern "C" void TIM7_LPTIM2_IRQHandler(void);            /* TIM7 and LPTIM2              */
extern "C" void TIM14_IRQHandler(void);                  /* TIM14                        */
extern "C" void TIM15_IRQHandler(void);                  /* TIM15                        */
extern "C" void TIM16_IRQHandler(void);                  /* TIM16                        */
extern "C" void TIM17_IRQHandler(void);                  /* TIM17                        */
extern "C" void I2C1_IRQHandler(void);                   /* I2C1                         */
extern "C" void I2C2_IRQHandler(void);                   /* I2C2                         */
extern "C" void SPI1_IRQHandler(void);                   /* SPI1                         */
extern "C" void SPI2_IRQHandler(void);                   /* SPI2                         */
extern "C" void USART1_IRQHandler(void);                 /* USART1                       */
extern "C" void USART2_IRQHandler(void);                 /* USART2                       */
extern "C" void USART3_4_LPUART1_IRQHandler(void);       /* USART3, USART4 and LPUART1   */
extern "C" void CEC_IRQHandler(void);                    /* CEC                          */

extern std::uint32_t _estack;
const uint32_t AHBPrescTable[16UL] = {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL, 6UL, 7UL, 8UL, 9UL};
const uint32_t APBPrescTable[8UL] =  {0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL};
volatile std::uint32_t systick;

const std::uintptr_t interruptVectorTable[] __attribute__((section(".isr_vector")))
{
  reinterpret_cast<std::uintptr_t>(&_estack),
  reinterpret_cast<std::uintptr_t>(Reset_Handler),
  reinterpret_cast<std::uintptr_t>(NMI_Handler),
  reinterpret_cast<std::uintptr_t>(HardFault_Handler),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(SVC_Handler),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(nullptr),
  reinterpret_cast<std::uintptr_t>(PendSV_Handler),
  reinterpret_cast<std::uintptr_t>(SysTick_Handler),
  reinterpret_cast<std::uintptr_t>(WWDG_IRQHandler),                   /* Window WatchDog              */
  reinterpret_cast<std::uintptr_t>(PVD_IRQHandler),                    /* PVD through EXTI Line detect */
  reinterpret_cast<std::uintptr_t>(RTC_TAMP_IRQHandler),               /* RTC through the EXTI line    */
  reinterpret_cast<std::uintptr_t>(FLASH_IRQHandler),                  /* FLASH                        */
  reinterpret_cast<std::uintptr_t>(RCC_IRQHandler),                    /* RCC                          */
  reinterpret_cast<std::uintptr_t>(EXTI0_1_IRQHandler),                /* EXTI Line 0 and 1            */
  reinterpret_cast<std::uintptr_t>(EXTI2_3_IRQHandler),                /* EXTI Line 2 and 3            */
  reinterpret_cast<std::uintptr_t>(EXTI4_15_IRQHandler),               /* EXTI Line 4 to 15            */
  reinterpret_cast<std::uintptr_t>(UCPD1_2_IRQHandler),                /* UCPD1, UCPD2                 */
  reinterpret_cast<std::uintptr_t>(DMA1_Channel1_IRQHandler),          /* DMA1 Channel 1               */
  reinterpret_cast<std::uintptr_t>(DMA1_Channel2_3_IRQHandler),        /* DMA1 Channel 2 and Channel 3 */
  reinterpret_cast<std::uintptr_t>(DMA1_Ch4_7_DMAMUX1_OVR_IRQHandler), /* DMA1 Channel 4 to Channel 7, DMAMUX1 overrun */
  reinterpret_cast<std::uintptr_t>(ADC1_COMP_IRQHandler),              /* ADC1, COMP1 and COMP2         */
  reinterpret_cast<std::uintptr_t>(TIM1_BRK_UP_TRG_COM_IRQHandler),    /* TIM1 Break, Update, Trigger and Commutation */
  reinterpret_cast<std::uintptr_t>(TIM1_CC_IRQHandler),                /* TIM1 Capture Compare         */
  reinterpret_cast<std::uintptr_t>(TIM2_IRQHandler),                   /* TIM2                         */
  reinterpret_cast<std::uintptr_t>(TIM3_IRQHandler),                   /* TIM3                         */
  reinterpret_cast<std::uintptr_t>(TIM6_DAC_LPTIM1_IRQHandler),        /* TIM6, DAC and LPTIM1         */
  reinterpret_cast<std::uintptr_t>(TIM7_LPTIM2_IRQHandler),            /* TIM7 and LPTIM2              */
  reinterpret_cast<std::uintptr_t>(TIM14_IRQHandler),                  /* TIM14                        */
  reinterpret_cast<std::uintptr_t>(TIM15_IRQHandler),                  /* TIM15                        */
  reinterpret_cast<std::uintptr_t>(TIM16_IRQHandler),                  /* TIM16                        */
  reinterpret_cast<std::uintptr_t>(TIM17_IRQHandler),                  /* TIM17                        */
  reinterpret_cast<std::uintptr_t>(I2C1_IRQHandler),                   /* I2C1                         */
  reinterpret_cast<std::uintptr_t>(I2C2_IRQHandler),                   /* I2C2                         */
  reinterpret_cast<std::uintptr_t>(SPI1_IRQHandler),                   /* SPI1                         */
  reinterpret_cast<std::uintptr_t>(SPI2_IRQHandler),                   /* SPI2                         */
  reinterpret_cast<std::uintptr_t>(USART1_IRQHandler),                 /* USART1                       */
  reinterpret_cast<std::uintptr_t>(USART2_IRQHandler),                 /* USART2                       */
  reinterpret_cast<std::uintptr_t>(USART3_4_LPUART1_IRQHandler),       /* USART3, USART4 and LPUART1   */
  reinterpret_cast<std::uintptr_t>(CEC_IRQHandler),                    /* CEC                          */
};

extern "C" void Reset_Handler(void)
{
  /* Stack-Pointer wird automatisch von der HW initialisiert */


  /* Copy the data segment initializers from flash to SRAM */
  extern std::uint8_t _sdata;
  extern std::uint8_t _edata;
  extern std::uint8_t _sidata;
  std::size_t size = static_cast<size_t>(&_edata - &_sdata);
  std::copy(&_sidata, &_sidata + size, &_sdata);

  /* Initialize bss section */
  extern std::uint8_t _sbss;
  extern std::uint8_t _ebss;
  std::fill(&_sbss, &_ebss, UINT8_C(0x00));

  /* Call the clock system initialization function.*/
  SystemInit();

  /* Initialize static objects by calling their constructors */
  typedef void (*function_t)();
  extern function_t __init_array_start;
  extern function_t __init_array_end;
  std::for_each(&__init_array_start, &__init_array_end, [](const function_t pfn) {
      pfn();
  });

  /* Jump to main */
  asm ("bl main");
}

extern "C" void NMI_Handler(void){}
extern "C" void HardFault_Handler(void){}
extern "C" void SVC_Handler(void){}
extern "C" void PendSV_Handler(void){}

extern "C" void SysTick_Handler(void)
{
  systick++;
}

extern "C" void WWDG_IRQHandler(void){}                   /* Window WatchDog              */
extern "C" void PVD_IRQHandler(void){}                    /* PVD through EXTI Line detect */
extern "C" void RTC_TAMP_IRQHandler(void){}               /* RTC through the EXTI line    */
extern "C" void FLASH_IRQHandler(void){}                  /* FLASH                        */
extern "C" void RCC_IRQHandler(void){}                    /* RCC                          */
extern "C" void EXTI0_1_IRQHandler(void){}                /* EXTI Line 0 and 1            */
extern "C" void EXTI2_3_IRQHandler(void){}                /* EXTI Line 2 and 3            */
extern "C" void EXTI4_15_IRQHandler(void){}               /* EXTI Line 4 to 15            */
extern "C" void UCPD1_2_IRQHandler(void){}                /* UCPD1, UCPD2                 */

extern "C" void DMA1_Channel2_3_IRQHandler(void){}        /* DMA1 Channel 2 and Channel 3 */
extern "C" void DMA1_Ch4_7_DMAMUX1_OVR_IRQHandler(void){} /* DMA1 Channel 4 to Channel 7, DMAMUX1 overrun */
extern "C" void ADC1_COMP_IRQHandler(void){}              /* ADC1, COMP1 and COMP2         */
extern "C" void TIM1_BRK_UP_TRG_COM_IRQHandler(void){LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_6);}    /* TIM1 Break, Update, Trigger and Commutation */
extern "C" void TIM1_CC_IRQHandler(void){}                /* TIM1 Capture Compare         */
extern "C" void TIM2_IRQHandler(void){}                   /* TIM2                         */
extern "C" void TIM3_IRQHandler(void){}                   /* TIM3                         */
extern "C" void TIM6_DAC_LPTIM1_IRQHandler(void){}        /* TIM6, DAC and LPTIM1         */
extern "C" void TIM7_LPTIM2_IRQHandler(void){}            /* TIM7 and LPTIM2              */

extern "C" void TIM15_IRQHandler(void){}                  /* TIM15                        */
extern "C" void TIM16_IRQHandler(void){}                  /* TIM16                        */
extern "C" void TIM17_IRQHandler(void){}                  /* TIM17                        */
extern "C" void I2C1_IRQHandler(void){}                   /* I2C1                         */
extern "C" void I2C2_IRQHandler(void){}                   /* I2C2                         */
extern "C" void SPI1_IRQHandler(void){}                   /* SPI1                         */
extern "C" void SPI2_IRQHandler(void){}                   /* SPI2                         */
extern "C" void USART1_IRQHandler(void){}                 /* USART1                       */
extern "C" void USART2_IRQHandler(void){}                 /* USART2                       */
extern "C" void CEC_IRQHandler(void){}                    /* CEC                          */

/* This variable is updated in three ways:
    1) by calling CMSIS function SystemCoreClockUpdate()
    2) by calling HAL API function HAL_RCC_GetHCLKFreq()
    3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
       Note: If you use this function to configure the system clock; then there
             is no need to call the 2 first functions listed above, since SystemCoreClock
             variable is updated automatically.
*/
uint32_t SystemCoreClock = 8000000;

extern "C" void SystemInit(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

  LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
  while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2)
  {
  }

  /* HSE configuration and activation */
  LL_RCC_HSE_EnableBypass();
  LL_RCC_HSE_Enable();
  while(LL_RCC_HSE_IsReady() != 1)
  {
  }

  /* Main PLL configuration and activation */
  LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_1, 16, LL_RCC_PLLR_DIV_2);
  LL_RCC_PLL_Enable();
  LL_RCC_PLL_EnableDomain_SYS();
  while(LL_RCC_PLL_IsReady() != 1)
  {
  }

  /* Set AHB prescaler*/
  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);

  /* Sysclk activation on the main PLL */
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
  {
  }

  /* Set APB1 prescaler*/
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);

  LL_Init1msTick(64000000);
  LL_SYSTICK_EnableIT();
  /* SysTick_IRQn interrupt configuration */
  NVIC_SetPriority(SysTick_IRQn, 3);
  /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
  LL_SetSystemCoreClock(64000000);

  /* Clock-Sources*/
  LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
  LL_RCC_SetTIMClockSource(LL_RCC_TIM1_CLKSOURCE_PCLK1);

  /* Enable Clocks for all Peripherals*/
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPUART1);
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_DAC1);
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM14);
}