1# Copyright 2020 Cypress Semiconductor Corporation 2# SPDX-License-Identifier: Apache-2.0 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16# From https://wiki.tcl-lang.org/page/constants 17proc const {name value} { 18 uplevel 1 [list set $name $value] 19 uplevel 1 [list trace var $name w {error constant;} ] 20} 21 22const ARG_IDX_SRC_FREQ 0 23const ARG_IDX_TARGET_FREQ 1 24const ARG_IDX_LF_MODE 2 25const ARG_IDX_MIN_POWER 3 26 27const FEEDBACK_DIV_KEY "feedbackDiv" 28const REFERENCE_DIV_KEY "referenceDiv" 29const OUTPUT_DIV_KEY "outputDiv" 30 31const MIN_IN_FREQ 4 32const MAX_IN_FREQ 64 33const PFD_MIN 4 34const PFD_MAX 8 35 36# PLL OUTPUT_DIV bitfield allowable range 37const OUT_MIN 2 38const OUT_MAX 16 39 40# PLL REFERENCE_DIV bitfield allowable range 41const REF_MIN 1 42const REF_MAX 18 43 44# PLL FEEDBACK_DIV bitfield allowable ranges, LF and normal modes 45const FB_MAX_LF 56 46const FB_MIN 22 47const FB_MAX_NORM 112 48 49# PLL Fvco range allowable ranges, LF and normal modes 50const VCO_MIN_LF 170 51const VCO_MAX_LF 200 52const VCO_MIN_NORM 200 53const VCO_MAX_NORM 400 54 55const DOUBLE_COMPARE_EPSILON 0.00001 56const MAX_DOUBLE 1.0e308 57 58const SUCCESS 0 59const ERROR_ARG_COUNT 1 60const ERROR_ID 2 61const ERROR_ARG_VALUE 3 62 63set channelName stdout 64 65if {[chan names ModusToolbox] eq "ModusToolbox"} { 66 set channelName ModusToolbox 67} 68 69proc solve_pll {} { 70 if {$::argc != $::ARG_IDX_MIN_POWER + 1} { 71 error "PLL Solver requires 4 arguments: 72\tdouble srcFreqMHz - Source clock frequency for the PLL in MHz. 73\tdouble targetFreqMHz - Output frequency of the PLL in MHz. 74\tboolean lfMode - CLK_PLL_CONFIG register, PLL_LF_MODE bit. 75\tboolean minPower - Optimize for min power rather min jitter." 76 return $::ERROR_ARG_COUNT 77 } 78 set srcFreqMHz [lindex $::argv $::ARG_IDX_SRC_FREQ] 79 set targetFreqMHz [lindex $::argv $::ARG_IDX_TARGET_FREQ] 80 set lfMode [string is true [lindex $::argv $::ARG_IDX_LF_MODE]] 81 set minPower [string is true [lindex $::argv $::ARG_IDX_MIN_POWER]] 82 if {![string is double $srcFreqMHz] || ![string is double $targetFreqMHz]} { 83 error "Unable to parse argument values: either $srcFreqMHz or $targetFreqMHz is not a floating-point number." 84 return $::ERROR_ARG_VALUE 85 } 86 set srcFreqMHz [expr {double($srcFreqMHz)}] 87 set targetFreqMHz [expr {double($targetFreqMHz)}] 88 if {$srcFreqMHz < $::MIN_IN_FREQ || $::MAX_IN_FREQ < $srcFreqMHz} { 89 error "Invalid PLL input frequency '$srcFreqMHz'. Must be within the range $::MIN_IN_FREQ-$::MAX_IN_FREQ." 90 return $::ERROR_ARG_VALUE 91 } 92 return [solve_pll_internal $srcFreqMHz $targetFreqMHz $lfMode $minPower] 93} 94 95proc solve_pll_internal {srcFreqMHz targetFreqMHz lfMode minPower} { 96 set divFb $::FB_MIN 97 set divRef $::REF_MIN 98 set divOut $::OUT_MIN 99 100 set freqRatio [expr {$srcFreqMHz / $targetFreqMHz}] 101 102 set leastErrorFound $::MAX_DOUBLE 103 set fbMax [expr {$lfMode ? $::FB_MAX_LF : $::FB_MAX_NORM}] 104 for {set p $::FB_MIN} {$p <= $fbMax} {incr p} { 105 for {set q $::REF_MIN} {$q <= $::REF_MAX} {incr q} { 106 set pfdRefFreq [expr {$srcFreqMHz / $q}] 107 set vcoFreq [expr {$pfdRefFreq * $p}] 108 109 if {![is_pfdref_in_range $pfdRefFreq] || ![is_vco_in_range $vcoFreq $lfMode]} { 110 continue 111 } 112 for {set o $::OUT_MIN} {$o <= $::OUT_MAX} {incr o} { 113 set error [expr {abs(1 - (($freqRatio * $p) / ($q * $o)))}] 114 # If quality is equal, then minPower means minimize divOut, and not minPower means max divOut 115 if {$error < $leastErrorFound || 116 ([double_equals $error $leastErrorFound] && 117 (($divOut > $o) == $minPower))} { 118 set leastErrorFound $error 119 set divFb $p 120 set divRef $q 121 set divOut $o 122 } 123 } 124 } 125 } 126 if {$leastErrorFound == $::MAX_DOUBLE} { 127 return $::ERROR_ID 128 } 129 puts $::channelName "param:$::FEEDBACK_DIV_KEY=$divFb" 130 puts $::channelName "param:$::REFERENCE_DIV_KEY=$divRef" 131 puts $::channelName "param:$::OUTPUT_DIV_KEY=$divOut" 132 return $::SUCCESS 133} 134 135proc is_vco_in_range {vcoFreq lfMode} { 136 return [expr {$lfMode ? ($::VCO_MIN_LF <= $vcoFreq && $vcoFreq <= $::VCO_MAX_LF) 137 : ($::VCO_MIN_NORM <= $vcoFreq && $vcoFreq <= $::VCO_MAX_NORM)}] 138} 139 140proc is_pfdref_in_range {pfdRefFrequency} { 141 return [expr {$::PFD_MIN <= $pfdRefFrequency && $pfdRefFrequency <= $::PFD_MAX}] 142} 143 144proc double_equals {a b} { 145 return [expr {abs($a - $b) < $::DOUBLE_COMPARE_EPSILON}] 146} 147 148solve_pll 149