Skip to content

Commit 9c7bbce

Browse files
soypatdeadprogram
authored andcommitted
rp2350: add pll generalized solution; fix ADC handles; pwm period fix
1 parent 0ec1cb1 commit 9c7bbce

9 files changed

+322
-27
lines changed

GNUmakefile

+2
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@ smoketest: testchdir
523523
# regression test for #2563
524524
cd tests/os/smoke && $(TINYGO) test -c -target=pybadge && rm smoke.test
525525
# test all examples (except pwm)
526+
$(TINYGO) build -size short -o test.hex -target=pga2350 examples/echo
527+
@$(MD5SUM) test.hex
526528
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
527529
@$(MD5SUM) test.hex
528530
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc

src/machine/board_pga2350.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//go:build pga2350
2+
3+
package machine
4+
5+
// PGA2350 pin definitions.
6+
const (
7+
GP0 = GPIO0
8+
GP1 = GPIO1
9+
GP2 = GPIO2
10+
GP3 = GPIO3
11+
GP4 = GPIO4
12+
GP5 = GPIO5
13+
GP6 = GPIO6
14+
GP7 = GPIO7
15+
GP8 = GPIO8
16+
GP9 = GPIO9
17+
GP10 = GPIO10
18+
GP11 = GPIO11
19+
GP12 = GPIO12
20+
GP13 = GPIO13
21+
GP14 = GPIO14
22+
GP15 = GPIO15
23+
GP16 = GPIO16
24+
GP17 = GPIO17
25+
GP18 = GPIO18
26+
GP19 = GPIO19
27+
GP20 = GPIO20
28+
GP21 = GPIO21
29+
GP22 = GPIO22
30+
GP26 = GPIO26
31+
GP27 = GPIO27
32+
GP28 = GPIO28
33+
GP29 = GPIO29
34+
GP30 = GPIO30 // peripherals: PWM7 channel A
35+
GP31 = GPIO31 // peripherals: PWM7 channel B
36+
GP32 = GPIO32 // peripherals: PWM8 channel A
37+
GP33 = GPIO33 // peripherals: PWM8 channel B
38+
GP34 = GPIO34 // peripherals: PWM9 channel A
39+
GP35 = GPIO35 // peripherals: PWM9 channel B
40+
GP36 = GPIO36 // peripherals: PWM10 channel A
41+
GP37 = GPIO37 // peripherals: PWM10 channel B
42+
GP38 = GPIO38 // peripherals: PWM11 channel A
43+
GP39 = GPIO39 // peripherals: PWM11 channel B
44+
GP40 = GPIO40 // peripherals: PWM8 channel A
45+
GP41 = GPIO41 // peripherals: PWM8 channel B
46+
GP42 = GPIO42 // peripherals: PWM9 channel A
47+
GP43 = GPIO43 // peripherals: PWM9 channel B
48+
GP44 = GPIO44 // peripherals: PWM10 channel A
49+
GP45 = GPIO45 // peripherals: PWM10 channel B
50+
GP46 = GPIO46 // peripherals: PWM11 channel A
51+
GP47 = GPIO47 // peripherals: PWM11 channel B
52+
53+
)
54+
55+
var DefaultUART = UART0
56+
57+
// Peripheral defaults.
58+
const (
59+
xoscFreq = 12 // MHz
60+
61+
I2C0_SDA_PIN = GP4
62+
I2C0_SCL_PIN = GP5
63+
64+
I2C1_SDA_PIN = GP2
65+
I2C1_SCL_PIN = GP3
66+
67+
// Default Serial Clock Bus 0 for SPI communications
68+
SPI0_SCK_PIN = GPIO18
69+
// Default Serial Out Bus 0 for SPI communications
70+
SPI0_SDO_PIN = GPIO19 // Tx
71+
// Default Serial In Bus 0 for SPI communications
72+
SPI0_SDI_PIN = GPIO16 // Rx
73+
74+
// Default Serial Clock Bus 1 for SPI communications
75+
SPI1_SCK_PIN = GPIO10
76+
// Default Serial Out Bus 1 for SPI communications
77+
SPI1_SDO_PIN = GPIO11 // Tx
78+
// Default Serial In Bus 1 for SPI communications
79+
SPI1_SDI_PIN = GPIO12 // Rx
80+
81+
UART0_TX_PIN = GPIO0
82+
UART0_RX_PIN = GPIO1
83+
UART1_TX_PIN = GPIO8
84+
UART1_RX_PIN = GPIO9
85+
UART_TX_PIN = UART0_TX_PIN
86+
UART_RX_PIN = UART0_RX_PIN
87+
)
88+
89+
// USB identifiers
90+
const (
91+
usb_STRING_PRODUCT = "PGA2350"
92+
usb_STRING_MANUFACTURER = "Pimoroni"
93+
)
94+
95+
var (
96+
usb_VID uint16 = 0x2E8A
97+
usb_PID uint16 = 0x000A
98+
)

src/machine/machine_rp2_2350.go

-10
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,6 @@ const (
3333
rp.PADS_BANK0_GPIO0_ISO_Msk
3434
)
3535

36-
// Analog pins on RP2350.
37-
const (
38-
ADC0 Pin = GPIO26
39-
ADC1 Pin = GPIO27
40-
ADC2 Pin = GPIO28
41-
ADC3 Pin = GPIO29
42-
43-
thermADC = 30
44-
)
45-
4636
const (
4737
PinOutput PinMode = iota
4838
PinInput

src/machine/machine_rp2_2350a.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//go:build rp2350 && !rp2350b
2+
3+
package machine
4+
5+
// Analog pins on RP2350a.
6+
const (
7+
ADC0 Pin = GPIO26
8+
ADC1 Pin = GPIO27
9+
ADC2 Pin = GPIO28
10+
ADC3 Pin = GPIO29
11+
12+
// fifth ADC channel.
13+
thermADC = 30
14+
)

src/machine/machine_rp2_2350b.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const (
3535
ADC5 Pin = GPIO45
3636
ADC6 Pin = GPIO46
3737
ADC7 Pin = GPIO47
38-
38+
// Ninth ADC channel.
3939
thermADC = 48
4040
)
4141

src/machine/machine_rp2_clocks.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ func (clk *clock) configure(src, auxsrc, srcFreq, freq uint32) {
137137

138138
}
139139

140+
const pllsysFB, pllsysPD1, pllsysPD2 uint32 = 125, 6, 2 // RP2040 running 125MHz with 1500MHz VCO.
141+
140142
// init initializes the clock hardware.
141143
//
142144
// Must be called before any other clock function.
@@ -163,8 +165,8 @@ func (clks *clocksType) init() {
163165
// REF FBDIV VCO POSTDIV
164166
// pllSys: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
165167
// pllUSB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
166-
pllSys.init(1, 1500*MHz, 6, 2)
167-
pllUSB.init(1, 480*MHz, 5, 2)
168+
pllSys.init(1, uint32(pllsysFB), uint32(pllsysPD1), uint32(pllsysPD2))
169+
pllUSB.init(1, 40, 5, 2)
168170

169171
// Configure clocks
170172
// clkRef = xosc (12MHz) / 1 = 12MHz

src/machine/machine_rp2_pll.go

+183-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ package machine
44

55
import (
66
"device/rp"
7+
"errors"
8+
"math"
9+
"math/bits"
710
"runtime/volatile"
811
"unsafe"
912
)
@@ -29,12 +32,11 @@ var (
2932
// Post Divider 1, postDiv1 with range 1-7 and be >= postDiv2.
3033
//
3134
// 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) {
3336
refFreq := xoscFreq / refdiv
3437

3538
// What are we multiplying the reference clock by to get the vco freq
3639
// (The regs are called div, because you divide the vco output and compare it to the refclk)
37-
fbdiv := vcoFreq / (refFreq * MHz)
3840

3941
// Check fbdiv range
4042
if !(fbdiv >= 16 && fbdiv <= 320) {
@@ -54,13 +56,14 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
5456
panic("postdiv1 should be greater than or equal to postdiv2")
5557
}
5658

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)
5861
if refFreq > vcoFreq/16 {
5962
panic("reference frequency should not be greater than vco frequency divided by 16")
6063
}
6164

6265
// 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
6467

6568
if pll.cs.HasBits(rp.PLL_SYS_CS_LOCK) &&
6669
refdiv == pll.cs.Get()&rp.PLL_SYS_CS_REFDIV_Msk &&
@@ -98,3 +101,179 @@ func (pll *pll) init(refdiv, vcoFreq, postDiv1, postDiv2 uint32) {
98101
pll.pwr.ClearBits(rp.PLL_SYS_PWR_POSTDIVPD)
99102

100103
}
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

Comments
 (0)