Skip to content

Commit 8d974be

Browse files
committed
pbio/os: Prototype simpler and unified async OS.
The goal is to: - Add error return value to all protothreads. - Reduce complexity and code size. - Avoid risk of overruning event queue. - Use single poll flag and no broadcasting between threads. - Cross-platform handling of events during sleep. - Make stm32, ev3, nxt all work the same way. For now the new OS also drives the contiki event loop so we can migrate processes one by one instead of breaking everything at once.
1 parent 75bb664 commit 8d974be

File tree

19 files changed

+476
-122
lines changed

19 files changed

+476
-122
lines changed

bricks/_common/micropython.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <pbio/button.h>
1111
#include <pbio/main.h>
12+
#include <pbio/os.h>
1213
#include <pbio/util.h>
1314
#include <pbio/protocol.h>
1415
#include <pbsys/main.h>
@@ -39,14 +40,9 @@
3940
// Implementation for MICROPY_EVENT_POLL_HOOK
4041
void pb_event_poll_hook(void) {
4142

42-
// Drive pbio event loop.
43-
while (pbio_do_one_event()) {
44-
}
45-
4643
mp_handle_pending(true);
4744

48-
// Platform-specific code to run on completing the poll hook.
49-
pb_event_poll_hook_leave();
45+
pbio_os_run_while_idle();
5046
}
5147

5248
// callback for when stop button is pressed in IDE or on hub

bricks/_common/sources.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ PBIO_SRC_C = $(addprefix lib/pbio/,\
215215
src/motor_process.c \
216216
src/motor/servo_settings.c \
217217
src/observer.c \
218+
src/os.c \
218219
src/parent.c \
219220
src/port.c \
220221
src/port_dcm_pup.c \

bricks/_common_stm32/mpconfigport.h

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,15 @@ typedef unsigned mp_uint_t; // must be pointer size
2424

2525
typedef long mp_off_t;
2626

27-
// We have inlined IRQ functions for efficiency (they are generally
28-
// 1 machine instruction).
29-
//
30-
// Note on IRQ state: you should not need to know the specific
31-
// value of the state variable, but rather just pass the return
32-
// value from disable_irq back to enable_irq. If you really need
33-
// to know the machine-specific values, see irq.h.
27+
#include <pbio/os.h>
3428

35-
static inline void enable_irq(mp_uint_t state) {
36-
__set_PRIMASK(state);
37-
}
38-
39-
static inline mp_uint_t disable_irq(void) {
40-
mp_uint_t state = __get_PRIMASK();
41-
__disable_irq();
42-
return state;
43-
}
44-
45-
#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
46-
#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state)
29+
#define MICROPY_BEGIN_ATOMIC_SECTION() pbio_os_hook_disable_irq()
30+
#define MICROPY_END_ATOMIC_SECTION(state) pbio_os_hook_enable_irq(state)
4731

4832
#define MICROPY_VM_HOOK_LOOP \
4933
do { \
50-
extern int pbio_do_one_event(void); \
51-
pbio_do_one_event(); \
34+
extern bool pbio_os_run_processes_once(void); \
35+
pbio_os_run_processes_once(); \
5236
} while (0);
5337

5438
#define MICROPY_GC_HOOK_LOOP(i) do { \

bricks/_common_stm32/mphalport.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,18 @@ void pb_stack_get_info(char **sstack, char **estack) {
2424
*estack = (char *)&_estack;
2525
}
2626

27-
void pb_event_poll_hook_leave(void) {
28-
// There is a possible race condition where an interrupt occurs and sets the
29-
// Contiki poll_requested flag after all events have been processed. So we
30-
// have a critical section where we disable interrupts and check see if there
31-
// are any last second events. If not, we can call __WFI(), which still wakes
32-
// up the CPU on interrupt even though interrupts are otherwise disabled.
33-
mp_uint_t state = disable_irq();
34-
if (!process_nevents()) {
35-
__WFI();
36-
}
37-
enable_irq(state);
27+
uint32_t pbio_os_hook_disable_irq(void) {
28+
mp_uint_t flags = __get_PRIMASK();
29+
__disable_irq();
30+
return flags;
31+
}
32+
33+
void pbio_os_hook_enable_irq(uint32_t flags) {
34+
__set_PRIMASK(flags);
35+
}
36+
37+
void pbio_os_hook_wait_for_interrupt(void) {
38+
__WFI();
3839
}
3940

4041
// using "internal" pbdrv variable

bricks/ev3/mpconfigport.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,15 @@ typedef unsigned mp_uint_t; // must be pointer size
7979

8080
typedef long mp_off_t;
8181

82-
#define CPSR_IRQ_MASK (1 << 7) // IRQ disable
83-
#define CPSR_FIQ_MASK (1 << 6) // FIQ disable
82+
#include <pbio/os.h>
8483

85-
#include <tiam1808/armv5/am1808/interrupt.h>
86-
87-
#define MICROPY_BEGIN_ATOMIC_SECTION() IntDisable()
88-
#define MICROPY_END_ATOMIC_SECTION(state) IntEnable(state)
84+
#define MICROPY_BEGIN_ATOMIC_SECTION() pbio_os_hook_disable_irq()
85+
#define MICROPY_END_ATOMIC_SECTION(state) pbio_os_hook_enable_irq(state)
8986

9087
#define MICROPY_VM_HOOK_LOOP \
9188
do { \
92-
extern int pbio_do_one_event(void); \
93-
pbio_do_one_event(); \
89+
extern bool pbio_os_run_processes_once(void); \
90+
pbio_os_run_processes_once(); \
9491
} while (0);
9592

9693
#define MICROPY_GC_HOOK_LOOP(i) do { \

bricks/ev3/mphalport.c

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,29 @@
1818
#include "py/mpconfig.h"
1919
#include "py/stream.h"
2020

21+
#include <tiam1808/armv5/am1808/interrupt.h>
22+
2123
void pb_stack_get_info(char **sstack, char **estack) {
2224
extern uint32_t _estack;
2325
extern uint32_t _sstack;
2426
*sstack = (char *)&_sstack;
2527
*estack = (char *)&_estack;
2628
}
2729

28-
static inline int arm_wfi(void) {
30+
uint32_t pbio_os_hook_disable_irq(void) {
31+
return IntDisable();
32+
}
33+
34+
void pbio_os_hook_enable_irq(uint32_t flags) {
35+
IntEnable(flags);
36+
}
37+
38+
void pbio_os_hook_wait_for_interrupt(void) {
2939
__asm volatile (
3040
"mov r0, #0\n"
3141
"mcr p15, 0, r0, c7, c0, 4\n" /* wait for interrupt */
3242
::: "r0"
3343
);
34-
return 0;
35-
}
36-
37-
void pb_event_poll_hook_leave(void) {
38-
// There is a possible race condition where an interrupt occurs and sets the
39-
// Contiki poll_requested flag after all events have been processed. So we
40-
// have a critical section where we disable interrupts and check see if there
41-
// are any last second events. If not, we can call __WFI(), which still wakes
42-
// up the CPU on interrupt even though interrupts are otherwise disabled.
43-
mp_uint_t state = MICROPY_BEGIN_ATOMIC_SECTION();
44-
if (!process_nevents()) {
45-
arm_wfi();
46-
}
47-
MICROPY_END_ATOMIC_SECTION(state);
4844
}
4945

5046
// Core delay function that does an efficient sleep and may switch thread context.

bricks/nxt/mpconfigport.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,23 @@ typedef long mp_off_t;
7979
// We need to provide a declaration/definition of alloca()
8080
#include <alloca.h>
8181

82-
uint32_t nx_interrupts_disable(void);
83-
void nx_interrupts_enable(uint32_t);
82+
#include <pbio/os.h>
8483

85-
#define MICROPY_BEGIN_ATOMIC_SECTION() nx_interrupts_disable()
86-
#define MICROPY_END_ATOMIC_SECTION(state) nx_interrupts_enable(state)
84+
#define MICROPY_BEGIN_ATOMIC_SECTION() pbio_os_hook_disable_irq()
85+
#define MICROPY_END_ATOMIC_SECTION(state) pbio_os_hook_enable_irq(state)
8786

8887
#define MICROPY_VM_HOOK_LOOP \
8988
do { \
90-
extern int pbio_do_one_event(void); \
91-
pbio_do_one_event(); \
89+
extern bool pbio_os_run_processes_once(void); \
90+
pbio_os_run_processes_once(); \
9291
} while (0);
9392

93+
#define MICROPY_GC_HOOK_LOOP(i) do { \
94+
if (((i) & 0xf) == 0) { \
95+
MICROPY_VM_HOOK_LOOP \
96+
} \
97+
} while (0)
98+
9499
#define MICROPY_EVENT_POLL_HOOK \
95100
do { \
96101
extern void pb_event_poll_hook(void); \

bricks/nxt/mphalport.c

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,17 @@
2020
#include "py/mpconfig.h"
2121
#include "py/stream.h"
2222

23-
void pb_event_poll_hook_leave(void) {
24-
// There is a possible race condition where an interrupt occurs and sets
25-
// the Contiki poll_requested flag after all events have been processed. So
26-
// we have a critical section where we disable interrupts and check see if
27-
// there are any last second events. If not, we can enter Idle Mode, which
28-
// still wakes up the CPU on interrupt even though interrupts are otherwise
29-
// disabled.
30-
uint32_t state = nx_interrupts_disable();
31-
32-
if (!process_nevents()) {
33-
// disable the processor clock which puts it in Idle Mode.
34-
AT91C_BASE_PMC->PMC_SCDR = AT91C_PMC_PCK;
35-
}
23+
uint32_t pbio_os_hook_disable_irq(void) {
24+
return nx_interrupts_disable();
25+
}
26+
27+
void pbio_os_hook_enable_irq(uint32_t flags) {
28+
nx_interrupts_enable(flags);
29+
}
3630

37-
nx_interrupts_enable(state);
31+
void pbio_os_hook_wait_for_interrupt(void) {
32+
// disable the processor clock which puts it in Idle Mode.
33+
AT91C_BASE_PMC->PMC_SCDR = AT91C_PMC_PCK;
3834
}
3935

4036
void pb_stack_get_info(char **sstack, char **estack) {

bricks/virtualhub/mp_port.c

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <contiki.h>
1616

1717
#include <pbio/main.h>
18+
#include <pbio/os.h>
1819
#include <pbsys/core.h>
1920
#include <pbsys/program_stop.h>
2021
#include <pbsys/status.h>
@@ -72,7 +73,8 @@ void pb_virtualhub_port_init(void) {
7273
pbsys_init();
7374

7475
pbsys_status_set(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING);
75-
while (pbio_do_one_event()) {
76+
77+
while (pbio_os_run_processes_once()) {
7678
}
7779

7880
pb_package_pybricks_init(true);
@@ -84,62 +86,44 @@ void pb_virtualhub_port_deinit(void) {
8486
pb_package_pybricks_deinit();
8587
}
8688

87-
// MICROPY_VM_HOOK_LOOP
88-
void pb_virtualhub_poll(void) {
89-
while (pbio_do_one_event()) {
90-
}
91-
}
89+
// Implementation for MICROPY_EVENT_POLL_HOOK
90+
void pb_event_poll_hook(void) {
9291

93-
// MICROPY_EVENT_POLL_HOOK
94-
void pb_virtualhub_event_poll(void) {
95-
start:
9692
mp_handle_pending(true);
9793

98-
int events_handled = 0;
99-
100-
while (pbio_do_one_event()) {
101-
events_handled++;
102-
}
94+
pbio_os_run_while_idle();
95+
}
10396

104-
// If there were any pbio events handled, don't sleep because there may
105-
// be something waiting on one of the events that was just handled.
106-
if (events_handled) {
107-
return;
108-
}
97+
// This is used instead of the uint32_t flags used in embedded builds.
98+
static sigset_t global_origmask;
10999

100+
uint32_t pbio_os_hook_disable_irq(void) {
110101
sigset_t sigmask;
111102
sigfillset(&sigmask);
103+
pthread_sigmask(SIG_SETMASK, &sigmask, &global_origmask);
104+
return 0;
105+
}
112106

113-
// disable "interrupts"
114-
sigset_t origmask;
115-
pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
116-
117-
if (process_nevents()) {
118-
// something was scheduled since the event loop above
119-
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
120-
goto start;
121-
}
107+
void pbio_os_hook_enable_irq(uint32_t flags) {
108+
pthread_sigmask(SIG_SETMASK, &global_origmask, NULL);
109+
}
122110

111+
void pbio_os_hook_wait_for_interrupt(void) {
123112
struct timespec timeout = {
124113
.tv_sec = 0,
125114
.tv_nsec = 100000,
126115
};
127-
128116
// "sleep" with "interrupts" enabled
129117
MP_THREAD_GIL_EXIT();
130-
pselect(0, NULL, NULL, NULL, &timeout, &origmask);
118+
pselect(0, NULL, NULL, NULL, &timeout, &global_origmask);
131119
MP_THREAD_GIL_ENTER();
132-
133-
// restore "interrupts"
134-
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
135120
}
136121

137-
138122
void pb_virtualhub_delay_us(mp_uint_t us) {
139123
mp_uint_t start = mp_hal_ticks_us();
140124

141125
while (mp_hal_ticks_us() - start < us) {
142-
pb_virtualhub_poll();
126+
MICROPY_VM_HOOK_LOOP;
143127
}
144128
}
145129

bricks/virtualhub/mpconfigvariant.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@
9797
} while (0)
9898

9999
#define MICROPY_VM_HOOK_LOOP do { \
100-
extern void pb_virtualhub_poll(void); \
101-
pb_virtualhub_poll(); \
100+
extern bool pbio_os_run_processes_once(void); \
101+
pbio_os_run_processes_once(); \
102102
} while (0);
103103

104104
#define MICROPY_GC_HOOK_LOOP(i) do { \
@@ -108,6 +108,6 @@
108108
} while (0)
109109

110110
#define MICROPY_EVENT_POLL_HOOK do { \
111-
extern void pb_virtualhub_event_poll(void); \
112-
pb_virtualhub_event_poll(); \
111+
extern void pb_event_poll_hook(void); \
112+
pb_event_poll_hook(); \
113113
} while (0);

0 commit comments

Comments
 (0)