122122uint8_t value_out = 0 ;
123123#endif
124124
125- #if MICROPY_ENABLE_PYSTACK
126- static size_t PLACE_IN_DTCM_BSS ( _pystack [ CIRCUITPY_PYSTACK_SIZE / sizeof ( size_t )]);
125+ #if MICROPY_ENABLE_PYSTACK && CIRCUITPY_OS_GETENV
126+ #include "shared-module/os/__init__.h"
127127#endif
128128
129129static void reset_devices (void ) {
@@ -132,7 +132,32 @@ static void reset_devices(void) {
132132 #endif
133133}
134134
135- STATIC void start_mp (supervisor_allocation * heap ) {
135+ #if MICROPY_ENABLE_PYSTACK
136+ STATIC supervisor_allocation * allocate_pystack (safe_mode_t safe_mode ) {
137+ mp_int_t pystack_size = CIRCUITPY_PYSTACK_SIZE ;
138+ #if CIRCUITPY_OS_GETENV && CIRCUITPY_SETTABLE_PYSTACK
139+ // Fetch value if exists from settings.toml
140+ // Leaves size to build default on any failure
141+ if (safe_mode == SAFE_MODE_NONE || safe_mode == SAFE_MODE_USER ) {
142+ (void )common_hal_os_getenv_int ("CIRCUITPY_PYSTACK_SIZE" , & pystack_size );
143+ // Check if value is valid
144+ pystack_size = pystack_size - pystack_size % sizeof (size_t ); // Round down to multiple of 4.
145+ if ((pystack_size < 384 ) || (pystack_size > 900000 )) {
146+ serial_write_compressed (translate ("\nInvalid CIRCUITPY_PYSTACK_SIZE\n\n\r" ));
147+ pystack_size = CIRCUITPY_PYSTACK_SIZE ; // Reset
148+ }
149+ }
150+ #endif
151+ supervisor_allocation * pystack = allocate_memory (pystack_size , false, false);
152+ if (pystack == NULL ) {
153+ serial_write_compressed (translate ("\nInvalid CIRCUITPY_PYSTACK_SIZE\n\n\r" ));
154+ pystack = allocate_memory (CIRCUITPY_PYSTACK_SIZE , false, false);
155+ }
156+ return pystack ;
157+ }
158+ #endif
159+
160+ STATIC void start_mp (supervisor_allocation * heap , supervisor_allocation * pystack ) {
136161 supervisor_workflow_reset ();
137162
138163 // Stack limit should be less than real stack size, so we have a chance
@@ -160,7 +185,7 @@ STATIC void start_mp(supervisor_allocation *heap) {
160185 readline_init0 ();
161186
162187 #if MICROPY_ENABLE_PYSTACK
163- mp_pystack_init (_pystack , _pystack + ( sizeof ( _pystack ) / sizeof (size_t ) ));
188+ mp_pystack_init (pystack -> ptr , pystack -> ptr + get_allocation_length ( pystack ) / sizeof (size_t ));
164189 #endif
165190
166191 #if MICROPY_ENABLE_GC
@@ -264,7 +289,7 @@ STATIC void count_strn(void *data, const char *str, size_t len) {
264289 * (size_t * )data += len ;
265290}
266291
267- STATIC void cleanup_after_vm (supervisor_allocation * heap , mp_obj_t exception ) {
292+ STATIC void cleanup_after_vm (supervisor_allocation * heap , supervisor_allocation * pystack , mp_obj_t exception ) {
268293 // Get the traceback of any exception from this run off the heap.
269294 // MP_OBJ_SENTINEL means "this run does not contribute to traceback storage, don't touch it"
270295 // MP_OBJ_NULL (=0) means "this run completed successfully, clear any stored traceback"
@@ -345,6 +370,9 @@ STATIC void cleanup_after_vm(supervisor_allocation *heap, mp_obj_t exception) {
345370 filesystem_flush ();
346371 stop_mp ();
347372 free_memory (heap );
373+ #if MICROPY_ENABLE_PYSTACK
374+ free_memory (pystack );
375+ #endif
348376 supervisor_move_memory ();
349377
350378 // Let the workflows know we've reset in case they want to restart.
@@ -399,10 +427,12 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
399427 };
400428 #endif
401429
430+ supervisor_allocation * pystack = NULL ;
431+ #if MICROPY_ENABLE_PYSTACK
432+ pystack = allocate_pystack (safe_mode );
433+ #endif
402434 supervisor_allocation * heap = allocate_remaining_memory ();
403-
404- // Prepare the VM state.
405- start_mp (heap );
435+ start_mp (heap , pystack );
406436
407437 #if CIRCUITPY_USB
408438 usb_setup_with_vm ();
@@ -450,7 +480,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
450480
451481
452482 // Finished executing python code. Cleanup includes filesystem flush and a board reset.
453- cleanup_after_vm (heap , _exec_result .exception );
483+ cleanup_after_vm (heap , pystack , _exec_result .exception );
454484 _exec_result .exception = NULL ;
455485
456486 // If a new next code file was set, that is a reason to keep it (obviously). Stuff this into
@@ -739,8 +769,12 @@ STATIC void __attribute__ ((noinline)) run_safemode_py(safe_mode_t safe_mode) {
739769 return ;
740770 }
741771
772+ supervisor_allocation * pystack = NULL ;
773+ #if MICROPY_ENABLE_PYSTACK
774+ pystack = allocate_pystack (safe_mode );
775+ #endif
742776 supervisor_allocation * heap = allocate_remaining_memory ();
743- start_mp (heap );
777+ start_mp (heap , pystack );
744778
745779 static const char * const safemode_py_filenames [] = {"safemode.py" , "safemode.txt" };
746780 maybe_run_list (safemode_py_filenames , MP_ARRAY_SIZE (safemode_py_filenames ));
@@ -751,7 +785,7 @@ STATIC void __attribute__ ((noinline)) run_safemode_py(safe_mode_t safe_mode) {
751785 set_safe_mode (SAFE_MODE_SAFEMODE_PY_ERROR );
752786 }
753787
754- cleanup_after_vm (heap , _exec_result .exception );
788+ cleanup_after_vm (heap , pystack , _exec_result .exception );
755789 _exec_result .exception = NULL ;
756790}
757791#endif
@@ -772,9 +806,12 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
772806
773807 // Do USB setup even if boot.py is not run.
774808
809+ supervisor_allocation * pystack = NULL ;
810+ #if MICROPY_ENABLE_PYSTACK
811+ pystack = allocate_pystack (safe_mode );
812+ #endif
775813 supervisor_allocation * heap = allocate_remaining_memory ();
776-
777- start_mp (heap );
814+ start_mp (heap , pystack );
778815
779816 #if CIRCUITPY_USB
780817 // Set up default USB values after boot.py VM starts but before running boot.py.
@@ -860,7 +897,7 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
860897
861898 port_post_boot_py (true);
862899
863- cleanup_after_vm (heap , _exec_result .exception );
900+ cleanup_after_vm (heap , pystack , _exec_result .exception );
864901 _exec_result .exception = NULL ;
865902
866903 port_post_boot_py (false);
@@ -871,12 +908,16 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
871908 #endif
872909}
873910
874- STATIC int run_repl (void ) {
911+ STATIC int run_repl (safe_mode_t safe_mode ) {
875912 int exit_code = PYEXEC_FORCED_EXIT ;
876913 stack_resize ();
877914 filesystem_flush ();
915+ supervisor_allocation * pystack = NULL ;
916+ #if MICROPY_ENABLE_PYSTACK
917+ pystack = allocate_pystack (safe_mode );
918+ #endif
878919 supervisor_allocation * heap = allocate_remaining_memory ();
879- start_mp (heap );
920+ start_mp (heap , pystack );
880921
881922 #if CIRCUITPY_USB
882923 usb_setup_with_vm ();
@@ -919,7 +960,7 @@ STATIC int run_repl(void) {
919960 exit_code = PYEXEC_DEEP_SLEEP ;
920961 }
921962 #endif
922- cleanup_after_vm (heap , MP_OBJ_SENTINEL );
963+ cleanup_after_vm (heap , pystack , MP_OBJ_SENTINEL );
923964
924965 // Also reset bleio. The above call omits it in case workflows should continue. In this case,
925966 // we're switching straight to another VM so we want to reset.
@@ -938,6 +979,7 @@ STATIC int run_repl(void) {
938979}
939980
940981int __attribute__((used )) main (void ) {
982+
941983 // initialise the cpu and peripherals
942984 set_safe_mode (port_init ());
943985
@@ -1038,7 +1080,7 @@ int __attribute__((used)) main(void) {
10381080 bool simulate_reset = true;
10391081 for (;;) {
10401082 if (!skip_repl ) {
1041- exit_code = run_repl ();
1083+ exit_code = run_repl (get_safe_mode () );
10421084 supervisor_set_run_reason (RUN_REASON_REPL_RELOAD );
10431085 }
10441086 if (exit_code == PYEXEC_FORCED_EXIT ) {
0 commit comments