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