diff --git a/src/boards/include/boards/adafruit_feather_rp2350.h b/src/boards/include/boards/adafruit_feather_rp2350.h index 08227df8a..7ef3f49b5 100644 --- a/src/boards/include/boards/adafruit_feather_rp2350.h +++ b/src/boards/include/boards/adafruit_feather_rp2350.h @@ -83,6 +83,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (8 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (8 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN 8 +#endif + +// Only some boards have PSRAM fitted, so auto-detect +#ifndef PICO_AUTO_DETECT_PSRAM_SIZE +#define PICO_AUTO_DETECT_PSRAM_SIZE 1 +#endif + pico_board_cmake_set_default(PICO_RP2350_A2_SUPPORTED, 1) #ifndef PICO_RP2350_A2_SUPPORTED #define PICO_RP2350_A2_SUPPORTED 1 diff --git a/src/boards/include/boards/adafruit_fruit_jam.h b/src/boards/include/boards/adafruit_fruit_jam.h index d0f9086b4..0ff04a2d7 100644 --- a/src/boards/include/boards/adafruit_fruit_jam.h +++ b/src/boards/include/boards/adafruit_fruit_jam.h @@ -205,6 +205,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN 47 +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + pico_board_cmake_set_default(PICO_RP2350_A2_SUPPORTED, 1) #ifndef PICO_RP2350_A2_SUPPORTED #define PICO_RP2350_A2_SUPPORTED 1 diff --git a/src/boards/include/boards/defcon32_badge.h b/src/boards/include/boards/defcon32_badge.h index ce18d6157..4d4f9d0c8 100644 --- a/src/boards/include/boards/defcon32_badge.h +++ b/src/boards/include/boards/defcon32_badge.h @@ -95,6 +95,16 @@ pico_board_cmake_set(PICO_PLATFORM, rp2350) #define PICO_AUDIO_PWM_MONO_PIN PICO_AUDIO_PWM_L_PIN #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN DEFCON32_BADGE_SRAM_CS_PIN +#endif + +// PSRAM not fitted by default, so auto-detect +#ifndef PICO_AUTO_DETECT_PSRAM_SIZE +#define PICO_AUTO_DETECT_PSRAM_SIZE 1 +#endif + // --- FLASH --- #define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 diff --git a/src/boards/include/boards/pimoroni_pga2350.h b/src/boards/include/boards/pimoroni_pga2350.h index 7849444f5..fb13c24ed 100644 --- a/src/boards/include/boards/pimoroni_pga2350.h +++ b/src/boards/include/boards/pimoroni_pga2350.h @@ -84,6 +84,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN PIMORONI_PGA2350_PSRAM_CS_PIN +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + // no PICO_SMPS_MODE_PIN // no PICO_VBUS_PIN // no PICO_VSYS_PIN diff --git a/src/boards/include/boards/pimoroni_pico_plus2_rp2350.h b/src/boards/include/boards/pimoroni_pico_plus2_rp2350.h index 6cae74b11..f3aacadfa 100644 --- a/src/boards/include/boards/pimoroni_pico_plus2_rp2350.h +++ b/src/boards/include/boards/pimoroni_pico_plus2_rp2350.h @@ -91,6 +91,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN PIMORONI_PICO_PLUS2_PSRAM_CS_PIN +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + // The GPIO Pin used to read VBUS to determine if the device is battery powered. #ifndef PICO_VBUS_PIN #define PICO_VBUS_PIN 24 diff --git a/src/boards/include/boards/pimoroni_pico_plus2_w_rp2350.h b/src/boards/include/boards/pimoroni_pico_plus2_w_rp2350.h index 7f125aaa0..d8a815635 100644 --- a/src/boards/include/boards/pimoroni_pico_plus2_w_rp2350.h +++ b/src/boards/include/boards/pimoroni_pico_plus2_w_rp2350.h @@ -83,6 +83,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN PIMORONI_PICO_PLUS2_W_PSRAM_CS_PIN +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + // The GPIO Pin used to monitor VSYS. Typically you would use this with ADC. // There is an example in adc/read_vsys in pico-examples. #ifndef PICO_VSYS_PIN diff --git a/src/boards/include/boards/sparkfun_iotnode_lorawan_rp2350.h b/src/boards/include/boards/sparkfun_iotnode_lorawan_rp2350.h index e0e329786..f410f0dc5 100644 --- a/src/boards/include/boards/sparkfun_iotnode_lorawan_rp2350.h +++ b/src/boards/include/boards/sparkfun_iotnode_lorawan_rp2350.h @@ -80,6 +80,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN 0 +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + pico_board_cmake_set_default(PICO_RP2350_A2_SUPPORTED, 1) #ifndef PICO_RP2350_A2_SUPPORTED #define PICO_RP2350_A2_SUPPORTED 1 diff --git a/src/boards/include/boards/sparkfun_iotredboard_rp2350.h b/src/boards/include/boards/sparkfun_iotredboard_rp2350.h index 0c1d425bc..c9249bfa0 100644 --- a/src/boards/include/boards/sparkfun_iotredboard_rp2350.h +++ b/src/boards/include/boards/sparkfun_iotredboard_rp2350.h @@ -90,6 +90,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN SPARKFUN_IOTREDBOARD_RP2350_PSRAM_CS_PIN +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + // The IoT RedBoard has an SD Card. #ifndef PICO_SD_CLK_PIN #define PICO_SD_CLK_PIN 10 diff --git a/src/boards/include/boards/sparkfun_promicro_rp2350.h b/src/boards/include/boards/sparkfun_promicro_rp2350.h index d247e597b..993404518 100644 --- a/src/boards/include/boards/sparkfun_promicro_rp2350.h +++ b/src/boards/include/boards/sparkfun_promicro_rp2350.h @@ -78,6 +78,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN 19 +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + // --- RP2350 VARIANT --- #define PICO_RP2350A 1 diff --git a/src/boards/include/boards/sparkfun_thingplus_rp2350.h b/src/boards/include/boards/sparkfun_thingplus_rp2350.h index 3f0e39a46..6fa09e4ad 100644 --- a/src/boards/include/boards/sparkfun_thingplus_rp2350.h +++ b/src/boards/include/boards/sparkfun_thingplus_rp2350.h @@ -82,6 +82,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN 8 +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + // The thing plus has a SD Card. #ifndef PICO_SD_CLK_PIN #define PICO_SD_CLK_PIN 2 diff --git a/src/boards/include/boards/sparkfun_xrp_controller.h b/src/boards/include/boards/sparkfun_xrp_controller.h index 6b7099559..1030642b7 100644 --- a/src/boards/include/boards/sparkfun_xrp_controller.h +++ b/src/boards/include/boards/sparkfun_xrp_controller.h @@ -82,6 +82,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN 47 +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + #ifndef CYW43_WL_GPIO_COUNT #define CYW43_WL_GPIO_COUNT 3 #endif diff --git a/src/boards/include/boards/weact_studio_rp2350b_core.h b/src/boards/include/boards/weact_studio_rp2350b_core.h index 5e05e24a5..73d492dfb 100644 --- a/src/boards/include/boards/weact_studio_rp2350b_core.h +++ b/src/boards/include/boards/weact_studio_rp2350b_core.h @@ -79,6 +79,16 @@ pico_board_cmake_set_default(PICO_FLASH_SIZE_BYTES, (16 * 1024 * 1024)) #define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) #endif +// --- PSRAM --- +#ifndef PICO_PSRAM_CS_PIN +#define PICO_PSRAM_CS_PIN WEACT_STUDIO_RP2350B_PSRAM_CS_PIN +#endif + +pico_board_cmake_set_default(PICO_PSRAM_SIZE_BYTES, (8 * 1024 * 1024)) +#ifndef PICO_PSRAM_SIZE_BYTES +#define PICO_PSRAM_SIZE_BYTES (8 * 1024 * 1024) +#endif + // --- RP2350 VARIANT --- // This means RP2350B. #define PICO_RP2350A 0 diff --git a/src/cmake/rp2_common.cmake b/src/cmake/rp2_common.cmake index 85a45bc15..b075ee8da 100644 --- a/src/cmake/rp2_common.cmake +++ b/src/cmake/rp2_common.cmake @@ -66,6 +66,7 @@ pico_add_subdirectory(rp2_common/hardware_xosc) if (PICO_COMBINED_DOCS OR NOT PICO_RP2040) pico_add_subdirectory(rp2_common/hardware_powman) + pico_add_subdirectory(rp2_common/hardware_psram) # Note in spite of the name this is usable on Arm as well as RISC-V: pico_add_subdirectory(rp2_common/hardware_riscv_platform_timer) pico_add_subdirectory(rp2_common/hardware_sha256) diff --git a/src/rp2040/pico_platform/memmap_blocked_ram.ld b/src/rp2040/pico_platform/memmap_blocked_ram.ld index 345036962..181bc37dc 100644 --- a/src/rp2040/pico_platform/memmap_blocked_ram.ld +++ b/src/rp2040/pico_platform/memmap_blocked_ram.ld @@ -12,6 +12,8 @@ INCLUDE "memmap_default.incl" * └── memmap_default.incl rp2_common/pico_standard_link * ├── set_memory_locations.incl rp2_common/pico_standard_link * ├── memory_flash.incl rp2_common/pico_standard_link + * ├── memory_psram.incl rp2040/pico_platform + * ├── memory_xip_ram.incl rp2_common/pico_standard_link * ├── memory_ram.incl rp2_common/pico_standard_link * ├── memory_scratch.incl rp2_common/pico_standard_link * ├── memory_generated.incl rp2_common/pico_standard_link @@ -42,6 +44,7 @@ INCLUDE "memmap_default.incl" * ├── section_generated_post_scratch.incl rp2_common/pico_standard_link * ├── section_extra_post_scratch.incl rp2_common/pico_standard_link * ├── sections_stack.incl rp2_common/pico_standard_link + * ├── sections_psram.incl rp2040/pico_platform * ├── section_flash_end.incl rp2_common/pico_standard_link * ├── section_end.incl rp2_common/pico_standard_link * ├── section_generated_post_end.incl rp2_common/pico_standard_link diff --git a/src/rp2040/pico_platform/memmap_copy_to_ram.ld b/src/rp2040/pico_platform/memmap_copy_to_ram.ld index a9e541e8d..b72e59054 100644 --- a/src/rp2040/pico_platform/memmap_copy_to_ram.ld +++ b/src/rp2040/pico_platform/memmap_copy_to_ram.ld @@ -9,6 +9,8 @@ INCLUDE "memmap_copy_to_ram.incl" * └── memmap_copy_to_ram.incl rp2_common/pico_standard_link * ├── set_memory_locations.incl rp2_common/pico_standard_link * ├── memory_flash.incl rp2_common/pico_standard_link + * ├── memory_psram.incl rp2040/pico_platform + * ├── memory_xip_ram.incl rp2_common/pico_standard_link * ├── memory_ram.incl rp2_common/pico_standard_link * ├── memory_scratch.incl rp2_common/pico_standard_link * ├── memory_generated.incl rp2_common/pico_standard_link @@ -38,6 +40,7 @@ INCLUDE "memmap_copy_to_ram.incl" * ├── section_generated_post_scratch.incl rp2_common/pico_standard_link * ├── section_extra_post_scratch.incl rp2_common/pico_standard_link * ├── sections_stack.incl rp2_common/pico_standard_link + * ├── sections_psram.incl rp2040/pico_platform * ├── section_flash_end.incl rp2_common/pico_standard_link * ├── section_end.incl rp2_common/pico_standard_link * ├── section_generated_post_end.incl rp2_common/pico_standard_link diff --git a/src/rp2040/pico_platform/memmap_default.ld b/src/rp2040/pico_platform/memmap_default.ld index f7e561e7c..80dc31092 100644 --- a/src/rp2040/pico_platform/memmap_default.ld +++ b/src/rp2040/pico_platform/memmap_default.ld @@ -9,6 +9,8 @@ INCLUDE "memmap_default.incl" * └── memmap_default.incl rp2_common/pico_standard_link * ├── set_memory_locations.incl rp2_common/pico_standard_link * ├── memory_flash.incl rp2_common/pico_standard_link + * ├── memory_psram.incl rp2040/pico_platform + * ├── memory_xip_ram.incl rp2_common/pico_standard_link * ├── memory_ram.incl rp2_common/pico_standard_link * ├── memory_scratch.incl rp2_common/pico_standard_link * ├── memory_generated.incl rp2_common/pico_standard_link @@ -39,6 +41,7 @@ INCLUDE "memmap_default.incl" * ├── section_generated_post_scratch.incl rp2_common/pico_standard_link * ├── section_extra_post_scratch.incl rp2_common/pico_standard_link * ├── sections_stack.incl rp2_common/pico_standard_link + * ├── sections_psram.incl rp2040/pico_platform * ├── section_flash_end.incl rp2_common/pico_standard_link * ├── section_end.incl rp2_common/pico_standard_link * ├── section_generated_post_end.incl rp2_common/pico_standard_link diff --git a/src/rp2040/pico_platform/memmap_no_flash.ld b/src/rp2040/pico_platform/memmap_no_flash.ld index 9e86b518d..3cfc12d40 100644 --- a/src/rp2040/pico_platform/memmap_no_flash.ld +++ b/src/rp2040/pico_platform/memmap_no_flash.ld @@ -8,6 +8,8 @@ INCLUDE "memmap_no_flash.incl" * memmap_no_flash.ld rp2040/pico_platform * └── memmap_no_flash.incl rp2_common/pico_standard_link * ├── set_memory_locations.incl rp2_common/pico_standard_link + * ├── memory_psram.incl rp2040/pico_platform + * ├── memory_xip_ram.incl rp2_common/pico_standard_link * ├── memory_ram.incl rp2_common/pico_standard_link * ├── memory_scratch.incl rp2_common/pico_standard_link * ├── memory_generated.incl rp2_common/pico_standard_link @@ -33,6 +35,7 @@ INCLUDE "memmap_no_flash.incl" * ├── section_generated_post_scratch.incl rp2_common/pico_standard_link * ├── section_extra_post_scratch.incl rp2_common/pico_standard_link * ├── sections_stack.incl rp2_common/pico_standard_link + * ├── sections_psram.incl rp2040/pico_platform * ├── section_end.incl rp2_common/pico_standard_link * ├── section_generated_post_end.incl rp2_common/pico_standard_link * ├── section_extra_post_end.incl rp2_common/pico_standard_link diff --git a/src/rp2040/pico_platform/script_include/memory_psram.incl b/src/rp2040/pico_platform/script_include/memory_psram.incl new file mode 100644 index 000000000..7613c5e15 --- /dev/null +++ b/src/rp2040/pico_platform/script_include/memory_psram.incl @@ -0,0 +1 @@ +/* RP2040 has no PSRAM support */ diff --git a/src/rp2040/pico_platform/script_include/sections_psram.incl b/src/rp2040/pico_platform/script_include/sections_psram.incl new file mode 100644 index 000000000..7613c5e15 --- /dev/null +++ b/src/rp2040/pico_platform/script_include/sections_psram.incl @@ -0,0 +1 @@ +/* RP2040 has no PSRAM support */ diff --git a/src/rp2350/boot_stage2/boot_stage2.ld b/src/rp2350/boot_stage2/boot_stage2.ld index ee1bce466..ad34a7265 100644 --- a/src/rp2350/boot_stage2/boot_stage2.ld +++ b/src/rp2350/boot_stage2/boot_stage2.ld @@ -1,7 +1,7 @@ MEMORY { /* We are loaded to the top 256 bytes of SRAM, which is above the bootrom - stack. Note 4 bytes occupied by checksum. */ - SRAM(rx) : ORIGIN = 0x20081f00, LENGTH = 252 + stack. */ + SRAM(rx) : ORIGIN = 0x20081f00, LENGTH = 256 } SECTIONS { diff --git a/src/rp2350/pico_platform/BUILD.bazel b/src/rp2350/pico_platform/BUILD.bazel index 4efa080e5..67054882b 100644 --- a/src/rp2350/pico_platform/BUILD.bazel +++ b/src/rp2350/pico_platform/BUILD.bazel @@ -93,6 +93,7 @@ cc_library( "//src/rp2350/pico_platform/script_include:rp2350_linker_scripts", "//src/rp2_common/pico_standard_link/script_include:rp2_linker_scripts", "//src/rp2_common/pico_standard_link:default_flash_region", + "//src/rp2_common/pico_standard_link:default_psram_region", "memmap_default", ], ) @@ -112,6 +113,7 @@ cc_library( "//src/rp2350/pico_platform/script_include:rp2350_linker_scripts", "//src/rp2_common/pico_standard_link/script_include:rp2_linker_scripts", "//src/rp2_common/pico_standard_link:default_flash_region", + "//src/rp2_common/pico_standard_link:default_psram_region", "memmap_copy_to_ram", ], ) @@ -130,6 +132,7 @@ cc_library( "//src/rp2_common/pico_crt0:no_warn_rwx_flag", "//src/rp2350/pico_platform/script_include:rp2350_linker_scripts", "//src/rp2_common/pico_standard_link/script_include:rp2_linker_scripts", + "//src/rp2_common/pico_standard_link:default_psram_region", "memmap_no_flash", ], ) diff --git a/src/rp2350/pico_platform/memmap_copy_to_ram.ld b/src/rp2350/pico_platform/memmap_copy_to_ram.ld index d8c1d8bec..c04e01990 100644 --- a/src/rp2350/pico_platform/memmap_copy_to_ram.ld +++ b/src/rp2350/pico_platform/memmap_copy_to_ram.ld @@ -9,6 +9,8 @@ INCLUDE "memmap_copy_to_ram.incl" * └── memmap_copy_to_ram.incl rp2_common/pico_standard_link * ├── set_memory_locations.incl rp2_common/pico_standard_link * ├── memory_flash.incl rp2_common/pico_standard_link + * ├── memory_psram.incl rp2_common/pico_standard_link + * ├── memory_xip_ram.incl rp2_common/pico_standard_link * ├── memory_ram.incl rp2_common/pico_standard_link * ├── memory_scratch.incl rp2_common/pico_standard_link * ├── memory_generated.incl rp2_common/pico_standard_link @@ -38,6 +40,7 @@ INCLUDE "memmap_copy_to_ram.incl" * ├── section_generated_post_scratch.incl rp2_common/pico_standard_link * ├── section_extra_post_scratch.incl rp2_common/pico_standard_link * ├── sections_stack.incl rp2_common/pico_standard_link + * ├── sections_psram.incl rp2_common/pico_standard_link * ├── section_flash_end.incl rp2_common/pico_standard_link * ├── section_end.incl rp2_common/pico_standard_link * ├── section_generated_post_end.incl rp2_common/pico_standard_link diff --git a/src/rp2350/pico_platform/memmap_default.ld b/src/rp2350/pico_platform/memmap_default.ld index b1d5121df..212529e6c 100644 --- a/src/rp2350/pico_platform/memmap_default.ld +++ b/src/rp2350/pico_platform/memmap_default.ld @@ -9,6 +9,8 @@ INCLUDE "memmap_default.incl" * └── memmap_default.incl rp2_common/pico_standard_link * ├── set_memory_locations.incl rp2_common/pico_standard_link * ├── memory_flash.incl rp2_common/pico_standard_link + * ├── memory_psram.incl rp2_common/pico_standard_link + * ├── memory_xip_ram.incl rp2_common/pico_standard_link * ├── memory_ram.incl rp2_common/pico_standard_link * ├── memory_scratch.incl rp2_common/pico_standard_link * ├── memory_generated.incl rp2_common/pico_standard_link @@ -39,6 +41,7 @@ INCLUDE "memmap_default.incl" * ├── section_generated_post_scratch.incl rp2_common/pico_standard_link * ├── section_extra_post_scratch.incl rp2_common/pico_standard_link * ├── sections_stack.incl rp2_common/pico_standard_link + * ├── sections_psram.incl rp2_common/pico_standard_link * ├── section_flash_end.incl rp2_common/pico_standard_link * ├── section_end.incl rp2_common/pico_standard_link * ├── section_generated_post_end.incl rp2_common/pico_standard_link diff --git a/src/rp2350/pico_platform/memmap_no_flash.ld b/src/rp2350/pico_platform/memmap_no_flash.ld index 34b3a33e7..e97e253c7 100644 --- a/src/rp2350/pico_platform/memmap_no_flash.ld +++ b/src/rp2350/pico_platform/memmap_no_flash.ld @@ -8,6 +8,8 @@ INCLUDE "memmap_no_flash.incl" * memmap_no_flash.ld rp2350/pico_platform * └── memmap_no_flash.incl rp2_common/pico_standard_link * ├── set_memory_locations.incl rp2_common/pico_standard_link + * ├── memory_psram.incl rp2_common/pico_standard_link + * ├── memory_xip_ram.incl rp2_common/pico_standard_link * ├── memory_ram.incl rp2_common/pico_standard_link * ├── memory_scratch.incl rp2_common/pico_standard_link * ├── memory_generated.incl rp2_common/pico_standard_link @@ -33,6 +35,7 @@ INCLUDE "memmap_no_flash.incl" * ├── section_generated_post_scratch.incl rp2_common/pico_standard_link * ├── section_extra_post_scratch.incl rp2_common/pico_standard_link * ├── sections_stack.incl rp2_common/pico_standard_link + * ├── sections_psram.incl rp2_common/pico_standard_link * ├── section_end.incl rp2_common/pico_standard_link * ├── section_generated_post_end.incl rp2_common/pico_standard_link * ├── section_extra_post_end.incl rp2_common/pico_standard_link diff --git a/src/rp2350/rp2350a_interface_pins.json b/src/rp2350/rp2350a_interface_pins.json index 7869d2aad..1a75f607d 100644 --- a/src/rp2350/rp2350a_interface_pins.json +++ b/src/rp2350/rp2350a_interface_pins.json @@ -89,6 +89,13 @@ "B": [15] } } + }, + "QMI": { + "instances": { + "0": { + "CS1N": [0, 8, 19] + } + } } }, "pins": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] diff --git a/src/rp2350/rp2350b_interface_pins.json b/src/rp2350/rp2350b_interface_pins.json index ee90ad27f..c8ed6bb85 100644 --- a/src/rp2350/rp2350b_interface_pins.json +++ b/src/rp2350/rp2350b_interface_pins.json @@ -105,6 +105,13 @@ "B": [39, 47] } } + }, + "QMI": { + "instances": { + "0": { + "CS1N": [0, 8, 19, 47] + } + } } }, "pins": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47] diff --git a/src/rp2_common/hardware_flash/flash.c b/src/rp2_common/hardware_flash/flash.c index fbca8de2c..bcc91bf59 100644 --- a/src/rp2_common/hardware_flash/flash.c +++ b/src/rp2_common/hardware_flash/flash.c @@ -89,6 +89,17 @@ static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) { // application. #if !PICO_RP2040 +static qmi_setup_function_t qmi_cs1_setup_function; + +bool flash_set_qmi_cs1_setup_function(qmi_setup_function_t function) { + if ((void*)function > (void*)SRAM_BASE) { + qmi_cs1_setup_function = function; + return true; + } else { + return false; + } +} + // This is specifically for saving/restoring the registers modified by RP2350 // flash_exit_xip() ROM func, not the entirety of the QMI window state. typedef struct flash_rp2350_qmi_save_state { @@ -104,7 +115,9 @@ static void __no_inline_not_in_flash_func(flash_rp2350_save_qmi_cs1)(flash_rp235 } static void __no_inline_not_in_flash_func(flash_rp2350_restore_qmi_cs1)(const flash_rp2350_qmi_save_state_t *state) { - if (flash_devinfo_get_cs_size(1) == FLASH_DEVINFO_SIZE_NONE) { + if (qmi_cs1_setup_function != NULL) { + qmi_cs1_setup_function(); + } else if (flash_devinfo_get_cs_size(1) == FLASH_DEVINFO_SIZE_NONE) { // Case 1: The RP2350 ROM sets QMI to a clean (03h read) configuration // during flash_exit_xip(), even though when CS1 is not enabled via // FLASH_DEVINFO it does not issue an XIP exit sequence to CS1. In @@ -248,11 +261,11 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con //----------------------------------------------------------------------------- // Lower-level flash access functions -#if !PICO_NO_FLASH // Bitbanging the chip select using IO overrides, in case RAM-resident IRQs // are still running, and the FIFO bottoms out. (the bootrom does the same) -static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) { +static void __no_inline_not_in_flash_func(flash_cs_force)(bool high, uint8_t cs) { #if PICO_RP2040 + (void)cs; uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW; @@ -261,15 +274,16 @@ static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) { IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS ); #else + invalid_params_if(HARDWARE_FLASH, cs > 1); if (high) { - hw_clear_bits(&qmi_hw->direct_csr, QMI_DIRECT_CSR_ASSERT_CS0N_BITS); + hw_clear_bits(&qmi_hw->direct_csr, cs == 0 ? QMI_DIRECT_CSR_ASSERT_CS0N_BITS : QMI_DIRECT_CSR_ASSERT_CS1N_BITS); } else { - hw_set_bits(&qmi_hw->direct_csr, QMI_DIRECT_CSR_ASSERT_CS0N_BITS); + hw_set_bits(&qmi_hw->direct_csr, cs == 0 ? QMI_DIRECT_CSR_ASSERT_CS0N_BITS : QMI_DIRECT_CSR_ASSERT_CS1N_BITS); } #endif } -void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) { +void __no_inline_not_in_flash_func(flash_do_cmd_cs)(const uint8_t *txbuf, uint8_t *rxbuf, size_t count, uint8_t cs) { rom_connect_internal_flash_fn connect_internal_flash_func = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); rom_flash_exit_xip_fn flash_exit_xip_func = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); @@ -282,7 +296,7 @@ void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t * connect_internal_flash_func(); flash_exit_xip_func(); - flash_cs_force(0); + flash_cs_force(0, cs); size_t tx_remaining = count; size_t rx_remaining = count; #if PICO_RP2040 @@ -320,13 +334,12 @@ void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t * } hw_clear_bits(&qmi_hw->direct_csr, QMI_DIRECT_CSR_EN_BITS); #endif - flash_cs_force(1); + flash_cs_force(1, cs); flash_flush_cache_func(); flash_enable_xip_via_boot2(); flash_restore_hardware_state(&state); } -#endif // Use standard RUID command to get a unique identifier for the flash (and // hence the board) diff --git a/src/rp2_common/hardware_flash/include/hardware/flash.h b/src/rp2_common/hardware_flash/include/hardware/flash.h index c34feb48e..37462169b 100644 --- a/src/rp2_common/hardware_flash/include/hardware/flash.h +++ b/src/rp2_common/hardware_flash/include/hardware/flash.h @@ -120,15 +120,14 @@ void flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count) */ void flash_get_unique_id(uint8_t *id_out); -/*! \brief Execute bidirectional flash command +/*! \brief Execute bidirectional QSPI command * \ingroup hardware_flash * - * Low-level function to execute a serial command on a flash device attached + * Low-level function to execute a serial command on a device attached * to the QSPI interface. Bytes are simultaneously transmitted and received * from txbuf and to rxbuf. Therefore, both buffers must be the same length, * count, which is the length of the overall transaction. This is useful for - * reading metadata from the flash chip, such as device ID or SFDP - * parameters. + * reading metadata from the chip, such as device ID or SFDP parameters. * * The XIP cache is flushed following each command, in case flash state * has been modified. Like other hardware_flash functions, the flash is not @@ -138,16 +137,44 @@ void flash_get_unique_id(uint8_t *id_out); * it is recommended that this function only be used to extract flash metadata * during startup, before the main application begins to run: see the * implementation of pico_get_unique_id() for an example of this. + * + * \if rp2040_specific + * On RP2040 the chip select index is ignored, as there is only one chip select. + * \endif + * + * \param txbuf Pointer to a byte buffer which will be transmitted + * \param rxbuf Pointer to a byte buffer where received data will be written. txbuf and rxbuf may be the same buffer. + * \param count Length in bytes of txbuf and of rxbuf + * \param cs Chip select index + */ +void flash_do_cmd_cs(const uint8_t *txbuf, uint8_t *rxbuf, size_t count, uint8_t cs); + +/*! \brief Execute bidirectional flash command on chip select 0 + * \ingroup hardware_flash + * + * See \ref flash_do_cmd_cs for more details. * * \param txbuf Pointer to a byte buffer which will be transmitted to the flash * \param rxbuf Pointer to a byte buffer where data received from the flash will be written. txbuf and rxbuf may be the same buffer. * \param count Length in bytes of txbuf and of rxbuf */ -void flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count); +static inline void flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) { + flash_do_cmd_cs(txbuf, rxbuf, count, 0); +} void flash_flush_cache(void); #if !PICO_RP2040 + +typedef void (*qmi_setup_function_t)(void); +/*! \brief Set the function to be called to setup the QMI CS1 configuration + * \ingroup hardware_flash + * + * \param function The function to be called to setup the QMI CS1 configuration + * \return true if the function was set, false if not (e.g. tried to set a function in flash) + */ +bool flash_set_qmi_cs1_setup_function(qmi_setup_function_t function); + typedef enum { FLASH_DEVINFO_SIZE_NONE = 0x0, FLASH_DEVINFO_SIZE_8K = 0x1, diff --git a/src/rp2_common/hardware_powman/BUILD.bazel b/src/rp2_common/hardware_powman/BUILD.bazel index 8c8971e42..d08cc8a69 100644 --- a/src/rp2_common/hardware_powman/BUILD.bazel +++ b/src/rp2_common/hardware_powman/BUILD.bazel @@ -8,8 +8,6 @@ cc_library( hdrs = ["include/hardware/powman.h"], implementation_deps = ["//src/rp2_common/hardware_gpio"], includes = ["include"], - - # TODO: RP2350 only, but doesn't appear gated in CMake. target_compatible_with = ["//bazel/constraint:rp2350"], deps = [ "//src/rp2_common:hardware_structs", diff --git a/src/rp2_common/hardware_psram/BUILD.bazel b/src/rp2_common/hardware_psram/BUILD.bazel new file mode 100644 index 000000000..4b48306d0 --- /dev/null +++ b/src/rp2_common/hardware_psram/BUILD.bazel @@ -0,0 +1,18 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "hardware_psram", + srcs = ["psram.c"], + hdrs = ["include/hardware/psram.h"], + includes = ["include"], + target_compatible_with = ["//bazel/constraint:rp2350"], + deps = [ + "//src/rp2_common:hardware_structs", + "//src/rp2_common:pico_platform", + "//src/rp2_common/hardware_clocks", + "//src/rp2_common/hardware_flash", + "//src/rp2_common/hardware_gpio", + ], +) diff --git a/src/rp2_common/hardware_psram/CMakeLists.txt b/src/rp2_common/hardware_psram/CMakeLists.txt new file mode 100644 index 000000000..861f419ec --- /dev/null +++ b/src/rp2_common/hardware_psram/CMakeLists.txt @@ -0,0 +1,4 @@ +pico_simple_hardware_target(psram) +pico_mirrored_target_link_libraries(hardware_psram INTERFACE hardware_flash hardware_clocks) + +target_link_libraries(hardware_psram INTERFACE hardware_gpio pico_runtime_init_headers) # not mirrored as only implementation requires it diff --git a/src/rp2_common/hardware_psram/include/hardware/psram.h b/src/rp2_common/hardware_psram/include/hardware/psram.h new file mode 100644 index 000000000..2c3d115dd --- /dev/null +++ b/src/rp2_common/hardware_psram/include/hardware/psram.h @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _HARDWARE_PSRAM_H +#define _HARDWARE_PSRAM_H + +#include "pico.h" +#include "hardware/clocks.h" +#include "hardware/flash.h" + +/** \file psram.h + * \defgroup pico_psram pico_psram + * + * \brief Low level PSRAM setup functions + * + * When using the runtime_init initialisation, this can initialise PSRAM in 3 ways, + * listed from highest to lowest priority: + * 1. If flash_devinfo is setup (e.g. configured in OTP), it will initialise PSRAM with + * the flash_devinfo CS1 size and GPIO + * 2. If `PICO_AUTO_DETECT_PSRAM` is set it will attempt to detect PSRAM size and CS GPIO + * on CS1. This will attempt to use all available CS GPIOs as chip selects, so they + * will be wiggled. By default, it will skip over some which are defined in the board + * header (see `PICO_AUTO_DETECT_PSRAM_CS_SKIP_DEFAULTS`). + * - If the CS GPIO is know and set in `PICO_PSRAM_CS_PIN`, you can just enable + * `PICO_AUTO_DETECT_PSRAM_SIZE` to only detect the size. Some board headers use + * this behaviour if they have variants both with and without PSRAM fitted + * (e.g. adafruit_feather_rp2350) + * 3. If `PICO_PSRAM_SIZE_BYTES` and `PICO_PSRAM_CS_PIN` are set (e.g. configured in the + * board header, or with \ref pico_override_psram_size) it will initialise PSRAM with + * that size and CS GPIO + * + * Only the `PICO_AUTO_DETECT_PSRAM` method will verify that PSRAM is present before + * using it. + * + * Variables can be placed in PSRAM using __psram or __psram_uninitialised macros, and + * you can also write directly to the memory addresses. + * + * If there are variables placed in PSRAM, it will setup XIP to cause bus faults on any + * access to PSRAM addresses greater than the size available. The \ref psram_check_address + * function should be used before accessing variables in PSRAM when auto-detection is on, + * to prevent these bus faults. + * + * Note some of these functions are *unsafe* if you are using both cores, and the other + * is executing from flash or psram concurrently with the operation. In this case, you + * must perform your own synchronisation to make sure that no XIP accesses take + * place while running these functions. One option is to use the + * \ref flash_safe_execute functions in \ref pico_flash. + * + * Likewise they are *unsafe* if you have interrupt handlers or an interrupt + * vector table in flash or psram, so you must disable interrupts before calling in + * this case - \ref flash_safe_execute handles this case too. + * + * The unsafe functions are: + * - \ref psram_reinitialise + * - \ref psram_detect_cs_and_size + * - \ref psram_detect_size + */ + +// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_PSRAM, Enable/disable assertions in the hardware_psram module, type=bool, default=0, group=hardware_psram +#ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_PSRAM +#define PARAM_ASSERTIONS_ENABLED_HARDWARE_PSRAM 0 +#endif + +// PICO_CONFIG: PICO_PSRAM_SIZE_BYTES, size of psram in bytes, type=int, default=Usually provided via board header if psram is present, group=hardware_psram +// PICO_CONFIG: PICO_PSRAM_CS_PIN, chip select pin for psram, type=int, default=Usually provided via board header if psram is present, group=hardware_psram + +// PICO_CONFIG: PICO_AUTO_DETECT_PSRAM, automatically detect if psram is present, type=bool, default=0, group=hardware_psram +#ifndef PICO_AUTO_DETECT_PSRAM +#define PICO_AUTO_DETECT_PSRAM 0 +#endif + +// PICO_CONFIG: PICO_AUTO_DETECT_PSRAM_SIZE, automatically detect psram size, type=bool, default=PICO_AUTO_DETECT_PSRAM, group=hardware_psram +#ifndef PICO_AUTO_DETECT_PSRAM_SIZE +#define PICO_AUTO_DETECT_PSRAM_SIZE PICO_AUTO_DETECT_PSRAM +#endif + +// PICO_CONFIG: PICO_AUTO_DETECT_PSRAM_CS, automatically detect psram chip select pin, type=bool, default=PICO_AUTO_DETECT_PSRAM, group=hardware_psram +#ifndef PICO_AUTO_DETECT_PSRAM_CS +#define PICO_AUTO_DETECT_PSRAM_CS PICO_AUTO_DETECT_PSRAM +#elif PICO_AUTO_DETECT_PSRAM_CS && !PICO_AUTO_DETECT_PSRAM_SIZE +#error "PICO_AUTO_DETECT_PSRAM_SIZE must be set to use PICO_AUTO_DETECT_PSRAM_CS" +#endif + +// PICO_CONFIG: PICO_AUTO_DETECT_PSRAM_CS_SKIP_DEFAULTS, skip any _DEFAULT_ GPIOs defined in the board header when auto-detecting psram chip select pin, type=bool, default=PICO_AUTO_DETECT_PSRAM_CS, group=hardware_psram +#ifndef PICO_AUTO_DETECT_PSRAM_CS_SKIP_DEFAULTS +#define PICO_AUTO_DETECT_PSRAM_CS_SKIP_DEFAULTS PICO_AUTO_DETECT_PSRAM_CS +#endif + +// PICO_CONFIG: PICO_DEFAULT_PSRAM_ID, Default ID of psram used for auto-detection, type=int, default=0x5D, group=hardware_psram +#ifndef PICO_DEFAULT_PSRAM_ID +#define PICO_DEFAULT_PSRAM_ID 0x5D +#endif + +// PICO_CONFIG: PICO_DEFAULT_PSRAM_MAX_FREQ, Default max frequency of psram, type=int, default=133 * MHZ, group=hardware_psram +#ifndef PICO_DEFAULT_PSRAM_MAX_FREQ +#define PICO_DEFAULT_PSRAM_MAX_FREQ 133 * MHZ +#endif + +// PICO_CONFIG: PICO_DEFAULT_PSRAM_MAX_SELECT, Default max select time of psram in ns, type=int, default=8000, group=hardware_psram +#ifndef PICO_DEFAULT_PSRAM_MAX_SELECT +#define PICO_DEFAULT_PSRAM_MAX_SELECT 8000 +#endif + +// PICO_CONFIG: PICO_DEFAULT_PSRAM_MIN_DESELECT, Default min deselect time of psram in ns, type=int, default=18, group=hardware_psram +#ifndef PICO_DEFAULT_PSRAM_MIN_DESELECT +#define PICO_DEFAULT_PSRAM_MIN_DESELECT 18 +#endif + + +// PICO_CONFIG: PICO_RUNTIME_SKIP_INIT_PSRAM, Skip calling of `runtime_init_setup_psram` function during runtime init, type=bool, default=0, group=pico_runtime_init +// PICO_CONFIG: PICO_RUNTIME_NO_INIT_PSRAM, Do not include SDK implementation of `runtime_init_setup_psram` function, type=bool, default=0, group=pico_runtime_init + +#ifndef PICO_RUNTIME_INIT_PSRAM +#define PICO_RUNTIME_INIT_PSRAM "11080" +#endif + +#ifndef PICO_RUNTIME_SKIP_INIT_PSRAM +#define PICO_RUNTIME_SKIP_INIT_PSRAM 0 +#endif + +#ifndef PICO_RUNTIME_NO_INIT_PSRAM +#define PICO_RUNTIME_NO_INIT_PSRAM 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \brief Check if PSRAM is available and initialised + * \ingroup hardware_psram + * + * \return true if PSRAM is available and initialised, false otherwise + */ +bool psram_is_available(void); + +/*! \brief Get the size of the PSRAM + * \ingroup hardware_psram + * + * Retrieve the size of the PSRAM, either from PICO_PSRAM_SIZE_BYTES, + * flash_devinfo, or auto-detection. + * + * \return size of PSRAM in bytes, or 0 if none + */ +size_t psram_get_size(void); + +/*! \brief Check if an address is in available PSRAM + * \ingroup hardware_psram + * + * \return true if the address is in available PSRAM, false otherwise + */ +bool psram_check_address(void* addr); + +/*! \brief Detect PSRAM size + * \ingroup hardware_psram + * + * This will read the ID of the PSRAM chip and return the size based on the ID. + * + * You must configure the GPIO function for the CS pin before calling this function, + * and should also configure the CS GPIO in flash_devinfo to prevent toggling of the + * previously configured GPIO (usually 0, so prints invalid characters to default UART). + * + * \return size of PSRAM, or 0 if none found + */ +size_t psram_detect_size(void); + +/*! \brief Detect PSRAM chip select pin and size + * \ingroup hardware_psram + * + * This runs \ref psram_detect_size() for each CS GPIO in the array in turn, + * and returns the size as soon as a PSRAM chip is detected. + * + * This will setup the CS GPIO using flash_devinfo if PSRAM is found. + * + * \param cs_gpios Array of CS GPIOs to try + * \param num Number of CS GPIOs in the array + * \return size of PSRAM, or 0 if none found + */ +size_t psram_detect_cs_and_size(uint8_t *cs_gpios, size_t num); + +/*! \brief Configure PSRAM timing parameters + * \ingroup hardware_psram + * + * This will calculate and set the PSRAM timing parameters based on the given values. + * + * Note: This will also implement the workaround for RP2350-E14 if PICO_RP2350_A2_SUPPORTED is set. + * + * \param max_psram_freq Maximum frequency of PSRAM + * \param max_select_ns Maximum select time in ns + * \param min_deselect_ns Minimum deselect time in ns + * \return PICO_OK on success, PICO_ERROR_INVALID_ARG if unable to calculate valid parameters + */ +int psram_configure_params(uint32_t max_psram_freq, uint32_t max_select_ns, uint32_t min_deselect_ns); + +/*! \brief Explicitly set PSRAM timing parameters + * \ingroup hardware_psram + * + * This will explicitly set the PSRAM timing parameters to the given values. + * + * This may be necessary if the parameters calculated by \ref psram_configure_params are not suitable. + * + * \param divisor Divisor for PSRAM clock + * \param rxdelay RX delay for PSRAM clock + * \param max_select Maximum select time in multiples of 64 system clocks + * \param min_deselect Minimum deselect time in system clock cycles - ceil(divisor / 2) + * \return PICO_OK on success, PICO_ERROR_INVALID_ARG if any of the parameters are invalid + */ +int psram_set_params(uint32_t divisor, uint32_t rxdelay, uint32_t max_select, uint32_t min_deselect); + +/*! \brief Re-initialise PSRAM + * \ingroup hardware_psram + * + * This will re-initialise the PSRAM with the parameters set by \ref psram_configure_params. + * + * This calls \ref flash_start_xip internally, so will reset any QSPI pads changes you have made. + * + * \return PICO_OK on success, PICO_ERROR_PRECONDITION_NOT_MET if the PSRAM size is not set in + * flash_devinfo or the PSRAM parameters are not set by \ref psram_configure_params or \ref psram_set_params + */ +int psram_reinitialise(void); + +/*! \brief Convert PSRAM EID to size + * \ingroup hardware_psram + * + * This will convert the PSRAM EID to the size in bytes. + * + * This is not intended to be called by the user, but is provided as a weak function so it can + * be overridden if other PSRAM chips are used that have different EID to size mapping. + * + * This is used by \ref psram_detect_size to check the KGD and convert the EID to the size. + * + * \param kgd Known Good Die + * \param eid EID + * \return size of PSRAM in bytes, or 0 if the KGD/EID is not recognised + */ +size_t psram_eid_to_size(uint8_t kgd, uint8_t eid); + +/*! \brief Provide a static PSRAM allocation, or malloc if PSRAM is not available + * \ingroup hardware_psram + * + * This will allocate a static buffer in PSRAM and if available use that, otherwise it will use the heap. + * + * This will fail to compile if PICO_PSRAM_SIZE_BYTES is not set + */ +#define psram_or_malloc(group, type, var, size) static type __psram_uninitialised(group) var##_psram[size]; type* var; if (psram_check_address(var##_psram + size)) { var = (type*)var##_psram; } else { var = (type*)malloc(size * sizeof(type)); } + +/*! \brief Free a buffer if it is not in PSRAM + * \ingroup hardware_psram + * + * This will free the buffer from \ref psram_or_malloc if it was created by malloc + */ +#define psram_or_free(var) if (!psram_check_address(var##_psram)) { free(var); } + +#ifdef __cplusplus +} +#endif + +#endif // _HARDWARE_PSRAM_H diff --git a/src/rp2_common/hardware_psram/psram.c b/src/rp2_common/hardware_psram/psram.c new file mode 100644 index 000000000..688c1dc77 --- /dev/null +++ b/src/rp2_common/hardware_psram/psram.c @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2026 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "hardware/psram.h" +#include +#include "hardware/address_mapped.h" +#include "hardware/clocks.h" +#include "hardware/gpio.h" +#include "hardware/flash.h" +#include "hardware/structs/qmi.h" +#include "hardware/structs/xip_ctrl.h" +#include "pico/runtime_init.h" + +// PSRAM SPI command codes +#define PSRAM_READ_ID_CMD 0x9F +#define PSRAM_QUAD_ENABLE_CMD 0x35 +#define PSRAM_QUAD_READ_CMD 0xEB +#define PSRAM_QUAD_WRITE_CMD 0x38 +#define PSRAM_NOOP_CMD 0xFF + + +size_t __weak psram_eid_to_size(uint8_t kgd, uint8_t eid) { + // Weak function to allow overriding if other PSRAM chips + // have different EID to size mapping + // Currently supports APS6404 and ISSI PSRAM + size_t psram_size = 0; + + if (kgd == PICO_DEFAULT_PSRAM_ID) { // Known Good Die - expects 0x5D + psram_size = 1024 * 1024; // 1 MiB + uint8_t size_id = eid >> 5; + if (size_id == 4) { // == 4 is for ISSI PSRAM + psram_size *= 16; // 16 MiB + } else if (eid == 0x26 || size_id == 2 || size_id == 3) { // == 3 is for ISSI PSRAM + psram_size *= 8; // 8 MiB + } else if (size_id == 1) { + psram_size *= 4; // 4 MiB + } else if (size_id == 0) { + psram_size *= 2; // 2 MiB + } + } + + return psram_size; +} + + +size_t psram_detect_size(void) { + // Save size to restore later + flash_devinfo_size_t prev_size = flash_devinfo_get_cs_size(1); + // Setup with non-zero size, so the bootrom will issue the XIP exit sequence to CS1 + // The GPIO doesn't matter, as the caller must set the correct function for the CS pin + flash_devinfo_set_cs_size(1, FLASH_DEVINFO_SIZE_8K); + + // Read ID command, followed by 7 NOOP commands to get the response + uint8_t txbuffer[8] = { PSRAM_READ_ID_CMD, PSRAM_NOOP_CMD, PSRAM_NOOP_CMD, PSRAM_NOOP_CMD, PSRAM_NOOP_CMD, PSRAM_NOOP_CMD, PSRAM_NOOP_CMD, PSRAM_NOOP_CMD }; + uint8_t rxbuffer[8] = { 0 }; + flash_do_cmd_cs(txbuffer, rxbuffer, sizeof(txbuffer), 1); + + // Restore previous size + flash_devinfo_set_cs_size(1, prev_size); + + uint8_t kgd = rxbuffer[5]; + uint8_t eid = rxbuffer[6]; + + return psram_eid_to_size(kgd, eid); +} + +size_t psram_detect_cs_and_size(uint8_t *cs_gpios, size_t num) { + gpio_function_t prev_funcs[num]; + size_t psram_size = 0; + for (size_t i=0; i < num; i++) { + // Save and clear all CS GPIO functions + uint8_t gpio = cs_gpios[i]; + prev_funcs[i] = gpio_get_function(gpio); + gpio_set_function(gpio, GPIO_FUNC_NULL); + } + for (size_t i=0; i < num; i++) { + uint8_t gpio = cs_gpios[i]; + flash_devinfo_set_cs_gpio(1, gpio); + gpio_set_function(gpio, GPIO_FUNC_XIP_CS1); + psram_size = psram_detect_size(); + if (psram_size > 0) { + // CS GPIO found, so will be left configured in flash_devinfo + break; + } + gpio_set_function(gpio, GPIO_FUNC_NULL); + } + for (size_t i=0; i < num; i++) { + // Restore previous function to all CS GPIOs + uint8_t gpio = cs_gpios[i]; + gpio_set_function(gpio, prev_funcs[i]); + } + + return psram_size; +} + +#if PICO_AUTO_DETECT_PSRAM_CS_SKIP_DEFAULTS +static size_t remove_defaults_from_cs_gpios(uint8_t *cs_gpios, size_t num) { + // To prevent trying to use a pin that is already defined for something + // else by the board header + size_t new_num = num; + for (size_t i=0; i < new_num; i++) { + if ( + #ifdef PICO_DEFAULT_UART_TX_PIN + cs_gpios[i] == PICO_DEFAULT_UART_TX_PIN || + #endif + #ifdef PICO_DEFAULT_UART_RX_PIN + cs_gpios[i] == PICO_DEFAULT_UART_RX_PIN || + #endif + #ifdef PICO_DEFAULT_I2C_SDA_PIN + cs_gpios[i] == PICO_DEFAULT_I2C_SDA_PIN || + #endif + #ifdef PICO_DEFAULT_I2C_SCL_PIN + cs_gpios[i] == PICO_DEFAULT_I2C_SCL_PIN || + #endif + #ifdef PICO_DEFAULT_SPI_SCK_PIN + cs_gpios[i] == PICO_DEFAULT_SPI_SCK_PIN || + #endif + #ifdef PICO_DEFAULT_SPI_TX_PIN + cs_gpios[i] == PICO_DEFAULT_SPI_TX_PIN || + #endif + #ifdef PICO_DEFAULT_SPI_RX_PIN + cs_gpios[i] == PICO_DEFAULT_SPI_RX_PIN || + #endif + #ifdef PICO_DEFAULT_SPI_CSN_PIN + cs_gpios[i] == PICO_DEFAULT_SPI_CSN_PIN || + #endif + #ifdef PICO_DEFAULT_LED_PIN + cs_gpios[i] == PICO_DEFAULT_LED_PIN || + #endif + #ifdef PICO_DEFAULT_WS2812_PIN + cs_gpios[i] == PICO_DEFAULT_WS2812_PIN || + #endif + #ifdef PICO_DEFAULT_WS2812_POWER_PIN + cs_gpios[i] == PICO_DEFAULT_WS2812_POWER_PIN || + #endif + #ifdef CYW43_DEFAULT_PIN_WL_REG_ON + cs_gpios[i] == CYW43_DEFAULT_PIN_WL_REG_ON || + #endif + #ifdef CYW43_DEFAULT_PIN_WL_DATA_OUT + cs_gpios[i] == CYW43_DEFAULT_PIN_WL_DATA_OUT || + #endif + #ifdef CYW43_DEFAULT_PIN_WL_DATA_IN + cs_gpios[i] == CYW43_DEFAULT_PIN_WL_DATA_IN || + #endif + #ifdef CYW43_DEFAULT_PIN_WL_HOST_WAKE + cs_gpios[i] == CYW43_DEFAULT_PIN_WL_HOST_WAKE || + #endif + #ifdef CYW43_DEFAULT_PIN_WL_CLOCK + cs_gpios[i] == CYW43_DEFAULT_PIN_WL_CLOCK || + #endif + #ifdef CYW43_DEFAULT_PIN_WL_CS + cs_gpios[i] == CYW43_DEFAULT_PIN_WL_CS || + #endif + false // so the || works + ) { + // Replace with last valid GPIO in the array + cs_gpios[i] = cs_gpios[new_num-1]; + new_num--; + // Check the same index again, as it has a new value + i--; + } + } + return new_num; +} +#endif + +static uint32_t psram_divisor = 0; +static uint32_t psram_rxdelay = 0; +static uint32_t psram_max_select = 0; +static uint32_t psram_min_deselect = 0; + +int psram_configure_params(uint32_t max_psram_freq, uint32_t max_select_ns, uint32_t min_deselect_ns) { + // Set PSRAM timing for APS6404 + // + // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133MHz. + // So: don't allow running at divisor 1 above 100MHz (because delay of 2 would be too late), + // and add an extra 1 to the rxdelay if the divided clock is > 100MHz (i.e. sys clock > 200MHz). + uint32_t clock_hz = clock_get_hz(clk_sys); + uint32_t divisor = (clock_hz + max_psram_freq - 1) / max_psram_freq; + if (divisor == 1 && clock_hz > 100000000) { + divisor = 2; + } + uint32_t rxdelay = divisor; + if (clock_hz / divisor > 100000000) { + rxdelay += 1; + } + + // - Max select is given in multiples of 64 system clocks. + // - Min deselect is given in system clock cycles - ceil(divisor / 2). + // Requires 64-bit maths as we're working with femtoseconds + uint32_t clock_period_fs = 1000000000000000ull / clock_hz; // 1s = 1000000000000000fs + uint32_t max_select = ((uint64_t)max_select_ns * 1000000ull) / (64ull * (uint64_t)clock_period_fs); // 1ns = 1000000fs + uint32_t min_deselect = (min_deselect_ns * 1000000 + (clock_period_fs - 1)) / clock_period_fs - (divisor + 1) / 2; + + return psram_set_params(divisor, rxdelay, max_select, min_deselect); +} + +int psram_set_params(uint32_t divisor, uint32_t rxdelay, uint32_t max_select, uint32_t min_deselect) +{ + invalid_params_if_and_return(HARDWARE_PSRAM, divisor & ~(QMI_M1_TIMING_CLKDIV_BITS >> QMI_M1_TIMING_CLKDIV_LSB), PICO_ERROR_INVALID_ARG); + invalid_params_if_and_return(HARDWARE_PSRAM, rxdelay & ~(QMI_M1_TIMING_RXDELAY_BITS >> QMI_M1_TIMING_RXDELAY_LSB), PICO_ERROR_INVALID_ARG); + invalid_params_if_and_return(HARDWARE_PSRAM, max_select & ~(QMI_M1_TIMING_MAX_SELECT_BITS >> QMI_M1_TIMING_MAX_SELECT_LSB), PICO_ERROR_INVALID_ARG); + invalid_params_if_and_return(HARDWARE_PSRAM, min_deselect & ~(QMI_M1_TIMING_MIN_DESELECT_BITS >> QMI_M1_TIMING_MIN_DESELECT_LSB), PICO_ERROR_INVALID_ARG); + // Provide method for explicitly setting the PSRAM parameters + psram_divisor = divisor; + psram_rxdelay = rxdelay; + psram_max_select = max_select; + psram_min_deselect = min_deselect; + + return PICO_OK; +} + +static void __no_inline_not_in_flash_func(psram_initialise_internal)(void) { + // Send QUAD_ENABLE command manually, as using flash_do_cmd_cs would call this function again, + // if it has been registered using flash_set_qmi_cs1_setup_function + // Enable direct mode, auto CS1, clkdiv of 10 + qmi_hw->direct_csr = 10 << QMI_DIRECT_CSR_CLKDIV_LSB | \ + QMI_DIRECT_CSR_EN_BITS | \ + QMI_DIRECT_CSR_AUTO_CS1N_BITS; + while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) { + tight_loop_contents(); + } + // Send QUAD_ENABLE command + qmi_hw->direct_tx = PSRAM_QUAD_ENABLE_CMD; + while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) { + tight_loop_contents(); + } + // Disable direct mode + qmi_hw->direct_csr = 0; + + qmi_hw->m[1].timing = 1 << QMI_M1_TIMING_COOLDOWN_LSB | + QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Crossing page boundary would need lower clock speed + psram_max_select << QMI_M1_TIMING_MAX_SELECT_LSB | + psram_min_deselect << QMI_M1_TIMING_MIN_DESELECT_LSB | + psram_rxdelay << QMI_M1_TIMING_RXDELAY_LSB | + psram_divisor << QMI_M1_TIMING_CLKDIV_LSB; + + // Read is all quad, with prefix of PSRAM_CMD_QUAD_READ, 6 wait cycles (so 6*4=24 dummy bits), no suffix + qmi_hw->m[1].rfmt = (QMI_M1_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_PREFIX_WIDTH_LSB | + QMI_M1_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_RFMT_ADDR_WIDTH_LSB | + QMI_M1_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_SUFFIX_WIDTH_LSB | + QMI_M1_RFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_RFMT_DUMMY_WIDTH_LSB | + QMI_M1_RFMT_DATA_WIDTH_VALUE_Q << QMI_M1_RFMT_DATA_WIDTH_LSB | + QMI_M1_RFMT_PREFIX_LEN_VALUE_8 << QMI_M1_RFMT_PREFIX_LEN_LSB | + QMI_M1_RFMT_DUMMY_LEN_VALUE_24 << QMI_M1_RFMT_DUMMY_LEN_LSB | + QMI_M1_RFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_RFMT_SUFFIX_LEN_LSB); + qmi_hw->m[1].rcmd = PSRAM_QUAD_READ_CMD << QMI_M1_RCMD_PREFIX_LSB; + + // Write is all quad, with prefix of PSRAM_CMD_QUAD_WRITE, no dummy, no suffix + qmi_hw->m[1].wfmt = (QMI_M1_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_PREFIX_WIDTH_LSB | + QMI_M1_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_WFMT_ADDR_WIDTH_LSB | + QMI_M1_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_SUFFIX_WIDTH_LSB | + QMI_M1_WFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_WFMT_DUMMY_WIDTH_LSB | + QMI_M1_WFMT_DATA_WIDTH_VALUE_Q << QMI_M1_WFMT_DATA_WIDTH_LSB | + QMI_M1_WFMT_PREFIX_LEN_VALUE_8 << QMI_M1_WFMT_PREFIX_LEN_LSB | + QMI_M1_WFMT_DUMMY_LEN_VALUE_NONE << QMI_M1_WFMT_DUMMY_LEN_LSB | + QMI_M1_WFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_WFMT_SUFFIX_LEN_LSB); + qmi_hw->m[1].wcmd = PSRAM_QUAD_WRITE_CMD << QMI_M1_WCMD_PREFIX_LSB; + + // Enable writes to PSRAM + hw_set_bits(&xip_ctrl_hw->ctrl, XIP_CTRL_WRITABLE_M1_BITS); +} + +static bool psram_initialized = false; + +int psram_reinitialise(void) { + // flash_devinfo must be configured correctly to use this function + invalid_params_if_and_return(HARDWARE_PSRAM, flash_devinfo_get_cs_size(1) == FLASH_DEVINFO_SIZE_NONE, PICO_ERROR_PRECONDITION_NOT_MET); + // psram_configure_params must have been called to set the parameters + invalid_params_if_and_return(HARDWARE_PSRAM, psram_divisor == 0, PICO_ERROR_PRECONDITION_NOT_MET); + +#if PICO_RP2350_A2_SUPPORTED + // Workaround for RP2350-E14, where the bootrom only does this for GPIO 0 instead of the correct CS pin + hw_clear_bits(&pads_bank0_hw->io[flash_devinfo_get_cs_gpio(1)], PADS_BANK0_GPIO0_ISO_BITS); +#endif + + // Register the function to initialise the QMI CS1 configuration + flash_set_qmi_cs1_setup_function(psram_initialise_internal); + + // Call flash_start_xip, which calls psram_initialise_internal + flash_start_xip(); + + psram_initialized = true; + + return PICO_OK; +} + +bool psram_is_available(void) { + return psram_initialized; +} + +size_t psram_get_size(void) { + if (!psram_initialized) { + return 0; + } + return flash_devinfo_size_to_bytes(flash_devinfo_get_cs_size(1)); +} + +bool psram_check_address(void* addr) { + uint32_t offset = (uint32_t)addr - (XIP_BASE + flash_devinfo_size_to_bytes(FLASH_DEVINFO_SIZE_MAX)); + if (offset < psram_get_size()) { + return true; + } else { + return false; + } +} + +#if !PICO_RUNTIME_NO_INIT_PSRAM +#if PICO_AUTO_DETECT_PSRAM_CS +#if PICO_RP2350 +#if PICO_RP2350A +#define PICO_AVAILABLE_CS1_GPIOS {0, 8, 19} +#else +#define PICO_AVAILABLE_CS1_GPIOS {0, 8, 19, 47} +#endif +#else +#error "PICO_AVAILABLE_CS1_GPIOS must be defined for this platform to use PICO_AUTO_DETECT_PSRAM_CS" +#endif +#endif + +void runtime_init_setup_psram(void) { + // Check if flash_devinfo is already configured by OTP (or an earlier runtime_init function, e.g. in kitchen_sink_psram_tiny) + bool __unused flash_devinfo_not_configured = flash_devinfo_get_cs_size(1) == FLASH_DEVINFO_SIZE_NONE; + + // Setup flash_devinfo from compile definitions + #if defined(PICO_PSRAM_CS_PIN) && !PICO_AUTO_DETECT_PSRAM_CS + if (flash_devinfo_not_configured) { + flash_devinfo_set_cs_gpio(1, PICO_PSRAM_CS_PIN); + } + #endif + #if defined(PICO_PSRAM_SIZE_BYTES) && !PICO_AUTO_DETECT_PSRAM_SIZE + if (flash_devinfo_not_configured) { + flash_devinfo_set_cs_size(1, flash_devinfo_bytes_to_size(PICO_PSRAM_SIZE_BYTES)); + } + #endif + + // Setup flash_devinfo with auto-detected size + #if PICO_AUTO_DETECT_PSRAM_SIZE + #if !defined(PICO_PSRAM_CS_PIN) && !PICO_AUTO_DETECT_PSRAM_CS + #error PICO_AUTO_DETECT_PSRAM_SIZE requires a specified PICO_PSRAM_CS_PIN or PICO_AUTO_DETECT_PSRAM_CS + #elif defined(PICO_PSRAM_CS_PIN) + gpio_set_function(PICO_PSRAM_CS_PIN, GPIO_FUNC_XIP_CS1); + #endif + if (flash_devinfo_not_configured) { + // Attempt to auto-detect the PSRAM size + #if PICO_AUTO_DETECT_PSRAM_CS + uint8_t cs_gpios[] = PICO_AVAILABLE_CS1_GPIOS; + size_t num_cs_gpios = count_of(cs_gpios); + #if PICO_AUTO_DETECT_PSRAM_CS_SKIP_DEFAULTS + num_cs_gpios = remove_defaults_from_cs_gpios(cs_gpios, count_of(cs_gpios)); + #endif + size_t psram_size = psram_detect_cs_and_size(cs_gpios, num_cs_gpios); + #else + size_t psram_size = psram_detect_size(); + #endif + // Set flash_devinfo size + if (psram_size > 0) flash_devinfo_set_cs_size(1, flash_devinfo_bytes_to_size(psram_size)); + } + #endif + + // The size has now been configured, so can cache the value + flash_devinfo_size_t psram_flash_devinfo_size = flash_devinfo_get_cs_size(1); + size_t psram_word_size = flash_devinfo_size_to_bytes(psram_flash_devinfo_size) >> 2; // >>2 is /4, for words + psram_initialized = psram_flash_devinfo_size != FLASH_DEVINFO_SIZE_NONE; + + static_assert(FLASH_DEVINFO_SIZE_MAX == FLASH_DEVINFO_SIZE_16M, "expected max region size of 16M"); + extern uint32_t __psram_start__; + extern uint32_t __psram_end__; + uint32_t psram_words = (uint32_t)(&__psram_end__ - &__psram_start__); + if (psram_words > psram_word_size) { + // Setup to bus fault for variables that don't fit in available PSRAM + int clear_regions = 0; // Clear no regions by default + if (psram_flash_devinfo_size == FLASH_DEVINFO_SIZE_NONE) { + clear_regions = 4; // Clear all PSRAM regions + } else if (psram_flash_devinfo_size < FLASH_DEVINFO_SIZE_4M) { + clear_regions = 3; // Clear last 3x 4M regions + // And reduce size of first PSRAM region + qmi_hw->atrans[4] = (1u << psram_flash_devinfo_size) << QMI_ATRANS4_SIZE_LSB; // units are 4 kiB + } else if (psram_flash_devinfo_size == FLASH_DEVINFO_SIZE_4M) { + clear_regions = 3; // Clear last 3x 4M regions + } else if (psram_flash_devinfo_size == FLASH_DEVINFO_SIZE_8M) { + clear_regions = 2; // Clear last 2x 4M regions + } + + for (int i = 8 - clear_regions; i < 8; i++) { + qmi_hw->atrans[i] = 0; + } + } + + if (!psram_initialized) { + return; + } + + // Configure the PSRAM parameters with the default values + int ret = psram_configure_params(PICO_DEFAULT_PSRAM_MAX_FREQ, PICO_DEFAULT_PSRAM_MAX_SELECT, PICO_DEFAULT_PSRAM_MIN_DESELECT); + if (ret != PICO_OK) { + psram_initialized = false; + return; + } + + // Initialise the PSRAM + ret = psram_reinitialise(); + if (ret != PICO_OK) { + psram_initialized = false; + return; + } + + // And load any initialised PSRAM data + extern uint32_t __psram_load_source__; + extern uint32_t __psram_load_start__; + extern uint32_t __psram_load_end__; + uint32_t stored_words = (uint32_t)(&__psram_load_end__ - &__psram_load_start__); + if (stored_words > 0) { + if (stored_words > psram_word_size) { + // Only copy into available PSRAM, to avoid triggering bus faults here, + // they will be triggered later when the variable is accessed + stored_words = psram_word_size; + } + memcpy(&__psram_load_start__, &__psram_load_source__, stored_words * sizeof(uint32_t)); + } +} + +#if defined(PICO_RUNTIME_INIT_PSRAM) && !PICO_RUNTIME_SKIP_INIT_PSRAM +PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_setup_psram, PICO_RUNTIME_INIT_PSRAM); +#endif +#endif diff --git a/src/rp2_common/hardware_sha256/BUILD.bazel b/src/rp2_common/hardware_sha256/BUILD.bazel index 129b82704..f56a7085c 100644 --- a/src/rp2_common/hardware_sha256/BUILD.bazel +++ b/src/rp2_common/hardware_sha256/BUILD.bazel @@ -7,7 +7,6 @@ cc_library( srcs = ["sha256.c"], hdrs = ["include/hardware/sha256.h"], includes = ["include"], - # TODO: RP2350 only, but doesn't appear gated in CMake. target_compatible_with = ["//bazel/constraint:rp2350"], deps = [ "//src/rp2_common:hardware_structs", diff --git a/src/rp2_common/pico_low_power/CMakeLists.txt b/src/rp2_common/pico_low_power/CMakeLists.txt index dac8473ee..ff14fbf64 100644 --- a/src/rp2_common/pico_low_power/CMakeLists.txt +++ b/src/rp2_common/pico_low_power/CMakeLists.txt @@ -40,21 +40,24 @@ function(pico_set_persistent_data_loc TARGET PERSISTENT_DATA_LOC) message(FATAL_ERROR "pico_set_persistent_data_loc is not supported on RP2040") endif() - # Configure override section_persistent_data.incl for the target - configure_file(${PICO_LOW_POWER_CURRENT_PATH}/section_persistent_data.incl.template ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/section_persistent_data.incl @ONLY) - pico_add_linker_script_override_path(${TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}) - if (PERSISTENT_DATA_LOC LESS 0x20000000) # XIP_SRAM, so pin the XIP_SRAM target_compile_definitions(${TARGET} PRIVATE PICO_CRT0_PIN_XIP_SRAM=1) + set(PERSISTENT_DATA_SECTION "XIP_RAM") elseif (PERSISTENT_DATA_LOC LESS 0x20040000) # SRAM0, so heap should come after persistent data pico_set_linker_script_var(${TARGET} HEAP_LOC __persistent_data_end__) + set(PERSISTENT_DATA_SECTION "RAM") elseif(PERSISTENT_DATA_LOC LESS 0x20080000) # SRAM1, so heap should come before persistent data pico_set_linker_script_var(${TARGET} HEAP_LIMIT ${PERSISTENT_DATA_LOC}) + set(PERSISTENT_DATA_SECTION "RAM") else() # Not supported in scratch, as the linker script will overwrite persistent data with scratch data message(FATAL_ERROR "pico_set_persistent_data_loc only supports persistent data in XIP_SRAM or SRAM0-7") endif() + + # Configure override section_persistent_data.incl for the target + configure_file(${PICO_LOW_POWER_CURRENT_PATH}/section_persistent_data.incl.template ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/section_persistent_data.incl @ONLY) + pico_add_linker_script_override_path(${TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}) endfunction() diff --git a/src/rp2_common/pico_low_power/section_persistent_data.incl.template b/src/rp2_common/pico_low_power/section_persistent_data.incl.template index 6f5916939..5bade31ee 100644 --- a/src/rp2_common/pico_low_power/section_persistent_data.incl.template +++ b/src/rp2_common/pico_low_power/section_persistent_data.incl.template @@ -4,6 +4,6 @@ SECTIONS __persistent_data_start__ = .; *(.persistent_data*) . = ALIGN(4); - } + } > @PERSISTENT_DATA_SECTION@ PROVIDE(__persistent_data_end__ = .); } \ No newline at end of file diff --git a/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h b/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h index 591e9e3b2..c53a6ba3c 100644 --- a/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h +++ b/src/rp2_common/pico_platform_sections/include/pico/platform/sections.h @@ -79,6 +79,32 @@ #define __scratch_y(group) __attribute__((section(".scratch_y." group))) #endif +/*! \brief Section attribute macro for placement in PSRAM + * \ingroup pico_platform + * + * PSRAM is commonly used for extra data sections. You can place data in initialised or + * uninitialised PSRAM, depending on how the data is loaded into the PSRAM. + * + * For example a `uint32_t` variable placed in PSRAM + * + * uint32_t __psram("my_group_name") foo = 23; + * + * Or placed in uninitialised PSRAM + * + * uint32_t __psram_uninitialised("my_group_name") foo; + * + * The section attribute is `.psram_initialised.` or `.psram_uninitialised.` + * + * \param group a string suffix to use in the section name to distinguish groups that can be linker + * garbage-collected independently + */ +#ifndef __psram +#define __psram(group) __attribute__((section(".psram_initialised." group))) +#endif +#ifndef __psram_uninitialised +#define __psram_uninitialised(group) __attribute__((section(".psram_uninitialised." group))) +#endif + /*! \brief Section attribute macro for data that is to be left uninitialized * \ingroup pico_platform * diff --git a/src/rp2_common/pico_standard_link/BUILD.bazel b/src/rp2_common/pico_standard_link/BUILD.bazel index 4ebfdd428..ae0c26dde 100644 --- a/src/rp2_common/pico_standard_link/BUILD.bazel +++ b/src/rp2_common/pico_standard_link/BUILD.bazel @@ -1,6 +1,7 @@ load("@rules_cc//cc:cc_library.bzl", "cc_library") load("//bazel:defs.bzl", "compatible_with_rp2") load("pico_flash_region.bzl", "generated_pico_flash_region") +load("pico_psram_region.bzl", "generated_pico_psram_region") package(default_visibility = ["//visibility:public"]) @@ -18,6 +19,11 @@ generated_pico_flash_region( }), ) +generated_pico_psram_region( + name = "default_psram_region", + psram_region_size = 0, +) + # It's possible to set linker scripts globally or on a per-binary basis. # # Setting globally to a custom linker script: diff --git a/src/rp2_common/pico_standard_link/CMakeLists.txt b/src/rp2_common/pico_standard_link/CMakeLists.txt index f4cf41ba3..5c53505cb 100644 --- a/src/rp2_common/pico_standard_link/CMakeLists.txt +++ b/src/rp2_common/pico_standard_link/CMakeLists.txt @@ -101,6 +101,30 @@ if (NOT TARGET pico_standard_link) set_property(TARGET ${TARGET} APPEND PROPERTY PICO_TARGET_LINKER_SCRIPT_OVERRIDE_PATHS "${PATH}") endfunction() + # pico_override_flash_size(TARGET FLASH_SIZE) + # \brief\ Override the FLASH size for a given target + # + # \param\ TARGET The target to override the FLASH size for + # \param\ FLASH_SIZE The FLASH size to set for the target + function(pico_override_flash_size TARGET FLASH_SIZE) + set(PICO_FLASH_SIZE_BYTES_STRING "${FLASH_SIZE}") + configure_file(${PICO_SDK_PATH}/src/rp2_common/pico_standard_link/pico_flash_region.template.ld ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/pico_flash_region.ld) + pico_add_linker_script_override_path(${TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}) + target_compile_definitions(${TARGET} PRIVATE "PICO_FLASH_SIZE_BYTES=${FLASH_SIZE}") + endfunction() + + # pico_override_psram_size(TARGET PSRAM_SIZE) + # \brief\ Override the PSRAM size for a given target + # + # \param\ TARGET The target to override the PSRAM size for + # \param\ PSRAM_SIZE The PSRAM size to set for the target + function(pico_override_psram_size TARGET PSRAM_SIZE) + set(PICO_PSRAM_SIZE_BYTES_STRING "${PSRAM_SIZE}") + configure_file(${PICO_SDK_PATH}/src/rp2_common/pico_standard_link/pico_psram_region.template.ld ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/pico_psram_region.ld) + pico_add_linker_script_override_path(${TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}) + target_compile_definitions(${TARGET} PRIVATE "PICO_PSRAM_SIZE_BYTES=${PSRAM_SIZE}") + endfunction() + # pico_set_binary_type(TARGET TYPE) # \brief\ Set the binary type for the target # @@ -155,11 +179,19 @@ if (NOT TARGET pico_standard_link) # since linker script can handle expressions; may as well leave it as one #math(EXPR PICO_FLASH_SIZE_BYTES_STRING "${PICO_FLASH_SIZE_BYTES}" OUTPUT_FORMAT HEXADECIMAL) set(PICO_FLASH_SIZE_BYTES_STRING "${PICO_FLASH_SIZE_BYTES}") - configure_file(${CMAKE_CURRENT_LIST_DIR}/pico_flash_region.template.ld ${CMAKE_BINARY_DIR}/pico_flash_region.ld) + configure_file(${CMAKE_CURRENT_LIST_DIR}/pico_flash_region.template.ld ${CMAKE_CURRENT_BINARY_DIR}/pico_flash_region.ld) # add include path for linker scripts to find it - list(APPEND PICO_LINKER_SCRIPT_INCLUDE_DIRS ${CMAKE_BINARY_DIR}) + list(APPEND PICO_LINKER_SCRIPT_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) pico_promote_common_scope_vars() + # add PSRAM region if it is enabled + if (PICO_PSRAM_SIZE_BYTES) + set(PICO_PSRAM_SIZE_BYTES_STRING "${PICO_PSRAM_SIZE_BYTES}") + else() + set(PICO_PSRAM_SIZE_BYTES_STRING "0") + endif() + configure_file(${CMAKE_CURRENT_LIST_DIR}/pico_psram_region.template.ld ${CMAKE_CURRENT_BINARY_DIR}/pico_psram_region.ld) + # add override paths from PICO_TARGET_LINKER_SCRIPT_OVERRIDE_PATHS target_link_options(pico_standard_link INTERFACE "LINKER:$<$>:-L$,,-L>>") diff --git a/src/rp2_common/pico_standard_link/pico_psram_region.bzl b/src/rp2_common/pico_standard_link/pico_psram_region.bzl new file mode 100644 index 000000000..8fde1d27c --- /dev/null +++ b/src/rp2_common/pico_standard_link/pico_psram_region.bzl @@ -0,0 +1,36 @@ +"""Utilities for creating psram regions.""" + +load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "use_cpp_toolchain") +load("@rules_cc//cc/common:cc_common.bzl", "cc_common") +load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") + +def _generated_pico_psram_region_impl(ctx): + psram_region_linker_fragment = ctx.actions.declare_file(ctx.label.name + "/ldinclude/pico_psram_region.ld") + link_include_dir = psram_region_linker_fragment.dirname + + file_contents = "\n".join(( + "PSRAM(rwx) : ORIGIN = 0x11000000, LENGTH = " + str(ctx.attr.psram_region_size), + )) + ctx.actions.write(psram_region_linker_fragment, file_contents) + linking_inputs = cc_common.create_linker_input( + owner = ctx.label, + user_link_flags = depset( + direct = ["-L" + str(link_include_dir)], + ), + additional_inputs = depset( + direct = [psram_region_linker_fragment], + ), + ) + return [ + DefaultInfo(files = depset([psram_region_linker_fragment])), + CcInfo(linking_context = cc_common.create_linking_context(linker_inputs = depset(direct = [linking_inputs]))), + ] + +generated_pico_psram_region = rule( + implementation = _generated_pico_psram_region_impl, + attrs = { + "psram_region_size": attr.int(mandatory = True), + }, + toolchains = use_cpp_toolchain(), + fragments = ["cpp"], +) diff --git a/src/rp2_common/pico_standard_link/pico_psram_region.template.ld b/src/rp2_common/pico_standard_link/pico_psram_region.template.ld new file mode 100644 index 000000000..45697f746 --- /dev/null +++ b/src/rp2_common/pico_standard_link/pico_psram_region.template.ld @@ -0,0 +1 @@ +PSRAM(rwx) : ORIGIN = 0x11000000, LENGTH = ${PICO_PSRAM_SIZE_BYTES_STRING} \ No newline at end of file diff --git a/src/rp2_common/pico_standard_link/script_include/memmap_copy_to_ram.incl b/src/rp2_common/pico_standard_link/script_include/memmap_copy_to_ram.incl index f54a3e833..1bf95c867 100644 --- a/src/rp2_common/pico_standard_link/script_include/memmap_copy_to_ram.incl +++ b/src/rp2_common/pico_standard_link/script_include/memmap_copy_to_ram.incl @@ -3,6 +3,8 @@ INCLUDE "set_memory_locations.incl" /* Include memory regions used */ INCLUDE "memory_flash.incl" +INCLUDE "memory_psram.incl" +INCLUDE "memory_xip_ram.incl" INCLUDE "memory_ram.incl" INCLUDE "memory_scratch.incl" INCLUDE "memory_generated.incl" diff --git a/src/rp2_common/pico_standard_link/script_include/memmap_default.incl b/src/rp2_common/pico_standard_link/script_include/memmap_default.incl index 4f3233509..d8e856b3b 100644 --- a/src/rp2_common/pico_standard_link/script_include/memmap_default.incl +++ b/src/rp2_common/pico_standard_link/script_include/memmap_default.incl @@ -3,6 +3,8 @@ INCLUDE "set_memory_locations.incl" /* Include memory regions used */ INCLUDE "memory_flash.incl" +INCLUDE "memory_psram.incl" +INCLUDE "memory_xip_ram.incl" INCLUDE "memory_ram.incl" INCLUDE "memory_scratch.incl" INCLUDE "memory_generated.incl" diff --git a/src/rp2_common/pico_standard_link/script_include/memmap_no_flash.incl b/src/rp2_common/pico_standard_link/script_include/memmap_no_flash.incl index c043470e9..264bf4e63 100644 --- a/src/rp2_common/pico_standard_link/script_include/memmap_no_flash.incl +++ b/src/rp2_common/pico_standard_link/script_include/memmap_no_flash.incl @@ -2,6 +2,8 @@ INCLUDE "set_memory_locations.incl" /* Include memory regions used */ +INCLUDE "memory_psram.incl" +INCLUDE "memory_xip_ram.incl" INCLUDE "memory_ram.incl" INCLUDE "memory_scratch.incl" INCLUDE "memory_generated.incl" diff --git a/src/rp2_common/pico_standard_link/script_include/memory_aliases_flash.incl b/src/rp2_common/pico_standard_link/script_include/memory_aliases_flash.incl index 370b63ed1..ea2b1bd49 100644 --- a/src/rp2_common/pico_standard_link/script_include/memory_aliases_flash.incl +++ b/src/rp2_common/pico_standard_link/script_include/memory_aliases_flash.incl @@ -2,5 +2,6 @@ REGION_ALIAS("TEXT_STORE", FLASH); REGION_ALIAS("RAM_STORE", FLASH); +REGION_ALIAS("PSRAM_STORE", FLASH); REGION_ALIAS("SCRATCH_X_STORE", FLASH); REGION_ALIAS("SCRATCH_Y_STORE", FLASH); diff --git a/src/rp2_common/pico_standard_link/script_include/memory_aliases_no_flash.incl b/src/rp2_common/pico_standard_link/script_include/memory_aliases_no_flash.incl index 258072736..27505d4ca 100644 --- a/src/rp2_common/pico_standard_link/script_include/memory_aliases_no_flash.incl +++ b/src/rp2_common/pico_standard_link/script_include/memory_aliases_no_flash.incl @@ -2,5 +2,6 @@ REGION_ALIAS("TEXT_STORE", RAM); REGION_ALIAS("RAM_STORE", RAM); +REGION_ALIAS("PSRAM_STORE", RAM); /* Can't store in PSRAM */ REGION_ALIAS("SCRATCH_X_STORE", SCRATCH_X); REGION_ALIAS("SCRATCH_Y_STORE", SCRATCH_Y); diff --git a/src/rp2_common/pico_standard_link/script_include/memory_psram.incl b/src/rp2_common/pico_standard_link/script_include/memory_psram.incl new file mode 100644 index 000000000..d177e8946 --- /dev/null +++ b/src/rp2_common/pico_standard_link/script_include/memory_psram.incl @@ -0,0 +1,4 @@ +MEMORY +{ + INCLUDE "pico_psram_region.ld" +} diff --git a/src/rp2_common/pico_standard_link/script_include/sections_copy_to_ram.incl b/src/rp2_common/pico_standard_link/script_include/sections_copy_to_ram.incl index 0c3c7d42e..c6e586b25 100644 --- a/src/rp2_common/pico_standard_link/script_include/sections_copy_to_ram.incl +++ b/src/rp2_common/pico_standard_link/script_include/sections_copy_to_ram.incl @@ -11,6 +11,7 @@ INCLUDE "sections_scratch.incl" INCLUDE "section_generated_post_scratch.incl" INCLUDE "section_extra_post_scratch.incl" INCLUDE "sections_stack.incl" +INCLUDE "sections_psram.incl" INCLUDE "section_flash_end.incl" INCLUDE "section_end.incl" INCLUDE "section_generated_post_end.incl" diff --git a/src/rp2_common/pico_standard_link/script_include/sections_default.incl b/src/rp2_common/pico_standard_link/script_include/sections_default.incl index 4e07b3968..8f15074f8 100644 --- a/src/rp2_common/pico_standard_link/script_include/sections_default.incl +++ b/src/rp2_common/pico_standard_link/script_include/sections_default.incl @@ -11,6 +11,7 @@ INCLUDE "sections_scratch.incl" INCLUDE "section_generated_post_scratch.incl" INCLUDE "section_extra_post_scratch.incl" INCLUDE "sections_stack.incl" +INCLUDE "sections_psram.incl" INCLUDE "section_flash_end.incl" INCLUDE "section_end.incl" INCLUDE "section_generated_post_end.incl" diff --git a/src/rp2_common/pico_standard_link/script_include/sections_no_flash.incl b/src/rp2_common/pico_standard_link/script_include/sections_no_flash.incl index e9ba353df..a7a467e36 100644 --- a/src/rp2_common/pico_standard_link/script_include/sections_no_flash.incl +++ b/src/rp2_common/pico_standard_link/script_include/sections_no_flash.incl @@ -11,6 +11,7 @@ INCLUDE "sections_scratch.incl" INCLUDE "section_generated_post_scratch.incl" INCLUDE "section_extra_post_scratch.incl" INCLUDE "sections_stack.incl" +INCLUDE "sections_psram.incl" INCLUDE "section_end.incl" INCLUDE "section_generated_post_end.incl" INCLUDE "section_extra_post_end.incl" diff --git a/src/rp2_common/pico_standard_link/script_include/sections_psram.incl b/src/rp2_common/pico_standard_link/script_include/sections_psram.incl new file mode 100644 index 000000000..7f986a357 --- /dev/null +++ b/src/rp2_common/pico_standard_link/script_include/sections_psram.incl @@ -0,0 +1,17 @@ +SECTIONS +{ + .psram_load : { + __psram_start__ = .; + __psram_load_start__ = .; + *(SORT(.psram_initialised.*)) + . = ALIGN(4); + __psram_load_end__ = .; + } > PSRAM AT> PSRAM_STORE + __psram_load_source__ = LOADADDR(.psram_load); + + .psram_noload (NOLOAD) : { + *(SORT(.psram_uninitialised.*)) + . = ALIGN(4); + __psram_end__ = .; + } > PSRAM +} diff --git a/test/kitchen_sink/BUILD.bazel b/test/kitchen_sink/BUILD.bazel index de466574b..189eef3be 100644 --- a/test/kitchen_sink/BUILD.bazel +++ b/test/kitchen_sink/BUILD.bazel @@ -2,6 +2,7 @@ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_cc//cc:cc_library.bzl", "cc_library") load("//bazel:defs.bzl", "compatible_with_rp2", "incompatible_with_config", "pico_generate_pio_header") load("//bazel/util:transition.bzl", "kitchen_sink_test_binary", "pico_set_binary_type", "pico_set_linker_script") +load("//src/rp2_common/pico_standard_link:pico_psram_region.bzl", "generated_pico_psram_region") package(default_visibility = ["//visibility:public"]) @@ -86,6 +87,7 @@ cc_library( "//test/pico_test", ] + select({ "//bazel/constraint:rp2350": [ + "//src/rp2_common/hardware_psram", "//src/rp2_common/hardware_sha256", ], "//conditions:default": [ @@ -266,3 +268,39 @@ kitchen_sink_test_binary( mbedtls_config = ":mbedtls_config", target_compatible_with = compatible_with_rp2(), ) + +cc_binary( + name = "kitchen_sink_psram_actual", + testonly = True, + srcs = ["kitchen_sink.c"], + defines = [ + "PICO_AUTO_DETECT_PSRAM=1", + 'PICO_PSRAM_SIZE_BYTES="(8 * 1024 * 1024)"', + ], + tags = ["manual"], # Built via kitchen_sink_psram + deps = [":kitchen_sink_common"], +) + +generated_pico_psram_region( + name = "8M_psram_region", + psram_region_size = (8 * 1024 * 1024), +) + +cc_library( + name = "kitchen_sink_psram_linker_script", + # The order of the linker scripts below is important. + # Prevent buildifier from reordering them. + # buildifier: leave-alone + deps = [ + "//test/kitchen_sink:8M_psram_region", + "//src/rp2_common/pico_standard_link:default_linker_script", + ], +) + +pico_set_linker_script( + name = "kitchen_sink_psram", + testonly = True, + src = ":kitchen_sink_psram_actual", + linker_script = "//test/kitchen_sink:kitchen_sink_psram_linker_script", + target_compatible_with = ["//bazel/constraint:rp2350"], +) diff --git a/test/kitchen_sink/CMakeLists.txt b/test/kitchen_sink/CMakeLists.txt index 999d8ddbc..a01304b99 100644 --- a/test/kitchen_sink/CMakeLists.txt +++ b/test/kitchen_sink/CMakeLists.txt @@ -18,6 +18,7 @@ set(KITCHEN_SINK_LIBS hardware_pio hardware_pll hardware_powman + hardware_psram hardware_pwm hardware_rcp hardware_resets @@ -169,6 +170,70 @@ pico_set_program_name(kitchen_sink "Wombat tentacles") pico_add_extra_outputs(kitchen_sink) target_compile_definitions(kitchen_sink PRIVATE KITCHEN_SINK_ID="regular binary") +if (NOT PICO_RP2040) + add_executable(kitchen_sink_psram_fixed ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + target_link_libraries(kitchen_sink_psram_fixed kitchen_sink_libs kitchen_sink_options) + target_compile_definitions(kitchen_sink_psram_fixed PRIVATE FIXED_PSRAM_SIZE=1) + if (NOT DEFINED PICO_PSRAM_SIZE_BYTES) + # Override the PSRAM size for boards that usually don't have PSRAM to allow + # variables in PSRAM, and auto-detect at runtime if PSRAM is present. + target_compile_definitions(kitchen_sink_psram_fixed PRIVATE PICO_AUTO_DETECT_PSRAM=1) + pico_override_psram_size(kitchen_sink_psram_fixed "(1 * 1024 * 1024)") + endif() + pico_add_extra_outputs(kitchen_sink_psram_fixed) + target_compile_definitions(kitchen_sink_psram_fixed PRIVATE KITCHEN_SINK_ID="psram fixed size binary") + set_target_properties(kitchen_sink_psram_fixed PROPERTIES PICO_TEST_FAILURE_STRING "ERROR:") + + add_executable(kitchen_sink_psram_tiny ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + target_link_libraries(kitchen_sink_psram_tiny kitchen_sink_libs kitchen_sink_options) + target_compile_definitions(kitchen_sink_psram_tiny PRIVATE FIXED_PSRAM_SIZE=1 TINY_PSRAM=1) + # Override the PSRAM size to allow variables in PSRAM + target_compile_definitions(kitchen_sink_psram_tiny PRIVATE PICO_AUTO_DETECT_PSRAM=1) + pico_override_psram_size(kitchen_sink_psram_tiny "(1 * 1024 * 1024)") + pico_add_extra_outputs(kitchen_sink_psram_tiny) + target_compile_definitions(kitchen_sink_psram_tiny PRIVATE KITCHEN_SINK_ID="psram tiny size binary") + # Expected to bus fault, so printing PASSED is a failure + set_target_properties(kitchen_sink_psram_tiny PROPERTIES PICO_TEST_SUCCESS_STRING "psram tiny size binary") + set_target_properties(kitchen_sink_psram_tiny PROPERTIES PICO_TEST_FAILURE_STRING "PASSED") + + add_executable(kitchen_sink_psram_small ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + target_link_libraries(kitchen_sink_psram_small kitchen_sink_libs kitchen_sink_options) + target_compile_definitions(kitchen_sink_psram_small PRIVATE FIXED_PSRAM_SIZE=1 SMALL_PSRAM=1) + # Override the PSRAM size to allow variables in PSRAM + target_compile_definitions(kitchen_sink_psram_small PRIVATE PICO_AUTO_DETECT_PSRAM=1) + pico_override_psram_size(kitchen_sink_psram_small "(1 * 1024 * 1024)") + pico_add_extra_outputs(kitchen_sink_psram_small) + target_compile_definitions(kitchen_sink_psram_small PRIVATE KITCHEN_SINK_ID="psram small size binary") + set_target_properties(kitchen_sink_psram_small PROPERTIES PICO_TEST_FAILURE_STRING "ERROR:") + + add_executable(kitchen_sink_psram_detect ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + target_link_libraries(kitchen_sink_psram_detect kitchen_sink_libs kitchen_sink_options) + target_compile_definitions(kitchen_sink_psram_detect PRIVATE PICO_AUTO_DETECT_PSRAM=1) + pico_add_extra_outputs(kitchen_sink_psram_detect) + target_compile_definitions(kitchen_sink_psram_detect PRIVATE KITCHEN_SINK_ID="psram detect size binary") + set_target_properties(kitchen_sink_psram_detect PROPERTIES PICO_TEST_FAILURE_STRING "ERROR:") + + add_executable(kitchen_sink_no_flash_psram_detect ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + target_link_libraries(kitchen_sink_no_flash_psram_detect kitchen_sink_libs kitchen_sink_options) + target_compile_definitions(kitchen_sink_no_flash_psram_detect PRIVATE PICO_AUTO_DETECT_PSRAM=1) + pico_set_binary_type(kitchen_sink_no_flash_psram_detect no_flash) + pico_add_extra_outputs(kitchen_sink_no_flash_psram_detect) + target_compile_definitions(kitchen_sink_no_flash_psram_detect PRIVATE KITCHEN_SINK_ID="psram detect size no_flash binary") + set_target_properties(kitchen_sink_no_flash_psram_detect PROPERTIES PICO_TEST_FAILURE_STRING "ERROR:") + + add_executable(kitchen_sink_psram_flash_fixed ${CMAKE_CURRENT_LIST_DIR}/kitchen_sink.c) + target_link_libraries(kitchen_sink_psram_flash_fixed kitchen_sink_libs kitchen_sink_options) + target_compile_definitions(kitchen_sink_psram_flash_fixed PRIVATE FIXED_PSRAM_SIZE=1) + # Override the FLASH and PSRAM size to check overriding works + # variables in PSRAM, and auto-detect at runtime if PSRAM is present. + target_compile_definitions(kitchen_sink_psram_flash_fixed PRIVATE PICO_AUTO_DETECT_PSRAM=1) + pico_override_flash_size(kitchen_sink_psram_flash_fixed "(1 * 1024 * 1024)") + pico_override_psram_size(kitchen_sink_psram_flash_fixed "(1 * 1024 * 1024)") + pico_add_extra_outputs(kitchen_sink_psram_flash_fixed) + target_compile_definitions(kitchen_sink_psram_flash_fixed PRIVATE KITCHEN_SINK_ID="psram and flash fixed size binary") + set_target_properties(kitchen_sink_psram_flash_fixed PROPERTIES PICO_TEST_FAILURE_STRING "ERROR:") +endif() + if (TARGET hardware_pio) pico_generate_pio_header(kitchen_sink ${CMAKE_CURRENT_LIST_DIR}/trivial.pio) endif() diff --git a/test/kitchen_sink/kitchen_sink.c b/test/kitchen_sink/kitchen_sink.c index 5a599f7bc..99ed3516d 100644 --- a/test/kitchen_sink/kitchen_sink.c +++ b/test/kitchen_sink/kitchen_sink.c @@ -21,6 +21,11 @@ #if LIB_PICO_AON_TIMER #include "pico/aon_timer.h" #endif +#if !PICO_RP2040 +#include "hardware/flash.h" +#include "hardware/psram.h" +#include "hardware/xip_cache.h" +#endif #else #include KITCHEN_SINK_INCLUDE_HEADER #endif @@ -47,6 +52,35 @@ uint32_t *foo = (uint32_t *) 200; uint32_t dma_to = 0; uint32_t dma_from = 0xaaaa5555; +#ifdef FIXED_PSRAM_SIZE +int __psram("foo") foo_psram = 23; +char __psram_uninitialised("bar") bar_psram[0x8000]; +#if defined(TINY_PSRAM) || defined(SMALL_PSRAM) +void make_tiny_psram(void) { +#if defined(TINY_PSRAM) + // Override flash_devinfo cs size to be tiny, so bar_psram doesn't fit in it + flash_devinfo_set_cs_size(1, FLASH_DEVINFO_SIZE_8K); +#elif defined(SMALL_PSRAM) + // Override flash_devinfo cs size to be small, so bar_psram fits but int_buffer doesn't + flash_devinfo_set_cs_size(1, FLASH_DEVINFO_SIZE_128K); +#endif + // Still auto-detect CS pin, as we don't know that + #if PICO_RP2350 + #if PICO_RP2350A + uint8_t cs_gpios[] = {0, 8, 19}; + #else + uint8_t cs_gpios[] = {0, 8, 19, 47}; + #endif + #else + // Unknown platform, just try 0 + uint8_t cs_gpios[] = {0} + #endif + psram_detect_cs_and_size(cs_gpios, count_of(cs_gpios)); +} +PICO_RUNTIME_INIT_FUNC_RUNTIME(make_tiny_psram, "11000"); +#endif +#endif + void __noinline spiggle(void) { dma_channel_config c = dma_channel_get_default_config(1); channel_config_set_bswap(&c, true); @@ -155,6 +189,91 @@ int main(void) { busy_wait_ms(500); } #endif + +#if PICO_PSRAM_SIZE_BYTES + psram_or_malloc("z0000", char, char_buffer, 0x8000); + memset(char_buffer, 0x55, 0x8000); + printf("char_buffer in %s at %p\n", char_buffer < (char*)SRAM_BASE ? "PSRAM" : "Normal SRAM", char_buffer); + psram_or_free(char_buffer); + psram_or_malloc("z0001", int, int_buffer, 0x8000); + memset(int_buffer, 0x55, 0x8000 * sizeof(int)); + printf("int_buffer in %s at %p\n", int_buffer < (int*)SRAM_BASE ? "PSRAM" : "Normal SRAM", int_buffer); + psram_or_free(int_buffer); +#endif + +#ifdef FIXED_PSRAM_SIZE + if (psram_is_available()) { + printf("PSRAM is available\n"); + memset(bar_psram, 0x55, sizeof(bar_psram)); + printf("foo_psram = %d, bar_psram = %02x\n", foo_psram, bar_psram[0]); + if (foo_psram != 23 || bar_psram[0] != 0x55) { + printf("ERROR: foo_psram = %d, bar_psram = %02x\n", foo_psram, bar_psram[0]); + } + // Make sure the write actually went to PSRAM + xip_cache_clean_all(); + xip_cache_invalidate_all(); + if (foo_psram != 23 || bar_psram[0] != 0x55) { + printf("ERROR: after flush foo_psram = %d, bar_psram = %02x\n", foo_psram, bar_psram[0]); + } + // Check PSRAM still works after flash functions + flash_range_erase(PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE); + if (foo_psram != 23 || bar_psram[0] != 0x55) { + printf("ERROR: after erase foo_psram = %d, bar_psram = %02x\n", foo_psram, bar_psram[0]); + } + foo_psram = 27; + memset(bar_psram, 0xab, sizeof(bar_psram)); + // Make sure the write actually went to PSRAM + xip_cache_clean_all(); + xip_cache_invalidate_all(); + printf("foo_psram = %d, bar_psram = %02x\n", foo_psram, bar_psram[0]); + if (foo_psram != 27 || bar_psram[0] != 0xab) { + printf("ERROR: after program foo_psram = %d, bar_psram = %02x\n", foo_psram, bar_psram[0]); + } + } else { + printf("PSRAM not available\n"); + } +#elif !PICO_RP2040 + if (psram_is_available()) { + printf("PSRAM is available, size = 0x%x\n", psram_get_size()); + size_t psram_size = psram_get_size(); + // Fill each half with different data, to check wrapping isn't ocurring + char *foo_psram = (char*)(XIP_BASE + 0x01000000); + size_t foo_psram_size = psram_size / 2; + char *bar_psram = foo_psram + foo_psram_size; + size_t bar_psram_size = foo_psram_size; + memset(foo_psram, 0x55, foo_psram_size); + memset(bar_psram, 0xab, bar_psram_size); + printf("foo_psram = %02x, bar_psram = %02x\n", foo_psram[0], bar_psram[0]); + if (foo_psram[0] != 0x55 || bar_psram[0] != 0xab) { + printf("ERROR: foo_psram = %02x, bar_psram = %02x\n", foo_psram[0], bar_psram[0]); + } + // Make sure the write actually went to PSRAM + xip_cache_clean_all(); + xip_cache_invalidate_all(); + if (foo_psram[0] != 0x55 || bar_psram[0] != 0xab) { + printf("ERROR: after flush foo_psram = %02x, bar_psram = %02x\n", foo_psram[0], bar_psram[0]); + } + // Check PSRAM still works after flash functions + flash_range_erase(PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE); + if (foo_psram[0] != 0x55 || bar_psram[0] != 0xab) { + printf("ERROR: after erase foo_psram = %02x, bar_psram = %02x\n", foo_psram[0], bar_psram[0]); + } + memset(foo_psram, 0xac, foo_psram_size); + memset(bar_psram, 0x56, bar_psram_size); + // Make sure the write actually went to PSRAM + xip_cache_clean_all(); + xip_cache_invalidate_all(); + printf("foo_psram = %02x, bar_psram = %02x\n", foo_psram[0], bar_psram[0]); + if (foo_psram[0] != 0xac || bar_psram[0] != 0x56) { + printf("ERROR: after program bar_psram = %02x\n", bar_psram[0]); + } + } else { + #if PICO_AUTO_DETECT_PSRAM // Only printout when trying to autodetect + printf("PSRAM not available\n"); + #endif + } +#endif + #ifndef __riscv exception_set_exclusive_handler(SVCALL_EXCEPTION, svc_call); // this should compile as we are Cortex-M diff --git a/tools/bazel_build.py b/tools/bazel_build.py index 7aa18173b..00e812d6f 100755 --- a/tools/bazel_build.py +++ b/tools/bazel_build.py @@ -44,6 +44,7 @@ "//test/kitchen_sink:kitchen_sink_lwip_background", "//test/kitchen_sink:kitchen_sink_ram_section", "//test/kitchen_sink:kitchen_sink_simple_overlay", + "//test/kitchen_sink:kitchen_sink_psram", "//test/pico_divider_test:pico_divider_test", "//test/pico_divider_test:pico_divider_nesting_test", "//test/pico_float_test:pico_double_test", @@ -79,8 +80,10 @@ "//test/pico_float_test:hazard3_test_gen", # No RISC-V on RP2040. "//test/pico_float_test:pico_float_test_hazard3", - # hardware_sha256 doesn't appear to work on RP2040. + # No sha256 on RP2040. "//test/pico_sha256_test:pico_sha256_test", + # No PSRAM on RP2040 + "//test/kitchen_sink:kitchen_sink_psram", ) ), }, @@ -116,10 +119,12 @@ "//test/pico_float_test:hazard3_test_gen", # No RISC-V on RP2040. "//test/pico_float_test:pico_float_test_hazard3", - # hardware_sha256 doesn't appear to work on RP2040. + # No sha256 on RP2040. "//test/pico_sha256_test:pico_sha256_test", # not supported by clang "//test/kitchen_sink:kitchen_sink_simple_overlay", + # No PSRAM on RP2040 + "//test/kitchen_sink:kitchen_sink_psram", ) ), }, @@ -158,8 +163,10 @@ "//test/pico_float_test:hazard3_test_gen", # No RISC-V on RP2040. "//test/pico_float_test:pico_float_test_hazard3", - # hardware_sha256 doesn't appear to work on RP2040. + # No sha256 on RP2040. "//test/pico_sha256_test:pico_sha256_test", + # No PSRAM on RP2040 + "//test/kitchen_sink:kitchen_sink_psram", ) ), }, diff --git a/tools/check_board_header.py b/tools/check_board_header.py index b23ffc169..623a11ce4 100755 --- a/tools/check_board_header.py +++ b/tools/check_board_header.py @@ -37,10 +37,24 @@ def __init__(self, message, errors): 'RP2350B': "src/rp2350/rp2350b_interface_pins.json", } -compulsory_cmake_settings = set(['PICO_PLATFORM']) -compulsory_cmake_default_settings = set(['PICO_FLASH_SIZE_BYTES']) -matching_cmake_default_settings = set(['PICO_FLASH_SIZE_BYTES', 'PICO_RP2350_A2_SUPPORTED']) -compulsory_defines = set(['PICO_FLASH_SIZE_BYTES']) +# names for which we MUST have a pico_board_cmake_set +compulsory_cmake_settings = frozenset(['PICO_PLATFORM']) + +# names for which we MUST have a pico_board_cmake_set_default +compulsory_cmake_default_settings = frozenset(['PICO_FLASH_SIZE_BYTES']) + +# names for which pico_board_cmake_set_default (if present) needs a matching #define (and vice-versa) +matching_cmake_default_settings = frozenset(['PICO_FLASH_SIZE_BYTES', 'PICO_RP2350_A2_SUPPORTED', 'PICO_PSRAM_SIZE_BYTES']) + +# names for which we MUST have a #define +compulsory_defines = frozenset(['PICO_FLASH_SIZE_BYTES']) + +# names where the presence of one #define requires other #defines to be present too +# (this is one-way and not bidirectional, so you'll need multiple entries for mutually-dependent #defines) +linked_defines = { + 'PICO_PSRAM_SIZE_BYTES': frozenset(['PICO_PSRAM_CS_PIN']), + 'PICO_AUTO_DETECT_PSRAM_SIZE': frozenset(['PICO_PSRAM_CS_PIN']), +} DefineType = namedtuple("DefineType", ["name", "value", "resolved_value", "lineno", "has_ifndef"]) @@ -456,6 +470,11 @@ def read_defines_from(header_file, defines_dict): for setting in compulsory_defines: if setting not in defines: errors.append(Exception("{} is missing a #define {}".format(board_header, setting))) + for setting in linked_defines: + if setting in defines: + for linked_define in linked_defines[setting]: + if linked_define not in defines: + errors.append(Exception("{} defines {} but is missing a corresponding #define {}".format(board_header, setting, linked_define))) if chip is None: errors.append(Exception("Couldn't determine chip for {}".format(board_header))) @@ -471,7 +490,7 @@ def read_defines_from(header_file, defines_dict): with open(interfaces_json) as interfaces_fh: interface_pins = json.load(interfaces_fh) allowed_interfaces = interface_pins["interfaces"] - allowed_pins = set(interface_pins["pins"]) + allowed_pins = frozenset(interface_pins["pins"]) # convert instance-keys to integers (allowed by Python but not by JSON) for interface in allowed_interfaces: instances = allowed_interfaces[interface]["instances"] @@ -553,6 +572,24 @@ def read_defines_from(header_file, defines_dict): if (name in cmake_default_settings or name.startswith("PICO_DEFAULT_")) and not define.has_ifndef: errors.append(Exception("{}:{} {} isn't enclosed in an #ifndef {} guard".format(board_header, define.lineno, name, name))) + # check for invalid PSRAM CS pin + if name == "PICO_PSRAM_CS_PIN": + interface = "QMI" + instance_num = 0 + function = "CS1N" + if interface not in allowed_interfaces: + errors.append(Exception("{}:{} {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json))) + continue + if instance_num not in allowed_interfaces[interface]["instances"]: + errors.append(Exception("{}:{} {} is set to an invalid instance {}".format(board_header, instance_define.lineno, instance_define, instance_num))) + continue + interface_instance = allowed_interfaces[interface]["instances"][instance_num] + if function not in interface_instance: + errors.append(Exception("{}:{} {} is defined but {} isn't a valid function for {}".format(board_header, define.lineno, name, function, instance_define))) + continue + if define.resolved_value not in interface_instance[function]: + errors.append(Exception("{}:{} {} is set to {} which isn't a valid pin for {} on {} {}".format(board_header, define.lineno, name, define.resolved_value, function, interface, instance_num))) + if not has_include_guard: errors.append(Exception("{} has no include-guard (expected {})".format(board_header, expected_include_guard))) if not has_board_detection and expected_board_detection != "NONE": diff --git a/tools/memmap_include_tree.py b/tools/memmap_include_tree.py index 807d45c13..6f090c2ae 100755 --- a/tools/memmap_include_tree.py +++ b/tools/memmap_include_tree.py @@ -45,7 +45,7 @@ ) # CMake generates this in the binary dir; skip it so the tree is useful without a build. -IGNORED_INCLUDE_NAMES = frozenset({"pico_flash_region.ld"}) +IGNORED_INCLUDE_NAMES = frozenset({"pico_flash_region.ld", "pico_psram_region.ld"}) # Shared CLI helpers (argparse help strings, colors, doc folding) for memmap_annotate.py CLI_HELP_SDK_ROOT = (