#!/usr/local/bin/perl5.8.0
###############################################################################################
# Copyright 2004, All Rights Reserved, Texas Instruments Incorporate#
# PLL.pm
#
# This script is a Perl module for use with genBootCfg.pl boot mode configuration tool
# for TMS320C672x boot load.
#
# 5-Apr-2005 v000.00.01
# - Created
#
# ###############################################################################################
# /*===========================================================================*/
# /* This software is provided under the following                             */
# /* License Agreement:                                                        */
# /*===========================================================================*/
# /* THIS PROGRAM IS PROVIDED "AS IS". TI MAKES NO WARRANTIES OR               */
# /* REPRESENTATIONS, EITHER EXPRESS, IMPLIED OR STATUTORY,                    */
# /* INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS              */
# /* FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR                    */
# /* COMPLETENESS OF RESPONSES, RESULTS AND LACK OF NEGLIGENCE.                */
# /* TI DISCLAIMS ANY WARRANTY OF TITLE, QUIET ENJOYMENT, QUIET                */
# /* POSSESSION, AND NON-INFRINGEMENT OF ANY THIRD PARTY                       */
# /* INTELLECTUAL PROPERTY RIGHTS WITH REGARD TO THE PROGRAM OR                */
# /* YOUR USE OF THE PROGRAM.                                                  */
# /*                                                                           */
# /* IN NO EVENT SHALL TI BE LIABLE FOR ANY SPECIAL, INCIDENTAL,               */
# /* CONSEQUENTIAL OR INDIRECT DAMAGES, HOWEVER CAUSED, ON ANY                 */
# /* THEORY OF LIABILITY AND WHETHER OR NOT TI HAS BEEN ADVISED                */
# /* OF THE POSSIBILITY OF SUCH DAMAGES, ARISING IN ANY WAY OUT                */
# /* OF THIS AGREEMENT, THE PROGRAM, OR YOUR USE OF THE PROGRAM.               */
# /* EXCLUDED DAMAGES INCLUDE, BUT ARE NOT LIMITED TO, COST OF                 */
# /* REMOVAL OR REINSTALLATION, COMPUTER TIME, LABOR COSTS, LOSS               */
# /* OF GOODWILL, LOSS OF PROFITS, LOSS OF SAVINGS, OR LOSS OF                 */
# /* USE OR INTERRUPTION OF BUSINESS. IN NO EVENT WILL TI'S                    */
# /* AGGREGATE LIABILITY UNDER THIS AGREEMENT OR ARISING OUT OF                */
# /* YOUR USE OF THE PROGRAM EXCEED FIVE HUNDRED DOLLARS                       */
# /* (U.S.$500).                                                               */
# /*                                                                           */
# /* Unless otherwise stated, the Program written and copyrighted              */
# /* by Texas Instruments is distributed as "freeware".  You may,              */
# /* only under TI's copyright in the Program, use and modify the              */
# /* Program without any charge or restriction.  You may                       */
# /* distribute to third parties, provided that you transfer a                 */
# /* copy of this license to the third party and the third party               */
# /* agrees to these terms by its first use of the Program. You                */
# /* must reproduce the copyright notice and any other legend of               */
# /* ownership on each copy or partial copy, of the Program.                   */
# /*                                                                           */
# /* You acknowledge and agree that the Program contains                       */
# /* copyrighted material, trade secrets and other TI proprietary              */
# /* information and is protected by copyright laws,                           */
# /* international copyright treaties, and trade secret laws, as               */
# /* well as other intellectual property laws.  To protect TI's                */
# /* rights in the Program, you agree not to decompile, reverse                */
# /* engineer, disassemble or otherwise translate any object code              */
# /* versions of the Program to a human-readable form.  You agree              */
# /* that in no event will you alter, remove or destroy any                    */
# /* copyright notice included in the Program.  TI reserves all                */
# /* rights not specifically granted under this license. Except                */
# /* as specifically provided herein, nothing in this agreement                */
# /* shall be construed as conferring by implication, estoppel,                */
# /* or otherwise, upon you, any license or other right under any              */
# /* TI patents, copyrights or trade secrets.                                  */
# /*                                                                           */
# /* You may not use the Program in non-TI devices.                            */
# /*===========================================================================*/
#use strict qw(subs refs);
#use warnings qw(all);
package PLL;

use FindBin;
use lib "$FindBin::Bin/../lib";
use Tk;
use Tk::Balloon;
use Tk::Checkbutton;
use Tk::FileSelect;
use Tk::Pane;
use Tk::Tiler;
use PinCfg;

###############################################################################
#  $pll_results       = new($num_results)
###############################################################################



sub new {
  my($class)       = shift(@_);
  my($input_freq)  = shift(@_);
  my($cpu_max)     = shift(@_);
  my($emif_max)    = shift(@_);
  my($int_osc)     = shift(@_);
  my($weight)      = shift(@_);
  my($DEBUG)       = shift(@_);
	
  my($this) = {};
  my($num_results) = 100;
  my($i) = 0;
	
	bless($this,$class);

	$this->{'osc'} = $input_freq;
	$this->{'int_osc'} = $int_osc;
	$this->{'weight'} = $weight;
	$this->{"cpu"} = [] ;
	$this->{"emif"} = [];
	$this->{"pllm"} = [];
	$this->{"div0"} = [];
	$this->{"div1"} = [];
	$this->{"div2"} = [];
	$this->{"div3"} = [];
	$this->{"cpu_weighted"} = [];
	$this->{"emif_weighted"} = [];
	$this->{'final'}{'pllm'} = 0;
	$this->{'final'}{'plldiv0'} = 0;
	$this->{'final'}{'plldiv1'} = 0;
	$this->{'final'}{'plldiv2'} = 0;
	$this->{'final'}{'plldiv3'} = 0;
	$this->{'num_results'} = $num_results;
	$this->{'rIndex'} = 0;
	$this->{'maxIndex'} = $num_results-1;

	for($i=0;$i < $num_results ; ++$i) {
	
	    $this->{"cpu"}[$i] = 0 ;
		$this->{"emif"}[$i] = 0;
		$this->{"pllm"}[$i] = 0;
		$this->{"div0"}[$i] = 0;
		$this->{"div1"}[$i] = 0;
		$this->{"div2"}[$i] = 0;
		$this->{"div3"}[$i] = 0;
		$this->{"cpu_weighted"}[$i] = 0;
		$this->{"emif_weighted"}[$i] = 0;
		
	}
		
	PLL->doPllCalc($input_freq, $cpu_max, $emif_max, $int_osc, $weight, $DEBUG, $this);
    return $this;
} 

#######################################################################
# doPllCalc   
#    Calculates best solution for pllM, plldiv0, plldiv1,plldiv2,
#    plldiv3
# Given:
#    int_osc        = internal oscillator frequency
#    input_freqeucy = PLL out frequency desired (int_osc/plldiv0)*pllM
#    cpu_max        = Maximum CPU frqeuency (dependency div1(SYSCLK1))
#    emif_max       = Maximum EMIF frequency(dependacy div3 (SYSCLK3))
#    weight         = Percentage to weigh CPU max freqency vs EMIF max
#                     frequency in determining "best" solution.
########################################################################
sub doPllCalc {
  my($class)       = shift(@_);
  my($input_freq)  = shift(@_);
  my($cpu_max)     = shift(@_);
  my($emif_max)    = shift(@_);
  my($int_osc)     = shift(@_);
  my($weight)      = shift(@_);
  my($DEBUG)       = shift(@_);
  my($this)        = shift(@_);
  	
  my($i,$j,$k,$cnt, $arg_count);
  my($div0,$div1,$div2,$div3,$pllm);

  
  # Oscillator and PLL spec limits 
  my($pllout_min) = 140.0;  #/* Table 35 sprs002f.pdf */
  my($pllout_max) = 600.0;  #/* Table 35 sprs002f.pdf */
  my($pllref_min) = 12.0;   #/* Table 35 sprs002f.pdf */
  my($pllref_max) = 100.0;  #/* Table 35 sprs002f.pdf */
  my($pllm_min) = 4;          #/* Table 37 sprs002f.pdf */
  my($pllm_max) = 25;         #/* Table 37 sprs002f.pdf */
  my($oscin_min) = 12.0;    #/* fosc in sprs002f.pdf */
  my($oscin_max) = 25.0;    #/* fosc in sprs002f.pdf */
  my($div0_max) = 32;         #/* Table 38 sprs002f.pdf */
  my($div0_min) = 1;          #/* Table 38 sprs002f.pdf */
  my($div1_max) = 32;         #/* Table 38 sprs002f.pdf */
  my($div1_min) = 1;          #/* Table 38 sprs002f.pdf */
  my($div2_max) = 32;         #/* Table 38 sprs002f.pdf */
  my($div2_min) = 1;          #/* Table 38 sprs002f.pdf */
  my($div3_max) = 32;         #/* Table 38 sprs002f.pdf */
  my($div3_min) = 1;          #/* Table 38 sprs002f.pdf */

  
 # /* initialize results */
  my($tmp_result) = 0.0;
  my($tmp_eff_pll_mult) = 0.0;
  my($result_cpu) = 0.0;
  my($result_emif) = 0.0;
  my($result_div0) = -1;
  my($result_pllm) = -1;
  my($result_div1) = -1;
  my($result_div2) = -1;
  my($result_div3) = -1;
  my($num_results) = $this->{'num_results'};
  
  my($status) = 0;
  
  #define num_results 100 /* hack to avoid mem alloc, checks in pgm to avoid out of bounds */
  
  
  #/* Initalize structure to illegal values to avoid possibility of false answers     */
  #/* Note: Storage decision presumes that first result found will be greater than -1 */
  for($i=0;$i<$num_results;$i++)
  	{
  	 $this->{'cpu'}[$i]=-1;
  	 $this->{'emif'}[$i]=-1;
  	 $this->{'pllm'}[$i]=-1;
  	 $this->{'div0'}[$i]=-1;
  	 $this->{'div1'}[$i]=-1;
  	 $this->{'div2'}[$i]=-1;
  	 $this->{'div3'}[$i]=-1;
  	 $this->{'cpu_weighted'}[$i]=-1;
  	 $this->{'emif_weighted'}[$i]=-1;
  	}


  #/* debug check to verify system parameters: default + command line override */
  if($DEBUG)
	{
	 printf("var init: cpu_max = %f\n",$cpu_max);
	 printf("var init: emif_max = %f\n",$emif_max);
	 printf("var init: input_freq = %f\n",$input_freq);
	 printf("var init: int_osc = %d\n",$int_osc);
	 printf("var init: weight = %d\n",$weight);
	}

  #/* Check CLKIN or OSCIN value versus PLL input spec */  
  if(($input_freq<$pllref_min) || ($input_freq > $pllref_max)) 
  {
  	 if($int_osc eq 1){ 
	  	 printf("OSCIN input out of PLL range, %f\n",$input_freq);
	 }
  	 else{
	    printf("CLKIN input out of PLL range, %f\n",$input_freq);
     }
  	 $status = -1;
  }

  #/* Additionally, check crystal value versus oscillator spec */
  if ($status == 0) {  
	  if($int_osc)
		{
	  	 if($input_freq<$oscin_min || $input_freq > $oscin_max) 
	  		{
	  		 printf("Crystal input out of range for internal oscillator, %f\n",$input_freq);
	  		 $status = -1;
	  		}
		}
  }

  if ($status == 0) {
	  #/* Set "spec" DIV0 max by PLLREF min limit */
	  #/* Ensure max is <= to spec */
	  $div0_max = min(floor($input_freq/$pllref_min),$div0_max);
	
	  #/* Set "spec" PLLM min by VCO min limit */  
	  #/* Ensure min is >= to spec */
	  $pllm_min = max(ceil($pllout_min/$input_freq),$pllm_min);
	
	  #/* Set "spec" PLLM max by VCO max limit */  
	  #/* Ensure max is <= to spec */
	  $pllm_max = min(floor($pllout_max/$input_freq*$div0_max),$pllm_max);
	
	  #/* Set "practical" DIV1 max by VCO and CPU max limit */
	  #/* Use conservative 50% of CPU max to set the search limit */
	  #/* Ensure max is <= to spec */
	  $div1_max = min(ceil($pllout_max/($cpu_max/2)),$div1_max);
	
	  #/* Set "practical" DIV3 max by VCO and EMIF max limit */
	  #/* Use conservative 50% of EMIF max to set the search limit */
	  #/* Ensure max is <= to spec */
	  $div3_max = min(ceil($pllout_max/($emif_max/2)),$div3_max);
  }

#   /* Basic Theory of the algorithm                                                 */
#   /*                                                                               */
#   /* As a general aide see fig 15 of sprs002f.pdf                                  */
#   /*                                                                               */
#   /* Key principles:                                                               */
#   /*   - input clock / div0 * pllm sets the pllout frequency                       */
#   /*   - The CPU, Peripherals, and EMIF then have their own dividers               */
#   /*   - CPU clock is set by div1                                                  */
#   /*   - Peripheral clock is set by div2, but it must always be 2xdiv1             */
#   /*   - EMIF clock is set by div3                                                 */
#   /*                                                                               */
#   /* Starting with the lowest frequency (div0_max, pllm_min, div1_max)             */
#   /* increment the frequency (dec div1) to the point just before it fails the spec */
#   /* The cpu result and div1 value are saved. Then do the same for div3            */
#   /* and store the emif and div3 results. There is one result stored for each      */
#   /* inner loop and this is indexed by cnt. After finding one result, the          */
#   /* outer loops of pllm and div0 are incremented and decremented respectively     */
#   /* to the end limits.                                                            */
#   /*                                                                               */
#   /* For each inner loop result, a figure of merit is calculated to determine      */
#   /* how far away from the spec the result is. One result is calculated            */
#   /* with a bias or weight to the cpu and one result is calculated with a bias     */
#   /* or weight to the emif. The bias here meaning to favor a result where the cpu  */
#   /* is close to the spec, but may not be the closest result of all to allow a     */
#   /* good result for the EMIF. The weight parameter is adjustable via command line */
#   

 if ($status == 0) {
	  $cnt=0;  #/* index through results structure */
	
	  for($div0=$div0_max;$div0>=$div0_min;$div0--)
		{
		 for($pllm=$pllm_min;$pllm<=$pllm_max;$pllm++)
			 {
			  for($div1=$div1_max;$div1>=$div1_min;$div1--)
			  	{
	                   #/* First time, check that pllout_max doesn't exceed limit */
			  	 $tmp_result=($input_freq/$div0)*$pllm;
			  	 last if($tmp_result>$pllout_max);
	
	                   #/* Second time, ensure that with DIV1, CPU doesn't exceed limit */
			  	 $tmp_result=(($input_freq/$div0)*$pllm)/$div1;
			  	 printf("result=%f div0=%d pllm=%d div1=%d\n",
			  	                        $tmp_result,$div0,$pllm,$div1) if ($DEBUG);
			  	 last if($tmp_result>$cpu_max);
			  	 if($tmp_result>$this->{'cpu'}[$cnt])
			  	 	{
			  	 	 $this->{'cpu'}[$cnt] =$tmp_result;
					 $this->{'pllm'}[$cnt]=$pllm;
			  	 	 $this->{'div0'}[$cnt]=$div0;
					 $this->{'div1'}[$cnt]=$div1;
					 $this->{'div2'}[$cnt]=$div1*2;
					}
			  	} #/* end of div1 */
			  	
			  for($div3=$div3_max;$div3>=$div3_min;$div3--)
			 	{
			  	 $tmp_result=(($input_freq/$this->{'div0'}[$cnt])*$this->{'pllm'}[$cnt])/$div3;
                 printf("result=%f, div3=%d\n",$tmp_result,$div3) if ($DEBUG);
			  	 last if($tmp_result>$emif_max);
			  	 if($tmp_result>$this->{'emif'}[$cnt])
			  	 	{
			  	 	 $this->{'emif'}[$cnt]=$tmp_result;
			  	 	 $this->{'div3'}[$cnt]=$div3;
			  	 	}
		  	 	 } #/* end of div3 */
	
			  $this->{'cpu_weighted'}[$cnt]=(($weight*$this->{'cpu'}[$cnt]/$cpu_max) + ($this->{'emif'}[$cnt]/$emif_max));
			  $this->{'emif_weighted'}[$cnt]=(($this->{'cpu'}[$cnt]/$cpu_max) + ($weight*$this->{'emif'}[$cnt]/$emif_max));
	
			  $cnt++;

			  if($cnt>=$num_results)
			  	{
			  	 printf("run time error, number of results > array %d > %d \n", $cnt, $num_results);
			  	 $status = -1;
			  	}	
			 } #/* end of pllm */
		} #/* end of div0 */
     }
     
#   /* This printf section shows all the results generated and their figure of merit. */
#   /* Very useful to determine if the bias or weight applied is driving to the       */
#   /* best result.                                                                   */
  
if ($status == 0) {
	  if($DEBUG) 
		{
		 printf("********************************\n\n");
		 for($i=0;$i<$cnt;$i++)
			{
			 printf("Best Result is CPU = %5.2f and EMIF = %5.2f\n",$this->{'cpu'}[$i],
			              $this->{'emif'}[$i]);
			 printf("DIV0=%d PLLM=%d DIV1=%d DIV2=%d DIV3=%d\n",$this->{'div0'}[$i],
			              $this->{'pllm'}[$i],$this->{'div1'}[$i],$this->{'div2'}[$i],$this->{'div3'}[$i]);
			 printf("cpu_ratio=%f emif_ratio=%f ",($this->{'cpu'}[$i]/$cpu_max),
			              ($this->{'emif'}[$i]/$emif_max));
			 printf("cpu_weight=%f emif_weight=%f\n",$THIS->{'cpu_weighted'}[$i],
			              $this->{'emif_weighted'}[$i]);
			 printf("\n***\n");
			}
		 printf("\n********************************\n");
		}
	
	#   /* print the winning cpu biased or weighted result along with the input       */
	#   /* parameters and resulting pll settings. The results are shown as they       */
	#   /* are in register values and how they are used symbolicaly in the DA6xx GELs */
	  printf("\n*****  Best Result CPU Weighted   *****\n") if ($DEBUG);
	  $tmp_result=0;
	  $tmp_eff_pll_mult=0;
	  $j=0;
	  for($i=0;$i<$cnt;$i++)
	     {
	      if($this->{'cpu_weighted'}[$i]>(0.99*$tmp_result))   #/* account for rounding */
	         {
	          #/* if there is a tie, take the result with highes pllout value */
	          if ( ($this->{'pllm'}[$i]/$this->{'div0'}[$i]) > $tmp_eff_pll_mult )
	             {
	              $tmp_result=$this->{'cpu_weighted'}[$i];
	              $tmp_eff_pll_mult=($this->{'pllm'}[$i]/$this->{'div0'}[$i]);
	              $j=$i;
	             }
	         }
	     }
	     
	  if($DEBUG) {   
		  printf("For Inputs CPU=%5.2fmhz, EMIF=%5.2fMhz, Input=%6.3fMHz, and IntOsc=%d\n",
		               $cpu_max,$emif_max,$input_freq,$int_osc); 
		  printf("Best CPU Weighted Result is CPU=%5.2fMHz and EMIF=%5.2fMHz\n",
		               $this->{'cpu'}[$j],$this->{'emif'}[$j]);
		  printf("cpu_weight=%f emif_weight=%f\n",$this->{'cpu_weighted'}[$j],
		                          $this->{'emif_weighted'}[$j]) if ($DEBUG);
		  printf("Using PLL Settings:\n");
		  printf("PLLM    = 0x%04X  or  TIMES%d\n",$this->{'pllm'}[$j],$this->{'pllm'}[$j]);
		  printf("PLLDIV0 = 0x%04X  or  DIV%d\n",$this->{'div0'}[$j]-1,$this->{'div0'}[$j]);
		  printf("PLLDIV1 = 0x%04X  or  DIV%d\n",$this->{'div1'}[$j]-1,$this->{'div1'}[$j]);
		  printf("PLLDIV2 = 0x%04X  or  DIV%d\n",$this->{'div2'}[$j]-1,$this->{'div2'}[$j]);
		  printf("PLLDIV3 = 0x%04X  or  DIV%d\n",$this->{'div3'}[$j]-1,$this->{'div3'}[$j]);
	  }
  }

#   /* print the winning emif biased or weighted result along with the input       */
#   /* parameters and resulting pll settings. The results are shown as they       */
#   /* are in register values and how they are used symbolicaly in the DA6xx GELs */

  if ($status == 0) {
	  printf("\n*****  Best Results EMIF Weighted  *****\n") if ($DEBUG);
	  $tmp_result=0;
	  $tmp_eff_pll_mult=0;
	  $j=0;
	  for($i=0;$i<$cnt;$i++)
	     {
	      if($this->{'emif_weighted'}[$i]>(0.99*$tmp_result)) #/* account for rounding */
	         {
	          #/* if there is a tie, take the result with highes pllout value */
	          if ( ($this->{'pllm'}[$i]/$this->{'div0'}[$i]) > $tmp_eff_pll_mult )
	             {
	              $tmp_result=$this->{'emif_weighted'}[$i];
	              $tmp_eff_pll_mult=($this->{'pllm'}[$i]/$this->{'div0'}[$i]);
	              $j=$i;
	             }
	         }
	     }
	  if($DEBUG) {    
		  printf("For Inputs CPU=%5.2fmhz, EMIF=%5.2fMhz, Input=%6.3fMHz, and IntOsc=%d\n",$cpu_max,$emif_max,
		               $input_freq,$int_osc); 
		  printf("Best EMIF Weighted Result is CPU=%5.2fMHz and EMIF=%5.2fMHz\n",$this->{'cpu'}[$j],
		               $this->{'emif'}[$j]);
		  printf("cpu_weight=%f emif_weight=%f\n",
		       $this->{'cpu_weighted'}[$j],$this->{'emif_weighted'}[$j]) if ($DEBUG);
		  printf("Using PLL Settings:\n");
		  printf("PLLM    = 0x%04X  or  TIMES%d\n",$this->{'pllm'}[$j],$this->{'pllm'}[$j]);
		  printf("PLLDIV0 = 0x%04X  or  DIV%d\n",$this->{'div0'}[$j]-1,$this->{'div0'}[$j]);
		  printf("PLLDIV1 = 0x%04X  or  DIV%d\n",$this->{'div1'}[$j]-1,$this->{'div1'}[$j]);
		  printf("PLLDIV2 = 0x%04X  or  DIV%d\n",$this->{'div2'}[$j]-1,$this->{'div2'}[$j]);
		  printf("PLLDIV3 = 0x%04X  or  DIV%d\n",$this->{'div3'}[$j]-1,$this->{'div3'}[$j]);
		
		  printf("\n**********  End  ***********************\n");
	  }
  }
  
  $this->{'rIndex'} = $j;
  $this->{'maxIndex'} = $cnt-1;
  $this->{'final'}{'pllm'} = $this->{'pllm'}[$j];
  $this->{'final'}{'plldiv0'} = $this->{'div0'}[$j]-1;
  $this->{'final'}{'plldiv1'} = $this->{'div1'}[$j]-1;
  $this->{'final'}{'plldiv2'} = $this->{'div2'}[$j]-1;
  $this->{'final'}{'plldiv3'} = $this->{'div3'}[$j]-1;
  
  return $status;
 
}



#----------------------------------------------------------------------------------------
sub max {
   my($a) = shift(@_);
   my($b) = shift(@_);
   my($c) = 0;
   
   $c = ($a > $b) ? $a : $b;
   
   return $c;
}


#----------------------------------------------------------------------------------------
sub min {
   my($a) = shift(@_);
   my($b) = shift(@_);
   my($c) = 0;
   
   $c = ($a < $b) ? $a : $b;
   
   return $c;
}


#----------------------------------------------------------------------------------------
sub ceil {
   my($x) = shift(@_);
   my($c) = 0;
   
   $c = int($x);
   ++$c if ($c < $x);
   
   return $c;
}

#----------------------------------------------------------------------------------------
sub floor {
   my($x) = shift(@_);
   my($c) = 0;
   
   $c = int($x);
   --$c if ($c > $x);
   
   return $c;
}
   
#----------------------------------------------------------------------------------------
sub delete {
  undef $_[1];
}

###############################################################################

return 1;  #the package needs to return a 1 to denote success

###############################################################################
# end of PLL.pm
###############################################################################
