aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--stmhal/Makefile2
-rw-r--r--stmhal/gccollect.c11
-rw-r--r--stmhal/main.c19
-rw-r--r--stmhal/mpconfigport.h2
-rw-r--r--stmhal/mpthreadport.c123
-rw-r--r--stmhal/mpthreadport.h45
-rw-r--r--stmhal/pendsv.c30
-rw-r--r--stmhal/pybthread.c97
-rw-r--r--stmhal/pybthread.h62
-rw-r--r--stmhal/stm32_it.c6
10 files changed, 392 insertions, 5 deletions
diff --git a/stmhal/Makefile b/stmhal/Makefile
index 3431c3105..a83f2c495 100644
--- a/stmhal/Makefile
+++ b/stmhal/Makefile
@@ -131,9 +131,11 @@ SRC_C = \
usbd_hid_interface.c \
usbd_msc_storage.c \
mphalport.c \
+ mpthreadport.c \
irq.c \
pendsv.c \
systick.c \
+ pybthread.c \
timer.c \
led.c \
pin.c \
diff --git a/stmhal/gccollect.c b/stmhal/gccollect.c
index de76b71ac..8a7bbf27f 100644
--- a/stmhal/gccollect.c
+++ b/stmhal/gccollect.c
@@ -27,8 +27,10 @@
#include <stdio.h>
#include <stdint.h>
+#include "py/mpstate.h"
#include "py/obj.h"
#include "py/gc.h"
+#include "py/mpthread.h"
#include "gccollect.h"
#include "systick.h"
@@ -48,7 +50,16 @@ void gc_collect(void) {
mp_uint_t sp = gc_helper_get_regs_and_sp(regs);
// trace the stack, including the registers (since they live on the stack in this function)
+ #if MICROPY_PY_THREAD
+ gc_collect_root((void**)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t));
+ #else
gc_collect_root((void**)sp, ((uint32_t)&_ram_end - sp) / sizeof(uint32_t));
+ #endif
+
+ // trace root pointers from any threads
+ #if MICROPY_PY_THREAD
+ mp_thread_gc_others();
+ #endif
// end the GC
gc_collect_end();
diff --git a/stmhal/main.c b/stmhal/main.c
index 722ca41b4..4ffa0d9ba 100644
--- a/stmhal/main.c
+++ b/stmhal/main.c
@@ -43,6 +43,7 @@
#include "systick.h"
#include "pendsv.h"
+#include "pybthread.h"
#include "gccollect.h"
#include "readline.h"
#include "modmachine.h"
@@ -67,6 +68,7 @@
void SystemClock_Config(void);
+pyb_thread_t pyb_thread_main;
fs_user_mount_t fs_user_mount_flash;
mp_vfs_mount_t mp_vfs_mount_flash;
@@ -419,11 +421,6 @@ STATIC uint update_reset_mode(uint reset_mode) {
int main(void) {
// TODO disable JTAG
- // Stack limit should be less than real stack size, so we have a chance
- // to recover from limit hit. (Limit is measured in bytes.)
- mp_stack_ctrl_init();
- mp_stack_set_limit((char*)&_ram_end - (char*)&_heap_end - 1024);
-
/* STM32F4xx HAL library initialization:
- Configure the Flash prefetch, instruction and Data caches
- Configure the Systick to generate an interrupt each 1 msec
@@ -457,6 +454,7 @@ int main(void) {
#endif
// basic sub-system init
+ pyb_thread_init(&pyb_thread_main);
pendsv_init();
led_init();
#if MICROPY_HW_HAS_SWITCH
@@ -502,6 +500,17 @@ soft_reset:
storage_init();
}
+ // Python threading init
+ #if MICROPY_PY_THREAD
+ mp_thread_init();
+ #endif
+
+ // Stack limit should be less than real stack size, so we have a chance
+ // to recover from limit hit. (Limit is measured in bytes.)
+ // Note: stack control relies on main thread being initialised above
+ mp_stack_ctrl_init();
+ mp_stack_set_limit((char*)&_ram_end - (char*)&_heap_end - 1024);
+
// GC init
gc_init(&_heap_start, &_heap_end);
diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h
index 873215458..083f75418 100644
--- a/stmhal/mpconfigport.h
+++ b/stmhal/mpconfigport.h
@@ -102,6 +102,8 @@
#define MICROPY_PY_SYS_PLATFORM "pyboard"
#endif
#define MICROPY_PY_UERRNO (1)
+#define MICROPY_PY_THREAD (0)
+#define MICROPY_PY_THREAD_GIL (0)
// extended modules
#define MICROPY_PY_UCTYPES (1)
diff --git a/stmhal/mpthreadport.c b/stmhal/mpthreadport.c
new file mode 100644
index 000000000..97c19647c
--- /dev/null
+++ b/stmhal/mpthreadport.c
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "py/mpconfig.h"
+#include "py/mpstate.h"
+#include "py/gc.h"
+#include "py/mpthread.h"
+#include "gccollect.h"
+
+#if MICROPY_PY_THREAD
+
+// the mutex controls access to the linked list
+STATIC mp_thread_mutex_t thread_mutex;
+
+void mp_thread_init(void) {
+ mp_thread_mutex_init(&thread_mutex);
+ mp_thread_set_state(&mp_state_ctx.thread);
+}
+
+void mp_thread_gc_others(void) {
+ mp_thread_mutex_lock(&thread_mutex, 1);
+ gc_collect_root((void**)&pyb_thread_cur, 1);
+ for (pyb_thread_t *th = pyb_thread_cur;; th = th->next) {
+ gc_collect_root(&th->arg, 1);
+ if (th != pyb_thread_cur) {
+ gc_collect_root(th->stack, th->stack_len);
+ }
+ if (th->next == pyb_thread_cur) {
+ break;
+ }
+ }
+ mp_thread_mutex_unlock(&thread_mutex);
+}
+
+void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) {
+ if (*stack_size == 0) {
+ *stack_size = 4096; // default stack size
+ } else if (*stack_size < 2048) {
+ *stack_size = 2048; // minimum stack size
+ }
+
+ // round stack size to a multiple of the word size
+ size_t stack_len = *stack_size / sizeof(uint32_t);
+ *stack_size = stack_len * sizeof(uint32_t);
+
+ // allocate stack and linked-list node (must be done outside thread_mutex lock)
+ uint32_t *stack = m_new(uint32_t, stack_len);
+ pyb_thread_t *th = m_new_obj(pyb_thread_t);
+
+ mp_thread_mutex_lock(&thread_mutex, 1);
+
+ // create thread
+ uint32_t id = pyb_thread_new(th, stack, stack_len, entry, arg);
+ if (id == 0) {
+ mp_thread_mutex_unlock(&thread_mutex);
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread"));
+ }
+
+ mp_thread_mutex_unlock(&thread_mutex);
+
+ // adjust stack_size to provide room to recover from hitting the limit
+ *stack_size -= 1024;
+}
+
+void mp_thread_start(void) {
+}
+
+void mp_thread_finish(void) {
+}
+
+void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
+ *mutex = 0;
+}
+
+int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
+ uint32_t irq_state = disable_irq();
+ if (*mutex) {
+ // mutex is locked
+ if (!wait) {
+ enable_irq(irq_state);
+ return 0; // failed to lock mutex
+ }
+ while (*mutex) {
+ enable_irq(irq_state);
+ pyb_thread_yield();
+ irq_state = disable_irq();
+ }
+ }
+ *mutex = 1;
+ enable_irq(irq_state);
+ return 1; // have mutex
+}
+
+void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
+ *mutex = 0;
+}
+
+#endif // MICROPY_PY_THREAD
diff --git a/stmhal/mpthreadport.h b/stmhal/mpthreadport.h
new file mode 100644
index 000000000..4fef323eb
--- /dev/null
+++ b/stmhal/mpthreadport.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef __MICROPY_INCLUDED_STMHAL_MPTHREADPORT_H__
+#define __MICROPY_INCLUDED_STMHAL_MPTHREADPORT_H__
+
+#include "py/mpthread.h"
+#include "pybthread.h"
+
+typedef uint32_t mp_thread_mutex_t;
+
+void mp_thread_init(void);
+void mp_thread_gc_others(void);
+
+static inline void mp_thread_set_state(void *state) {
+ pyb_thread_set_local(state);
+}
+
+static inline struct _mp_state_thread_t *mp_thread_get_state(void) {
+ return pyb_thread_get_local();
+}
+
+#endif // __MICROPY_INCLUDED_STMHAL_MPTHREADPORT_H__
diff --git a/stmhal/pendsv.c b/stmhal/pendsv.c
index 61fe95439..e6df84b29 100644
--- a/stmhal/pendsv.c
+++ b/stmhal/pendsv.c
@@ -89,6 +89,35 @@ void pendsv_isr_handler(void) {
// sp[1]: 0xfffffff9
// sp[0]: ?
+#if MICROPY_PY_THREAD
+ __asm volatile (
+ "ldr r1, pendsv_object_ptr\n"
+ "ldr r0, [r1]\n"
+ "cmp r0, 0\n"
+ "beq .no_obj\n"
+ "str r0, [sp, #0]\n" // store to r0 on stack
+ "mov r0, #0\n"
+ "str r0, [r1]\n" // clear pendsv_object
+ "ldr r0, nlr_jump_ptr\n"
+ "str r0, [sp, #24]\n" // store to pc on stack
+ "bx lr\n" // return from interrupt; will return to nlr_jump
+
+ ".no_obj:\n" // pendsv_object==NULL
+ "push {r4-r11, lr}\n"
+ "vpush {s16-s31}\n"
+ "mov r0, sp\n" // pass sp to save
+ "mov r4, lr\n" // save lr because we are making a call
+ "bl pyb_thread_next\n" // get next thread to execute
+ "mov lr, r4\n" // restore lr
+ "mov sp, r0\n" // switch stacks
+ "vpop {s16-s31}\n"
+ "pop {r4-r11, lr}\n"
+ "bx lr\n" // return from interrupt; will return to new thread
+ ".align 2\n"
+ "pendsv_object_ptr: .word pendsv_object\n"
+ "nlr_jump_ptr: .word nlr_jump\n"
+ );
+#else
__asm volatile (
"ldr r0, pendsv_object_ptr\n"
"ldr r0, [r0]\n"
@@ -108,6 +137,7 @@ void pendsv_isr_handler(void) {
"pendsv_object_ptr: .word pendsv_object\n"
"nlr_jump_ptr: .word nlr_jump\n"
);
+#endif
/*
uint32_t x[2] = {0x424242, 0xdeaddead};
diff --git a/stmhal/pybthread.c b/stmhal/pybthread.c
new file mode 100644
index 000000000..9f9f82a45
--- /dev/null
+++ b/stmhal/pybthread.c
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "py/obj.h"
+#include "gccollect.h"
+#include "irq.h"
+#include "pybthread.h"
+
+#if MICROPY_PY_THREAD
+
+int pyb_thread_enabled;
+pyb_thread_t *pyb_thread_cur;
+
+void pyb_thread_init(pyb_thread_t *thread) {
+ pyb_thread_cur = thread;
+ pyb_thread_cur->sp = NULL; // will be set when this thread switches out
+ pyb_thread_cur->local_state = 0; // will be set by mp_thread_init
+ pyb_thread_cur->arg = NULL;
+ pyb_thread_cur->stack = &_heap_end;
+ pyb_thread_cur->stack_len = ((uint32_t)&_estack - (uint32_t)&_heap_end) / sizeof(uint32_t);
+ pyb_thread_cur->prev = thread;
+ pyb_thread_cur->next = thread;
+}
+
+STATIC void pyb_thread_terminate(void) {
+ uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV);
+ pyb_thread_cur->prev->next = pyb_thread_cur->next;
+ pyb_thread_cur->next->prev = pyb_thread_cur->prev;
+ if (pyb_thread_cur->next == pyb_thread_cur->prev) {
+ pyb_thread_enabled = 0;
+ }
+ restore_irq_pri(irq_state);
+ pyb_thread_yield(); // should not return
+}
+
+uint32_t pyb_thread_new(pyb_thread_t *thread, void *stack, size_t stack_len, void *entry, void *arg) {
+ uint32_t *stack_top = (uint32_t*)stack + stack_len; // stack is full descending
+ *--stack_top = 0x01000000; // xPSR (thumb bit set)
+ *--stack_top = (uint32_t)entry & 0xfffffffe; // pc (must have bit 0 cleared, even for thumb code)
+ *--stack_top = (uint32_t)pyb_thread_terminate; // lr
+ *--stack_top = 0; // r12
+ *--stack_top = 0; // r3
+ *--stack_top = 0; // r2
+ *--stack_top = 0; // r1
+ *--stack_top = (uint32_t)arg; // r0
+ *--stack_top = 0xfffffff9; // lr (return to thread mode, non-FP, use MSP)
+ stack_top -= 8; // r4-r11
+ stack_top -= 16; // s16-s31 (we assume all threads use FP registers)
+ thread->sp = stack_top;
+ thread->local_state = 0;
+ thread->arg = arg;
+ thread->stack = stack;
+ thread->stack_len = stack_len;
+ uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV);
+ pyb_thread_enabled = 1;
+ thread->next = pyb_thread_cur->next;
+ thread->prev = pyb_thread_cur;
+ pyb_thread_cur->next->prev = thread;
+ pyb_thread_cur->next = thread;
+ restore_irq_pri(irq_state);
+ return (uint32_t)thread; // success
+}
+
+// should only be called from pendsv_isr_handler
+void *pyb_thread_next(void *sp) {
+ pyb_thread_cur->sp = sp;
+ pyb_thread_cur = pyb_thread_cur->next;
+ return pyb_thread_cur->sp;
+}
+
+#endif // MICROPY_PY_THREAD
diff --git a/stmhal/pybthread.h b/stmhal/pybthread.h
new file mode 100644
index 000000000..d4310c66a
--- /dev/null
+++ b/stmhal/pybthread.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_STMHAL_PYBTHREAD_H
+#define MICROPY_INCLUDED_STMHAL_PYBTHREAD_H
+
+typedef struct _pyb_thread_t {
+ void *sp;
+ uint32_t local_state;
+ void *arg; // thread Python args, a GC root pointer
+ void *stack; // pointer to the stack
+ size_t stack_len; // number of words in the stack
+ struct _pyb_thread_t *prev;
+ struct _pyb_thread_t *next;
+} pyb_thread_t;
+
+extern int pyb_thread_enabled;
+extern pyb_thread_t *pyb_thread_cur;
+
+void pyb_thread_init(pyb_thread_t *th);
+uint32_t pyb_thread_new(pyb_thread_t *th, void *stack, size_t stack_len, void *entry, void *arg);
+
+static inline uint32_t pyb_thread_get_id(void) {
+ return (uint32_t)pyb_thread_cur;
+}
+
+static inline void pyb_thread_set_local(void *value) {
+ pyb_thread_cur->local_state = (uint32_t)value;
+}
+
+static inline void *pyb_thread_get_local(void) {
+ return (void*)pyb_thread_cur->local_state;
+}
+
+static inline void pyb_thread_yield(void) {
+ SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
+}
+
+#endif // MICROPY_INCLUDED_STMHAL_PYBTHREAD_H
diff --git a/stmhal/stm32_it.c b/stmhal/stm32_it.c
index e1129ac39..a6503d310 100644
--- a/stmhal/stm32_it.c
+++ b/stmhal/stm32_it.c
@@ -73,6 +73,7 @@
#include "py/obj.h"
#include "pendsv.h"
#include "irq.h"
+#include "pybthread.h"
#include "extint.h"
#include "timer.h"
#include "uart.h"
@@ -287,6 +288,11 @@ void SysTick_Handler(void) {
if (DMA_IDLE_ENABLED() && DMA_IDLE_TICK(uwTick)) {
dma_idle_handler(uwTick);
}
+
+ // signal a thread switch at 4ms=250Hz
+ if (pyb_thread_enabled && (uwTick & 0x03) == 0x03) {
+ SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
+ }
}
/******************************************************************************/