Add overclocking

.. not currently compatible with PSRAM (sadly), even though I tried
doing the right things with PSRAM clocking.
This commit is contained in:
Jeff Epler 2025-03-27 09:34:09 -05:00
parent 082f3175f0
commit bfc9f6f0f0
6 changed files with 234 additions and 5 deletions

View file

@ -39,18 +39,24 @@ jobs:
./fruitjam-build.sh -m 4096 -v
./fruitjam-build.sh -m 400
./fruitjam-build.sh -m 400 -v
./fruitjam-build.sh -m 400 -o
./fruitjam-build.sh -m 400 -v -o
mkdir uf2s
cp build*/*.uf2 uf2s/
mkdir elfs
cp build*/*.elf elfs/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: uf2 files
path: build*/*.uf2
path: uf2s/*
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: elf files
path: build*/*.elf
path: elfs/*
- name: Create release
if: startsWith(github.ref, 'refs/tags/')

View file

@ -55,6 +55,8 @@ set(HSTX_D0P 15 CACHE STRING "HSTX D0+ PIN")
set(HSTX_D1P 17 CACHE STRING "HSTX D1+ PIN")
set(HSTX_D2P 19 CACHE STRING "HSTX D2+ PIN")
option(OVERCLOCK "Overclock to 264MHz (known incompatible with psram)" OFF)
# Options for analog VGA output
option(USE_VGA_RES "Video uses VGA (640x480) resolution" OFF)
set(VIDEO_PIN 18 CACHE STRING "VGA Video GPIO base pin (followed by VS, CLK, HS)")
@ -84,6 +86,13 @@ else()
set(VIDEO_SRC src/video_vga.c)
endif()
if (OVERCLOCK)
add_compile_definitions(OVERCLOCK=1)
set(OPT_OC "-oc")
else()
set(OPT_OC "")
endif()
if (USE_VGA_RES)
add_compile_definitions(USE_VGA_RES=1)
add_compile_definitions(DISP_WIDTH=640)
@ -105,7 +114,7 @@ else()
set(OPT_PSRAM "")
endif()
set(FIRMWARE "pico-mac-${PICO_BOARD}-${MEMSIZE}k-${RES}${OPT_PSRAM}")
set(FIRMWARE "pico-mac-${PICO_BOARD}-${MEMSIZE}k-${RES}${OPT_PSRAM}${OPT_OC}")
# initialize the SDK based on PICO_SDK_PATH
@ -180,6 +189,7 @@ if (TARGET tinyusb_device)
${VIDEO_SRC}
src/kbd.c
src/hid.c
src/clocking.c
${EXTRA_SD_SRC}
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c

View file

@ -12,9 +12,13 @@ DISP_HEIGHT=342
MEMSIZE=400
DISC_IMAGE=
CMAKE_ARGS=""
OVERCLOCK=0
while getopts "hvd:m:" o; do
while getopts "hovd:m:" o; do
case "$o" in
(o)
OVERCLOCK=1
;;
(v)
DISP_WIDTH=640
DISP_HEIGHT=480
@ -30,8 +34,9 @@ while getopts "hvd:m:" o; do
echo "Usage: $0 [-v] [-m KiB] [-d diskimage]"
echo ""
echo " -v: Use framebuffer resolution 640x480 instead of 512x342"
echo " -m: Set memory size in KiB"
echo " -m: Set memory size in KiB (over 400kB requires psram)"
echo " -d: Specify disc image to include"
echo " -o: Overclock to 264MHz (known to be incompatible with psram)"
echo ""
echo "PSRAM is automatically set depending on memory & framebuffer details"
exit
@ -44,6 +49,9 @@ shift $((OPTIND-1))
TAG=fruitjam_${DISP_WIDTH}x${DISP_HEIGHT}_${MEMSIZE}k
PSRAM=$((MEMSIZE > 400))
if [ $PSRAM -ne 0 ] ; then
if [ $OVERCLOCK -ne 0 ]; then
echo "*** Overclock + PSRAM is known not to work. You have been warned."
fi
TAG=${TAG}_psram
CMAKE_ARGS="$CMAKE_ARGS -DUSE_PSRAM=1"
fi
@ -61,6 +69,10 @@ if [ -n "$DISC_IMAGE" ] && [ -f "$DISC_IMAGE" ]; then
TAG=${TAG}_${DISC_IMAGE}
fi
if [ $OVERCLOCK -ne 0 ]; then
TAG=${TAG}_overclock
fi
set -x
rm -rf build_${TAG}
cmake -S . -B build_${TAG} \
@ -72,5 +84,7 @@ cmake -S . -B build_${TAG} \
-DSD_TX=35 -DSD_RX=36 -DSD_SCK=34 -DSD_CS=39 -DUSE_SD=1 \
-DUART_TX=44 -DUART_RX=45 -DUART=0 \
-DBOARD_FILE=boards/adafruit_fruit_jam.c \
-DSD_MHZ=16 \
-DOVERCLOCK=${OVERCLOCK} \
${CMAKE_ARGS} "$@"
make -C build_${TAG} -j$(nproc)

11
include/clocking.h Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
enum clk_sys_speed {
CLK_SYS_264MHZ = 2,
CLK_SYS_176MHZ = 3,
CLK_SYS_132MHZ = 4,
};
extern void overclock(enum clk_sys_speed clk_sys_div, uint32_t bit_clk_hz);

172
src/clocking.c Normal file
View file

@ -0,0 +1,172 @@
#include "clocking.h"
#include <stdio.h>
#include "pico.h"
#include "pico/stdio.h"
#include "hardware/clocks.h"
#include "hardware/pll.h"
#include "hardware/structs/ioqspi.h"
#include "hardware/structs/qmi.h"
#include "hardware/sync.h"
#include "hardware/vreg.h"
static void __no_inline_not_in_flash_func(set_qmi_timing)() {
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS)
;
qmi_hw->m[0].timing = 0x40000202;
//qmi_hw->m[0].timing = 0x40000101;
// Force a read through XIP to ensure the timing is applied
volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
(void) *ptr;
}
#ifndef RP2350_PSRAM_MAX_SELECT_FS64
#define RP2350_PSRAM_MAX_SELECT_FS64 (125000000)
#endif
#ifndef RP2350_PSRAM_MIN_DESELECT_FS
#define RP2350_PSRAM_MIN_DESELECT_FS (50000000)
#endif
#ifndef RP2350_PSRAM_MAX_SCK_HZ
#define RP2350_PSRAM_MAX_SCK_HZ (109000000)
#endif
#define SEC_TO_FS 1000000000000000ll
static void __no_inline_not_in_flash_func(set_psram_timing)(void) {
// Get secs / cycle for the system clock - get before disabling interrupts.
uint32_t sysHz = (uint32_t)clock_get_hz(clk_sys);
// Calculate the clock divider - goal to get clock used for PSRAM <= what
// the PSRAM IC can handle - which is defined in RP2350_PSRAM_MAX_SCK_HZ
volatile uint8_t clockDivider = (sysHz + RP2350_PSRAM_MAX_SCK_HZ - 1) / RP2350_PSRAM_MAX_SCK_HZ;
uint32_t intr_stash = save_and_disable_interrupts();
// Get the clock femto seconds per cycle.
uint32_t fsPerCycle = SEC_TO_FS / sysHz;
// the maxSelect value is defined in units of 64 clock cycles
// So maxFS / (64 * fsPerCycle) = maxSelect = RP2350_PSRAM_MAX_SELECT_FS64/fsPerCycle
volatile uint8_t maxSelect = RP2350_PSRAM_MAX_SELECT_FS64 / fsPerCycle;
// minDeselect time - in system clock cycle
// Must be higher than 50ns (min deselect time for PSRAM) so add a fsPerCycle - 1 to round up
// So minFS/fsPerCycle = minDeselect = RP2350_PSRAM_MIN_DESELECT_FS/fsPerCycle
volatile uint8_t minDeselect = (RP2350_PSRAM_MIN_DESELECT_FS + fsPerCycle - 1) / fsPerCycle;
printf("syshz=%u\n", sysHz);
printf("Max Select: %d, Min Deselect: %d, clock divider: %d\n", maxSelect, minDeselect, clockDivider);
qmi_hw->m[1].timing = QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Break between pages.
3 << QMI_M1_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles.
1 << QMI_M1_TIMING_COOLDOWN_LSB | 1 << QMI_M1_TIMING_RXDELAY_LSB |
maxSelect << QMI_M1_TIMING_MAX_SELECT_LSB | minDeselect << QMI_M1_TIMING_MIN_DESELECT_LSB |
clockDivider << QMI_M1_TIMING_CLKDIV_LSB;
restore_interrupts(intr_stash);
}
static void __no_inline_not_in_flash_func(clock_init)(int sys_clk_div) {
uint32_t intr_stash = save_and_disable_interrupts();
// Before messing with clock speeds ensure QSPI clock is nice and slow
hw_write_masked(&qmi_hw->m[0].timing, 6, QMI_M0_TIMING_CLKDIV_BITS);
// We're going to go fast, boost the voltage a little
vreg_set_voltage(VREG_VOLTAGE_1_15);
// Force a read through XIP to ensure the timing is applied before raising the clock rate
volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
(void) *ptr;
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS);
while (clocks_hw->clk[clk_sys].selected != 0x1)
tight_loop_contents();
hw_write_masked(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, CLOCKS_CLK_REF_CTRL_SRC_BITS);
while (clocks_hw->clk[clk_ref].selected != 0x4)
tight_loop_contents();
// Stop the other clocks so we don't worry about overspeed
clock_stop(clk_usb);
clock_stop(clk_adc);
clock_stop(clk_peri);
clock_stop(clk_hstx);
// Set USB PLL to 528MHz
pll_init(pll_usb, PLL_COMMON_REFDIV, 1584 * MHZ, 3, 1);
const uint32_t usb_pll_freq = 528 * MHZ;
// CLK SYS = PLL USB 528MHz / sys_clk_div = 264MHz, 176MHz, or 132MHz
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq, usb_pll_freq / sys_clk_div);
// CLK PERI = PLL USB 528MHz / 4 = 132MHz
clock_configure(clk_peri,
0, // Only AUX mux on ADC
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq, usb_pll_freq / 4);
// CLK USB = PLL USB 528MHz / 11 = 48MHz
clock_configure(clk_usb,
0, // No GLMUX
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq,
USB_CLK_KHZ * KHZ);
// CLK ADC = PLL USB 528MHz / 11 = 48MHz
clock_configure(clk_adc,
0, // No GLMUX
CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq,
USB_CLK_KHZ * KHZ);
// Now we are running fast set fast QSPI clock and read delay
set_qmi_timing();
restore_interrupts(intr_stash);
}
void overclock(enum clk_sys_speed clk_sys_div, uint32_t bit_clk_khz) {
clock_init(clk_sys_div);
stdio_init_all();
set_psram_timing();
#define SHOW_CLK(i) printf("clk_get_hz(%s) -> %u\n", #i, clock_get_hz(i));
SHOW_CLK(clk_gpout0);
SHOW_CLK(clk_gpout1);
SHOW_CLK(clk_gpout2);
SHOW_CLK(clk_gpout3);
SHOW_CLK(clk_ref);
SHOW_CLK(clk_sys);
SHOW_CLK(clk_peri);
SHOW_CLK(clk_hstx);
SHOW_CLK(clk_usb);
SHOW_CLK(clk_adc);
const uint32_t dvi_clock_khz = bit_clk_khz >> 1;
printf("bit_clk_khz = %u dvi_clock_khz = %u\n", bit_clk_khz, dvi_clock_khz);
uint vco_freq, post_div1, post_div2;
if (!check_sys_clock_khz(dvi_clock_khz, &vco_freq, &post_div1, &post_div2))
panic("System clock of %u kHz cannot be exactly achieved", dvi_clock_khz);
const uint32_t freq = vco_freq / (post_div1 * post_div2);
// Set the sys PLL to the requested freq
pll_init(pll_sys, PLL_COMMON_REFDIV, vco_freq, post_div1, post_div2);
// CLK HSTX = Requested freq
clock_configure(clk_hstx,
0,
CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
freq, freq);
}

View file

@ -45,6 +45,7 @@
#include "tusb.h"
#include "umac.h"
#include "clocking.h"
#if USE_SD
#include "f_util.h"
@ -471,6 +472,9 @@ static void __no_inline_not_in_flash_func(setup_psram)(void) {
int main()
{
#if defined(OVERCLOCK) && OVERCLOCK+0
overclock(CLK_SYS_264MHZ, 252000);
#endif
// set_sys_clock_khz(250*1000, true);
setup_psram();
@ -478,6 +482,18 @@ int main()
stdio_init_all();
io_init();
#define SHOW_CLK(i) printf("clk_get_hz(%s) -> %u\n", #i, clock_get_hz(i));
SHOW_CLK(clk_gpout0);
SHOW_CLK(clk_gpout1);
SHOW_CLK(clk_gpout2);
SHOW_CLK(clk_gpout3);
SHOW_CLK(clk_ref);
SHOW_CLK(clk_sys);
SHOW_CLK(clk_peri);
SHOW_CLK(clk_hstx);
SHOW_CLK(clk_usb);
SHOW_CLK(clk_adc);
#if ENABLE_AUDIO
audio_setup();
#endif