Move all host examples under host/

This commit is contained in:
Toby Jaffey 2025-12-11 12:57:59 +00:00
parent 1213c5673f
commit 7353810199
16 changed files with 104 additions and 143 deletions

View file

@ -0,0 +1,23 @@
TOPDIR=../../
# Auto-generate the Arduino example
# Arduino cannot set -DUVM32_MEMORY_SIZE, so add a config file
# Arduino expects .cpp files
# make test, runs in qemu
all:
echo '#include "config.h"' > uvm32.cpp
cat ${TOPDIR}/uvm32/uvm32.c >> uvm32.cpp
cp ${TOPDIR}/uvm32/uvm32.h uvm32.h
cp ${TOPDIR}/uvm32/mini-rv32ima.h mini-rv32ima.h
cp ${TOPDIR}/common/uvm32_common_custom.h uvm32_common_custom.h
cp ${TOPDIR}/common/uvm32_sys.h uvm32_sys.h
xxd -n mandel -i ${TOPDIR}/precompiled/mandel.bin | sed -e "s/unsigned char/const unsigned char/" > mandel.h
arduino-cli compile -b arduino:avr:uno -e
test: all
qemu-system-avr -machine uno -bios build/arduino.avr.uno/host-arduino.ino.elf -nographic -serial mon:stdio
clean:
rm -rf uvm32.cpp uvm32.h mini-rv32ima.h uvm32_common_custom.h uvm32_sys.h mandel.h build

View file

@ -0,0 +1,2 @@
// Arduino cannot do -DUVM32_MEMORY_SIZE, so set this explicitly
#define UVM32_MEMORY_SIZE 512

View file

@ -0,0 +1,73 @@
#include "config.h"
#include "uvm32.h"
#include "uvm32_common_custom.h"
#include "mandel.h"
uvm32_state_t vmst;
uvm32_evt_t evt;
bool isrunning = false;
uint32_t led_time = 0;
bool led_state = false;
void setup(void) {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
isrunning = false;
}
void loop(void) {
// flash LED rapidly to show main loop is still running
if (millis() > led_time + 50) {
digitalWrite(LED_BUILTIN, led_state);
led_state = !led_state;
led_time = millis();
}
if (isrunning) {
uvm32_run(&vmst, &evt, 0xFFFFFFFF); // num instructions before vm considered hung
switch(evt.typ) {
case UVM32_EVT_END:
isrunning = false;
break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch(evt.data.syscall.code) {
case UVM32_SYSCALL_YIELD:
break;
case UVM32_SYSCALL_PUTC:
Serial.print((char)uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
Serial.print(str);
Serial.println("");
} break;
case UVM32_SYSCALL_PRINT: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
Serial.print(str);
} break;
case UVM32_SYSCALL_MILLIS: {
uvm32_setval(&vmst, &evt, RET, millis());
} break;
default:
Serial.print("Unhandled syscall: ");
Serial.print(evt.data.syscall.code);
Serial.println("");
break;
}
break;
case UVM32_EVT_ERR:
Serial.print("Error: ");
Serial.println(evt.data.err.errstr);
isrunning = false;
break;
}
} else {
Serial.println("Running VM");
// setup vm
uvm32_init(&vmst);
uvm32_load(&vmst, mandel, mandel_len);
isrunning = true;
}
}

8
hosts/host-mini/Makefile Normal file
View file

@ -0,0 +1,8 @@
TOPDIR=../../
all:
xxd -n mandel -i ${TOPDIR}/precompiled/mandel.bin | sed -e "s/unsigned char/const unsigned char/" > mandel.h
gcc -Wall -DUVM32_MEMORY_SIZE=512 -O2 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-mini ${TOPDIR}/uvm32/uvm32.c host-mini.c
clean:
rm -f host-mini

View file

@ -0,0 +1,48 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
#include "mandel.h"
int main(int argc, char *argv[]) {
uvm32_state_t vmst;
uvm32_evt_t evt;
bool isrunning = true;
uvm32_init(&vmst);
uvm32_load(&vmst, mandel, mandel_len);
while(isrunning) {
uvm32_run(&vmst, &evt, 100); // num instructions before vm considered hung
switch(evt.typ) {
case UVM32_EVT_END:
isrunning = false;
break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch(evt.data.syscall.code) {
case UVM32_SYSCALL_YIELD:
break;
case UVM32_SYSCALL_PUTC:
printf("%c", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
printf("%s\n", str);
} break;
default:
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
break;
}
break;
case UVM32_EVT_ERR:
printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode);
break;
default:
break;
}
}
return 0;
}

28
hosts/host-mini/mandel.h Normal file
View file

@ -0,0 +1,28 @@
const unsigned char mandel[] = {
0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x13, 0xb7, 0x08, 0x00, 0x01,
0x93, 0x88, 0x28, 0x00, 0x73, 0x00, 0x00, 0x00, 0x23, 0x26, 0x11, 0x00,
0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01, 0x73, 0x00, 0x00, 0x00,
0x13, 0x01, 0x01, 0xff, 0x23, 0x26, 0x81, 0x00, 0x37, 0xf5, 0xff, 0xff,
0xb7, 0x15, 0x00, 0x00, 0x37, 0xe6, 0xff, 0xff, 0x13, 0x07, 0xf0, 0x01,
0xb7, 0x47, 0x00, 0x00, 0xb7, 0x06, 0x00, 0x01, 0x13, 0x08, 0xd5, 0xcc,
0x93, 0x82, 0x35, 0x33, 0x13, 0x03, 0x76, 0xe6, 0x93, 0x83, 0x35, 0xb3,
0x13, 0x86, 0x16, 0x00, 0x63, 0xce, 0x02, 0x09, 0x13, 0x0e, 0x03, 0x00,
0x63, 0xce, 0x63, 0x06, 0x93, 0x0f, 0x00, 0x00, 0x13, 0x0f, 0x00, 0x00,
0x13, 0x04, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, 0x93, 0x06, 0x00, 0x02,
0x13, 0x85, 0x06, 0xfe, 0x63, 0x64, 0xa7, 0x04, 0x33, 0x05, 0xff, 0x01,
0x63, 0xe0, 0xa7, 0x04, 0x13, 0x05, 0x00, 0x00, 0x93, 0x05, 0x00, 0x00,
0x93, 0x08, 0x06, 0x00, 0xb3, 0x8e, 0x8e, 0x02, 0x33, 0x0f, 0xff, 0x41,
0x13, 0xd4, 0xbe, 0x40, 0xb3, 0x0e, 0xcf, 0x01, 0x73, 0x00, 0x00, 0x00,
0x33, 0x04, 0x04, 0x01, 0x33, 0x85, 0xde, 0x03, 0x13, 0x5f, 0xc5, 0x00,
0x33, 0x05, 0x84, 0x02, 0x93, 0x5f, 0xc5, 0x00, 0x93, 0x86, 0x16, 0x00,
0x6f, 0xf0, 0x9f, 0xfb, 0x13, 0x85, 0x06, 0x00, 0x93, 0x05, 0x00, 0x00,
0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x0e, 0x1e, 0x09,
0xe3, 0xd6, 0xc3, 0xf9, 0x13, 0x05, 0xa0, 0x00, 0x93, 0x05, 0x00, 0x00,
0x93, 0x08, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x13, 0x08, 0x98, 0x19,
0xe3, 0xd6, 0x02, 0xf7, 0x37, 0x05, 0x00, 0x80, 0x13, 0x05, 0x05, 0x12,
0x93, 0x08, 0x30, 0x00, 0x93, 0x05, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x03, 0x24, 0xc1, 0x00, 0x13, 0x01, 0x01, 0x01, 0x67, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00
};
unsigned int mandel_len = 300;

View file

@ -0,0 +1,7 @@
TOPDIR = ../../
all:
xxd -n fib -i ${TOPDIR}/precompiled/fib.bin | sed -e "s/unsigned char/const unsigned char/" > fib.h
gcc -Wall -DUVM32_MEMORY_SIZE=16386 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host-parallel ${TOPDIR}/uvm32/uvm32.c host-parallel.c
clean:
rm -f host-parallel

View file

@ -0,0 +1,70 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
// Run multiple scheduled VMs in parallel until all have ended
#define NUM_VM 4 // number of vms
#define SCHEDULE SCHEDULE_RANDOM // scheduling algorithm
// scheduling algorithms
#define SCHEDULE_ROUNDROBIN() scheduler_index = (scheduler_index + 1) % NUM_VM
#define SCHEDULE_RANDOM() scheduler_index = rand()%NUM_VM
#include "fib.h"
int main(int argc, char *argv[]) {
uvm32_state_t vmst[NUM_VM];
uvm32_evt_t evt;
int numVmRunning = NUM_VM;
int scheduler_index = 0;
for (int i=0;i<NUM_VM;i++) {
uvm32_init(&vmst[i]);
uvm32_load(&vmst[i], fib, fib_len);
}
while(numVmRunning > 0) {
if (uvm32_hasEnded(&vmst[scheduler_index])) {
// this vm has already completed, pick another
SCHEDULE();
continue;
}
uvm32_run(&vmst[scheduler_index], &evt, 100); // num instructions before vm considered hung
switch(evt.typ) {
case UVM32_EVT_END:
printf("[VM %d ended]\n", scheduler_index);
numVmRunning--;
break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch(evt.data.syscall.code) {
case UVM32_SYSCALL_YIELD:
break;
case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst[scheduler_index], &evt, ARG0);
if (str[0] != 0) {
printf("[VM %d] %s\n", scheduler_index, str);
}
} break;
case UVM32_SYSCALL_PRINTDEC:
printf("[VM %d] %d\n", scheduler_index, uvm32_getval(&vmst[scheduler_index], &evt, ARG0));
break;
default:
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
break;
}
break;
case UVM32_EVT_ERR:
printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode);
break;
default:
break;
}
SCHEDULE();
}
return 0;
}

7
hosts/host/Makefile Normal file
View file

@ -0,0 +1,7 @@
TOPDIR=../../
all:
gcc -Wall -Werror -pedantic -std=c99 -O2 -DUVM32_MEMORY_SIZE=65536 -I${TOPDIR}/uvm32 -I${TOPDIR}/common -o host ${TOPDIR}/uvm32/uvm32.c host.c
clean:
rm -f host

261
hosts/host/host.c Normal file
View file

@ -0,0 +1,261 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
// stash terminal settings on startup
static struct termios orig_termios;
void disableRawMode(void) {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
printf("\033[?25h"); // show cursor
fflush(stdout);
}
void cleanexit(int sig) {
disableRawMode();
exit(0);
}
void enableRawMode(void) {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disableRawMode);
signal(SIGINT, cleanexit);
struct termios raw = orig_termios;
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN);
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_cflag |= (CS8);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
}
static uint8_t *read_file(const char* filename, int *len) {
FILE* f = fopen(filename, "rb");
uint8_t *buf = NULL;
if (f == NULL) {
fprintf(stderr, "error: can't open file '%s'.\n", filename);
return NULL;
}
fseek(f, 0, SEEK_END);
size_t file_size = ftell(f);
rewind(f);
if (NULL == (buf = malloc(file_size))) {
fclose(f);
return NULL;
}
size_t result = fread(buf, sizeof(uint8_t), file_size, f);
if (result != file_size) {
fprintf(stderr, "error: while reading file '%s'\n", filename);
free(buf);
fclose(f);
return NULL;
}
fclose(f);
*len = file_size;
return buf;
}
// Poll for input, inelegant, but works and doesn't confuse the main
// flow of the program with select/poll
bool poll_getch(uint8_t* c) {
struct timeval tv;
fd_set readfds;
int ret;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
if (ret == -1) {
printf("console read failed\r\n");
exit(1);
} else if (!ret) {
// timeout
return false;
}
if (FD_ISSET(STDIN_FILENO, &readfds)) {
int len;
len = read(STDIN_FILENO, c, 1);
if (len == -1) {
printf("console read failed\r\n");
exit(1);
}
if (len) {
if (*c == 0x03 || *c == 0x04) { // ctrl-c ctrl-d
disableRawMode();
exit(0);
}
if (*c == 0x0d) {
*c = '\n';
}
return true;
}
}
return false;
}
void hexdump(const uint8_t *p, int len) {
while(len--) {
printf("%02x", *p++);
}
}
bool memdump(const char *filename, const uint8_t *buf, int len) {
FILE* f = fopen(filename, "wb");
if (f == NULL) {
fprintf(stderr, "error: can't open file '%s'.\n", filename);
return false;
}
size_t result = fwrite(buf, sizeof(uint8_t), len, f);
if (result != len) {
fprintf(stderr, "error: while writing file '%s'\n", filename);
return false;
}
fclose(f);
return true;
}
int main(int argc, char *argv[]) {
uvm32_state_t vmst;
uint32_t max_instrs_per_run = 500000;
clock_t start_time = clock() / (CLOCKS_PER_SEC / 1000);
argc--;
argv++;
if (argc < 1) {
printf("<romfile> [max_instrs_per_run]\n");
return 1;
}
if (argc > 1) {
max_instrs_per_run = strtoll(argv[1], NULL, 10);
}
int romlen = 0;
uint8_t *rom = read_file(argv[0], &romlen);
if (NULL == rom) {
printf("file read failed!\n");
return 1;
}
uvm32_init(&vmst);
if (!uvm32_load(&vmst, rom, romlen)) {
printf("load failed!\n");
return 1;
}
uvm32_evt_t evt;
bool isrunning = true;
uint32_t total_instrs = 0;
uint32_t num_syscalls = 0;
// setup terminal for getch()
enableRawMode();
while(isrunning) {
total_instrs += uvm32_run(&vmst, &evt, max_instrs_per_run); // num instructions before vm considered hung
num_syscalls++;
switch(evt.typ) {
case UVM32_EVT_END:
printf("UVM32_EVT_END\n");
isrunning = false;
break;
case UVM32_EVT_ERR:
printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode);
if (evt.data.err.errcode == UVM32_ERR_HUNG) {
printf("VM may have hung, increase max_instrs_per_run\n");
uvm32_clearError(&vmst); // allow to continue
} else {
isrunning = false;
memdump("host-ram.dump", vmst.memory, UVM32_MEMORY_SIZE);
printf("memory dumped to host-ram.dump, pc=0x%08x\n", vmst.core.pc);
}
break;
case UVM32_EVT_SYSCALL:
switch(evt.data.syscall.code) {
case UVM32_SYSCALL_PRINTBUF: {
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1);
while(buf.len--) {
printf("%02x", *buf.ptr++);
}
} break;
case UVM32_SYSCALL_YIELD: {
// uint32_t yield_typ = uvm32_getval(&vmst, &evt, ARG0);
// printf("YIELD type=%d\n", yield_typ);
// uvm32_setval(&vmst, &evt, RET, 123);
} break;
case UVM32_SYSCALL_PRINT: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
printf("%s", str);
} break;
case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
printf("%s\n", str);
} break;
case UVM32_SYSCALL_PRINTDEC:
printf("%d", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PUTC:
printf("%c", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PRINTHEX:
printf("%08x", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_MILLIS: {
clock_t now = clock() / (CLOCKS_PER_SEC / 1000);
uvm32_setval(&vmst, &evt, RET, now - start_time);
} break;
case UVM32_SYSCALL_GETC: {
uint8_t c;
if (poll_getch(&c)) {
uvm32_setval(&vmst, &evt, RET, c);
} else {
uvm32_setval(&vmst, &evt, RET, 0xFFFFFFFF);
}
} break;
default:
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
break;
}
break;
default:
printf("Bad evt %d\n", evt.typ);
return 1;
break;
}
fflush(stdout);
}
printf("Executed total of %d instructions and %d syscalls\n", (int)total_instrs, (int)num_syscalls);
free(rom);
// put terminal back to how it was
disableRawMode();
return 0;
}