/*
 * 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 */");
    
    /* IRC OSC setup - begin */    
    CWriter.line("    /*!< Set up IRC */");
    /* Enable the IRC for safety switching */
    CWriter.line("    POWER_DisablePD(kPDRUNCFG_PD_IRC_OSC);                   /*!< Ensure IRC OSC is on */");
    CWriter.line("    POWER_DisablePD(kPDRUNCFG_PD_IRC);                       /*!< Ensure IRC is on */");
        
    /* Cores at first */
    CWriter.line("    CLOCK_AttachClk(kIRC12M_to_MAIN_CLK);                    /*!< Switch to IRC 12MHz first to ensure we can change voltage without accidentally");
    CWriter.line("                                                                  being below the voltage for current speed */");
    CWriter.line("");
    
    /* RTC OSC setup - begin */
    if (isRealClockElementUsed(cfg, "RTC.rtc_osc_32k_clk")) {
        CWriter.line("    /*!< Set up RTC OSC */");
        CWriter.line("");
    }
    if (cfg.existsId("RTC.rtc_32k_osc")) {
      var rtc_osc_powerDownVal = cfg.getBitFieldValueAsBigInteger("SYSCON::PDRUNCFG","PDEN_32K_OSC");
      if ((rtc_osc_powerDownVal == 0) && isRealClockElementUsed(cfg, "RTC.rtc_32k_osc")) {
          //CWriter.line("    POWER_DisablePD(kPDRUNCFG_PD_32K_OSC);                          /*!< Powered the 32 kHz RTC oscillator */");
      }
      else if (rtc_osc_powerDownVal == 1) {
          CWriter.line("    /*!< Configure RTC OSC */");
          CWriter.line("    POWER_EnablePD(kPDRUNCFG_PD_32K_OSC);                    /*!< Powered down the 32 kHz RTC oscillator */");
          CWriter.line("");
      }
      
      var rtcoscctrl_enVal = cfg.getBitFieldValueAsBigInteger("SYSCON::RTCOSCCTRL","EN");
      if ((rtcoscctrl_enVal == 1) && isRealClockElementUsed(cfg, "RTC.rtc_osc_32k_clk")) {
          //CWriter.line("    SYSCON->RTCOSCCTRL = SYSCON_RTCOSCCTRL_EN_MASK;               /*!< RTC oscillator 32kHz output is available to the other modules */");
      }
      else if ((rtcoscctrl_enVal == 0) && isRealClockElementUsed(cfg, "RTC.rtc_32k_osc")) {
          CWriter.line("    /*!< Configure RTC OSC */");
          CWriter.line("    SYSCON->RTCOSCCTRL = 0x0U;                               /*!< RTC oscillator 32kHz output is not available */");
          CWriter.line("");
      }
    }
    /* RTC OSC setup - end */
    
    /* WDT OSC setup - begin */
    if (cfg.existsId("SYSCON.wdt_osc") && isRealClockElementUsed(cfg, "SYSCON.wdt_osc")) {
      CWriter.line("    /*!< Set up WDT OSC */");
      // CWriter.line("    POWER_EnablePD(kPDRUNCFG_PD_WDT_OSC);                    /*!< Ensure WDT OSC is off for setup changes */");
      CWriter.line("    POWER_DisablePD(kPDRUNCFG_PD_WDT_OSC);                   /*!< Ensure WDT OSC is on */");
      CWriter.line("");
    }
    /* WDT OSC setup - end */

    var selSources = {
      "SYSCON.wdt_osc": "kWDT_OSC", 
      "SYSCON.irc_clk": "kIRC12M", 
      "SYSCON.clk_in": "kCLKIN", 
      "RTC.rtc_osc_32k_clk": "kRTC_OSC", 
      "SYSCON.MAINCLKSELA": "tunel2MAINCLKSELA_source",
      "SYSCON.MAINCLKSELB": "kMAIN_CLK", 
      "SYSCON.SYSPLLCLKSEL": "kSYS_PLL_IN",
      "SYSCON.PLL_BYPASS": "kSYS_PLL_OUT",
      "SYSCON.CLKOUTSELA": "tunel1CLKOUTSELA_source", 

      "SYSCON.main_clk": "kMAIN_CLK",
      "SYSCON.exclk_out": "kCLKIN",
      "SYSCON.pll_clk": "kSYS_PLL_OUT",
      "SYSCON.irc_clk_out": "kIRC12M",
      "SYSCON.wdt_clk": "kWDT_OSC",
      "ASYNC_SYSCON.ASYNCAPBCLKSELA": "tunel3ASYNCAPBCLKSELA_source"
    }
    var sels = {
      "SYSCON.SYSPLLCLKSEL": ["SYS_PLL","kIRC12M"],
      "SYSCON.MAINCLKSELB": ["MAIN_CLK","kIRC12M"],
      "SYSCON.ADCCLKSEL": ["ADC_CLK","kMAIN_CLK"],
      "SYSCON.CLKOUTSELB": ["CLKOUT","kMAIN_CLK"],
      "ASYNC_SYSCON.ASYNCAPBCLKSELB": ["ASYNC_APB","kMAIN_CLK"]
    }
    
    /* PLL setup - begin */    
    var pll = "SYSCON.PLL"
    var pllClkSel = "SYSCON.SYSPLLCLKSEL";
    if (cfg.existsId(pll) && isRealClockElementUsed(cfg, pll)) {
      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 SYSPLLCLKSEL 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 directiVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLCTRL","DIRECTI");
      if (directiVal != 0) {
        var syspllctrlVal = syspllctrlVal.concat(" | SYSCON_SYSPLLCTRL_DIRECTI_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");
      }
      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("    POWER_DisablePD(kPDRUNCFG_PD_SYS_PLL);                   /*!< Ensure PLL is on  */");
      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 7ms 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 */");
        }
      }
    }
    else if (cfg.existsId(pllClkSel) && isRealClockElementUsed(cfg, pllClkSel)) {
        var actPLLmode = cfg.getValueAsText("PLL_Mode");
        if (actPLLmode == "Power_down") {
            CWriter.line("    /*!< PLL is in power_down mode */");
            var pllbypassVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLCTRL","BYPASS");
            if (pllbypassVal == 0) {
            CWriter.line("    SYSCON->SYSPLLCTRL &= ~SYSCON_SYSPLLCTRL_BYPASS_MASK;    /*!< PLL CCO is used to create the PLL output. */");
            }
        }
    }
    CWriter.line("");
    /* PLL setup - end */
	
	/* 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 = {
      "TRACE_clock": "Trace clock output",
      "SYSTICK_clock": "SYSTICK clock output",
      "System_clock": "System clock output",
      "ASYNCADC_clock": "Async ADC clock output",
      "WDT_clock": "WDT clock output",
      "CLKOUT_clock": "CLKOUT clock output",
      "ASYNCAPB_clock": "Async APB 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("");
    /* IRC OSC setup - end */
    
    CWriter.line("    /*!< Set up dividers */");
    /* Set up dividers  */
    var divs = {
      "SYSCON.TRACECLKDIV": ["kCLOCK_DivTraceClk","0","SYSCON.MAINCLKSELB"],
      "SYSCON.SYSTICKCLKDIV": ["kCLOCK_DivSystickClk","0","SYSCON.MAINCLKSELB"],
      "SYSCON.AHBCLKDIV": ["kCLOCK_DivAhbClk","1","SYSCON.MAINCLKSELB"],
      "SYSCON.ADCCLKDIV": ["kCLOCK_DivAdcClk","0","SYSCON.ADCCLKSEL"],
      "SYSCON.CLKOUTDIV": ["kCLOCK_DivClkOut","0","SYSCON.CLKOUTSELB"],
      "ASYNC_SYSCON.ASYNCCLKDIV": ["kCLOCK_DivAsyncApbClk","1","ASYNC_SYSCON.ASYNCAPBCLKSELB"]
    }
    for (eachDiv in divs) {
      if (cfg.existsId(eachDiv)) {
        var actDiv = divs[eachDiv];
        var divDescr = eachDiv.substr(7);
        var actScale = 0;
        if (actDiv[0] == "kCLOCK_DivAsyncApbClk"){
          actScale = cfg.getValueAsText(eachDiv + ".scale");
          if ((actDiv[1] != actScale) && isRealClockElementUsed(cfg, actDiv[2])) {
            CWriter.line("    SYSCON->ASYNCAPBCTRL = SYSCON_ASYNCAPBCTRL_ENABLE_MASK;   /*!< Enable ASYNC APB subsystem */");
            CWriter.line("    Clock_SetAsyncClkDiv(" + actScale + "U);                  /*!< Set ASYNCCLKDIV divider to value " + actScale + " */");
          }
        }
        else if (actDiv[0] == "kCLOCK_DivTraceClk"){
          actScale = cfg.getValueAsText(eachDiv + ".scale");
          if ((actDiv[1] != actScale) && isRealClockElementUsed(cfg, actDiv[2])) {
            CWriter.line("    SYSCON->TRACECLKDIV = " + actScale + "UL;                            /*!< Set TRACECLKDIV divider to value " + actScale + " */");
          }
        }
        else if (actDiv[0] == "kCLOCK_DivAhbClk"){
          actScale = cfg.getValueAsText(eachDiv + ".scale");
          if (isRealClockElementUsed(cfg, actDiv[2])) {
            CWriter.line("    CLOCK_SetClkDiv(" + actDiv[0] + ", "  + actScale + "U, false);              /*!< Set " + divDescr + " divider to value " + actScale + " */");
          }
        }
        else {
          actScale = cfg.getValueAsText(eachDiv + ".scale");
          if ((actDiv[1] != actScale) && isRealClockElementUsed(cfg, actDiv[2])) {
            CWriter.line("    CLOCK_SetClkDiv(" + actDiv[0] + ", "  + actScale + "U, false);              /*!< Set " + divDescr + " divider to value " + actScale + " */");
          }
        }
      }
    }
    if (cfg.existsId("ASYNC_SYSCON.ASYNCCLKDIV") && isRealClockElementUsed(cfg, "ASYNC_SYSCON.ASYNCCLKDIV")) {
        var asyncApbSubsysVal = cfg.getBitFieldValueAsBigInteger("SYSCON::ASYNCAPBCTRL","ENABLE");
        if (asyncApbSubsysVal != 1) {
            CWriter.line("    SYSCON->ASYNCAPBCTRL = 0x0U;                               /*!< Disable ASYNC APB subsystem */");
        }
        else {
            //CWriter.line("    SYSCON->ASYNCAPBCTRL = SYSCON_ASYNCAPBCTRL_ENABLE_MASK;       /*!< Enable ASYNC APB subsystem */");
        }
    }
    CWriter.line("");
    
    /* Attach clocks to the peripheries */
    CWriter.line("    /*!< Set up clock selectors - Attach clocks to the peripheries */");
    
    var usedMAINCLKSELA = false;
    var usedCLKOUTSELA = false;
    var usedASYNCAPBCLKSELA = 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);
        }
        if (actSrcValue == "tunel1CLKOUTSELA_source") {
          usedCLKOUTSELA = true;
          actSrcValue = selSources[cfg.getValueAsText("SYSCON.CLKOUTSELA.sel")];
          srcDescr = actSrcValue.substr(1);
        }
        if (actSrcValue == "tunel3ASYNCAPBCLKSELA_source") {
          usedASYNCAPBCLKSELA = true;
          actSrcValue = selSources[cfg.getValueAsText("ASYNC_SYSCON.ASYNCAPBCLKSELA.sel")];
          srcDescr = actSrcValue.substr(1);
        }
        
        var actSel = sels[eachSel];
        var wholeSignalName = actSrcValue + "_to_" + actSel[0];
        if (actSel[0] == "MAIN_CLK") {
          CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                      /*!< Switch " + actSel[0] + " to " + srcDescr + " */");
        }
        else if (actSrcValue != actSel[1]) {
          CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                      /*!< Switch " + actSel[0] + " to " + srcDescr + " */");
        }
      }
    }
    
    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);
        var selval = cfg.getBitFieldValueAsBigInteger("SYSCON::MAINCLKSELA", "SEL")
        if (selval != 0) {
          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 */");
        }
      }
    }
    if (usedCLKOUTSELA == false) {
      if (cfg.existsId("SYSCON.CLKOUTSELA") && isRealClockElementUsed(cfg, "SYSCON.CLKOUTSELA")) {
        var actSrcValue = selSources[cfg.getValueAsText("SYSCON.CLKOUTSELA.sel")];
        var srcDescr = actSrcValue.substr(1);
        var selval = cfg.getBitFieldValueAsBigInteger("SYSCON::CLKOUTSELA", "SEL")
        if (selval != 0) {
          CWriter.line("    SYSCON->CLKOUTSELA = ((SYSCON->CLKOUTSELA & ~SYSCON_CLKOUTSELA_SEL_MASK) | SYSCON_CLKOUTSELA_SEL(" + cfg.getBitFieldValue("SYSCON::CLKOUTSELA", "SEL") + "U));    /*!< Switch CLKOUTSELA to " + srcDescr + " even it is not used for CLKOUTSELB */");
        }
      }
    }
    if (usedASYNCAPBCLKSELA == false) {
      if (cfg.existsId("ASYNC_SYSCON.ASYNCAPBCLKSELA") && isRealClockElementUsed(cfg, "ASYNC_SYSCON.ASYNCAPBCLKSELA")) {
        var actSrcValue = selSources[cfg.getValueAsText("ASYNC_SYSCON.ASYNCAPBCLKSELA.sel")];
        var srcDescr = actSrcValue.substr(1);
        var selval = cfg.getBitFieldValueAsBigInteger("ASYNC_SYSCON::ASYNCAPBCLKSELA", "SEL")
        if (selval != 0) {
          CWriter.line("    ASYNC_SYSCON->ASYNCAPBCLKSELA = ((ASYNC_SYSCON->ASYNCAPBCLKSELA & ~ASYNC_SYSCON_ASYNCAPBCLKSELA_SEL_MASK) | ASYNC_SYSCON_ASYNCAPBCLKSELA_SEL(" + cfg.getBitFieldValue("ASYNC_SYSCON::ASYNCAPBCLKSELA", "SEL") + "U));    /*!< Switch ASYNCAPBCLKSELA to " + srcDescr + " even it is not used for ASYNCAPBCLKSELB */");
        }
      }
    }
    CWriter.line("");
    
    /* Disable IRC OSC if the user don't want it */
    if (cfg.existsId("SYSCON.irc_clk") && isRealClockElementUsed(cfg, "SYSCON.irc_clk")) {
    }  
    else {
      CWriter.line("    POWER_EnablePD(kPDRUNCFG_PD_IRC_OSC);                    /*!< Disable IRC OSC if the user don't want it */");
      CWriter.line("    POWER_EnablePD(kPDRUNCFG_PD_IRC);                        /*!< Disable IRC if the user don't want it */");
      CWriter.line("");
    }

    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
  // 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]));
        }
      }
    }
  }
}