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 ID_SLAVE "Slave" 23const ID_MASTER "Master" 24 25const ARG_IDX_MODE 0 26const ARG_IDX_DATA_RATE_HZ 1 27const ARG_IDX_SCB_CLK_HZ 2 28 29const LOW_OVERSAMPLE "lowOversample" 30const HIGH_OVERSAMPLE "highOversample" 31const DIGITAL_FILTER "digitalFilter" 32 33const DIV_MHZ 1000000 34const DIV_KHZ 1000 35 36# Supported data rates ranges 37const I2C_DATA_RATE_STD 100000 38const I2C_DATA_RATE_FST 400000 39const I2C_DATA_RATE_FSTP 1000000 40 41# Master clock (Hz) and duty cycle limits for standard mode 42const I2C_MASTER_STD_CLK_MIN_MHZ 1.55 43const I2C_MASTER_STD_CLK_MAX_MHZ 3.2 44const I2C_MASTER_STD_CLK_MIN_HZ 1550000 45const I2C_MASTER_STD_CLK_MAX_HZ 3200000 46const I2C_MASTER_STD_SCL_LOW 5000 ;# tLOW + tF 4700 + 300 47const I2C_MASTER_STD_SCL_HIGH 5000 ;# tHIGH + tR 4000 + 1000 48 49# Master clock (Hz) and duty cycle limits for fast mode 50const I2C_MASTER_FST_CLK_MIN_MHZ 7.82 51const I2C_MASTER_FST_CLK_MAX_MHZ 10.0 52const I2C_MASTER_FST_CLK_MIN_HZ 7820000 53const I2C_MASTER_FST_CLK_MAX_HZ 10000000 54const I2C_MASTER_FST_SCL_LOW 1600 ;# tLOW + tF 1300 + 300 55const I2C_MASTER_FST_SCL_HIGH 900 ;# tHIGH + tR 600 + 300 56 57# Master clock (Hz) and duty cycle limits for fast plus mode 58const I2C_MASTER_FSTP_CLK_MIN_MHZ 14.32 59const I2C_MASTER_FSTP_CLK_MAX_MHZ 25.8 60const I2C_MASTER_FSTP_CLK_MIN_HZ 14320000 61const I2C_MASTER_FSTP_CLK_MAX_HZ 25800000 62const I2C_MASTER_FSTP_SCL_LOW 620 ;# tLOW + tF 500 + 120 63const I2C_MASTER_FSTP_SCL_HIGH 380 ;# tHIGH + tR 260 + 120 64 65const I2C_LOW_PHASE_MAX 16 66const I2C_HIGH_PHASE_MAX 16 67const I2C_DUTY_CYCLE_MAX 32 68 69const SUCCESS 0 70const ERROR_ARG_COUNT 1 71const ERROR_ID 2 72const ERROR_ARG_VALUE 3 73const ERROR_I2C_FREQ_RANGE 4 74const ERROR_DATA_RATE_RANGE 5 75 76set channelName stdout 77 78if {[chan names ModusToolbox] eq "ModusToolbox"} { 79 set channelName ModusToolbox 80} 81 82proc solve_i2c {} { 83 if {$::argc != $::ARG_IDX_SCB_CLK_HZ + 1} { 84 error "I2C data rate solver requires 3 arguments: 85\tstring id - Master or Slave. 86\tinteger dataRateHz - Desired I2C data rate in Hz. 87\tinteger scbClockHz - SCB source clock frequency in Hz." 88 return $::ERROR_ARG_COUNT 89 } 90 set mode [lindex $::argv $::ARG_IDX_MODE] 91 if {[string compare -nocase $mode $::ID_SLAVE] == 0} { 92 return [output_slave_values] 93 } 94 if {[string compare -nocase $mode $::ID_MASTER] != 0} { 95 error "Unhandled solver: $mode" 96 return $::ERROR_ID 97 } 98 set dataRateHz [lindex $::argv $::ARG_IDX_DATA_RATE_HZ] 99 set scbClockHz [lindex $::argv $::ARG_IDX_SCB_CLK_HZ] 100 if {![string is integer $dataRateHz] || ![string is integer $scbClockHz]} { 101 error "Unable to parse argument values: either $dataRateHz or $scbClockHz is not an integer." 102 return $::ERROR_ARG_VALUE 103 } 104 return [master_set_data_rate $dataRateHz $scbClockHz] 105} 106 107proc output_results {lowPhase highPhase enableMedian} { 108 set digitalFilterStr [expr {$enableMedian ? "true" : "false"}] 109 puts $::channelName "param:$::LOW_OVERSAMPLE=$lowPhase" 110 puts $::channelName "param:$::HIGH_OVERSAMPLE=$highPhase" 111 puts $::channelName "param:$::DIGITAL_FILTER=$digitalFilterStr" 112} 113 114proc master_set_data_rate {dataRateHz scbClockHz} { 115 set sclLow 0 116 set sclHigh 0 117 118 # Calculated values 119 set lowPhase 0 120 set highPhase 0 121 set enableMedian false 122 123 # Get duration of SCL low and high for the selected data rate 124 if {$dataRateHz == 0} { 125 return $::ERROR_DATA_RATE_RANGE 126 } 127 if {$dataRateHz <= $::I2C_DATA_RATE_STD} { 128 if {$scbClockHz < $::I2C_MASTER_STD_CLK_MIN_HZ || $scbClockHz > $::I2C_MASTER_STD_CLK_MAX_HZ} { 129 output_results $lowPhase $highPhase $enableMedian 130 return $::ERROR_I2C_FREQ_RANGE 131 } 132 set sclLow $::I2C_MASTER_STD_SCL_LOW 133 set sclHigh $::I2C_MASTER_STD_SCL_HIGH 134 } elseif {$dataRateHz <= $::I2C_DATA_RATE_FST} { 135 if {$scbClockHz < $::I2C_MASTER_FST_CLK_MIN_HZ || $scbClockHz > $::I2C_MASTER_FST_CLK_MAX_HZ} { 136 output_results $lowPhase $highPhase $enableMedian 137 return $::ERROR_I2C_FREQ_RANGE 138 } 139 set sclLow $::I2C_MASTER_FST_SCL_LOW 140 set sclHigh $::I2C_MASTER_FST_SCL_HIGH 141 } elseif {$dataRateHz <= $::I2C_DATA_RATE_FSTP} { 142 if {$scbClockHz < $::I2C_MASTER_FSTP_CLK_MIN_HZ || $scbClockHz > $::I2C_MASTER_FSTP_CLK_MAX_HZ} { 143 output_results $lowPhase $highPhase $enableMedian 144 return $::ERROR_I2C_FREQ_RANGE 145 } 146 set sclLow $::I2C_MASTER_FSTP_SCL_LOW 147 set sclHigh $::I2C_MASTER_FSTP_SCL_HIGH 148 set enableMedian true 149 } else { 150 output_results $lowPhase $highPhase $enableMedian 151 return $::ERROR_I2C_FREQ_RANGE 152 } 153 set updateLowPhase false 154 set actualDataRateHz 0 155 156 # Get period of the SCB clock in ns 157 set period [expr {1000000000 / $scbClockHz}] 158 159 # Get low phase minimum value in SCB clocks 160 set lowPhase [expr {$sclLow / $period}] 161 162 if {($period * $lowPhase) < $sclLow} { 163 incr lowPhase 164 } 165 if {$lowPhase > $::I2C_LOW_PHASE_MAX} { 166 set lowPhase $::I2C_LOW_PHASE_MAX 167 } 168 169 # Define if updateLowPhase low phase 170 set updateLowPhase [expr {$lowPhase < $::I2C_LOW_PHASE_MAX}] 171 172 # Get high phase minimum value in SCB clocks 173 set highPhase [expr {$sclHigh / $period}] 174 175 if {($period * $highPhase) < $sclHigh} { 176 incr highPhase 177 } 178 if {$highPhase > $::I2C_HIGH_PHASE_MAX} { 179 set highPhase $::I2C_HIGH_PHASE_MAX 180 } 181 182 # Get actual data rate 183 set actualDataRateHz [expr {$scbClockHz / ($lowPhase + $highPhase)}] 184 185 # Find desired data rate 186 while {($actualDataRateHz > $dataRateHz) && (($lowPhase + $highPhase) < $::I2C_DUTY_CYCLE_MAX)} { 187 # Increase low and high phase to reach desired data rate 188 if {$updateLowPhase} { 189 if {$lowPhase < $::I2C_LOW_PHASE_MAX} { 190 # Update low phase 191 incr lowPhase 192 set updateLowPhase false 193 } 194 } elseif {$highPhase < $::I2C_HIGH_PHASE_MAX} { 195 # Update high phase 196 incr highPhase 197 set updateLowPhase [expr {$lowPhase < $::I2C_LOW_PHASE_MAX}] 198 } 199 200 # Update actual data rate 201 set actualDataRateHz [expr {$scbClockHz / ($lowPhase + $highPhase)}] 202 } 203 204 output_results $lowPhase $highPhase $enableMedian 205 return $::SUCCESS 206} 207 208proc output_slave_values {} { 209 output_results 0 0 false 210 return $::SUCCESS 211} 212 213solve_i2c 214