Skip to content

Commit a42564b

Browse files
Allow support of crystals other than 12MHz (#1024) (#1272)
* Allow pre-processor overrides for Clock/PLL setup * Use `_KHZ` rather than `_MHZ` for `XOSC_` `SYS_CLOCK_` etc definitions (`_MHZ` versions are provided for compatibility when `_KHZ` is a multiple of 1000) Co-authored-by: graham sanderson <[email protected]>
1 parent cb0ac5b commit a42564b

File tree

9 files changed

+168
-66
lines changed

9 files changed

+168
-66
lines changed

src/common/pico_stdlib/include/pico/stdlib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ void set_sys_clock_pll(uint32_t vco_freq, uint post_div1, uint post_div2);
105105
* \ingroup pico_stdlib
106106
*
107107
* \param freq_khz Requested frequency
108-
* \param vco_freq_out On success, the voltage controller oscillator frequeucny to be used by the SYS PLL
108+
* \param vco_freq_out On success, the voltage controlled oscillator frequency to be used by the SYS PLL
109109
* \param post_div1_out On success, The first post divider for the SYS PLL
110110
* \param post_div2_out On success, The second post divider for the SYS PLL.
111111
* @return true if the frequency is possible and the output parameters have been written.

src/host/pico_platform/include/hardware/platform_defs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
#define NUM_SPIN_LOCKS 32u
1919

20-
#define XOSC_MHZ 12
20+
#define XOSC_KHZ 12000u
2121

2222
#define NUM_SPIN_LOCKS 32u
2323

src/rp2040/hardware_regs/include/hardware/platform_defs.h

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,38 @@
3737

3838
#define PIO_INSTRUCTION_COUNT _u(32)
3939

40-
// PICO_CONFIG: XOSC_MHZ, The crystal oscillator frequency in Mhz, type=int, default=12, advanced=true, group=hardware_base
41-
#ifndef XOSC_MHZ
42-
#define XOSC_MHZ _u(12)
40+
// PICO_CONFIG: XOSC_KHZ, The crystal oscillator frequency in kHz, type=int, default=12000, advanced=true, group=hardware_base
41+
// NOTE: The system and USB clocks are generated from the frequency using two PLLs.
42+
// If you override this define, or SYS_CLK_KHZ/USB_CLK_KHZ below, you will *also* need to add your own adjusted PLL set-up defines to
43+
// override the defaults which live in src/rp2_common/hardware_clocks/include/hardware/clocks.h
44+
// Please see the comments there about calculating the new PLL setting values.
45+
#ifndef XOSC_KHZ
46+
#define XOSC_KHZ _u(12000)
47+
#endif
48+
49+
// PICO_CONFIG: SYS_CLK_KHZ, The system operating frequency in kHz, type=int, default=125000, advanced=true, group=hardware_base
50+
#ifndef SYS_CLK_KHZ
51+
#define SYS_CLK_KHZ _u(125000)
52+
#endif
53+
54+
// PICO_CONFIG: USB_CLK_KHZ, USB clock frequency. Must be 48MHz for the USB interface to operate correctly, type=int, default=48000, advanced=true, group=hardware_base
55+
#ifndef USB_CLK_KHZ
56+
#define USB_CLK_KHZ _u(48000)
57+
#endif
58+
59+
// For backwards compatibility define XOSC_MHZ if the frequency is indeed an integer number of Mhz.
60+
#if defined(XOSC_KHZ) && !defined(XOSC_MHZ) && (XOSC_KHZ % 1000 == 0)
61+
#define XOSC_MHZ (XOSC_KHZ / 1000)
62+
#endif
63+
64+
// For backwards compatibility define SYS_CLK_MHZ if the frequency is indeed an integer number of Mhz.
65+
#if defined(SYS_CLK_KHZ) && !defined(SYS_CLK_MHZ) && (SYS_CLK_KHZ % 1000 == 0)
66+
#define SYS_CLK_MHZ (SYS_CLK_KHZ / 1000)
67+
#endif
68+
69+
// For backwards compatibility define USB_CLK_MHZ if the frequency is indeed an integer number of Mhz.
70+
#if defined(USB_CLK_KHZ) && !defined(USB_CLK_MHZ) && (USB_CLK_KHZ % 1000 == 0)
71+
#define USB_CLK_MHZ (USB_CLK_KHZ / 1000)
4372
#endif
4473

4574
#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS)

src/rp2_common/hardware_clocks/clocks.c

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
#include "hardware/irq.h"
1515
#include "hardware/gpio.h"
1616

17+
// The RTC clock frequency is 48MHz divided by power of 2 (to ensure an integer
18+
// division ratio will be used in the clocks block). A divisor of 1024 generates
19+
// an RTC clock tick of 46875Hz. This frequency is relatively close to the
20+
// customary 32 or 32.768kHz 'slow clock' crystals and provides good timing resolution.
21+
#define RTC_CLOCK_FREQ_HZ (USB_CLK_KHZ * KHZ / 1024)
22+
1723
check_hw_layout(clocks_hw_t, clk[clk_adc].selected, CLOCKS_CLK_ADC_SELECTED_OFFSET);
1824
check_hw_layout(clocks_hw_t, fc0.result, CLOCKS_FC0_RESULT_OFFSET);
1925
check_hw_layout(clocks_hw_t, ints, CLOCKS_INTS_OFFSET);
@@ -113,15 +119,15 @@ bool clock_configure(enum clock_index clk_index, uint32_t src, uint32_t auxsrc,
113119
/// \end::clock_configure[]
114120

115121
void clocks_init(void) {
116-
// Start tick in watchdog
117-
watchdog_start_tick(XOSC_MHZ);
122+
// Start tick in watchdog, the argument is in 'cycles per microsecond' i.e. MHz
123+
watchdog_start_tick(XOSC_KHZ / KHZ);
118124

119125
// Everything is 48MHz on FPGA apart from RTC. Otherwise set to 0 and will be set in clock configure
120126
if (running_on_fpga()) {
121127
for (uint i = 0; i < CLK_COUNT; i++) {
122128
configured_freq[i] = 48 * MHZ;
123129
}
124-
configured_freq[clk_rtc] = 46875;
130+
configured_freq[clk_rtc] = RTC_CLOCK_FREQ_HZ;
125131
return;
126132
}
127133

@@ -139,63 +145,56 @@ void clocks_init(void) {
139145
while (clocks_hw->clk[clk_ref].selected != 0x1)
140146
tight_loop_contents();
141147

142-
/// \tag::pll_settings[]
143-
// Configure PLLs
144-
// REF FBDIV VCO POSTDIV
145-
// PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHz / 6 / 2 = 125MHz
146-
// PLL USB: 12 / 1 = 12MHz * 100 = 1200MHz / 5 / 5 = 48MHz
147-
/// \end::pll_settings[]
148-
149148
/// \tag::pll_init[]
150-
pll_init(pll_sys, 1, 1500 * MHZ, 6, 2);
151-
pll_init(pll_usb, 1, 1200 * MHZ, 5, 5);
149+
pll_init(pll_sys, PLL_COMMON_REFDIV, PLL_SYS_VCO_FREQ_KHZ * KHZ, PLL_SYS_POSTDIV1, PLL_SYS_POSTDIV2);
150+
pll_init(pll_usb, PLL_COMMON_REFDIV, PLL_USB_VCO_FREQ_KHZ * KHZ, PLL_USB_POSTDIV1, PLL_USB_POSTDIV2);
152151
/// \end::pll_init[]
153152

154153
// Configure clocks
155-
// CLK_REF = XOSC (12MHz) / 1 = 12MHz
154+
// CLK_REF = XOSC (usually) 12MHz / 1 = 12MHz
156155
clock_configure(clk_ref,
157156
CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC,
158157
0, // No aux mux
159-
12 * MHZ,
160-
12 * MHZ);
158+
XOSC_KHZ * KHZ,
159+
XOSC_KHZ * KHZ);
161160

162161
/// \tag::configure_clk_sys[]
163-
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
162+
// CLK SYS = PLL SYS (usually) 125MHz / 1 = 125MHz
164163
clock_configure(clk_sys,
165164
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
166165
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
167-
125 * MHZ,
168-
125 * MHZ);
166+
SYS_CLK_KHZ * KHZ,
167+
SYS_CLK_KHZ * KHZ);
169168
/// \end::configure_clk_sys[]
170169

171-
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
170+
// CLK USB = PLL USB 48MHz / 1 = 48MHz
172171
clock_configure(clk_usb,
173172
0, // No GLMUX
174173
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
175-
48 * MHZ,
176-
48 * MHZ);
174+
USB_CLK_KHZ * KHZ,
175+
USB_CLK_KHZ * KHZ);
177176

178-
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
177+
// CLK ADC = PLL USB 48MHZ / 1 = 48MHz
179178
clock_configure(clk_adc,
180179
0, // No GLMUX
181180
CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
182-
48 * MHZ,
183-
48 * MHZ);
181+
USB_CLK_KHZ * KHZ,
182+
USB_CLK_KHZ * KHZ);
184183

185-
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
184+
// CLK RTC = PLL USB 48MHz / 1024 = 46875Hz
186185
clock_configure(clk_rtc,
187186
0, // No GLMUX
188187
CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
189-
48 * MHZ,
190-
46875);
188+
USB_CLK_KHZ * KHZ,
189+
RTC_CLOCK_FREQ_HZ);
191190

192191
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
193192
// Normally choose clk_sys or clk_usb
194193
clock_configure(clk_peri,
195194
0,
196195
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
197-
125 * MHZ,
198-
125 * MHZ);
196+
SYS_CLK_KHZ * KHZ,
197+
SYS_CLK_KHZ * KHZ);
199198
}
200199

201200
/// \tag::clock_get_hz[]

src/rp2_common/hardware_clocks/include/hardware/clocks.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,70 @@ extern "C" {
8989
#define KHZ 1000
9090
#define MHZ 1000000
9191

92+
/// \tag::pll_settings[]
93+
//
94+
// There are two PLLs in RP2040:
95+
// 1. The 'SYS PLL' generates the 125MHz system clock, the frequency is defined by `SYS_CLK_KHZ`.
96+
// 2. The 'USB PLL' generates the 48MHz USB clock, the frequency is defined by `USB_CLK_KHZ`.
97+
//
98+
// The two PLLs use the crystal oscillator output directly as their reference frequency input; the PLLs reference
99+
// frequency cannot be reduced by the dividers present in the clocks block. The crystal frequency is defined by `XOSC_KHZ` or
100+
// `XOSC_MHZ`.
101+
//
102+
// The system's default definitions are correct for the above frequencies with a 12MHz
103+
// crystal frequency. If different frequencies are required, these must be defined in
104+
// the board configuration file together with the revised PLL settings
105+
// Use `vcocalc.py` to check and calculate new PLL settings if you change any of these frequencies.
106+
//
107+
// Default PLL configuration:
108+
// REF FBDIV VCO POSTDIV
109+
// PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHz / 6 / 2 = 125MHz
110+
// PLL USB: 12 / 1 = 12MHz * 100 = 1200MHz / 5 / 5 = 48MHz
111+
/// \end::pll_settings[]
112+
113+
// PICO_CONFIG: PLL_COMMON_REFDIV, PLL reference divider setting - used for both PLLs, type=int, default=1, advanced=true, group=hardware_clocks
114+
#ifndef PLL_COMMON_REFDIV
115+
#define PLL_COMMON_REFDIV 1
116+
#endif
117+
118+
#if (SYS_CLK_KHZ == 125000) && (XOSC_KHZ == 12000) && (PLL_COMMON_REFDIV == 1)
119+
// PLL settings for standard 125 MHz system clock.
120+
// PICO_CONFIG: PLL_SYS_VCO_FREQ_KHZ, System clock PLL frequency, type=int, default=1500 * KHZ, advanced=true, group=hardware_clocks
121+
#ifndef PLL_SYS_VCO_FREQ_KHZ
122+
#define PLL_SYS_VCO_FREQ_KHZ (1500 * KHZ)
123+
#endif
124+
// PICO_CONFIG: PLL_SYS_POSTDIV1, System clock PLL post divider 1 setting, type=int, default=6, advanced=true, group=hardware_clocks
125+
#ifndef PLL_SYS_POSTDIV1
126+
#define PLL_SYS_POSTDIV1 6
127+
#endif
128+
// PICO_CONFIG: PLL_SYS_POSTDIV2, System clock PLL post divider 2 setting, type=int, default=2, advanced=true, group=hardware_clocks
129+
#ifndef PLL_SYS_POSTDIV2
130+
#define PLL_SYS_POSTDIV2 2
131+
#endif
132+
#endif // SYS_CLK_KHZ == 125000 && XOSC_KHZ == 12000 && PLL_COMMON_REFDIV == 1
133+
#if !defined(PLL_SYS_VCO_FREQ_KHZ) || !defined(PLL_SYS_POSTDIV1) || !defined(PLL_SYS_POSTDIV2)
134+
#error PLL_SYS_VCO_FREQ_KHZ, PLL_SYS_POSTDIV1 and PLL_SYS_POSTDIV2 must all be specified when using custom clock setup
135+
#endif
136+
137+
#if (USB_CLK_KHZ == 48000) && (XOSC_KHZ == 12000) && (PLL_COMMON_REFDIV == 1)
138+
// PLL settings for a USB clock of 48MHz.
139+
// PICO_CONFIG: PLL_USB_VCO_FREQ_KHZ, USB clock PLL frequency, type=int, default=1200 * KHZ, advanced=true, group=hardware_clocks
140+
#ifndef PLL_USB_VCO_FREQ_KHZ
141+
#define PLL_USB_VCO_FREQ_KHZ (1200 * KHZ)
142+
#endif
143+
// PICO_CONFIG: PLL_USB_POSTDIV1, USB clock PLL post divider 1 setting, type=int, default=5, advanced=true, group=hardware_clocks
144+
#ifndef PLL_USB_POSTDIV1
145+
#define PLL_USB_POSTDIV1 5
146+
#endif
147+
// PICO_CONFIG: PLL_USB_POSTDIV2, USB clock PLL post divider 2 setting, type=int, default=5, advanced=true, group=hardware_clocks
148+
#ifndef PLL_USB_POSTDIV2
149+
#define PLL_USB_POSTDIV2 5
150+
#endif
151+
#endif // USB_CLK_KHZ == 48000 && XOSC_KHZ == 12000 && PLL_COMMON_REFDIV == 1
152+
#if !defined(PLL_USB_VCO_FREQ_KHZ) || !defined(PLL_USB_POSTDIV1) || !defined(PLL_USB_POSTDIV2)
153+
#error PLL_USB_VCO_FREQ_KHZ, PLL_USB_POSTDIV1 and PLL_USB_POSTDIV2 must all be specified when using custom clock setup.
154+
#endif
155+
92156
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_CLOCKS, Enable/disable assertions in the clocks module, type=bool, default=0, group=hardware_clocks
93157
#ifndef PARAM_ASSERTIONS_ENABLED_CLOCKS
94158
#define PARAM_ASSERTIONS_ENABLED_CLOCKS 0

src/rp2_common/hardware_pll/include/hardware/pll.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,20 @@ typedef pll_hw_t *PLL;
3131
#define pll_sys pll_sys_hw
3232
#define pll_usb pll_usb_hw
3333

34+
#ifndef PICO_PLL_VCO_MIN_FREQ_KHZ
3435
#ifndef PICO_PLL_VCO_MIN_FREQ_MHZ
35-
#define PICO_PLL_VCO_MIN_FREQ_MHZ 750
36+
#define PICO_PLL_VCO_MIN_FREQ_KHZ (750 * KHZ)
37+
#else
38+
#define PICO_PLL_VCO_MIN_FREQ_KHZ (PICO_PLL_VCO_MIN_FREQ_MHZ * KHZ)
39+
#endif
3640
#endif
3741

42+
#ifndef PICO_PLL_VCO_MAX_FREQ_KHZ
3843
#ifndef PICO_PLL_VCO_MAX_FREQ_MHZ
39-
#define PICO_PLL_VCO_MAX_FREQ_MHZ 1600
44+
#define PICO_PLL_VCO_MAX_FREQ_KHZ (1600 * KHZ)
45+
#else
46+
#define PICO_PLL_VCO_MAX_FREQ_KHZ (PICO_PLL_VCO_MAX_FREQ_MHZ * KHZ)
47+
#endif
4048
#endif
4149

4250
/*! \brief Initialise specified PLL.

src/rp2_common/hardware_pll/pll.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44
* SPDX-License-Identifier: BSD-3-Clause
55
*/
66

7-
// For MHZ definitions etc
7+
// For frequency and PLL definitions etc.
88
#include "hardware/clocks.h"
99
#include "hardware/pll.h"
1010
#include "hardware/resets.h"
1111

1212
/// \tag::pll_init_calculations[]
1313
void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div2) {
14-
uint32_t ref_mhz = XOSC_MHZ / refdiv;
14+
uint32_t ref_freq = XOSC_KHZ * KHZ / refdiv;
1515

1616
// Check vco freq is in an acceptable range
17-
assert(vco_freq >= (PICO_PLL_VCO_MIN_FREQ_MHZ * MHZ) && vco_freq <= (PICO_PLL_VCO_MAX_FREQ_MHZ * MHZ));
17+
assert(vco_freq >= (PICO_PLL_VCO_MIN_FREQ_KHZ * KHZ) && vco_freq <= (PICO_PLL_VCO_MAX_FREQ_KHZ * KHZ));
1818

1919
// What are we multiplying the reference clock by to get the vco freq
2020
// (The regs are called div, because you divide the vco output and compare it to the refclk)
21-
uint32_t fbdiv = vco_freq / (ref_mhz * MHZ);
21+
uint32_t fbdiv = vco_freq / ref_freq;
2222
/// \end::pll_init_calculations[]
2323

2424
// fbdiv
@@ -32,7 +32,7 @@ void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div
3232
// postdiv1 is designed to operate with a higher input frequency than postdiv2
3333

3434
// Check that reference frequency is no greater than vco / 16
35-
assert(ref_mhz <= (vco_freq / 16));
35+
assert(ref_freq <= (vco_freq / 16));
3636

3737
// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
3838
uint32_t pdiv = (post_div1 << PLL_PRIM_POSTDIV1_LSB) |

src/rp2_common/hardware_xosc/xosc.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66

77
#include "pico.h"
88

9-
// For MHZ definitions etc
9+
// For frequency related definitions etc
1010
#include "hardware/clocks.h"
1111

1212
#include "hardware/platform_defs.h"
1313
#include "hardware/regs/xosc.h"
1414
#include "hardware/xosc.h"
1515

16-
#if XOSC_MHZ < 1 || XOSC_MHZ > 50
17-
#error XOSC_MHZ must be in the range 1-50
16+
#if XOSC_KHZ < (1 * KHZ) || XOSC_KHZ > (50 * KHZ)
17+
// Note: Although an external clock can be supplied up to 50 MHz, the maximum frequency the
18+
// XOSC cell is specified to work with a crystal is less, please see the RP2040 Datasheet.
19+
#error XOSC_KHZ must be in the range 1,000-50,000KHz i.e. 1-50MHz XOSC frequency
1820
#endif
1921

20-
#define STARTUP_DELAY (((((XOSC_MHZ * MHZ) / 1000) + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER)
22+
#define STARTUP_DELAY (((XOSC_KHZ + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER)
2123

2224
// The DELAY field in xosc_hw->startup is 14 bits wide.
2325
#if STARTUP_DELAY >= (1 << 13)

0 commit comments

Comments
 (0)