Test metering, prove that system can resume after a hang error with a range of meter sizes

This commit is contained in:
Toby Jaffey 2025-12-10 22:30:19 +00:00
parent 9d9e7542fc
commit ac1975a820
14 changed files with 222 additions and 53 deletions

View file

@ -1,4 +1,10 @@
TESTS = example_1 stackoverflow custom_syscall
TESTS = \
basic_syscalls \
stackoverflow \
custom_syscall \
syscall_args \
meter
RUNCMD = $(foreach TEST,${TESTS},make -C ${TEST} &&)
CLEANCMD = $(foreach TEST,${TESTS},make -C ${TEST} clean &&)

View file

@ -0,0 +1,10 @@
#include "uvm32_target.h"
void main(void) {
println("Hello world");
print("Hello world");
printdec(42);
printhex(0xDEADBEEF);
putc('G');
}

View file

@ -0,0 +1,56 @@
#include <string.h>
#include "unity.h"
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
#include "rom-header.h"
static uvm32_state_t vmst;
static uvm32_evt_t evt;
void setUp(void) {
// runs before each test
uvm32_init(&vmst);
uvm32_load(&vmst, rom_bin, rom_bin_len);
}
void tearDown(void) {
}
void test_syscalls(void) {
// check for println syscall
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN);
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world"));
// check for print syscall
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINT);
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), "Hello world"));
// check for printdec syscall
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTDEC);
TEST_ASSERT_EQUAL(42, uvm32_getval(&vmst, &evt, ARG0));
// check for printhex syscall
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTHEX);
TEST_ASSERT_EQUAL(0xDEADBEEF, uvm32_getval(&vmst, &evt, ARG0));
// check for putc syscall
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PUTC);
TEST_ASSERT_EQUAL('G', uvm32_getval(&vmst, &evt, ARG0));
// run vm to completion
uvm32_run(&vmst, &evt, 10000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
}

View file

@ -1,6 +0,0 @@
#include "uvm32_target.h"
void main(void) {
println("Hello world");
}

View file

@ -1,33 +0,0 @@
#include <string.h>
#include "unity.h"
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
#include "rom-header.h"
static uvm32_state_t vmst;
static uvm32_evt_t evt;
void setUp(void) {
// runs before each test
uvm32_init(&vmst);
uvm32_load(&vmst, rom_bin, rom_bin_len);
}
void tearDown(void) {
}
void test_helloworld1(void) {
// run the vm
uvm32_run(&vmst, &evt, 10000);
// check for println syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, UVM32_SYSCALL_PRINTLN);
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
TEST_ASSERT_EQUAL(0, strcmp(str, "Hello world"));
// run vm to completion
uvm32_run(&vmst, &evt, 10000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
}

33
test/meter/Makefile Normal file
View file

@ -0,0 +1,33 @@
C_COMPILER=gcc
UNITY_ROOT=../unity
CFLAGS=-std=c99
CFLAGS += -Wall
CFLAGS += -Werror
CFLAGS += -DUVM32_MEMORY_SIZE=16384
SUITE_NAME=tests
TARGET_BASE1=test1
TARGET1 = $(TARGET_BASE1)
SRC_FILES1=$(UNITY_ROOT)/src/unity.c test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c ../../uvm32/uvm32.c
INC_DIRS=-I$(UNITY_ROOT)/src -I../../uvm32/ -I../../common -Irom
.PHONY: rom
default: $(SRC_FILES1) rom
@$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1)
@ ./$(TARGET1)
rom:
@(cd rom && make)
test/test_runners/${SUITE_NAME}_Runner.c: test/${SUITE_NAME}.c
@mkdir -p test/test_runners
@ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c
clean:
rm -rf $(TARGET1) test/test_runners
(cd rom && make clean)

14
test/meter/rom/Makefile Normal file
View file

@ -0,0 +1,14 @@
TOPDIR=../../../
PROJECT:=$(shell basename ${PWD})
SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S
OPT=-O0 # don't let optimiser remove stall loop
all: all_common
@# Convert ROM to C file and header
@xxd -i ${PROJECT}.bin > ${PROJECT}-header.c
@echo "extern unsigned char ${PROJECT}_bin[]; extern int ${PROJECT}_bin_len;" > ${PROJECT}-header.h
test: test_common
clean: clean_common
rm -f ${PROJECT}-header.h ${PROJECT}-header.c
include ${TOPDIR}/apps/makefile.common

15
test/meter/rom/rom.c Normal file
View file

@ -0,0 +1,15 @@
#include "uvm32_target.h"
void stall(void) {
volatile uint32_t i;
for (i=0;i<100;i++) {
}
}
void main(void) {
for (int i=0;i<100;i++) {
stall();
printdec(i);
}
}

58
test/meter/test/tests.c Normal file
View file

@ -0,0 +1,58 @@
#include <string.h>
#include "unity.h"
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
#include "rom-header.h"
static uvm32_state_t vmst;
static uvm32_evt_t evt;
void setUp(void) {
// runs before each test
uvm32_init(&vmst);
uvm32_load(&vmst, rom_bin, rom_bin_len);
}
void tearDown(void) {
}
// run program until printdec() reaches 100
// every time it hangs, resume
// run in batches of num_instr
void metered_run(uint32_t num_instr) {
uint32_t expected = 0;
while(expected < 100) {
uvm32_run(&vmst, &evt, num_instr);
switch(evt.typ) {
case UVM32_EVT_SYSCALL: {
TEST_ASSERT_EQUAL(UVM32_SYSCALL_PRINTDEC, evt.data.syscall.code);
uint32_t val = uvm32_getval(&vmst, &evt, ARG0);
TEST_ASSERT_EQUAL(val, expected);
expected++;
} break;
case UVM32_EVT_ERR:
TEST_ASSERT_EQUAL(evt.data.err.errcode, UVM32_ERR_HUNG);
uvm32_clearError(&vmst); // clear the hung error so it can continue
break;
case UVM32_EVT_END:
TEST_ASSERT_EQUAL(0, 1); // trigger an assert, we didn't get to 100000 yet
break;
}
}
// run vm to completion
uvm32_run(&vmst, &evt, 10000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
}
void test_meter(void) {
// test many different meter values, including 0 (which should run 1 instruction, as 0 is going to hang)
for (uint32_t i=0;i<1000;i++) {
uvm32_init(&vmst);
uvm32_load(&vmst, rom_bin, rom_bin_len);
metered_run(i);
}
}