8000 ports/rp2: PSRAM support. · micropython/micropython@37b869b · GitHub
[go: up one dir, main page]

Skip to content

Commit 37b869b

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 8b0bb53 commit 37b869b

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
@@ -165,6 +165,7 @@ set(MICROPY_SOURCE_PORT
165165
pendsv.c
166166
rp2_flash.c
167167
rp2_pio.c
168+
rp2_psram.c
168169
rp2_dma.c
169170
uart.c
170171
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)
0