Skip to content

Commit bc2fb20

Browse files
MichaelBellGadgetoid
authored andcommitted
rp2/rp2_flash: Configure optimal flash timings.
Configure flash timings dynamically to match the system clock. Reconfigure timings after flash writes. Changes: ports/rp2/main.c: Set default flash timings. ports/rp2/modmachine.c: Configure optimal flash timings on freq change. ports/rp2/rp2_flash.c: Reconfigure flash when leaving critical section. Signed-off-by: Phil Howard <github@gadgetoid.com>
1 parent 23f045c commit bc2fb20

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

ports/rp2/main.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include <stdio.h>
2828

29+
#include "rp2_flash.h"
2930
#include "py/compile.h"
3031
#include "py/cstack.h"
3132
#include "py/runtime.h"
@@ -94,6 +95,9 @@ int main(int argc, char **argv) {
9495
// Hook for setting up anything that needs to be super early in the boot-up process.
9596
MICROPY_BOARD_STARTUP();
9697

98+
// Set the flash divisor to an appropriate value
99+
rp2_flash_set_timing();
100+
97101
#if MICROPY_HW_ENABLE_PSRAM
98102
size_t psram_size = psram_init(MICROPY_HW_PSRAM_CS_PIN);
99103
#endif

ports/rp2/modmachine.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "modmachine.h"
3333
#include "uart.h"
3434
#include "rp2_psram.h"
35+
#include "rp2_flash.h"
3536
#include "clocks_extra.h"
3637
#include "hardware/pll.h"
3738
#include "hardware/structs/rosc.h"
@@ -95,6 +96,11 @@ static mp_obj_t mp_machine_get_freq(void) {
9596

9697
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
9798
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+
98104
if (!set_sys_clock_khz(freq / 1000, false)) {
99105
mp_raise_ValueError(MP_ERROR_TEXT("cannot change frequency"));
100106
}
@@ -112,6 +118,12 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
112118
}
113119
}
114120
}
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+
115127
#if MICROPY_HW_ENABLE_UART_REPL
116128
setup_default_uart();
117129
mp_uart_init();

ports/rp2/rp2_flash.c

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
#include "hardware/flash.h"
3535
#include "pico/binary_info.h"
3636
#include "rp2_psram.h"
37+
#ifdef PICO_RP2350
38+
#include "hardware/structs/ioqspi.h"
39+
#include "hardware/structs/qmi.h"
40+
#else
41+
#include "hardware/structs/ssi.h"
42+
#endif
3743

3844
#define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE)
3945

@@ -94,6 +100,48 @@ static bool use_multicore_lockout(void) {
94100
;
95101
}
96102

103+
// Function to set the flash divisor to the correct divisor, assumes interrupts disabled
104+
// and core1 locked out if relevant.
105+
static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) {
106+
107+
// Use the minimum divisor assuming a 133MHz flash.
108+
const int max_flash_freq = 133000000;
109+
int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq;
110+
111+
#if PICO_RP2350
112+
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
113+
while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) {
114+
;
115+
}
116+
117+
// RX delay equal to the divisor means sampling at the same time as the next falling edge of SCK after the
118+
// falling edge that generated the data. This is pretty tight at 133MHz but seems to work with the Winbond flash chips.
119+
const int rxdelay = divisor;
120+
qmi_hw->m[0].timing = (1 << QMI_M0_TIMING_COOLDOWN_LSB) |
121+
rxdelay << QMI_M1_TIMING_RXDELAY_LSB |
122+
divisor << QMI_M1_TIMING_CLKDIV_LSB;
123+
124+
// Force a read through XIP to ensure the timing is applied
125+
volatile uint32_t *ptr = (volatile uint32_t *)0x14000000;
126+
(void)*ptr;
127+
#else
128+
// RP2040 SSI hardware only supports even divisors
129+
if (divisor & 1) {
130+
divisor += 1;
131+
}
132+
133+
// Wait for SSI not busy
134+
while (ssi_hw->sr & SSI_SR_BUSY_BITS) {
135+
;
136+
}
137+
138+
// Disable, set the new divisor, and re-enable
139+
hw_clear_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS);
140+
ssi_hw->baudr = divisor;
141+
hw_set_bits(&ssi_hw->ssienr, SSI_SSIENR_SSI_EN_BITS);
142+
#endif
143+
}
144+
97145
// Flash erase and write must run with interrupts disabled and the other core suspended,
98146
// because the XIP bit gets disabled.
99147
static uint32_t begin_critical_flash_section(void) {
@@ -117,8 +165,9 @@ static uint32_t begin_critical_flash_section(void) {
117165
}
118166

119167
static void end_critical_flash_section(uint32_t state) {
168+
// The ROM function to program flash will have reset flash and PSRAM timings to defaults
169+
rp2_flash_set_timing_internal(clock_get_hz(clk_sys));
120170
#if MICROPY_HW_ENABLE_PSRAM
121-
// The ROM function to program flash will reset PSRAM timings to defaults
122171
psram_init(MICROPY_HW_PSRAM_CS_PIN);
123172
#endif
124173
restore_interrupts(state);
@@ -313,3 +362,23 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) {
313362
}
314363
}
315364
#endif
365+
366+
// Modify the flash timing. Ensure flash access is suspended while
367+
// the timings are altered.
368+
void rp2_flash_set_timing_for_freq(int clock_hz) {
369+
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
370+
multicore_lockout_start_blocking();
371+
}
372+
uint32_t state = save_and_disable_interrupts();
373+
374+
rp2_flash_set_timing_internal(clock_hz);
375+
376+
restore_interrupts(state);
377+
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
378+
multicore_lockout_end_blocking();
379+
}
380+
}
381+
382+
void rp2_flash_set_timing(void) {
383+
rp2_flash_set_timing_for_freq(clock_get_hz(clk_sys));
384+
}

ports/rp2/rp2_flash.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2025 Mike Bell
7+
* Phil Howard
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#ifndef MICROPY_INCLUDED_RP2_RP2_FLASH_H
29+
#define MICROPY_INCLUDED_RP2_RP2_FLASH_H
30+
31+
extern void rp2_flash_set_timing_for_freq(int clock_hz);
32+
extern void rp2_flash_set_timing(void);
33+
34+
#endif

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy