/*
 * 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\"",   configurations: new Array(), condition: false},
  fsl_power:    {name: "power", code: "#include \"fsl_power.h\"", configurations: new Array(), condition: true},
  fsl_clock:    {name: "clock", code: "#include \"fsl_clock.h\"", configurations: new Array(), condition: true},
  clock_config: {name: "",   code: "#include \"clock_config.h\"", configurations: "", 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("{");
    /* 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  */");
    /* 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 */");
    if (cfg.existsId("SYSCON._clk_in") && isRealClockElementUsed(cfg, "SYSCON._clk_in")) 
    {
      CWriter.line("    POWER_DisablePD(kPDRUNCFG_PD_SYS_OSC);          /*!< Enable System Oscillator Power */");
      CWriter.line("    SYSCON->SYSOSCCTRL = ((SYSCON->SYSOSCCTRL & ~SYSCON_SYSOSCCTRL_FREQRANGE_MASK) | SYSCON_SYSOSCCTRL_FREQRANGE(" + cfg.getBitFieldValue("SYSCON::SYSOSCCTRL", "FREQRANGE") + "U)); /*!< Set system oscillator range */");
    }    
    if (configID == "BOARD_BootClockFRO12M")
    {
        CWriter.line("    CLOCK_SetupFROClocking(12000000U);                    /*!< Set up FRO to the 12 MHz, just for sure */");   
    }
    /* 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": "ASYNCAPB 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",
      "FXCOM8_clock": "FXCOM8 clock output",
      "FXCOM9_clock": "FXCOM9 clock output",
      "FXCOMs_CLK32K_clock": "FXCOMs CLK32K clock output",
      "Master_clock": "Master clock output",
      "SYSTICK_clock": "SYSTICK clock output",
      "System_clock": "System clock output",
      "USB_clock": "USB clock output",
    }

         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.LCDCLK_EXT": "kLCDCLKIN", 
      "SYSCON.WDT_FREQ": "kWDT_OSC", 
      "SYSCON.WDT_DIV": "kWDT_OSC", 
      "RTC.rtc_osc_32k_clk": "kOSC32K_OSC", 
      "SYSCON.PLL_BYPASS": "kSYS_PLL",
      "SYSCON.fc6_fclk_clk": "kI2C_CLK_FC6", 
      "SYSCON.FROHFCLKDIV": "kFRO_HF", 
      "SYSCON.USBDIRECT": "kUSB_PLL",       
      "SYSCON.audio_pll_clk": "kAUDIO_PLL",
      "SYSCON.AUDPLL_BYPASS": "kAUDIO_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",
      "SYSCON.AUDPLLCLKSEL": "AUDIO_PLL",
      "ASYNC_SYSCON.ASYNCAPBCLKSELA": "ASYNC_APB", 
      "SYSCON.ADCCLKSEL": "ADC_CLK",
      "SYSCON.USB0CLKSEL": "USB0_CLK",
      "SYSCON.USB1CLKSEL": "USB1_CLK",
      "SYSCON.SDIOCLKSEL": "SDIO_CLK",
      "SYSCON.FXCLKSEL0": "FLEXCOMM0", 
      "SYSCON.FXCLKSEL1": "FLEXCOMM1", 
      "SYSCON.FXCLKSEL2": "FLEXCOMM2", 
      "SYSCON.FXCLKSEL3": "FLEXCOMM3", 
      "SYSCON.FXCLKSEL4": "FLEXCOMM4", 
      "SYSCON.FXCLKSEL5": "FLEXCOMM5", 
      "SYSCON.FXCLKSEL6": "FLEXCOMM6", 
      "SYSCON.FXCLKSEL7": "FLEXCOMM7",
      "SYSCON.FXCLKSEL8": "FLEXCOMM8", 
      "SYSCON.FXCLKSEL9": "FLEXCOMM9",
      "SYSCON.SPIFICLKSEL": "SPIFICLK",
      "SYSCON.SCTCLKSEL": "SCT_CLK",  
      "SYSCON.SPIFICLKSEL": "SPIFI_CLK",
      "SYSCON.LCDCLKSEL": "LCD_CLK", 
      "SYSCON.FRGCLKSEL": "FRG",       
      "SYSCON.MCLKCLKSEL": "MCLK", 
      "SYSCON.DMICCLKSEL": "DMIC",
      "SYSCON.USBCLKSEL": "USB_CLK", 
      "SYSCON.CLKOUTSELA": "CLKOUT"
    } 

    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 */");
      
      
      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 */");
      
      var rtcCtrlVal = "";
      var rtc_osc_pdVal = cfg.getBitFieldValueAsBigInteger("RTC::CTRL","RTC_OSC_PD");    

      if (rtc_osc_pdVal != 0) {      
        rtcCtrlVal = rtcCtrlVal.concat("RTC_CTRL_RTC_OSC_PD_MASK");
      }
      else
      {
        rtcCtrlVal = rtcCtrlVal.concat("~RTC_CTRL_RTC_OSC_PD_MASK");
      }

      CWriter.line("    RTC->CTRL &= " + rtcCtrlVal + ";                                 /*!< Setup RTC oscillator */");
      
      if (rtcCtrlVal == "") {
        rtcCtrlVal = "0x0U";
      }
      var rtcoscctrlVal = "";
      var rtcoscctrl_enVal = cfg.getBitFieldValueAsBigInteger("SYSCON::RTCOSCCTRL","EN");
      if (rtcoscctrl_enVal != 0) {
        rtcoscctrlVal = rtcoscctrlVal.concat("SYSCON_RTCOSCCTRL_EN_MASK");
      }
      else {
        rtcoscctrlVal = "0x0U";
      }    
      
      CWriter.line("    SYSCON->RTCOSCCTRL = " + rtcoscctrlVal + ";              /*!< Setup RTC oscillator clock source availability to other modules */");
      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 */

    /* SYS PLL setup - begin */
    var actPLLmode = cfg.getValueAsText("PLL_Mode");
    if (actPLLmode == "Normal") {
    var pllClkSel = "SYSCON.PLL";  
    if (cfg.existsId(pllClkSel) && isRealClockElementUsed(cfg, pllClkSel)) {
      CWriter.line("    /*!< Set up SYS PLL */");
      var syspllctrlVal = "";
      var syspllmdecVal = ""
      var syspllndecVal = ""
      var syspllpdecVal = ""
      var mdecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLMDEC","MDEC");    
      var ndecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLNDEC","NDEC");    
      var pdecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::SYSPLLPDEC","PDEC");    

      if (actPLLmode == "Normal") {
        syspllmdecVal = syspllmdecVal.concat("(SYSCON_SYSPLLMDEC_MDEC("+ mdecVal + "U))");
        syspllndecVal = syspllndecVal.concat("(SYSCON_SYSPLLNDEC_NDEC("+ ndecVal + "U))");
        syspllpdecVal = syspllpdecVal.concat("(SYSCON_SYSPLLPDEC_PDEC("+ pdecVal + "U))");

        var actSyspllclksel = cfg.getValueAsText("SYSCON.SYSPLLCLKSEL.sel");

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

        selr = 0       
        seli= Math.round(seli)    
        selp= Math.round(selp)    
        selr= Math.round(selr)          
        syspllctrlVal = syspllctrlVal.concat(" SYSCON_SYSPLLCTRL_SELI(" + seli + "U) | SYSCON_SYSPLLCTRL_SELP(" + selp + "U) | SYSCON_SYSPLLCTRL_SELR(" + selr + "U)");

      }
      
      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");
      if( pllbypassVal != 0) {
        var syspllctrlVal = syspllctrlVal.concat(" | SYSCON_SYSPLLCTRL_BYPASS_MASK");
        var flagsVal = "";
      }
      else
      {
        var flagsVal = "PLL_SETUPFLAG_WAITLOCK | PLL_SETUPFLAG_POWERUP";
      }
    
      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("        .pllctrl = " + syspllctrlVal + ",");
      CWriter.line("        .pllmdec = " + syspllmdecVal + ",");
      CWriter.line("        .pllndec = " + syspllndecVal + ",");
      CWriter.line("        .pllpdec = " + syspllpdecVal + ",");

      if (flagsVal != "") {
        CWriter.line("        .pllRate = " + pllFreq + "U,");
        CWriter.line("        .flags =  " + flagsVal);
      }
      else
      {
        CWriter.line("        .pllRate = " + pllFreq + "U,");
      }
      CWriter.line("    };");
      var syspllclksel = selSources[cfg.getValueAsText("SYSCON.SYSPLLCLKSEL" + ".sel")];
      syspllclksel_whole = syspllclksel + "_to_" + "SYS_PLL";
      if(syspllclksel_whole == "kOSC32K_OSC_to_SYS_PLL")
      {
        CWriter.line("    CLOCK_AttachClk(kOSC32K_to_SYS_PLL);                /*!< Set sys pll clock source*/");     

      }
      else
      {
        CWriter.line("    CLOCK_AttachClk(" + syspllclksel_whole + ");        /*!< Set sys pll clock source*/");     
      }

      CWriter.line("    CLOCK_SetPLLFreq(&pllSetup);                 /*!< Configure PLL to the desired value */"); 
      
    } 
    }
    /* SYS PLL setup - end */


    /* AUDIO PLL setup - begin */
    actPLLmode = cfg.getValueAsText("PLL_Mode");
    if (actPLLmode == "Normal") {
    var pllClkSel = "SYSCON.AUDPLL";  
    if (cfg.existsId(pllClkSel) && isRealClockElementUsed(cfg, pllClkSel)) {
      CWriter.line("    /*!< Set up AUDIO PLL */");
      var syspllctrlVal = "";
      var syspllmdecVal = ""
      var syspllndecVal = ""
      var syspllpdecVal = ""
      var audpllfrac = ""

      var mdecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::AUDPLLMDEC","MDEC");    
      var ndecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::AUDPLLNDEC","NDEC");    
      var pdecVal = cfg.getBitFieldValueAsBigInteger("SYSCON::AUDPLLPDEC","PDEC");    

      if (actPLLmode == "Normal") {
        syspllmdecVal = syspllmdecVal.concat("(SYSCON_AUDPLLMDEC_MDEC("+ mdecVal + "U))");
        syspllndecVal = syspllndecVal.concat("(SYSCON_AUDPLLNDEC_NDEC("+ ndecVal + "U))");
        syspllpdecVal = syspllpdecVal.concat("(SYSCON_AUDPLLPDEC_PDEC("+ pdecVal + "U))");

        var actSyspllclksel = cfg.getValueAsText("SYSCON.AUDPLLCLKSEL.sel");

        var seli = 0;
        var selp = 0;
        var multiply_by = cfg.getValueAsBigInteger("SYSCON.AUD_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));
          }
        }

        selr = 0;
        seli= Math.round(seli)    
        selp= Math.round(selp)    
        selr= Math.round(selr)    

        syspllctrlVal = syspllctrlVal.concat("SYSCON_AUDPLLCTRL_SELI(" + seli + "U) | SYSCON_AUDPLLCTRL_SELP(" + selp + "U) | SYSCON_AUDPLLCTRL_SELR(" + selr + "U)");

      }
      
      var directoVal = cfg.getBitFieldValueAsBigInteger("SYSCON::AUDPLLCTRL","DIRECTO");
      if( directoVal != 0) {
        var syspllctrlVal = syspllctrlVal.concat(" | SYSCON_AUDPLLCTRL_DIRECTO_MASK");
      }
      
      var pllbypassVal = cfg.getBitFieldValueAsBigInteger("SYSCON::AUDPLLCTRL","BYPASS");
      if( pllbypassVal != 0) {
        var syspllctrlVal = syspllctrlVal.concat(" | SYSCON_AUDPLLCTRL_BYPASS_MASK");
        var flagsVal = "";
      }
      else
      {
        var flagsVal = "PLL_SETUPFLAG_WAITLOCK | PLL_SETUPFLAG_POWERUP";
      }
    
      var pllFreq = cfg.getValueAsText("SYSCON.AUDPLL_BYPASS.outFreq");
    
      if (pllFreq != "N/A") {                      
        pllFreq = cfg.getValueAsBigInteger("SYSCON.AUDPLL_BYPASS.outFreq"); 
      } 

      CWriter.line("    const pll_setup_t audio_pllSetup = {");
      CWriter.line("        .pllctrl = " + syspllctrlVal + ",");
      CWriter.line("        .pllmdec = " + syspllmdecVal + ",");
      CWriter.line("        .pllndec = " + syspllndecVal + ",");
      CWriter.line("        .pllpdec = " + syspllpdecVal + ",");

      if (flagsVal != "") {
        CWriter.line("        .pllRate = " + pllFreq + "U,");
        CWriter.line("        .flags =  " + flagsVal);
      }
      else
      {
        CWriter.line("        .pllRate = " + pllFreq + "U");
      }
      CWriter.line("    };");
      var audiopllclksel = selSources[cfg.getValueAsText("SYSCON.AUDPLLCLKSEL" + ".sel")];
      audiopllclksel_whole = audiopllclksel + "_to_" + "AUDIO_PLL";
      CWriter.line("    CLOCK_AttachClk(" + audiopllclksel_whole + ");                         /*!< Set audio pll clock source*/");   
      CWriter.line("    CLOCK_SetAudioPLLFreq(&audio_pllSetup);                        /*!< Configure PLL to the desired value */"); 
      CWriter.line("");
    }
    }
    /* AUDIO PLL setup - end */ 

    /* usb PLL setup - begin */
    actPLLmode = cfg.getValueAsText("PLL_Mode");
    if (actPLLmode == "Normal") {
    var pllClkSel = "SYSCON.USBPLL";  
    if (cfg.existsId(pllClkSel) && isRealClockElementUsed(cfg, pllClkSel) && cfg.existsId(pllClkSel) && isRealClockElementUsed(cfg, "SYSCON._clk_in")) {
      CWriter.line("    /*!< Set up USB PLL */");
      //scriptApi.logWarning(cfg.getValueAsText("PLL_Mode"));
      var direct = "false";
      var bypass = "false";
      var fbsel = "false";
      var usbpllmselval = "";
      var usbpllpselval = "";
      var usbpllnselval = "";

      var usbpllmsel = cfg.getBitFieldValueAsBigInteger("SYSCON::USBPLLCTRL","MSEL");    
      var usbpllpsel = cfg.getBitFieldValueAsBigInteger("SYSCON::USBPLLCTRL","PSEL");    
      var usbpllnsel = cfg.getBitFieldValueAsBigInteger("SYSCON::USBPLLCTRL","NSEL");    

      if (actPLLmode == "Normal") {
        usbpllmselval = usbpllmselval.concat("(SYSCON_USBPLLCTRL_MSEL("+ usbpllmsel + "U))");
        usbpllnselval = usbpllnselval.concat("(SYSCON_USBPLLCTRL_NSEL("+ usbpllnsel + "U))");
        usbpllpselval = usbpllpselval.concat("(SYSCON_USBPLLCTRL_PSEL("+ usbpllpsel + "U))");
      }
      
      var directoVal = cfg.getBitFieldValueAsBigInteger("SYSCON::USBPLLCTRL","DIRECT");
      if( directoVal != 0) {
        direct = "true"
      }
      
      var fbselVal = cfg.getValueAsText("SYSCON_USBPLLCTRL_FBSEL_CFG");
      if( fbselVal == "Enabled") {
        fbsel = "true"
      }      
      var pllbypassVal = cfg.getBitFieldValueAsBigInteger("SYSCON::USBPLLCTRL","BYPASS");
      if( pllbypassVal != 0) {
        bypass = "true"
        var flagsVal = "";
      }
      else
      {
        var flagsVal = "PLL_SETUPFLAG_WAITLOCK | PLL_SETUPFLAG_POWERUP";
      }
    
      var clk_in_Freq = cfg.getValueAsText("SYSCON._clk_in");
    
      if (clk_in_Freq != "N/A") {                      
        clk_in_Freq = cfg.getValueAsBigInteger("SYSCON._clk_in.outFreq"); 
      } 

      CWriter.line("    const usb_pll_setup_t usb_pllSetup = {");
      CWriter.line("        .msel = " + usbpllmsel + "U,");
      CWriter.line("        .nsel = " + usbpllnsel + "U,");
      CWriter.line("        .psel = " + usbpllpsel + "U,");
      CWriter.line("        .direct = " + direct + ",");
      CWriter.line("        .bypass = " + bypass + ",");
      CWriter.line("        .fbsel = " + fbsel + ",");
      CWriter.line("        .inputRate = " + clk_in_Freq + "U,");
      CWriter.line("    };");
      CWriter.line("    CLOCK_SetUsbPLLFreq(&usb_pllSetup);                        /*!< Configure PLL to the desired value */"); 
      CWriter.line("");
    }
    }
    /* USB 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.CLKOUTDIV": "kCLOCK_DivClkOut", 
      "SYSCON.ADCCLKDIV": "kCLOCK_DivAdcAsyncClk", 
      "SYSCON.USB0CLKDIV": "kCLOCK_DivUsb0Clk",
      "SYSCON.USB1CLKDIV": "kCLOCK_DivUsb1Clk",
      "SYSCON.MCLKCLKDIV": "kCLOCK_DivMClk",
      "SYSCON.DMICCLKDIV": "kCLOCK_DivDmicClk", 
      "SYSCON.SDIOCLKDIV": "kCLOCK_DivSdioClk", 
      "SYSCON.EMCCLKDIV": "kCLOCK_DivEmcClk", 
      "SYSCON.CAN0CLKDIV": "kCLOCK_DivCan0Clk",
      "SYSCON.CAN1CLKDIV": "kCLOCK_DivCan1Clk",
      "SYSCON.SC0CLKDIV": "kCLOCK_DivSmartCard0Clk",
      "SYSCON.SC1CLKDIV": "kCLOCK_DivSmartCard1Clk",
      "SYSCON.SCTCLKDIV": "kCLOCK_DivSctClk",
      "SYSCON.LCDCLKDIV": "kCLOCK_DivLcdClk",
    }
    
    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);                  /*!< Reset divider counter and set divider to value " + actScale + " */");  
        }
        else if (actDiv == "kCLOCK_DivSystickClk") {
          if (cfg.getBitFieldValueAsBigInteger("SYSCON::SYSTICKCLKDIV","HALT") != 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_DivEmcClk") {
          if (cfg.getBitFieldValueAsBigInteger("SYSCON::EMCCLKDIV","HALT") != 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_DivCan0Clk") {
          if (cfg.getValueAsText("MCAN0_OUTPUT_ENDI") != "Disabled")
          {
            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_DivCan1Clk") {
          if (cfg.getValueAsText("MCAN1_OUTPUT_ENDI") != "Disabled")
          {
            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_DivSmartCard0Clk") {
          if (cfg.getValueAsText("SC0_OUTPUT_ENDI") != "Disabled")
          {
            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_DivSmartCard1Clk") {
          if (cfg.getValueAsText("SC1_OUTPUT_ENDI") != "Disabled")
          {
            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 frohfclkdiv_configuration = false
  var usedMAINCLKSELA = false;
  actSel = sels["SYSCON.FRGCLKSEL"];
  srcDescr = selSources[cfg.getValueAsText("SYSCON.FRGCLKSEL" + ".sel")].substr(1);
  frgWholeSignalName = selSources[cfg.getValueAsText("SYSCON.FRGCLKSEL" + ".sel")] + "_to_" + sels["SYSCON.FRGCLKSEL"] 
  if (cfg.getValueAsText("SYSCON.FRGCLKSEL" + ".sel") != "NO_CLOCK")
  {
      var multValue = cfg.getBitFieldValue("SYSCON::FRGCTRL", "MULT"); 
      CWriter.line("    CLOCK_AttachClk(" + frgWholeSignalName + ");                   /*!< Switch " + actSel + " to " + srcDescr + " */");                      
      CWriter.line("    SYSCON->FRGCTRL = ((SYSCON->FRGCTRL & ~SYSCON_FRGCTRL_MULT_MASK) | SYSCON_FRGCTRL_MULT(" + multValue + "U)); /*!< Set FRG MULT to value " + multValue + " */");
  } 
  for (eachSel in sels) {      
    if (cfg.existsId(eachSel) && isRealClockElementUsed(cfg, eachSel)) {
      var actSrcValue = selSources[cfg.getValueAsText(eachSel + ".sel")];
      var srcDescr = actSrcValue.substr(1);
      var actSel = sels[eachSel];        
      var wholeSignalName = actSrcValue + "_to_" + actSel;
      if (actSrcValue == "tunel2MAINCLKSELA_source") {
        usedMAINCLKSELA = true;
        actSrcValue = selSources[cfg.getValueAsText("SYSCON.MAINCLKSELA.sel")];
        srcDescr = actSrcValue.substr(1);
        wholeSignalName = actSrcValue + "_to_" + actSel;
        CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                     /*!< Switch " + actSel + " to " + srcDescr + " */");            

      }
      else if (actSel == "ASYNC_APB") {
        var asyncApbSubsysVal = cfg.getBitFieldValueAsBigInteger("SYSCON::ASYNCAPBCTRL","ENABLE");
        if (asyncApbSubsysVal != 0) {
          CWriter.line("    SYSCON->ASYNCAPBCTRL = SYSCON_ASYNCAPBCTRL_ENABLE_MASK;       /*!< Enable ASYNC APB subsystem */");
          CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");        
        }
      }
      else if (actSel ==  "SYS_PLL" ) {

        if (isRealClockElementUsed(cfg, "SYSCON.PLL")){
            
            // CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");            
        }

      }
      else if (actSel ==  "AUDIO_PLL" ) {

        if (isRealClockElementUsed(cfg, "SYSCON.AUDPLL")){
            
            // CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");            
        }
      }
      else if(cfg.getValueAsText(eachSel + ".sel") == "SYSCON.FROHFCLKDIV")
      {
        if(frohfclkdiv_configuration == false){
          var multValue = cfg.getBitFieldValue("SYSCON::FROHFCLKDIV", "DIV"); 
          frohfclkdiv_configuration =true;
          CWriter.line("    SYSCON->FROHFCLKDIV = ((SYSCON->FROHFCLKDIV & ~SYSCON_FROHFCLKDIV_DIV_MASK) | SYSCON_FROHFCLKDIV_DIV(" + multValue + "U)); /*!< Set FROHF CLKDIV  to value " + multValue + " */");
        }
        if(wholeSignalName == "kFRO_HF_to_DMIC")
        {
           CWriter.line("    CLOCK_AttachClk(" + "kFRO_HF_DIV_to_DMIC" + ");                  /*!< Switch " + actSel + " to " + "FRO_HF_DIV" + " */");
        }
        else
        {
           CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                      /*!< Switch " + actSel + " to " + srcDescr + " */");            
        }

      }

      else if (wholeSignalName =="kMAIN_CLK_to_USB1_CLK" || wholeSignalName =="kMAIN_CLK_to_SDIO_CLK" || wholeSignalName =="kMAIN_CLK_to_SCT_CLK" || wholeSignalName =="kOSC32K_OSC_to_MAIN_CLK"){
        if (wholeSignalName =="kMAIN_CLK_to_USB1_CLK"){
            CWriter.line("    CLOCK_AttachClk(" + "kFRO_HF_to_USB1_CLK" + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");
        }
        if (wholeSignalName =="kMAIN_CLK_to_SDIO_CLK"){
            CWriter.line("    CLOCK_AttachClk(" + "kMCLK_to_SDIO_CLK" + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");
        }
        if (wholeSignalName =="kMAIN_CLK_to_SCT_CLK"){
            CWriter.line("    CLOCK_AttachClk(" + "kMCLK_to_SCT_CLK" + ");                  /*!< Switch " + actSel + " to " + srcDescr + " */");
        }
        if (wholeSignalName == "kOSC32K_OSC_to_MAIN_CLK"){
            CWriter.line("    CLOCK_AttachClk(" + "kOSC32K_to_MAIN_CLK" + ");               /*!< Switch " + actSel + " to " + srcDescr + " */");
        }
      }
      else {
            CWriter.line("    CLOCK_AttachClk(" + wholeSignalName + ");                  /*!< Switch " + actSel + " 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);
      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;");
  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 pushDependency(dependencyHelper, dependency) {
  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]));
  }
}

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