Merge "Pull common functions/consts into test_v_helpers.h"
diff --git a/springbok/CMakeLists.txt b/springbok/CMakeLists.txt
index 0ba73f6..f8ffbc2 100644
--- a/springbok/CMakeLists.txt
+++ b/springbok/CMakeLists.txt
@@ -6,7 +6,9 @@
 target_sources(springbok_intrinsic
     PRIVATE
       crt0.s
+      irq_vector.s
       springbok_gloss.cpp
+      springbok.cpp
 )
 
 target_include_directories(springbok_intrinsic PUBLIC include)
diff --git a/springbok/crt0.s b/springbok/crt0.s
index e724a02..8e71aad 100644
--- a/springbok/crt0.s
+++ b/springbok/crt0.s
@@ -65,6 +65,8 @@
         li a1, 0x2000
         or a0, a0, a1
         csrw mstatus, a0
+        la a0, exception_handler
+        csrw mtvec, a0
 
         #############################################################
         # Clear BSS, stack and unused DTCM memory, set up sentinels #
@@ -256,3 +258,22 @@
         # stack corruption!
 2:      li   a0, 1
         ret
+
+.weak exception_handler
+exception_handler:
+        .word 0x0000307B # finish (encoded as custom3<func3=3>)
+        li   t4, 0 # ERROR logging level
+        li   t5, 0 # Return 0
+        la a0, exception_msg
+        csrr t5, mcause
+        .word 0x001E50EFB # simprint t4, a0, t5 (encoded as custom3<func3=0>)
+        call print_csrs
+        j _finish
+
+.weak print_csrs
+print_csrs:
+        mret
+
+.rodata
+exception_msg:
+        .string "Exception occurred\0"
diff --git a/springbok/irq_vector.s b/springbok/irq_vector.s
new file mode 100644
index 0000000..1787f67
--- /dev/null
+++ b/springbok/irq_vector.s
@@ -0,0 +1,8 @@
+  // Exception handler.
+  .section .vectors, "ax"
+  .option norvc
+  .option push
+  .option norelax
+  .org 0x00
+  j _start
+  j exception_handler
diff --git a/springbok/matcha.ld b/springbok/matcha.ld
index 7e5ccab..cabdf13 100644
--- a/springbok/matcha.ld
+++ b/springbok/matcha.ld
@@ -13,8 +13,14 @@
 PROVIDE( _stack_start_sentinel = ORIGIN(DTCM) + LENGTH(DTCM) - STACK_SIZE );
 PROVIDE( _stack_end_sentinel = ORIGIN(DTCM) + LENGTH(DTCM) - 64 );
 
+_boot_address = ORIGIN(ITCM);
+ENTRY(_boot_address)
+
 SECTIONS
 {
+        .vectors _boot_address : ALIGN(4) {
+                KEEP(*(.vectors))
+        } > ITCM
         .text :
         {
                 _stext = .;
diff --git a/springbok/springbok.cpp b/springbok/springbok.cpp
new file mode 100644
index 0000000..678e196
--- /dev/null
+++ b/springbok/springbok.cpp
@@ -0,0 +1,40 @@
+#include "springbok.h"
+
+extern "C" void print_csrs(void) {
+    uint32_t mcause;
+    uint32_t mepc;
+    uint32_t mtval;
+    uint32_t misa;
+    uint32_t mtvec;
+    uint32_t mhartid;
+    uint32_t marchid;
+    uint32_t mvendorid;
+    uint32_t mimpid;
+    __asm__ volatile("csrr %[MCAUSE], mcause"
+                   : [MCAUSE] "=r"(mcause):);
+    __asm__ volatile("csrr %[MEPC], mepc"
+                   : [MEPC] "=r"(mepc):);
+    __asm__ volatile("csrr %[MTVAL], mtval"
+                   : [MTVAL] "=r"(mtval):);
+    __asm__ volatile("csrr %[MISA], misa"
+                   : [MISA] "=r"(misa):);
+    __asm__ volatile("csrr %[MTVEC], mtvec"
+                   : [MTVEC] "=r"(mtvec):);
+    __asm__ volatile("csrr %[MHARTID], mhartid"
+                   : [MHARTID] "=r"(mhartid):);
+    __asm__ volatile("csrr %[MARCHID], marchid"
+                   : [MARCHID] "=r"(marchid):);
+    __asm__ volatile("csrr %[MVENDORID], mvendorid"
+                   : [MVENDORID] "=r"(mvendorid):);
+    __asm__ volatile("csrr %[MIMPID], mimpid"
+                   : [MIMPID] "=r"(mimpid):);
+    LOG_ERROR("MCAUSE:\t\t0x%08X", mcause);
+    LOG_ERROR("MEPC:\t\t0x%08X", mepc);
+    LOG_ERROR("MTVAL:\t\t0x%08X", mtval);
+    LOG_ERROR("MISA:\t\t0x%08X", misa);
+    LOG_ERROR("MTVEC:\t\t0x%08X", mtvec);
+    LOG_ERROR("MHARTID:\t\t0x%08X", mhartid);
+    LOG_ERROR("MARCHID:\t\t0x%08X", marchid);
+    LOG_ERROR("MVENDORID:\t0x%08X", mvendorid);
+    LOG_ERROR("MIMPID:\t\t0x%08X", mimpid);
+ }
diff --git a/tests/include/test_v_helpers.h b/tests/include/test_v_helpers.h
index 05c4a4c..7b02208 100644
--- a/tests/include/test_v_helpers.h
+++ b/tests/include/test_v_helpers.h
@@ -60,6 +60,9 @@
 int set_vsetvli(VSEW sew, VLMUL lmul, uint32_t avl);
 
 void zero_vector_registers();
+// Set AVL = constant 17 for now.
+const uint32_t AVL_CONST = 17;
+uint32_t set_vsetivli(VSEW sew, VLMUL lmul);
 
 template <typename T>
 void assert_vec_elem_eq(int avl, void *test_vector_1, void *test_vector_2) {
diff --git a/tests/test_v_helpers.cpp b/tests/test_v_helpers.cpp
index b7efbcb..013184e 100644
--- a/tests/test_v_helpers.cpp
+++ b/tests/test_v_helpers.cpp
@@ -265,4 +265,97 @@
   __asm__ volatile("vmv.v.i v24, 0");
 }
 
+uint32_t set_vsetivli(VSEW sew, VLMUL lmul) {
+  uint32_t vl = 0;
+  switch (lmul) {
+    case VLMUL::LMUL_M1:
+      switch (sew) {
+        case VSEW::SEW_E8:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e8, m1, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E16:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e16, m1, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E32:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e32, m1, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        default:
+          return 0;
+      }
+      break;
+    case VLMUL::LMUL_M2:
+      switch (sew) {
+        case VSEW::SEW_E8:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e8, m2, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E16:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e16, m2, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E32:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e32, m2, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        default:
+          return 0;
+      }
+      break;
+    case VLMUL::LMUL_M4:
+      switch (sew) {
+        case VSEW::SEW_E8:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e8, m4, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E16:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e16, m4, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E32:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e32, m4, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        default:
+          return 0;
+      }
+      break;
+    case VLMUL::LMUL_M8:
+      switch (sew) {
+        case VSEW::SEW_E8:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e8, m8, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E16:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e16, m8, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        case VSEW::SEW_E32:
+          __asm__ volatile("vsetivli %[VL], %[AVL], e32, m8, tu, mu"
+                           : [VL] "=r"(vl)
+                           : [AVL] "n"(AVL_CONST));
+          break;
+        default:
+          return 0;
+      }
+      break;
+    default:
+      return 0;
+  }
+  return vl;
+}
+
 }  // namespace test_v_helpers
diff --git a/tests/vsetvl_test.cpp b/tests/vsetvl_test.cpp
index 8e926c3..6ea9b73 100644
--- a/tests/vsetvl_test.cpp
+++ b/tests/vsetvl_test.cpp
@@ -21,18 +21,21 @@
 }
 
 static void test_vsetvl(VSEW sew, VLMUL vlmul, uint32_t width, float lmul) {
+  size_t vl = 0;
   for (int i = 0; i < AVL_COUNT; i++) {
-    size_t vl = set_vsetvl_intrinsic(sew, vlmul, AVLS[i]);
+    vl = set_vsetvl_intrinsic(sew, vlmul, AVLS[i]);
     EXPECT_EQ(vl, calculate_vl(width, AVLS[i], lmul));
   }
   for (int i = 0; i < AVL_COUNT; i++) {
-    uint32_t vl = set_vsetvl(sew, vlmul, AVLS[i], false, false);
+    vl = set_vsetvl(sew, vlmul, AVLS[i], false, false);
     EXPECT_EQ(vl, calculate_vl(width, AVLS[i], lmul));
   }
   for (int i = 0; i < AVL_COUNT; i++) {
-    uint32_t vl = set_vsetvli(sew, vlmul, AVLS[i]);
+    vl = set_vsetvli(sew, vlmul, AVLS[i]);
     EXPECT_EQ(vl, calculate_vl(width, AVLS[i], lmul));
   }
+  vl = set_vsetivli(sew, vlmul);
+  EXPECT_EQ(vl, calculate_vl(width, AVL_CONST, lmul));
 }
 
 static void test_vsetvlmax(VSEW sew, VLMUL vlmul, uint32_t width, float lmul) {