From 01156d510c728408be4fd3100bcee18e8985ac00 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Sat, 1 Feb 2014 16:04:34 +0000
Subject: [PATCH] stm: Add support for ctrl-C to interrupt running Python.

Using PendSV interrupt at lowest priority, code can now raise an
exception during an interrupt by calling pendsv_nlr_jump.  The exception
will be raised when all interrupts are finished.  This is used to trap
ctrl-C from the USB VCP to break out of running Python code.
---
 stm/Makefile       |  1 +
 stm/led.c          |  1 +
 stm/main.c         | 17 +++++++++++++++-
 stm/pendsv.c       | 49 ++++++++++++++++++++++++++++++++++++++++++++++
 stm/pendsv.h       |  3 +++
 stm/stm32fxxx_it.c |  2 ++
 stm/usb.c          | 22 +++++++++++++++++++++
 stm/usb.h          |  5 +++++
 8 files changed, 99 insertions(+), 1 deletion(-)
 create mode 100644 stm/pendsv.c
 create mode 100644 stm/pendsv.h

diff --git a/stm/Makefile b/stm/Makefile
index 195825238..003bf32e7 100644
--- a/stm/Makefile
+++ b/stm/Makefile
@@ -45,6 +45,7 @@ SRC_C = \
 	string0.c \
 	malloc0.c \
 	systick.c  \
+	pendsv.c \
 	gccollect.c \
 	lexerfatfs.c \
 	led.c \
diff --git a/stm/led.c b/stm/led.c
index 96fac58e2..c517c3f6e 100644
--- a/stm/led.c
+++ b/stm/led.c
@@ -103,6 +103,7 @@ void led_toggle(pyb_led_t led) {
             return;
     }
 
+    // XXX this assumes LED is driven by a low MCU output (true for PYBv3, false for PYBv4)
     if (!(port->ODR & pin)) {
         // turn LED off
         PYB_LED_OFF(port, pin);
diff --git a/stm/main.c b/stm/main.c
index 313a868c4..b7d201cf0 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -30,6 +30,7 @@
 #include "gc.h"
 #include "gccollect.h"
 #include "systick.h"
+#include "pendsv.h"
 #include "led.h"
 #include "servo.h"
 #include "lcd.h"
@@ -327,7 +328,7 @@ int readline(vstr_t *line, const char *prompt) {
             }
         }
         if (escape == 0) {
-            if (c == 4 && vstr_len(line) == len) {
+            if (c == VCP_CHAR_CTRL_D && vstr_len(line) == len) {
                 return 0;
             } else if (c == '\r') {
                 stdout_tx_str("\r\n");
@@ -435,10 +436,14 @@ void do_repl(void) {
                 nlr_buf_t nlr;
                 uint32_t start = sys_tick_counter;
                 if (nlr_push(&nlr) == 0) {
+                    usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C); // allow ctrl-C to interrupt us
                     rt_call_function_0(module_fun);
+                    usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
                     nlr_pop();
                 } else {
                     // uncaught exception
+                    // FIXME it could be that an interrupt happens just before we disable it here
+                    usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
                     mp_obj_print_exception((mp_obj_t)nlr.ret_val);
                 }
 
@@ -488,11 +493,15 @@ bool do_file(const char *filename) {
 
     nlr_buf_t nlr;
     if (nlr_push(&nlr) == 0) {
+        usb_vcp_set_interrupt_char(VCP_CHAR_CTRL_C); // allow ctrl-C to interrupt us
         rt_call_function_0(module_fun);
+        usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
         nlr_pop();
         return true;
     } else {
         // uncaught exception
+        // FIXME it could be that an interrupt happens just before we disable it here
+        usb_vcp_set_interrupt_char(VCP_CHAR_NONE); // disable interrupt
         mp_obj_print_exception((mp_obj_t)nlr.ret_val);
         return false;
     }
@@ -560,6 +569,10 @@ mp_obj_t pyb_rng_get(void) {
     return mp_obj_new_int(RNG_GetRandomNumber() >> 16);
 }
 
+mp_obj_t pyb_millis(void) {
+    return mp_obj_new_int(sys_tick_counter);
+}
+
 int main(void) {
     // TODO disable JTAG
 
@@ -592,6 +605,7 @@ int main(void) {
 
     // basic sub-system init
     sys_tick_init();
+    pendsv_init();
     led_init();
 
 #if MICROPY_HW_ENABLE_RTC
@@ -693,6 +707,7 @@ soft_reset:
         rt_store_attr(m, MP_QSTR_Usart, rt_make_function_n(2, pyb_Usart));
         rt_store_attr(m, qstr_from_str("ADC_all"), (mp_obj_t)&pyb_ADC_all_obj);
         rt_store_attr(m, MP_QSTR_ADC, (mp_obj_t)&pyb_ADC_obj);
+        rt_store_attr(m, qstr_from_str("millis"), rt_make_function_n(0, pyb_millis));
         rt_store_name(MP_QSTR_pyb, m);
 
         rt_store_name(MP_QSTR_open, rt_make_function_n(2, pyb_io_open));
diff --git a/stm/pendsv.c b/stm/pendsv.c
new file mode 100644
index 000000000..e8e5b840c
--- /dev/null
+++ b/stm/pendsv.c
@@ -0,0 +1,49 @@
+#include <stdlib.h>
+#include <stm32f4xx.h>
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+#include "pendsv.h"
+
+static mp_obj_t pendsv_object = MP_OBJ_NULL;
+
+void pendsv_init(void) {
+    // set PendSV interrupt at lowest priority
+    NVIC_SetPriority(PendSV_IRQn, 0xff);
+}
+
+// call this function to raise a pending exception during an interrupt
+// it 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(mp_obj_t o) {
+    pendsv_object = o;
+    SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
+}
+
+void pendsv_isr_handler(void) {
+    // re-jig the stack so that when we return from this interrupt handler
+    // it returns instead to nlr_jump with argument pendsv_object
+    __asm volatile (
+        "ldr r0, pendsv_object_ptr\n"
+        "ldr r0, [r0]\n"
+        "str r0, [sp, #0]\n"
+        "ldr r0, nlr_jump_ptr\n"
+        "str r0, [sp, #24]\n"
+        "bx lr\n"
+        ".align 2\n"
+        "pendsv_object_ptr: .word pendsv_object\n"
+        "nlr_jump_ptr: .word nlr_jump\n"
+    );
+
+    /*
+    sp[0] = r0 = exception to raise
+    sp[6] = pc = nlr_jump
+    uint32_t x[2] = {0x424242, 0xdeaddead};
+    printf("PendSV: %p\n", x);
+    for (uint32_t *p = (uint32_t*)(((uint32_t)x - 15) & 0xfffffff0), i = 64; i > 0; p += 4, i -= 4) {
+        printf(" %p: %08x %08x %08x %08x\n", p, p[0], p[1], p[2], p[3]);
+    }
+    */
+}
diff --git a/stm/pendsv.h b/stm/pendsv.h
new file mode 100644
index 000000000..b07ab7137
--- /dev/null
+++ b/stm/pendsv.h
@@ -0,0 +1,3 @@
+void pendsv_init(void);
+void pendsv_nlr_jump(mp_obj_t o);
+void pendsv_isr_handler(void);
diff --git a/stm/stm32fxxx_it.c b/stm/stm32fxxx_it.c
index 7b5030bc6..97cdee6a8 100644
--- a/stm/stm32fxxx_it.c
+++ b/stm/stm32fxxx_it.c
@@ -151,6 +151,8 @@ void DebugMon_Handler(void)
   */
 void PendSV_Handler(void)
 {
+    extern void pendsv_isr_handler(void);
+    pendsv_isr_handler();
 }
 
 /**
diff --git a/stm/usb.c b/stm/usb.c
index 802a70c6d..97124f549 100644
--- a/stm/usb.c
+++ b/stm/usb.c
@@ -11,6 +11,7 @@
 #include "mpconfig.h"
 #include "qstr.h"
 #include "obj.h"
+#include "pendsv.h"
 #include "usb.h"
 
 #ifdef USE_DEVICE_MODE
@@ -23,6 +24,8 @@ static int dev_is_enabled = 0;
 static char rx_buf[64];
 static int rx_buf_in;
 static int rx_buf_out;
+static int interrupt_char = VCP_CHAR_NONE;
+mp_obj_t mp_const_vcp_interrupt = MP_OBJ_NULL;
 
 void pyb_usb_dev_init(void) {
 #ifdef USE_DEVICE_MODE
@@ -33,7 +36,11 @@ void pyb_usb_dev_init(void) {
     }
     rx_buf_in = 0;
     rx_buf_out = 0;
+    interrupt_char = VCP_CHAR_NONE;
     dev_is_enabled = 1;
+
+    // create an exception object for interrupting by VCP
+    mp_const_vcp_interrupt = mp_obj_new_exception(qstr_from_str("VCPInterrupt"));
 #endif
 }
 
@@ -41,9 +48,24 @@ bool usb_vcp_is_enabled(void) {
     return dev_is_enabled;
 }
 
+void usb_vcp_set_interrupt_char(int c) {
+    if (dev_is_enabled) {
+        interrupt_char = c;
+    }
+}
+
 void usb_vcp_receive(const char *buf, uint32_t len) {
     if (dev_is_enabled) {
         for (int i = 0; i < len; i++) {
+
+            // catch special interrupt character
+            if (buf[i] == interrupt_char) {
+                // raise exception when interrupts are finished
+                pendsv_nlr_jump(mp_const_vcp_interrupt);
+                interrupt_char = VCP_CHAR_NONE;
+                continue;
+            }
+
             rx_buf[rx_buf_in++] = buf[i];
             if (rx_buf_in >= sizeof(rx_buf)) {
                 rx_buf_in = 0;
diff --git a/stm/usb.h b/stm/usb.h
index c3b39ca84..1f263bd07 100644
--- a/stm/usb.h
+++ b/stm/usb.h
@@ -1,5 +1,10 @@
+#define VCP_CHAR_NONE   (0)
+#define VCP_CHAR_CTRL_C (3)
+#define VCP_CHAR_CTRL_D (4)
+
 void pyb_usb_dev_init(void);
 bool usb_vcp_is_enabled(void);
+void usb_vcp_set_interrupt_char(int c);
 int usb_vcp_rx_any(void);
 char usb_vcp_rx_get(void);
 void usb_vcp_send_str(const char* str);
-- 
GitLab