/*
 * Copyright 2016 Freescale Semiconductor
 * Copyright 2016-2018 NXP
 * To be used with MCUXpresso Config Tools under its Software License Agreement.
 */

/**
 * Code generation script producing clock_config.c
 *
 */

// SDK header includes
var sdkIncludes = {
  //#include fsl_common.h moved into h file
  fsl_rtc:      {name: "rtc",   code: "#include \"fsl_rtc.h\"",   configuration: "", condition: false},
  fsl_power:    {name: "power", code: "#include \"fsl_power.h\"", configuration: "", condition: true},
  fsl_clock:    {name: "clock", code: "#include \"fsl_clock.h\"", configuration: "", condition: true},
  clock_config: {name: "",   code: "#include \"clock_config.h\"", configuration: "", condition: true},
}

/**
 * Writes How to setup comment block
 *
 */
function writeHowToSetupComment() {
  CWriter.writeLines([
  "/***********************************************************************************************************************",
  " * This file was generated by the MCUXpresso Config Tools. Any manual edits made to this file",
  " * will be overwritten if the respective MCUXpresso Config Tools is used to update this file.",
  " **********************************************************************************************************************/",
  "/*",
  " * How to set up clock using clock driver functions:",
  " *",
  " * 1. Setup clock sources.",
  " *",
  " * 2. Setup voltage for the fastest of the clock outputs",
  " *",
  " * 3. Set up wait states of the flash.",
  " *",
  " * 4. Set up all dividers.",
  " *",
  " * 5. Set up all selectors to provide selected clocks.",
  " */"]);
}

// get function called from BOARD_InitBootClocks() function and create BOARD_InitBootClocks() function body
function getInitBootClocksFunctions() {
  var initFunctions = new Array();
  initFunctions.push('/*******************************************************************************');
  initFunctions.push(' ************************ BOARD_InitBootClocks function ************************');
  initFunctions.push(' ******************************************************************************/');

  initFunctions.push('void BOARD_InitBootClocks(void)');
  initFunctions.push('{');

  for (var cfgIndex = 0; cfgIndex < Gen.configs.length; cfgIndex++) {
    var cfg = Gen.configs[cfgIndex];
    if (cfg.isCalledFromDefaultInit()) {
      initFunctions.push('    ' + Gen.getConfigID(cfg) + '();');
    }
  }

  initFunctions.push('}');
  initFunctions.push('');
  return initFunctions;
}

/**
 * Generate code which calls sdk function for configuration of given peripheral clock selector
 *
 * return value - string lines in array 
 */
function generateSetPeripheralClock(cfg, elementIds, bitFieldIds, functionName, comment) {
  var allElementsExist = true;
  for (elementIdIndex = 0; elementIdIndex < elementIds.length; elementIdIndex++) {
    if (!HwAbstr.clockElementExist(cfg, elementIds[elementIdIndex]) || !HwAbstr.isClockElementUsed(cfg, elementIds[elementIdIndex])) {
      allElementsExist = false;
    }
  }
  if (allElementsExist) {
      CWriter.line("    /* " + comment + " */");
      var code = "    " + functionName + "(";
      var parameters = new Array();
      for (bitFieldIdIndex = 0; bitFieldIdIndex < bitFieldIds.length; bitFieldIdIndex++) {
        parameters.push(OutputUtils.getFieldValue(cfg, bitFieldIds[bitFieldIdIndex], ""));
      }
      code += parameters.join(", ") + ");";
      CWriter.line(code);
  }
}

function getCCondition(condId) {
  var conditions = {
    "dmx32" : "FSL_CLOCK_DRIVER_VERSION >= MAKE_VERSION(2, 2, 0)",   // [KPSDK-9157] (.dmx32) - Update CLOCK_SetFeiMode/CLOCK_SetFbiMode/CLOCK_BootToFeiMode() to support set MCG_C4[DMX32]=1 in FEI/FBI modes.
  };
  if (conditions[condId] == null) {
    scriptApi.logError("[DEBUG] Unknown value for " + condId + " (see getCCondition())."); 
  }
  return conditions[condId];  
}
   
   
  function isRealClockElementUsed(cfg, id) {
    settingId =  id + ".enable";
    var setting = cfg.getValueAsText(settingId);
    if (setting == null) {
      scriptApi.logError("[DEBUG] Unknown id: " + settingId); 
      return false; 
    } 
    return (setting == "true");
  }   
/**
 * Generates one boot function for a given clock configuration
 *
 * @param cfg Clock configuration object
 */
function GenerateBootFunction(cfg) {
  var configID = Gen.getConfigID(cfg);
  //var powerMode = HwAbstr.getPowerMode(cfg);
  //var pllMode = HwAbstr.getPLLMode(cfg);  
  
  CWriter.line("void " + configID + "(void)");
  CWriter.line("{");
  CWriter.line("#ifndef SDK_SECONDARY_CORE");
  
    /* Power up the clock sources */
    CWriter.line("    /*!< Set up the clock sources */");
    
    /* FRO OSC setup - begin */    
    CWriter.line("    /*!< Set up FRO */");
    /* Enable the FRO for safety switching */
    CWriter.line("    POWER_DisablePD(kPDRUNCFG_PD_FRO_EN);                   /*!< Ensure FRO is on  */");
    CWriter.line("    CLOCK_SetupFROClocking(12000000U);                    /*!< Set up FRO to the 12 MHz, just for sure */");   
        
    /* Cores at first */
    CWriter.line("    CLOCK_AttachClk(kFRO12M_to_MAIN_CLK);                  /*!< Switch to FRO 12MHz first to ensure we can change voltage without accidentally");
    CWriter.line("                                                                being below the voltage for current speed */");
    /* Core clock branch */    
    var coreFreq = cfg.getValueAsText("System_clock.outFreq");
    if (coreFreq != "N/A") {                      
      coreFreq = cfg.getValueAsBigInteger("System_clock.outFreq"); 
    }     
    var coreFreqIsBigger = BigNumber.compare(coreFreq, 12000000);
    
    var outputs = {
      "ADC_clock": "ADC clock output",
      "ASYNCAPB_clock": "Async APB clock output",
      "CLKOUT_clock": "CLKOUT clock output",
      "DMIC_clock": "DMIC clock output",
      "FXCOM0_clock": "FXCOM0 clock output",
      "FXCOM1_clock": "FXCOM1 clock output",
      "FXCOM2_clock": "FXCOM2 clock output",
      "FXCOM3_clock": "FXCOM3 clock output",
      "FXCOM4_clock": "FXCOM4 clock output",
      "FXCOM5_clock": "FXCOM5 clock output",
      "FXCOM6_clock": "FXCOM6 clock output",
      "FXCOM7_clock": "FXCOM7 clock output",
      "FXCOMs_CLK32K_clock": "FXCOMs CLK32K clock output",
      "Master_clock": "Master clock output",
      "System_clock": "System clock output",
      "SYSTICK_clock": "SYSTICK clock output",
      "TRACE_clock": "Trace clock output",
      "USB_clock": "USB clock output"
    }
    var fastestFreq = coreFreq;
    var fastestOutput = "System_clock";
    for (eachOutput in outputs) {
      var eachOutputFreq = cfg.getValueAsText(eachOutput + ".outFreq");
      if (eachOutputFreq != "N/A") {                      
        eachOutputFreq = cfg.getValueAsBigInteger(eachOutput + ".outFreq");
      }     
      var eachOutputFreqIsBigger = BigNumber.compare(eachOutputFreq, fastestFreq);
      if (eachOutputFreqIsBigger >  0) {
        fastestFreq = eachOutputFreq;
        fastestOutput = eachOutput;
      }
    }
    CWriter.line("    POWER_SetVoltageForFreq(" + fastestFreq + "U);             /*!< Set voltage for the one of the fastest clock outputs: " + outputs[fastestOutput] + " */");    
    if (coreFreqIsBigger > 0) {
      CWriter.line("    CLOCK_SetFLASHAccessCyclesForFreq(" + coreFreq + "U);   /*!< Set FLASH wait states for core */");
    } 
    else {
      CWriter.line("    CLOCK_SetFLASHAccessCyclesForFreq(" + 12000000 + "U);   /*!< Set FLASH wait states for core */");
    }
    CWriter.line("");
    /* FRO OSC setup - end */
    
    /* RTC OSC setup - begin */
    if (cfg.existsId("RTC.rtc_32k_osc") && isRealClockElementUsed(cfg, "RTC.rtc_32k_osc")) {
      CWriter.line("    /*!< Set up RTC OSC */");
      
      var rtcCtrlVal = "";      
      var rtc_osc_bypassVal = cfg.getBitFieldValueAsBigInteger("RTC::CTRL"," ");    
      if (rtc_osc_bypassVal != 0) {
        var rtcCtrlComment = "";
        rtcCtrlVal = rtcCtrlVal.concat("RTC_CTRL_RTC_OSC_BYPASS_MASK");
        rtcCtrlComment = rtcCtrlComment.concat("RTC oscillator is bypassed");
        var rtc_osc_powerDownVal = cfg.getBitFieldValueAsBigInteger("RTC::CTRL","RTC_OSC_PD");        
        if (rtc_osc_powerDownVal != 0) {
          rtcCtrlVal = rtcCtrlVal.concat(" | RTC_CTRL_RTC_OSC_PD_MASK");
          rtcCtrlComment = rtcCtrlComment.concat(" and power-down");
        }
        CWriter.line("    CLOCK_EnableClock(kCLOCK_Rtc);                          /*!< Enable the RTC peripheral clock */");
        CWriter.line("    RTC->CTRL &= ~RTC_CTRL_SWRESET_MASK;                    /*!< Make sure the reset bit is cleared */");
        CWriter.line("    RTC->CTRL |= " + rtcCtrlVal + "; /*!< " + rtcCtrlComment + " */");
      } 
      else {
        sdkIncludes["fsl_rtc"]["condition"] = true;
        sdkIncludes["fsl_rtc"]["configurations"].push(configID);
        CWriter.line("    RTC_Init(RTC);                                                /*!< Setup RTC oscillator */");       
      }    

      var rtcoscctrlVal = "";
      var rtcoscctrlComment = "";
      var rtcoscctrl_enVal = cfg.getBitFieldValueAsBigInteger("SYSCON::RTCOSCCTRL","EN");
      if (rtcoscctrl_enVal != 0) {
        rtcoscctrlVal = rtcoscctrlVal.concat("SYSCON_RTCOSCCTRL_EN_MASK");
        rtcoscctrlComment = "RTC oscillator 32kHz output is available to the other modules";
      }
      else {
        rtcoscctrlVal = "0x0U";
        rtcoscctrlComment = "RTC oscillator 32kHz output is not available";
      }    
      
      CWriter.line("    SYSCON->RTCOSCCTRL = " + rtcoscctrlVal + ";               /*!< " + rtcoscctrlComment + " */");
      CWriter.line("");
    }
    /* RTC OSC setup - end */
    
    /* WDT OSC setup - begin */
    if (cfg.existsId("SYSCON.WDT_DIV") && isRealClockElementUsed(cfg, "SYSCON.WDT_DIV")) {
      CWriter.line("    /*!< Set up WDT OSC */");
      CWriter.line("    POWER_EnablePD(kPDRUNCFG_PD_WDT_OSC);                    /*!< Ensure WDT OSC is off for setup changes */");
      var freqselVal = cfg.getBitFieldValueAsBigInteger("SYSCON::WDTOSCCTRL","FREQSEL");                  
      var divselVal = cfg.getBitFieldValueAsBigInteger("SYSCON::WDTOSCCTRL","DIVSEL");                        
      var wdtoscctrlVal = "(SYSCON_WDTOSCCTRL_DIVSEL("+ divselVal + ") | SYSCON_WDTOSCCTRL_FREQSEL("+ freqselVal + "))";
      CWriter.line("    SYSCON->WDTOSCCTRL = " + wdtoscctrlVal + ";              /*!< Setup WDT oscillator */");    
      CWriter.line("    POWER_DisablePD(kPDRUNCFG_PD_WDT_OSC);                   /*!< Ensure WDT OSC is on */");
      CWriter.line("");
    }
    /* WDT OSC setup - end */

    var selSources = {
      "SYSCON.clk_in": "kEXT_CLK",
      "SYSCON.mclk_in": "kMCLK",
      "SYSCON.fro_hf": "kFRO_HF", 
      "SYSCON.fro_12m": "kFRO12M", 
      "SYSCON.fro_12m_clk": "kFRO12M", 
      "SYSCON.WDT_DIV": "kWDT_OSC", 
      "RTC.rtc_osc_32k_clk": "kOSC32K", 
      "SYSCON.PLL_BYPASS": "kSYS_PLL",       
      "SYSCON.MAINCLKSELB": "kMAIN_CLK", 
      "SYSCON.main_clk": "kMAIN_CLK", 
      "SYSCON.MAINCLKSELA": "tunel2MAINCLKSELA_source",
      "SYSCON.FRGCTRL_DIV": "kFRG", 
      "NO_CLOCK": "kNONE"      
    }
    
    var sels = {
      "SYSCON.MAINCLKSELB": "MAIN_CLK",
      //"SYSCON.SYSPLLCLKSEL": "SYS_PLL",
      "ASYNC_SYSCON.ASYNCAPBCLKSELA": "ASYNC_APB", 
      "SYSCON.ADCCLKSEL": "ADC_CLK", 
      "SYSCON.FXCOMCLKSEL0": "FLEXCOMM0", 
      "SYSCON.FXCOMCLKSEL1": "FLEXCOMM1", 
      "SYSCON.FXCOMCLKSEL2": "FLEXCOMM2", 
      "SYSCON.FXCOMCLKSEL3": "FLEXCOMM3", 
      "SYSCON.FXCOMCLKSEL4": "FLEXCOMM4", 
      "SYSCON.FXCOMCLKSEL5": "FLEXCOMM5", 
      "SYSCON.FXCOMCLKSEL6": "FLEXCOMM6", 
      "SYSCON.FXCOMCLKSEL7": "FLEXCOMM7", 
      "SYSCON.FRGCLKSEL": "FRG",       
      "SYSCON.MCLKCLKSEL": "MCLK", 
      "SYSCON.DMICCLKSEL": "DMIC",
      "SYSCON.USBCLKSEL": "USB_CLK", 
      "SYSCON.CLKOUTSELA": "CLKOUT"
    }
    
    
    /* PLL setup - begin */    
    var pllClkSel = "SYSCON.SYSPLLCLKSEL";    
    if (cfg.existsId(pllClkSel) && isRealClockElementUsed(cfg, pllClkSel)) {
      CWriter.line("    /*!< Set up PLL */");
      //scriptApi.logWarning(cfg.getValueAsText("PLL_Mode"));      
      var actPllSrcValue = selSources[cfg.getValueAsText(pllClkSel + ".sel")];
      var srcPllDescr = actPllSrcValue.substr(1);
      var selectorName = "SYS_PLL"
      var wholeSignalName = actPllSrcValue + "_to_" + selectorName;
      CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch PLL clock source selector to " + srcPllDescr + " */");        
      
      var actPLLmode = cfg.getValueAsText("PLL_Mode");
      var syspllctrlVal = "";  
      var syspllssctrlVal0 = "";
      var syspllssctrlVal1 = "";
      var mdecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLSSCTRL0","MDEC");    
      var pllWaitForLock = true;
      var reasonNoWait = "";
      
      if (actPLLmode == "Normal") {
        syspllctrlVal = syspllctrlVal.concat("SYSCON_SYSPLLCTRL_BANDSEL_MASK");      
        syspllssctrlVal0 = syspllssctrlVal0.concat("(SYSCON_SYSPLLSSCTRL0_MDEC("+ mdecVal + "U) | SYSCON_SYSPLLSSCTRL0_SEL_EXT_MASK)");
        syspllssctrlVal1 = "0x0U";
        var actSyspllclksel = cfg.getValueAsText("SYSCON.SYSPLLCLKSEL.sel");
        if (actSyspllclksel == "RTC.rtc_osc_32k_clk") {
          syspllctrlVal = syspllctrlVal.concat(" | SYSCON_SYSPLLCTRL_SELI(1U) | SYSCON_SYSPLLCTRL_SELP(6U)");
        }
        else {      
          var seli = 0;
          var selp = 0;
          var multiply_by = cfg.getValueAsBigInteger("SYSCON.M_MULT.scale");
          
          if (multiply_by < 60)
          {
            seli = (multiply_by & 0x3c) + 4;
            selp = (multiply_by >> 1) + 1;
          }
          else
          {
            selp = 31;
            if (multiply_by > 16384)
            {
                seli = 1;
            }
            else if (multiply_by > 8192)
            {
                seli = 2;
            }
            else if (multiply_by > 2048)
            {
                seli = 4;
            }
            else if (multiply_by >= 501)
            {
                seli = 8;
            }
            else
            {
                seli = 4 * (1024 / (multiply_by + 9));
            }
          }            
          syspllctrlVal = syspllctrlVal.concat(" | SYSCON_SYSPLLCTRL_SELI(" + seli + "U) | SYSCON_SYSPLLCTRL_SELP(" + selp + "U)");
        }
      }
      else {
        pllWaitForLock = false;
        reasonNoWait = "PLL in Fractional/Spread spectrum mode"
        syspllctrlVal = syspllctrlVal.concat("SYSCON_SYSPLLCTRL_UPLIMOFF_MASK");    
        syspllssctrlVal0 = "0x0U";
        var ss_progmodfm_t = {
          0: "kSS_MF_512",      
          1: "kSS_MF_384", 
          2: "kSS_MF_256", 
          3: "kSS_MF_128", 
          4: "kSS_MF_64", 
          5: "kSS_MF_32", 
          6: "kSS_MF_24", 
          7: "kSS_MF_16" 
        }
        
        var ss_progmoddp_t = {
          0: "kSS_MR_K0",      
          1: "kSS_MR_K1", 
          2: "kSS_MR_K1_5", 
          3: "kSS_MR_K2", 
          4: "kSS_MR_K3", 
          5: "kSS_MR_K4", 
          6: "kSS_MR_K6", 
          7: "kSS_MR_K8" 
        }
        
        var ss_modwvctrl_t = {
          0: "kSS_MC_NOC",      
          1: "kSS_MC_RECC", 
          2: "kSS_MC_MAXC",
        }
        
        var mdVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLSSCTRL1","MD");
        var mfVal = cfg.getBitFieldValue("SYSCON::SYSPLLSSCTRL1","MF");
        var mrVal = cfg.getBitFieldValue("SYSCON::SYSPLLSSCTRL1","MR");
        var mcVal = cfg.getBitFieldValue("SYSCON::SYSPLLSSCTRL1","MC");
        var pdVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLSSCTRL1","PD");
        var ditherVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLSSCTRL1","DITHER");      
        mfEnumVal = ss_progmodfm_t[mfVal];
        mrEnumVal = ss_progmoddp_t[mrVal];
        mcEnumVal = ss_modwvctrl_t[mcVal];      
        syspllssctrlVal1 = syspllssctrlVal1.concat("(SYSCON_SYSPLLSSCTRL1_MD("+ mdVal + "U) | (uint32_t)("+ mfEnumVal + ") | (uint32_t)("+ mrEnumVal + ") | (uint32_t)("+ mcEnumVal + ")");
        if (pdVal != 0) {
          syspllssctrlVal1 = syspllssctrlVal1.concat(" | SYSCON_SYSPLLSSCTRL1_PD_MASK");
        }
        if (ditherVal != 0) {
          syspllssctrlVal1 = syspllssctrlVal1.concat(" | SYSCON_SYSPLLSSCTRL1_DITHER_MASK");
        }
        syspllssctrlVal1 = syspllssctrlVal1.concat(")");
        
      }
      
      var bypassccodiv2Val = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLCTRL","BYPASSCCODIV2");
      if (bypassccodiv2Val != 0) {
        var syspllctrlVal = syspllctrlVal.concat(" | SYSCON_SYSPLLCTRL_BYPASSCCODIV2_MASK");
      }
      
      var directoVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLCTRL","DIRECTO");
      if (directoVal != 0) {
        var syspllctrlVal = syspllctrlVal.concat(" | SYSCON_SYSPLLCTRL_DIRECTO_MASK");
      }
      
      var pllbypassVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLCTRL","BYPASS");
      var pllInpFreq = cfg.getValueAsText("SYSCON.SYSPLLCLKSEL.outFreq");
    
      if (pllInpFreq != "N/A") {                      
        pllInpFreq = cfg.getValueAsBigInteger("SYSCON.SYSPLLCLKSEL.outFreq"); 
      }           
      
      if (pllInpFreq < 100000) {
        pllWaitForLock = false;
        reasonNoWait = "PLL input less than 100 kHz"
      }
      else if (pllInpFreq > 20000000) {        
        pllWaitForLock = false;
        reasonNoWait = "PLL input more than 20 MHz"
      }
      var flagsVal = "";
      if (pllbypassVal != 0) {
        var syspllctrlVal = syspllctrlVal.concat(" | SYSCON_SYSPLLCTRL_BYPASS_MASK");        
        flagsVal = "PLL_SETUPFLAG_POWERUP";
      }
      else if (pllWaitForLock != true) {
        flagsVal = "PLL_SETUPFLAG_POWERUP";
      }
      else
      {
        flagsVal = "PLL_SETUPFLAG_WAITLOCK";
      }

      var syspllndecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLNDEC","NDEC");
      var syspllpdecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLPDEC","PDEC");
      
      var pllFreq = cfg.getValueAsText("SYSCON.PLL_BYPASS.outFreq");
    
      if (pllFreq != "N/A") {                      
        pllFreq = cfg.getValueAsBigInteger("SYSCON.PLL_BYPASS.outFreq"); 
      } 

      CWriter.line("    const pll_setup_t pllSetup = {");
      CWriter.line("        .syspllctrl = " + syspllctrlVal + ",");
      CWriter.line("        .syspllndec = SYSCON_SYSPLLNDEC_NDEC(" + syspllndecVal + "U),");
      CWriter.line("        .syspllpdec = SYSCON_SYSPLLPDEC_PDEC(" + syspllpdecVal + "U),");
      CWriter.line("        .syspllssctrl = {" + syspllssctrlVal0 + "," + syspllssctrlVal1 + "},");    
      if (flagsVal != "") {
        CWriter.line("        .pllRate = " + pllFreq + "U,");
        CWriter.line("        .flags =  " + flagsVal);
      }
      else
      {
        CWriter.line("        .pllRate = " + pllFreq + "U");
      }
      CWriter.line("    };");
      CWriter.line("    CLOCK_SetPLLFreq(&pllSetup); /*!< Configure PLL to the desired values */"); 
      CWriter.line("");
      if ((pllWaitForLock != true) && (flagsVal != "")) {
        CWriter.line("    /* " + reasonNoWait + " */");
        var useSystick = true;
        if (useSystick) {
          CWriter.line("    /* SYSTICK is used for waiting for PLL stabilization */");
          CWriter.line("");
          
          var systickScale = 0;
          var systickDiv = "kCLOCK_DivSystickClk";
          CWriter.line("    CLOCK_SetClkDiv(" + systickDiv + ", "  + systickScale + "U, true);              /*!< Reset SysTick divider counter and halt it */");
          systickScale = 3;
          CWriter.line("    CLOCK_SetClkDiv(" + systickDiv + ", "  + systickScale + "U, false);             /*!< Set SysTick divider to value " + systickScale + " */");
          
          var count = 27999;
          
          CWriter.line("    SysTick->LOAD = " + count + "UL;                              /*!< Set SysTick count value */");
          CWriter.line("    SysTick->VAL = 0UL;                                           /*!< Reset current count value */");
          CWriter.line("    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;                      /*!< Enable SYSTICK */");
          CWriter.line("    while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) != SysTick_CTRL_COUNTFLAG_Msk){}   /*!< Waiting for PLL stabilization */");
          CWriter.line("    SysTick->CTRL = 0UL;                                          /*!< Stop SYSTICK */");
        }
        else {
          CWriter.line("    for(uint32_t i=0UL; i < 100000UL; i++){}   /* Waiting for PLL stabilization */");
        }      
        CWriter.line("");
      }
    }   
    /* PLL setup - end */    
    
    var usedFRO_HF  = false;
    for (eachSel in sels) {      
      if (cfg.existsId(eachSel) && isRealClockElementUsed(cfg, eachSel)) {
        var actSrcValue = selSources[cfg.getValueAsText(eachSel + ".sel")];        
        if (actSrcValue == "tunel2MAINCLKSELA_source") {          
          actSrcValue = selSources[cfg.getValueAsText("SYSCON.MAINCLKSELA.sel")];          
        }
        if ((!usedFRO_HF && actSrcValue == "kFRO_HF")) {
          usedFRO_HF = true;          
          var froFreq = cfg.getValueAsText("SYSCON.fro_hf.outFreq");
          if (froFreq != "N/A") {                      
            froFreq = cfg.getValueAsBigInteger("SYSCON.fro_hf.outFreq"); 
          }   
          CWriter.line("    CLOCK_SetupFROClocking(" + froFreq + "U);              /*!< Set up high frequency FRO output to selected frequency */");        
          CWriter.line("");
        }  
      }  
    }        
    CWriter.line("    /*!< Set up dividers */");
    /* Set up dividers  */
    var divs = {
      "SYSCON.AHBCLKDIV": "kCLOCK_DivAhbClk", 
      "SYSCON.SYSTICKCLKDIV": "kCLOCK_DivSystickClk",      
      "SYSCON.TRACECLKDIV": "kCLOCK_DivTraceClk",      
      "SYSCON.CLKOUTDIV": "kCLOCK_DivClkOut", 
      "SYSCON.ADCCLKDIV": "kCLOCK_DivAdcAsyncClk", 
      "SYSCON.USBCLKDIV": "kCLOCK_DivUsbClk", 
      "SYSCON.FRGCTRL_MUL": "kCLOCK_DivFrg",       
      "SYSCON.DMICCLKDIV": "kCLOCK_DivDmicClk", 
      "SYSCON.MCLKDIV": "kCLOCK_DivFxI2s0MClk"
    }
    var systickClkHaltVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSTICKCLKDIV","HALT");
    var traceClkHaltVal = cfg.getBitFieldValueAsBigInteger("SYSCON::TRACECLKDIV","HALT");
    for (eachDiv in divs) {      
      if (cfg.existsId(eachDiv) && isRealClockElementUsed(cfg, eachDiv)) {
        var actDiv = divs[eachDiv];
        var divDescr = eachDiv.substr(7);        
        var actScale = 0;
        if (actDiv == "kCLOCK_DivAhbClk"){
          actScale = cfg.getValueAsText(eachDiv + ".scale");
          CWriter.line("    CLOCK_SetClkDiv(" + actDiv + ", "  + actScale + "U, false);                  /*!< Set " + divDescr + " divider to value " + actScale + " */");  
        }
        else if (actDiv == "kCLOCK_DivSystickClk") {
          if (systickClkHaltVal != 1) {
            actScale = 0;
            CWriter.line("    CLOCK_SetClkDiv(" + actDiv + ", "  + actScale + "U, true);                  /*!< Reset " + divDescr + " divider counter and halt it */");
            actScale = cfg.getValueAsText(eachDiv + ".scale");
            CWriter.line("    CLOCK_SetClkDiv(" + actDiv + ", "  + actScale + "U, false);                  /*!< Set " + divDescr + " divider to value " + actScale + " */");  
          }
        }
        else if (actDiv == "kCLOCK_DivTraceClk") {
          if (traceClkHaltVal != 1) {          
            actScale = 0;
            CWriter.line("    CLOCK_SetClkDiv(" + actDiv + ", "  + actScale + "U, true);                  /*!< Reset " + divDescr + " divider counter and halt it */");
            actScale = cfg.getValueAsText(eachDiv + ".scale");
            CWriter.line("    CLOCK_SetClkDiv(" + actDiv + ", "  + actScale + "U, false);                  /*!< Set " + divDescr + " divider to value " + actScale + " */");  
          }
        }
        else {
          actScale = 0;
          CWriter.line("    CLOCK_SetClkDiv(" + actDiv + ", "  + actScale + "U, true);                  /*!< Reset " + divDescr + " divider counter and halt it */");
          actScale = cfg.getValueAsText(eachDiv + ".scale");
          CWriter.line("    CLOCK_SetClkDiv(" + actDiv + ", "  + actScale + "U, false);                  /*!< Set " + divDescr + " divider to value " + actScale + " */");        
        }
      }
    }        
    CWriter.line("");
    
    /* Attach clocks to the peripheries */       
    CWriter.line("    /*!< Set up clock selectors - Attach clocks to the peripheries */");
  
    var asyncApbSubsysVal = cfg.getBitFieldValueAsBigInteger("SYSCON::ASYNCAPBCTRL","ENABLE");
    if (asyncApbSubsysVal != 0) {
      CWriter.line("    SYSCON->ASYNCAPBCTRL = SYSCON_ASYNCAPBCTRL_ENABLE_MASK;       /*!< Enable ASYNC APB subsystem */");
    }
    
    var usedMAINCLKSELA = false;    
    for (eachSel in sels) {      
      if (cfg.existsId(eachSel) && isRealClockElementUsed(cfg, eachSel)) {
        var actSrcValue = selSources[cfg.getValueAsText(eachSel + ".sel")];
        var srcDescr = actSrcValue.substr(1);
        if (actSrcValue == "tunel2MAINCLKSELA_source") {
          usedMAINCLKSELA = true;
          actSrcValue = selSources[cfg.getValueAsText("SYSCON.MAINCLKSELA.sel")];
          srcDescr = actSrcValue.substr(1);
        }
        var actSel = sels[eachSel];        
        var wholeSignalName = actSrcValue + "_to_" + actSel;
        if (wholeSignalName != "kMAIN_CLK_to_USB_CLK") {
          if (actSel == "FRG") {            
            var multValue = cfg.getBitFieldValue("SYSCON::FRGCTRL", "MULT");            
            CWriter.line("    SYSCON->FRGCTRL = ((SYSCON->FRGCTRL & ~SYSCON_FRGCTRL_MULT_MASK) | SYSCON_FRGCTRL_MULT(" + multValue + "U)); /*!< Set FRG MULT to value " + multValue + " */");
            CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");        
          } 
          else if (actSel == "ASYNC_APB") {
            if (asyncApbSubsysVal != 0) {
              CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");        
            }
          }
          else {
            CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");        
          }
        }                    
        else {
          /* Exception for the non-existent kMAIN_CLK_to_USB_CLK = MUX_A(CM_USBCLKSEL, 2), will be in the next SDK update, cause I add it there */
          CWriter.line("    SYSCON->USBCLKSEL = ((SYSCON->USBCLKSEL & ~SYSCON_USBCLKSEL_SEL_MASK) | SYSCON_USBCLKSEL_SEL(" + cfg.getBitFieldValue("SYSCON::USBCLKSEL", "SEL") + "U)); /*!< Switch USB_CLK to " + srcDescr + " in this way, cause " + wholeSignalName + " value is missing in the clock_attach_id_t enumeration */");
        }          
      }
    }
    
    if (usedMAINCLKSELA == false) {
      if (cfg.existsId("SYSCON.MAINCLKSELA") && isRealClockElementUsed(cfg, "SYSCON.MAINCLKSELA")) {
        var actSrcValue = selSources[cfg.getValueAsText("SYSCON.MAINCLKSELA.sel")];
        var srcDescr = actSrcValue.substr(1);
        CWriter.line("    SYSCON->MAINCLKSELA = ((SYSCON->MAINCLKSELA & ~SYSCON_MAINCLKSELA_SEL_MASK) | SYSCON_MAINCLKSELA_SEL(" + cfg.getBitFieldValue("SYSCON::MAINCLKSELA", "SEL") + "U)); /*!< Switch MAINCLKSELA to " + srcDescr + " even it is not used for MAINCLKSELB */");
      }
    } 
    /* Disable FRO OSC if the user don't want it */
    if (cfg.existsId("SYSCON.fro_12m") && isRealClockElementUsed(cfg, "SYSCON.fro_12m")) {      
    }  
    else {
      CWriter.line("    POWER_EnablePD(kPDRUNCFG_PD_FRO_EN);                   /*!< Disable FRO OSC if the user don't want it */");
    }

	CWriter.line("    /*< Set SystemCoreClock variable. */");
	CWriter.line("    SystemCoreClock = " + configID.toUpperCase() + "_CORE_CLOCK;");

// Here comes the code called from secondary core
//CWriter.line("#else");
//CWriter.line("  // Initialization code of the secondary core, e.g. power mode");

  CWriter.line("#endif");

  CWriter.line("}");
  CWriter.line("");  
}

function createDependency(module, component, coreID) {
  var dependency = { 
    resourceType: "SWComponent",
    resourceId: "platform.drivers." + module,
    description: "Clocks initialization requires the " + module.toUpperCase() + " Driver in the project.",
    problem_level: 2,
    source: "Clocks:" + component,
    features:
    [{ feature_name: "enabled",
        evaluation: "equal",
        configuration: coreID,
        data: [{
          value: "true",
          unit: null,
          min:  null,
          max: null
       }]
    }]   
  };
  return dependency;
}

function coreUsed(coreID) {
  return ((coreList[coreID]['role'] != 'unused') && (coreList[coreID]['role'] != 'unspecified'));
}

function pushDependency(dependencyHelper, dependency) {
  if (coreUsed(dependency['features'][0]['configuration'])) {
    dependencyHelper.addDependency(dependency);
  }
}
  
/////////////////// code generation  //////////////////////////////////

/*
// write header to the output
CWriter.write(CommonHeaderText());
CWriter.write("");
*/
// write How to setup comment
writeHowToSetupComment();
CWriter.write("");
// write global Yaml block
if (Gen.profile.getYaml() != null) {
  CWriter.write("/* clang-format off */");
  CWriter.write(Gen.profile.getYaml());
  CWriter.write("/* clang-format on */");
} 
CWriter.write("");
// write block of #includes
var optionalIncludesPosition = CWriter.line(null);
CWriter.write("");

// allocate place for block of #defines 
var defineBlockPosition = CWriter.line(null);
// create block instance of #defines 
var defineBlockC = new DefineBlock();
SDKMapper.setCWriterDefineContainer = defineBlockC;   
var defineBlockLinesC = OutputUtils.getDefinitionIntro();

//write common variables introduction
CWriter.writeLines(OutputUtils.getVariablesIntro());
CWriter.writeLines([
"/* System clock frequency. */",
"extern uint32_t SystemCoreClock;",
]);
CWriter.write("");
  
// allocate place for FllStableDelay, InitRtc,... functions 
var internalFunctionHeaderPosition = CWriter.line(null);
var internalFunctionPosition = CWriter.line(null);
var fllStableDelayFunctionReq = false;
var initRtcFunctionReq = false;
var enableRtcOscFunctionReq = false;
var enableIrc48MOscReq = false;
var externSetRtcClockReq = false;
var externSetUsbPhyClockReq = false;
var externSetI2s0MasterClockReq = false;
var externSetModemClockReq = false;
var enableUsbPhyPllReq = false;
var enableUsbPhyPfdClockReq = false;
var setUsbSlowClockReq = false;
var setFllExtRefDivReq = false;
var setPllClkSelReq = false;
var setFlexioS0ClockReq = false;
var setI2s0MasterClockReq = false;
var setPllFllSelDivFracReq = false;
var setModemClockReq = false;

// write clock initialization functions
CWriter.writeLines(getInitBootClocksFunctions());

for (var cfgIndex = 0; cfgIndex < Gen.configs.length; cfgIndex++) {
  var cfg = Gen.configs[cfgIndex];
  // write configuration introduction
  CWriter.writeLines(OutputUtils.getConfigurationIntro(cfg));
  // write Yaml block
  if (cfg.getYaml() != null) {
    CWriter.write("/* clang-format off */");
    CWriter.write(cfg.getYaml());
    CWriter.write("/* clang-format on */");
  }
  CWriter.line("");

  // write variables introduction
  CWriter.writeLines(OutputUtils.getVariablesIntro(cfg));
  // generate the structure variable for each configuration 
  

  // write code introduction
  CWriter.writeLines(OutputUtils.getCodeIntro(cfg));
  // write boot function
  GenerateBootFunction(cfg);
}

// write "CODE" header if an internal function is generated
if (!CWriter.isPositionEmpty(internalFunctionPosition)) {
  CWriter.writeLines(OutputUtils.getCodeIntro(), internalFunctionHeaderPosition);
}

// complete defines block and write it
defineBlockLinesC = defineBlockLinesC.concat(defineBlockC.getDefineBlock()).concat("");
CWriter.writeLines(defineBlockLinesC, defineBlockPosition);

// generate #includes and register its dependencies
var dependencyHelper = scriptApi.getDependencyHelper();


var coreListTxt = scriptApi.getProfile().getMcuInfo().getCoresList();
var coreList = JSON.parse(coreListTxt);
var coreIds = Object.keys(coreList);
for (var coreId = 0; coreId < coreIds.length; coreId++) {
  for(var configID = 0; configID < Gen.configs.length; configID++) {
    pushDependency(dependencyHelper, createDependency("common", Gen.getConfigID(Gen.configs[configID]), coreIds[coreId]));
  }
}

//pushDependency(dependencyHelper, createDependency("common", Gen.getConfigID(Gen.configs[0])));
sdkIncludes["fsl_clock"]["configurations"] = Gen.configs;
sdkIncludes["fsl_power"]["configurations"] = Gen.configs;

var sdkIncludesKeys = Object.keys(sdkIncludes);

for (includeIndex = 0; includeIndex < sdkIncludesKeys.length; includeIndex++) {
  var includeItem = sdkIncludes[sdkIncludesKeys[includeIndex]];
  if (includeItem["condition"]) {
    CWriter.write(includeItem["code"], optionalIncludesPosition);
    if (includeItem["name"] != "") {
      for (var coreId = 0; coreId < coreIds.length; coreId++) {
        for(var configID = 0; configID < includeItem["configurations"].length; configID++) {
          pushDependency(dependencyHelper, createDependency(includeItem["name"], Gen.getConfigID(includeItem["configurations"][configID]), coreIds[coreId]));
        }
      }
    }
  }
}