From a6386f74b821e34e86f1853b539b3140db7ad406 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 31 Jan 2017 13:58:34 +1100 Subject: stmhal/pendsv: Fill in comments about what the stack contains. --- stmhal/pendsv.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'stmhal/pendsv.c') diff --git a/stmhal/pendsv.c b/stmhal/pendsv.c index ff4480eed..61fe95439 100644 --- a/stmhal/pendsv.c +++ b/stmhal/pendsv.c @@ -70,21 +70,21 @@ void pendsv_isr_handler(void) { // on entry to this (naked) function, stack has the following layout: // // stack layout with DEBUG disabled: - // sp[6]: pc - // sp[5]: ? - // sp[4]: ? - // sp[3]: ? - // sp[2]: ? - // sp[1]: ? + // sp[6]: pc=r15 + // sp[5]: lr=r14 + // sp[4]: r12 + // sp[3]: r3 + // sp[2]: r2 + // sp[1]: r1 // sp[0]: r0 // // stack layout with DEBUG enabled: - // sp[8]: pc - // sp[7]: lr - // sp[6]: ? - // sp[5]: ? - // sp[4]: ? - // sp[3]: ? + // sp[8]: pc=r15 + // sp[7]: lr=r14 + // sp[6]: r12 + // sp[5]: r3 + // sp[4]: r2 + // sp[3]: r1 // sp[2]: r0 // sp[1]: 0xfffffff9 // sp[0]: ? -- cgit v1.2.3 From 882ec01e42227445a63f6d5b38cac14d8635f2ad Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 31 Jan 2017 13:59:53 +1100 Subject: stmhal: Initial implementation of multithreading, currently disabled. This patch brings the _thread module to stmhal/pyboard. There is a very simple round-robin thread scheduler, which is disabled if there is only one thread (for efficiency when threading is not used). The scheduler currently switches threads at a rate of 250Hz using the systick timer and the pend-SV interrupt. The GIL is disabled so one must be careful to use lock objects to prevent concurrent access of objects. The threading is disabled by default, one can enabled it with the config option MICROPY_PY_THREAD to test it out. --- stmhal/Makefile | 2 + stmhal/gccollect.c | 11 +++++ stmhal/main.c | 19 ++++++-- stmhal/mpconfigport.h | 2 + stmhal/mpthreadport.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ stmhal/mpthreadport.h | 45 ++++++++++++++++++ stmhal/pendsv.c | 30 ++++++++++++ stmhal/pybthread.c | 97 +++++++++++++++++++++++++++++++++++++++ stmhal/pybthread.h | 62 +++++++++++++++++++++++++ stmhal/stm32_it.c | 6 +++ 10 files changed, 392 insertions(+), 5 deletions(-) create mode 100644 stmhal/mpthreadport.c create mode 100644 stmhal/mpthreadport.h create mode 100644 stmhal/pybthread.c create mode 100644 stmhal/pybthread.h (limited to 'stmhal/pendsv.c') 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 #include +#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 + +#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 +#include + +#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; + } } /******************************************************************************/ -- cgit v1.2.3 From e5cc681cb1b5163b9ae3453df85344326baf9759 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Feb 2017 16:39:30 +1100 Subject: stmhal: Use generic interrupt char code. --- stmhal/Makefile | 1 + stmhal/mphalport.c | 4 ---- stmhal/pendsv.c | 7 ++++--- stmhal/pendsv.h | 2 +- stmhal/usb.c | 14 +++----------- stmhal/usb.h | 1 - stmhal/usbd_cdc_interface.c | 22 +++++----------------- stmhal/usbd_cdc_interface.h | 1 - 8 files changed, 14 insertions(+), 38 deletions(-) (limited to 'stmhal/pendsv.c') diff --git a/stmhal/Makefile b/stmhal/Makefile index edd5e0619..76579d130 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -112,6 +112,7 @@ SRC_LIB = $(addprefix lib/,\ netutils/netutils.c \ timeutils/timeutils.c \ utils/pyexec.c \ + utils/interrupt_char.c \ ) DRIVERS_SRC_C = $(addprefix drivers/,\ diff --git a/stmhal/mphalport.c b/stmhal/mphalport.c index ca8e1c1bd..64c1164cc 100644 --- a/stmhal/mphalport.c +++ b/stmhal/mphalport.c @@ -21,10 +21,6 @@ NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } -void mp_hal_set_interrupt_char(int c) { - usb_vcp_set_interrupt_char(c); -} - int mp_hal_stdin_rx_chr(void) { for (;;) { #if 0 diff --git a/stmhal/pendsv.c b/stmhal/pendsv.c index e6df84b29..4c2a14de1 100644 --- a/stmhal/pendsv.c +++ b/stmhal/pendsv.c @@ -29,6 +29,7 @@ #include "py/mpstate.h" #include "py/runtime.h" +#include "lib/utils/interrupt_char.h" #include "pendsv.h" #include "irq.h" @@ -52,12 +53,12 @@ void pendsv_init(void) { // PENDSV feature. This will wait until all interrupts are finished then raise // the given exception object using nlr_jump in the context of the top-level // thread. -void pendsv_nlr_jump(void *o) { +void pendsv_kbd_intr(void) { if (MP_STATE_VM(mp_pending_exception) == MP_OBJ_NULL) { - MP_STATE_VM(mp_pending_exception) = o; + mp_keyboard_interrupt(); } else { MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; - pendsv_object = o; + pendsv_object = &MP_STATE_VM(mp_kbd_exception); SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } } diff --git a/stmhal/pendsv.h b/stmhal/pendsv.h index 7886d9f98..77c78d4c1 100644 --- a/stmhal/pendsv.h +++ b/stmhal/pendsv.h @@ -25,7 +25,7 @@ */ void pendsv_init(void); -void pendsv_nlr_jump(void *val); +void pendsv_kbd_intr(void); // since we play tricks with the stack, the compiler must not generate a // prelude for this function diff --git a/stmhal/usb.c b/stmhal/usb.c index 7eb3f179a..c413ce4ba 100644 --- a/stmhal/usb.c +++ b/stmhal/usb.c @@ -38,6 +38,7 @@ #include "py/runtime.h" #include "py/stream.h" #include "py/mperrno.h" +#include "py/mphal.h" #include "bufhelper.h" #include "usb.h" @@ -96,7 +97,7 @@ const mp_obj_tuple_t pyb_usb_hid_keyboard_obj = { }; void pyb_usb_init0(void) { - USBD_CDC_SetInterrupt(-1); + mp_hal_set_interrupt_char(-1); MP_STATE_PORT(pyb_hid_report_desc) = MP_OBJ_NULL; } @@ -141,15 +142,6 @@ bool usb_vcp_is_enabled(void) { return (pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED) != 0; } -void usb_vcp_set_interrupt_char(int c) { - if (pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED) { - if (c != -1) { - mp_obj_exception_clear_traceback(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); - } - USBD_CDC_SetInterrupt(c); - } -} - int usb_vcp_recv_byte(uint8_t *c) { return USBD_CDC_Rx(c, 1, 0); } @@ -364,7 +356,7 @@ STATIC mp_obj_t pyb_usb_vcp_make_new(const mp_obj_type_t *type, size_t n_args, s } STATIC mp_obj_t pyb_usb_vcp_setinterrupt(mp_obj_t self_in, mp_obj_t int_chr_in) { - usb_vcp_set_interrupt_char(mp_obj_get_int(int_chr_in)); + mp_hal_set_interrupt_char(mp_obj_get_int(int_chr_in)); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_usb_vcp_setinterrupt_obj, pyb_usb_vcp_setinterrupt); diff --git a/stmhal/usb.h b/stmhal/usb.h index e153f0c6b..bc2b91c3d 100644 --- a/stmhal/usb.h +++ b/stmhal/usb.h @@ -61,7 +61,6 @@ void pyb_usb_init0(void); bool pyb_usb_dev_init(uint16_t vid, uint16_t pid, usb_device_mode_t mode, USBD_HID_ModeInfoTypeDef *hid_info); void pyb_usb_dev_deinit(void); bool usb_vcp_is_enabled(void); -void usb_vcp_set_interrupt_char(int c); int usb_vcp_recv_byte(uint8_t *c); // if a byte is available, return 1 and put the byte in *c, else return 0 void usb_vcp_send_strn(const char* str, int len); void usb_vcp_send_strn_cooked(const char *str, int len); diff --git a/stmhal/usbd_cdc_interface.c b/stmhal/usbd_cdc_interface.c index 1f46b9dcc..1c12cdc1c 100644 --- a/stmhal/usbd_cdc_interface.c +++ b/stmhal/usbd_cdc_interface.c @@ -43,6 +43,7 @@ #include "py/mpstate.h" #include "py/obj.h" +#include "lib/utils/interrupt_char.h" #include "irq.h" #include "timer.h" #include "usb.h" @@ -79,8 +80,6 @@ static uint16_t UserTxBufPtrOutShadow = 0; // shadow of above static uint8_t UserTxBufPtrWaitCount = 0; // used to implement a timeout waiting for low-level USB driver static uint8_t UserTxNeedEmptyPacket = 0; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size -static int user_interrupt_char = -1; - /* Private function prototypes -----------------------------------------------*/ static int8_t CDC_Itf_Init (void); static int8_t CDC_Itf_DeInit (void); @@ -147,13 +146,6 @@ static int8_t CDC_Itf_Init(void) UserRxBufCur = 0; UserRxBufLen = 0; - /* NOTE: we cannot reset these here, because USBD_CDC_SetInterrupt - * may be called before this init function to set these values. - * This can happen if the USB enumeration occurs after the call to - * USBD_CDC_SetInterrupt. - user_interrupt_char = -1; - */ - return (USBD_OK); } @@ -339,7 +331,7 @@ static int8_t CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len) { uint32_t delta_len; - if (user_interrupt_char == -1) { + if (mp_interrupt_char == -1) { // no special interrupt character delta_len = *Len; @@ -350,10 +342,10 @@ static int8_t CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len) { uint8_t *src = Buf; uint8_t *buf_top = Buf + *Len; for (; src < buf_top; src++) { - if (*src == user_interrupt_char) { + if (*src == mp_interrupt_char) { char_found = true; - // raise exception when interrupts are finished - pendsv_nlr_jump(&MP_STATE_VM(mp_kbd_exception)); + // raise KeyboardInterrupt when interrupts are finished + pendsv_kbd_intr(); } else { if (char_found) { *dest = *src; @@ -385,10 +377,6 @@ int USBD_CDC_IsConnected(void) { return dev_is_connected; } -void USBD_CDC_SetInterrupt(int chr) { - user_interrupt_char = chr; -} - int USBD_CDC_TxHalfEmpty(void) { int32_t tx_waiting = (int32_t)UserTxBufPtrIn - (int32_t)UserTxBufPtrOut; if (tx_waiting < 0) { diff --git a/stmhal/usbd_cdc_interface.h b/stmhal/usbd_cdc_interface.h index 2ea1a42c4..d96861a7e 100644 --- a/stmhal/usbd_cdc_interface.h +++ b/stmhal/usbd_cdc_interface.h @@ -32,7 +32,6 @@ extern const USBD_CDC_ItfTypeDef USBD_CDC_fops; int USBD_CDC_IsConnected(void); -void USBD_CDC_SetInterrupt(int chr); int USBD_CDC_TxHalfEmpty(void); int USBD_CDC_Tx(const uint8_t *buf, uint32_t len, uint32_t timeout); -- cgit v1.2.3 From ebbaf7ee57a7c70067b8d1cc1d148cbdaf14762f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Mar 2017 18:56:46 +1100 Subject: stmhal/pendsv: Disable interrupts during a thread switch. We can actually handle interrupts during a thread switch (because we always have a valid stack), but only if those interrupts don't access any of the thread state (because the state may not correspond to the stack pointer). So to be on the safe side we disable interrupts during the very short period of the thread state+stack switch. --- stmhal/pendsv.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'stmhal/pendsv.c') diff --git a/stmhal/pendsv.c b/stmhal/pendsv.c index 4c2a14de1..200d13a5a 100644 --- a/stmhal/pendsv.c +++ b/stmhal/pendsv.c @@ -106,11 +106,14 @@ void pendsv_isr_handler(void) { ".no_obj:\n" // pendsv_object==NULL "push {r4-r11, lr}\n" "vpush {s16-s31}\n" + "mrs r5, primask\n" // save PRIMASK in r5 + "cpsid i\n" // disable interrupts while we change stacks "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 + "msr primask, r5\n" // reenable interrupts "vpop {s16-s31}\n" "pop {r4-r11, lr}\n" "bx lr\n" // return from interrupt; will return to new thread -- cgit v1.2.3