From 3de24619b288d73961d76cc5ab738e340b18e37f Mon Sep 17 00:00:00 2001
From: Sebastien Bourdeauducq <sb@m-labs.hk>
Date: Sun, 21 Sep 2014 23:36:10 +0800
Subject: [PATCH] corecom: exception support

---
 artiq/devices/core.py           |  2 +-
 artiq/devices/corecom_dummy.py  |  7 ++++++-
 artiq/devices/corecom_serial.py | 17 +++++++++++------
 soc/runtime/corecom.h           |  8 +++++++-
 soc/runtime/corecom_serial.c    | 21 ++++++++++++++++++---
 soc/runtime/main.c              | 22 ++++++++++++++++------
 6 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/artiq/devices/core.py b/artiq/devices/core.py
index 8630ef4d0..8635f75a6 100644
--- a/artiq/devices/core.py
+++ b/artiq/devices/core.py
@@ -70,4 +70,4 @@ class Core:
         binary = get_runtime_binary(self.runtime_env, func_def)
         self.core_com.load(binary)
         self.core_com.run(func_def.name)
-        self.core_com.serve(rpc_map)
+        self.core_com.serve(rpc_map, exception_map)
diff --git a/artiq/devices/corecom_dummy.py b/artiq/devices/corecom_dummy.py
index 003daba4b..306865da6 100644
--- a/artiq/devices/corecom_dummy.py
+++ b/artiq/devices/corecom_dummy.py
@@ -25,9 +25,14 @@ class CoreCom:
     def run(self, kname):
         print("RUN: "+kname)
 
-    def serve(self, rpc_map):
+    def serve(self, rpc_map, exception_map):
         print("================")
         print(" RPC map")
         print("================")
         for k, v in sorted(rpc_map.items(), key=itemgetter(0)):
             print(str(k)+" -> "+str(v))
+        print("================")
+        print(" Exception map")
+        print("================")
+        for k, v in sorted(exception_map.items(), key=itemgetter(0)):
+            print(str(k)+" -> "+str(v))
diff --git a/artiq/devices/corecom_serial.py b/artiq/devices/corecom_serial.py
index 86c2aea46..019c7803e 100644
--- a/artiq/devices/corecom_serial.py
+++ b/artiq/devices/corecom_serial.py
@@ -31,8 +31,9 @@ class _D2HMsgType(Enum):
     CRC_FAILED = 6
     OBJECT_UNRECOGNIZED = 7
     KERNEL_FINISHED = 8
-    KERNEL_STARTUP_FAILED = 9
-    RPC_REQUEST = 10
+    KERNEL_EXCEPTION = 9
+    KERNEL_STARTUP_FAILED = 10
+    RPC_REQUEST = 11
 
 
 def _write_exactly(f, data):
@@ -141,12 +142,10 @@ class CoreCom:
             _write_exactly(self.port, struct.pack("b", ord(c)))
         logger.debug("running kernel: {}".format(kname))
 
-    def serve(self, rpc_map):
+    def serve(self, rpc_map, exception_map):
         while True:
             msg = self._get_device_msg()
-            if msg == _D2HMsgType.KERNEL_FINISHED:
-                return
-            elif msg == _D2HMsgType.RPC_REQUEST:
+            if msg == _D2HMsgType.RPC_REQUEST:
                 rpc_num, n_args = struct.unpack(">hb",
                                                 _read_exactly(self.port, 3))
                 args = []
@@ -160,5 +159,11 @@ class CoreCom:
                 _write_exactly(self.port, struct.pack(">l", r))
                 logger.debug("rpc service: {} ({}) == {}".format(
                     rpc_num, args, r))
+            elif msg == _D2HMsgType.KERNEL_EXCEPTION:
+                (exception_num, ) = struct.unpack(">l",
+                                                  _read_exactly(self.port, 4))
+                raise exception_map[exception_num]
+            elif msg == _D2HMsgType.KERNEL_FINISHED:
+                return
             else:
                 raise IOError("Incorrect request from device: "+str(msg))
diff --git a/soc/runtime/corecom.h b/soc/runtime/corecom.h
index 1bc552bba..fe5a67439 100644
--- a/soc/runtime/corecom.h
+++ b/soc/runtime/corecom.h
@@ -1,8 +1,14 @@
 #ifndef __CORECOM_H
 #define __CORECOM_H
 
+enum {
+	KERNEL_RUN_FINISHED,
+	KERNEL_RUN_EXCEPTION,
+	KERNEL_RUN_STARTUP_FAILED
+};
+
 typedef int (*object_loader)(void *, int);
-typedef int (*kernel_runner)(const char *);
+typedef int (*kernel_runner)(const char *, int *);
 
 void corecom_serve(object_loader load_object, kernel_runner run_kernel);
 int corecom_rpc(int rpc_num, int n_args, ...);
diff --git a/soc/runtime/corecom_serial.c b/soc/runtime/corecom_serial.c
index b1457f6e0..fdb445a8d 100644
--- a/soc/runtime/corecom_serial.c
+++ b/soc/runtime/corecom_serial.c
@@ -25,6 +25,7 @@ enum {
     MSGTYPE_OBJECT_UNRECOGNIZED,
 
     MSGTYPE_KERNEL_FINISHED,
+    MSGTYPE_KERNEL_EXCEPTION,
     MSGTYPE_KERNEL_STARTUP_FAILED,
 
     MSGTYPE_RPC_REQUEST,
@@ -114,7 +115,7 @@ static void receive_and_run_kernel(kernel_runner run_kernel)
     int length;
     int i;
     char kernel_name[256];
-    int r;
+    int r, eid;
 
     length = receive_int();
     if(length > (sizeof(kernel_name)-1)) {
@@ -125,8 +126,22 @@ static void receive_and_run_kernel(kernel_runner run_kernel)
         kernel_name[i] = receive_char();
     kernel_name[length] = 0;
 
-    r = run_kernel(kernel_name);
-    send_char(r ? MSGTYPE_KERNEL_FINISHED : MSGTYPE_KERNEL_STARTUP_FAILED);
+    r = run_kernel(kernel_name, &eid);
+    switch(r) {
+        case KERNEL_RUN_FINISHED:
+            send_char(MSGTYPE_KERNEL_FINISHED);
+            break;
+        case KERNEL_RUN_EXCEPTION:
+            send_char(MSGTYPE_KERNEL_EXCEPTION);
+            send_int(eid);
+            break;
+        case KERNEL_RUN_STARTUP_FAILED:
+            send_char(MSGTYPE_KERNEL_STARTUP_FAILED);
+            break;
+        default:
+            corecom_log("BUG: run_kernel returned unexpected value '%d'", r);
+            break;
+    }
 }
 
 void corecom_serve(object_loader load_object, kernel_runner run_kernel)
diff --git a/soc/runtime/main.c b/soc/runtime/main.c
index 113aed9f4..777d81b53 100644
--- a/soc/runtime/main.c
+++ b/soc/runtime/main.c
@@ -8,6 +8,7 @@
 
 #include "corecom.h"
 #include "elf_loader.h"
+#include "exceptions.h"
 #include "services.h"
 #include "rtio.h"
 #include "dds.h"
@@ -64,19 +65,28 @@ static int load_object(void *buffer, int length)
 
 typedef void (*kernel_function)(void);
 
-static int run_kernel(const char *kernel_name)
+static int run_kernel(const char *kernel_name, int *eid)
 {
     kernel_function k;
+    struct exception_env ee;
+    int exception_occured;
 
     k = find_symbol(symtab, kernel_name);
     if(k == NULL) {
         corecom_log("Failed to find kernel entry point '%s' in object", kernel_name);
-        return 0;
+        return KERNEL_RUN_STARTUP_FAILED;
+    }
+
+    exception_occured = exception_catch(&ee, eid);
+    if(exception_occured)
+        return KERNEL_RUN_EXCEPTION;
+    else {
+        rtio_init();
+        flush_cpu_icache();
+        k();
+        exception_pop();
+        return KERNEL_RUN_FINISHED;
     }
-    rtio_init();
-    flush_cpu_icache();
-    k();
-    return 1;
 }
 
 static void blink_led(void)