Skip to content

Commit 87bc47d

Browse files
committedMar 4, 2025
Merge branch 'feature/psram'
2 parents b4cf82b + bc76694 commit 87bc47d

File tree

8 files changed

+358
-5
lines changed

8 files changed

+358
-5
lines changed
 

‎ports/rp2/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ set(MICROPY_SOURCE_PORT
173173
${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c
174174
)
175175

176+
if(PICO_RP2350)
177+
list(APPEND MICROPY_SOURCE_PORT
178+
rp2_psram.c
179+
)
180+
endif()
181+
176182
set(MICROPY_SOURCE_QSTR
177183
${MICROPY_SOURCE_PY}
178184
${MICROPY_DIR}/shared/readline/readline.c

‎ports/rp2/main.c

+23
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#include <stdio.h>
2828

29+
#include "rp2_psram.h"
30+
#include "rp2_flash.h"
2931
#include "py/compile.h"
3032
#include "py/cstack.h"
3133
#include "py/runtime.h"
@@ -93,6 +95,13 @@ int main(int argc, char **argv) {
9395
// Hook for setting up anything that needs to be super early in the boot-up process.
9496
MICROPY_BOARD_STARTUP();
9597

98+
// Set the flash divisor to an appropriate value
99+
rp2_flash_set_timing();
100+
101+
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
102+
size_t psram_size = psram_init(MICROPY_HW_PSRAM_CS_PIN);
103+
#endif
104+
96105
#if MICROPY_HW_ENABLE_UART_REPL
97106
bi_decl(bi_program_feature("UART REPL"))
98107
setup_default_uart();
@@ -120,7 +129,21 @@ int main(int argc, char **argv) {
120129

121130
// Initialise stack extents and GC heap.
122131
mp_cstack_init_with_top(&__StackTop, &__StackTop - &__StackBottom);
132+
133+
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
134+
if (psram_size) {
135+
#if MICROPY_GC_SPLIT_HEAP
136+
gc_init(&__GcHeapStart, &__GcHeapEnd);
137+
gc_add((void *)PSRAM_LOCATION, (void *)(PSRAM_LOCATION + psram_size));
138+
#else
139+
gc_init((void *)PSRAM_LOCATION, (void *)(PSRAM_LOCATION + psram_size));
140+
#endif
141+
} else {
142+
gc_init(&__GcHeapStart, &__GcHeapEnd);
143+
}
144+
#else
123145
gc_init(&__GcHeapStart, &__GcHeapEnd);
146+
#endif
124147

125148
#if MICROPY_PY_LWIP
126149
// lwIP doesn't allow to reinitialise itself by subsequent calls to this function

‎ports/rp2/modmachine.c

+16
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "mp_usbd.h"
3232
#include "modmachine.h"
3333
#include "uart.h"
34+
#include "rp2_psram.h"
35+
#include "rp2_flash.h"
3436
#include "clocks_extra.h"
3537
#include "hardware/pll.h"
3638
#include "hardware/structs/rosc.h"
@@ -94,6 +96,11 @@ static mp_obj_t mp_machine_get_freq(void) {
9496

9597
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
9698
mp_int_t freq = mp_obj_get_int(args[0]);
99+
100+
// If necessary, increase the flash divider before increasing the clock speed
101+
const int old_freq = clock_get_hz(clk_sys);
102+
rp2_flash_set_timing_for_freq(MAX(freq, old_freq));
103+
97104
if (!set_sys_clock_khz(freq / 1000, false)) {
98105
mp_raise_ValueError(MP_ERROR_TEXT("cannot change frequency"));
99106
}
@@ -111,10 +118,19 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
111118
}
112119
}
113120
}
121+
122+
// If clock speed was reduced, maybe we can reduce the flash divider
123+
if (freq < old_freq) {
124+
rp2_flash_set_timing_for_freq(freq);
125+
}
126+
114127
#if MICROPY_HW_ENABLE_UART_REPL
115128
setup_default_uart();
116129
mp_uart_init();
117130
#endif
131+
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
132+
psram_init(MICROPY_HW_PSRAM_CS_PIN);
133+
#endif
118134
}
119135

120136
static void mp_machine_idle(void) {

‎ports/rp2/mpconfigport.h

+12
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,20 @@
7171
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
7272
#endif
7373

74+
#ifndef MICROPY_HW_ENABLE_PSRAM
75+
#define MICROPY_HW_ENABLE_PSRAM (0)
76+
#endif
77+
7478
// Memory allocation policies
79+
#if MICROPY_HW_ENABLE_PSRAM
80+
#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t
81+
#define MICROPY_ALLOC_GC_STACK_SIZE (1024) // Avoid slowdown when GC stack overflow causes a full sweep of PSRAM-backed heap
82+
#else
7583
#define MICROPY_GC_STACK_ENTRY_TYPE uint16_t
84+
#endif
85+
#ifndef MICROPY_GC_SPLIT_HEAP
86+
#define MICROPY_GC_SPLIT_HEAP MICROPY_HW_ENABLE_PSRAM // whether PSRAM is added to or replaces the heap
87+
#endif
7688
#define MICROPY_ALLOC_PATH_MAX (128)
7789
#define MICROPY_QSTR_BYTES_IN_HASH (1)
7890

‎ports/rp2/rp2_flash.c

+117-5
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,19 @@
2626

2727
#include <string.h>
2828

29+
#include "rp2_psram.h"
2930
#include "py/mphal.h"
3031
#include "py/runtime.h"
3132
#include "extmod/vfs.h"
3233
#include "modrp2.h"
3334
#include "hardware/flash.h"
3435
#include "pico/binary_info.h"
36+
#ifdef PICO_RP2350
37+
#include "hardware/structs/ioqspi.h"
38+
#include "hardware/structs/qmi.h"
39+
#else
40+
#include "hardware/structs/ssi.h"
41+
#endif
3542

3643
#define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE)
3744

@@ -80,16 +87,75 @@ static bool use_multicore_lockout(void) {
8087
;
8188
}
8289

90+
// Function to set the flash divisor to the correct divisor, assumes interrupts disabled
91+
// and core1 locked out if relevant.
92+
static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) {
93+
94+
// Use the minimum divisor assuming a 133MHz flash.
95+
const int max_flash_freq = 133000000;
96+
int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq;
97+
98+
#if PICO_RP2350
99+
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
100+
while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) {
101+
;
102+
}
103+
104+
// RX delay equal to the divisor means sampling at the same time as the next falling edge of SCK after the
105+
// falling edge that generated the data. This is pretty tight at 133MHz but seems to work with the Winbond flash chips.
106+
const int rxdelay = divisor;
107+
qmi_hw->m[0].timing = (1 << QMI_M0_TIMING_COOLDOWN_LSB) |
108+
rxdelay << QMI_M1_TIMING_RXDELAY_LSB |
109+
divisor << QMI_M1_TIMING_CLKDIV_LSB;
110+
111+
// Force a read through XIP to ensure the timing is applied
112+
volatile uint32_t *ptr = (volatile uint32_t *)0x14000000;
113+
(void)*ptr;
114+
#else
115+
// RP2040 SSI hardware only supports even divisors
116+
if (divisor & 1) {
117+
divisor += 1;
118+
}
119+
120+
// Wait for SSI not busy
121+
while (ssi_hw->sr & SSI_SR_BUSY_BITS) {
122+
;
123+
}
124+
125+
// Disable, set the new divisor, and re-enable
126+
hw_clear_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS);
127+
ssi_hw->baudr = divisor;
128+
hw_set_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS);
129+
#endif
130+
}
131+
83132
// Flash erase and write must run with interrupts disabled and the other core suspended,
84133
// because the XIP bit gets disabled.
85134
static uint32_t begin_critical_flash_section(void) {
86135
if (use_multicore_lockout()) {
87136
multicore_lockout_start_blocking();
88137
}
89-
return save_and_disable_interrupts();
138+
uint32_t state = save_and_disable_interrupts();
139+
140+
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
141+
// We're about to invalidate the XIP cache, clean it first to commit any dirty writes to PSRAM
142+
// Use the upper 16k of the maintenance space (0x1bffc000 through 0x1bffffff) to workaround
143+
// incorrect behaviour of the XIP clean operation, where it also alters the tag of the associated
144+
// cache line: https://forums.raspberrypi.com/viewtopic.php?t=378249#p2263677
145+
volatile uint8_t *maintenance_ptr = (volatile uint8_t *)(XIP_SRAM_BASE + (XIP_MAINTENANCE_BASE - XIP_BASE));
146+
for (int i = 1; i < 16 * 1024; i += 8) {
147+
maintenance_ptr[i] = 0;
148+
}
149+
#endif
150+
151+
return state;
90152
}
91153

92154
static void end_critical_flash_section(uint32_t state) {
155+
rp2_flash_set_timing_internal(clock_get_hz(clk_sys));
156+
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
157+
psram_init(MICROPY_HW_PSRAM_CS_PIN);
158+
#endif
93159
restore_interrupts(state);
94160
if (use_multicore_lockout()) {
95161
multicore_lockout_end_blocking();
@@ -155,11 +221,16 @@ static mp_obj_t rp2_flash_readblocks(size_t n_args, const mp_obj_t *args) {
155221
}
156222
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_flash_readblocks_obj, 3, 4, rp2_flash_readblocks);
157223

224+
static inline size_t min_size(size_t a, size_t b) {
225+
return a < b ? a : b;
226+
}
227+
158228
static mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
159229
rp2_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
160230
uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES;
161231
mp_buffer_info_t bufinfo;
162232
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
233+
163234
if (n_args == 3) {
164235
mp_uint_t atomic_state = begin_critical_flash_section();
165236
flash_range_erase(self->flash_base + offset, bufinfo.len);
@@ -169,10 +240,31 @@ static mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
169240
} else {
170241
offset += mp_obj_get_int(args[3]);
171242
}
172-
mp_uint_t atomic_state = begin_critical_flash_section();
173-
flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len);
174-
end_critical_flash_section(atomic_state);
175-
mp_event_handle_nowait();
243+
244+
if ((uintptr_t)bufinfo.buf >= SRAM_BASE) {
245+
mp_uint_t atomic_state = begin_critical_flash_section();
246+
flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len);
247+
end_critical_flash_section(atomic_state);
248+
mp_event_handle_nowait();
249+
} else {
250+
size_t bytes_left = bufinfo.len;
251+
size_t bytes_offset = 0;
252+
static uint8_t copy_buffer[BLOCK_SIZE_BYTES] = {0};
253+
254+
while (bytes_left) {
255+
memcpy(copy_buffer, bufinfo.buf + bytes_offset, min_size(bytes_left, BLOCK_SIZE_BYTES));
256+
mp_uint_t atomic_state = begin_critical_flash_section();
257+
flash_range_program(self->flash_base + offset + bytes_offset, copy_buffer, min_size(bytes_left, BLOCK_SIZE_BYTES));
258+
end_critical_flash_section(atomic_state);
259+
bytes_offset += BLOCK_SIZE_BYTES;
260+
if (bytes_left <= BLOCK_SIZE_BYTES) {
261+
break;
262+
}
263+
bytes_left -= BLOCK_SIZE_BYTES;
264+
mp_event_handle_nowait();
265+
}
266+
}
267+
176268
// TODO check return value
177269
return mp_const_none;
178270
}
@@ -220,3 +312,23 @@ MP_DEFINE_CONST_OBJ_TYPE(
220312
make_new, rp2_flash_make_new,
221313
locals_dict, &rp2_flash_locals_dict
222314
);
315+
316+
// Modify the flash timing. Ensure flash access is suspended while
317+
// the timings are altered.
318+
void rp2_flash_set_timing_for_freq(int clock_hz) {
319+
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
320+
multicore_lockout_start_blocking();
321+
}
322+
uint32_t state = save_and_disable_interrupts();
323+
324+
rp2_flash_set_timing_internal(clock_hz);
325+
326+
restore_interrupts(state);
327+
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
328+
multicore_lockout_end_blocking();
329+
}
330+
}
331+
332+
void rp2_flash_set_timing() {
333+
rp2_flash_set_timing_for_freq(clock_get_hz(clk_sys));
334+
}

‎ports/rp2/rp2_flash.h

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef MICROPY_INCLUDED_RP2_MACHINE_FLASH_H
2+
#define MICROPY_INCLUDED_RP2_MACHINE_FLASH_H
3+
4+
extern void rp2_flash_set_timing_for_freq(int clock_hz);
5+
extern void rp2_flash_set_timing();
6+
7+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.