From 751f068486beb7cf8acfdae9a24f43ac4612a15a Mon Sep 17 00:00:00 2001 From: Toby Jaffey Date: Mon, 8 Dec 2025 02:44:38 +0000 Subject: [PATCH] Rework host/target interface to use ecall and proper syscalls instead of CSRs --- README.md | 8 +- apps/crt0.s | 10 -- apps/fib/Makefile | 2 +- apps/hello-asm/Makefile | 2 +- apps/hello-asm/hello-asm.s | 9 -- apps/helloworld/Makefile | 2 +- apps/helloworld/helloworld.c | 14 ++- apps/rust-hello/src/main.rs | 29 +++--- apps/sketch/Makefile | 2 +- apps/zig-mandel/src/uvm.zig | 42 +++----- apps/zigtris/src/uvm.zig | 102 ++++++++----------- common/uvm32_common_custom.h | 2 +- common/uvm32_target.h | 34 ++++--- common/uvm32_target_custom.h | 17 ---- host-arduino/host-arduino.ino | 41 ++++---- host-arduino/uvm32.cpp | 178 +++++++++++++++++++--------------- host-arduino/uvm32.h | 8 +- host-mini/host-mini.c | 12 +-- host-parallel/host-parallel.c | 12 +-- host/host.c | 16 +-- precompiled/fib.bin | Bin 252 -> 476 bytes precompiled/helloworld.bin | Bin 60 -> 48 bytes precompiled/mandel.bin | Bin 204 -> 236 bytes precompiled/zigtris.bin | Bin 16284 -> 16364 bytes uvm32/uvm32.c | 174 ++++++++++++++++++--------------- uvm32/uvm32.h | 8 +- 26 files changed, 360 insertions(+), 364 deletions(-) delete mode 100644 apps/crt0.s delete mode 100644 apps/hello-asm/hello-asm.s diff --git a/README.md b/README.md index a317b95..16a1b48 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ If the bytecode attempts to execute more instructions than the the passed value ## Internals -uvm32 emulates a RISC-V 32bit CPU using [mini-rv32ima](https://github.com/cnlohr/mini-rv32ima). All IO from vm bytecode to the host is performed using [CSRs](https://five-embeddev.com/riscv-priv-isa-manual/Priv-v1.12/priv-csrs.html). Each "function" provided by the host requires a unique CSR value. A CSR passes a single `uint32_t` from bytecode to the host. +uvm32 emulates a RISC-V 32bit CPU using [mini-rv32ima](https://github.com/cnlohr/mini-rv32ima). All IO from vm bytecode to the host is performed using `ecall` syscalls. Each "function" provided by the host requires a unique syscall value. A syscall passes a single `uint32_t` from bytecode to the host and may receive a returned `uint32_t`. The host may treat the value as a pointer and modify memory. uvm32 is always in one of 4 states, paused, running, ended or error. @@ -102,7 +102,7 @@ There are two system ioreqs used by uvm32, `halt()` and `yield()`. `halt()` tells the host that the program has ended normally. `yield()` tells the host that the program requires more instructions to be executed. New ioreqs can be added to the host via `uvm32_init()`. -Each ioreq maps a CSR number to a value understood by the host (`F_PRINTD` below) and has an associated type which tells the host how to interpret the data passed to the CSR. +Each ioreq maps a syscall number to a value understood by the host (`F_PRINTD` below) and has an associated type which tells the host how to interpret the data passed to the syscall. Here is a full example of a working VM host from [apps/host-mini](apps/host-mini) @@ -116,7 +116,7 @@ Here is a full example of a working VM host from [apps/host-mini](apps/host-mini #include "../common/uvm32_common_custom.h" // Precompiled binary program to print integers -// This code expects to print via CSR 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) +// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) uint8_t rom[] = { 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, @@ -131,7 +131,7 @@ typedef enum { // Map VM ioreq IOREQ_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32 const uvm32_mapping_t env[] = { - { .csr = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, + { .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, }; int main(int argc, char *argv[]) { diff --git a/apps/crt0.s b/apps/crt0.s deleted file mode 100644 index 3209b9d..0000000 --- a/apps/crt0.s +++ /dev/null @@ -1,10 +0,0 @@ -.section .initial_jump , "ax", %progbits -.global _start -.align 4 -_start: -# sp is already setup by vm -sw ra,12(sp) -jal ra, main -csrwi 0x138,0 # halt -.section .data - diff --git a/apps/fib/Makefile b/apps/fib/Makefile index 6e39a11..fed2e20 100644 --- a/apps/fib/Makefile +++ b/apps/fib/Makefile @@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LIBS:= #-lgcc # needed for softfp -SRCS=${PROJECT}.c ../crt0.s +SRCS=${PROJECT}.c ../crt0.S all: ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} diff --git a/apps/hello-asm/Makefile b/apps/hello-asm/Makefile index 5a0f92c..8b5e7ae 100644 --- a/apps/hello-asm/Makefile +++ b/apps/hello-asm/Makefile @@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LIBS:= #-lgcc # needed for softfp -SRCS=hello-asm.s +SRCS=hello-asm.S all: ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} diff --git a/apps/hello-asm/hello-asm.s b/apps/hello-asm/hello-asm.s deleted file mode 100644 index 3c1fabe..0000000 --- a/apps/hello-asm/hello-asm.s +++ /dev/null @@ -1,9 +0,0 @@ -.section .initial_jump , "ax", %progbits -.global _start -_start: -la a5, str -csrrw zero,0x13b,a5 # println -csrwi 0x138,0 # halt -str: -.ascii "Hi" - diff --git a/apps/helloworld/Makefile b/apps/helloworld/Makefile index e508431..abff4b9 100644 --- a/apps/helloworld/Makefile +++ b/apps/helloworld/Makefile @@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LIBS:= #-lgcc # needed for softfp -SRCS=${PROJECT}.c ../crt0.s +SRCS=${PROJECT}.c ../crt0.S all: ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} diff --git a/apps/helloworld/helloworld.c b/apps/helloworld/helloworld.c index 975bddd..6d248a4 100644 --- a/apps/helloworld/helloworld.c +++ b/apps/helloworld/helloworld.c @@ -2,6 +2,18 @@ #include "uvm32_target.h" void main(void) { - println("Hello world"); + for (int i=0;i<10;i++) { + printd(i); + } +#if 0 + uint32_t c; + while(c = getc()) { + if (c != 0xFFFFFFFF) { + print("Got: "); + printx(c); + println(""); + } + } +#endif } diff --git a/apps/rust-hello/src/main.rs b/apps/rust-hello/src/main.rs index fc5c879..4d6281d 100644 --- a/apps/rust-hello/src/main.rs +++ b/apps/rust-hello/src/main.rs @@ -9,26 +9,27 @@ use core::panic::PanicInfo; include!(concat!(env!("OUT_DIR"), "/bindings.rs")); // startup code -global_asm!(include_str!("../../crt0.s")); +global_asm!(include_str!("../../crt0.S")); -fn println(message: &str) { +fn syscall(id: u32, n: u32) -> u32 { + let mut value; unsafe { - asm!( - "csrw {i}, {x}", - i = const IOREQ_PRINTLN, - x = in(reg) message.as_ptr(), + asm!("ecall", + in("a0") n, + in("a7") id, + lateout("a1") value, ); } + return value; +} + +fn println(message: &str) { + let addr_value = message.as_ptr() as u32; + syscall(IOREQ_PRINTLN, addr_value); } fn printd(n: u32) { - unsafe { - asm!( - "csrw {i}, {x}", - i = const IOREQ_PRINTD, - x = in(reg) n, - ); - } + syscall(IOREQ_PRINTD, n); } #[no_mangle] @@ -36,7 +37,7 @@ pub extern "C" fn main() { for i in 0..10 { printd(i); } - println("Hello, world!"); + println("Hello, world!\0"); } #[panic_handler] diff --git a/apps/sketch/Makefile b/apps/sketch/Makefile index ac77db1..524771d 100644 --- a/apps/sketch/Makefile +++ b/apps/sketch/Makefile @@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LIBS:= #-lgcc # needed for softfp -SRCS=${PROJECT}.c ../crt0.s +SRCS=${PROJECT}.c ../crt0.S all: ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} diff --git a/apps/zig-mandel/src/uvm.zig b/apps/zig-mandel/src/uvm.zig index e488c09..056da22 100644 --- a/apps/zig-mandel/src/uvm.zig +++ b/apps/zig-mandel/src/uvm.zig @@ -4,35 +4,25 @@ const uvm32 = @cImport({ }); const std = @import("std"); +pub inline fn syscall(id: u32, param: u32) u32 { + var val: u32 = undefined; + asm volatile ("ecall" + : [val] "={a1}" (val), + : [param] "{a0}" (param), + [id] "{a7}" (id), + : .{ .memory = true }); + return val; +} + pub inline fn println(val: [:0]const u8) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTLN}) ++ ", %[arg1]" - : - : [arg1] "r" (val.ptr), - ); -} - -pub inline fn printd(val: u32) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTD}) ++ ", %[arg1]" - : - : [arg1] "r" (val), - ); -} - -pub inline fn printx(val: u32) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTX}) ++ ", %[arg1]" - : - : [arg1] "r" (val), - ); -} - -pub inline fn printc(val: u32) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTC}) ++ ", %[arg1]" - : - : [arg1] "r" (val), - ); + _ = syscall(uvm32.IOREQ_PRINTLN, @intFromPtr(val.ptr)); } pub inline fn yield() void { - asm volatile (std.fmt.comptimePrint("csrwi 0x{x}, 0", .{uvm32.IOREQ_YIELD})); + _ = syscall(uvm32.IOREQ_YIELD, 0); +} + +pub inline fn printc(c:u8) void { + _ = syscall(uvm32.IOREQ_PRINTC, c); } diff --git a/apps/zigtris/src/uvm.zig b/apps/zigtris/src/uvm.zig index 631c481..5a66218 100644 --- a/apps/zigtris/src/uvm.zig +++ b/apps/zigtris/src/uvm.zig @@ -4,70 +4,52 @@ const uvm32 = @cImport({ }); const std = @import("std"); -// dupeZ would be better, but want to avoid using an allocator -var new_buf:[128]u8 = undefined; -pub inline fn print(m: []const u8) void { - @memcpy(new_buf[0..m.len], m); - new_buf[m.len] = 0; - const s = new_buf[0..m.len :0]; - - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINT}) ++ ", %[arg1]" - : - : [arg1] "r" (s.ptr), - ); +pub inline fn syscall(id: u32, param: u32) u32 { + var val: u32 = undefined; + asm volatile ("ecall" + : [val] "={a1}" (val), + : [param] "{a0}" (param), + [id] "{a7}" (id), + : .{ .memory = true }); + return val; } -pub inline fn println(val: [:0]const u8) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTLN}) ++ ", %[arg1]" - : - : [arg1] "r" (val.ptr), - ); -} - -pub inline fn printd(val: u32) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTD}) ++ ", %[arg1]" - : - : [arg1] "r" (val), - ); -} - -pub inline fn printx(val: u32) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTX}) ++ ", %[arg1]" - : - : [arg1] "r" (val), - ); -} - -pub inline fn printc(val: u32) void { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTC}) ++ ", %[arg1]" - : - : [arg1] "r" (val), - ); -} - -pub inline fn yield() void { - asm volatile (std.fmt.comptimePrint("csrwi 0x{x}, 0", .{uvm32.IOREQ_YIELD})); -} - -var millis_storage:u32 = 0; -pub inline fn millis() u32 { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_MILLIS}) ++ ", %[arg1]" - : - : [arg1] "r" (&millis_storage), - ); - return millis_storage; -} - -var getch_storage:u32 = 0; pub inline fn getch() ?u8 { - asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_GETC}) ++ ", %[arg1]" - : - : [arg1] "r" (&getch_storage), - ); - if (getch_storage <= 0xFF) { - return @truncate(getch_storage); - } else { + const key = syscall(uvm32.IOREQ_GETC, 0); + if (key == 0xFFFFFFFF) { return null; + } else { + return @truncate(key); } } +pub inline fn millis() u32 { + return syscall(uvm32.IOREQ_MILLIS, 0); +} + +// dupeZ would be better, but want to avoid using an allocator +// this is of course, unsafe... +var termination_buf:[128]u8 = undefined; + +pub inline fn print(m: []const u8) void { + @memcpy(termination_buf[0..m.len], m); + termination_buf[m.len] = 0; + const s = termination_buf[0..m.len :0]; + _ = syscall(uvm32.IOREQ_PRINT, @intFromPtr(s.ptr)); +} + +pub inline fn println(m: []const u8) void { + @memcpy(termination_buf[0..m.len], m); + termination_buf[m.len] = 0; + const s = termination_buf[0..m.len :0]; + _ = syscall(uvm32.IOREQ_PRINTLN, @intFromPtr(s.ptr)); +} + +pub inline fn yield() void { + _ = syscall(uvm32.IOREQ_YIELD, 0); +} + +pub inline fn printc(c:u8) void { + _ = syscall(uvm32.IOREQ_PRINTC, c); +} + diff --git a/common/uvm32_common_custom.h b/common/uvm32_common_custom.h index 7f6d0a8..1131458 100644 --- a/common/uvm32_common_custom.h +++ b/common/uvm32_common_custom.h @@ -1,6 +1,6 @@ // Definitions needed by both host and target -// CSRs for exposed host functions +// syscalls for exposed host functions #define IOREQ_PRINT 0x13A #define IOREQ_PRINTLN 0x13B #define IOREQ_PRINTD 0x13C diff --git a/common/uvm32_target.h b/common/uvm32_target.h index 2a9edf2..b4afdb3 100644 --- a/common/uvm32_target.h +++ b/common/uvm32_target.h @@ -9,17 +9,29 @@ typedef int bool; #define true 1 #define false 0 -// Convenience macro for defining CSR helper functions -#define xstr(a) str(a) -#define str(a) #a -#define DEFINE_CSR_WRITE_FUNCTION(function_name, csr, typ) \ - static void function_name(typ val) { \ - asm volatile( ".option norvc\ncsrrw x0," xstr(csr) ", %0\n" : : "r" (val)); \ - } -#define DEFINE_CSR_WRITE_FUNCTION_VOID(function_name, csr) \ - static void function_name(void) { \ - asm volatile( ".option norvc\ncsrwi " xstr(csr) ", 0"); \ - } +static uint32_t syscall(uint32_t id, uint32_t param) { + register uint32_t a0 asm("a0") = (uint32_t)(param); + register uint32_t a1 asm("a1"); + register uint32_t a7 asm("a7") = (uint32_t)(id); + + asm volatile ( + "ecall" + : "=r"(a1) // output + : "r"(a0), "r"(a7) // input + : "memory" + ); + return a1; +} + +#define syscall_cast(id, x) syscall((uint32_t)id, (uint32_t)x) +#define println(x) syscall_cast(IOREQ_PRINTLN, x) +#define print(x) syscall_cast(IOREQ_PRINT, x) +#define printd(x) syscall_cast(IOREQ_PRINTD, x) +#define printx(x) syscall_cast(IOREQ_PRINTX, x) +#define millis() syscall_cast(IOREQ_MILLIS, 0) +#define printc() syscall_cast(IOREQ_PRINTC, 0) +#define getc() syscall_cast(IOREQ_GETC, 0) +#define yield() syscall_cast(IOREQ_YIELD, 0) #include "uvm32_common_custom.h" #include "uvm32_target_custom.h" diff --git a/common/uvm32_target_custom.h b/common/uvm32_target_custom.h index 8aa3e12..e69de29 100644 --- a/common/uvm32_target_custom.h +++ b/common/uvm32_target_custom.h @@ -1,17 +0,0 @@ -// Define wrapper functions for target code to call CSRs - -DEFINE_CSR_WRITE_FUNCTION(print, IOREQ_PRINT, const char *) -DEFINE_CSR_WRITE_FUNCTION(printd, IOREQ_PRINTD, uint32_t) -DEFINE_CSR_WRITE_FUNCTION(printx, IOREQ_PRINTX, uint32_t) -DEFINE_CSR_WRITE_FUNCTION(printc, IOREQ_PRINTC, char) -DEFINE_CSR_WRITE_FUNCTION(println, IOREQ_PRINTLN, const char *) -DEFINE_CSR_WRITE_FUNCTION_VOID(halt, IOREQ_HALT) -DEFINE_CSR_WRITE_FUNCTION_VOID(yield, IOREQ_YIELD) -DEFINE_CSR_WRITE_FUNCTION(millis_internal, IOREQ_MILLIS, uint32_t *) - -static inline uint32_t millis(void) { - static uint32_t m; - millis_internal(&m); - return m; -} - diff --git a/host-arduino/host-arduino.ino b/host-arduino/host-arduino.ino index 269b443..fb0d14c 100644 --- a/host-arduino/host-arduino.ino +++ b/host-arduino/host-arduino.ino @@ -3,25 +3,28 @@ #include "common/uvm32_common_custom.h" // Precompiled binary program to print integers -// This code expects to print via CSR 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) +// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) uint8_t rom[] = { - 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x80, 0x00, 0x73, 0x50, 0x80, 0x13, - 0x37, 0xf6, 0xff, 0xff, 0xb7, 0x17, 0x00, 0x00, 0x37, 0xe7, 0xff, 0xff, - 0x13, 0x05, 0xf0, 0x01, 0xb7, 0x45, 0x00, 0x00, 0x13, 0x06, 0xd6, 0xcc, - 0x93, 0x86, 0x37, 0x33, 0x13, 0x07, 0x77, 0xe6, 0x93, 0x87, 0x37, 0xb3, - 0x13, 0x08, 0xa0, 0x00, 0x63, 0xcc, 0xc6, 0x06, 0x93, 0x08, 0x07, 0x00, - 0x63, 0xc2, 0xe7, 0x06, 0x93, 0x03, 0x00, 0x00, 0x13, 0x03, 0x00, 0x00, - 0x13, 0x0e, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, 0x93, 0x02, 0x00, 0x02, - 0x13, 0x8f, 0x02, 0xfe, 0x63, 0x6e, 0xe5, 0x03, 0x33, 0x0f, 0x73, 0x00, - 0x63, 0xea, 0xe5, 0x03, 0x33, 0x8e, 0xce, 0x03, 0xb3, 0x0e, 0x73, 0x40, - 0x73, 0x50, 0x90, 0x13, 0x13, 0x5e, 0xbe, 0x40, 0xb3, 0x8e, 0x1e, 0x01, - 0x33, 0x0e, 0xce, 0x00, 0x33, 0x83, 0xde, 0x03, 0x13, 0x53, 0xc3, 0x00, - 0xb3, 0x03, 0xce, 0x03, 0x93, 0xd3, 0xc3, 0x00, 0x93, 0x82, 0x12, 0x00, - 0x6f, 0xf0, 0x5f, 0xfc, 0x73, 0x90, 0x02, 0x14, 0x93, 0x88, 0x18, 0x09, - 0xe3, 0xd2, 0x17, 0xfb, 0x73, 0x10, 0x08, 0x14, 0x13, 0x06, 0x96, 0x19, - 0xe3, 0xd8, 0xc6, 0xf8, 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0c, - 0x73, 0x10, 0xb5, 0x13, 0x67, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 + 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0x93, 0x08, 0x80, 0x13, + 0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00, + 0x37, 0xe6, 0xff, 0xff, 0x93, 0x06, 0xf0, 0x01, 0x13, 0x07, 0xd5, 0xcc, + 0x93, 0x87, 0x35, 0x33, 0x13, 0x08, 0x76, 0xe6, 0x93, 0x82, 0x35, 0xb3, + 0x37, 0x43, 0x00, 0x00, 0x63, 0xc8, 0xe7, 0x08, 0x93, 0x03, 0x08, 0x00, + 0x63, 0xca, 0x02, 0x07, 0x93, 0x08, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00, + 0x13, 0x0e, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, 0x13, 0x06, 0x00, 0x02, + 0x13, 0x05, 0x06, 0xfe, 0x63, 0xe2, 0xa6, 0x04, 0x33, 0x85, 0x15, 0x01, + 0x63, 0x6e, 0xa3, 0x02, 0x13, 0x05, 0x00, 0x00, 0x33, 0x8e, 0xce, 0x03, + 0xb3, 0x8e, 0x15, 0x41, 0x93, 0x08, 0x90, 0x13, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x5e, 0xbe, 0x40, 0xb3, 0x8e, 0x7e, 0x00, 0x33, 0x0e, 0xee, 0x00, + 0xb3, 0x85, 0xde, 0x03, 0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xce, 0x03, + 0x93, 0x58, 0xc5, 0x00, 0x13, 0x06, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb, + 0x93, 0x08, 0x00, 0x14, 0x13, 0x05, 0x06, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x93, 0x83, 0x13, 0x09, 0xe3, 0xda, 0x72, 0xf8, 0x13, 0x05, 0xa0, 0x00, + 0x93, 0x08, 0x00, 0x14, 0x73, 0x00, 0x00, 0x00, 0x13, 0x07, 0x97, 0x19, + 0xe3, 0xdc, 0xe7, 0xf6, 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0e, + 0x93, 0x08, 0xb0, 0x13, 0x73, 0x00, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 }; // Create an identifier for our host handler @@ -98,5 +101,5 @@ void loop(void) { delay(2000); } - return 0; + return; } diff --git a/host-arduino/uvm32.cpp b/host-arduino/uvm32.cpp index 69da7c4..4851a58 100644 --- a/host-arduino/uvm32.cpp +++ b/host-arduino/uvm32.cpp @@ -24,7 +24,7 @@ static void setup_err_evt(uvm32_state_t *vmst, uvm32_evt_t *evt) { static void setStatus(uvm32_state_t *vmst, uvm32_status_t newStatus) { if (vmst->status == UVM32_STATUS_ERROR) { - // always stay in error state until a uvm32_reset() + // always stay in error state until a uvm32_init() return; } else { vmst->status = newStatus; @@ -47,7 +47,7 @@ void uvm32_init(uvm32_state_t *vmst, const uvm32_mapping_t *mappings, uint32_t n // setup stack pointer // la sp, _sstack // addi sp,sp,-16 - vmst->core->regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE) - 16; + vmst->core->regs[2] = (MINIRV32_RAM_IMAGE_OFFSET + UVM32_MEMORY_SIZE - sizeof(struct MiniRV32IMAState)) - 16; vmst->core->regs[10] = 0x00; //hart ID vmst->core->regs[11] = 0; vmst->core->extraflags |= 3; // Machine-mode. @@ -67,8 +67,45 @@ bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) { return true; } +// Read C-string up to terminator and return len,ptr +static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t terminator, uvm32_evt_ioreq_buf_t *buf) { + uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; + uint32_t p = ptrstart; + if (p >= UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + buf->ptr = NULL; + buf->len = 0; + return; + } + while(vmst->memory[p] != terminator) { + p++; + if (p >= UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + buf->ptr = NULL; + buf->len = 0; + return; + } + } + buf->ptr = &vmst->memory[ptrstart]; + buf->len = p - ptrstart; +} + +#if 0 +static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_ioreq_buf_t *buf) { + uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; + if (ptrstart + len >= UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + } + buf->ptr = &vmst->memory[ptrstart]; + buf->len = len; +} +#endif + + uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) { uint32_t num_instr = 0; +// uvm32_evt_ioreq_buf_t b; + if (vmst->status != UVM32_STATUS_PAUSED) { setStatusErr(vmst, UVM32_ERR_NOTREADY); setup_err_evt(vmst, evt); @@ -80,7 +117,65 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) // run CPU until no longer in running state while(vmst->status == UVM32_STATUS_RUNNING) { uint64_t elapsedUs = 1; - if (0 != MiniRV32IMAStep(vmst, vmst->core, vmst->memory, 0, elapsedUs, 1)) { + uint32_t ret; + ret = MiniRV32IMAStep(vmst, vmst->core, vmst->memory, 0, elapsedUs, 1); + if (3 == ret) { + const uint32_t syscall = vmst->core->regs[17]; // a7 + uint32_t value = vmst->core->regs[10]; // a0 + bool syscall_valid = false; + // on exception we should jump to mtvec, but we handle directly + // and skip over the ecall instruction + vmst->core->pc += 4; + switch(syscall) { + // inbuilt syscalls + case IOREQ_HALT: + setStatus(vmst, UVM32_STATUS_ENDED); + syscall_valid = true; + break; + case IOREQ_YIELD: + vmst->ioevt.typ = UVM32_EVT_YIELD; + setStatus(vmst, UVM32_STATUS_PAUSED); + syscall_valid = true; + break; + + // user defined syscalls + default: + // search in mappings + for (int i=0;inumMappings;i++) { + if (syscall == vmst->mappings[i].syscall) { + // setup ioevt.data according to mapping typ + switch(vmst->mappings[i].typ) { + case IOREQ_TYP_VOID: + break; + case IOREQ_TYP_U32_WR: + vmst->ioevt.data.ioreq.val.u32 = value; + break; + case IOREQ_TYP_BUF_TERMINATED_WR: + get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf); + break; + case IOREQ_TYP_U32_RD: +// get_safeptr(vmst, value, 4, &b); + vmst->ioevt.data.ioreq.val.u32p = &vmst->core->regs[11]; // r1, //(uint32_t *)b.ptr; + break; + } + vmst->ioevt.typ = UVM32_EVT_IOREQ; + vmst->ioevt.data.ioreq.code = vmst->mappings[i].code; + vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ; +//#warning FIXME, retval +// vmst->core->regs[11] = 456; // r1 + setStatus(vmst, UVM32_STATUS_PAUSED); + syscall_valid = true; + break; // stop searching + } + } + // no mapping found + if (!syscall_valid) { + setStatusErr(vmst, UVM32_ERR_BAD_SYSCALL); + } + break; + } + } else if (ret != 0) { + // unhandled exception setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); setup_err_evt(vmst, evt); } @@ -116,83 +211,6 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) } } -// Read C-string up to terminator and return len,ptr -static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t terminator, uvm32_evt_ioreq_buf_t *buf) { - uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - uint32_t p = ptrstart; - if (p >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = NULL; - buf->len = 0; - return; - } - while(vmst->memory[p] != terminator) { - p++; - if (p >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = NULL; - buf->len = 0; - return; - } - } - buf->ptr = &vmst->memory[ptrstart]; - buf->len = p - ptrstart; -} - -static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_ioreq_buf_t *buf) { - uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - if (ptrstart + len >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - } - buf->ptr = &vmst->memory[ptrstart]; - buf->len = len; -} - -void uvm32_HandleOtherCSRWrite(void *userdata, uint16_t csrno, uint32_t value) { - uvm32_evt_ioreq_buf_t b; - uvm32_state_t *vmst = (uvm32_state_t *)userdata; - - switch(csrno) { - case IOREQ_HALT: - setStatus(vmst, UVM32_STATUS_ENDED); - break; - case IOREQ_YIELD: - vmst->ioevt.typ = UVM32_EVT_YIELD; - setStatus(vmst, UVM32_STATUS_PAUSED); - break; - - default: - // search in mappings - for (int i=0;inumMappings;i++) { - if (csrno == vmst->mappings[i].csr) { - // setup ioevt.data according to mapping typ - switch(vmst->mappings[i].typ) { - case IOREQ_TYP_VOID: - break; - case IOREQ_TYP_U32_WR: - vmst->ioevt.data.ioreq.val.u32 = value; - break; - case IOREQ_TYP_BUF_TERMINATED_WR: - get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf); - break; - case IOREQ_TYP_U32_RD: - get_safeptr(vmst, value, 4, &b); - vmst->ioevt.data.ioreq.val.u32p = (uint32_t *)b.ptr; - break; - } - vmst->ioevt.typ = UVM32_EVT_IOREQ; - vmst->ioevt.data.ioreq.code = vmst->mappings[i].code; - vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ; - setStatus(vmst, UVM32_STATUS_PAUSED); - return; - } - } - // no mapping found - setStatusErr(vmst, UVM32_ERR_BAD_CSR); - break; - } -} - bool uvm32_hasEnded(const uvm32_state_t *vmst) { return vmst->status == UVM32_STATUS_ENDED; } diff --git a/host-arduino/uvm32.h b/host-arduino/uvm32.h index 4caaa7b..9a55b76 100644 --- a/host-arduino/uvm32.h +++ b/host-arduino/uvm32.h @@ -13,7 +13,7 @@ X(UVM32_ERR_NOTREADY) \ X(UVM32_ERR_MEM_RD) \ X(UVM32_ERR_MEM_WR) \ - X(UVM32_ERR_BAD_CSR) \ + X(UVM32_ERR_BAD_SYSCALL) \ X(UVM32_ERR_HUNG) \ X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_STATE) \ @@ -40,9 +40,9 @@ typedef enum { typedef uint32_t uvm32_user_ioreq_code_t; -// user supplied mapping from csr to typed ioreq +// user supplied mapping from syscall to typed ioreq typedef struct { - uint32_t csr; + uint32_t syscall; uvm32_user_ioreq_code_t code; uvm32_ioreq_typ_t typ; } uvm32_mapping_t; @@ -75,11 +75,9 @@ typedef struct { } data; } uvm32_evt_t; -void uvm32_HandleOtherCSRWrite(void *userdata, uint16_t csrno, uint32_t value); #define MINIRV32_DECORATE static #define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE #define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return 3;} -#define MINIRV32_OTHERCSR_WRITE(csrno, value) uvm32_HandleOtherCSRWrite(userdata, csrno, value); #ifndef MINIRV32_IMPLEMENTATION #define MINIRV32_STEPPROTO #endif diff --git a/host-mini/host-mini.c b/host-mini/host-mini.c index dce277a..fe1e76a 100644 --- a/host-mini/host-mini.c +++ b/host-mini/host-mini.c @@ -5,12 +5,12 @@ #include "../common/uvm32_common_custom.h" // Precompiled binary program to print integers -// This code expects to print via CSR 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) +// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) uint8_t rom[] = { - 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, - 0x13, 0x07, 0xa0, 0x00, 0x73, 0x90, 0xc7, 0x13, 0x93, 0x87, 0x17, 0x00, - 0xe3, 0x9c, 0xe7, 0xfe, 0x67, 0x80, 0x00, 0x00 + 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x93, 0x08, 0x80, 0x13, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00, + 0x93, 0x07, 0xa0, 0x00, 0x93, 0x08, 0xc0, 0x13, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x05, 0x15, 0x00, 0xe3, 0x1a, 0xf5, 0xfe, 0x67, 0x80, 0x00, 0x00 }; // Create an identifier for our host handler @@ -20,7 +20,7 @@ typedef enum { // Map VM ioreq IOREQ_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32 const uvm32_mapping_t env[] = { - { .csr = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, + { .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, }; int main(int argc, char *argv[]) { diff --git a/host-parallel/host-parallel.c b/host-parallel/host-parallel.c index 699258d..f9f09e0 100644 --- a/host-parallel/host-parallel.c +++ b/host-parallel/host-parallel.c @@ -13,12 +13,12 @@ #define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM // Precompiled binary program to print integers -// This code expects to print via CSR 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) +// This code expects to print via syscall 0x13C (IOREQ_PRINTD in common/uvm32_common_custom.h) uint8_t rom[] = { - 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, - 0x13, 0x07, 0xa0, 0x00, 0x73, 0x90, 0xc7, 0x13, 0x93, 0x87, 0x17, 0x00, - 0xe3, 0x9c, 0xe7, 0xfe, 0x67, 0x80, 0x00, 0x00 + 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x93, 0x08, 0x80, 0x13, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00, + 0x93, 0x07, 0xa0, 0x00, 0x93, 0x08, 0xc0, 0x13, 0x73, 0x00, 0x00, 0x00, + 0x13, 0x05, 0x15, 0x00, 0xe3, 0x1a, 0xf5, 0xfe, 0x67, 0x80, 0x00, 0x00 }; // Create an identifier for our host handler @@ -28,7 +28,7 @@ typedef enum { // Map VM ioreq IOREQ_PRINTD to F_PRINTD, tell VM to expect write of a U32 const uvm32_mapping_t env[] = { - { .csr = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, + { .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, }; int main(int argc, char *argv[]) { diff --git a/host/host.c b/host/host.c index 41b122f..d5876a7 100644 --- a/host/host.c +++ b/host/host.c @@ -22,15 +22,15 @@ typedef enum { F_GETC, } f_code_t; -// Map exposed ioreqs to CSRs +// Map exposed ioreqs to syscalls const uvm32_mapping_t env[] = { - { .csr = IOREQ_PRINTLN, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN }, - { .csr = IOREQ_PRINT, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINT }, - { .csr = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, - { .csr = IOREQ_PRINTX, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTX }, - { .csr = IOREQ_PRINTC, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTC }, - { .csr = IOREQ_MILLIS, .typ = IOREQ_TYP_U32_RD, .code = F_MILLIS }, - { .csr = IOREQ_GETC, .typ = IOREQ_TYP_U32_RD, .code = F_GETC }, + { .syscall = IOREQ_PRINTLN, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN }, + { .syscall = IOREQ_PRINT, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINT }, + { .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, + { .syscall = IOREQ_PRINTX, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTX }, + { .syscall = IOREQ_PRINTC, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTC }, + { .syscall = IOREQ_MILLIS, .typ = IOREQ_TYP_U32_RD, .code = F_MILLIS }, + { .syscall = IOREQ_GETC, .typ = IOREQ_TYP_U32_RD, .code = F_GETC }, }; void disableRawMode(void) { diff --git a/precompiled/fib.bin b/precompiled/fib.bin index 1786739d5ea12638ef76b28f599b1f5d4f4df87e..9d95c6d11e29c3aa485af1a9ee37ad5a117dfe32 100755 GIT binary patch literal 476 zcmZvZPfG$p6u{rxoiW!g$^>16-5L?K2*N%3f;Qq zp)b%E@K{mc21!Ary;x(WdhRSjf5eL0L^8p0nOl}7t zSRQ2U24rOeWqB9n<^f9Q2<1)zWT}C|;-D-5#TclZ17ekdd)PNZXM|=cz>spxDq>h2 zfmsV6tTn*gLd`dHANOfs)CYU0cn$=A5R-WLrTUEet`Rc_Ufldqkp(V0K^4(?lGB`u z0i)RXH}y;$wV(DPSH;MyM|WZw@^h%qtx@myKyhoyus_raIbVOy1+$1{&;D~xJ!ziH zQb6_eHpJE*Qomz7d{;cbWRBkiXF&G`Vu8ry7x{?j#lz`62VZkk#EXL8rS&g@`^F@; sUz0eUtLU*v`{#r~F{+_@`+M&NcX5%(9UL17h|c|y4xJTzR0#+vebo7Z;OiK;;M&m-aOmt{^9gSL zLbIexCGn*;cR7c1?me8FU0q-YcV;J!aT=inq5<{b%Yk<`sRFKBrWEG(9UuM+wyao`2CJsyR|+@lw$Bv8(PN$b=PLBx zFGo?gA-F7b6i?^2+%PK_@9NRM*Tq*y?IryTnJy0dQBmI=hCh0I$nURj(x;-B4}38* K%Z1IKXZQnNPfN%E diff --git a/precompiled/helloworld.bin b/precompiled/helloworld.bin index 3fca079c8b17bfd2b490d6caf97c4722a430962b..07ed4a216a07f9a0d4fdb9b26bb5081c5e878459 100755 GIT binary patch literal 48 wcmY#Z6J&VLz`!_}qd~Zs0Rn_s85kzBFJPF=aR4kP%qq(8SnBJ)^ach70MK>{BLDyZ literal 60 zcmY#Z6J&VLz`$4>&>+kJ1>4yf8YZ{1GZ#wivsCq|Nl>B`@ks7e)Y`cc2i?vj}4Pdj=8-S8f R;E|e>ldn*oUzC%=002VFMkoLP literal 204 zcmY#Z6J&VL(7;d}&>(F7?f?Jn;tUMt&;S1yX8pjp-IaksnC;q`$!+Gw!tCYGCbye! z7Uo#MkbLGC+hh)QhU7!f*(Ngs)qpS`1H)txW@2Cx?q~X!ocEO3n7^1I`4y1vJIB14 zuh^kDV1lr4+&+iReR7P(eCHU9o9{6T2OnnG%zTb{^5w$}lbeJX@;}7?DW1S2GPy&7 z^YJC|-^BtPBEoFbBp=^6_QRZ&p+T6Hm8V!>t8jV)0~mOu=H%onl;;=aq%Z&g0eM7` diff --git a/precompiled/zigtris.bin b/precompiled/zigtris.bin index 9ce1ebb77919c4daaae4b11cb0e76c4d143b75c3..96ccb8f64f73c5c79383cc5d114ec8714aef57e8 100755 GIT binary patch delta 4346 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 delta 4288 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 UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + buf->ptr = NULL; + buf->len = 0; + return; + } + while(vmst->memory[p] != terminator) { + p++; + if (p >= UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + buf->ptr = NULL; + buf->len = 0; + return; + } + } + buf->ptr = &vmst->memory[ptrstart]; + buf->len = p - ptrstart; +} + +#if 0 +static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_ioreq_buf_t *buf) { + uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; + if (ptrstart + len >= UVM32_MEMORY_SIZE) { + setStatusErr(vmst, UVM32_ERR_MEM_RD); + } + buf->ptr = &vmst->memory[ptrstart]; + buf->len = len; +} +#endif + + uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) { uint32_t num_instr = 0; +// uvm32_evt_ioreq_buf_t b; + if (vmst->status != UVM32_STATUS_PAUSED) { setStatusErr(vmst, UVM32_ERR_NOTREADY); setup_err_evt(vmst, evt); @@ -79,7 +116,65 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) // run CPU until no longer in running state while(vmst->status == UVM32_STATUS_RUNNING) { uint64_t elapsedUs = 1; - if (0 != MiniRV32IMAStep(vmst, vmst->core, vmst->memory, 0, elapsedUs, 1)) { + uint32_t ret; + ret = MiniRV32IMAStep(vmst, vmst->core, vmst->memory, 0, elapsedUs, 1); + if (3 == ret) { + const uint32_t syscall = vmst->core->regs[17]; // a7 + uint32_t value = vmst->core->regs[10]; // a0 + bool syscall_valid = false; + // on exception we should jump to mtvec, but we handle directly + // and skip over the ecall instruction + vmst->core->pc += 4; + switch(syscall) { + // inbuilt syscalls + case IOREQ_HALT: + setStatus(vmst, UVM32_STATUS_ENDED); + syscall_valid = true; + break; + case IOREQ_YIELD: + vmst->ioevt.typ = UVM32_EVT_YIELD; + setStatus(vmst, UVM32_STATUS_PAUSED); + syscall_valid = true; + break; + + // user defined syscalls + default: + // search in mappings + for (int i=0;inumMappings;i++) { + if (syscall == vmst->mappings[i].syscall) { + // setup ioevt.data according to mapping typ + switch(vmst->mappings[i].typ) { + case IOREQ_TYP_VOID: + break; + case IOREQ_TYP_U32_WR: + vmst->ioevt.data.ioreq.val.u32 = value; + break; + case IOREQ_TYP_BUF_TERMINATED_WR: + get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf); + break; + case IOREQ_TYP_U32_RD: +// get_safeptr(vmst, value, 4, &b); + vmst->ioevt.data.ioreq.val.u32p = &vmst->core->regs[11]; // r1, //(uint32_t *)b.ptr; + break; + } + vmst->ioevt.typ = UVM32_EVT_IOREQ; + vmst->ioevt.data.ioreq.code = vmst->mappings[i].code; + vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ; +//#warning FIXME, retval +// vmst->core->regs[11] = 456; // r1 + setStatus(vmst, UVM32_STATUS_PAUSED); + syscall_valid = true; + break; // stop searching + } + } + // no mapping found + if (!syscall_valid) { + setStatusErr(vmst, UVM32_ERR_BAD_SYSCALL); + } + break; + } + } else if (ret != 0) { + // unhandled exception setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE); setup_err_evt(vmst, evt); } @@ -115,83 +210,6 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) } } -// Read C-string up to terminator and return len,ptr -static void get_safeptr_terminated(uvm32_state_t *vmst, uint32_t addr, uint8_t terminator, uvm32_evt_ioreq_buf_t *buf) { - uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - uint32_t p = ptrstart; - if (p >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = NULL; - buf->len = 0; - return; - } - while(vmst->memory[p] != terminator) { - p++; - if (p >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - buf->ptr = NULL; - buf->len = 0; - return; - } - } - buf->ptr = &vmst->memory[ptrstart]; - buf->len = p - ptrstart; -} - -static void get_safeptr(uvm32_state_t *vmst, uint32_t addr, uint32_t len, uvm32_evt_ioreq_buf_t *buf) { - uint32_t ptrstart = addr - MINIRV32_RAM_IMAGE_OFFSET; - if (ptrstart + len >= UVM32_MEMORY_SIZE) { - setStatusErr(vmst, UVM32_ERR_MEM_RD); - } - buf->ptr = &vmst->memory[ptrstart]; - buf->len = len; -} - -void uvm32_HandleOtherCSRWrite(void *userdata, uint16_t csrno, uint32_t value) { - uvm32_evt_ioreq_buf_t b; - uvm32_state_t *vmst = (uvm32_state_t *)userdata; - - switch(csrno) { - case IOREQ_HALT: - setStatus(vmst, UVM32_STATUS_ENDED); - break; - case IOREQ_YIELD: - vmst->ioevt.typ = UVM32_EVT_YIELD; - setStatus(vmst, UVM32_STATUS_PAUSED); - break; - - default: - // search in mappings - for (int i=0;inumMappings;i++) { - if (csrno == vmst->mappings[i].csr) { - // setup ioevt.data according to mapping typ - switch(vmst->mappings[i].typ) { - case IOREQ_TYP_VOID: - break; - case IOREQ_TYP_U32_WR: - vmst->ioevt.data.ioreq.val.u32 = value; - break; - case IOREQ_TYP_BUF_TERMINATED_WR: - get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf); - break; - case IOREQ_TYP_U32_RD: - get_safeptr(vmst, value, 4, &b); - vmst->ioevt.data.ioreq.val.u32p = (uint32_t *)b.ptr; - break; - } - vmst->ioevt.typ = UVM32_EVT_IOREQ; - vmst->ioevt.data.ioreq.code = vmst->mappings[i].code; - vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ; - setStatus(vmst, UVM32_STATUS_PAUSED); - return; - } - } - // no mapping found - setStatusErr(vmst, UVM32_ERR_BAD_CSR); - break; - } -} - bool uvm32_hasEnded(const uvm32_state_t *vmst) { return vmst->status == UVM32_STATUS_ENDED; } diff --git a/uvm32/uvm32.h b/uvm32/uvm32.h index 4caaa7b..9a55b76 100644 --- a/uvm32/uvm32.h +++ b/uvm32/uvm32.h @@ -13,7 +13,7 @@ X(UVM32_ERR_NOTREADY) \ X(UVM32_ERR_MEM_RD) \ X(UVM32_ERR_MEM_WR) \ - X(UVM32_ERR_BAD_CSR) \ + X(UVM32_ERR_BAD_SYSCALL) \ X(UVM32_ERR_HUNG) \ X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_STATE) \ @@ -40,9 +40,9 @@ typedef enum { typedef uint32_t uvm32_user_ioreq_code_t; -// user supplied mapping from csr to typed ioreq +// user supplied mapping from syscall to typed ioreq typedef struct { - uint32_t csr; + uint32_t syscall; uvm32_user_ioreq_code_t code; uvm32_ioreq_typ_t typ; } uvm32_mapping_t; @@ -75,11 +75,9 @@ typedef struct { } data; } uvm32_evt_t; -void uvm32_HandleOtherCSRWrite(void *userdata, uint16_t csrno, uint32_t value); #define MINIRV32_DECORATE static #define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE #define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return 3;} -#define MINIRV32_OTHERCSR_WRITE(csrno, value) uvm32_HandleOtherCSRWrite(userdata, csrno, value); #ifndef MINIRV32_IMPLEMENTATION #define MINIRV32_STEPPROTO #endif