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" 30const FRAC_DIV_KEY "fracDiv" 31 32const MIN_IN_FREQ 4 33const MAX_IN_FREQ 64 34const FVCO_MIN 400 35const FVCO_MAX 800 36 37# PLL OUTPUT_DIV bitfield allowable range 38const OUT_MIN 2 39const OUT_MAX 16 40 41# PLL REFERENCE_DIV bitfield allowable range 42const REF_MIN 1 43const REF_MAX 16 44 45# PLL FEEDBACK_DIV bitfield allowable ranges 46const FB_MIN 16 47const FB_MAX 200 48 49const SUCCESS 0 50const ERROR_ARG_COUNT 1 51const ERROR_ID 2 52const ERROR_ARG_VALUE 3 53 54set channelName stdout 55 56if {[chan names ModusToolbox] eq "ModusToolbox"} { 57 set channelName ModusToolbox 58} 59 60proc solve_pll {} { 61 if {$::argc != $::ARG_IDX_MIN_POWER + 1} { 62 error "PLL Solver requires 4 arguments: 63\tdouble srcFreqMHz - Source clock frequency for the PLL in MHz. 64\tdouble targetFreqMHz - Output frequency of the PLL in MHz. 65\tboolean lfMode - CLK_PLL_CONFIG register, PLL_LF_MODE bit. 66\tboolean minPower - Optimize for min power rather min jitter." 67 return $::ERROR_ARG_COUNT 68 } 69 set srcFreqMHz [lindex $::argv $::ARG_IDX_SRC_FREQ] 70 set targetFreqMHz [lindex $::argv $::ARG_IDX_TARGET_FREQ] 71 set lfMode [string is true [lindex $::argv $::ARG_IDX_LF_MODE]] 72 set minPower [string is true [lindex $::argv $::ARG_IDX_MIN_POWER]] 73 if {![string is double $srcFreqMHz] || ![string is double $targetFreqMHz]} { 74 error "Unable to parse argument values: either $srcFreqMHz or $targetFreqMHz is not a floating-point number." 75 return $::ERROR_ARG_VALUE 76 } 77 set srcFreqMHz [expr {double($srcFreqMHz)}] 78 set targetFreqMHz [expr {double($targetFreqMHz)}] 79 if {$srcFreqMHz < $::MIN_IN_FREQ || $::MAX_IN_FREQ < $srcFreqMHz} { 80 error "Invalid PLL input frequency '$srcFreqMHz'. Must be within the range $::MIN_IN_FREQ-$::MAX_IN_FREQ." 81 return $::ERROR_ARG_VALUE 82 } 83 return [solve_pll_internal $srcFreqMHz $targetFreqMHz $lfMode $minPower] 84} 85 86proc solve_pll_internal {srcFreqMHz targetFreqMHz lfMode minPower} { 87 set divFb $::FB_MIN 88 set divRef $::REF_MIN 89 set divOut $::OUT_MIN 90 set fracDiv 0 91 set foutBest 0 92 93 for {set q $::REF_MIN} {$q <= $::REF_MAX} {incr q} { 94 for {set p $::FB_MIN} {$p <= $::FB_MAX} {incr p} { 95 set fvco [expr {($srcFreqMHz * $p)/ $q}] 96 97 if {![is_pfdref_in_range $fvco]} { 98 continue 99 } 100 for {set o $::OUT_MIN} {$o <= $::OUT_MAX} {incr o} { 101 set tempVco [expr { $targetFreqMHz * $o}] 102 set tempFeedBackDivLeftShifted [expr { ((($tempVco) * pow (2, 24)) * $q) / $srcFreqMHz }] 103 set feedBackFracDiv [expr { (int($tempFeedBackDivLeftShifted) & (( 1 << 24 ) - 1)) }] 104 set fout [expr {($srcFreqMHz * ( ($p << 24) + $feedBackFracDiv) / ($q * $o)) / pow (2, 24)}] 105 if {int($foutBest) == int($targetFreqMHz)} { 106 break; 107 } 108 set foutBest $fout 109 set divFb $p 110 set divRef $q 111 set divOut $o 112 set fracDiv $feedBackFracDiv 113 } 114 } 115 } 116 puts $::channelName "param:$::FEEDBACK_DIV_KEY=$divFb" 117 puts $::channelName "param:$::REFERENCE_DIV_KEY=$divRef" 118 puts $::channelName "param:$::OUTPUT_DIV_KEY=$divOut" 119 puts $::channelName "param:$::FRAC_DIV_KEY=$fracDiv" 120 return $::SUCCESS 121} 122 123proc is_pfdref_in_range {fvco} { 124 return [expr {$::FVCO_MIN <= $fvco && $fvco <= $::FVCO_MAX}] 125} 126 127solve_pll 128