basic framework for json validation
This commit is contained in:
parent
81f7d8f1ad
commit
c62aadd318
7 changed files with 143 additions and 26 deletions
|
@ -95,3 +95,8 @@ pkg_check_modules(PANEL REQUIRED panel)
|
||||||
target_link_libraries(kek ${PANEL_LIBRARIES})
|
target_link_libraries(kek ${PANEL_LIBRARIES})
|
||||||
target_include_directories(kek PUBLIC ${PANEL_INCLUDE_DIRS})
|
target_include_directories(kek PUBLIC ${PANEL_INCLUDE_DIRS})
|
||||||
target_compile_options(kek PUBLIC ${PANEL_CFLAGS_OTHER})
|
target_compile_options(kek PUBLIC ${PANEL_CFLAGS_OTHER})
|
||||||
|
|
||||||
|
pkg_check_modules(JANSSON REQUIRED jansson)
|
||||||
|
target_link_libraries(kek ${JANSSON_LIBRARIES})
|
||||||
|
target_include_directories(kek PUBLIC ${JANSSON_INCLUDE_DIRS})
|
||||||
|
target_compile_options(kek PUBLIC ${JANSSON_CFLAGS_OTHER})
|
||||||
|
|
12
cpu.cpp
12
cpu.cpp
|
@ -1637,7 +1637,6 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt)
|
||||||
{
|
{
|
||||||
DOLOG(debug, true, "*** CPU::TRAP %o, new-ipl: %d, is-interrupt: %d ***", vector, new_ipl, is_interrupt);
|
DOLOG(debug, true, "*** CPU::TRAP %o, new-ipl: %d, is-interrupt: %d ***", vector, new_ipl, is_interrupt);
|
||||||
|
|
||||||
int processing_trap_depth = 0;
|
|
||||||
uint16_t before_psw = 0;
|
uint16_t before_psw = 0;
|
||||||
uint16_t before_pc = 0;
|
uint16_t before_pc = 0;
|
||||||
|
|
||||||
|
@ -1650,14 +1649,15 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt)
|
||||||
if (processing_trap_depth >= 2) {
|
if (processing_trap_depth >= 2) {
|
||||||
DOLOG(debug, true, "Trap depth %d", processing_trap_depth);
|
DOLOG(debug, true, "Trap depth %d", processing_trap_depth);
|
||||||
|
|
||||||
|
if (processing_trap_depth >= 3) {
|
||||||
|
*event = EVENT_HALT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (kernel_mode)
|
if (kernel_mode)
|
||||||
vector = 4;
|
vector = 4;
|
||||||
|
|
||||||
setRegister(6, 04);
|
setRegister(6, 04);
|
||||||
|
|
||||||
if (processing_trap_depth >= 3) {
|
|
||||||
// TODO: halt?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
before_psw = getPSW();
|
before_psw = getPSW();
|
||||||
|
@ -1689,6 +1689,8 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt)
|
||||||
pushStack(before_psw);
|
pushStack(before_psw);
|
||||||
pushStack(before_pc);
|
pushStack(before_pc);
|
||||||
|
|
||||||
|
processing_trap_depth = 0;
|
||||||
|
|
||||||
// if we reach this point then the trap was processed without causing
|
// if we reach this point then the trap was processed without causing
|
||||||
// another trap
|
// another trap
|
||||||
break;
|
break;
|
||||||
|
|
1
cpu.h
1
cpu.h
|
@ -35,6 +35,7 @@ private:
|
||||||
uint16_t psw { 0 };
|
uint16_t psw { 0 };
|
||||||
uint16_t fpsr { 0 };
|
uint16_t fpsr { 0 };
|
||||||
uint16_t stackLimitRegister { 0377 };
|
uint16_t stackLimitRegister { 0377 };
|
||||||
|
int processing_trap_depth { 0 };
|
||||||
uint64_t instruction_count { 0 };
|
uint64_t instruction_count { 0 };
|
||||||
uint64_t running_since { 0 };
|
uint64_t running_since { 0 };
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
|
||||||
|
int disassemble(cpu *const c, console *const cnsl, const uint16_t pc, const bool instruction_only);
|
||||||
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const bool tracing);
|
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const bool tracing);
|
||||||
void run_bic(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const bool tracing, const uint16_t bic_start);
|
void run_bic(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const bool tracing, const uint16_t bic_start);
|
||||||
|
|
|
@ -13,25 +13,29 @@ class PDP1170_wrapper(PDP1170):
|
||||||
self.reset_mem_transactions_dict()
|
self.reset_mem_transactions_dict()
|
||||||
|
|
||||||
def reset_mem_transactions_dict(self):
|
def reset_mem_transactions_dict(self):
|
||||||
self.mem_transactions_w = dict()
|
self.mem_transactions = dict()
|
||||||
|
|
||||||
def get_mem_transactions_dict(self):
|
def get_mem_transactions_dict(self):
|
||||||
return self.mem_transactions_w
|
return self.mem_transactions
|
||||||
|
|
||||||
def physRW(self, physaddr, value=None):
|
def physRW(self, physaddr, value=None):
|
||||||
if value == None: # read
|
if value == None: # read
|
||||||
return super().physRW(physaddr, value)
|
self.mem_transactions[physaddr] = random.randint(0, 65536)
|
||||||
|
return super().physRW(physaddr, self.mem_transactions[physaddr])
|
||||||
|
|
||||||
self.mem_transactions_w[physaddr] = value
|
self.mem_transactions[physaddr] = value
|
||||||
super().physRW(physaddr, value)
|
super().physRW(physaddr, value)
|
||||||
|
|
||||||
def physRW_N(self, physaddr, nwords, words=None):
|
def physRW_N(self, physaddr, nwords, words=None):
|
||||||
|
temp_addr = physaddr
|
||||||
if words == None:
|
if words == None:
|
||||||
|
for i in range(nwords):
|
||||||
|
physRW(temp_addr, random.randint(0, 65536))
|
||||||
|
temp_addr += 2
|
||||||
return super().physRW_N(physaddr, nwords)
|
return super().physRW_N(physaddr, nwords)
|
||||||
|
|
||||||
temp_addr = physaddr
|
|
||||||
for w in words:
|
for w in words:
|
||||||
self.mem_transactions_w[temp_addr] = w
|
self.mem_transactions[temp_addr] = w
|
||||||
temp_addr += 2
|
temp_addr += 2
|
||||||
|
|
||||||
super().physRW_N(physaddr, nwords, words=words)
|
super().physRW_N(physaddr, nwords, words=words)
|
||||||
|
@ -40,8 +44,8 @@ class test_generator:
|
||||||
def _invoke_bp(self, a, i):
|
def _invoke_bp(self, a, i):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _run_test(self, mem_setup, reg_setup):
|
def _run_test(self, addr, mem_setup, reg_setup, psw):
|
||||||
p = PDP1170_wrapper()
|
p = PDP1170_wrapper(loglevel='DEBUG')
|
||||||
|
|
||||||
for a, v in mem_setup:
|
for a, v in mem_setup:
|
||||||
p.mmu.wordRW(a, v)
|
p.mmu.wordRW(a, v)
|
||||||
|
@ -51,19 +55,23 @@ class test_generator:
|
||||||
|
|
||||||
p.reset_mem_transactions_dict()
|
p.reset_mem_transactions_dict()
|
||||||
|
|
||||||
p.run(breakpoint=self._invoke_bp)
|
p._syncregs()
|
||||||
|
p.run_steps(pc=addr, steps=1)
|
||||||
|
p._syncregs()
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def create_test(self):
|
def create_test(self):
|
||||||
out = { }
|
out = { }
|
||||||
|
|
||||||
|
addr = random.randint(0, 65536) & ~3
|
||||||
|
|
||||||
# TODO what is the maximum size of an instruction?
|
# TODO what is the maximum size of an instruction?
|
||||||
mem_kv = []
|
mem_kv = []
|
||||||
mem_kv.append((0o1000, random.randint(0, 65536)))
|
mem_kv.append((addr + 0, random.randint(0, 65536)))
|
||||||
mem_kv.append((0o1002, random.randint(0, 65536)))
|
mem_kv.append((addr + 2, random.randint(0, 65536)))
|
||||||
mem_kv.append((0o1004, random.randint(0, 65536)))
|
mem_kv.append((addr + 4, random.randint(0, 65536)))
|
||||||
mem_kv.append((0o1006, random.randint(0, 65536)))
|
mem_kv.append((addr + 6, random.randint(0, 65536)))
|
||||||
|
|
||||||
out['memory-before'] = dict()
|
out['memory-before'] = dict()
|
||||||
for a, v in mem_kv:
|
for a, v in mem_kv:
|
||||||
|
@ -72,18 +80,21 @@ class test_generator:
|
||||||
reg_kv = []
|
reg_kv = []
|
||||||
for i in range(7):
|
for i in range(7):
|
||||||
reg_kv.append((i, random.randint(0, 65536)))
|
reg_kv.append((i, random.randint(0, 65536)))
|
||||||
reg_kv.append((7, 0o1000))
|
reg_kv.append((7, addr))
|
||||||
|
|
||||||
out['registers-before'] = dict()
|
out['registers-before'] = dict()
|
||||||
for r, v in reg_kv:
|
for r, v in reg_kv:
|
||||||
out['registers-before'][r] = v
|
out['registers-before'][r] = v
|
||||||
|
|
||||||
|
out['registers-before']['psw'] = 0 # TODO random.randint(0, 65536)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
p = self._run_test(mem_kv, reg_kv)
|
p = self._run_test(addr, mem_kv, reg_kv, out['registers-before']['psw'])
|
||||||
|
|
||||||
out['registers-after'] = dict()
|
out['registers-after'] = dict()
|
||||||
for r, v in reg_kv:
|
for r, v in reg_kv:
|
||||||
out['registers-after'][r] = v
|
out['registers-after'][r] = v
|
||||||
|
out['registers-after']['psw'] = p.psw
|
||||||
|
|
||||||
out['memory-after'] = dict()
|
out['memory-after'] = dict()
|
||||||
for a, v in mem_kv:
|
for a, v in mem_kv:
|
||||||
|
@ -96,8 +107,9 @@ class test_generator:
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
except:
|
except Exception as e:
|
||||||
print('test failed')
|
# handle PDP11 traps; store them
|
||||||
|
print('test failed', e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
fh = open(sys.argv[1], 'w')
|
fh = open(sys.argv[1], 'w')
|
||||||
|
@ -105,7 +117,7 @@ fh = open(sys.argv[1], 'w')
|
||||||
t = test_generator()
|
t = test_generator()
|
||||||
|
|
||||||
tests = []
|
tests = []
|
||||||
for i in range(0, 1024):
|
for i in range(0, 4096):
|
||||||
test = t.create_test()
|
test = t.create_test()
|
||||||
if test != None:
|
if test != None:
|
||||||
tests.append(test)
|
tests.append(test)
|
||||||
|
|
2
log.cpp
2
log.cpp
|
@ -15,7 +15,7 @@
|
||||||
#include "win32.h"
|
#include "win32.h"
|
||||||
|
|
||||||
|
|
||||||
static const char *logfile = strdup("/tmp/myip.log");
|
static const char *logfile = strdup("/tmp/kek.log");
|
||||||
log_level_t log_level_file = warning;
|
log_level_t log_level_file = warning;
|
||||||
log_level_t log_level_screen = warning;
|
log_level_t log_level_screen = warning;
|
||||||
FILE *lfh = nullptr;
|
FILE *lfh = nullptr;
|
||||||
|
|
100
main.cpp
100
main.cpp
|
@ -1,7 +1,8 @@
|
||||||
// (C) 2018-2023 by Folkert van Heusden
|
// (C) 2018-2024 by Folkert van Heusden
|
||||||
// Released under MIT license
|
// Released under MIT license
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <jansson.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -49,6 +50,91 @@ void sw_handler(int s)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int run_cpu_validation(const std::string & filename)
|
||||||
|
{
|
||||||
|
json_error_t error;
|
||||||
|
json_t *json = json_load_file(filename.c_str(), 0, &error);
|
||||||
|
if (!json)
|
||||||
|
error_exit(false, "%s", error.text);
|
||||||
|
|
||||||
|
size_t n_ok = 0;
|
||||||
|
size_t array_size = json_array_size(json);
|
||||||
|
for(size_t i=0; i<array_size; i++) {
|
||||||
|
json_t *test = json_array_get(json, i);
|
||||||
|
|
||||||
|
// create environment
|
||||||
|
event = 0;
|
||||||
|
bus *b = new bus();
|
||||||
|
cpu *c = new cpu(b, &event);
|
||||||
|
b->add_cpu(c);
|
||||||
|
|
||||||
|
// {"memory-before": {"512": 51435, "514": 45610, "516": 15091, "518": 43544}, "registers-before": {"0": 64423, "1": 1, "2": 41733, "3": 14269, "4": 48972, "5": 42770, "6": 57736, "7": 512}, "registers-after": {"0": 64423, "1": 1, "2": 41733, "3": 14269, "4": 48972, "5": 42770, "6": 57736, "7": 512}, "memory-after": {"512": 51435, "514": 45610, "516": 15091, "518": 43544}}
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
json_t *memory_before = json_object_get(test, "memory-before");
|
||||||
|
const char *key = nullptr;
|
||||||
|
json_t *value = nullptr;
|
||||||
|
json_object_foreach(memory_before, key, value) {
|
||||||
|
b->writePhysical(atoi(key), json_integer_value(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *registers_before = json_object_get(test, "registers-before");
|
||||||
|
json_object_foreach(registers_before, key, value) {
|
||||||
|
if (strcmp(key, "psw") == 0)
|
||||||
|
c->setPSW(uint16_t(json_integer_value(value)), false); // TODO: without the AND
|
||||||
|
else
|
||||||
|
c->setRegister(atoi(key), json_integer_value(value), rm_cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
c->step_a();
|
||||||
|
disassemble(c, nullptr, c->getPC(), false);
|
||||||
|
c->step_b();
|
||||||
|
|
||||||
|
// validate
|
||||||
|
bool err = false;
|
||||||
|
json_t *memory_after = json_object_get(test, "memory-after");
|
||||||
|
json_object_foreach(memory_before, key, value) {
|
||||||
|
uint16_t mem_contains = b->readPhysical(atoi(key));
|
||||||
|
uint16_t should_be = json_integer_value(value);
|
||||||
|
|
||||||
|
if (mem_contains != should_be) {
|
||||||
|
DOLOG(warning, true, "memory address %s mismatch (is: %06o, should be: %06o)", key, mem_contains, should_be);
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *registers_after = json_object_get(test, "registers-after");
|
||||||
|
json_object_foreach(registers_before, key, value) {
|
||||||
|
uint16_t register_is = 0;
|
||||||
|
|
||||||
|
if (strcmp(key, "psw") == 0)
|
||||||
|
register_is = c->getPSW();
|
||||||
|
else
|
||||||
|
register_is = c->getRegister(atoi(key), rm_cur);
|
||||||
|
uint16_t should_be = json_integer_value(value);
|
||||||
|
|
||||||
|
if (register_is != should_be) {
|
||||||
|
DOLOG(warning, true, "register %s mismatch (is: %06o, should be: %06o)", key, register_is, should_be);
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
DOLOG(warning, true, "%s", json_dumps(test, 0));
|
||||||
|
else
|
||||||
|
n_ok++;
|
||||||
|
|
||||||
|
// clean-up
|
||||||
|
delete b;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_decref(json);
|
||||||
|
|
||||||
|
printf("# ok: %zu out of %zu\n", n_ok, array_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void help()
|
void help()
|
||||||
{
|
{
|
||||||
printf("-h this help\n");
|
printf("-h this help\n");
|
||||||
|
@ -65,6 +151,7 @@ void help()
|
||||||
printf("-t enable tracing (disassemble to stderr, requires -d as well)\n");
|
printf("-t enable tracing (disassemble to stderr, requires -d as well)\n");
|
||||||
printf("-l x log to file x\n");
|
printf("-l x log to file x\n");
|
||||||
printf("-L x,y set log level for screen (x) and file (y)\n");
|
printf("-L x,y set log level for screen (x) and file (y)\n");
|
||||||
|
printf("-J x run validation suite x against the CPU emulation\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
@ -95,14 +182,20 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
disk_backend *temp_d = nullptr;
|
disk_backend *temp_d = nullptr;
|
||||||
|
|
||||||
|
std::string validate_json;
|
||||||
|
|
||||||
int opt = -1;
|
int opt = -1;
|
||||||
while((opt = getopt(argc, argv, "hm:T:Br:R:p:ndtL:b:l:s:Q:N:")) != -1)
|
while((opt = getopt(argc, argv, "hm:T:Br:R:p:ndtL:b:l:s:Q:N:J:")) != -1)
|
||||||
{
|
{
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
help();
|
help();
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
case 'J':
|
||||||
|
validate_json = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'Q':
|
case 'Q':
|
||||||
test = optarg;
|
test = optarg;
|
||||||
break;
|
break;
|
||||||
|
@ -210,6 +303,9 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
setlog(logfile, ll_file, ll_screen);
|
setlog(logfile, ll_file, ll_screen);
|
||||||
|
|
||||||
|
if (validate_json.empty() == false)
|
||||||
|
return run_cpu_validation(validate_json);
|
||||||
|
|
||||||
bus *b = new bus();
|
bus *b = new bus();
|
||||||
|
|
||||||
b->set_console_switches(console_switches);
|
b->set_console_switches(console_switches);
|
||||||
|
|
Loading…
Add table
Reference in a new issue