Refactor WASM runner and logging functionality; add heartbeat task and improve initialization process

This commit is contained in:
Lukas Höppner
2026-08-12 01:22:09 +02:00
parent 27c3aaf88f
commit b623e5cf0a
13 changed files with 261 additions and 95 deletions
+2 -2
View File
@@ -84,10 +84,11 @@ else()
# WAMR Desktop-Konfiguration # WAMR Desktop-Konfiguration
set(WAMR_BUILD_INTERP 1 CACHE BOOL "" FORCE) set(WAMR_BUILD_INTERP 1 CACHE BOOL "" FORCE)
set(WAMR_BUILD_FAST_INTERP 1 CACHE BOOL "" FORCE) set(WAMR_BUILD_FAST_INTERP 1 CACHE BOOL "" FORCE)
# set(WAMR_BUILD_INTERP_TICKS 1 CACHE BOOL "" FORCE)
set(WAMR_BUILD_JIT 0 CACHE BOOL "" FORCE) set(WAMR_BUILD_JIT 0 CACHE BOOL "" FORCE)
set(WAMR_BUILD_AOT 0 CACHE BOOL "" FORCE) set(WAMR_BUILD_AOT 0 CACHE BOOL "" FORCE)
set(WAMR_BUILD_LIBC_BUILTIN 1 CACHE BOOL "" FORCE) set(WAMR_BUILD_LIBC_BUILTIN 1 CACHE BOOL "" FORCE)
# Disable WASI # Disable WASI
set(WAMR_BUILD_LIBC_WASI 0 CACHE BOOL "" FORCE) set(WAMR_BUILD_LIBC_WASI 0 CACHE BOOL "" FORCE)
@@ -99,7 +100,6 @@ else()
set(WAMR_BUILD_PLATFORM "linux" CACHE STRING "" FORCE) set(WAMR_BUILD_PLATFORM "linux" CACHE STRING "" FORCE)
endif() endif()
# define Target 'freertos_kernel'
FetchContent_MakeAvailable(FreeRTOS_Kernel wamr_runtime) FetchContent_MakeAvailable(FreeRTOS_Kernel wamr_runtime)
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
+1
View File
@@ -3,6 +3,7 @@
/* Desktop Simulator Settings */ /* Desktop Simulator Settings */
#define configUSE_PREEMPTION 1 #define configUSE_PREEMPTION 1
#define configUSE_TIME_SLICING 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_IDLE_HOOK 1 #define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0 #define configUSE_TICK_HOOK 0
+1 -1
View File
@@ -1,6 +1,6 @@
#ifndef NOVA64_API_H #ifndef NOVA64_API_H
#define NOVA64_API_H #define NOVA64_API_H
void register_nova64_imports(void); void nova64_register_imports(void);
#endif // NOVA64_API_H #endif // NOVA64_API_H
+3 -2
View File
@@ -28,7 +28,8 @@ bool wasm_runner_initialize(void);
*/ */
bool wasm_runner_start_program( bool wasm_runner_start_program(
const char *module_path, const char *module_path,
const wasm_runner_framebuffer_config_t *framebuffer_config, const wasm_runner_framebuffer_config_t *framebuffer_config);
wasm_runner_instance_t **out_instance);
void wasm_runner_terminate(void);
#endif // WASM_RUNNER_H #endif // WASM_RUNNER_H
+23 -5
View File
@@ -1,24 +1,42 @@
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "nova64_internal.h"
#include "projdefs.h"
#include "task.h" #include "task.h"
#include "wasm_export.h" #include "wasm_export.h"
#include "projdefs.h" #include <string.h>
static void native_yield(wasm_exec_env_t exec_env, uint32_t timeout_ms) { static void native_yield(wasm_exec_env_t exec_env, uint32_t timeout_ms) {
if (timeout_ms == 0) { if (timeout_ms == 0) {
// Gibt die Rest-Quantenzeit ab, blockiert aber nicht künstlich
taskYIELD(); taskYIELD();
} else { } else {
// Blockiert den Task für x Millisekunden
vTaskDelay(pdMS_TO_TICKS(timeout_ms)); vTaskDelay(pdMS_TO_TICKS(timeout_ms));
} }
} }
static void native_log(wasm_exec_env_t exec_env, const char *message) {
(void)exec_env;
nova64_log("[WASM LOG] ");
const char *text = message ? message : "";
size_t len = strlen(text);
bool has_newline = len > 0 && text[len - 1] == '\n';
if (has_newline) {
nova64_log("%s", text);
} else {
nova64_log("%s\n", text);
}
}
static NativeSymbol native_symbols[] = { static NativeSymbol native_symbols[] = {
{"yield", (void *)native_yield, "(i)", NULL}, {"nova64_yield", (void *)native_yield, "(i)", NULL},
{"nova64_log", (void *)native_log, "($)", NULL},
// { "flush_framebuffer", (void*)native_flush_framebuffer, "()", NULL } // { "flush_framebuffer", (void*)native_flush_framebuffer, "()", NULL }
}; };
void register_nova64_imports(void) { void nova64_register_imports(void) {
wasm_runtime_register_natives("nova64", native_symbols, wasm_runtime_register_natives("nova64", native_symbols,
sizeof(native_symbols) / sizeof(NativeSymbol)); sizeof(native_symbols) / sizeof(NativeSymbol));
} }
+105 -40
View File
@@ -24,14 +24,48 @@ static nova64_io_interface_t g_io_interface_storage;
static const nova64_io_interface_t *g_io_interface = NULL; static const nova64_io_interface_t *g_io_interface = NULL;
static QueueHandle_t event_queue = NULL; static QueueHandle_t event_queue = NULL;
static TaskHandle_t boot_task_handle = NULL; static TaskHandle_t boot_task_handle = NULL;
static TaskHandle_t launcher_task_handle = NULL; static TaskHandle_t init_task_handle = NULL;
static TaskHandle_t heartbeat_task_handle = NULL;
#if defined(NOVA64_SHUTDOWN_AVAILABLE) #if defined(NOVA64_SHUTDOWN_AVAILABLE)
static TaskHandle_t shutdown_task_handle = NULL; static TaskHandle_t shutdown_task_handle = NULL;
static volatile bool shutdown_requested = false; volatile bool shutdown_requested = false;
#endif #endif
static const char *const kInternalLauncherPath = "SYS:/launcher.wasm"; static const char *const kInternalInitPath = "SYS:/init.wasm";
#if defined(_WIN32)
#include <windows.h>
static SRWLOCK g_log_lock = SRWLOCK_INIT;
static inline void log_lock(void) { AcquireSRWLockExclusive(&g_log_lock); }
static inline void log_unlock(void) { ReleaseSRWLockExclusive(&g_log_lock); }
#elif defined(__unix__) || defined(__APPLE__)
#include <pthread.h>
static pthread_mutex_t g_log_lock = PTHREAD_MUTEX_INITIALIZER;
static inline void log_lock(void) { pthread_mutex_lock(&g_log_lock); }
static inline void log_unlock(void) { pthread_mutex_unlock(&g_log_lock); }
#else
#include "semphr.h"
static SemaphoreHandle_t g_log_lock = NULL;
static inline void log_lock(void) {
if (!g_log_lock) {
g_log_lock = xSemaphoreCreateMutex();
}
if (g_log_lock && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
xSemaphoreTake(g_log_lock, portMAX_DELAY);
}
}
static inline void log_unlock(void) {
if (g_log_lock && xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
xSemaphoreGive(g_log_lock);
}
}
#endif
void nova64_log(const char *format, ...) { void nova64_log(const char *format, ...) {
char buffer[512]; char buffer[512];
@@ -40,48 +74,41 @@ void nova64_log(const char *format, ...) {
vsnprintf(buffer, sizeof(buffer), format, args); vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args); va_end(args);
log_lock();
if (g_io_interface && g_io_interface->log_message) { if (g_io_interface && g_io_interface->log_message) {
g_io_interface->log_message(buffer); g_io_interface->log_message(buffer);
} else { } else {
fputs(buffer, stdout); fputs(buffer, stdout);
fflush(stdout);
} }
log_unlock();
} }
static bool initialize_wasm_runtime(void) { static bool initialize_wasm_runtime(void) {
nova64_log("Initializing WASM runtime...\n"); nova64_log("Initializing WASM runtime...\n");
wasm_runner_initialize(); wasm_runner_initialize();
// TODO: Replace this stub with real WAMR initialization.
return true; return true;
} }
static bool load_launcher_from_internal_storage(void) { static void start_init_from_internal_storage(void *params) {
nova64_log("Loading launcher from internal storage (%s)...\n",
kInternalLauncherPath);
// TODO: Implement the actual internal storage filesystem and launcher load.
return true;
}
static void nova64_wasm_launcher_task(void *params) {
(void)params; (void)params;
nova64_log("WASM launcher task started.\n"); wasm_runner_start_program(kInternalInitPath, NULL);
// TODO: Execute the loaded WASM launcher module here with the WAMR engine.
for (;;) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
} }
static bool start_launcher_task(void) { static bool start_init_task(void) {
BaseType_t result = xTaskCreate(nova64_wasm_launcher_task, "Nova64Launcher", BaseType_t result =
4096 / sizeof(StackType_t), NULL, xTaskCreate(start_init_from_internal_storage, "Nova64Init",
tskIDLE_PRIORITY + 1, &launcher_task_handle); 4096 / sizeof(StackType_t), NULL, tskIDLE_PRIORITY + 1,
&init_task_handle);
if (result != pdPASS) { if (result != pdPASS) {
nova64_log("Failed to create launcher task.\n"); nova64_log("Failed to create init task.\n");
return false; return false;
} }
nova64_log("Launcher task created successfully.\n"); nova64_log("Init task created successfully.\n");
return true; return true;
} }
@@ -90,14 +117,34 @@ static void nova64_shutdown_task(void *params) {
(void)params; (void)params;
nova64_log("Shutdown task started.\n"); nova64_log("Shutdown task started.\n");
shutdown_requested = false;
for (;;) { for (;;) {
ulTaskNotifyTake(pdTRUE,
pdMS_TO_TICKS(50)); // Wait for notification or timeout
if (shutdown_requested) { if (shutdown_requested) {
nova64_log("Shutdown requested; ending scheduler.\n"); break;
vTaskEndScheduler();
return;
} }
vTaskDelay(pdMS_TO_TICKS(100));
} }
nova64_log("Shutdown requested; terminating WASM runtime.\n");
wasm_runner_terminate();
if (heartbeat_task_handle != NULL) {
vTaskDelete(heartbeat_task_handle);
heartbeat_task_handle = NULL;
}
vTaskDelay(pdMS_TO_TICKS(100)); // Allow time for tasks to clean up
shutdown_task_handle = NULL;
#if defined(_WIN32)
ExitThread(0);
#elif defined(__unix__) || defined(__APPLE__)
pthread_exit(NULL);
#else
vTaskDelete(NULL);
#endif
} }
static bool start_shutdown_task(void) { static bool start_shutdown_task(void) {
@@ -115,6 +162,27 @@ static bool start_shutdown_task(void) {
} }
#endif #endif
void heartbeat_task(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(1000);
uint32_t seconds = 0;
for (;;) {
vTaskDelayUntil(&xLastWakeTime, xFrequency);
seconds++;
nova64_log("[Heartbeat] System running for %lu s | FreeRTOS Tick: %lu\n",
(unsigned long)seconds, (unsigned long)xTaskGetTickCount());
}
}
void start_heartbeat_task(void) {
xTaskCreate(heartbeat_task, "heartbeat", 2048, NULL, 1,
&heartbeat_task_handle);
}
static void nova64_boot_task(void *params) { static void nova64_boot_task(void *params) {
(void)params; (void)params;
nova64_log("Boot task started.\n"); nova64_log("Boot task started.\n");
@@ -125,17 +193,12 @@ static void nova64_boot_task(void *params) {
return; return;
} }
if (!load_launcher_from_internal_storage()) { if (!start_init_task()) {
nova64_log("Launcher loading failed.\n"); nova64_log("Init startup failed.\n");
vTaskDelete(NULL);
return;
}
if (!start_launcher_task()) {
nova64_log("Launcher startup failed.\n");
vTaskDelete(NULL); vTaskDelete(NULL);
return; return;
} }
start_heartbeat_task();
nova64_log("Boot process completed.\n"); nova64_log("Boot process completed.\n");
vTaskDelete(NULL); vTaskDelete(NULL);
@@ -153,10 +216,6 @@ NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) {
g_io_interface_storage = *io_interface; g_io_interface_storage = *io_interface;
g_io_interface = &g_io_interface_storage; g_io_interface = &g_io_interface_storage;
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
shutdown_requested = false;
#endif
nova64_log("System Initializing...\n"); nova64_log("System Initializing...\n");
event_queue = xQueueCreate(10, sizeof(uint32_t)); event_queue = xQueueCreate(10, sizeof(uint32_t));
@@ -206,7 +265,13 @@ NOVA64_API bool nova64_shutdown(void) {
} }
nova64_log("Scheduler shutdown requested from host.\n"); nova64_log("Scheduler shutdown requested from host.\n");
shutdown_requested = true; shutdown_requested = true;
if (shutdown_task_handle != NULL) {
xTaskNotifyGive(shutdown_task_handle);
return true;
}
return true; return true;
} }
#endif #endif
+107 -23
View File
@@ -1,28 +1,75 @@
#include "wasm_runner.h" #include "wasm_runner.h"
#include "FreeRTOS.h"
#include "nova64_api.h"
#include "nova64_fs.h" #include "nova64_fs.h"
#include "nova64_internal.h" #include "nova64_internal.h"
#include "nova64_api.h" #include "task.h"
#include "wasm_export.h" #include "wasm_export.h"
#include <stdlib.h> #include <stdlib.h>
struct wasm_runner_instance { #define MAX_WASM_TASKS 4
void *native_instance;
char *module_path; typedef enum { SLOT_FREE = 0, SLOT_RUNNING, SLOT_STOPPING } slot_state_t;
wasm_runner_framebuffer_config_t framebuffer_config;
wasm_runner_instance_t *next; typedef struct {
}; int id;
slot_state_t state;
// TaskHandle_t freertos_handle;
wasm_module_inst_t module_inst;
wasm_exec_env_t exec_env;
} wasm_task_slot_t;
static wasm_task_slot_t g_wasm_slots[MAX_WASM_TASKS];
char error_buf[128]; char error_buf[128];
uint32_t stack_size = 8092, heap_size = 8092; uint32_t stack_size = 8092, heap_size = 8092;
bool wasm_runner_initialize(void) { bool wasm_runner_initialize(void) {
for (int i = 0; i < MAX_WASM_TASKS; i++) {
g_wasm_slots[i].id = i;
g_wasm_slots[i].state = SLOT_FREE;
// g_wasm_slots[i].freertos_handle = NULL;
g_wasm_slots[i].module_inst = NULL;
}
wasm_runtime_init(); wasm_runtime_init();
register_nova64_imports(); nova64_register_imports();
nova64_log("WASM runtime initialized successfully.\n"); nova64_log("WASM runtime initialized successfully.\n");
const char *module_file = "SYS:/init.wasm"; return true;
int32_t file_size = nova64_get_file_size(module_file); }
bool wasm_runner_destroy(void) {
wasm_runtime_destroy();
return true;
}
bool wasm_runner_start_program(
const char *module_path,
const wasm_runner_framebuffer_config_t *framebuffer_config) {
nova64_log("Running WASM module: %s\n", module_path);
wasm_task_slot_t *slot = NULL;
for (int i = 0; i < MAX_WASM_TASKS; i++) {
if (g_wasm_slots[i].state == SLOT_FREE) {
slot = &g_wasm_slots[i];
break;
}
}
if (!slot) {
nova64_log("Maximum number of WASM tasks (%d) reached!\n", MAX_WASM_TASKS);
return false;
}
if (!wasm_runtime_init_thread_env()) {
nova64_log("Failed to initialize WASM runtime thread environment.\n");
return false;
}
int32_t file_size = nova64_get_file_size(module_path);
if (file_size <= 0) { if (file_size <= 0) {
nova64_log("Failed to determine WASM module size.\n"); nova64_log("Failed to determine WASM module size.\n");
return false; return false;
@@ -34,7 +81,7 @@ bool wasm_runner_initialize(void) {
return false; return false;
} }
if (!nova64_read_file_to_buffer(module_file, buffer, (size_t)file_size)) { if (!nova64_read_file_to_buffer(module_path, buffer, (size_t)file_size)) {
nova64_log("Failed to read WASM module into buffer.\n"); nova64_log("Failed to read WASM module into buffer.\n");
free(buffer); free(buffer);
return false; return false;
@@ -43,36 +90,73 @@ bool wasm_runner_initialize(void) {
wasm_module_t module = wasm_runtime_load(buffer, (uint32_t)file_size, wasm_module_t module = wasm_runtime_load(buffer, (uint32_t)file_size,
error_buf, sizeof(error_buf)); error_buf, sizeof(error_buf));
wasm_module_inst_t module_inst = wasm_runtime_instantiate( slot->module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
module, stack_size, heap_size, error_buf, sizeof(error_buf)); error_buf, sizeof(error_buf));
wasm_function_inst_t start_func = wasm_function_inst_t start_func =
wasm_runtime_lookup_function(module_inst, "_start"); wasm_runtime_lookup_function(slot->module_inst, "_start");
if (start_func == NULL) { if (start_func == NULL) {
nova64_log("Failed to find '_start' function in WASM module: %s\n", nova64_log("Failed to find '_start' function in WASM module: %s\n",
wasm_runtime_get_exception(module_inst)); wasm_runtime_get_exception(slot->module_inst));
free(buffer); free(buffer);
return false; return false;
} }
wasm_exec_env_t exec_env = slot->exec_env = wasm_runtime_create_exec_env(slot->module_inst, stack_size);
wasm_runtime_create_exec_env(module_inst, stack_size);
nova64_log("WASM module execution environment created successfully.\n");
uint32_t argv[2] = {5, 3}; // Example arguments for the "add" function uint32_t argv[2] = {5, 3}; // Example arguments for the "add" function
if (wasm_runtime_call_wasm(exec_env, start_func, 2, argv)) { slot->state = SLOT_RUNNING;
nova64_log("WASM module executed successfully. Sum: %d\n", argv[0]);
if (wasm_runtime_call_wasm(slot->exec_env, start_func, 0, NULL)) {
nova64_log("WASM module executed successfully.\n");
} else { } else {
nova64_log("Failed to execute WASM module: %s\n", nova64_log("Failed to execute WASM module: %s\n",
wasm_runtime_get_exception(module_inst)); wasm_runtime_get_exception(slot->module_inst));
} }
wasm_runtime_destroy_exec_env(exec_env); slot->state = SLOT_STOPPING;
wasm_runtime_deinstantiate(module_inst);
wasm_runtime_destroy_exec_env(slot->exec_env);
wasm_runtime_deinstantiate(slot->module_inst);
wasm_runtime_unload(module); wasm_runtime_unload(module);
free(buffer); free(buffer);
wasm_runtime_destroy(); wasm_runtime_destroy_thread_env();
slot->module_inst = NULL;
slot->exec_env = NULL;
slot->state = SLOT_FREE;
vTaskDelete(NULL);
return true; return true;
} }
void wasm_runner_terminate(void) {
nova64_log("Stopping all WASM tasks...\n");
for (int i = 0; i < MAX_WASM_TASKS; i++) {
if (g_wasm_slots[i].state == SLOT_RUNNING && g_wasm_slots[i].module_inst) {
g_wasm_slots[i].state = SLOT_STOPPING;
wasm_runtime_terminate(g_wasm_slots[i].module_inst);
}
}
bool any_running = true;
while (any_running) {
any_running = false;
for (int i = 0; i < MAX_WASM_TASKS; i++) {
if (g_wasm_slots[i].state != SLOT_FREE) {
any_running = true;
break;
}
}
if (any_running) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
nova64_log("All WASM tasks successfully stopped.\n");
}
+1 -3
View File
@@ -1,7 +1,4 @@
using System;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using Zio; using Zio;
using Zio.FileSystems; using Zio.FileSystems;
@@ -76,6 +73,7 @@ internal static class Program
private static readonly Nova64Core.LogMessageDelegate LogMessage = (message) => private static readonly Nova64Core.LogMessageDelegate LogMessage = (message) =>
{ {
Console.Write($"[Core] {message}"); Console.Write($"[Core] {message}");
Console.Out.Flush();
}; };
private static GCHandle? _ioHandle; private static GCHandle? _ioHandle;
Binary file not shown.
-17
View File
@@ -1,17 +0,0 @@
package main
// Declare a main function, this is the entrypoint into our go module
// That will be run. In our example, we won't need this
func main() {
_ = add(1, 2)
}
// This exports an add function.
// It takes in two 32-bit integer values
// And returns a 32-bit integer value.
// To make this function callable from JavaScript,
// we need to add the: "export add" comment above the function
//export add
func add(x int, y int) int {
return x + y
}
+3 -2
View File
@@ -1,9 +1,10 @@
extern "nova64" fn yield(timeout_ms: u32) void; const nova64 = @import("nova64.zig");
pub fn main() void { pub fn main() void {
_ = add(1, 2); _ = add(1, 2);
while (true) { while (true) {
yield(16); // Yield for 1 second nova64.log("Hello from Zig!");
// nova64.yield(1000);
} }
} }
+10
View File
@@ -0,0 +1,10 @@
pub extern "nova64" fn nova64_yield(timeout_ms: u32) void;
pub extern "nova64" fn nova64_log(message: [*]const u8) void;
pub fn log(message: []const u8) void {
nova64_log(message.ptr);
}
pub fn yield(timeout_ms: u32) void {
nova64_yield(timeout_ms);
}
+5
View File
@@ -0,0 +1,5 @@
extern "nova64" fn log(message: [*]const u8) void;
pub fn main() void {
log(&"Hello from Zig!"[0]);
}