Add test cases for triggering program faults

- Adds a set of test cases that do things like illegal instructions,
  load/store faults, misaligned execution, etc.
- Minor changes to build rules to allow tests to "fail successfully",
  and to allow specifying which sims to run on, instead of
  unconditionally running on all sims.

Change-Id: Ie0ae0bf20ed28a610c969e7d8bbb482ce5f69224
diff --git a/build_tools/bazel/kelvin.bzl b/build_tools/bazel/kelvin.bzl
index c29614a..484afc4 100644
--- a/build_tools/bazel/kelvin.bzl
+++ b/build_tools/bazel/kelvin.bzl
@@ -208,6 +208,9 @@
         hw_test_tags = [],
         iss_test_size = "small",
         iss_test_tags = [],
+        expect_success = True,
+        hw = True,
+        iss = True,
         **kwargs):
     """A sh_test wrapper for kelvin binaries
 
@@ -230,42 +233,46 @@
         **kwargs
     )
 
+    tests = []
+
     iss_test = "{}_iss".format(name)
-    native.sh_test(
-        name = iss_test,
-        size = iss_test_size,
-        srcs = [
-            "@kelvin_sw//build_tools:test_runner.sh",
-        ],
-        args = [
-            "$(location %s.elf)" % kelvin_elf,
-        ],
-        data = [
-            "{}.elf".format(kelvin_elf),
-        ],
-        tags = ["iss"] + iss_test_tags + tags,
-    )
+    if iss:
+        native.sh_test(
+            name = iss_test,
+            size = iss_test_size,
+            srcs = [
+                "@kelvin_sw//build_tools:test_runner.sh",
+            ],
+            args = [
+                "$(location %s.elf)" % kelvin_elf,
+            ],
+            data = [
+                "{}.elf".format(kelvin_elf),
+            ],
+            tags = ["iss"] + iss_test_tags + tags,
+        )
+        tests.append(iss_test)
 
     hw_test = "{}_hw".format(name)
-    native.sh_test(
-        name = hw_test,
-        size = hw_test_size,
-        srcs = ["@kelvin_sw//build_tools:core_sim_test_runner.sh"],
-        args = [
-            "$(location %s.bin)" % kelvin_elf,
-        ],
-        data = [
-            "{}.bin".format(kelvin_elf),
-        ],
-        tags = ["systemc"] + hw_test_tags + tags,
-    )
+    if hw:
+        native.sh_test(
+            name = hw_test,
+            size = hw_test_size,
+            srcs = ["@kelvin_sw//build_tools:core_sim_test_runner.sh"],
+            args = [
+                "$(location %s.bin)" % kelvin_elf,
+                str(expect_success),
+            ],
+            data = [
+                "{}.bin".format(kelvin_elf),
+            ],
+            tags = ["systemc"] + hw_test_tags + tags,
+        )
+        tests.append(hw_test)
 
     native.test_suite(
         name = name,
-        tests = [
-            iss_test,
-            hw_test,
-        ],
+        tests = tests,
         tags = tags
     )
 
diff --git a/build_tools/core_sim_test_runner.sh b/build_tools/core_sim_test_runner.sh
index eb3ad5a..2d5b029 100755
--- a/build_tools/core_sim_test_runner.sh
+++ b/build_tools/core_sim_test_runner.sh
@@ -16,7 +16,7 @@
 # Test runner for SystemC simulator. Note the input should be the .bin file.
 
 function print_usage {
-  echo "Usage: core_sim_test_runner.sh <bin location> [extra flags]"
+  echo "Usage: core_sim_test_runner.sh <bin location> <expected result>"
 }
 
 if [[ $1 == "--help" ]]; then
@@ -42,8 +42,19 @@
 
 BIN_FILE=$(realpath $1)
 shift 1
-SIM_OUT=$(${CORE_SIM} "${BIN_FILE}" $@)
+EXPECTED_RESULT=${1:-True}
+shift 1
+
+SIM_OUT=$(${CORE_SIM} "${BIN_FILE}")
 RESULT=$?
 echo "${SIM_OUT}"
 
-exit ${RESULT}
+if [[ "${EXPECTED_RESULT}" == "True" ]]; then
+  exit ${RESULT}
+elif [[ "${EXPECTED_RESULT}" == "False" ]]; then
+  if [[ ${RESULT} -eq 0 ]]; then
+    exit -1
+  else
+    exit 0
+  fi
+fi
diff --git a/crt/kelvin_tcm.ld b/crt/kelvin_tcm.ld
index b498a59..b24c11a 100644
--- a/crt/kelvin_tcm.ld
+++ b/crt/kelvin_tcm.ld
@@ -24,10 +24,12 @@
 
     .init.array : ALIGN(16) {
       __init_array_start = .;
+      __init_array_start__ = .;
       *(.init_array)
       *(.init_array.*)
       . = ALIGN(16);
       __init_array_end = .;
+      __init_array_end__ = .;
     } > ITCM
 
     .rodata : ALIGN(16) {
@@ -52,6 +54,11 @@
       *(.sdata.*)
       *(.data)
       *(.data.*)
+      /**
+       * Memory location for the return value from main,
+       * which could be inspected by another core in the system.
+       **/
+      _ret = .;
       . = ALIGN(16);
         __data_end__ = .;
     } > DTCM
diff --git a/tests/kelvin_isa/BUILD b/tests/kelvin_isa/BUILD
index 5861d6a..dabb7d2 100644
--- a/tests/kelvin_isa/BUILD
+++ b/tests/kelvin_isa/BUILD
@@ -33,6 +33,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -47,6 +48,132 @@
 )
 
 kelvin_test(
+    name = "instr_fault",
+    srcs = [
+        "instr_fault.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
+    name = "illegal",
+    srcs = [
+        "illegal.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
+    name = "instr_align_0",
+    srcs = [
+        "instr_align_0.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
+    name = "instr_align_1",
+    srcs = [
+        "instr_align_1.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
+    name = "instr_align_2",
+    srcs = [
+        "instr_align_2.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
+    name = "load_fault_0",
+    srcs = [
+        "load_fault_0.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
+    name = "load_fault_1",
+    srcs = [
+        "load_fault_1.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
+    name = "store_fault_0",
+    srcs = [
+        "store_fault_0.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    # This passes on core_sim, but should fail on core_mini_sim.
+    # expect_success = False,
+)
+
+kelvin_test(
+    name = "store_fault_1",
+    srcs = [
+        "store_fault_1.cc",
+    ],
+    hw_test_size = "small",
+    deps = [
+        ":kelvin_test",
+    ],
+    iss = False,
+    expect_success = False,
+    tags = ["intentional_failure"],
+)
+
+kelvin_test(
     name = "acset",
     srcs = [
         "acset.cc",
@@ -55,6 +182,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -66,6 +194,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -79,6 +208,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -89,6 +219,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -99,6 +230,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -109,6 +241,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -119,6 +252,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -129,6 +263,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 cc_library(
@@ -147,6 +282,7 @@
         ":kelvin_test",
         ":vdwconv_data",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -157,6 +293,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -167,6 +304,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -177,6 +315,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -188,6 +327,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -199,6 +339,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -210,6 +351,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -220,6 +362,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -230,6 +373,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -240,6 +384,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -250,6 +395,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -260,6 +406,7 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
 
 kelvin_test(
@@ -271,4 +418,5 @@
     deps = [
         ":kelvin_test",
     ],
+    tags = ["tcm-incompatible"],
 )
diff --git a/tests/kelvin_isa/illegal.cc b/tests/kelvin_isa/illegal.cc
new file mode 100644
index 0000000..418ee00
--- /dev/null
+++ b/tests/kelvin_isa/illegal.cc
@@ -0,0 +1,21 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+int main(int argc, char** argv) {
+  asm volatile(".word 0x02007043"); // fmadd.d f0, f0, f0, f0
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/instr_align_0.cc b/tests/kelvin_isa/instr_align_0.cc
new file mode 100644
index 0000000..303e279
--- /dev/null
+++ b/tests/kelvin_isa/instr_align_0.cc
@@ -0,0 +1,22 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+int main(int argc, char** argv) {
+  // Instruction address misaligned
+  asm volatile("la ra, 0x3; jalr ra,ra");
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/instr_align_1.cc b/tests/kelvin_isa/instr_align_1.cc
new file mode 100644
index 0000000..1ee81d1
--- /dev/null
+++ b/tests/kelvin_isa/instr_align_1.cc
@@ -0,0 +1,22 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+int main(int argc, char** argv) {
+  // Instruction address misaligned
+  asm volatile("jal x0, .+2");
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/instr_align_2.cc b/tests/kelvin_isa/instr_align_2.cc
new file mode 100644
index 0000000..21aa0b7
--- /dev/null
+++ b/tests/kelvin_isa/instr_align_2.cc
@@ -0,0 +1,22 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+int main(int argc, char** argv) {
+  // Instruction address misaligned
+  asm volatile("beqz x0, .+2");
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/instr_fault.cc b/tests/kelvin_isa/instr_fault.cc
new file mode 100644
index 0000000..c3e974f
--- /dev/null
+++ b/tests/kelvin_isa/instr_fault.cc
@@ -0,0 +1,19 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+int main(int argc, char** argv) {
+  asm volatile("la ra, 0x40000000; jalr ra, ra");
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/load_fault_0.cc b/tests/kelvin_isa/load_fault_0.cc
new file mode 100644
index 0000000..c3e974f
--- /dev/null
+++ b/tests/kelvin_isa/load_fault_0.cc
@@ -0,0 +1,19 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+int main(int argc, char** argv) {
+  asm volatile("la ra, 0x40000000; jalr ra, ra");
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/load_fault_1.cc b/tests/kelvin_isa/load_fault_1.cc
new file mode 100644
index 0000000..78f7beb
--- /dev/null
+++ b/tests/kelvin_isa/load_fault_1.cc
@@ -0,0 +1,23 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+int main(int argc, char** argv) {
+  volatile uint32_t* load_bad_addr = (uint32_t*)0xA0000000;
+  volatile uint32_t foo = *load_bad_addr;
+  (void)foo;
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/store_fault_0.cc b/tests/kelvin_isa/store_fault_0.cc
new file mode 100644
index 0000000..68c5094
--- /dev/null
+++ b/tests/kelvin_isa/store_fault_0.cc
@@ -0,0 +1,23 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+int main(int argc, char** argv) {
+  // Store Fault (internal)
+  volatile uint32_t* store_bad_addr = (uint32_t*)4L;
+  *store_bad_addr = 0xdeadbeef;
+
+  return 0;
+}
diff --git a/tests/kelvin_isa/store_fault_1.cc b/tests/kelvin_isa/store_fault_1.cc
new file mode 100644
index 0000000..bb5d2fc
--- /dev/null
+++ b/tests/kelvin_isa/store_fault_1.cc
@@ -0,0 +1,23 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstdint>
+
+int main(int argc, char** argv) {
+  // Store Fault (external)
+  volatile uint32_t* store_bad_addr = (uint32_t*)0xA0000000;
+  *store_bad_addr = 0xdeadbeef;
+
+  return 0;
+}