Skip to content

Commit 325f90a

Browse files
GadgetoidgigapodMichaelBell
committed
ports/rp2: PSRAM support.
Add PSRAM support with auto detection. Performs a best-effort attempt to detect attached PSRAM, configure it and *add* it to the MicroPython heap. If PSRAM is not present, should fall back to use internal RAM. Introduce two new port/board defines: * MICROPY_HW_ENABLE_PSRAM to enable PSRAM. * MICROPY_HW_PSRAM_CS_PIN to define the chip-select pin. Changes: ports/rp2/rp2_psram.c/h: Add new PSRAM module. ports/rp2/main.c: Add optional PSRAM support. ports/rp2/CMakeLists.txt: Include rp2_psram.c. ports/rp2/rp2_flash.c: Add buffered write to avoid reads from PSRAM. ports/rp2/mpconfigport.h: Enable MICROPY_GC_SPLIT_HEAP for boards that set MICROPY_HW_ENABLE_PSRAM. Co-authored-by: Kirk Benell <kirk.benell@sparkfun.com> Co-authored-by: Mike Bell <mike@mercuna.com> Signed-off-by: Phil Howard <phil@gadgetoid.com>
1 parent 48e53eb commit 325f90a

File tree

6 files changed

+246
-5
lines changed

6 files changed

+246
-5
lines changed

ports/rp2/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ set(MICROPY_SOURCE_PORT
164164
pendsv.c
165165
rp2_flash.c
166166
rp2_pio.c
167+
rp2_psram.c
167168
rp2_dma.c
168169
uart.c
169170
usbd.c

ports/rp2/main.c

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

2727
#include <stdio.h>
2828

29+
#include "rp2_psram.h"
2930
#include "py/compile.h"
3031
#include "py/runtime.h"
3132
#include "py/gc.h"
@@ -115,7 +116,14 @@ int main(int argc, char **argv) {
115116
// Initialise stack extents and GC heap.
116117
mp_stack_set_top(&__StackTop);
117118
mp_stack_set_limit(&__StackTop - &__StackBottom - 256);
119+
118120
gc_init(&__GcHeapStart, &__GcHeapEnd);
121+
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
122+
size_t psram_size = psram_init(MICROPY_HW_PSRAM_CS_PIN);
123+
if (psram_size) {
124+
gc_add((void *)PSRAM_LOCATION, (void *)(PSRAM_LOCATION + psram_size));
125+
}
126+
#endif
119127

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

ports/rp2/mpconfigport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373

7474
// Memory allocation policies
7575
#define MICROPY_GC_STACK_ENTRY_TYPE uint16_t
76+
#ifdef MICROPY_HW_ENABLE_PSRAM
77+
#define MICROPY_GC_SPLIT_HEAP (1)
78+
#endif
7679
#define MICROPY_ALLOC_PATH_MAX (128)
7780
#define MICROPY_QSTR_BYTES_IN_HASH (1)
7881

ports/rp2/rp2_flash.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
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"
@@ -76,10 +77,21 @@ static uint32_t begin_critical_flash_section(void) {
7677
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
7778
multicore_lockout_start_blocking();
7879
}
79-
return save_and_disable_interrupts();
80+
uint32_t state = save_and_disable_interrupts();
81+
82+
// We're about to invalidate the XIP cache, clean it first to commit any dirty writes to PSRAM
83+
uint8_t *maintenance_ptr = (uint8_t *)XIP_MAINTENANCE_BASE;
84+
for (int i = 1; i < 16 * 1024; i += 8) {
85+
maintenance_ptr[i] = 0;
86+
}
87+
88+
return state;
8089
}
8190

8291
static void end_critical_flash_section(uint32_t state) {
92+
#if defined(MICROPY_HW_PSRAM_CS_PIN) && MICROPY_HW_ENABLE_PSRAM
93+
psram_init(MICROPY_HW_PSRAM_CS_PIN);
94+
#endif
8395
restore_interrupts(state);
8496
if (multicore_lockout_victim_is_initialized(1 - get_core_num())) {
8597
multicore_lockout_end_blocking();
@@ -145,11 +157,16 @@ static mp_obj_t rp2_flash_readblocks(size_t n_args, const mp_obj_t *args) {
145157
}
146158
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_flash_readblocks_obj, 3, 4, rp2_flash_readblocks);
147159

160+
static inline size_t min_size(size_t a, size_t b) {
161+
return a < b ? a : b;
162+
}
163+
148164
static mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
149165
rp2_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
150166
uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES;
151167
mp_buffer_info_t bufinfo;
152168
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
169+
153170
if (n_args == 3) {
154171
mp_uint_t atomic_state = begin_critical_flash_section();
155172
flash_range_erase(self->flash_base + offset, bufinfo.len);
@@ -159,10 +176,31 @@ static mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
159176
} else {
160177
offset += mp_obj_get_int(args[3]);
161178
}
162-
mp_uint_t atomic_state = begin_critical_flash_section();
163-
flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len);
164-
end_critical_flash_section(atomic_state);
165-
mp_event_handle_nowait();
179+
180+
if ((uintptr_t)bufinfo.buf >= SRAM_BASE) {
181+
mp_uint_t atomic_state = begin_critical_flash_section();
182+
flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len);
183+
end_critical_flash_section(atomic_state);
184+
mp_event_handle_nowait();
185+
} else {
186+
size_t bytes_left = bufinfo.len;
187+
size_t bytes_offset = 0;
188+
static uint8_t copy_buffer[BLOCK_SIZE_BYTES] = {0};
189+
190+
while (bytes_left) {
191+
memcpy(copy_buffer, bufinfo.buf + bytes_offset, min_size(bytes_left, BLOCK_SIZE_BYTES));
192+
mp_uint_t atomic_state = begin_critical_flash_section();
193+
flash_range_program(self->flash_base + offset + bytes_offset, copy_buffer, min_size(bytes_left, BLOCK_SIZE_BYTES));
194+
end_critical_flash_section(atomic_state);
195+
bytes_offset += BLOCK_SIZE_BYTES;
196+
if (bytes_left <= BLOCK_SIZE_BYTES) {
197+
break;
198+
}
199+
bytes_left -= BLOCK_SIZE_BYTES;
200+
mp_event_handle_nowait();
201+
}
202+
}
203+
166204
// TODO check return value
167205
return mp_const_none;
168206
}

ports/rp2/rp2_psram.c

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#include "hardware/structs/ioqspi.h"
2+
#include "hardware/structs/qmi.h"
3+
#include "hardware/structs/xip_ctrl.h"
4+
#include "hardware/sync.h"
5+
#include "rp2_psram.h"
6+
7+
8+
void __no_inline_not_in_flash_func(psram_set_qmi_timing)() {
9+
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
10+
while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) {
11+
;
12+
}
13+
14+
// For > 133 MHz
15+
qmi_hw->m[0].timing = 0x40000202;
16+
17+
// For <= 133 MHz
18+
// qmi_hw->m[0].timing = 0x40000101;
19+
20+
// Force a read through XIP to ensure the timing is applied
21+
volatile uint32_t *ptr = (volatile uint32_t *)0x14000000;
22+
(void)*ptr;
23+
}
24+
25+
size_t __no_inline_not_in_flash_func(psram_detect)() {
26+
int psram_size = 0;
27+
28+
uint32_t intr_stash = save_and_disable_interrupts();
29+
30+
// Try and read the PSRAM ID via direct_csr.
31+
qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS;
32+
33+
// Need to poll for the cooldown on the last XIP transfer to expire
34+
// (via direct-mode BUSY flag) before it is safe to perform the first
35+
// direct-mode operation
36+
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
37+
}
38+
39+
// Exit out of QMI in case we've inited already
40+
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
41+
42+
// Transmit as quad.
43+
qmi_hw->direct_tx = QMI_DIRECT_TX_OE_BITS | QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | 0xf5;
44+
45+
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
46+
}
47+
48+
(void)qmi_hw->direct_rx;
49+
50+
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS);
51+
52+
// Read the id
53+
qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS;
54+
uint8_t kgd = 0;
55+
uint8_t eid = 0;
56+
57+
for (size_t i = 0; i < 7; i++)
58+
{
59+
if (i == 0) {
60+
qmi_hw->direct_tx = 0x9f;
61+
} else {
62+
qmi_hw->direct_tx = 0xff;
63+
}
64+
65+
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS) == 0) {
66+
}
67+
68+
while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) {
69+
}
70+
71+
if (i == 5) {
72+
kgd = qmi_hw->direct_rx;
73+
} else if (i == 6) {
74+
eid = qmi_hw->direct_rx;
75+
} else {
76+
(void)qmi_hw->direct_rx;
77+
}
78+
}
79+
80+
// Disable direct csr.
81+
qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS);
82+
83+
if (kgd == 0x5D) {
84+
psram_size = 1024 * 1024; // 1 MiB
85+
uint8_t size_id = eid >> 5;
86+
if (eid == 0x26 || size_id == 2) {
87+
psram_size *= 8; // 8 MiB
88+
} else if (size_id == 0) {
89+
psram_size *= 2; // 2 MiB
90+
} else if (size_id == 1) {
91+
psram_size *= 4; // 4 MiB
92+
}
93+
}
94+
95+
restore_interrupts(intr_stash);
96+
return psram_size;
97+
}
98+
99+
size_t __no_inline_not_in_flash_func(psram_init)(uint cs_pin) {
100+
gpio_set_function(cs_pin, GPIO_FUNC_XIP_CS1);
101+
102+
size_t psram_size = psram_detect();
103+
104+
if (!psram_size) {
105+
return 0;
106+
}
107+
108+
psram_set_qmi_timing();
109+
110+
// Enable direct mode, PSRAM CS, clkdiv of 10.
111+
qmi_hw->direct_csr = 10 << QMI_DIRECT_CSR_CLKDIV_LSB | \
112+
QMI_DIRECT_CSR_EN_BITS | \
113+
QMI_DIRECT_CSR_AUTO_CS1N_BITS;
114+
while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) {
115+
;
116+
}
117+
118+
// Enable QPI mode on the PSRAM
119+
const uint CMD_QPI_EN = 0x35;
120+
qmi_hw->direct_tx = QMI_DIRECT_TX_NOPUSH_BITS | CMD_QPI_EN;
121+
122+
while (qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) {
123+
;
124+
}
125+
126+
#if 0
127+
// Set PSRAM timing for APS6404:
128+
// - Max select assumes a sys clock speed >= 240MHz
129+
// - Min deselect assumes a sys clock speed <= 305MHz
130+
// - Clkdiv of 2 is OK up to 266MHz.
131+
qmi_hw->m[1].timing = 1 << QMI_M1_TIMING_COOLDOWN_LSB |
132+
QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB |
133+
30 << QMI_M1_TIMING_MAX_SELECT_LSB |
134+
5 << QMI_M1_TIMING_MIN_DESELECT_LSB |
135+
3 << QMI_M1_TIMING_RXDELAY_LSB |
136+
2 << QMI_M1_TIMING_CLKDIV_LSB;
137+
#else
138+
// Set PSRAM timing for APS6404:
139+
// - Max select assumes a sys clock speed >= 120MHz
140+
// - Min deselect assumes a sys clock speed <= 138MHz
141+
// - Clkdiv of 1 is OK up to 133MHz.
142+
qmi_hw->m[1].timing = 1 << QMI_M1_TIMING_COOLDOWN_LSB |
143+
QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB |
144+
15 << QMI_M1_TIMING_MAX_SELECT_LSB |
145+
2 << QMI_M1_TIMING_MIN_DESELECT_LSB |
146+
2 << QMI_M1_TIMING_RXDELAY_LSB |
147+
1 << QMI_M1_TIMING_CLKDIV_LSB;
148+
#endif
149+
150+
// Set PSRAM commands and formats
151+
qmi_hw->m[1].rfmt =
152+
QMI_M0_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_PREFIX_WIDTH_LSB | \
153+
QMI_M0_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M0_RFMT_ADDR_WIDTH_LSB | \
154+
QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_RFMT_SUFFIX_WIDTH_LSB | \
155+
QMI_M0_RFMT_DUMMY_WIDTH_VALUE_Q << QMI_M0_RFMT_DUMMY_WIDTH_LSB | \
156+
QMI_M0_RFMT_DATA_WIDTH_VALUE_Q << QMI_M0_RFMT_DATA_WIDTH_LSB | \
157+
QMI_M0_RFMT_PREFIX_LEN_VALUE_8 << QMI_M0_RFMT_PREFIX_LEN_LSB | \
158+
6 << QMI_M0_RFMT_DUMMY_LEN_LSB;
159+
160+
qmi_hw->m[1].rcmd = 0xEB;
161+
162+
qmi_hw->m[1].wfmt =
163+
QMI_M0_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_PREFIX_WIDTH_LSB | \
164+
QMI_M0_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M0_WFMT_ADDR_WIDTH_LSB | \
165+
QMI_M0_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M0_WFMT_SUFFIX_WIDTH_LSB | \
166+
QMI_M0_WFMT_DUMMY_WIDTH_VALUE_Q << QMI_M0_WFMT_DUMMY_WIDTH_LSB | \
167+
QMI_M0_WFMT_DATA_WIDTH_VALUE_Q << QMI_M0_WFMT_DATA_WIDTH_LSB | \
168+
QMI_M0_WFMT_PREFIX_LEN_VALUE_8 << QMI_M0_WFMT_PREFIX_LEN_LSB;
169+
170+
qmi_hw->m[1].wcmd = 0x38;
171+
172+
// Disable direct mode
173+
qmi_hw->direct_csr = 0;
174+
175+
// Enable writes to PSRAM
176+
hw_set_bits(&xip_ctrl_hw->ctrl, XIP_CTRL_WRITABLE_M1_BITS);
177+
178+
// TODO: Detect PSRAM ID and size
179+
return psram_size;
180+
}

ports/rp2/rp2_psram.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "pico/stdlib.h"
2+
3+
#ifndef MICROPY_INCLUDED_RP2_MACHINE_PSRAM_H
4+
#define MICROPY_INCLUDED_RP2_MACHINE_PSRAM_H
5+
6+
#define PSRAM_LOCATION _u(0x11000000)
7+
8+
extern void psram_set_qmi_timing();
9+
extern size_t psram_init(uint cs_pin);
10+
11+
#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