/*
 * Copyright 2016 Freescale Semiconductor
 * Copyright 2016-2017 NXP
 * To be used with KEx Tools. See Software License Agreement of KEx Tools.
 */

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

// SDK header includes
var sdkIncludes = {
  //#include fsl_common.h moved into h file
  //fsl_common:   {code: "#include \"fsl_common.h\"",   condition: true},
  clock_config: {name: "",    code: "#include \"clock_config.h\"", configuration: "",   condition: true},
}

/**
 * Writes How to setup comment block
 *
 */
function writeHowToSetupComment() {
  CWriter.writeLines([
  "/*",
  " * How to setup clock using clock driver functions:",
  " *",
  " * 1. Call CLOCK_InitXXXPLL() to configure corresponding PLL clock.",
  " *",	
  " * 2. Call CLOCK_InitXXXpfd() to configure corresponding PLL pfd clock.",
  " *",
  " * 3. Call CLOCK_SetMux() to configure corresponding clock source for target clock out.",
  " *",
  " * 4. Call CLOCK_SetDiv() to configure corresponding clock divider for target clock out.",
  " *",
  " * 5. Call CLOCK_SetXtalFreq() to set XTAL frequency based on board settings.",
  " *",
  " */"]);
}

/**
 * Generates one configuration structure for a given clock configuration
 *
 * @param cfg Clock configuration object
 */
function GenerateCLKConfigStructure(cfg) {
  var configID = Gen.getConfigID(cfg);
  var powerMode = HwAbstr.getPowerMode(cfg);

  //If ARM PLL Configuration structure needed.
  if (HwAbstr.isClockElementUsed(cfg, "armPll")) {
    ClockConfigH.isArmPllConfigExtern[configID] = true;
    CWriter.line("const clock_arm_pll_config_t armPllConfig_" + configID + " ="); 
    CWriter.line("    {"); 
    OutputUtils.writeStructItemInitLine(cfg, 9, "armPllConfig.loopDivider");
    CWriter.line("    };");
  }

  //If SYS PLL Configuration structure needed.
  if (HwAbstr.isPllActive(cfg, "2")) {
    ClockConfigH.isSysPllConfigExtern[configID] = true;
    CWriter.line("const clock_sys_pll_config_t sysPllConfig_" + configID + " ="); 
    CWriter.line("    {"); 
    OutputUtils.writeStructItemInitLine(cfg, 9, "sysPllConfig.loopDivider");
    OutputUtils.writeStructItemInitLine(cfg, 9, "sysPllConfig.numerator");
    OutputUtils.writeStructItemInitLine(cfg, 9, "sysPllConfig.denominator");
    CWriter.line("    };");
  }

  //If USB1 PLL Configuration structure needed.
  if (HwAbstr.isClockElementUsed(cfg, "usb1Pll")) {
    ClockConfigH.isUsb1PllConfigExtern[configID] = true;
    CWriter.line("const clock_usb_pll_config_t usb1PllConfig_" + configID + " ="); 
    CWriter.line("    {"); 
    OutputUtils.writeStructItemInitLine(cfg, 9, "usb1PllConfig.loopDivider");
    CWriter.line("    };");
  }

  //If USB2 PLL Configuration structure needed.
  if (HwAbstr.isClockElementUsed(cfg, "usb2Pll")) {
    ClockConfigH.isUsb2PllConfigExtern[configID] = true;
    CWriter.line("const clock_usb_pll_config_t usb2PllConfig_" + configID + " ="); 
    CWriter.line("    {"); 
    OutputUtils.writeStructItemInitLine(cfg, 9, "usb2PllConfig.loopDivider");
    CWriter.line("    };");
  }

  //If AUDIO PLL Configuration structure needed.
  if (HwAbstr.isPllActive(cfg, "4")) {
    ClockConfigH.isAudioPllConfigExtern[configID] = true;
    CWriter.line("const clock_audio_pll_config_t audioPllConfig_" + configID + " ="); 
    CWriter.line("    {"); 
    OutputUtils.writeStructItemInitLine(cfg, 9, "audioPllConfig.loopDivider");
    OutputUtils.writeStructItemInitLine(cfg, 9, "audioPllConfig.postDivider");
    OutputUtils.writeStructItemInitLine(cfg, 9, "audioPllConfig.numerator");
    OutputUtils.writeStructItemInitLine(cfg, 9, "audioPllConfig.denominator");
    CWriter.line("    };");
  }

  //If VIDEO PLL Configuration structure needed.
  if (HwAbstr.isPllActive(cfg, "5")) {
    ClockConfigH.isVideoPllConfigExtern[configID] = true;
    CWriter.line("const clock_video_pll_config_t videoPllConfig_" + configID + " ="); 
    CWriter.line("    {"); 
    OutputUtils.writeStructItemInitLine(cfg, 9, "videoPllConfig.loopDivider");
    OutputUtils.writeStructItemInitLine(cfg, 9, "videoPllConfig.postDivider");
    OutputUtils.writeStructItemInitLine(cfg, 9, "videoPllConfig.numerator");
    OutputUtils.writeStructItemInitLine(cfg, 9, "videoPllConfig.denominator");
    CWriter.line("    };");
  }

  //If ENET PLL Configuration structure needed.
  if (HwAbstr.isClockElementUsed(cfg, "enetPll")) {
    ClockConfigH.isEnetPllConfigExtern[configID] = true;
    CWriter.line("const clock_enet_pll_config_t enetPllConfig_" + configID + " ="); 
    CWriter.line("    {"); 
    OutputUtils.writeStructItemInitLine(cfg, 9, "enetPllConfig.enableClkOutput0");
    OutputUtils.writeStructItemInitLine(cfg, 9, "enetPllConfig.enableClkOutput1");
    OutputUtils.writeStructItemInitLine(cfg, 9, "enetPllConfig.loopDivider");
    CWriter.line("    };");
  }
}
 

// 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;
}
   
/**
 * 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);
  CWriter.line("void " + configID + "(void)");
  CWriter.line("{");

  /* Init RTCOSC clock */
  if (HwAbstr.isClockElementUsed(cfg, "rtcOsc")) {
    CWriter.line("    /* Init RTC OSC clock frequency. */");
    CWriter.line("    CLOCK_SetRtcXtalFreq(32768U);");
  }

  /* Init 1MHz clock */
  if (HwAbstr.isClockElementUsed(cfg, "clk1mSrc")) {
    CWriter.line("    /* Enable 1MHz clock output. */");
    CWriter.line("    XTALOSC24M->OSC_CONFIG2 |= XTALOSC24M_OSC_CONFIG2_ENABLE_1M_MASK;");
    if (HwAbstr.isCorrect1mClockUsed(cfg)) {
      CWriter.line("    /* Use correct 1MHz clock output. */");
      CWriter.line("    XTALOSC24M->OSC_CONFIG2 |= XTALOSC24M_OSC_CONFIG2_MUX_1M_MASK;");
    } else {
      CWriter.line("    /* Use free 1MHz clock output. */");
      CWriter.line("    XTALOSC24M->OSC_CONFIG2 &= ~XTALOSC24M_OSC_CONFIG2_MUX_1M_MASK;");
    }
  } else {
      CWriter.line("    /* Disable 1MHz clock output. */");
      CWriter.line("    XTALOSC24M->OSC_CONFIG2 &= ~XTALOSC24M_OSC_CONFIG2_ENABLE_1M_MASK;");
  }

  /* Init 24MHz osc clock */
  if (HwAbstr.isClockElementUsed(cfg, "osc")) {
    CWriter.line("    /* Set XTAL 24MHz clock frequency. */");
    CWriter.line("    CLOCK_SetXtalFreq(24000000U);");
    if(HwAbstr.isPowerDownModeEnabled(cfg)){
      CWriter.line("    /* Disable XTAL 24MHz clock source. */");
      CWriter.line("    CLOCK_DeinitExternalClk();");
    } else {
      CWriter.line("    /* Enable XTAL 24MHz clock source. */");
      CWriter.line("    CLOCK_InitExternalClk(0);");
    }
    if(HwAbstr.isInternalRcEnabled(cfg)) {
      CWriter.line("    /* Enable internal RC. */");
      CWriter.line("    CLOCK_InitRcOsc24M();");
    } else {
      CWriter.line("    /* Disable internal RC. */");
      CWriter.line("    CLOCK_DeinitRcOsc24M();");
    }
    if(HwAbstr.isInternalRcClockUsed(cfg)) {
      CWriter.line("    /* Switch clock source to internal RC. */");
      CWriter.line("    CLOCK_SwitchOsc(kCLOCK_RcOsc);");
      CWriter.line("    /* Configure self-tuning for RC OSC */");
      CWriter.line("    XTALOSC24M->OSC_CONFIG0 = XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR(0x4) | XTALOSC24M_OSC_CONFIG0_SET_HYST_MINUS(0x2) |");
      CWriter.line("                              XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG(0xA7) |");
      CWriter.line("                              XTALOSC24M_OSC_CONFIG0_START_MASK | XTALOSC24M_OSC_CONFIG0_ENABLE_MASK;");
      CWriter.line("    XTALOSC24M->OSC_CONFIG1 = XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR(0x40) | XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG(0x2DC);");
    } else {
      CWriter.line("    /* Switch clock source to external OSC. */");
      CWriter.line("    CLOCK_SwitchOsc(kCLOCK_XtalOsc);");
    }
    CWriter.line("    /* Set Oscillator ready counter value. */");
    CWriter.line("    CCM->CCR = (CCM->CCR & (~CCM_CCR_OSCNT_MASK)) | CCM_CCR_OSCNT("+ HwAbstr.getOscReadyCount(cfg) + ");");
  } else {
    CWriter.line("    /* Disable 24M OSC. */");
    CWriter.line("    CCM->CCR &= ~CCM_CCR_COSC_EN_MASK;");
  }

  CWriter.line("    /* Setting PeriphClk2Mux and PeriphMux to provide stable clock before PLLs are initialed */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_PeriphClk2Mux, 1); /* Set PERIPH_CLK2 MUX to OSC */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_PeriphMux, 1);     /* Set PERIPH_CLK MUX to PERIPH_CLK2 */");
  if (cfg.getValueAsText("CCM.AHB_CLK_ROOT.outFreq") == "600 MHz") {
    CWriter.line("    /* Setting the VDD_SOC to 1.5V. It is necessary to config AHB to 600Mhz. */");
    CWriter.line("    DCDC->REG3 = (DCDC->REG3 & (~DCDC_REG3_TRG_MASK)) | DCDC_REG3_TRG(0x12);");
    CWriter.line("    /* Waiting for DCDC_STS_DC_OK bit is asserted */");
    CWriter.line("    while (DCDC_REG0_STS_DC_OK_MASK != (DCDC_REG0_STS_DC_OK_MASK & DCDC->REG0))");
    CWriter.line("    {");
    CWriter.line("    }");
  }

  /* Init ARM PLL */
  if (HwAbstr.isPllActive(cfg, "1")) {
    CWriter.line("    /* Init ARM PLL. */");
    CWriter.line("    /* Bypass PLL first. */");
    CWriter.line("    CCM_ANALOG->PLL_ARM = (CCM_ANALOG->PLL_ARM & (~CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC_MASK)) |");
    CWriter.line("                          CCM_ANALOG_PLL_ARM_BYPASS_MASK | CCM_ANALOG_PLL_ARM_BYPASS_CLK_SRC(" + HwAbstr.getPllBypassClkSrc(cfg, "1") + ");");
    CWriter.line("    CCM_ANALOG->PLL_ARM = (CCM_ANALOG->PLL_ARM & (~(CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK | CCM_ANALOG_PLL_ARM_POWERDOWN_MASK))) |");
    CWriter.line("                          CCM_ANALOG_PLL_ARM_ENABLE_MASK | CCM_ANALOG_PLL_ARM_DIV_SELECT(" + HwAbstr.getPllLoopDivider(cfg, "1") + ");");
    CWriter.line("    while ((CCM_ANALOG->PLL_ARM & CCM_ANALOG_PLL_ARM_LOCK_MASK) == 0)");
    CWriter.line("    {");
    CWriter.line("    }");
    if (!HwAbstr.isPllBypassed(cfg, "1")) {
      CWriter.line("    /* Disable bypass for ARM PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_ARM &= ~CCM_ANALOG_PLL_ARM_BYPASS_MASK;");
    }
    if (!HwAbstr.isArmPllOutputEnabled(cfg)) {
      CWriter.line("    /* Disable ARM PLL output. */");
      CWriter.line("    CCM_ANALOG->PLL_ARM &= ~CCM_ANALOG_PLL_ARM_ENABLE_MASK;");
    }
  } else {
    CWriter.line("    /* Set ARM PLL bypass clock source. */");
    CWriter.line("    CLOCK_SetPllBypassRefClkSrc(CCM_ANALOG, kCLOCK_PllArm, " + HwAbstr.getPllBypassClkSrc(cfg, "1") + ");");
    CWriter.line("    /* DeInit ARM PLL. */");
    CWriter.line("    CLOCK_DeinitArmPll();");
    if (HwAbstr.isPllBypassed(cfg, "1")) {
      CWriter.line("    /* Bypass ARM PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_ARM |= CCM_ANALOG_PLL_ARM_BYPASS_MASK;");
      if (HwAbstr.isArmPllOutputEnabled(cfg)) {
        CWriter.line("    /* Enable ARM PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_ARM |= CCM_ANALOG_PLL_ARM_ENABLE_MASK;");
      }
    }
  }

  /* Init System PLL */
  CWriter.line("    /* In SDK projects, SDRAM (configured by SEMC) will be initialized in either debug script or dcd.");
  CWriter.line("     * With this macro SKIP_SYSCLK_INIT, system pll (selected to be SEMC source clock in SDK projects) will be left unchanged.");
  CWriter.line("     * Note: If another clock source is selected for SEMC, user may want to avoid changing that clock as well.*/");
  CWriter.line("#ifndef SKIP_SYSCLK_INIT");
  if (HwAbstr.isPllActive(cfg, "2")) {
    CWriter.line("    /* Init System PLL. */");
    CWriter.line("    /* Bypass PLL first. */");
    CWriter.line("    CCM_ANALOG->PLL_SYS = (CCM_ANALOG->PLL_SYS & (~CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC_MASK)) |");
    CWriter.line("                          CCM_ANALOG_PLL_SYS_BYPASS_MASK | CCM_ANALOG_PLL_SYS_BYPASS_CLK_SRC(" + HwAbstr.getPllBypassClkSrc(cfg, "2") + ");");
    CWriter.line("    CCM_ANALOG->PLL_SYS = (CCM_ANALOG->PLL_SYS & (~(CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK | CCM_ANALOG_PLL_SYS_POWERDOWN_MASK))) |");
    CWriter.line("                          CCM_ANALOG_PLL_SYS_ENABLE_MASK | CCM_ANALOG_PLL_SYS_DIV_SELECT(" + HwAbstr.getPllLoopDivider(cfg, "2") + ");");
    CWriter.line("    while ((CCM_ANALOG->PLL_SYS & CCM_ANALOG_PLL_SYS_LOCK_MASK) == 0)");
    CWriter.line("    {");
    CWriter.line("    }");
    if (HwAbstr.isSysPfdEnabled(cfg, "0")) {
      tmp = HwAbstr.getSysPfdFrac(cfg, "0");
      CWriter.line("    /* Init System pfd0. */");
      CWriter.line("    CLOCK_InitSysPfd(kCLOCK_Pfd0, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable System pfd0. */");
      CWriter.line("    CLOCK_DeinitSysPfd(kCLOCK_Pfd0);");
    }
    if (HwAbstr.isSysPfdEnabled(cfg, "1")) {
      tmp = HwAbstr.getSysPfdFrac(cfg, "1");
      CWriter.line("    /* Init System pfd1. */");
      CWriter.line("    CLOCK_InitSysPfd(kCLOCK_Pfd1, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable System pfd1. */");
      CWriter.line("    CLOCK_DeinitSysPfd(kCLOCK_Pfd1);");
    }
    if (HwAbstr.isSysPfdEnabled(cfg, "2")) {
      tmp = HwAbstr.getSysPfdFrac(cfg, "2");
      CWriter.line("    /* Init System pfd2. */");
      CWriter.line("    CLOCK_InitSysPfd(kCLOCK_Pfd2, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable System pfd2. */");
      CWriter.line("    CLOCK_DeinitSysPfd(kCLOCK_Pfd2);");
    }
    if (HwAbstr.isSysPfdEnabled(cfg, "3")) {
      tmp = HwAbstr.getSysPfdFrac(cfg, "3");
      CWriter.line("    /* Init System pfd3. */");
      CWriter.line("    CLOCK_InitSysPfd(kCLOCK_Pfd3, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable System pfd3. */");
      CWriter.line("    CLOCK_DeinitSysPfd(kCLOCK_Pfd3);");
    }
    if (HwAbstr.isPfdOffsetEnabled(cfg, "2")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_SYS |= CCM_ANALOG_PLL_SYS_PFD_OFFSET_EN_MASK;");
    } else {
      CWriter.line("    /* Disable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_SYS &= ~CCM_ANALOG_PLL_SYS_PFD_OFFSET_EN_MASK;");
    }
    if (!HwAbstr.isPllBypassed(cfg, "2")) {
      CWriter.line("    /* Disable bypass for System PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_SYS &= ~CCM_ANALOG_PLL_SYS_BYPASS_MASK;");
    }
    if (!HwAbstr.isSysPllOutputEnabled(cfg)) {
      CWriter.line("    /* Disable System PLL output. */");
      CWriter.line("    CCM_ANALOG->PLL_SYS &= ~CCM_ANALOG_PLL_SYS_ENABLE_MASK;");
    }
  } else {
    CWriter.line("    /* Set System PLL bypass clock source. */");
    CWriter.line("    CLOCK_SetPllBypassRefClkSrc(CCM_ANALOG, kCLOCK_PllSys, " + HwAbstr.getPllBypassClkSrc(cfg, "2") + ");");
    CWriter.line("    /* DeInit System PLL. */");
    CWriter.line("    CLOCK_DeinitSysPll();");
    if (HwAbstr.isPfdOffsetEnabled(cfg, "2")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_SYS |= CCM_ANALOG_PLL_SYS_PFD_OFFSET_EN_MASK;");
    }
    if (HwAbstr.isPllBypassed(cfg, "2")) {
      CWriter.line("    /* Bypass System PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_SYS |= CCM_ANALOG_PLL_SYS_BYPASS_MASK;");
      if (HwAbstr.isSysPllOutputEnabled(cfg)) {
        CWriter.line("    /* Enable System PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_SYS |= CCM_ANALOG_PLL_SYS_ENABLE_MASK;");
      }
    }
  }
  CWriter.line("#endif");

  /* Init Usb1 PLL */
  CWriter.line("    /* In SDK projects, external flash (configured by FLEXSPI) will be initialized by dcd.");
  CWriter.line("     * With this macro XIP_EXTERNAL_FLASH, usb1 pll (selected to be FLEXSPI clock source in SDK projects) will be left unchanged.");
  CWriter.line("     * Note: If another clock source is selected for FLEXSPI, user may want to avoid changing that clock as well.*/");
  CWriter.line("#if !(defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1))");
  if (HwAbstr.isPllActive(cfg, "3")) {
    CWriter.line("    /* Init Usb1 PLL. */");
    CWriter.line("    /* Bypass PLL first. */");
    CWriter.line("    CCM_ANALOG->PLL_USB1 = (CCM_ANALOG->PLL_USB1 & (~CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_MASK)) |");
    CWriter.line("                           CCM_ANALOG_PLL_USB1_BYPASS_MASK | CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC(" + HwAbstr.getPllBypassClkSrc(cfg, "3") + ");");
    CWriter.line("    CCM_ANALOG->PLL_USB1 = (CCM_ANALOG->PLL_USB1 & (~CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK)) |");
    CWriter.line("                           CCM_ANALOG_PLL_USB1_ENABLE_MASK | CCM_ANALOG_PLL_USB1_POWER_MASK |");
    CWriter.line("                           CCM_ANALOG_PLL_USB1_EN_USB_CLKS_MASK | CCM_ANALOG_PLL_USB1_DIV_SELECT(0);");
    CWriter.line("    while ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_LOCK_MASK) == 0)");
    CWriter.line("    {");
    CWriter.line("    }");
    if (HwAbstr.isUsb1PfdEnabled(cfg, "0")) {
      tmp = HwAbstr.getUsb1PfdFrac(cfg, "0");
      CWriter.line("    /* Init Usb1 pfd0. */");
      CWriter.line("    CLOCK_InitUsb1Pfd(kCLOCK_Pfd0, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable Usb1 pfd0. */");
      CWriter.line("    CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd0);");
    }
    if (HwAbstr.isUsb1PfdEnabled(cfg, "1")) {
      tmp = HwAbstr.getUsb1PfdFrac(cfg, "1");
      CWriter.line("    /* Init Usb1 pfd1. */");
      CWriter.line("    CLOCK_InitUsb1Pfd(kCLOCK_Pfd1, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable Usb1 pfd1. */");
      CWriter.line("    CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd1);");
    }
    if (HwAbstr.isUsb1PfdEnabled(cfg, "2")) {
      tmp = HwAbstr.getUsb1PfdFrac(cfg, "2");
      CWriter.line("    /* Init Usb1 pfd2. */");
      CWriter.line("    CLOCK_InitUsb1Pfd(kCLOCK_Pfd2, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable Usb1 pfd2. */");
      CWriter.line("    CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd2);");
    }
    if (HwAbstr.isUsb1PfdEnabled(cfg, "3")) {
      tmp = HwAbstr.getUsb1PfdFrac(cfg, "3");
      CWriter.line("    /* Init Usb1 pfd3. */");
      CWriter.line("    CLOCK_InitUsb1Pfd(kCLOCK_Pfd3, " + tmp + ");");
    } else {
      CWriter.line("    /* Disable Usb1 pfd3. */");
      CWriter.line("    CLOCK_DeinitUsb1Pfd(kCLOCK_Pfd3);");
    }
    if (!HwAbstr.isUsbPllOutputForUsbphyEnabled(cfg, "1")) {
      CWriter.line("    /* Disable Usb1 PLL output for USBPHY1. */");
      CWriter.line("    CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_EN_USB_CLKS_MASK;");
    }
    if (!HwAbstr.isPllBypassed(cfg, "3")) {
      CWriter.line("    /* Disable bypass for Usb1 PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_BYPASS_MASK;");
    }
    if (!HwAbstr.isUsb1PllOutputEnabled(cfg)) {
      CWriter.line("    /* Disable Usb1 PLL output. */");
      CWriter.line("    CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_ENABLE_MASK;");
    }
  } else {
    CWriter.line("    /* Set Usb1 PLL bypass clock source. */");
    CWriter.line("    CLOCK_SetPllBypassRefClkSrc(CCM_ANALOG, kCLOCK_PllUsb1, " + HwAbstr.getPllBypassClkSrc(cfg, "3") + ");");
    CWriter.line("    /* DeInit Usb1 PLL. */");
    CWriter.line("    CLOCK_DeinitUsb1Pll();");
    if (HwAbstr.isUsbPllOutputForUsbphyEnabled(cfg, "1")) {
      CWriter.line("    /* Enable Usb1 PLL output for USBPHY1. */");
      CWriter.line("    CCM_ANALOG->PLL_USB1 |= CCM_ANALOG_PLL_USB1_EN_USB_CLKS_MASK;");
    }
    if (HwAbstr.isPllBypassed(cfg, "3")) {
      CWriter.line("    /* Bypass Usb1 PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_USB1 |= CCM_ANALOG_PLL_USB1_BYPASS_MASK;");
      if (HwAbstr.isUsb1PllOutputEnabled(cfg)) {
        CWriter.line("    /* Enable Usb1 PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_USB1 |= CCM_ANALOG_PLL_USB1_ENABLE_MASK;");
      }
    }
  }
  CWriter.line("#endif");

  /* Init Audio PLL */
  if (HwAbstr.isPllActive(cfg, "4")) {
    CWriter.line("    /* Init Audio PLL. */");
    CWriter.line("    uint32_t pllAudio;");
    CWriter.line("    /* Disable Audio PLL output before initial Audio PLL. */");
    CWriter.line("    CCM_ANALOG->PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_ENABLE_MASK;");
    CWriter.line("    /* Bypass PLL first */");
    CWriter.line("    CCM_ANALOG->PLL_AUDIO = (CCM_ANALOG->PLL_AUDIO & (~CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC_MASK)) |");
    CWriter.line("                            CCM_ANALOG_PLL_AUDIO_BYPASS_MASK | CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC(" + HwAbstr.getPllBypassClkSrc(cfg, "4") + ");");
    CWriter.line("    CCM_ANALOG->PLL_AUDIO_NUM = CCM_ANALOG_PLL_AUDIO_NUM_A(" + HwAbstr.getPllNumerator(cfg, "4") + ");");
    CWriter.line("    CCM_ANALOG->PLL_AUDIO_DENOM = CCM_ANALOG_PLL_AUDIO_DENOM_B(" + HwAbstr.getPllDenominator(cfg, "4") + ");");
    CWriter.line("    pllAudio = (CCM_ANALOG->PLL_AUDIO & (~(CCM_ANALOG_PLL_AUDIO_DIV_SELECT_MASK | CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK))) |");
    CWriter.line("               CCM_ANALOG_PLL_AUDIO_ENABLE_MASK | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(" + HwAbstr.getPllLoopDivider(cfg, "4") + ");");
    CWriter.line("    pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(" + HwAbstr.getPllPostDivider(cfg, "4") + ");");
    if (HwAbstr.getAudioDividerLSB(cfg)) {
      CWriter.line("    CCM_ANALOG->MISC2 |= CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;");
    } else {
      CWriter.line("    CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;");
    }
    if (HwAbstr.getAudioDividerMSB(cfg)) {
      CWriter.line("    CCM_ANALOG->MISC2 |= CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK;");
    } else {
      CWriter.line("    CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK;");
    }
    CWriter.line("    CCM_ANALOG->PLL_AUDIO = pllAudio;");
    CWriter.line("    while ((CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK_MASK) == 0)");
    CWriter.line("    {");
    CWriter.line("    }");
    if (!HwAbstr.isPllBypassed(cfg, "4")) {
      CWriter.line("    /* Disable bypass for Audio PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS_MASK;");
    }
    if (!HwAbstr.isAudioPllOutputEnabled(cfg)) {
      CWriter.line("    /* Disable Audio PLL output. */");
      CWriter.line("    CCM_ANALOG->PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_ENABLE_MASK;");
    }
    if (HwAbstr.isPfdOffsetEnabled(cfg, "4")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_PFD_OFFSET_EN_MASK;");
    } else {
      CWriter.line("    /* Disable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_PFD_OFFSET_EN_MASK;");
    }
  } else {
    CWriter.line("    /* Set Audio PLL bypass clock source. */");
    CWriter.line("    CLOCK_SetPllBypassRefClkSrc(CCM_ANALOG, kCLOCK_PllAudio, " + HwAbstr.getPllBypassClkSrc(cfg, "4") + ");");
    CWriter.line("    /* DeInit Audio PLL. */");
    CWriter.line("    CLOCK_DeinitAudioPll();");
    if (HwAbstr.isPfdOffsetEnabled(cfg, "4")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_PFD_OFFSET_EN_MASK;");
    }
    if (HwAbstr.isPllBypassed(cfg, "4")) {
      CWriter.line("    /* Bypass Audio PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_BYPASS_MASK;");
      if (HwAbstr.getAudioDividerLSB(cfg)) {
        CWriter.line("    CCM_ANALOG->MISC2 |= CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;");
      } else {
        CWriter.line("    CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;");
      }
      if (HwAbstr.getAudioDividerMSB(cfg)) {
        CWriter.line("    CCM_ANALOG->MISC2 |= CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK;");
      } else {
        CWriter.line("    CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK;");
      }
      if (HwAbstr.isAudioPllOutputEnabled(cfg)) {
        CWriter.line("    /* Enable Audio PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_ENABLE_MASK;");
      }
    }
  }

  /* Init Video PLL */
  if (HwAbstr.isPllActive(cfg, "5")) {
    CWriter.line("    /* Init Video PLL. */");
    CWriter.line("    uint32_t pllVideo;");
    CWriter.line("    /* Disable Video PLL output before initial Video PLL. */");
    CWriter.line("    CCM_ANALOG->PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_ENABLE_MASK;");
    CWriter.line("    /* Bypass PLL first */");
    CWriter.line("    CCM_ANALOG->PLL_VIDEO = (CCM_ANALOG->PLL_VIDEO & (~CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC_MASK)) |");
    CWriter.line("                            CCM_ANALOG_PLL_VIDEO_BYPASS_MASK | CCM_ANALOG_PLL_VIDEO_BYPASS_CLK_SRC(" + HwAbstr.getPllBypassClkSrc(cfg, "5") + ");");
    CWriter.line("    CCM_ANALOG->PLL_VIDEO_NUM = CCM_ANALOG_PLL_VIDEO_NUM_A(" + HwAbstr.getPllNumerator(cfg, "5") + ");");
    CWriter.line("    CCM_ANALOG->PLL_VIDEO_DENOM = CCM_ANALOG_PLL_VIDEO_DENOM_B(" + HwAbstr.getPllDenominator(cfg, "5") + ");");
    CWriter.line("    pllVideo = (CCM_ANALOG->PLL_VIDEO & (~(CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK | CCM_ANALOG_PLL_VIDEO_POWERDOWN_MASK))) |");
    CWriter.line("               CCM_ANALOG_PLL_VIDEO_ENABLE_MASK |CCM_ANALOG_PLL_VIDEO_DIV_SELECT(" + HwAbstr.getPllLoopDivider(cfg, "5") + ");");
    CWriter.line("    pllVideo |= CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(" + HwAbstr.getPllPostDivider(cfg, "5") + ");");
    tmp = HwAbstr.getVideoDivider(cfg);
    CWriter.line("    CCM_ANALOG->MISC2 = (CCM_ANALOG->MISC2 & (~CCM_ANALOG_MISC2_VIDEO_DIV_MASK)) | CCM_ANALOG_MISC2_VIDEO_DIV("+ tmp + ");");
    CWriter.line("    CCM_ANALOG->PLL_VIDEO = pllVideo;");
    CWriter.line("    while ((CCM_ANALOG->PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_LOCK_MASK) == 0)");
    CWriter.line("    {");
    CWriter.line("    }");
    if (!HwAbstr.isPllBypassed(cfg, "5")) {
      CWriter.line("    /* Disable bypass for Video PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_BYPASS_MASK;");
    }
    if (!HwAbstr.isVideoPllOutputEnabled(cfg)) {
      CWriter.line("    /* Disable Video PLL output. */");
      CWriter.line("    CCM_ANALOG->PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_ENABLE_MASK;");
    }
    if (HwAbstr.isPfdOffsetEnabled(cfg, "5")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_VIDEO |= CCM_ANALOG_PLL_VIDEO_PFD_OFFSET_EN_MASK;");
    } else {
      CWriter.line("    /* Disable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_PFD_OFFSET_EN_MASK;");
    }
  } else {
    CWriter.line("    /* Set Video PLL bypass clock source. */");
    CWriter.line("    CLOCK_SetPllBypassRefClkSrc(CCM_ANALOG, kCLOCK_PllVideo, " + HwAbstr.getPllBypassClkSrc(cfg, "5") + ");");
    CWriter.line("    /* DeInit Video PLL. */");
    CWriter.line("    CLOCK_DeinitVideoPll();");
    if (HwAbstr.isPfdOffsetEnabled(cfg, "5")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_VIDEO |= CCM_ANALOG_PLL_VIDEO_PFD_OFFSET_EN_MASK;");
    }
    if (HwAbstr.isPllBypassed(cfg, "5")) {
      CWriter.line("    /* Bypass Video PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_VIDEO |= CCM_ANALOG_PLL_VIDEO_BYPASS_MASK;");
      tmp = HwAbstr.getVideoDivider(cfg);
      CWriter.line("    CCM_ANALOG->MISC2 = (CCM_ANALOG->MISC2 & (~CCM_ANALOG_MISC2_VIDEO_DIV_MASK)) | CCM_ANALOG_MISC2_VIDEO_DIV("+ tmp + ");");
      if (HwAbstr.isVideoPllOutputEnabled(cfg)) {
        CWriter.line("    /* Enable Video PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_VIDEO |= CCM_ANALOG_PLL_VIDEO_ENABLE_MASK;");
      }
    }
  }

  /* Init Enet PLL */
  if (HwAbstr.isPllActive(cfg, "6")) {
    CWriter.line("    /* Init Enet PLL. */");
    CWriter.line("    uint32_t enet_pll = CCM_ANALOG_PLL_ENET_DIV_SELECT(" + HwAbstr.getPllLoopDivider(cfg, "6") + ");");
    CWriter.line("    /* Bypass PLL first */");
    CWriter.line("    CCM_ANALOG->PLL_ENET = (CCM_ANALOG->PLL_ENET & (~CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC_MASK)) |");
    CWriter.line("                           CCM_ANALOG_PLL_ENET_BYPASS_MASK | CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC(" + HwAbstr.getPllBypassClkSrc(cfg, "7") + ");");
    if (HwAbstr.isEnet1PllOutputEnabled(cfg)) {
      CWriter.line("    enet_pll |= CCM_ANALOG_PLL_ENET_ENABLE_MASK;");
    }
    if (HwAbstr.isEnet2PllOutputEnabled(cfg)) {
      CWriter.line("    enet_pll |= CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN_MASK;");
    }
    CWriter.line("    CCM_ANALOG->PLL_ENET = (CCM_ANALOG->PLL_ENET & (~(CCM_ANALOG_PLL_ENET_DIV_SELECT_MASK | CCM_ANALOG_PLL_ENET_POWERDOWN_MASK))) | enet_pll;");
    CWriter.line("    while ((CCM_ANALOG->PLL_ENET & CCM_ANALOG_PLL_ENET_LOCK_MASK) == 0)");
    CWriter.line("    {");
    CWriter.line("    }");
    if (!HwAbstr.isPllBypassed(cfg, "6")) {
      CWriter.line("    /* Disable bypass for Enet PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_ENET &= ~CCM_ANALOG_PLL_ENET_BYPASS_MASK;");
    }
    if (HwAbstr.isPfdOffsetEnabled(cfg, "6")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_ENET |= CCM_ANALOG_PLL_ENET_PFD_OFFSET_EN_MASK;");
    } else {
      CWriter.line("    /* Disable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_ENET &= ~CCM_ANALOG_PLL_ENET_PFD_OFFSET_EN_MASK;");
    }
  } else {
    CWriter.line("    /* Set Enet PLL bypass clock source. */");
    CWriter.line("    CLOCK_SetPllBypassRefClkSrc(CCM_ANALOG, kCLOCK_PllEnet0, " + HwAbstr.getPllBypassClkSrc(cfg, "6") + ");");
    CWriter.line("    /* DeInit Enet PLL. */");
    CWriter.line("    CLOCK_DeinitEnetPll();");
    if (HwAbstr.isPfdOffsetEnabled(cfg, "6")) {
      CWriter.line("    /* Enable pfd offset. */");
      CWriter.line("    CCM_ANALOG->PLL_ENET |= CCM_ANALOG_PLL_ENET_PFD_OFFSET_EN_MASK;");
    }
    if (HwAbstr.isPllBypassed(cfg, "6")) {
      CWriter.line("    /* Bypass Enet PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_ENET |= CCM_ANALOG_PLL_ENET_BYPASS_MASK;");
      if (HwAbstr.isEnet1PllOutputEnabled(cfg)) {
        tmp = HwAbstr.getPllLoopDivider(cfg, "6");
        CWriter.line("    CCM_ANALOG->PLL_ENET = (CCM_ANALOG->PLL_ENET & (~CCM_ANALOG_PLL_ENET_DIV_SELECT_MASK)) | CCM_ANALOG_PLL_ENET_DIV_SELECT("+ tmp + ");");
        CWriter.line("    /* Enable Enet1 PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_ENET |= CCM_ANALOG_PLL_ENET_ENABLE_MASK;");
      }
      if (HwAbstr.isEnet2PllOutputEnabled(cfg)) {
        CWriter.line("    /* Enable Enet2 PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_ENET |= CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN_MASK;");
      }
    }
  }

  /* Init Usb2 PLL */
  if (HwAbstr.isPllActive(cfg, "7")) {
    CWriter.line("    /* Init Usb2 PLL. */");
    CWriter.line("    /* Bypass PLL first. */");
    CWriter.line("    CCM_ANALOG->PLL_USB2 = (CCM_ANALOG->PLL_USB2 & (~CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_MASK)) |");
    CWriter.line("                           CCM_ANALOG_PLL_USB2_BYPASS_MASK | CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC(" + HwAbstr.getPllBypassClkSrc(cfg, "7") + ");");
    CWriter.line("    CCM_ANALOG->PLL_USB2 = (CCM_ANALOG->PLL_USB2 & (~CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK)) |");
    CWriter.line("                           CCM_ANALOG_PLL_USB2_ENABLE_MASK | CCM_ANALOG_PLL_USB2_POWER_MASK |");
    CWriter.line("                           CCM_ANALOG_PLL_USB2_EN_USB_CLKS_MASK | CCM_ANALOG_PLL_USB2_DIV_SELECT(0);");
    CWriter.line("    while ((CCM_ANALOG->PLL_USB2 & CCM_ANALOG_PLL_USB2_LOCK_MASK) == 0)");
    CWriter.line("    {");
    CWriter.line("    }");
    if (!HwAbstr.isUsbPllOutputForUsbphyEnabled(cfg, "2")) {
      CWriter.line("    /* Disable Usb2 PLL output for USBPHY2. */");
      CWriter.line("    CCM_ANALOG->PLL_USB2 &= ~CCM_ANALOG_PLL_USB2_EN_USB_CLKS_MASK;");
    }
    if (!HwAbstr.isPllBypassed(cfg, "7")) {
      CWriter.line("    /* Disable bypass for Usb2 PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_USB2 &= ~CCM_ANALOG_PLL_USB2_BYPASS_MASK;");
    }
    if (!HwAbstr.isUsb2PllOutputEnabled(cfg)) {
      CWriter.line("    /* Disable Usb2 PLL output. */");
      CWriter.line("    CCM_ANALOG->PLL_USB2 &= ~CCM_ANALOG_PLL_USB2_ENABLE_MASK;");
    }
  } else {
    CWriter.line("    /* Set Usb2 PLL bypass clock source. */");
    CWriter.line("    CLOCK_SetPllBypassRefClkSrc(CCM_ANALOG, kCLOCK_PllUsb2, " + HwAbstr.getPllBypassClkSrc(cfg, "7") + ");");
    CWriter.line("    /* DeInit Usb2 PLL. */");
    CWriter.line("    CLOCK_DeinitUsb2Pll();");
    if (HwAbstr.isUsbPllOutputForUsbphyEnabled(cfg, "2")) {
      CWriter.line("    /* Enable Usb2 PLL output for USBPHY2. */");
      CWriter.line("    CCM_ANALOG->PLL_USB2 |= CCM_ANALOG_PLL_USB2_EN_USB_CLKS_MASK;");
    }
    if (HwAbstr.isPllBypassed(cfg, "7")) {
      CWriter.line("    /* Bypass Usb2 PLL. */");
      CWriter.line("    CCM_ANALOG->PLL_USB2 |= CCM_ANALOG_PLL_USB2_BYPASS_MASK;");
      if (HwAbstr.isUsb2PllOutputEnabled(cfg)) {
        CWriter.line("    /* Enable Usb2 PLL output. */");
        CWriter.line("    CCM_ANALOG->PLL_USB2 |= CCM_ANALOG_PLL_USB2_ENABLE_MASK;");
      } 
    }
  }

  /* Set clock mux and divider */
  CWriter.line("    /* Set AHB_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_AhbDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_AhbDiv") +");");

  CWriter.line("    /* Set IPG_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_IpgDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_IpgDiv") +");");

  CWriter.line("    /* Set ARM_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_ArmDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_ArmDiv") +");");

  CWriter.line("    /* Set preperiph clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_PrePeriphMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_PrePeriphMux") +");");

  if (HwAbstr.getClockMux(cfg, "kCLOCK_PeriphMux") != 0) {
    CWriter.line("    /* Set periph clock source to other clock source before change periph clock2 source. */");
  } else {
    CWriter.line("    /* Set periph clock source. */");
  }
  CWriter.line("    CLOCK_SetMux(kCLOCK_PeriphMux, 0);");
 
  CWriter.line("    /* Set PERIPH_CLK2_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_PeriphClk2Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_PeriphClk2Div") +");");

  CWriter.line("    /* Set periph clock2 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_PeriphClk2Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_PeriphClk2Mux") +");");

  if (HwAbstr.getClockMux(cfg, "kCLOCK_PeriphMux") != 0) {
    CWriter.line("    /* Set periph clock source. */");
    CWriter.line("    CLOCK_SetMux(kCLOCK_PeriphMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_PeriphMux") +");");
  }

  CWriter.line("    /* Set PERCLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_PerclkDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_PerclkDiv") +");");

  CWriter.line("    /* Set per clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_PerclkMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_PerclkMux") +");");

  CWriter.line("    /* Set USDHC1_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Usdhc1Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Usdhc1Div") +");");

  CWriter.line("    /* Set Usdhc1 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Usdhc1Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Usdhc1Mux") +");");

  CWriter.line("    /* Set USDHC2_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Usdhc2Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Usdhc2Div") +");");

  CWriter.line("    /* Set Usdhc2 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Usdhc2Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Usdhc2Mux") +");");

  CWriter.line("    /* In SDK projects, SDRAM (configured by SEMC) will be initialized in either debug script or dcd.");
  CWriter.line("     * With this macro SKIP_SYSCLK_INIT, system pll (selected to be SEMC source clock in SDK projects) will be left unchanged.");
  CWriter.line("     * Note: If another clock source is selected for SEMC, user may want to avoid changing that clock as well.*/");
  CWriter.line("#ifndef SKIP_SYSCLK_INIT");
  CWriter.line("    /* Set SEMC_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_SemcDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_SemcDiv") +");");

  CWriter.line("    /* Set Semc alt clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_SemcAltMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_SemcAltMux") +");");

  CWriter.line("    /* Set Semc clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_SemcMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_SemcMux") +");");
  CWriter.line("#endif");

  CWriter.line("    /* In SDK projects, external flash (configured by FLEXSPI) will be initialized by dcd.");
  CWriter.line("     * With this macro XIP_EXTERNAL_FLASH, usb1 pll (selected to be FLEXSPI clock source in SDK projects) will be left unchanged.");
  CWriter.line("     * Note: If another clock source is selected for FLEXSPI, user may want to avoid changing that clock as well.*/");
  CWriter.line("#if !(defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1))");
  CWriter.line("    /* Set FLEXSPI_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_FlexspiDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_FlexspiDiv") +");");
  CWriter.line("    /* Set Flexspi clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_FlexspiMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_FlexspiMux") +");");
  CWriter.line("#endif");

  CWriter.line("    /* Set CSI_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_CsiDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_CsiDiv") +");");

  CWriter.line("    /* Set Csi clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_CsiMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_CsiMux") +");");

  CWriter.line("    /* Set LPSPI_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_LpspiDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_LpspiDiv") +");");

  CWriter.line("    /* Set Lpspi clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_LpspiMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_LpspiMux") +");");

  CWriter.line("    /* Set TRACE_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_TraceDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_TraceDiv") +");");

  CWriter.line("    /* Set Trace clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_TraceMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_TraceMux") +");");

  CWriter.line("    /* Set SAI1_CLK_PRED. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Sai1PreDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Sai1PreDiv") +");");

  CWriter.line("    /* Set SAI1_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Sai1Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Sai1Div") +");");

  CWriter.line("    /* Set Sai1 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Sai1Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Sai1Mux") +");");

  CWriter.line("    /* Set SAI2_CLK_PRED. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Sai2PreDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Sai2PreDiv") +");");

  CWriter.line("    /* Set SAI2_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Sai2Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Sai2Div") +");");

  CWriter.line("    /* Set Sai2 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Sai2Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Sai2Mux") +");");

  CWriter.line("    /* Set SAI3_CLK_PRED. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Sai3PreDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Sai3PreDiv") +");");

  CWriter.line("    /* Set SAI3_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Sai3Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Sai3Div") +");");

  CWriter.line("    /* Set Sai3 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Sai3Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Sai3Mux") +");");

  CWriter.line("    /* Set LPI2C_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Lpi2cDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Lpi2cDiv") +");");

  CWriter.line("    /* Set Lpi2c clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Lpi2cMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Lpi2cMux") +");");

  CWriter.line("    /* Set CAN_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_CanDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_CanDiv") +");");

  CWriter.line("    /* Set Can clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_CanMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_CanMux") +");");

  CWriter.line("    /* Set UART_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_UartDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_UartDiv") +");");

  CWriter.line("    /* Set Uart clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_UartMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_UartMux") +");");

  CWriter.line("    /* Set LCDIF_PRED. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_LcdifPreDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_LcdifPreDiv") +");");

  CWriter.line("    /* Set LCDIF_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_LcdifDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_LcdifDiv") +");");

  CWriter.line("    /* Set Lcdif pre clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_LcdifPreMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_LcdifPreMux") +");");

  CWriter.line("    /* Set SPDIF0_CLK_PRED. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Spdif0PreDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Spdif0PreDiv") +");");

  CWriter.line("    /* Set SPDIF0_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Spdif0Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Spdif0Div") +");");

  CWriter.line("    /* Set Spdif clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_SpdifMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_SpdifMux") +");");

  CWriter.line("    /* Set FLEXIO1_CLK_PRED. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Flexio1PreDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Flexio1PreDiv") +");");

  CWriter.line("    /* Set FLEXIO1_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Flexio1Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Flexio1Div") +");");

  CWriter.line("    /* Set Flexio1 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Flexio1Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Flexio1Mux") +");");

  CWriter.line("    /* Set FLEXIO2_CLK_PRED. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Flexio2PreDiv, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Flexio2PreDiv") +");");

  CWriter.line("    /* Set FLEXIO2_CLK_PODF. */");
  CWriter.line("    CLOCK_SetDiv(kCLOCK_Flexio2Div, " + HwAbstr.getClockDiv(cfg, "kCLOCK_Flexio2Div") +");");

  CWriter.line("    /* Set Flexio2 clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Flexio2Mux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Flexio2Mux") +");");

  CWriter.line("    /* Set Pll3 sw clock source. */");
  CWriter.line("    CLOCK_SetMux(kCLOCK_Pll3SwMux, " + HwAbstr.getClockMux(cfg, "kCLOCK_Pll3SwMux") +");");

  CWriter.line("    /* Set lvds1 clock source. */");
  CWriter.line("    CCM_ANALOG->MISC1 = (CCM_ANALOG->MISC1 & (~CCM_ANALOG_MISC1_LVDS1_CLK_SEL_MASK)) | CCM_ANALOG_MISC1_LVDS1_CLK_SEL(" + HwAbstr.getLvds1ClkMux(cfg) + ");");

  if (HwAbstr.isClockElementUsed(cfg, "clk1")) {
    if (HwAbstr.isClk1InputEnabled(cfg)) {
      CWriter.line("    /* Enable LVDS input buffer. */");
      CWriter.line("    CCM_ANALOG->MISC1 |= CCM_ANALOG_MISC1_LVDSCLK1_IBEN_MASK;");
      CWriter.line("    CCM_ANALOG->MISC1 &= ~CCM_ANALOG_MISC1_LVDSCLK1_OBEN_MASK;");
    } else {
      CWriter.line("    /* Enable LVDS output buffer. */");
      CWriter.line("    CCM_ANALOG->MISC1 &= ~CCM_ANALOG_MISC1_LVDSCLK1_IBEN_MASK;");
      CWriter.line("    CCM_ANALOG->MISC1 |= CCM_ANALOG_MISC1_LVDSCLK1_OBEN_MASK;");
    }
  }

  CWriter.line("    /* Set clock out1 divider. */");
  CWriter.line("    CCM->CCOSR = (CCM->CCOSR & (~CCM_CCOSR_CLKO1_DIV_MASK)) | CCM_CCOSR_CLKO1_DIV(" + HwAbstr.getClko1Div(cfg) + ");");
  CWriter.line("    /* Set clock out1 source. */");
  CWriter.line("    CCM->CCOSR = (CCM->CCOSR & (~CCM_CCOSR_CLKO1_SEL_MASK)) | CCM_CCOSR_CLKO1_SEL(" + HwAbstr.getClko1Mux(cfg) + ");");
  CWriter.line("    /* Set clock out2 divider. */");
  CWriter.line("    CCM->CCOSR = (CCM->CCOSR & (~CCM_CCOSR_CLKO2_DIV_MASK)) | CCM_CCOSR_CLKO2_DIV(" + HwAbstr.getClko2Div(cfg) + ");");
  CWriter.line("    /* Set clock out2 source. */");
  CWriter.line("    CCM->CCOSR = (CCM->CCOSR & (~CCM_CCOSR_CLKO2_SEL_MASK)) | CCM_CCOSR_CLKO2_SEL(" + HwAbstr.getClko2Mux(cfg) + ");");
  if (HwAbstr.isClko2Drived(cfg)) {
    CWriter.line("    /* Set clock out1 drives clock out2. */");
    CWriter.line("    CCM->CCOSR |= CCM_CCOSR_CLK_OUT_SEL_MASK;");
  } else {
    CWriter.line("    /* Set clock out1 drives clock out1. */");
    CWriter.line("    CCM->CCOSR &= ~CCM_CCOSR_CLK_OUT_SEL_MASK;");
  }
  if (HwAbstr.isClko1Enabled(cfg)) {
    CWriter.line("    /* Enable clock out1. */");
    CWriter.line("    CCM->CCOSR |= CCM_CCOSR_CLKO1_EN_MASK;");
  } else {
    CWriter.line("    /* Disable clock out1. */");
    CWriter.line("    CCM->CCOSR &= ~CCM_CCOSR_CLKO1_EN_MASK;");
  }

  if (HwAbstr.isClko2Enabled(cfg)) {
    CWriter.line("    /* Enable clock out2. */");
    CWriter.line("    CCM->CCOSR |= CCM_CCOSR_CLKO2_EN_MASK;");
  } else {
    if (HwAbstr.isClko2Drived(cfg)) {
      CWriter.line("    /* Enable clock out2. */");
      CWriter.line("    /* NOTE: CLKO2 output should be enabled if you set clock out1 drives clock out2. */");
      CWriter.line("    CCM->CCOSR |= CCM_CCOSR_CLKO2_EN_MASK;");
    } else {
      CWriter.line("    /* Disable clock out2. */");
      CWriter.line("    CCM->CCOSR &= ~CCM_CCOSR_CLKO2_EN_MASK;");
    }
  }

  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
CWriter.write(Gen.profile.getYaml());
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 fircSafeConfigFunctionReq = false;
var fircSafeConfigFunctionReq = false;
var getClkOutSelectFunctionReq = false;
var getScgClkOutSelFunctionReq = false;
var getRtc32KClkSrcFunctionReq = false;
var getCanFltClkSelFunctionReq = false;
var getSetTraceClockFunctionReq = 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
  CWriter.write(cfg.getYaml());
  CWriter.line("");

  // write variables introduction
  CWriter.writeLines(OutputUtils.getVariablesIntro(cfg));
  // generate the structure variable for each configuration 
//  GenerateCLKConfigStructure(cfg);
  // write code introduction
  CWriter.writeLines(OutputUtils.getCodeIntro(cfg));
  // write boot function
  GenerateBootFunction(cfg);
}

// write internal functions on position internalFunctionPosition
if (getRtc32KClkSrcFunctionReq) {
  CWriter.writeLines(getRtc32KClkSrcFunction().concat(""), internalFunctionPosition);
}
if (getCanFltClkSelFunctionReq) {
  CWriter.writeLines(getCanFltClkSelFunction().concat(""), internalFunctionPosition);
}
if (getSetTraceClockFunctionReq) {
  CWriter.writeLines(getSetTraceClockFunction().concat(""), internalFunctionPosition);
}
if (getScgClkOutSelFunctionReq) {
  CWriter.writeLines(getScgClkOutSelFunction().concat(""), internalFunctionPosition);
}
if (getClkOutSelectFunctionReq) {
  CWriter.writeLines(getClkOutSelectFunction().concat(""), internalFunctionPosition);
}
if (fircSafeConfigFunctionReq) {
  CWriter.writeLines(getFircSafteConfigFunction().concat(""), internalFunctionPosition);
}
 
// 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 sdkIncludesKeys = Object.keys(sdkIncludes);

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++) {
  pushDependency(dependencyHelper, createDependency("common", Gen.getConfigID(Gen.configs[0]), coreIds[coreId]));
}
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++) {
        pushDependency(dependencyHelper, createDependency(includeItem["name"], includeItem["configuration"], coreIds[coreId]));
      }
    }
  }
}
//  scriptApi.logWarning("configName " + this.armPllConfigExternPosition[configID]);
