This commit is contained in:
Toby Jaffey 2025-12-08 07:31:21 +00:00
parent 7bf906f369
commit f6cd790fcb
5 changed files with 33 additions and 6 deletions

View file

@ -1,4 +1,5 @@
all: all:
cat uvm32/uvm32.h uvm32/uvm32.c uvm32/mini-rv32ima.h common/*.h > uvm32-single-file.c
(cd host && make) (cd host && make)
(cd host-mini && make) (cd host-mini && make)
(cd host-parallel && make) (cd host-parallel && make)

View file

@ -33,6 +33,11 @@ Although based on a fully fledged CPU emulator, uvm32 is intended for executing
host/host precompiled/mandel.bin host/host precompiled/mandel.bin
host/host precompiled/zigtris.bin host/host precompiled/zigtris.bin
Build sample apps (sets up docker for cross compiler)
cd apps
make
Build one of the sample apps (requires docker for C, or Zig, or Rust) Build one of the sample apps (requires docker for C, or Zig, or Rust)
cd apps/helloworld && make cd apps/helloworld && make
@ -78,7 +83,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 `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 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 syscall 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.
@ -95,6 +100,30 @@ stateDiagram
At boot, the whole memory is zeroed. The user program is placed at the start, the CPU registers are stored at the end. The stack pointer is set to the start of the CPU registers and grows downwards. At boot, the whole memory is zeroed. The user program is placed at the start, the CPU registers are stored at the end. The stack pointer is set to the start of the CPU registers and grows downwards.
## syscall ABI
All communication between bytecode and the vm host is performed via syscalls.
To make a syscall, register `a7` is set with the syscall number (an `IOREQ_x`) and `a0` is set with the syscall parameter. The response is returned in `a1`.
[target.h](common/uvm32_target.h#L12)
```c
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;
}
```
## ioreqs ## ioreqs
There are two system ioreqs used by uvm32, `halt()` and `yield()`. There are two system ioreqs used by uvm32, `halt()` and `yield()`.

View file

@ -34,7 +34,6 @@ static uint32_t syscall(uint32_t id, uint32_t param) {
#define yield() syscall_cast(IOREQ_YIELD, 0) #define yield() syscall_cast(IOREQ_YIELD, 0)
#include "uvm32_common_custom.h" #include "uvm32_common_custom.h"
#include "uvm32_target_custom.h"
// provide main, with setup()/loop() flow // provide main, with setup()/loop() flow
void setup(void); void setup(void);

View file

@ -153,15 +153,13 @@ uint32_t uvm32_run(uvm32_state_t *vmst, uvm32_evt_t *evt, uint32_t instr_meter)
get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf); get_safeptr_terminated(vmst, value, 0x00, &vmst->ioevt.data.ioreq.val.buf);
break; break;
case IOREQ_TYP_U32_RD: case IOREQ_TYP_U32_RD:
// get_safeptr(vmst, value, 4, &b); // pass link to r1 for user function to update
vmst->ioevt.data.ioreq.val.u32p = &vmst->core->regs[11]; // r1, //(uint32_t *)b.ptr; vmst->ioevt.data.ioreq.val.u32p = &vmst->core->regs[11];
break; break;
} }
vmst->ioevt.typ = UVM32_EVT_IOREQ; vmst->ioevt.typ = UVM32_EVT_IOREQ;
vmst->ioevt.data.ioreq.code = vmst->mappings[i].code; vmst->ioevt.data.ioreq.code = vmst->mappings[i].code;
vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ; vmst->ioevt.data.ioreq.typ = vmst->mappings[i].typ;
//#warning FIXME, retval
// vmst->core->regs[11] = 456; // r1
setStatus(vmst, UVM32_STATUS_PAUSED); setStatus(vmst, UVM32_STATUS_PAUSED);
syscall_valid = true; syscall_valid = true;
break; // stop searching break; // stop searching