Add exception handling for instruction / ld errors

Addresses #18
diff --git a/sw/examples/hello_usbdev/hello_usbdev.c b/sw/examples/hello_usbdev/hello_usbdev.c
index 639c734..fe65611 100644
--- a/sw/examples/hello_usbdev/hello_usbdev.c
+++ b/sw/examples/hello_usbdev/hello_usbdev.c
@@ -57,11 +57,7 @@
 
 static int usleep(unsigned long usec) { return usleep_ibex(usec); }
 
-// called from ctr0 when something bad happens
-// char I=illegal instruction, A=lsu error (address), E=ecall
-void trap_handler(uint32_t mepc, char c) {
-  uart_send_char(c);
-  uart_send_uint(mepc, 32);
+static void test_error(void) {
   while (1) {
     gpio_write_all(0xAA00);  // pattern
     usleep(200 * 1000);
@@ -70,6 +66,18 @@
   }
 }
 
+// Override default handler routines
+void handler_instr_ill_fault(void) {
+  uart_send_str("Instruction Illegal fault");
+  test_error();
+}
+
+// Override default handler routines
+void handler_lsu_fault(void) {
+  uart_send_str("Load/Store Fault");
+  test_error();
+}
+
 #define MK_PRINT(c) \
   (((c != 0xa) && (c != 0xd) && ((c < 32) || (c > 126))) ? '_' : c)
 
diff --git a/sw/lib/handler.c b/sw/lib/handler.c
index cefe4e5..20d0ba7 100644
--- a/sw/lib/handler.c
+++ b/sw/lib/handler.c
@@ -2,7 +2,10 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
+#include "handler.h"
+
 #include "common.h"
+#include "uart.h"
 
 /**
  * Default exception handler. Can be overidden.
@@ -24,23 +27,135 @@
  */
 void handler_irq_external(void) __attribute__((aligned(4), interrupt, weak));
 
+/**
+ * Instruction access fault. Can be overriden.
+ */
+void handler_instr_acc_fault(void) __attribute__((aligned(4), interrupt, weak));
+
+/**
+ * Illegal Instruction fault. Can be overriden.
+ */
+void handler_instr_ill_fault(void) __attribute__((aligned(4), interrupt, weak));
+;
+
+/**
+ * Breakpoint handler. Can be overriden.
+ */
+void handler_bkpt(void) __attribute__((aligned(4), interrupt, weak));
+
+/**
+ * Load store unit fault. Can be overriden.
+ */
+void handler_lsu_fault(void) __attribute__((aligned(4), interrupt, weak));
+
+/**
+ * Exception call handler. Can be overriden.
+ */
+void handler_ecall(void) __attribute__((aligned(4), interrupt, weak));
+
+/**
+ * Return value of mtval
+ */
+static uint32_t get_mtval(void) {
+  uint32_t mtval;
+  asm volatile("csrr %0, mtval" : "=r"(mtval) : :);
+  return mtval;
+}
+
+/**
+ * Default Error Handling
+ * @param error message supplied by caller
+ * TODO - this will be soon by a real print formatting
+ */
+static void print_exc_msg(const char *msg) {
+  const uint32_t mtval = get_mtval();
+  uart_send_str((char *)msg);
+  uart_send_str("MTVAL value is ");
+  uart_send_uint(mtval, 32);
+  uart_send_str("\n");
+  while (1) {
+  };
+}
+
 // Below functions are default weak exception handlers meant to be overriden
 void handler_exception(void) {
-  while (1) {
+  uint32_t mcause;
+  exc_id_t exc_cause;
+
+  asm volatile("csrr %0 , mcause" : "=r"(mcause) : :);
+  exc_cause = (exc_id_t)(mcause & kIdMax);
+
+  switch (exc_cause) {
+    case kInstMisa:
+      handler_instr_acc_fault();
+      break;
+    case kInstAccFault:
+      handler_instr_acc_fault();
+      break;
+    case kInstIllegalFault:
+      handler_instr_ill_fault();
+      break;
+    case kBkpt:
+      handler_bkpt();
+      break;
+    case kLoadAccFault:
+      handler_lsu_fault();
+      break;
+    case kStrAccFault:
+      handler_lsu_fault();
+      break;
+    case kECall:
+      handler_ecall();
+      break;
+    default:
+      while (1) {
+      };
   }
 }
 
 void handler_irq_software(void) {
+  uart_send_str("Software IRQ triggered!\n");
   while (1) {
   }
 }
 
 void handler_irq_timer(void) {
+  uart_send_str("Timer IRQ triggered!\n");
   while (1) {
   }
 }
 
 void handler_irq_external(void) {
+  uart_send_str("External IRQ triggered!\n");
+  while (1) {
+  }
+}
+
+void handler_instr_acc_fault(void) {
+  const char fault_msg[] =
+      "Instruction access fault, mtval shows fault address\n";
+  print_exc_msg(fault_msg);
+}
+
+void handler_instr_ill_fault(void) {
+  const char fault_msg[] =
+      "Illegal Instruction fault, mtval shows instruction content\n";
+  print_exc_msg(fault_msg);
+}
+
+void handler_bkpt(void) {
+  const char exc_msg[] =
+      "Breakpoint triggerd, mtval shows the breakpoint address\n";
+  print_exc_msg(exc_msg);
+}
+
+void handler_lsu_fault(void) {
+  const char exc_msg[] = "Load/Store fault, mtval shows the fault address\n";
+  print_exc_msg(exc_msg);
+}
+
+void handler_ecall(void) {
+  uart_send_str("Environment call encountered\n");
   while (1) {
   }
 }
diff --git a/sw/lib/handler.h b/sw/lib/handler.h
new file mode 100644
index 0000000..6c99ef4
--- /dev/null
+++ b/sw/lib/handler.h
@@ -0,0 +1,15 @@
+#ifndef _HANDLER_H_
+#define _HANDLER_H_
+
+typedef enum exc_id {
+  kInstMisa = 0,
+  kInstAccFault = 1,
+  kInstIllegalFault = 2,
+  kBkpt = 3,
+  kLoadAccFault = 5,
+  kStrAccFault = 7,
+  kECall = 11,
+  kIdMax = 31
+} exc_id_t;
+
+#endif