ports/mimxrt: Add thread support.

Signed-off-by: tonyzhangnxp <tony.zhang@nxp.com>
pull/13755/head
tonyzhangnxp 2024-02-27 17:32:20 +08:00
rodzic 31e131bd71
commit d092ee8480
19 zmienionych plików z 754 dodań i 10 usunięć

Wyświetl plik

@ -215,6 +215,10 @@ SRC_C += \
systick.c \
ticks.c \
tusb_port.c \
mpthreadport.c \
pybthread.c\
gccollect.c\
SHARED_SRC_C += \
shared/libc/printf.c \

Wyświetl plik

@ -8,3 +8,5 @@ MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB
MICROPY_PY_NETWORK_NINAW10 ?= 1
MICROPY_PY_SSL ?= 1
MICROPY_SSL_MBEDTLS ?= 1
CFLAGS += -DMICROPY_PY_THREAD=0

Wyświetl plik

@ -14,6 +14,8 @@ else
JLINK_CONNECTION_SETTINGS = -USB
endif
CFLAGS += -DMICROPY_PY_THREAD=0
deploy_jlink: $(BUILD)/firmware.hex
$(Q)$(TOUCH) $(JLINK_COMMANDER_SCRIPT)
$(ECHO) "ExitOnError 1" > $(JLINK_COMMANDER_SCRIPT)

Wyświetl plik

@ -6,3 +6,5 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash
MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB
MICROPY_BOOT_BUFFER_SIZE = (32 * 1024)
CFLAGS += -DMICROPY_PY_THREAD=0

Wyświetl plik

@ -3,11 +3,14 @@
#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-1070evk"
#if MICROPY_PY_THREAD
#else
#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(bool); \
mp_handle_pending(true); \
} while (0);
#endif
// MIMXRT1170_EVK has 2 user LEDs
#define MICROPY_HW_LED1_PIN (pin_GPIO_AD_04)

Wyświetl plik

@ -14,6 +14,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
CFLAGS += -DMICROPY_PY_THREAD=1
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
CFLAGS += -DCPU_MIMXRT1176DVMAA_cm7 \

Wyświetl plik

@ -7,6 +7,7 @@ MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB
MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB
CFLAGS += -DMICROPY_HW_FLASH_DQS=kFlexSPIReadSampleClk_LoopbackInternally
CFLAGS += -DMICROPY_PY_THREAD=0
SRC_C += \
hal/flexspi_nor_flash.c \

Wyświetl plik

@ -0,0 +1,50 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 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 "py/gc.h"
#include "py/mpthread.h"
#include "shared/runtime/gchelper.h"
#include "shared/runtime/softtimer.h"
#include "gccollect.h"
void gc_collect(void) {
// start the GC
gc_collect_start();
// trace the stack and registers
gc_helper_collect_regs_and_stack();
// trace root pointers from any threads
#if MICROPY_PY_THREAD
mp_thread_gc_others();
#endif
// trace soft timer nodes
soft_timer_gc_mark_all();
// end the GC
gc_collect_end();
}

Wyświetl plik

@ -0,0 +1,44 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 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_STM32_GCCOLLECT_H
#define MICROPY_INCLUDED_STM32_GCCOLLECT_H
// variables defining memory layout
// (these probably belong somewhere else...)
extern uint32_t _etext;
extern uint32_t _sidata;
extern uint32_t _ram_start;
extern uint32_t _sdata;
extern uint32_t _edata;
extern uint32_t _sbss;
extern uint32_t _ebss;
extern uint32_t _heap_start;
extern uint32_t _heap_end;
extern uint32_t _sstack;
extern uint32_t _estack;
extern uint32_t _ram_end;
#endif // MICROPY_INCLUDED_STM32_GCCOLLECT_H

Wyświetl plik

@ -56,6 +56,10 @@
#include "systick.h"
#include "extmod/modnetwork.h"
#if MICROPY_PY_THREAD
static pyb_thread_t pyb_thread_main;
#endif
extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end;
void board_init(void);
@ -64,7 +68,9 @@ int main(void) {
board_init();
ticks_init();
pendsv_init();
#if MICROPY_PY_THREAD
pyb_thread_init(&pyb_thread_main);
#endif
#if MICROPY_PY_LWIP
// lwIP doesn't allow to reinitialise itself by subsequent calls to this function
// because the system timeout list (next_timeout) is only ever reset by BSS clearing.
@ -96,6 +102,11 @@ int main(void) {
led_init();
#endif
// Python threading init
#if MICROPY_PY_THREAD
mp_thread_init();
#endif
mp_stack_set_top(&_estack);
mp_stack_set_limit(&_estack - &_sstack - 1024);
@ -163,12 +174,6 @@ int main(void) {
return 0;
}
void gc_collect(void) {
gc_collect_start();
gc_helper_collect_regs_and_stack();
gc_collect_end();
}
void nlr_jump_fail(void *val) {
for (;;) {
}

Wyświetl plik

@ -134,6 +134,11 @@ uint32_t trng_random_u32(void);
#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL)
// #define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL)
#ifndef MICROPY_PY_THREAD
#define MICROPY_PY_THREAD (1)
#endif
#ifndef MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1)
#endif
@ -182,6 +187,24 @@ extern const struct _mp_obj_type_t mp_network_cyw43_type;
#define MP_STATE_PORT MP_STATE_VM
#if MICROPY_PY_THREAD
#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(bool); \
mp_handle_pending(true); \
if (pyb_thread_enabled) { \
MP_THREAD_GIL_EXIT(); \
pyb_thread_yield(); \
MP_THREAD_GIL_ENTER(); \
} else { \
__WFE(); \
} \
} while (0);
#define MICROPY_THREAD_YIELD() pyb_thread_yield()
#else
// Miscellaneous settings
#ifndef MICROPY_EVENT_POLL_HOOK
#define MICROPY_EVENT_POLL_HOOK \
@ -192,6 +215,10 @@ extern const struct _mp_obj_type_t mp_network_cyw43_type;
} while (0);
#endif
#define MICROPY_THREAD_YIELD()
#endif
#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1))
#define MP_HAL_CLEANINVALIDATE_DCACHE(addr, size) \

Wyświetl plik

@ -34,7 +34,7 @@
#include "ticks.h"
#include "tusb.h"
#include "fsl_snvs_lp.h"
#include "led.h"
#ifndef MICROPY_HW_STDIN_BUFFER_LEN
#define MICROPY_HW_STDIN_BUFFER_LEN 512
#endif
@ -112,6 +112,24 @@ int mp_hal_stdin_rx_chr(void) {
}
}
NORETURN void boardctrl_fatal_error(const char *msg) {
for (volatile uint delay = 0; delay < 10000000; delay++) {
}
led_state(1, 1);
mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14);
mp_hal_stdout_tx_strn(msg, strlen(msg));
for (uint i = 0;;) {
led_toggle(1);
for (volatile uint delay = 0; delay < 10000000; delay++) {
}
if (i >= 16) {
// to conserve power
__WFI();
}
}
}
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
mp_uint_t ret = len;
bool did_write = false;

Wyświetl plik

@ -154,5 +154,5 @@ void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]);
void mp_hal_get_mac(int idx, uint8_t buf[6]);
void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest);
void mp_hal_get_unique_id(uint8_t id[]);
NORETURN void boardctrl_fatal_error(const char *msg);
#endif // MICROPY_INCLUDED_MIMXRT_MPHALPORT_H

Wyświetl plik

@ -0,0 +1,99 @@
/*
* 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/runtime.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);
for (pyb_thread_t *th = pyb_thread_all; th != NULL; th = th->all_next) {
gc_collect_root((void **)&th, 1);
gc_collect_root(&th->arg, 1);
gc_collect_root(&th->stack, 1);
if (th != pyb_thread_cur) {
gc_collect_root(th->stack, th->stack_len);
}
}
mp_thread_mutex_unlock(&thread_mutex);
}
mp_uint_t mp_thread_get_id(void) {
return (uint32_t)pyb_thread_cur;
}
mp_uint_t 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);
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("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;
return id;
}
void mp_thread_start(void) {
}
void mp_thread_finish(void) {
}
#endif // MICROPY_PY_THREAD

Wyświetl plik

@ -0,0 +1,53 @@
/*
* 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 "py/mpthread.h"
#include "pybthread.h"
typedef pyb_mutex_t mp_thread_mutex_t;
void mp_thread_init(void);
void mp_thread_gc_others(void);
static inline void mp_thread_set_state(struct _mp_state_thread_t *state) {
pyb_thread_set_local(state);
}
static inline struct _mp_state_thread_t *mp_thread_get_state(void) {
return pyb_thread_get_local();
}
static inline void mp_thread_mutex_init(mp_thread_mutex_t *m) {
pyb_mutex_init(m);
}
static inline int mp_thread_mutex_lock(mp_thread_mutex_t *m, int wait) {
return pyb_mutex_lock(m, wait);
}
static inline void mp_thread_mutex_unlock(mp_thread_mutex_t *m) {
pyb_mutex_unlock(m);
}

Wyświetl plik

@ -30,11 +30,17 @@
#include "pendsv.h"
#include "irq.h"
void *pendsv_object;
#if defined(PENDSV_DISPATCH_NUM_SLOTS)
uint32_t pendsv_dispatch_active;
pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS];
#endif
void pendsv_init(void) {
#if defined(PENDSV_DISPATCH_NUM_SLOTS)
pendsv_dispatch_active = false;
#endif
// set PendSV interrupt at lowest priority
NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV);
}
@ -42,10 +48,11 @@ void pendsv_init(void) {
#if defined(PENDSV_DISPATCH_NUM_SLOTS)
void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) {
pendsv_dispatch_table[slot] = f;
pendsv_dispatch_active = true;
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
void PendSV_Handler(void) {
void pendsv_dispatch_handler(void) {
for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) {
if (pendsv_dispatch_table[i] != NULL) {
pendsv_dispatch_t f = pendsv_dispatch_table[i];
@ -55,3 +62,99 @@ void PendSV_Handler(void) {
}
}
#endif
__attribute__((naked)) void PendSV_Handler(void) {
// Handle a PendSV interrupt
//
// For the case of an asynchronous exception, re-jig the
// stack so that when we return from this interrupt handler
// it returns instead to nlr_jump with argument pendsv_object
// note that stack has a different layout if DEBUG is enabled
//
// For the case of a thread switch, swap stacks.
//
// on entry to this (naked) function, stack has the following layout:
//
// stack layout with DEBUG disabled:
// 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=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]: ?
__asm volatile (
#if defined(PENDSV_DISPATCH_NUM_SLOTS)
// Check if there are any pending calls to dispatch to
"ldr r1, pendsv_dispatch_active_ptr\n"
"ldr r0, [r1]\n"
"cmp r0, #0\n"
"beq .no_dispatch\n"
"mov r2, #0\n"
"str r2, [r1]\n" // clear pendsv_dispatch_active
"b pendsv_dispatch_handler\n" // jump to the handler
".no_dispatch:\n"
#endif
// Check if there is an active object to throw via nlr_jump
"ldr r1, pendsv_object_ptr\n"
"ldr r0, [r1]\n"
"cmp r0, #0\n"
"beq .no_obj\n"
#if defined(PENDSV_DEBUG)
"str r0, [sp, #8]\n" // store to r0 on stack
#else
"str r0, [sp, #0]\n" // store to r0 on stack
#endif
"mov r0, #0\n"
"str r0, [r1]\n" // clear pendsv_object
"ldr r0, nlr_jump_ptr\n"
#if defined(PENDSV_DEBUG)
"str r0, [sp, #32]\n" // store to pc on stack
#else
"str r0, [sp, #24]\n" // store to pc on stack
#endif
"bx lr\n" // return from interrupt; will return to nlr_jump
".no_obj:\n" // pendsv_object==NULL
#if MICROPY_PY_THREAD
// Do a thread context switch
"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" // re-enable interrupts
"vpop {s16-s31}\n"
"pop {r4-r11, lr}\n"
"bx lr\n" // return from interrupt; will return to new thread
#else
// Spurious pendsv, just return
"bx lr\n"
#endif
// Data
".align 2\n"
#if defined(PENDSV_DISPATCH_NUM_SLOTS)
"pendsv_dispatch_active_ptr: .word pendsv_dispatch_active\n"
#endif
"pendsv_object_ptr: .word pendsv_object\n"
"nlr_jump_ptr: .word nlr_jump\n"
);
}

Wyświetl plik

@ -0,0 +1,238 @@
/*
* 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 "py/obj.h"
#include "gccollect.h"
#include "irq.h"
#include "pybthread.h"
#include "mphalport.h"
#if MICROPY_PY_THREAD
#define PYB_MUTEX_UNLOCKED ((void *)0)
#define PYB_MUTEX_LOCKED ((void *)1)
#ifndef MICROPY_BOARD_FATAL_ERROR
#define MICROPY_BOARD_FATAL_ERROR boardctrl_fatal_error
#endif
// These macros are used when we only need to protect against a thread
// switch; other interrupts are still allowed to proceed.
#define RAISE_IRQ_PRI() raise_irq_pri(IRQ_PRI_PENDSV)
#define RESTORE_IRQ_PRI(state) restore_irq_pri(state)
volatile int pyb_thread_enabled;
pyb_thread_t *volatile pyb_thread_all;
pyb_thread_t *volatile pyb_thread_cur;
static inline void pyb_thread_add_to_runable(pyb_thread_t *thread) {
thread->run_prev = pyb_thread_cur->run_prev;
thread->run_next = pyb_thread_cur;
pyb_thread_cur->run_prev->run_next = thread;
pyb_thread_cur->run_prev = thread;
}
static inline void pyb_thread_remove_from_runable(pyb_thread_t *thread) {
if (thread->run_next == thread) {
MICROPY_BOARD_FATAL_ERROR("deadlock");
}
thread->run_prev->run_next = thread->run_next;
thread->run_next->run_prev = thread->run_prev;
}
void pyb_thread_init(pyb_thread_t *thread) {
pyb_thread_enabled = 0;
pyb_thread_all = thread;
pyb_thread_cur = thread;
thread->sp = NULL; // will be set when this thread switches out
thread->local_state = 0; // will be set by mp_thread_init
thread->arg = NULL;
thread->stack = &_sstack;
thread->stack_len = ((uint32_t)&_estack - (uint32_t)&_sstack) / sizeof(uint32_t);
thread->all_next = NULL;
thread->run_prev = thread;
thread->run_next = thread;
thread->queue_next = NULL;
}
void pyb_thread_deinit() {
uint32_t irq_state = disable_irq();
pyb_thread_enabled = 0;
pyb_thread_all = pyb_thread_cur;
pyb_thread_cur->all_next = NULL;
pyb_thread_cur->run_prev = pyb_thread_cur;
pyb_thread_cur->run_next = pyb_thread_cur;
enable_irq(irq_state);
}
static void pyb_thread_terminate(void) {
uint32_t irq_state = disable_irq();
pyb_thread_t *thread = pyb_thread_cur;
// take current thread off the run list
pyb_thread_remove_from_runable(thread);
// take current thread off the list of all threads
for (pyb_thread_t **n = (pyb_thread_t **)&pyb_thread_all;; n = &(*n)->all_next) {
if (*n == thread) {
*n = thread->all_next;
break;
}
}
// clean pointers as much as possible to help GC
thread->all_next = NULL;
thread->queue_next = NULL;
thread->stack = NULL;
if (pyb_thread_all->all_next == NULL) {
// only 1 thread left
pyb_thread_enabled = 0;
}
// thread switch will occur after we enable irqs
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
enable_irq(irq_state);
// should not return
MICROPY_BOARD_FATAL_ERROR("could not terminate");
}
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;
thread->queue_next = NULL;
uint32_t irq_state = disable_irq();
pyb_thread_enabled = 1;
thread->all_next = pyb_thread_all;
pyb_thread_all = thread;
pyb_thread_add_to_runable(thread);
enable_irq(irq_state);
return (uint32_t)thread; // success
}
void pyb_thread_dump(const mp_print_t *print) {
if (!pyb_thread_enabled) {
mp_printf(print, "THREAD: only main thread\n");
} else {
mp_printf(print, "THREAD:\n");
for (pyb_thread_t *th = pyb_thread_all; th != NULL; th = th->all_next) {
bool runable = false;
for (pyb_thread_t *th2 = pyb_thread_cur;; th2 = th2->run_next) {
if (th == th2) {
runable = true;
break;
}
if (th2->run_next == pyb_thread_cur) {
break;
}
}
mp_printf(print, " id=%p sp=%p sz=%u", th, th->stack, th->stack_len);
if (runable) {
mp_printf(print, " (runable)");
}
mp_printf(print, "\n");
}
}
}
// 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->run_next;
pyb_thread_cur->timeslice = 4; // in milliseconds
return pyb_thread_cur->sp;
}
void pyb_mutex_init(pyb_mutex_t *m) {
*m = PYB_MUTEX_UNLOCKED;
}
int pyb_mutex_lock(pyb_mutex_t *m, int wait) {
uint32_t irq_state = RAISE_IRQ_PRI();
if (*m == PYB_MUTEX_UNLOCKED) {
// mutex is available
*m = PYB_MUTEX_LOCKED;
RESTORE_IRQ_PRI(irq_state);
} else {
// mutex is locked
if (!wait) {
RESTORE_IRQ_PRI(irq_state);
return 0; // failed to lock mutex
}
if (*m == PYB_MUTEX_LOCKED) {
*m = pyb_thread_cur;
} else {
for (pyb_thread_t *n = *m;; n = n->queue_next) {
if (n->queue_next == NULL) {
n->queue_next = pyb_thread_cur;
break;
}
}
}
pyb_thread_cur->queue_next = NULL;
// take current thread off the run list
pyb_thread_remove_from_runable(pyb_thread_cur);
// thread switch will occur after we enable irqs
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
RESTORE_IRQ_PRI(irq_state);
// when we come back we have the mutex
}
return 1; // have mutex
}
void pyb_mutex_unlock(pyb_mutex_t *m) {
uint32_t irq_state = RAISE_IRQ_PRI();
if (*m == PYB_MUTEX_LOCKED) {
// no threads are blocked on the mutex
*m = PYB_MUTEX_UNLOCKED;
} else {
// at least one thread is blocked on this mutex
pyb_thread_t *th = *m;
if (th->queue_next == NULL) {
// no other threads are blocked
*m = PYB_MUTEX_LOCKED;
} else {
// at least one other thread is still blocked
*m = th->queue_next;
}
// put unblocked thread on runable list
pyb_thread_add_to_runable(th);
}
RESTORE_IRQ_PRI(irq_state);
}
#endif // MICROPY_PY_THREAD

Wyświetl plik

@ -0,0 +1,79 @@
/*
* 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_STM32_PYBTHREAD_H
#define MICROPY_INCLUDED_STM32_PYBTHREAD_H
#include "py/mpprint.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
uint32_t timeslice;
struct _pyb_thread_t *all_next;
struct _pyb_thread_t *run_prev;
struct _pyb_thread_t *run_next;
struct _pyb_thread_t *queue_next;
} pyb_thread_t;
typedef pyb_thread_t *pyb_mutex_t;
extern volatile int pyb_thread_enabled;
extern pyb_thread_t *volatile pyb_thread_all;
extern pyb_thread_t *volatile pyb_thread_cur;
void pyb_thread_init(pyb_thread_t *th);
void pyb_thread_deinit();
uint32_t pyb_thread_new(pyb_thread_t *th, void *stack, size_t stack_len, void *entry, void *arg);
void pyb_thread_dump(const mp_print_t *print);
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) {
if (pyb_thread_cur->run_next == pyb_thread_cur) {
__WFI();
} else {
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
}
void pyb_mutex_init(pyb_mutex_t *m);
int pyb_mutex_lock(pyb_mutex_t *m, int wait);
void pyb_mutex_unlock(pyb_mutex_t *m);
#endif // MICROPY_INCLUDED_STM32_PYBTHREAD_H

Wyświetl plik

@ -51,6 +51,18 @@ void SysTick_Handler(void) {
if (soft_timer_next == uw_tick) {
pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler);
}
#if MICROPY_PY_THREAD
if (pyb_thread_enabled) {
if (pyb_thread_cur->timeslice == 0) {
if (pyb_thread_cur->run_next != pyb_thread_cur) {
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}
} else {
--pyb_thread_cur->timeslice;
}
}
#endif
}
bool systick_has_passed(uint32_t start_tick, uint32_t delay_ms) {