Skip to content

Commit 885dbec

Browse files
authored
Merge pull request #8357 from eightycc/memorymap
Add memorymap support to RP2 port
2 parents 5f51853 + 0e0941d commit 885dbec

File tree

9 files changed

+257
-29
lines changed

9 files changed

+257
-29
lines changed

locale/circuitpython.pot

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,14 @@ msgstr ""
462462

463463
#: ports/espressif/common-hal/memorymap/AddressRange.c
464464
#: ports/nrf/common-hal/memorymap/AddressRange.c
465+
#: ports/raspberrypi/common-hal/memorymap/AddressRange.c
465466
msgid "Address range not allowed"
466467
msgstr ""
467468

469+
#: shared-bindings/memorymap/AddressRange.c
470+
msgid "Address range wraps around"
471+
msgstr ""
472+
468473
#: ports/espressif/common-hal/canio/CAN.c
469474
msgid "All CAN peripherals are in use"
470475
msgstr ""
@@ -2136,6 +2141,10 @@ msgstr ""
21362141
msgid "UUID value is not str, int or byte buffer"
21372142
msgstr ""
21382143

2144+
#: ports/raspberrypi/common-hal/memorymap/AddressRange.c
2145+
msgid "Unable to access unaligned IO register"
2146+
msgstr ""
2147+
21392148
#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c
21402149
#: ports/atmel-samd/common-hal/audioio/AudioOut.c
21412150
#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c
@@ -2189,14 +2198,14 @@ msgstr ""
21892198
msgid "Unable to start mDNS query"
21902199
msgstr ""
21912200

2192-
#: shared-bindings/memorymap/AddressRange.c
2193-
msgid "Unable to write to address."
2194-
msgstr ""
2195-
21962201
#: shared-bindings/nvm/ByteArray.c
21972202
msgid "Unable to write to nvm."
21982203
msgstr ""
21992204

2205+
#: ports/raspberrypi/common-hal/memorymap/AddressRange.c
2206+
msgid "Unable to write to read-only memory"
2207+
msgstr ""
2208+
22002209
#: shared-bindings/alarm/SleepMemory.c
22012210
msgid "Unable to write to sleep_memory."
22022211
msgstr ""

ports/espressif/common-hal/memorymap/AddressRange.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ size_t common_hal_memorymap_addressrange_get_length(const memorymap_addressrange
6666
return self->len;
6767
}
6868

69-
bool common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_obj_t *self,
69+
void common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_obj_t *self,
7070
size_t start_index, uint8_t *values, size_t len) {
7171
uint8_t *address = self->start_address + start_index;
7272
#pragma GCC diagnostic push
@@ -83,8 +83,6 @@ bool common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_ob
8383
memcpy(address, values, len);
8484
}
8585
#pragma GCC diagnostic pop
86-
87-
return true;
8886
}
8987

9088
void common_hal_memorymap_addressrange_get_bytes(const memorymap_addressrange_obj_t *self,

ports/nrf/common-hal/memorymap/AddressRange.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ void common_hal_memorymap_addressrange_construct(memorymap_addressrange_obj_t *s
9090
self->len = length;
9191
}
9292

93-
uint32_t common_hal_memorymap_addressrange_get_length(const memorymap_addressrange_obj_t *self) {
93+
size_t common_hal_memorymap_addressrange_get_length(const memorymap_addressrange_obj_t *self) {
9494
return self->len;
9595
}
9696

9797

98-
bool common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_obj_t *self,
99-
uint32_t start_index, uint8_t *values, uint32_t len) {
98+
void common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_obj_t *self,
99+
size_t start_index, uint8_t *values, size_t len) {
100100
uint8_t *address = self->start_address + start_index;
101101
#pragma GCC diagnostic push
102102
#pragma GCC diagnostic ignored "-Wcast-align"
@@ -112,12 +112,10 @@ bool common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_ob
112112
memcpy(address, values, len);
113113
}
114114
#pragma GCC diagnostic pop
115-
116-
return true;
117115
}
118116

119117
void common_hal_memorymap_addressrange_get_bytes(const memorymap_addressrange_obj_t *self,
120-
uint32_t start_index, uint32_t len, uint8_t *values) {
118+
size_t start_index, size_t len, uint8_t *values) {
121119
uint8_t *address = self->start_address + start_index;
122120
#pragma GCC diagnostic push
123121
#pragma GCC diagnostic ignored "-Wcast-align"
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 microDev
7+
* Copyright (c) 2023 Bob Abeles
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+
#include <string.h>
29+
30+
#include "shared-bindings/memorymap/AddressRange.h"
31+
32+
#include "py/runtime.h"
33+
34+
#include "hardware/regs/addressmap.h"
35+
36+
// RP2 address map ranges, must be arranged in order by ascending start address
37+
addressmap_rp2_range_t rp2_ranges[] = {
38+
{(uint8_t *)ROM_BASE, 0x00004000, ROM}, // boot ROM
39+
{(uint8_t *)XIP_BASE, 0x00100000, XIP}, // XIP normal cache operation
40+
{(uint8_t *)XIP_NOALLOC_BASE, 0x00100000, XIP}, // XIP check for hit, no update on miss
41+
{(uint8_t *)XIP_NOCACHE_BASE, 0x00100000, XIP}, // XIP don't check for hit, no update on miss
42+
{(uint8_t *)XIP_NOCACHE_NOALLOC_BASE, 0x00100000, XIP}, // XIP bypass cache completely
43+
{(uint8_t *)XIP_CTRL_BASE, 0x00004000, IO}, // XIP control registers
44+
{(uint8_t *)XIP_SRAM_BASE, 0x00004000, SRAM}, // XIP SRAM 16KB XIP cache
45+
{(uint8_t *)XIP_SSI_BASE, 0x00004000, IO}, // XIP SSI registers
46+
{(uint8_t *)SRAM_BASE, 0x00042000, SRAM}, // SRAM 256KB striped plus 16KB contiguous
47+
{(uint8_t *)SRAM0_BASE, 0x00040000, SRAM}, // SRAM0 to SRAM3 256KB non-striped
48+
{(uint8_t *)SYSINFO_BASE, 0x00070000, IO}, // APB peripherals
49+
{(uint8_t *)DMA_BASE, 0x00004000, IO}, // DMA registers
50+
{(uint8_t *)USBCTRL_DPRAM_BASE, 0x00001000, SRAM}, // USB DPSRAM 4KB
51+
{(uint8_t *)USBCTRL_REGS_BASE, 0x00004000, IO}, // USB registers
52+
{(uint8_t *)PIO0_BASE, 0x00004000, IO}, // PIO0 registers
53+
{(uint8_t *)PIO1_BASE, 0x00004000, IO}, // PIO1 registers
54+
{(uint8_t *)SIO_BASE, 0x00001000, IO}, // SIO registers, no aliases
55+
{(uint8_t *)PPB_BASE, 0x00004000, IO} // PPB registers
56+
};
57+
58+
void common_hal_memorymap_addressrange_construct(memorymap_addressrange_obj_t *self,
59+
uint8_t *start_address, size_t length) {
60+
for (size_t i = 0; i < MP_ARRAY_SIZE(rp2_ranges); i++) {
61+
if (start_address <= rp2_ranges[i].start_address) {
62+
uint8_t *range_end_address = rp2_ranges[i].start_address + rp2_ranges[i].len - 1;
63+
uint8_t *end_address = start_address + length - 1;
64+
if (start_address > range_end_address || end_address > range_end_address) {
65+
break;
66+
}
67+
self->start_address = start_address;
68+
self->len = length;
69+
self->type = rp2_ranges[i].type;
70+
return;
71+
}
72+
}
73+
74+
mp_raise_ValueError(translate("Address range not allowed"));
75+
}
76+
77+
size_t common_hal_memorymap_addressrange_get_length(const memorymap_addressrange_obj_t *self) {
78+
return self->len;
79+
}
80+
81+
void common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_obj_t *self,
82+
size_t start_index, uint8_t *values, size_t len) {
83+
uint8_t *dest_addr = self->start_address + start_index;
84+
switch (self->type) {
85+
case SRAM:
86+
// Writes to SRAM may be arbitrary length and alignment. We use memcpy() which
87+
// may optimize aligned writes depending on CIRCUITPY_FULL_BUILD of the CP build.
88+
memcpy(dest_addr, values, len);
89+
break;
90+
case IO:
91+
if ((size_t)dest_addr & 0x03 || len & 0x03) {
92+
// Unaligned access or unaligned length not supported by RP2 for IO registers
93+
mp_raise_RuntimeError(translate("Unable to access unaligned IO register"));
94+
} else {
95+
// Aligned access and length, use 32-bit writes
96+
uint32_t *dest_addr32 = (uint32_t *)dest_addr;
97+
size_t access_count = len >> 2;
98+
for (size_t i = 0; i < access_count; i++) {
99+
*dest_addr32++ = ((uint32_t *)values)[i];
100+
}
101+
}
102+
break;
103+
case XIP:
104+
case ROM:
105+
// XIP and ROM are read-only
106+
mp_raise_RuntimeError(translate("Unable to write to read-only memory"));
107+
break;
108+
}
109+
}
110+
111+
void common_hal_memorymap_addressrange_get_bytes(const memorymap_addressrange_obj_t *self,
112+
size_t start_index, size_t len, uint8_t *values) {
113+
uint8_t *src_addr = self->start_address + start_index;
114+
switch (self->type) {
115+
case SRAM:
116+
case XIP:
117+
case ROM:
118+
// Reads from these sources may be arbitrary length and alignment. We use memcpy()
119+
// which may optimize aligned writes depending on CIRCUITPY_FULL_BUILD of the CP build.
120+
memcpy(values, src_addr, len);
121+
break;
122+
case IO:
123+
if ((size_t)src_addr & 0x03 || len & 0x03) {
124+
// Unaligned access or unaligned length not supported by RP2 for IO registers
125+
mp_raise_RuntimeError(translate("Unable to access unaligned IO register"));
126+
} else {
127+
// Aligned access and length, use 32-bit reads
128+
uint32_t *src_addr32 = (uint32_t *)src_addr;
129+
size_t access_count = len >> 2;
130+
for (size_t i = 0; i < access_count; i++) {
131+
((uint32_t *)values)[i] = *src_addr32++;
132+
}
133+
}
134+
break;
135+
}
136+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 microDev
7+
* Copyright (c) 2023 Bob Abeles
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_RASPBERRYPI_COMMON_HAL_MEMORYMAP_ADDRESSRANGE_H
29+
#define MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MEMORYMAP_ADDRESSRANGE_H
30+
31+
#include "py/obj.h"
32+
33+
// depending on the section memory type, different access methods and rules apply
34+
typedef enum { SRAM, ROM, XIP, IO } memorymap_rp2_section_t;
35+
36+
typedef struct {
37+
mp_obj_base_t base;
38+
uint8_t *start_address;
39+
size_t len;
40+
memorymap_rp2_section_t type;
41+
} memorymap_addressrange_obj_t;
42+
43+
typedef struct {
44+
uint8_t *start_address;
45+
size_t len;
46+
memorymap_rp2_section_t type;
47+
} addressmap_rp2_range_t;
48+
49+
#endif // MICROPY_INCLUDED_RASPBERRYPI_COMMON_HAL_MEMORYMAP_ADDRESSRANGE_H
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// No memorymap module functions.

ports/raspberrypi/mpconfigport.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CIRCUITPY_FULL_BUILD ?= 1
1515
CIRCUITPY_AUDIOMP3 ?= 1
1616
CIRCUITPY_BITOPS ?= 1
1717
CIRCUITPY_IMAGECAPTURE ?= 1
18+
CIRCUITPY_MEMORYMAP ?= 1
1819
CIRCUITPY_PWMIO ?= 1
1920
CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_DISPLAYIO)
2021
CIRCUITPY_ROTARYIO ?= 1

shared-bindings/memorymap/AddressRange.c

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,32 @@
4747
//| import memorymap
4848
//| rtc_slow_mem = memorymap.AddressRange(start=0x50000000, length=0x2000)
4949
//| rtc_slow_mem[0:3] = b"\xcc\x10\x00"
50+
//|
51+
//| Example I/O register usage on RP2040::
52+
//|
53+
//| import binascii
54+
//| import board
55+
//| import digitalio
56+
//| import memorymap
57+
//|
58+
//| def rp2040_set_pad_drive(p, d):
59+
//| pads_bank0 = memorymap.AddressRange(start=0x4001C000, length=0x4000)
60+
//| pad_ctrl = int.from_bytes(pads_bank0[p*4+4:p*4+8], "little")
61+
//| # Pad control register is updated using an MP-safe atomic XOR
62+
//| pad_ctrl ^= (d << 4)
63+
//| pad_ctrl &= 0x00000030
64+
//| pads_bank0[p*4+0x3004:p*4+0x3008] = pad_ctrl.to_bytes(4, "little")
65+
//|
66+
//| def rp2040_get_pad_drive(p):
67+
//| pads_bank0 = memorymap.AddressRange(start=0x4001C000, length=0x4000)
68+
//| pad_ctrl = int.from_bytes(pads_bank0[p*4+4:p*4+8], "little")
69+
//| return (pad_ctrl >> 4) & 0x3
70+
//|
71+
//| # set GPIO16 pad drive strength to 12 mA
72+
//| rp2040_set_pad_drive(16, 3)
73+
//|
74+
//| # print GPIO16 pad drive strength
75+
//| print(rp2040_get_pad_drive(16))
5076
//| """
5177

5278
//| def __init__(self, *, start, length) -> None:
@@ -57,17 +83,29 @@
5783
STATIC mp_obj_t memorymap_addressrange_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
5884
enum { ARG_start, ARG_length };
5985
static const mp_arg_t allowed_args[] = {
60-
{ MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT },
86+
{ MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
6187
{ MP_QSTR_length, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT },
6288
};
6389
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
6490
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
6591

66-
size_t start =
67-
mp_arg_validate_int_min(args[ARG_start].u_int, 0, MP_QSTR_start);
92+
// Argument start is a pointer into the address map, so we validate it here because a
93+
// signed int argument will overflow if it is in the upper half of the map.
94+
size_t start;
95+
if (mp_obj_is_small_int(args[ARG_start].u_obj)) {
96+
start = MP_OBJ_SMALL_INT_VALUE(args[ARG_start].u_obj);
97+
} else if (mp_obj_is_type(args[ARG_start].u_obj, &mp_type_int)) {
98+
start = mp_obj_int_get_uint_checked(args[ARG_start].u_obj);
99+
} else {
100+
mp_obj_t arg = mp_unary_op(MP_UNARY_OP_INT, args[ARG_start].u_obj);
101+
start = mp_obj_int_get_uint_checked(arg);
102+
}
68103
size_t length =
69104
mp_arg_validate_int_min(args[ARG_length].u_int, 1, MP_QSTR_length);
70-
105+
// Check for address range wrap here as this can break port-specific code due to size_t overflow.
106+
if (start + length - 1 < start) {
107+
mp_raise_ValueError(translate("Address range wraps around"));
108+
}
71109

72110
memorymap_addressrange_obj_t *self = mp_obj_malloc(memorymap_addressrange_obj_t, &memorymap_addressrange_type);
73111

@@ -104,7 +142,8 @@ STATIC MP_DEFINE_CONST_DICT(memorymap_addressrange_locals_dict, memorymap_addres
104142
//| def __getitem__(self, index: int) -> int:
105143
//| """Returns the value(s) at the given index.
106144
//|
107-
//| 1, 2, 4 and 8 byte aligned reads will be done in one transaction.
145+
//| 1, 2, 4 and 8 byte aligned reads will be done in one transaction
146+
//| when possible.
108147
//| All others may use multiple transactions."""
109148
//| ...
110149
//| @overload
@@ -113,7 +152,8 @@ STATIC MP_DEFINE_CONST_DICT(memorymap_addressrange_locals_dict, memorymap_addres
113152
//| def __setitem__(self, index: int, value: int) -> None:
114153
//| """Set the value(s) at the given index.
115154
//|
116-
//| 1, 2, 4 and 8 byte aligned writes will be done in one transaction.
155+
//| 1, 2, 4 and 8 byte aligned writes will be done in one transaction
156+
//| when possible.
117157
//| All others may use multiple transactions."""
118158
//| ...
119159
//|
@@ -154,9 +194,7 @@ STATIC mp_obj_t memorymap_addressrange_subscr(mp_obj_t self_in, mp_obj_t index_i
154194
mp_raise_NotImplementedError(translate("array/bytes required on right side"));
155195
}
156196

157-
if (!common_hal_memorymap_addressrange_set_bytes(self, slice.start, src_items, src_len)) {
158-
mp_raise_RuntimeError(translate("Unable to write to address."));
159-
}
197+
common_hal_memorymap_addressrange_set_bytes(self, slice.start, src_items, src_len);
160198
return mp_const_none;
161199
#else
162200
return MP_OBJ_NULL; // op not supported
@@ -184,9 +222,7 @@ STATIC mp_obj_t memorymap_addressrange_subscr(mp_obj_t self_in, mp_obj_t index_i
184222
mp_arg_validate_int_range(byte_value, 0, 255, MP_QSTR_bytes);
185223

186224
uint8_t short_value = byte_value;
187-
if (!common_hal_memorymap_addressrange_set_bytes(self, index, &short_value, 1)) {
188-
mp_raise_RuntimeError(translate("Unable to write to address."));
189-
}
225+
common_hal_memorymap_addressrange_set_bytes(self, index, &short_value, 1);
190226
return mp_const_none;
191227
}
192228
}

0 commit comments

Comments
 (0)