Skip to content

Commit 777542c

Browse files
committed
Add basic memory allocation outside Python runtime
This allows for the heap to fill all space but the stack. It also allows us to designate space for memory outside the runtime for things such as USB descriptors, flash cache and main filename. Fixes micropython#754
1 parent bf03312 commit 777542c

File tree

8 files changed

+233
-35
lines changed

8 files changed

+233
-35
lines changed

main.c

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
#include "mpconfigboard.h"
4747
#include "supervisor/cpu.h"
48+
#include "supervisor/memory.h"
4849
#include "supervisor/port.h"
4950
#include "supervisor/filesystem.h"
5051
// TODO(tannewt): Figure out how to choose language at compile time.
@@ -73,12 +74,21 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
7374
}
7475
}
7576

76-
static char heap[PORT_HEAP_SIZE];
77-
78-
void reset_mp(void) {
77+
void start_mp(supervisor_allocation* heap) {
7978
reset_status_led();
8079
autoreload_stop();
8180

81+
// Stack limit should be less than real stack size, so we have a chance
82+
// to recover from limit hit. (Limit is measured in bytes.)
83+
mp_stack_ctrl_init();
84+
mp_stack_set_limit((char*)&_estack - (char*)&_ebss - 1024);
85+
86+
#if MICROPY_MAX_STACK_USAGE
87+
// _ezero (same as _ebss) is an int, so start 4 bytes above it.
88+
mp_stack_set_bottom(&_ezero + 1);
89+
mp_stack_fill_with_sentinel();
90+
#endif
91+
8292
// Sync the file systems in case any used RAM from the GC to cache. As soon
8393
// as we re-init the GC all bets are off on the cache.
8494
filesystem_flush();
@@ -87,7 +97,7 @@ void reset_mp(void) {
8797
readline_init0();
8898

8999
#if MICROPY_ENABLE_GC
90-
gc_init(heap, heap + sizeof(heap));
100+
gc_init(heap->ptr, heap->ptr + heap->length);
91101
#endif
92102
mp_init();
93103
mp_obj_list_init(mp_sys_path, 0);
@@ -99,6 +109,11 @@ void reset_mp(void) {
99109

100110
mp_obj_list_init(mp_sys_argv, 0);
101111
}
112+
113+
void stop_mp(void) {
114+
115+
}
116+
102117
#define STRING_LIST(...) {__VA_ARGS__, ""}
103118

104119
// Look for the first file that exists in the list of filenames, using mp_import_stat().
@@ -124,7 +139,7 @@ bool maybe_run_list(const char ** filenames, pyexec_result_t* exec_result) {
124139
return true;
125140
}
126141

127-
bool start_mp(safe_mode_t safe_mode) {
142+
bool run_code_py(safe_mode_t safe_mode) {
128143
bool serial_connected_at_start = serial_connected();
129144
#ifdef CIRCUITPY_AUTORELOAD_DELAY_MS
130145
if (serial_connected_at_start) {
@@ -155,14 +170,18 @@ bool start_mp(safe_mode_t safe_mode) {
155170
const char *supported_filenames[] = STRING_LIST("code.txt", "code.py", "main.py", "main.txt");
156171
const char *double_extension_filenames[] = STRING_LIST("code.txt.py", "code.py.txt", "code.txt.txt","code.py.py",
157172
"main.txt.py", "main.py.txt", "main.txt.txt","main.py.py");
158-
reset_mp();
173+
174+
supervisor_allocation* heap = allocate_remaining_memory();
175+
start_mp(heap);
159176
found_main = maybe_run_list(supported_filenames, &result);
160177
if (!found_main){
161178
found_main = maybe_run_list(double_extension_filenames, &result);
162179
if (found_main) {
163180
serial_write(MSG_DOUBLE_FILE_EXTENSION);
164181
}
165182
}
183+
stop_mp();
184+
free_memory(heap);
166185

167186
reset_port();
168187
reset_board();
@@ -306,27 +325,36 @@ void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
306325
// Reset to remove any state that boot.py setup. It should only be used to
307326
// change internal state that's not in the heap.
308327
reset_port();
309-
reset_mp();
310328
}
311329
}
312330

331+
int run_repl(void) {
332+
int exit_code = PYEXEC_FORCED_EXIT;
333+
supervisor_allocation* heap = allocate_remaining_memory();
334+
start_mp(heap);
335+
autoreload_suspend();
336+
new_status_color(REPL_RUNNING);
337+
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
338+
exit_code = pyexec_raw_repl();
339+
} else {
340+
exit_code = pyexec_friendly_repl();
341+
}
342+
reset_port();
343+
reset_board();
344+
stop_mp();
345+
free_memory(heap);
346+
autoreload_resume();
347+
return exit_code;
348+
}
349+
313350
int __attribute__((used)) main(void) {
351+
memory_init();
352+
314353
// initialise the cpu and peripherals
315354
safe_mode_t safe_mode = port_init();
316355

317356
rgb_led_status_init();
318357

319-
// Stack limit should be less than real stack size, so we have a chance
320-
// to recover from limit hit. (Limit is measured in bytes.)
321-
mp_stack_set_top((char*)&_estack);
322-
mp_stack_set_limit((char*)&_estack - (char*)&_ebss - 1024);
323-
324-
#if MICROPY_MAX_STACK_USAGE
325-
// _ezero (same as _ebss) is an int, so start 4 bytes above it.
326-
mp_stack_set_bottom(&_ezero + 1);
327-
mp_stack_fill_with_sentinel();
328-
#endif
329-
330358
// Create a new filesystem only if we're not in a safe mode.
331359
// A power brownout here could make it appear as if there's
332360
// no SPI flash filesystem, and we might erase the existing one.
@@ -335,7 +363,6 @@ int __attribute__((used)) main(void) {
335363
// Reset everything and prep MicroPython to run boot.py.
336364
reset_port();
337365
reset_board();
338-
reset_mp();
339366

340367
// Turn on autoreload by default but before boot.py in case it wants to change it.
341368
autoreload_enable();
@@ -355,24 +382,14 @@ int __attribute__((used)) main(void) {
355382
bool first_run = true;
356383
for (;;) {
357384
if (!skip_repl) {
358-
reset_mp();
359-
autoreload_suspend();
360-
new_status_color(REPL_RUNNING);
361-
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
362-
exit_code = pyexec_raw_repl();
363-
} else {
364-
exit_code = pyexec_friendly_repl();
365-
}
366-
autoreload_resume();
367-
reset_port();
368-
reset_board();
385+
exit_code = run_repl();
369386
}
370387
if (exit_code == PYEXEC_FORCED_EXIT) {
371388
if (!first_run) {
372389
serial_write(MSG_SOFT_REBOOT MSG_NEWLINE);
373390
}
374391
first_run = false;
375-
skip_repl = start_mp(safe_mode);
392+
skip_repl = run_code_py(safe_mode);
376393
} else if (exit_code != 0) {
377394
break;
378395
}

ports/atmel-samd/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ ifeq ($(DEBUG), 1)
106106
# Turn on Python modules useful for debugging (e.g. uheap, ustack).
107107
CFLAGS += -ggdb
108108
# You may want to disable -flto if it interferes with debugging.
109-
CFLAGS += -flto
109+
#CFLAGS += -flto
110110
# You may want to enable these flags to make setting breakpoints easier.
111111
## CFLAGS += -fno-inline -fno-ipa-sra
112112
ifeq ($(CHIP_FAMILY), samd21)

ports/atmel-samd/external_flash/external_flash.c

100644100755
Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "py/runtime.h"
3838
#include "lib/oofatfs/ff.h"
3939
#include "shared-bindings/microcontroller/__init__.h"
40+
#include "supervisor/memory.h"
4041
#include "supervisor/shared/rgb_led_status.h"
4142

4243
#include "hal_gpio.h"
@@ -59,6 +60,8 @@ static const external_flash_device* flash_device = NULL;
5960
// cache.
6061
static uint32_t dirty_mask;
6162

63+
static supervisor_allocation* supervisor_cache = NULL;
64+
6265
// Wait until both the write enable and write in progress bits have cleared.
6366
static bool wait_for_flash_ready(void) {
6467
uint8_t read_status_response[1] = {0x00};
@@ -308,6 +311,23 @@ static bool flush_scratch_flash(void) {
308311
static bool allocate_ram_cache(void) {
309312
uint8_t blocks_per_sector = SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE;
310313
uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
314+
315+
uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t);
316+
// Attempt to allocate outside the heap first.
317+
supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false);
318+
if (supervisor_cache != NULL) {
319+
MP_STATE_VM(flash_ram_cache) = (uint8_t **) supervisor_cache->ptr;
320+
uint8_t* page_start = (uint8_t *) supervisor_cache->ptr + table_size;
321+
322+
for (uint8_t i = 0; i < blocks_per_sector; i++) {
323+
for (uint8_t j = 0; j < pages_per_block; j++) {
324+
uint32_t offset = i * pages_per_block + j;
325+
MP_STATE_VM(flash_ram_cache)[offset] = page_start + offset * SPI_FLASH_PAGE_SIZE;
326+
}
327+
}
328+
return true;
329+
}
330+
311331
MP_STATE_VM(flash_ram_cache) = m_malloc_maybe(blocks_per_sector * pages_per_block * sizeof(uint32_t), false);
312332
if (MP_STATE_VM(flash_ram_cache) == NULL) {
313333
return false;
@@ -383,14 +403,19 @@ static bool flush_ram_cache(bool keep_cache) {
383403
write_flash(current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE,
384404
MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j],
385405
SPI_FLASH_PAGE_SIZE);
386-
if (!keep_cache) {
406+
if (!keep_cache && supervisor_cache == NULL) {
387407
m_free(MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j]);
388408
}
389409
}
390410
}
391411
// We're done with the cache for now so give it back.
392412
if (!keep_cache) {
393-
m_free(MP_STATE_VM(flash_ram_cache));
413+
if (supervisor_cache != NULL) {
414+
free_memory(supervisor_cache);
415+
supervisor_cache = NULL;
416+
} else {
417+
m_free(MP_STATE_VM(flash_ram_cache));
418+
}
394419
MP_STATE_VM(flash_ram_cache) = NULL;
395420
}
396421
return true;

ports/atmel-samd/supervisor/filesystem.c

100644100755
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ void filesystem_init(bool create_allowed, bool force_create) {
5151

5252
// try to mount the flash
5353
FRESULT res = f_mount(&vfs_fat->fatfs);
54+
force_create = true;
5455

5556
if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) {
5657
// No filesystem so create a fresh one, or reformat has been requested.

ports/atmel-samd/supervisor/memory.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "supervisor/memory.h"
28+
29+
#include <stddef.h>
30+
31+
#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT 8
32+
33+
static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
34+
// We use uint32_t* to ensure word (4 byte) alignment.
35+
uint32_t* low_address;
36+
uint32_t* high_address;
37+
extern uint32_t _ebss;
38+
extern uint32_t _estack;
39+
40+
void memory_init(void) {
41+
low_address = &_ebss;
42+
high_address = &_estack;
43+
44+
// snag 3k for stack
45+
allocate_memory(1024*4, true);
46+
}
47+
48+
void free_memory(supervisor_allocation* allocation) {
49+
uint8_t index = 0;
50+
bool found = false;
51+
for (index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
52+
found = allocation == &allocations[index];
53+
if (found) {
54+
break;
55+
}
56+
}
57+
if (allocation->ptr == high_address) {
58+
high_address += allocation->length / 4;
59+
} else if (allocation->ptr + allocation->length / 4 == low_address) {
60+
low_address = allocation->ptr;
61+
}
62+
allocation->ptr = NULL;
63+
}
64+
65+
supervisor_allocation* allocate_remaining_memory(void) {
66+
if (low_address == high_address) {
67+
return NULL;
68+
}
69+
return allocate_memory((high_address - low_address) * 4, false);
70+
}
71+
72+
supervisor_allocation* allocate_memory(uint32_t length, bool high) {
73+
if ((high_address - low_address) * 4 < (int32_t) length) {
74+
return NULL;
75+
}
76+
uint8_t index = 0;
77+
int8_t direction = 1;
78+
if (high) {
79+
index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1;
80+
direction = -1;
81+
}
82+
for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index += direction) {
83+
if (allocations[index].ptr == NULL) {
84+
break;
85+
}
86+
}
87+
if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT) {
88+
return NULL;
89+
}
90+
supervisor_allocation* alloc = &allocations[index];
91+
if (high) {
92+
high_address -= length / 4;
93+
alloc->ptr = high_address;
94+
} else {
95+
alloc->ptr = low_address;
96+
low_address += length / 4;
97+
}
98+
alloc->length = length;
99+
return alloc;
100+
}

shared-bindings/supervisor/__init__.c

100644100755
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
#include "supervisor/shared/autoreload.h"
3232

3333
#include "supervisor/shared/rgb_led_status.h"
34-
34+
3535
#include "shared-bindings/supervisor/__init__.h"
3636
#include "shared-bindings/supervisor/Runtime.h"
3737

@@ -107,6 +107,15 @@ STATIC mp_obj_t supervisor_reload(void) {
107107
}
108108
MP_DEFINE_CONST_FUN_OBJ_0(supervisor_reload_obj, supervisor_reload);
109109

110+
//| .. method:: set_main_file(filename)
111+
//|
112+
//| Set which file to run after a reload.
113+
//|
114+
STATIC mp_obj_t supervisor_set_main_file(void) {
115+
116+
return mp_const_none;
117+
}
118+
MP_DEFINE_CONST_FUN_OBJ_0(supervisor_set_main_file_obj, supervisor_set_main_file);
110119

111120
STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
112121
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) },
@@ -115,6 +124,8 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
115124
{ MP_OBJ_NEW_QSTR(MP_QSTR_set_rgb_status_brightness), MP_ROM_PTR(&supervisor_set_rgb_status_brightness_obj) },
116125
{ MP_ROM_QSTR(MP_QSTR_runtime), MP_ROM_PTR(&common_hal_supervisor_runtime_obj) },
117126
{ MP_ROM_QSTR(MP_QSTR_reload), MP_ROM_PTR(&supervisor_reload_obj) },
127+
{ MP_ROM_QSTR(MP_QSTR_set_main_file), MP_ROM_PTR(&supervisor_set_main_file_obj) },
128+
118129
};
119130

120131
STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table);

0 commit comments

Comments
 (0)