@@ -4,6 +4,9 @@ package machine
4
4
5
5
import (
6
6
"device/rp"
7
+ "errors"
8
+ "math"
9
+ "math/bits"
7
10
"runtime/volatile"
8
11
"unsafe"
9
12
)
@@ -29,12 +32,11 @@ var (
29
32
// Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2.
30
33
//
31
34
// Post Divider 2, postDiv2 with range 1-7.
32
- func (pll * pll ) init (refdiv , vcoFreq , postDiv1 , postDiv2 uint32 ) {
35
+ func (pll * pll ) init (refdiv , fbdiv , postDiv1 , postDiv2 uint32 ) {
33
36
refFreq := xoscFreq / refdiv
34
37
35
38
// What are we multiplying the reference clock by to get the vco freq
36
39
// (The regs are called div, because you divide the vco output and compare it to the refclk)
37
- fbdiv := vcoFreq / (refFreq * MHz )
38
40
39
41
// Check fbdiv range
40
42
if ! (fbdiv >= 16 && fbdiv <= 320 ) {
@@ -54,13 +56,14 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
54
56
panic ("postdiv1 should be greater than or equal to postdiv2" )
55
57
}
56
58
57
- // Check that reference frequency is no greater than vco / 16
59
+ // Check that reference frequency is no greater than vcoFreq / 16
60
+ vcoFreq := calcVCO (xoscFreq , fbdiv , refdiv )
58
61
if refFreq > vcoFreq / 16 {
59
62
panic ("reference frequency should not be greater than vco frequency divided by 16" )
60
63
}
61
64
62
65
// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
63
- pdiv := postDiv1 << rp .PLL_SYS_PRIM_POSTDIV1_Pos | postDiv2 << rp .PLL_SYS_PRIM_POSTDIV2_Pos
66
+ pdiv := uint32 ( postDiv1 ) << rp .PLL_SYS_PRIM_POSTDIV1_Pos | uint32 ( postDiv2 ) << rp .PLL_SYS_PRIM_POSTDIV2_Pos
64
67
65
68
if pll .cs .HasBits (rp .PLL_SYS_CS_LOCK ) &&
66
69
refdiv == pll .cs .Get ()& rp .PLL_SYS_CS_REFDIV_Msk &&
@@ -98,3 +101,179 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
98
101
pll .pwr .ClearBits (rp .PLL_SYS_PWR_POSTDIVPD )
99
102
100
103
}
104
+
105
+ var errVCOOverflow = errors .New ("VCO calculation overflow; use lower MHz" )
106
+
107
+ // pllSearch enables searching for a good PLL configuration.
108
+ // Example for 12MHz crystal and RP2040:
109
+ //
110
+ // fbdiv, refdiv, pd1, pd2, _ := pllSearch{LockRefDiv:1}.CalcDivs(12*MHz, 125*MHz, MHz)
111
+ //
112
+ // Example for 12MHz crystal and RP2350:
113
+ //
114
+ // fbdiv, refdiv, pd1, pd2, _ := pllSearch{LockRefDiv:1}.CalcDivs(12*MHz, 133*MHz, MHz)
115
+ type pllSearch struct {
116
+ LowerVCO bool
117
+ LockRefDiv uint8
118
+ }
119
+
120
+ func (ps pllSearch ) CalcDivs (xoscRef , targetFreq , MHz uint64 ) (fbdiv uint64 , refdiv , pd1 , pd2 uint8 , err error ) {
121
+ genTable ()
122
+ var bestFreq , bestFbdiv uint64
123
+ var bestRefdiv , bestpd1 , bestpd2 uint8
124
+ maxVCO , minVCO := 1600 * MHz , 750 * MHz
125
+ var bestMargin int64 = int64 (maxVCO )
126
+ iters := 0
127
+ for refdiv = 1 ; refdiv < 64 ; refdiv ++ {
128
+ if ps .LockRefDiv != 0 && refdiv != ps .LockRefDiv {
129
+ continue
130
+ }
131
+ firstFBDiv := minVCO * uint64 (refdiv ) / xoscRef
132
+ for fbdiv = firstFBDiv ; fbdiv < 321 ; fbdiv ++ {
133
+ overflow , vco := bits .Mul64 (xoscRef , fbdiv )
134
+ vco /= uint64 (refdiv )
135
+ if overflow != 0 {
136
+ return fbdiv , refdiv , pd1 , pd2 , errVCOOverflow
137
+ } else if vco > maxVCO {
138
+ break
139
+ }
140
+ calcPD12 := vco / targetFreq
141
+ if calcPD12 < 1 {
142
+ calcPD12 = 1
143
+ } else if calcPD12 > 49 {
144
+ calcPD12 = 49
145
+ }
146
+ iters ++
147
+ pd1 = pdTable [calcPD12 ].hivco [0 ]
148
+ pd2 = pdTable [calcPD12 ].hivco [1 ]
149
+ fout , err := pllFreqOutPostdiv (xoscRef , fbdiv , MHz , refdiv , pd1 , pd2 )
150
+ found := false
151
+ margin := abs (int64 (fout ) - int64 (targetFreq ))
152
+ if err == nil && margin <= bestMargin {
153
+ found = true
154
+ bestFreq = fout
155
+ bestFbdiv = fbdiv
156
+ bestpd1 = pd1
157
+ bestpd2 = pd2
158
+ bestRefdiv = refdiv
159
+ bestMargin = margin
160
+ }
161
+ pd1 = pdTable [calcPD12 ].lovco [0 ]
162
+ pd2 = pdTable [calcPD12 ].lovco [1 ]
163
+ fout , err = pllFreqOutPostdiv (xoscRef , fbdiv , MHz , refdiv , pd1 , pd2 )
164
+ margin = abs (int64 (fout ) - int64 (targetFreq ))
165
+ if err == nil && margin <= bestMargin {
166
+ found = true
167
+ bestFreq = fout
168
+ bestFbdiv = fbdiv
169
+ bestpd1 = pd1
170
+ bestpd2 = pd2
171
+ bestRefdiv = refdiv
172
+ bestMargin = margin
173
+ }
174
+ if found && ps .LowerVCO {
175
+ break
176
+ }
177
+ }
178
+ }
179
+ if bestFreq == 0 {
180
+ return fbdiv , refdiv , pd1 , pd2 , errors .New ("no best frequency found" )
181
+ }
182
+ return bestFbdiv , bestRefdiv , bestpd1 , bestpd2 , nil
183
+ }
184
+
185
+ func abs (a int64 ) int64 {
186
+ if a == math .MinInt64 {
187
+ return math .MaxInt64
188
+ } else if a < 0 {
189
+ return - a
190
+ }
191
+ return a
192
+ }
193
+
194
+ func pllFreqOutPostdiv (xosc , fbdiv , MHz uint64 , refdiv , postdiv1 , postdiv2 uint8 ) (foutpostdiv uint64 , err error ) {
195
+ // testing grounds.
196
+ const (
197
+ mhz = 1
198
+ cfref = 12 * mhz // given by crystal oscillator selection.
199
+ crefd = 1
200
+ cfbdiv = 100
201
+ cvco = cfref * cfbdiv / crefd
202
+ cpd1 = 6
203
+ cpd2 = 2
204
+ foutpd = (cfref / crefd ) * cfbdiv / (cpd1 * cpd2 )
205
+ )
206
+ refFreq := xosc / uint64 (refdiv )
207
+ overflow , vco := bits .Mul64 (xosc , fbdiv )
208
+ vco /= uint64 (refdiv )
209
+ foutpostdiv = vco / uint64 (postdiv1 * postdiv2 )
210
+ switch {
211
+ case refdiv < 1 || refdiv > 63 :
212
+ err = errors .New ("reference divider out of range" )
213
+ case fbdiv < 16 || fbdiv > 320 :
214
+ err = errors .New ("feedback divider out of range" )
215
+ case postdiv1 < 1 || postdiv1 > 7 :
216
+ err = errors .New ("postdiv1 out of range" )
217
+ case postdiv2 < 1 || postdiv2 > 7 :
218
+ err = errors .New ("postdiv2 out of range" )
219
+ case postdiv1 < postdiv2 :
220
+ err = errors .New ("user error: use higher value for postdiv1 for lower power consumption" )
221
+ case vco < 750 * MHz || vco > 1600 * MHz :
222
+ err = errors .New ("VCO out of range" )
223
+ case refFreq < 5 * MHz :
224
+ err = errors .New ("minimum reference frequency breach" )
225
+ case refFreq > vco / 16 :
226
+ err = errors .New ("maximum reference frequency breach" )
227
+ case vco > 1200 * MHz && vco < 1600 * MHz && xosc < 75 * MHz && refdiv != 1 :
228
+ err = errors .New ("refdiv should be 1 for given VCO and reference frequency" )
229
+ case overflow != 0 :
230
+ err = errVCOOverflow
231
+ }
232
+ if err != nil {
233
+ return 0 , err
234
+ }
235
+ return foutpostdiv , nil
236
+ }
237
+
238
+ func calcVCO (xoscFreq , fbdiv , refdiv uint32 ) uint32 {
239
+ const maxXoscMHz = math .MaxUint32 / 320 / MHz // 13MHz maximum xosc apparently.
240
+ if fbdiv > 320 || xoscFreq > math .MaxUint32 / 320 {
241
+ panic ("invalid VCO calculation args" )
242
+ }
243
+ return xoscFreq * fbdiv / refdiv
244
+ }
245
+
246
+ var pdTable = [50 ]struct {
247
+ hivco [2 ]uint8
248
+ lovco [2 ]uint8
249
+ }{}
250
+
251
+ func genTable () {
252
+ if pdTable [1 ].hivco [1 ] != 0 {
253
+ return // Already generated.
254
+ }
255
+ for product := 1 ; product < len (pdTable ); product ++ {
256
+ bestProdhi := 255
257
+ bestProdlo := 255
258
+ for pd1 := 7 ; pd1 > 0 ; pd1 -- {
259
+ for pd2 := pd1 ; pd2 > 0 ; pd2 -- {
260
+ gotprod := pd1 * pd2
261
+ if abs (int64 (gotprod - product )) < abs (int64 (bestProdlo - product )) {
262
+ bestProdlo = gotprod
263
+ pdTable [product ].lovco [0 ] = uint8 (pd1 )
264
+ pdTable [product ].lovco [1 ] = uint8 (pd2 )
265
+ }
266
+ }
267
+ }
268
+ for pd1 := 1 ; pd1 < 8 ; pd1 ++ {
269
+ for pd2 := 1 ; pd2 <= pd1 ; pd2 ++ {
270
+ gotprod := pd1 * pd2
271
+ if abs (int64 (gotprod - product )) < abs (int64 (bestProdhi - product )) {
272
+ bestProdhi = gotprod
273
+ pdTable [product ].hivco [0 ] = uint8 (pd1 )
274
+ pdTable [product ].hivco [1 ] = uint8 (pd2 )
275
+ }
276
+ }
277
+ }
278
+ }
279
+ }
0 commit comments