#include "board.h"
#include "mik32_hal_pcc.h"
#include "mik32_hal_irq.h"
#include "Arduino.h"

// --------------------- flash cache limit --------------------- // 
#include "mcu32_memory_map.h"
#include "mik32_hal_spifi_w25.h"

// define board flash size
#if defined(ARDUINO_START_MIK32_V1)
    #define FLASH_SIZE  0x00400000  // 4 Mb
#elif defined(ARDUINO_ACE_UNO_16MB)
    #define FLASH_SIZE  0x01000000  // 16 Mb
#elif defined(ARDUINO_ACE_UNO_32MB)
    #define FLASH_SIZE  0x02000000  // 32 Mb
#else
    #define FLASH_SIZE  0x00800000  // 8 Mb
#endif
// define margin from flash end
#define FLASH_END_OFFSET    0x1000

// values from ld script
extern uint8_t __RODATA__[];

// RAM-function for updating flash cache limit by app size to improve performance
__attribute__((section(".ram_text"))) void updateCacheLimit(void)
{
    uint32_t new_limit = (uintptr_t)__RODATA__ - SPIFI_BASE_ADDRESS;
    // limit cache size by flash size with margin
    if (new_limit > (FLASH_SIZE - FLASH_END_OFFSET))
        new_limit = FLASH_SIZE - FLASH_END_OFFSET;

    uint32_t MCMDbackup = SPIFI_CONFIG->MCMD;           // save current value from MCMD
    SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_RESET_M;    // reset MCMD mode for writing new CLIMIT
    SPIFI_CONFIG->CLIMIT = new_limit;                   // set new CLIMIT
    SPIFI_CONFIG->MCMD = MCMDbackup;                    // restore MCMD value
}

// --------------------- init --------------------- // 
// called from crt0.S before constructors initialization
extern "C" void SystemInit(void)
{
    // set irq vector to ram region
    write_csr(mtvec, 0x02000000);

    HAL_Init();

    // gpio clock
    __HAL_PCC_GPIO_0_CLK_ENABLE();
    __HAL_PCC_GPIO_1_CLK_ENABLE();
    __HAL_PCC_GPIO_2_CLK_ENABLE();
    __HAL_PCC_GPIO_IRQ_CLK_ENABLE();
   
    // for delays
    SysTick_Init();   
    // update flash cache limit by app size
    updateCacheLimit();
}

// called after setup()
void post_init(void)
{
    if(isInterruptsEnabled()) // if user has called noInterrupts() in setup(), this value is false
        interrupts(); // enable global interrupts
}

// --------------------- other --------------------- // 
volatile bool use_error_messages = true;
// print error message to Serial
extern "C" void ErrorMsgHandler(const char * msg)
{
    // function works if Serial is used in sketch and user doesn't turn it off
    #ifdef HardwareSerial_h         
        if(use_error_messages&&Serial)
            Serial.println(msg);
    #endif
}


