Rework host/target interface to use ecall and proper syscalls instead of CSRs

This commit is contained in:
Toby Jaffey 2025-12-08 02:44:38 +00:00
parent a305fce466
commit 751f068486
26 changed files with 360 additions and 364 deletions

View file

@ -78,7 +78,7 @@ If the bytecode attempts to execute more instructions than the the passed value
## Internals ## 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. 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. `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()`. 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) 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" #include "../common/uvm32_common_custom.h"
// Precompiled binary program to print integers // 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[] = { uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, 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 // Map VM ioreq IOREQ_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
const uvm32_mapping_t env[] = { 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[]) { int main(int argc, char *argv[]) {

View file

@ -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

View file

@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static
LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections
LIBS:= #-lgcc # needed for softfp LIBS:= #-lgcc # needed for softfp
SRCS=${PROJECT}.c ../crt0.s SRCS=${PROJECT}.c ../crt0.S
all: all:
${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS}

View file

@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static
LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections
LIBS:= #-lgcc # needed for softfp LIBS:= #-lgcc # needed for softfp
SRCS=hello-asm.s SRCS=hello-asm.S
all: all:
${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS}

View file

@ -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"

View file

@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static
LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections
LIBS:= #-lgcc # needed for softfp LIBS:= #-lgcc # needed for softfp
SRCS=${PROJECT}.c ../crt0.s SRCS=${PROJECT}.c ../crt0.S
all: all:
${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS}

View file

@ -2,6 +2,18 @@
#include "uvm32_target.h" #include "uvm32_target.h"
void main(void) { 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
} }

View file

@ -9,26 +9,27 @@ use core::panic::PanicInfo;
include!(concat!(env!("OUT_DIR"), "/bindings.rs")); include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
// startup code // 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 { unsafe {
asm!( asm!("ecall",
"csrw {i}, {x}", in("a0") n,
i = const IOREQ_PRINTLN, in("a7") id,
x = in(reg) message.as_ptr(), 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) { fn printd(n: u32) {
unsafe { syscall(IOREQ_PRINTD, n);
asm!(
"csrw {i}, {x}",
i = const IOREQ_PRINTD,
x = in(reg) n,
);
}
} }
#[no_mangle] #[no_mangle]
@ -36,7 +37,7 @@ pub extern "C" fn main() {
for i in 0..10 { for i in 0..10 {
printd(i); printd(i);
} }
println("Hello, world!"); println("Hello, world!\0");
} }
#[panic_handler] #[panic_handler]

View file

@ -10,7 +10,7 @@ CFLAGS+=-g -Os -march=rv32ima_zicsr -mabi=ilp32 -static
LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections LDFLAGS:= -T ../linker.ld -nostdlib -Wl,--gc-sections
LIBS:= #-lgcc # needed for softfp LIBS:= #-lgcc # needed for softfp
SRCS=${PROJECT}.c ../crt0.s SRCS=${PROJECT}.c ../crt0.S
all: all:
${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS} ${PREFIX}gcc -o ${PROJECT}.elf ${CFLAGS} ${LDFLAGS} ${SRCS} ${LIBS}

View file

@ -4,35 +4,25 @@ const uvm32 = @cImport({
}); });
const std = @import("std"); 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 { pub inline fn println(val: [:0]const u8) void {
asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINTLN}) ++ ", %[arg1]" _ = syscall(uvm32.IOREQ_PRINTLN, @intFromPtr(val.ptr));
:
: [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 { 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);
} }

View file

@ -4,70 +4,52 @@ const uvm32 = @cImport({
}); });
const std = @import("std"); const std = @import("std");
// dupeZ would be better, but want to avoid using an allocator pub inline fn syscall(id: u32, param: u32) u32 {
var new_buf:[128]u8 = undefined; var val: u32 = undefined;
pub inline fn print(m: []const u8) void { asm volatile ("ecall"
@memcpy(new_buf[0..m.len], m); : [val] "={a1}" (val),
new_buf[m.len] = 0; : [param] "{a0}" (param),
const s = new_buf[0..m.len :0]; [id] "{a7}" (id),
: .{ .memory = true });
asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_PRINT}) ++ ", %[arg1]" return val;
:
: [arg1] "r" (s.ptr),
);
} }
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 { pub inline fn getch() ?u8 {
asm volatile ("csrw " ++ std.fmt.comptimePrint("0x{x}", .{uvm32.IOREQ_GETC}) ++ ", %[arg1]" const key = syscall(uvm32.IOREQ_GETC, 0);
: if (key == 0xFFFFFFFF) {
: [arg1] "r" (&getch_storage),
);
if (getch_storage <= 0xFF) {
return @truncate(getch_storage);
} else {
return null; 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);
}

View file

@ -1,6 +1,6 @@
// Definitions needed by both host and target // Definitions needed by both host and target
// CSRs for exposed host functions // syscalls for exposed host functions
#define IOREQ_PRINT 0x13A #define IOREQ_PRINT 0x13A
#define IOREQ_PRINTLN 0x13B #define IOREQ_PRINTLN 0x13B
#define IOREQ_PRINTD 0x13C #define IOREQ_PRINTD 0x13C

View file

@ -9,17 +9,29 @@ typedef int bool;
#define true 1 #define true 1
#define false 0 #define false 0
// Convenience macro for defining CSR helper functions static uint32_t syscall(uint32_t id, uint32_t param) {
#define xstr(a) str(a) register uint32_t a0 asm("a0") = (uint32_t)(param);
#define str(a) #a register uint32_t a1 asm("a1");
#define DEFINE_CSR_WRITE_FUNCTION(function_name, csr, typ) \ register uint32_t a7 asm("a7") = (uint32_t)(id);
static void function_name(typ val) { \
asm volatile( ".option norvc\ncsrrw x0," xstr(csr) ", %0\n" : : "r" (val)); \ asm volatile (
} "ecall"
#define DEFINE_CSR_WRITE_FUNCTION_VOID(function_name, csr) \ : "=r"(a1) // output
static void function_name(void) { \ : "r"(a0), "r"(a7) // input
asm volatile( ".option norvc\ncsrwi " xstr(csr) ", 0"); \ : "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_common_custom.h"
#include "uvm32_target_custom.h" #include "uvm32_target_custom.h"

View file

@ -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;
}

View file

@ -3,25 +3,28 @@
#include "common/uvm32_common_custom.h" #include "common/uvm32_common_custom.h"
// Precompiled binary program to print integers // 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[] = { uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x80, 0x00, 0x73, 0x50, 0x80, 0x13, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0x93, 0x08, 0x80, 0x13,
0x37, 0xf6, 0xff, 0xff, 0xb7, 0x17, 0x00, 0x00, 0x37, 0xe7, 0xff, 0xff, 0x73, 0x00, 0x00, 0x00, 0x37, 0xf5, 0xff, 0xff, 0xb7, 0x15, 0x00, 0x00,
0x13, 0x05, 0xf0, 0x01, 0xb7, 0x45, 0x00, 0x00, 0x13, 0x06, 0xd6, 0xcc, 0x37, 0xe6, 0xff, 0xff, 0x93, 0x06, 0xf0, 0x01, 0x13, 0x07, 0xd5, 0xcc,
0x93, 0x86, 0x37, 0x33, 0x13, 0x07, 0x77, 0xe6, 0x93, 0x87, 0x37, 0xb3, 0x93, 0x87, 0x35, 0x33, 0x13, 0x08, 0x76, 0xe6, 0x93, 0x82, 0x35, 0xb3,
0x13, 0x08, 0xa0, 0x00, 0x63, 0xcc, 0xc6, 0x06, 0x93, 0x08, 0x07, 0x00, 0x37, 0x43, 0x00, 0x00, 0x63, 0xc8, 0xe7, 0x08, 0x93, 0x03, 0x08, 0x00,
0x63, 0xc2, 0xe7, 0x06, 0x93, 0x03, 0x00, 0x00, 0x13, 0x03, 0x00, 0x00, 0x63, 0xca, 0x02, 0x07, 0x93, 0x08, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00,
0x13, 0x0e, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, 0x93, 0x02, 0x00, 0x02, 0x13, 0x0e, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, 0x13, 0x06, 0x00, 0x02,
0x13, 0x8f, 0x02, 0xfe, 0x63, 0x6e, 0xe5, 0x03, 0x33, 0x0f, 0x73, 0x00, 0x13, 0x05, 0x06, 0xfe, 0x63, 0xe2, 0xa6, 0x04, 0x33, 0x85, 0x15, 0x01,
0x63, 0xea, 0xe5, 0x03, 0x33, 0x8e, 0xce, 0x03, 0xb3, 0x0e, 0x73, 0x40, 0x63, 0x6e, 0xa3, 0x02, 0x13, 0x05, 0x00, 0x00, 0x33, 0x8e, 0xce, 0x03,
0x73, 0x50, 0x90, 0x13, 0x13, 0x5e, 0xbe, 0x40, 0xb3, 0x8e, 0x1e, 0x01, 0xb3, 0x8e, 0x15, 0x41, 0x93, 0x08, 0x90, 0x13, 0x73, 0x00, 0x00, 0x00,
0x33, 0x0e, 0xce, 0x00, 0x33, 0x83, 0xde, 0x03, 0x13, 0x53, 0xc3, 0x00, 0x13, 0x5e, 0xbe, 0x40, 0xb3, 0x8e, 0x7e, 0x00, 0x33, 0x0e, 0xee, 0x00,
0xb3, 0x03, 0xce, 0x03, 0x93, 0xd3, 0xc3, 0x00, 0x93, 0x82, 0x12, 0x00, 0xb3, 0x85, 0xde, 0x03, 0x93, 0xd5, 0xc5, 0x00, 0x33, 0x05, 0xce, 0x03,
0x6f, 0xf0, 0x5f, 0xfc, 0x73, 0x90, 0x02, 0x14, 0x93, 0x88, 0x18, 0x09, 0x93, 0x58, 0xc5, 0x00, 0x13, 0x06, 0x16, 0x00, 0x6f, 0xf0, 0xdf, 0xfb,
0xe3, 0xd2, 0x17, 0xfb, 0x73, 0x10, 0x08, 0x14, 0x13, 0x06, 0x96, 0x19, 0x93, 0x08, 0x00, 0x14, 0x13, 0x05, 0x06, 0x00, 0x73, 0x00, 0x00, 0x00,
0xe3, 0xd8, 0xc6, 0xf8, 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x0c, 0x93, 0x83, 0x13, 0x09, 0xe3, 0xda, 0x72, 0xf8, 0x13, 0x05, 0xa0, 0x00,
0x73, 0x10, 0xb5, 0x13, 0x67, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x08, 0x00, 0x14, 0x73, 0x00, 0x00, 0x00, 0x13, 0x07, 0x97, 0x19,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00 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 // Create an identifier for our host handler
@ -98,5 +101,5 @@ void loop(void) {
delay(2000); delay(2000);
} }
return 0; return;
} }

View file

@ -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) { static void setStatus(uvm32_state_t *vmst, uvm32_status_t newStatus) {
if (vmst->status == UVM32_STATUS_ERROR) { 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; return;
} else { } else {
vmst->status = newStatus; 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 // setup stack pointer
// la sp, _sstack // la sp, _sstack
// addi sp,sp,-16 // 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[10] = 0x00; //hart ID
vmst->core->regs[11] = 0; vmst->core->regs[11] = 0;
vmst->core->extraflags |= 3; // Machine-mode. vmst->core->extraflags |= 3; // Machine-mode.
@ -67,8 +67,45 @@ bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) {
return true; 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 uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
uint32_t num_instr = 0; uint32_t num_instr = 0;
// uvm32_evt_ioreq_buf_t b;
if (vmst->status != UVM32_STATUS_PAUSED) { if (vmst->status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY); setStatusErr(vmst, UVM32_ERR_NOTREADY);
setup_err_evt(vmst, evt); 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 // run CPU until no longer in running state
while(vmst->status == UVM32_STATUS_RUNNING) { while(vmst->status == UVM32_STATUS_RUNNING) {
uint64_t elapsedUs = 1; 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;i<vmst->numMappings;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); setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
setup_err_evt(vmst, evt); 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;i<vmst->numMappings;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) { bool uvm32_hasEnded(const uvm32_state_t *vmst) {
return vmst->status == UVM32_STATUS_ENDED; return vmst->status == UVM32_STATUS_ENDED;
} }

View file

@ -13,7 +13,7 @@
X(UVM32_ERR_NOTREADY) \ X(UVM32_ERR_NOTREADY) \
X(UVM32_ERR_MEM_RD) \ X(UVM32_ERR_MEM_RD) \
X(UVM32_ERR_MEM_WR) \ X(UVM32_ERR_MEM_WR) \
X(UVM32_ERR_BAD_CSR) \ X(UVM32_ERR_BAD_SYSCALL) \
X(UVM32_ERR_HUNG) \ X(UVM32_ERR_HUNG) \
X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_CORE) \
X(UVM32_ERR_INTERNAL_STATE) \ X(UVM32_ERR_INTERNAL_STATE) \
@ -40,9 +40,9 @@ typedef enum {
typedef uint32_t uvm32_user_ioreq_code_t; 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 { typedef struct {
uint32_t csr; uint32_t syscall;
uvm32_user_ioreq_code_t code; uvm32_user_ioreq_code_t code;
uvm32_ioreq_typ_t typ; uvm32_ioreq_typ_t typ;
} uvm32_mapping_t; } uvm32_mapping_t;
@ -75,11 +75,9 @@ typedef struct {
} data; } data;
} uvm32_evt_t; } uvm32_evt_t;
void uvm32_HandleOtherCSRWrite(void *userdata, uint16_t csrno, uint32_t value);
#define MINIRV32_DECORATE static #define MINIRV32_DECORATE static
#define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE #define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE
#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return 3;} #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 #ifndef MINIRV32_IMPLEMENTATION
#define MINIRV32_STEPPROTO #define MINIRV32_STEPPROTO
#endif #endif

View file

@ -5,12 +5,12 @@
#include "../common/uvm32_common_custom.h" #include "../common/uvm32_common_custom.h"
// Precompiled binary program to print integers // 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[] = { uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x93, 0x08, 0x80, 0x13,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00,
0x13, 0x07, 0xa0, 0x00, 0x73, 0x90, 0xc7, 0x13, 0x93, 0x87, 0x17, 0x00, 0x93, 0x07, 0xa0, 0x00, 0x93, 0x08, 0xc0, 0x13, 0x73, 0x00, 0x00, 0x00,
0xe3, 0x9c, 0xe7, 0xfe, 0x67, 0x80, 0x00, 0x00 0x13, 0x05, 0x15, 0x00, 0xe3, 0x1a, 0xf5, 0xfe, 0x67, 0x80, 0x00, 0x00
}; };
// Create an identifier for our host handler // 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 // Map VM ioreq IOREQ_PRINTD (0x13C) to F_PRINTD, tell VM to expect write of a U32
const uvm32_mapping_t env[] = { 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[]) { int main(int argc, char *argv[]) {

View file

@ -13,12 +13,12 @@
#define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM #define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM
// Precompiled binary program to print integers // 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[] = { uint8_t rom[] = {
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x73, 0x50, 0x80, 0x13, 0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0x00, 0x01, 0x93, 0x08, 0x80, 0x13,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x07, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00,
0x13, 0x07, 0xa0, 0x00, 0x73, 0x90, 0xc7, 0x13, 0x93, 0x87, 0x17, 0x00, 0x93, 0x07, 0xa0, 0x00, 0x93, 0x08, 0xc0, 0x13, 0x73, 0x00, 0x00, 0x00,
0xe3, 0x9c, 0xe7, 0xfe, 0x67, 0x80, 0x00, 0x00 0x13, 0x05, 0x15, 0x00, 0xe3, 0x1a, 0xf5, 0xfe, 0x67, 0x80, 0x00, 0x00
}; };
// Create an identifier for our host handler // 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 // Map VM ioreq IOREQ_PRINTD to F_PRINTD, tell VM to expect write of a U32
const uvm32_mapping_t env[] = { 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[]) { int main(int argc, char *argv[]) {

View file

@ -22,15 +22,15 @@ typedef enum {
F_GETC, F_GETC,
} f_code_t; } f_code_t;
// Map exposed ioreqs to CSRs // Map exposed ioreqs to syscalls
const uvm32_mapping_t env[] = { const uvm32_mapping_t env[] = {
{ .csr = IOREQ_PRINTLN, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN }, { .syscall = IOREQ_PRINTLN, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINTLN },
{ .csr = IOREQ_PRINT, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINT }, { .syscall = IOREQ_PRINT, .typ = IOREQ_TYP_BUF_TERMINATED_WR, .code = F_PRINT },
{ .csr = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD }, { .syscall = IOREQ_PRINTD, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTD },
{ .csr = IOREQ_PRINTX, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTX }, { .syscall = IOREQ_PRINTX, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTX },
{ .csr = IOREQ_PRINTC, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTC }, { .syscall = IOREQ_PRINTC, .typ = IOREQ_TYP_U32_WR, .code = F_PRINTC },
{ .csr = IOREQ_MILLIS, .typ = IOREQ_TYP_U32_RD, .code = F_MILLIS }, { .syscall = IOREQ_MILLIS, .typ = IOREQ_TYP_U32_RD, .code = F_MILLIS },
{ .csr = IOREQ_GETC, .typ = IOREQ_TYP_U32_RD, .code = F_GETC }, { .syscall = IOREQ_GETC, .typ = IOREQ_TYP_U32_RD, .code = F_GETC },
}; };
void disableRawMode(void) { void disableRawMode(void) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -66,8 +66,45 @@ bool uvm32_load(uvm32_state_t *vmst, uint8_t *rom, int len) {
return true; 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 uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter) {
uint32_t num_instr = 0; uint32_t num_instr = 0;
// uvm32_evt_ioreq_buf_t b;
if (vmst->status != UVM32_STATUS_PAUSED) { if (vmst->status != UVM32_STATUS_PAUSED) {
setStatusErr(vmst, UVM32_ERR_NOTREADY); setStatusErr(vmst, UVM32_ERR_NOTREADY);
setup_err_evt(vmst, evt); 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 // run CPU until no longer in running state
while(vmst->status == UVM32_STATUS_RUNNING) { while(vmst->status == UVM32_STATUS_RUNNING) {
uint64_t elapsedUs = 1; 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;i<vmst->numMappings;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); setStatusErr(vmst, UVM32_ERR_INTERNAL_CORE);
setup_err_evt(vmst, evt); 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;i<vmst->numMappings;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) { bool uvm32_hasEnded(const uvm32_state_t *vmst) {
return vmst->status == UVM32_STATUS_ENDED; return vmst->status == UVM32_STATUS_ENDED;
} }

View file

@ -13,7 +13,7 @@
X(UVM32_ERR_NOTREADY) \ X(UVM32_ERR_NOTREADY) \
X(UVM32_ERR_MEM_RD) \ X(UVM32_ERR_MEM_RD) \
X(UVM32_ERR_MEM_WR) \ X(UVM32_ERR_MEM_WR) \
X(UVM32_ERR_BAD_CSR) \ X(UVM32_ERR_BAD_SYSCALL) \
X(UVM32_ERR_HUNG) \ X(UVM32_ERR_HUNG) \
X(UVM32_ERR_INTERNAL_CORE) \ X(UVM32_ERR_INTERNAL_CORE) \
X(UVM32_ERR_INTERNAL_STATE) \ X(UVM32_ERR_INTERNAL_STATE) \
@ -40,9 +40,9 @@ typedef enum {
typedef uint32_t uvm32_user_ioreq_code_t; 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 { typedef struct {
uint32_t csr; uint32_t syscall;
uvm32_user_ioreq_code_t code; uvm32_user_ioreq_code_t code;
uvm32_ioreq_typ_t typ; uvm32_ioreq_typ_t typ;
} uvm32_mapping_t; } uvm32_mapping_t;
@ -75,11 +75,9 @@ typedef struct {
} data; } data;
} uvm32_evt_t; } uvm32_evt_t;
void uvm32_HandleOtherCSRWrite(void *userdata, uint16_t csrno, uint32_t value);
#define MINIRV32_DECORATE static #define MINIRV32_DECORATE static
#define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE #define MINI_RV32_RAM_SIZE UVM32_MEMORY_SIZE
#define MINIRV32_POSTEXEC(pc, ir, retval) {if (retval > 0) return 3;} #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 #ifndef MINIRV32_IMPLEMENTATION
#define MINIRV32_STEPPROTO #define MINIRV32_STEPPROTO
#endif #endif