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 <graham.sanderson@raspberrypi.com>
This commit is contained in:
andygpz11 2023-03-21 17:49:20 +00:00 committed by GitHub
parent cb0ac5bd1a
commit a42564b4b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 168 additions and 66 deletions

View File

@ -105,7 +105,7 @@ void set_sys_clock_pll(uint32_t vco_freq, uint post_div1, uint post_div2);
* \ingroup pico_stdlib
*
* \param freq_khz Requested frequency
* \param vco_freq_out On success, the voltage controller oscillator frequeucny to be used by the SYS PLL
* \param vco_freq_out On success, the voltage controlled oscillator frequency to be used by the SYS PLL
* \param post_div1_out On success, The first post divider for the SYS PLL
* \param post_div2_out On success, The second post divider for the SYS PLL.
* @return true if the frequency is possible and the output parameters have been written.

View File

@ -17,7 +17,7 @@
#define NUM_SPIN_LOCKS 32u
#define XOSC_MHZ 12
#define XOSC_KHZ 12000u
#define NUM_SPIN_LOCKS 32u

View File

@ -37,9 +37,38 @@
#define PIO_INSTRUCTION_COUNT _u(32)
// PICO_CONFIG: XOSC_MHZ, The crystal oscillator frequency in Mhz, type=int, default=12, advanced=true, group=hardware_base
#ifndef XOSC_MHZ
#define XOSC_MHZ _u(12)
// PICO_CONFIG: XOSC_KHZ, The crystal oscillator frequency in kHz, type=int, default=12000, advanced=true, group=hardware_base
// NOTE: The system and USB clocks are generated from the frequency using two PLLs.
// 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
// override the defaults which live in src/rp2_common/hardware_clocks/include/hardware/clocks.h
// Please see the comments there about calculating the new PLL setting values.
#ifndef XOSC_KHZ
#define XOSC_KHZ _u(12000)
#endif
// PICO_CONFIG: SYS_CLK_KHZ, The system operating frequency in kHz, type=int, default=125000, advanced=true, group=hardware_base
#ifndef SYS_CLK_KHZ
#define SYS_CLK_KHZ _u(125000)
#endif
// 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
#ifndef USB_CLK_KHZ
#define USB_CLK_KHZ _u(48000)
#endif
// For backwards compatibility define XOSC_MHZ if the frequency is indeed an integer number of Mhz.
#if defined(XOSC_KHZ) && !defined(XOSC_MHZ) && (XOSC_KHZ % 1000 == 0)
#define XOSC_MHZ (XOSC_KHZ / 1000)
#endif
// For backwards compatibility define SYS_CLK_MHZ if the frequency is indeed an integer number of Mhz.
#if defined(SYS_CLK_KHZ) && !defined(SYS_CLK_MHZ) && (SYS_CLK_KHZ % 1000 == 0)
#define SYS_CLK_MHZ (SYS_CLK_KHZ / 1000)
#endif
// For backwards compatibility define USB_CLK_MHZ if the frequency is indeed an integer number of Mhz.
#if defined(USB_CLK_KHZ) && !defined(USB_CLK_MHZ) && (USB_CLK_KHZ % 1000 == 0)
#define USB_CLK_MHZ (USB_CLK_KHZ / 1000)
#endif
#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS)

View File

@ -14,6 +14,12 @@
#include "hardware/irq.h"
#include "hardware/gpio.h"
// The RTC clock frequency is 48MHz divided by power of 2 (to ensure an integer
// division ratio will be used in the clocks block). A divisor of 1024 generates
// an RTC clock tick of 46875Hz. This frequency is relatively close to the
// customary 32 or 32.768kHz 'slow clock' crystals and provides good timing resolution.
#define RTC_CLOCK_FREQ_HZ (USB_CLK_KHZ * KHZ / 1024)
check_hw_layout(clocks_hw_t, clk[clk_adc].selected, CLOCKS_CLK_ADC_SELECTED_OFFSET);
check_hw_layout(clocks_hw_t, fc0.result, CLOCKS_FC0_RESULT_OFFSET);
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,
/// \end::clock_configure[]
void clocks_init(void) {
// Start tick in watchdog
watchdog_start_tick(XOSC_MHZ);
// Start tick in watchdog, the argument is in 'cycles per microsecond' i.e. MHz
watchdog_start_tick(XOSC_KHZ / KHZ);
// Everything is 48MHz on FPGA apart from RTC. Otherwise set to 0 and will be set in clock configure
if (running_on_fpga()) {
for (uint i = 0; i < CLK_COUNT; i++) {
configured_freq[i] = 48 * MHZ;
}
configured_freq[clk_rtc] = 46875;
configured_freq[clk_rtc] = RTC_CLOCK_FREQ_HZ;
return;
}
@ -139,63 +145,56 @@ void clocks_init(void) {
while (clocks_hw->clk[clk_ref].selected != 0x1)
tight_loop_contents();
/// \tag::pll_settings[]
// Configure PLLs
// REF FBDIV VCO POSTDIV
// PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHz / 6 / 2 = 125MHz
// PLL USB: 12 / 1 = 12MHz * 100 = 1200MHz / 5 / 5 = 48MHz
/// \end::pll_settings[]
/// \tag::pll_init[]
pll_init(pll_sys, 1, 1500 * MHZ, 6, 2);
pll_init(pll_usb, 1, 1200 * MHZ, 5, 5);
pll_init(pll_sys, PLL_COMMON_REFDIV, PLL_SYS_VCO_FREQ_KHZ * KHZ, PLL_SYS_POSTDIV1, PLL_SYS_POSTDIV2);
pll_init(pll_usb, PLL_COMMON_REFDIV, PLL_USB_VCO_FREQ_KHZ * KHZ, PLL_USB_POSTDIV1, PLL_USB_POSTDIV2);
/// \end::pll_init[]
// Configure clocks
// CLK_REF = XOSC (12MHz) / 1 = 12MHz
// CLK_REF = XOSC (usually) 12MHz / 1 = 12MHz
clock_configure(clk_ref,
CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC,
0, // No aux mux
12 * MHZ,
12 * MHZ);
XOSC_KHZ * KHZ,
XOSC_KHZ * KHZ);
/// \tag::configure_clk_sys[]
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
// CLK SYS = PLL SYS (usually) 125MHz / 1 = 125MHz
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
125 * MHZ,
125 * MHZ);
SYS_CLK_KHZ * KHZ,
SYS_CLK_KHZ * KHZ);
/// \end::configure_clk_sys[]
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
// CLK USB = PLL USB 48MHz / 1 = 48MHz
clock_configure(clk_usb,
0, // No GLMUX
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
48 * MHZ);
USB_CLK_KHZ * KHZ,
USB_CLK_KHZ * KHZ);
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
// CLK ADC = PLL USB 48MHZ / 1 = 48MHz
clock_configure(clk_adc,
0, // No GLMUX
CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
48 * MHZ);
USB_CLK_KHZ * KHZ,
USB_CLK_KHZ * KHZ);
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
// CLK RTC = PLL USB 48MHz / 1024 = 46875Hz
clock_configure(clk_rtc,
0, // No GLMUX
CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
46875);
USB_CLK_KHZ * KHZ,
RTC_CLOCK_FREQ_HZ);
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
// Normally choose clk_sys or clk_usb
clock_configure(clk_peri,
0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
125 * MHZ,
125 * MHZ);
SYS_CLK_KHZ * KHZ,
SYS_CLK_KHZ * KHZ);
}
/// \tag::clock_get_hz[]

View File

@ -89,6 +89,70 @@ extern "C" {
#define KHZ 1000
#define MHZ 1000000
/// \tag::pll_settings[]
//
// There are two PLLs in RP2040:
// 1. The 'SYS PLL' generates the 125MHz system clock, the frequency is defined by `SYS_CLK_KHZ`.
// 2. The 'USB PLL' generates the 48MHz USB clock, the frequency is defined by `USB_CLK_KHZ`.
//
// The two PLLs use the crystal oscillator output directly as their reference frequency input; the PLLs reference
// frequency cannot be reduced by the dividers present in the clocks block. The crystal frequency is defined by `XOSC_KHZ` or
// `XOSC_MHZ`.
//
// The system's default definitions are correct for the above frequencies with a 12MHz
// crystal frequency. If different frequencies are required, these must be defined in
// the board configuration file together with the revised PLL settings
// Use `vcocalc.py` to check and calculate new PLL settings if you change any of these frequencies.
//
// Default PLL configuration:
// REF FBDIV VCO POSTDIV
// PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHz / 6 / 2 = 125MHz
// PLL USB: 12 / 1 = 12MHz * 100 = 1200MHz / 5 / 5 = 48MHz
/// \end::pll_settings[]
// PICO_CONFIG: PLL_COMMON_REFDIV, PLL reference divider setting - used for both PLLs, type=int, default=1, advanced=true, group=hardware_clocks
#ifndef PLL_COMMON_REFDIV
#define PLL_COMMON_REFDIV 1
#endif
#if (SYS_CLK_KHZ == 125000) && (XOSC_KHZ == 12000) && (PLL_COMMON_REFDIV == 1)
// PLL settings for standard 125 MHz system clock.
// PICO_CONFIG: PLL_SYS_VCO_FREQ_KHZ, System clock PLL frequency, type=int, default=1500 * KHZ, advanced=true, group=hardware_clocks
#ifndef PLL_SYS_VCO_FREQ_KHZ
#define PLL_SYS_VCO_FREQ_KHZ (1500 * KHZ)
#endif
// PICO_CONFIG: PLL_SYS_POSTDIV1, System clock PLL post divider 1 setting, type=int, default=6, advanced=true, group=hardware_clocks
#ifndef PLL_SYS_POSTDIV1
#define PLL_SYS_POSTDIV1 6
#endif
// PICO_CONFIG: PLL_SYS_POSTDIV2, System clock PLL post divider 2 setting, type=int, default=2, advanced=true, group=hardware_clocks
#ifndef PLL_SYS_POSTDIV2
#define PLL_SYS_POSTDIV2 2
#endif
#endif // SYS_CLK_KHZ == 125000 && XOSC_KHZ == 12000 && PLL_COMMON_REFDIV == 1
#if !defined(PLL_SYS_VCO_FREQ_KHZ) || !defined(PLL_SYS_POSTDIV1) || !defined(PLL_SYS_POSTDIV2)
#error PLL_SYS_VCO_FREQ_KHZ, PLL_SYS_POSTDIV1 and PLL_SYS_POSTDIV2 must all be specified when using custom clock setup
#endif
#if (USB_CLK_KHZ == 48000) && (XOSC_KHZ == 12000) && (PLL_COMMON_REFDIV == 1)
// PLL settings for a USB clock of 48MHz.
// PICO_CONFIG: PLL_USB_VCO_FREQ_KHZ, USB clock PLL frequency, type=int, default=1200 * KHZ, advanced=true, group=hardware_clocks
#ifndef PLL_USB_VCO_FREQ_KHZ
#define PLL_USB_VCO_FREQ_KHZ (1200 * KHZ)
#endif
// PICO_CONFIG: PLL_USB_POSTDIV1, USB clock PLL post divider 1 setting, type=int, default=5, advanced=true, group=hardware_clocks
#ifndef PLL_USB_POSTDIV1
#define PLL_USB_POSTDIV1 5
#endif
// PICO_CONFIG: PLL_USB_POSTDIV2, USB clock PLL post divider 2 setting, type=int, default=5, advanced=true, group=hardware_clocks
#ifndef PLL_USB_POSTDIV2
#define PLL_USB_POSTDIV2 5
#endif
#endif // USB_CLK_KHZ == 48000 && XOSC_KHZ == 12000 && PLL_COMMON_REFDIV == 1
#if !defined(PLL_USB_VCO_FREQ_KHZ) || !defined(PLL_USB_POSTDIV1) || !defined(PLL_USB_POSTDIV2)
#error PLL_USB_VCO_FREQ_KHZ, PLL_USB_POSTDIV1 and PLL_USB_POSTDIV2 must all be specified when using custom clock setup.
#endif
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_CLOCKS, Enable/disable assertions in the clocks module, type=bool, default=0, group=hardware_clocks
#ifndef PARAM_ASSERTIONS_ENABLED_CLOCKS
#define PARAM_ASSERTIONS_ENABLED_CLOCKS 0

View File

@ -31,12 +31,20 @@ typedef pll_hw_t *PLL;
#define pll_sys pll_sys_hw
#define pll_usb pll_usb_hw
#ifndef PICO_PLL_VCO_MIN_FREQ_KHZ
#ifndef PICO_PLL_VCO_MIN_FREQ_MHZ
#define PICO_PLL_VCO_MIN_FREQ_MHZ 750
#define PICO_PLL_VCO_MIN_FREQ_KHZ (750 * KHZ)
#else
#define PICO_PLL_VCO_MIN_FREQ_KHZ (PICO_PLL_VCO_MIN_FREQ_MHZ * KHZ)
#endif
#endif
#ifndef PICO_PLL_VCO_MAX_FREQ_KHZ
#ifndef PICO_PLL_VCO_MAX_FREQ_MHZ
#define PICO_PLL_VCO_MAX_FREQ_MHZ 1600
#define PICO_PLL_VCO_MAX_FREQ_KHZ (1600 * KHZ)
#else
#define PICO_PLL_VCO_MAX_FREQ_KHZ (PICO_PLL_VCO_MAX_FREQ_MHZ * KHZ)
#endif
#endif
/*! \brief Initialise specified PLL.

View File

@ -4,21 +4,21 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
// For MHZ definitions etc
// For frequency and PLL definitions etc.
#include "hardware/clocks.h"
#include "hardware/pll.h"
#include "hardware/resets.h"
/// \tag::pll_init_calculations[]
void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div2) {
uint32_t ref_mhz = XOSC_MHZ / refdiv;
uint32_t ref_freq = XOSC_KHZ * KHZ / refdiv;
// Check vco freq is in an acceptable range
assert(vco_freq >= (PICO_PLL_VCO_MIN_FREQ_MHZ * MHZ) && vco_freq <= (PICO_PLL_VCO_MAX_FREQ_MHZ * MHZ));
assert(vco_freq >= (PICO_PLL_VCO_MIN_FREQ_KHZ * KHZ) && vco_freq <= (PICO_PLL_VCO_MAX_FREQ_KHZ * KHZ));
// What are we multiplying the reference clock by to get the vco freq
// (The regs are called div, because you divide the vco output and compare it to the refclk)
uint32_t fbdiv = vco_freq / (ref_mhz * MHZ);
uint32_t fbdiv = vco_freq / ref_freq;
/// \end::pll_init_calculations[]
// fbdiv
@ -32,7 +32,7 @@ void pll_init(PLL pll, uint refdiv, uint vco_freq, uint post_div1, uint post_div
// postdiv1 is designed to operate with a higher input frequency than postdiv2
// Check that reference frequency is no greater than vco / 16
assert(ref_mhz <= (vco_freq / 16));
assert(ref_freq <= (vco_freq / 16));
// div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
uint32_t pdiv = (post_div1 << PLL_PRIM_POSTDIV1_LSB) |

View File

@ -6,18 +6,20 @@
#include "pico.h"
// For MHZ definitions etc
// For frequency related definitions etc
#include "hardware/clocks.h"
#include "hardware/platform_defs.h"
#include "hardware/regs/xosc.h"
#include "hardware/xosc.h"
#if XOSC_MHZ < 1 || XOSC_MHZ > 50
#error XOSC_MHZ must be in the range 1-50
#if XOSC_KHZ < (1 * KHZ) || XOSC_KHZ > (50 * KHZ)
// Note: Although an external clock can be supplied up to 50 MHz, the maximum frequency the
// XOSC cell is specified to work with a crystal is less, please see the RP2040 Datasheet.
#error XOSC_KHZ must be in the range 1,000-50,000KHz i.e. 1-50MHz XOSC frequency
#endif
#define STARTUP_DELAY (((((XOSC_MHZ * MHZ) / 1000) + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER)
#define STARTUP_DELAY (((XOSC_KHZ + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER)
// The DELAY field in xosc_hw->startup is 14 bits wide.
#if STARTUP_DELAY >= (1 << 13)

View File

@ -21,8 +21,8 @@ void set_sys_clock_48mhz() {
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
48 * MHZ);
USB_CLK_KHZ * KHZ,
USB_CLK_KHZ * KHZ);
// Turn off PLL sys for good measure
pll_deinit(pll_sys);
@ -31,8 +31,8 @@ void set_sys_clock_48mhz() {
clock_configure(clk_peri,
0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
48 * MHZ,
48 * MHZ);
USB_CLK_KHZ * KHZ,
USB_CLK_KHZ * KHZ);
}
}
@ -41,21 +41,21 @@ void set_sys_clock_pll(uint32_t vco_freq, uint post_div1, uint post_div2) {
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
48 * MHZ);
USB_CLK_KHZ * KHZ,
USB_CLK_KHZ * KHZ);
pll_init(pll_sys, 1, vco_freq, post_div1, post_div2);
pll_init(pll_sys, PLL_COMMON_REFDIV, vco_freq, post_div1, post_div2);
uint32_t freq = vco_freq / (post_div1 * post_div2);
// Configure clocks
// CLK_REF = XOSC (12MHz) / 1 = 12MHz
// CLK_REF is the XOSC source
clock_configure(clk_ref,
CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC,
0, // No aux mux
12 * MHZ,
12 * MHZ);
XOSC_KHZ * KHZ,
XOSC_KHZ * KHZ);
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
// CLK SYS = PLL SYS (usually) 125MHz / 1 = 125MHz
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
@ -64,23 +64,23 @@ void set_sys_clock_pll(uint32_t vco_freq, uint post_div1, uint post_div2) {
clock_configure(clk_peri,
0, // Only AUX mux on ADC
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
48 * MHZ);
USB_CLK_KHZ * KHZ,
USB_CLK_KHZ * KHZ);
}
}
bool check_sys_clock_khz(uint32_t freq_khz, uint *vco_out, uint *postdiv1_out, uint *postdiv_out) {
uint crystal_freq_khz = clock_get_hz(clk_ref) / 1000;
bool check_sys_clock_khz(uint32_t freq_khz, uint *vco_out, uint *postdiv1_out, uint *postdiv2_out) {
uint reference_freq_khz = XOSC_KHZ / PLL_COMMON_REFDIV;
for (uint fbdiv = 320; fbdiv >= 16; fbdiv--) {
uint vco = fbdiv * crystal_freq_khz;
if (vco < PICO_PLL_VCO_MIN_FREQ_MHZ * 1000 || vco > PICO_PLL_VCO_MAX_FREQ_MHZ * 1000) continue;
uint vco_khz = fbdiv * reference_freq_khz;
if (vco_khz < PICO_PLL_VCO_MIN_FREQ_KHZ || vco_khz > PICO_PLL_VCO_MAX_FREQ_KHZ) continue;
for (uint postdiv1 = 7; postdiv1 >= 1; postdiv1--) {
for (uint postdiv2 = postdiv1; postdiv2 >= 1; postdiv2--) {
uint out = vco / (postdiv1 * postdiv2);
if (out == freq_khz && !(vco % (postdiv1 * postdiv2))) {
*vco_out = vco * 1000;
uint out = vco_khz / (postdiv1 * postdiv2);
if (out == freq_khz && !(vco_khz % (postdiv1 * postdiv2))) {
*vco_out = vco_khz * KHZ;
*postdiv1_out = postdiv1;
*postdiv_out = postdiv2;
*postdiv2_out = postdiv2;
return true;
}
}