[ottf] enable context switching between tasks
This commits enables FreeRTOS cooperative scheduling by:
1. invoking a context switch upon encountering a machine-mode ecall, and
2. adding several FreeRTOS wrapper functions to the OTTF API that
simplify the FreeRTOS interface by providing functions to:
a. create additional FreeRTOS tasks,
b. delete the currently running task,
c. cooperatively yield control flow between tasks, and
d. get the name of the task that is currently executing.
Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/sw/device/lib/testing/test_framework/ottf_main.c b/sw/device/lib/testing/test_framework/ottf_main.c
index b3c8fe3..340fd16 100644
--- a/sw/device/lib/testing/test_framework/ottf_main.c
+++ b/sw/device/lib/testing/test_framework/ottf_main.c
@@ -33,12 +33,42 @@
OT_ASSERT_MEMBER_OFFSET(ottf_test_config_t, enable_concurrency, 0);
OT_ASSERT_MEMBER_SIZE(ottf_test_config_t, enable_concurrency, 1);
+// Pointer to the current FreeRTOS Task Control Block, which should be non-NULL
+// when OTTF concurrency is enabled, and test code is executed within FreeRTOS
+// tasks.
+extern void *pxCurrentTCB;
+
+// `extern` declarations to give the inline functions in the corresponding
+// header a link location.
+extern bool ottf_task_create(TaskFunction_t task_function,
+ const char *task_name,
+ configSTACK_DEPTH_TYPE task_stack_depth,
+ uint32_t task_priority);
+extern void ottf_task_yield(void);
+extern void ottf_task_delete_self(void);
+extern char *ottf_task_get_self_name(void);
+
// UART for communication with host.
static dif_uart_t uart0;
// A global random number generator testutil handle.
rand_testutils_rng_t rand_testutils_rng_ctx;
+// The OTTF overrides the default machine ecall exception handler to implement
+// FreeRTOS context switching, required for supporting cooperative scheduling.
+void ottf_machine_ecall_handler(void) {
+ if (pxCurrentTCB != NULL) {
+ // If the pointer to the current TCB is not NULL, we are operating in
+ // concurrency mode. In this case, our default behavior is to assume a
+ // context switch has been requested.
+ vTaskSwitchContext();
+ return;
+ }
+ LOG_ERROR(
+ "OTTF currently only supports use of machine-mode ecall for FreeRTOS "
+ "context switching.");
+}
+
static void init_uart(void) {
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart0));
@@ -98,9 +128,9 @@
// Run the test.
if (kOttfTestConfig.enable_concurrency) {
// Run `test_main()` in a FreeRTOS task, allowing other FreeRTOS tasks to
- // be spawned, if requested in the main test task.
- xTaskCreate(test_wrapper, "TestTask", configMINIMAL_STACK_SIZE, NULL,
- tskIDLE_PRIORITY + 1, NULL);
+ // be spawned, if requested in the main test task. Note, we spawn the main
+ // test task at a priority level of 0.
+ ottf_task_create(test_wrapper, "test_main", kOttfFreeRtosMinStackSize, 0);
vTaskStartScheduler();
} else {
// Otherwise, launch `test_main()` on bare-metal.
diff --git a/sw/device/lib/testing/test_framework/ottf_main.h b/sw/device/lib/testing/test_framework/ottf_main.h
index 448aa1f..b5abd8f 100644
--- a/sw/device/lib/testing/test_framework/ottf_main.h
+++ b/sw/device/lib/testing/test_framework/ottf_main.h
@@ -7,8 +7,8 @@
#include <stdbool.h>
-// This private header is included here so that OTTF users can include a single
-// header in their test application (the `ottf_main.h` header).
+#include "external/freertos/include/FreeRTOS.h"
+#include "external/freertos/include/task.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
@@ -31,6 +31,13 @@
extern bool test_main(void);
/**
+ * OTTF Constants.
+ */
+enum {
+ kOttfFreeRtosMinStackSize = configMINIMAL_STACK_SIZE,
+};
+
+/**
* Returns the UART that is the console device.
*/
dif_uart_t *ottf_console(void);
@@ -45,4 +52,62 @@
*/
extern bool manufacturer_post_test_hook(void);
+/**
+ * Create a FreeRTOS task.
+ *
+ * Tasks should be implemented as functions that never return. However, they may
+ * delete themselves using the `ottf_task_delete_self()` function defined below.
+ *
+ * Additionally, tasks are always run at a priority level higher than that of
+ * the FreeRTOS idle task's (which is a priority of 0).
+ *
+ * See the FreeRTOS `xTaskCreate` documentation for more details:
+ * https://www.freertos.org/a00125.html.
+ *
+ * @param task_function The name of the function that implements the task.
+ * @param task_name A task identification string used to help debugging.
+ * @param task_stack_depth The amount of memory to reserve for the task's stack.
+ * @param task_priority The numerical priority of the task.
+ * @return A boolean encoding the success of the operation.
+ */
+inline bool ottf_task_create(TaskFunction_t task_function,
+ const char *task_name,
+ configSTACK_DEPTH_TYPE task_stack_depth,
+ uint32_t task_priority) {
+ return xTaskCreate(/*pvTaskCode=*/task_function, /*pcName=*/task_name,
+ /*usStackDepth=*/task_stack_depth, /*pvParameters=*/NULL,
+ /*uxPriority=*/tskIDLE_PRIORITY + 1 + task_priority,
+ /*pxCreatedTask=*/NULL) == pdPASS
+ ? true
+ : false;
+}
+
+/**
+ * Yield control flow to another FreeRTOS task of equal or higher priority.
+ *
+ * Note, if there are no other tasks of equal or higher priority, then the
+ * calling task will continue executing. See the FreeRTOS `taskYIELD`
+ * documentation for more details:
+ * https://www.freertos.org/a00020.html#taskYIELD.
+ */
+inline void ottf_task_yield(void) { taskYIELD(); }
+
+/**
+ * Delete the calling FreeRTOS task.
+ *
+ * See the FreeRTOS `vTaskDelete` documentation for more details:
+ * https://www.freertos.org/a00126.html.
+ */
+inline void ottf_task_delete_self(void) { vTaskDelete(/*xTask=*/NULL); }
+
+/**
+ * Returns the name of the currently executing FreeRTOS task.
+ *
+ * See the FreeRTOS `pcTaskGetName` documentation for more details:
+ * https://www.freertos.org/a00021.html#pcTaskGetName.
+ */
+inline char *ottf_task_get_self_name(void) {
+ return pcTaskGetName(/*xTaskToQuery=*/NULL);
+}
+
#endif // OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_OTTF_MAIN_H_
diff --git a/sw/device/tests/example_test_from_flash.c b/sw/device/tests/example_test_from_flash.c
index 8672959..8c73b2b 100644
--- a/sw/device/tests/example_test_from_flash.c
+++ b/sw/device/tests/example_test_from_flash.c
@@ -45,6 +45,10 @@
*
* See `sw/device/lib/testing/test_framework/ottf_isrs.c` for implementation
* details of the default OTTF exception handlers.
+ *
+ * Note, the `ottf_machine_ecall_handler` cannot be overridden when using the
+ * full OTTF, as it it used to implement FreeRTOS context switching. See its
+ * implementation in `sw/device/lib/testing/test_framework/ottf_main.c`.
*/
// void ottf_exception_handler(void) {}
// void ottf_instr_misaligned_fault_handler(void) {}
@@ -52,7 +56,6 @@
// void ottf_illegal_instr_fault_handler(void) {}
// void ottf_breakpoint_handler(void) {}
// void ottf_load_store_fault_handler(void) {}
-// void ottf_machine_ecall_handler(void) {}
// void ottf_user_ecall_handler(void) {}
/**