exclusively use PLL_USB for clocking

master v10.02
Peter Lawrence 2021-02-09 16:01:10 -06:00
rodzic 05f9c15b09
commit c200bafb75
4 zmienionych plików z 78 dodań i 96 usunięć

Wyświetl plik

@ -1,92 +0,0 @@
/* this function was salvaged from pico-bootrom/bootrom/bootrom_main.c */
/**
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <rp2040.h>
// USB bootloader requires clk_sys and clk_usb at 48 MHz. For this to work,
// xosc must be running at 12 MHz. It is possible that:
//
// - No crystal is present (and XI may not be properly grounded)
// - xosc output is much greater than 12 MHz
//
// In this case we *must* leave clk_sys in a safe state, and ideally, never
// return from this function. This is because boards which are not designed to
// use USB will still enter the USB bootcode when booted with a blank flash.
void _usb_clock_setup(void) {
// First make absolutely sure clk_ref is running: needed for resuscitate,
// and to run clk_sys while configuring sys PLL. Assume that rosc is not
// configured to run faster than clk_sys max (as this is officially out of
// spec)
// If user previously configured clk_ref to a different source (e.g.
// GPINx), then halted that source, the glitchless mux can't switch away
// from the dead source-- nothing we can do about this here.
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
hw_clear_bits(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_BITS);
// Resuscitate logic will switch clk_sys to clk_ref if it is inadvertently stopped
clocks_hw->resus.ctrl =
CLOCKS_CLK_SYS_RESUS_CTRL_ENABLE_BITS |
(CLOCKS_CLK_SYS_RESUS_CTRL_TIMEOUT_RESET
<< CLOCKS_CLK_SYS_RESUS_CTRL_TIMEOUT_LSB);
// Resetting PLL regs or changing XOSC range can glitch output, so switch
// clk_sys away before touching. Not worried about clk_usb as USB is held
// in reset.
hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS);
while (!(clocks_hw->clk[clk_sys].selected & 1u));
// rosc can not (while following spec) run faster than clk_sys max, so
// it's safe now to clear dividers in clkslices.
clocks_hw->clk[clk_sys].div = 0x100; // int 1 frac 0
clocks_hw->clk[clk_usb].div = 0x100;
// Try to get the crystal running. If no crystal is present, XI should be
// grounded, so STABLE counter will never complete. Poor designs might
// leave XI floating, in which case we may eventually drop through... in
// this case we rely on PLL not locking, and/or resuscitate counter.
//
// Don't touch range setting: user would only have changed if crystal
// needs it, and running crystal out of range can produce glitchy output.
// Note writing a "bad" value (non-aax) to RANGE has no effect.
xosc_hw->ctrl = XOSC_CTRL_ENABLE_VALUE_ENABLE << XOSC_CTRL_ENABLE_LSB;
while (!(xosc_hw->status & XOSC_STATUS_STABLE_BITS));
// Sys PLL setup:
// - VCO freq 1200 MHz, so feedback divisor of 100. Range is 400 MHz to 1.6 GHz
// - Postdiv1 of 5, down to 240 MHz (appnote recommends postdiv1 >= postdiv2)
// - Postdiv2 of 5, down to 48 MHz
//
// Total postdiv of 25 means that too-fast xtal will push VCO out of
// lockable range *before* clk_sys goes out of closure (factor of 1.88)
hw_set_bits(&resets_hw->reset, RESETS_RESET_PLL_SYS_BITS);
hw_clear_bits(&resets_hw->reset, RESETS_RESET_PLL_SYS_BITS);
while (!(resets_hw->reset_done & RESETS_RESET_DONE_PLL_SYS_BITS));
pll_sys_hw->cs = 1u << PLL_CS_REFDIV_LSB;
pll_sys_hw->fbdiv_int = 100;
pll_sys_hw->prim =
(5u << PLL_PRIM_POSTDIV1_LSB) |
(5u << PLL_PRIM_POSTDIV2_LSB);
// Power up VCO, wait for lock
hw_clear_bits(&pll_sys_hw->pwr, PLL_PWR_PD_BITS | PLL_PWR_VCOPD_BITS);
while (!(pll_sys_hw->cs & PLL_CS_LOCK_BITS));
// Power up post-dividers, which ungates PLL final output
hw_clear_bits(&pll_sys_hw->pwr, PLL_PWR_POSTDIVPD_BITS);
// Glitchy switch of clk_usb, clk_sys aux to sys PLL output.
clocks_hw->clk[clk_sys].ctrl = 0;
clocks_hw->clk[clk_usb].ctrl =
CLOCKS_CLK_USB_CTRL_ENABLE_BITS |
(CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS
<< CLOCKS_CLK_USB_CTRL_AUXSRC_LSB);
// Glitchless switch of clk_sys to aux source (sys PLL)
hw_set_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS);
while (!(clocks_hw->clk[clk_sys].selected & 0x2u));
}

Wyświetl plik

@ -26,16 +26,90 @@
#include "tusb.h"
#include <rp2040.h>
#include "hardware/pll.h"
/*
This is a more streamlined alternative to the current pico-sdk based TinyUSB board support package.
Sticking to C and avoiding all that C++ yields a much smaller executable.
*/
void _usb_clock_setup(void); /* clock initialization routine borrowed from pico-bootrom */
/* overhaul of clock_configure() from pico-sdk to use much less memory */
bool simple_clock_configure(enum clock_index clk_index, uint32_t src, uint32_t auxsrc, bool glitchless)
{
const uint32_t div = 0x100; /* always 1:1 ratio */
clock_hw_t *clock = &clocks_hw->clk[clk_index];
// If increasing divisor, set divisor before source. Otherwise set source
// before divisor. This avoids a momentary overspeed when e.g. switching
// to a faster source and increasing divisor to compensate.
if (div > clock->div)
clock->div = div;
// If switching a glitchless slice (ref or sys) to an aux source, switch
// away from aux *first* to avoid passing glitches when changing aux mux.
// Assume (!!!) glitchless source 0 is no faster than the aux source.
if (glitchless)
{
hw_clear_bits(&clock->ctrl, CLOCKS_CLK_REF_CTRL_SRC_BITS);
while (!(clock->selected & 1u));
}
// If no glitchless mux, cleanly stop the clock to avoid glitches
// propagating when changing aux mux. Note it would be a really bad idea
// to do this on one of the glitchless clocks (clk_sys, clk_ref).
else
{
hw_clear_bits(&clock->ctrl, CLOCKS_CLK_GPOUT0_CTRL_ENABLE_BITS);
}
// Set aux mux first, and then glitchless mux if this clock has one
hw_write_masked(&clock->ctrl,
(auxsrc << CLOCKS_CLK_SYS_CTRL_AUXSRC_LSB),
CLOCKS_CLK_SYS_CTRL_AUXSRC_BITS
);
if (glitchless)
{
hw_write_masked(&clock->ctrl,
src << CLOCKS_CLK_REF_CTRL_SRC_LSB,
CLOCKS_CLK_REF_CTRL_SRC_BITS
);
while (!(clock->selected & (1u << src)));
}
hw_set_bits(&clock->ctrl, CLOCKS_CLK_GPOUT0_CTRL_ENABLE_BITS);
// Now that the source is configured, we can trust that the user-supplied
// divisor is a safe value.
clock->div = div;
return true;
}
static void usb_clock_init(void)
{
hw_set_bits(&resets_hw->reset, RESETS_RESET_PLL_USB_BITS);
hw_clear_bits(&resets_hw->reset, RESETS_RESET_PLL_USB_BITS);
while (~resets_hw->reset_done & RESETS_RESET_PLL_USB_BITS);
pll_init(pll_usb_hw, 1, 480 * 1000000, 5, 2);
// CLK SYS = PLL USB (48MHz) / 1 = 48MHz
simple_clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
true);
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
simple_clock_configure(clk_usb,
0, // No GLMUX
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
false);
}
void board_init(void)
{
_usb_clock_setup();
usb_clock_init();
}
void irq_set_exclusive_handler(unsigned int num, void *handler) {}

Wyświetl plik

@ -15,12 +15,12 @@
<file file_name="./main.c" />
<file file_name="./usb_descriptors.c" />
<file file_name="myboard.c" />
<file file_name="clock_setup.c" />
<file file_name="spawn.c" />
</folder>
<folder Name="hal">
<file file_name="$(TOP)/hw/bsp/board.c" />
<file file_name="$(TOP)/src/portable/raspberrypi/rp2040/rp2040_usb.c" />
<file file_name="$(TOP)/hw/mcu/raspberrypi/pico-sdk/src/rp2_common/hardware_pll/pll.c" />
</folder>
<folder Name="CMSIS-DAP">
<file file_name="./CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c" />

Wyświetl plik

@ -23,7 +23,7 @@ tusb_desc_device_t const desc_device =
/* using Dapper Miser CMSIS-DAP VID:PID */
.idVendor = 0x1209,
.idProduct = 0x2488,
.bcdDevice = 0x1001,
.bcdDevice = 0x1002,
.iManufacturer = 0,
.iProduct = STRID_PRODUCT,