diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2018d0f..7107b81 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -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/') diff --git a/CMakeLists.txt b/CMakeLists.txt index 83e1c1c..0ce599c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/fruitjam-build.sh b/fruitjam-build.sh index 6a1ca40..1d140db 100755 --- a/fruitjam-build.sh +++ b/fruitjam-build.sh @@ -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) diff --git a/include/clocking.h b/include/clocking.h new file mode 100644 index 0000000..6b987bd --- /dev/null +++ b/include/clocking.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +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); diff --git a/src/clocking.c b/src/clocking.c new file mode 100644 index 0000000..d095255 --- /dev/null +++ b/src/clocking.c @@ -0,0 +1,172 @@ +#include "clocking.h" + +#include +#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); +} diff --git a/src/main.c b/src/main.c index c4bd9ae..8283058 100644 --- a/src/main.c +++ b/src/main.c @@ -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