Created
March 17, 2025 01:51
-
-
Save eightycc/b61813c05899281ce7d2a2f86490be3b to your computer and use it in GitHub Desktop.
Overclock RP2350 with PSRAM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Generated Cmake Pico project file | |
cmake_minimum_required(VERSION 3.13) | |
set(CMAKE_C_STANDARD 11) | |
set(CMAKE_CXX_STANDARD 17) | |
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | |
# Initialise pico_sdk from installed location | |
# (note this can come from environment, CMake cache etc) | |
# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == | |
if(WIN32) | |
set(USERHOME $ENV{USERPROFILE}) | |
else() | |
set(USERHOME $ENV{HOME}) | |
endif() | |
set(sdkVersion 2.1.1) | |
set(toolchainVersion 14_2_Rel1) | |
set(picotoolVersion 2.1.1) | |
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) | |
if (EXISTS ${picoVscode}) | |
include(${picoVscode}) | |
endif() | |
# ==================================================================================== | |
set(PICO_BOARD adafruit_feather_rp2350 CACHE STRING "Board type") | |
# Pull in Raspberry Pi Pico SDK (must be before project) | |
include(pico_sdk_import.cmake) | |
project(overclock_rp2350 C CXX ASM) | |
# Initialise the Raspberry Pi Pico SDK | |
pico_sdk_init() | |
# Add executable. Default name is the project name, version 0.1 | |
add_executable(overclock_rp2350 overclock_rp2350.c ) | |
pico_set_program_name(overclock_rp2350 "overclock_rp2350") | |
pico_set_program_version(overclock_rp2350 "0.1") | |
# Modify the below lines to enable/disable output over UART/USB | |
pico_enable_stdio_uart(overclock_rp2350 0) | |
pico_enable_stdio_usb(overclock_rp2350 1) | |
# Add the standard library to the build | |
target_link_libraries(overclock_rp2350 | |
pico_malloc | |
pico_rand | |
pico_stdlib | |
pico_time | |
hardware_dma | |
) | |
# Add the standard include files to the build | |
target_include_directories(overclock_rp2350 PRIVATE | |
${CMAKE_CURRENT_LIST_DIR} | |
) | |
pico_add_extra_outputs(overclock_rp2350) | |
# Overclock RP2350 to 240MHz, increase VREG voltage to 1.20V | |
target_compile_definitions(overclock_rp2350 PRIVATE | |
PLL_SYS_REFDIV=1 | |
PLL_SYS_VCO_FREQ_HZ=1440000000 | |
PLL_SYS_POSTDIV1=6 | |
PLL_SYS_POSTDIV2=1 | |
SYS_CLK_HZ=240000000 | |
SYS_CLK_VREG_VOLTAGE_AUTO_ADJUST=1 | |
SYS_CLK_VREG_VOLTAGE_MIN=VREG_VOLTAGE_1_20 | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "pico/flash.h" | |
#include "pico/stdlib.h" | |
#include "pico/rand.h" | |
#include "pico/time.h" | |
#include "hardware/dma.h" | |
#include "hardware/flash.h" | |
#include "hardware/sync.h" | |
#include "hardware/xip_cache.h" | |
#include "hardware/regs/addressmap.h" | |
#include "hardware/regs/qmi.h" | |
#include "hardware/regs/xip.h" | |
#include "hardware/structs/xip_ctrl.h" | |
#include "hardware/structs/qmi.h" | |
#include "hardware/structs/xip_ctrl.h" | |
#define PICO_PSRAM_CHIP_SELECT 8 // Feather RP2350 GPIO pin for PSRAM chip select | |
#define XIP_PSRAM_CACHED 0x11000000 | |
#define XIP_PSRAM_NOCACHE 0x15000000 | |
#define BUF_SIZE (128*1024) | |
#define FLASH_TARGET_OFFSET (1024 * 1024) // +1MB should be safe to use | |
static volatile uint32_t random_buf[BUF_SIZE/4]; | |
static volatile uint32_t copy_buf[BUF_SIZE/4]; | |
static size_t _psram_size; | |
static uint8_t _psram_readid_response[8]; | |
// Activate PSRAM. (Copied from CircuitPython ports/raspberrypi/supervisor/port.c) | |
static void __no_inline_not_in_flash_func(setup_psram)(void) { | |
gpio_set_function(PICO_PSRAM_CHIP_SELECT, GPIO_FUNC_XIP_CS1); | |
_psram_size = 0; | |
uint32_t save_irq_status = save_and_disable_interrupts(); | |
// Try and read the PSRAM ID via direct_csr. | |
qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | | |
QMI_DIRECT_CSR_EN_BITS; | |
// Need to poll for the cooldown on the last XIP transfer to expire | |
// (via direct-mode BUSY flag) before it is safe to perform the first | |
// direct-mode operation | |
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { | |
} | |
// Exit out of QMI in case we've inited already | |
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; | |
// Transmit as quad. | |
qmi_hw->direct_tx = QMI_DIRECT_TX_OE_BITS | | |
QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | | |
0xf5; | |
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { | |
} | |
(void)qmi_hw->direct_rx; | |
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS); | |
// Read the id | |
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; | |
uint8_t kgd = 0; | |
uint8_t eid = 0; | |
for (size_t i = 0; i < 12; i++) { | |
if (i == 0) { | |
qmi_hw->direct_tx = 0x9f; | |
} else { | |
qmi_hw->direct_tx = 0xff; | |
} | |
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS) == 0) { | |
} | |
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { | |
} | |
// buffer read id response eliding the first 4 bytes (cmd + 24-bit addr) | |
if (i >= 4) { | |
_psram_readid_response[i - 4] = qmi_hw->direct_rx; | |
} else { | |
(void)qmi_hw->direct_rx; | |
} | |
if (i == 5) { | |
kgd = _psram_readid_response[i-4]; | |
} else if (i == 6) { | |
eid = _psram_readid_response[i-4]; | |
} | |
} | |
// Disable direct csr. | |
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); | |
if (kgd != 0x5D) { | |
restore_interrupts(save_irq_status); | |
return; | |
} | |
// Enable quad mode. | |
qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | | |
QMI_DIRECT_CSR_EN_BITS; | |
// Need to poll for the cooldown on the last XIP transfer to expire | |
// (via direct-mode BUSY flag) before it is safe to perform the first | |
// direct-mode operation | |
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { | |
} | |
// RESETEN, RESET, quad enable, toggle wrap boundary mode | |
for (uint8_t i = 0; i < 4; i++) { | |
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; | |
switch (i) { | |
case 0: | |
// RESETEN | |
qmi_hw->direct_tx = 0x66; | |
break; | |
case 1: | |
// RESET | |
qmi_hw->direct_tx = 0x99; | |
break; | |
case 2: | |
// Quad enable | |
qmi_hw->direct_tx = 0x35; | |
break; | |
case 3: | |
// Toggle wrap boundary mode | |
qmi_hw->direct_tx = 0xc0; | |
break; | |
} | |
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) { | |
} | |
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS); | |
for (size_t j = 0; j < 20; j++) { | |
asm ("nop"); | |
} | |
(void)qmi_hw->direct_rx; | |
} | |
// Disable direct csr. | |
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); | |
// PSRAM timings with 150MHz SCK, 6.667 ns per cycle. | |
// COOLDOWN: 2'b01 64 SCK = 426.7ns | |
// PAGEBREAK: 2'b10 break bursts at 1024-byte page boundaries | |
// reserved: 2'b00 | |
// SELECT_SETUP: 1'b0 0.5 SCK = 3.33ns | |
// SELECT_HOLD: 2'b11 1.0 SCK = 6.67ns | |
// MAX_SELECT: 6'b01_0000 16 x 64 SCK = 6.827us | |
// MIN_DESELECT: 4'b0111 7.5 x SCK = 50.0ns | |
// reserved: 1'b0 | |
// RXDELAY: 3'b001 0.5 SCK = 3.33ns | |
// CLKDIV: 8'b0000_0010 150 / 2 = 75MHz | |
// Revised PSRAM timings with 240MHz SCK, 4.167 ns per cycle. | |
// COOLDOWN: 2'b01 64 SCK = 266.7ns | |
// PAGEBREAK: 2'b10 break bursts at 1024-byte page boundaries | |
// reserved: 2'b00 | |
// SELECT_SETUP: 1'b0 0.5 SCK = 2.08ns | |
// SELECT_HOLD: 2'b11 1.0 SCK = 4.17ns | |
// MAX_SELECT: 6'b01_1101 29 x 64 SCK = 7.734us | |
// MIN_DESELECT: 4'b1100 12.5 x SCK = 52.1ns | |
// reserved: 1'b0 | |
// RXDELAY: 3'b010 1.0 SCK = 4.17ns | |
// CLKDIV: 8'b0000_0010 240 / 2 = 120MHz | |
qmi_hw->m[1].timing = | |
QMI_M0_TIMING_PAGEBREAK_VALUE_1024 << QMI_M0_TIMING_PAGEBREAK_LSB | // Break between pages. | |
3 << QMI_M0_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles. | |
1 << QMI_M0_TIMING_COOLDOWN_LSB | | |
2 << QMI_M0_TIMING_RXDELAY_LSB | | |
29 << QMI_M0_TIMING_MAX_SELECT_LSB | // In units of 64 system clock cycles. PSRAM says 8us max. 8 / 0.00752 / 64 = 16.62 | |
12 << QMI_M0_TIMING_MIN_DESELECT_LSB | // In units of system clock cycles. PSRAM says 50ns.50 / 7.52 = 6.64 | |
2 << QMI_M0_TIMING_CLKDIV_LSB; | |
qmi_hw->m[1].rfmt = (QMI_M0_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_PREFIX_WIDTH_LSB | | |
QMI_M0_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M0_RFMT_ADDR_WIDTH_LSB | | |
QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_SUFFIX_WIDTH_LSB | | |
QMI_M0_RFMT_DUMMY_WIDTH_VALUE_Q << QMI_M0_RFMT_DUMMY_WIDTH_LSB | | |
QMI_M0_RFMT_DUMMY_LEN_VALUE_24 << QMI_M0_RFMT_DUMMY_LEN_LSB | | |
QMI_M0_RFMT_DATA_WIDTH_VALUE_Q << QMI_M0_RFMT_DATA_WIDTH_LSB | | |
QMI_M0_RFMT_PREFIX_LEN_VALUE_8 << QMI_M0_RFMT_PREFIX_LEN_LSB | | |
QMI_M0_RFMT_SUFFIX_LEN_VALUE_NONE << QMI_M0_RFMT_SUFFIX_LEN_LSB); | |
qmi_hw->m[1].rcmd = 0xeb << QMI_M0_RCMD_PREFIX_LSB | | |
0 << QMI_M0_RCMD_SUFFIX_LSB; | |
qmi_hw->m[1].wfmt = (QMI_M0_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_PREFIX_WIDTH_LSB | | |
QMI_M0_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M0_WFMT_ADDR_WIDTH_LSB | | |
QMI_M0_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_SUFFIX_WIDTH_LSB | | |
QMI_M0_WFMT_DUMMY_WIDTH_VALUE_Q << QMI_M0_WFMT_DUMMY_WIDTH_LSB | | |
QMI_M0_WFMT_DUMMY_LEN_VALUE_NONE << QMI_M0_WFMT_DUMMY_LEN_LSB | | |
QMI_M0_WFMT_DATA_WIDTH_VALUE_Q << QMI_M0_WFMT_DATA_WIDTH_LSB | | |
QMI_M0_WFMT_PREFIX_LEN_VALUE_8 << QMI_M0_WFMT_PREFIX_LEN_LSB | | |
QMI_M0_WFMT_SUFFIX_LEN_VALUE_NONE << QMI_M0_WFMT_SUFFIX_LEN_LSB); | |
qmi_hw->m[1].wcmd = 0x38 << QMI_M0_WCMD_PREFIX_LSB | | |
0 << QMI_M0_WCMD_SUFFIX_LSB; | |
restore_interrupts(save_irq_status); | |
_psram_size = 1024 * 1024; // 1 MiB | |
uint8_t size_id = eid >> 5; | |
if (eid == 0x26 || size_id == 2) { | |
_psram_size *= 8; | |
} else if (size_id == 0) { | |
_psram_size *= 2; | |
} else if (size_id == 1) { | |
_psram_size *= 4; | |
} | |
// Mark that we can write to PSRAM. | |
xip_ctrl_hw->ctrl |= XIP_CTRL_WRITABLE_M1_BITS; | |
// Test write to the PSRAM. | |
volatile uint32_t *psram_nocache = (volatile uint32_t *)XIP_PSRAM_NOCACHE; | |
psram_nocache[0] = 0x12345678; | |
volatile uint32_t readback = psram_nocache[0]; | |
if (readback != 0x12345678) { | |
_psram_size = 0; | |
return; | |
} | |
} | |
// Flash erase and program function trampolines | |
static void call_flash_range_erase(void *param) { | |
uint32_t offset = ((uintptr_t*)param)[0]; | |
size_t len = ((uintptr_t*)param)[1]; | |
flash_range_erase(offset, len); | |
} | |
static void call_flash_range_program(void *param) { | |
uint32_t offset = ((uintptr_t*)param)[0]; | |
const uint8_t *data = (const uint8_t *)((uintptr_t*)param)[1]; | |
size_t len = ((uintptr_t*)param)[2]; | |
flash_range_program(offset, data, len); | |
} | |
static bool verify_buffer(const uint32_t *src, const uint32_t *dst, size_t len) { | |
for (size_t i = 0; i < len / 4; i++) { | |
if (src[i] != dst[i]) { | |
printf("Buffer mismatch at index %d: %08x != %08x\n", i, src[i], dst[i]); | |
return false; | |
} | |
} | |
return true; | |
} | |
static uint32_t time_copy_buffer(const uint32_t *src, uint32_t *dst, size_t len) { | |
uint32_t start_time = time_us_32(); | |
for (int i = 0; i < (len / 4); i++) { | |
dst[i] = src[i]; | |
} | |
uint32_t end_time = time_us_32(); | |
return end_time - start_time; | |
} | |
static uint32_t time_xip_stream(const uint32_t *src, uint32_t *dst, size_t len) { | |
// Set up XIP to stream data from flash or PSRAM to SRAM via DMA channel 0 | |
while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY)) | |
(void) xip_ctrl_hw->stream_fifo; | |
xip_ctrl_hw->stream_addr = (uint32_t)src; | |
// NOTE: stream_ctr counts 32-bit words | |
xip_ctrl_hw->stream_ctr = len / 4; | |
// Configure DMA channel 0 to stream from XIP | |
const uint dma_chan = 0; | |
dma_channel_config cfg = dma_channel_get_default_config(dma_chan); | |
channel_config_set_read_increment(&cfg, false); | |
channel_config_set_write_increment(&cfg, true); | |
channel_config_set_dreq(&cfg, DREQ_XIP_STREAM); | |
uint32_t start_time = time_us_32(); | |
dma_channel_configure( | |
dma_chan, | |
&cfg, | |
(void *) dst, // Write addr | |
(const void *) XIP_AUX_BASE, // Read addr | |
len / 4, // Transfer count | |
true // Start immediately! | |
); | |
//printf("DMA channel %d started\n", dma_chan); | |
dma_channel_wait_for_finish_blocking(dma_chan); | |
uint32_t end_time = time_us_32(); | |
return end_time - start_time; | |
} | |
int main() | |
{ | |
stdio_init_all(); | |
sleep_ms(1000); // Allow time to start USB terminal | |
setup_psram(); | |
printf("PSRAM size: %d bytes\n", _psram_size); | |
printf("PSRAM read ID: %02x %02x %02x %02x %02x %02x %02x %02x\n", | |
_psram_readid_response[0], _psram_readid_response[1], _psram_readid_response[2], _psram_readid_response[3], | |
_psram_readid_response[4], _psram_readid_response[5], _psram_readid_response[6], _psram_readid_response[7]); | |
printf("Buffer size: %d bytes\n", BUF_SIZE); | |
// Fill an SRAM buffer with a random pattern | |
uint32_t start_time = time_us_32(); | |
for (int i = 0; i < (BUF_SIZE / 4); i++) { | |
((uint32_t *)random_buf)[i] = get_rand_32(); | |
} | |
uint32_t end_time = time_us_32(); | |
printf("SRAM buffer random filled in %d us\n", end_time - start_time); | |
// Flash random buffer and verify | |
printf("Erasing random buffer in flash\n"); | |
uintptr_t flash_erase_params[] = {FLASH_TARGET_OFFSET, BUF_SIZE}; | |
int rc = flash_safe_execute(call_flash_range_erase, flash_erase_params, UINT32_MAX); | |
if (rc < 0) { | |
printf("Flash range erase failed: %d\n", rc); | |
return -1; | |
} | |
printf("Programming random buffer to flash\n"); | |
uintptr_t flash_program_params[] = {FLASH_TARGET_OFFSET, (uintptr_t)random_buf, BUF_SIZE}; | |
rc = flash_safe_execute(call_flash_range_program, flash_program_params, UINT32_MAX); | |
if (rc < 0) { | |
printf("Flash range program failed: %d\n", rc); | |
return -1; | |
} | |
printf("Verifying random flash buffer\n"); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)(XIP_BASE + FLASH_TARGET_OFFSET), BUF_SIZE / 4)) { | |
printf("Flash buffer verification failed\n"); | |
} | |
// Copy SRAM -> SRAM and verify | |
uint32_t copy_time = time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)copy_buf, BUF_SIZE); | |
printf("SRAM -> SRAM copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)copy_buf, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy Flash(cached) -> SRAM and verify | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_BASE + FLASH_TARGET_OFFSET, BUF_SIZE); | |
printf("Flash(cached) -> SRAM copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)copy_buf, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy Flash(no cache) -> SRAM and verify | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_NOCACHE_NOALLOC_BASE + FLASH_TARGET_OFFSET, BUF_SIZE); | |
printf("Flash(no cache) -> SRAM copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)copy_buf, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy SRAM -> PSRAM(cached) and verify | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_PSRAM_CACHED, BUF_SIZE); | |
printf("SRAM -> PSRAM(cached) copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)XIP_PSRAM_CACHED, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy SRAM -> PSRAM(no cache) and verify | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_PSRAM_NOCACHE, BUF_SIZE); | |
printf("SRAM -> PSRAM(no cache) copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)XIP_PSRAM_NOCACHE, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy PSRAM(cached) -> SRAM and verify | |
xip_cache_clean_all(); | |
time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_PSRAM_NOCACHE, BUF_SIZE); | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)XIP_PSRAM_CACHED, (uint32_t *)copy_buf, BUF_SIZE); | |
printf("PSRAM(cached) -> SRAM copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)copy_buf, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy PSRAM(no cache) -> SRAM and verify | |
xip_cache_clean_all(); | |
time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_PSRAM_NOCACHE, BUF_SIZE); | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)XIP_PSRAM_NOCACHE, (uint32_t *)copy_buf, BUF_SIZE); | |
printf("PSRAM(no cache) -> SRAM copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)copy_buf, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy PSRAM(cached) -> PSRAM(cached) and verify | |
xip_cache_clean_all(); | |
time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_PSRAM_NOCACHE, BUF_SIZE); | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)XIP_PSRAM_CACHED, (uint32_t *)XIP_PSRAM_CACHED+BUF_SIZE, BUF_SIZE); | |
printf("PSRAM(cached) -> PSRAM(cached) copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)XIP_PSRAM_CACHED+BUF_SIZE, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy PSRAM(no cache) -> PSRAM(no cache) and verify | |
xip_cache_clean_all(); | |
time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_PSRAM_NOCACHE, BUF_SIZE); | |
xip_cache_clean_all(); | |
copy_time = time_copy_buffer((const uint32_t *)XIP_PSRAM_NOCACHE, (uint32_t *)XIP_PSRAM_NOCACHE+BUF_SIZE, BUF_SIZE); | |
printf("PSRAM(no cache) -> PSRAM(no cache) copied in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)XIP_PSRAM_NOCACHE+BUF_SIZE, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy Flash(no cache) -> SRAM via XIP stream and verify | |
xip_cache_clean_all(); | |
copy_time = time_xip_stream((const uint32_t *)(XIP_NOCACHE_NOALLOC_BASE + FLASH_TARGET_OFFSET), (uint32_t *)copy_buf, BUF_SIZE); | |
printf("Flash(no cache) -> SRAM via XIP stream in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)copy_buf, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
// Copy PSRAM(no cache) -> SRAM via XIP stream and verify | |
xip_cache_clean_all(); | |
time_copy_buffer((const uint32_t *)random_buf, (uint32_t *)XIP_PSRAM_NOCACHE, BUF_SIZE); | |
xip_cache_clean_all(); | |
copy_time = time_xip_stream((const uint32_t *)(XIP_PSRAM_NOCACHE), (uint32_t *)copy_buf, BUF_SIZE); | |
printf("PSRAM(no cache) -> SRAM via XIP stream in %d us, %d ns/word\n", copy_time, (copy_time * 1000) / (BUF_SIZE / 4)); | |
if (!verify_buffer((const uint32_t *)random_buf, (const uint32_t *)copy_buf, BUF_SIZE)) { | |
printf("Buffer verification failed\n"); | |
} | |
printf("\n\n"); | |
sleep_ms(1000); // Allow time to flush output | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This example overclocks an Adafruit RP2350 Feather to 240 MHz. I chose 240 MHz because it is an integral multiple of 48 MHz, allowing it to be used as the clock source for USB. This frees the USB PLL so that it can be used to generate an HSTX clock that is an integral multiple of the desired pixel clock. This should also yield more stable operation of Pico-PIO-USB in full-speed mode because its PIO clock will be exactly 48 MHz without requiring a fractional divisor.
The PSRAM timing parameters were adjusted to operate its QSPI interface at 120 MHz. Additionally, it was necessary to toggle the PSRAM into wrap boundary mode using the 0xc0 command.
Results: