[ottf,tests] Migrate crt_test to use OTTF ISRs.

The crt_test checks the operations in the testing `rom_ext`'s `flash_crt.S`.
Because of this, it does not run within the context of the OTTF.
However, the `flash_crt.S` does set the `mtvec` pointer, so ISRs must be
present in case an unexpected interrupt is fired.

As described in #8015, we plan to deprecate the default ISR handlers in
`sw/device/lib/handler.<c,h>` favor of the OTTF ISRs so that only on a
single set of default testing ISRs must be maintained. Therefore, this
commit migrates the crt_test to make use of the OTTF ISRs. Additionally,
this commit makes it possible to use the OTTF ISRs without requiring the
OTTF be linked into the test image too.

Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/sw/device/lib/testing/test_framework/ottf_isrs.S b/sw/device/lib/testing/test_framework/ottf_isrs.S
index 080c18d..426c1d0 100644
--- a/sw/device/lib/testing/test_framework/ottf_isrs.S
+++ b/sw/device/lib/testing/test_framework/ottf_isrs.S
@@ -56,9 +56,13 @@
 .balign 4
 .type save_current_sp_to_tcb, @function
 save_current_sp_to_tcb:
-  la t0, kTestConfig  // Load the pointer to the test configuration struct.
-  lb t1, 0(t0)        // Load the first byte of the test_config struct, which
-                      // contains the `enable_concurrency` field to check.
+  la t0, kTestConfig       // Load the pointer to the test configuration struct.
+  beqz t0, .L_skip_sp_save // If the kTestConfig pointer is NULL, we are using
+                           // these ISRs without the OTTF, so no need to check
+                           // if concurrency is enabled.
+  lb t1, 0(t0)             // Load the first byte of the test_config struct,
+                           // which contains the `enable_concurrency` field to
+                           // check.
   beqz t1, .L_skip_sp_save
   lw t2, pxCurrentTCB
   sw sp, 0(t2)
@@ -330,9 +334,13 @@
   // meaning a test is run as a FreeRTOS task, where each task maintains its own
   // stack. Otherwise, the test is run on bare-metal, and there is no TCB, and
   // only a single stack/stack pointer.
-  la t0, kTestConfig  // Load the pointer to the test configuration struct.
-  lb t1, 0(t0)        // Load the first byte of the test_config struct, which
-                      // contains the `enable_concurrency` field to check.
+  la t0, kTestConfig          // Load pointer to the test configuration struct.
+  beqz t0, .L_skip_sp_restore // If kTestConfig pointer is NULL, we are using
+                              // these ISRs without the OTTF, so no need to
+                              // check if concurrency is enabled.
+  lb t1, 0(t0)                // Load the first byte of the test_config struct,
+                              // which contains the `enable_concurrency` field
+                              // to check.
   beqz t1, .L_skip_sp_restore
   lw  t2, pxCurrentTCB
   lw  sp, 0(t2)
diff --git a/sw/device/lib/testing/test_framework/ottf_isrs.c b/sw/device/lib/testing/test_framework/ottf_isrs.c
index 211e1f8..716a8b3 100644
--- a/sw/device/lib/testing/test_framework/ottf_isrs.c
+++ b/sw/device/lib/testing/test_framework/ottf_isrs.c
@@ -10,6 +10,15 @@
 #include "sw/device/lib/runtime/ibex.h"
 #include "sw/device/lib/runtime/log.h"
 
+/**
+ * These weak symbols (pxCurrentTCB and kTestConfig) enable the OTTF ISRs to be
+ * used without the OTTF itself. This enables us to maintain one set of default
+ * ISRs for testing, while also enabling writing tests that do not make use of
+ * the OTTF. See the `crt_test` in `sw/device/tests/crt_test.c` for an example.
+ */
+OT_ATTR_WEAK uintptr_t pxCurrentTCB = (uintptr_t)NULL;
+OT_ATTR_WEAK uintptr_t kTestConfig = (uintptr_t)NULL;
+
 OT_ATTR_WEAK void ottf_exception_handler(void) {
   uint32_t mcause = ibex_mcause_read();
 
diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
index e17eaa3..2df3710 100644
--- a/sw/device/tests/meson.build
+++ b/sw/device/tests/meson.build
@@ -956,14 +956,18 @@
     dependencies: [
       riscv_crt,
       device_lib,
-      sw_lib_irq_handlers,
       sw_lib_testing_test_status,
       sw_lib_runtime_print,
       sw_lib_runtime_log,
       sw_lib_dif_uart,
-      # Explicitly do not pull in the OTTF; we need to run right after
-      # the CRT is done executing.
+      # Explicitly DO NOT pull in the OTTF; we need to run right after
+      # the CRT is done executing. However, DO pull in the OTTF default ISRs.
+      # While this test does not override any of the default symbols, they
+      # should be linked in since the `mtvec` is set to point to these in the
+      # `sw/device/exts/common/flash_crt.S` initialization assembly (contained
+      # in the riscv_crt target above).
       # sw_lib_testing_ottf,
+      sw_lib_testing_ottf_isrs,
     ],
   )