This commit is contained in:
folkert van heusden 2024-03-28 15:41:44 +01:00
parent c62aadd318
commit a0630fd485
Signed by untrusted user who does not match committer: folkert
GPG key ID: 6B6455EDFEED3BD1
5 changed files with 190 additions and 84 deletions

View file

@ -6,6 +6,9 @@ cmake_minimum_required(VERSION 3.9)
add_compile_options(-Wall -pedantic -Wextra) add_compile_options(-Wall -pedantic -Wextra)
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
@ -69,9 +72,9 @@ endif (WIN32)
include(CheckIPOSupported) include(CheckIPOSupported)
check_ipo_supported(RESULT supported) check_ipo_supported(RESULT supported)
set(CMAKE_BUILD_TYPE RelWithDebInfo) #set(CMAKE_BUILD_TYPE RelWithDebInfo)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) #set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
#set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE)

23
cpu.cpp
View file

@ -1,4 +1,4 @@
// (C) 2018-2023 by Folkert van Heusden // (C) 2018-2024 by Folkert van Heusden
// Released under MIT license // Released under MIT license
#include <assert.h> #include <assert.h>
@ -179,6 +179,27 @@ uint16_t cpu::addRegister(const int nr, const rm_selection_t mode_selection, con
return pc += value; return pc += value;
} }
void cpu::lowlevel_register_set(const uint8_t set, const uint8_t reg, const uint16_t value)
{
if (reg < 6)
regs0_5[set][reg] = value;
else if (reg == 6)
sp[set == 0 ? 0 : 3] = value;
else
pc = value;
}
uint16_t cpu::lowlevel_register_get(const uint8_t set, const uint8_t reg)
{
if (reg < 6)
return regs0_5[set][reg];
if (reg == 6)
return sp[set == 0 ? 0 : 3];
return pc;
}
bool cpu::getBitPSW(const int bit) const bool cpu::getBitPSW(const int bit) const
{ {
return (psw >> bit) & 1; return (psw >> bit) & 1;

8
cpu.h
View file

@ -1,4 +1,4 @@
// (C) 2018-2023 by Folkert van Heusden // (C) 2018-2024 by Folkert van Heusden
// Released under MIT license // Released under MIT license
#pragma once #pragma once
@ -64,7 +64,6 @@ private:
void addToMMR1(const uint8_t mode, const uint8_t reg, const word_mode_t word_mode); void addToMMR1(const uint8_t mode, const uint8_t reg, const word_mode_t word_mode);
gam_rc_t getGAM(const uint8_t mode, const uint8_t reg, const word_mode_t word_mode, const rm_selection_t mode_selection, const bool read_value = true); gam_rc_t getGAM(const uint8_t mode, const uint8_t reg, const word_mode_t word_mode, const rm_selection_t mode_selection, const bool read_value = true);
gam_rc_t getGAMAddress(const uint8_t mode, const int reg, const word_mode_t word_mode); gam_rc_t getGAMAddress(const uint8_t mode, const int reg, const word_mode_t word_mode);
bool putGAM(const gam_rc_t & g, const uint16_t value); // returns false when flag registers should not be updated bool putGAM(const gam_rc_t & g, const uint16_t value); // returns false when flag registers should not be updated
@ -143,8 +142,11 @@ public:
uint16_t getPC() const { return pc; } uint16_t getPC() const { return pc; }
void setRegister(const int nr, const uint16_t value, const rm_selection_t mode_selection = rm_cur); void setRegister(const int nr, const uint16_t value, const rm_selection_t mode_selection = rm_cur);
void setRegisterLowByte(const int nr, const word_mode_t word_mode, const uint16_t value); void setRegisterLowByte(const int nr, const word_mode_t word_mode, const uint16_t value);
// used by 'main' for json-validation
void lowlevel_register_set(const uint8_t set, const uint8_t reg, const uint16_t value);
uint16_t lowlevel_register_get(const uint8_t set, const uint8_t reg);
void lowlevel_psw_set(const uint16_t value) { psw = value; }
void setStackPointer(const int which, const uint16_t value) { assert(which >= 0 && which < 4); sp[which] = value; } void setStackPointer(const int which, const uint16_t value) { assert(which >= 0 && which < 4); sp[which] = value; }
void setPC(const uint16_t value) { pc = value; } void setPC(const uint16_t value) { pc = value; }

View file

@ -2,6 +2,7 @@
import json import json
from machine import PDP1170 from machine import PDP1170
from pdptraps import PDPTrap, PDPTraps
import random import random
import sys import sys
@ -14,23 +15,32 @@ class PDP1170_wrapper(PDP1170):
def reset_mem_transactions_dict(self): def reset_mem_transactions_dict(self):
self.mem_transactions = dict() self.mem_transactions = dict()
self.before = dict()
def get_mem_before(self):
return self.before
def get_mem_transactions_dict(self): def get_mem_transactions_dict(self):
return self.mem_transactions return self.mem_transactions
def put(self, physaddr, value):
self.before[physaddr] = value
super().physRW(physaddr, value)
def physRW(self, physaddr, value=None): def physRW(self, physaddr, value=None):
if value == None: # read if value == None: # read
self.mem_transactions[physaddr] = random.randint(0, 65536) if not physaddr in self.mem_transactions and not physaddr in self.before:
return super().physRW(physaddr, self.mem_transactions[physaddr]) self.before[physaddr] = random.randint(0, 65536)
return super().physRW(physaddr, self.before[physaddr])
self.mem_transactions[physaddr] = value self.mem_transactions[physaddr] = value
super().physRW(physaddr, value) return super().physRW(physaddr, value)
def physRW_N(self, physaddr, nwords, words=None): def physRW_N(self, physaddr, nwords, words=None):
temp_addr = physaddr temp_addr = physaddr
if words == None: if words == None:
for i in range(nwords): for i in range(nwords):
physRW(temp_addr, random.randint(0, 65536)) self.physRW(temp_addr, random.randint(0, 65536))
temp_addr += 2 temp_addr += 2
return super().physRW_N(physaddr, nwords) return super().physRW_N(physaddr, nwords)
@ -38,32 +48,28 @@ class PDP1170_wrapper(PDP1170):
self.mem_transactions[temp_addr] = w self.mem_transactions[temp_addr] = w
temp_addr += 2 temp_addr += 2
super().physRW_N(physaddr, nwords, words=words) return super().physRW_N(physaddr, nwords, words=words)
class test_generator: class test_generator:
def _invoke_bp(self, a, i): def _invoke_bp(self, a, i):
return True return True
def _run_test(self, addr, mem_setup, reg_setup, psw): def put_registers(self, p, target, tag):
p = PDP1170_wrapper(loglevel='DEBUG') target[tag] = dict()
target[tag][0] = dict()
target[tag][1] = dict()
for set_ in range(0, 2):
for reg_ in range(0, 6):
target[tag][set_][reg_] = p.registerfiles[set_][reg_]
for a, v in mem_setup: target[tag]['sp'] = p.stackpointers
p.mmu.wordRW(a, v) target[tag]['pc'] = p.PC
for r, v in reg_setup:
p.r[r] = v
p.reset_mem_transactions_dict()
p._syncregs()
p.run_steps(pc=addr, steps=1)
p._syncregs()
return p
def create_test(self): def create_test(self):
out = { } out = { }
p = PDP1170_wrapper(loglevel='DEBUG')
addr = random.randint(0, 65536) & ~3 addr = random.randint(0, 65536) & ~3
# TODO what is the maximum size of an instruction? # TODO what is the maximum size of an instruction?
@ -72,41 +78,59 @@ class test_generator:
mem_kv.append((addr + 2, random.randint(0, 65536))) mem_kv.append((addr + 2, random.randint(0, 65536)))
mem_kv.append((addr + 4, random.randint(0, 65536))) mem_kv.append((addr + 4, random.randint(0, 65536)))
mem_kv.append((addr + 6, 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:
out['memory-before'][a] = v p.put(a, v)
reg_kv = []
for i in range(7):
reg_kv.append((i, random.randint(0, 65536)))
reg_kv.append((7, addr))
out['registers-before'] = dict()
for r, v in reg_kv:
out['registers-before'][r] = v
out['registers-before']['psw'] = 0 # TODO random.randint(0, 65536)
try: try:
p = self._run_test(addr, mem_kv, reg_kv, out['registers-before']['psw']) # generate & set PSW
while True:
try:
p.psw = random.randint(0, 65536)
break
except PDPTraps.ReservedInstruction as ri:
pass
out['registers-after'] = dict() # generate other registers
reg_kv = []
for i in range(7):
reg_kv.append((i, random.randint(0, 65536)))
reg_kv.append((7, addr))
# set registers
set_ = (p.psw >> 11) & 1
for r, v in reg_kv: for r, v in reg_kv:
out['registers-after'][r] = v p.registerfiles[set_][r] = v
p.registerfiles[1 - set_][r] = (~v) & 65535 # make sure it triggers errors
assert p.registerfiles[set_][r] == p.r[r]
p.r[6] = p.registerfiles[set_][6]
p._syncregs()
self.put_registers(p, out, 'registers-before')
out['registers-before']['psw'] = p.psw
p.run_steps(pc=addr, steps=1)
self.put_registers(p, out, 'registers-after')
out['registers-after']['psw'] = p.psw out['registers-after']['psw'] = p.psw
mb = p.get_mem_before()
for a in mb:
out['memory-before'][a] = mb[a]
out['memory-after'] = dict() out['memory-after'] = dict()
for a, v in mem_kv:
out['memory-after'][a] = v
mem_transactions = p.get_mem_transactions_dict() mem_transactions = p.get_mem_transactions_dict()
for a in mem_transactions: for a in mem_transactions:
out['memory-after'][a] = mem_transactions[a] out['memory-after'][a] = mem_transactions[a]
# TODO originele geheugeninhouden checken
# TODO check if mem_transactions affects I/O, then return None # TODO check if mem_transactions affects I/O, then return None
return out return out
except PDPTraps.ReservedInstruction as pri:
return None
except Exception as e: except Exception as e:
# handle PDP11 traps; store them # handle PDP11 traps; store them
print('test failed', e) print('test failed', e)
@ -122,5 +146,5 @@ for i in range(0, 4096):
if test != None: if test != None:
tests.append(test) tests.append(test)
fh.write(json.dumps(tests)) fh.write(json.dumps(tests, indent=4))
fh.close() fh.close()

132
main.cpp
View file

@ -1,6 +1,7 @@
// (C) 2018-2024 by Folkert van Heusden // (C) 2018-2024 by Folkert van Heusden
// Released under MIT license // Released under MIT license
#include <assert.h>
#include <atomic> #include <atomic>
#include <jansson.h> #include <jansson.h>
#include <signal.h> #include <signal.h>
@ -70,20 +71,40 @@ int run_cpu_validation(const std::string & filename)
// {"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}} // {"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"); // initialize
const char *key = nullptr; json_t *memory_before = json_object_get(test, "memory-before");
json_t *value = nullptr; const char *key = nullptr;
json_object_foreach(memory_before, key, value) { json_t *value = nullptr;
b->writePhysical(atoi(key), json_integer_value(value)); 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_t *const 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 const char *key = nullptr;
else json_t *value = nullptr;
c->setRegister(atoi(key), json_integer_value(value), rm_cur); json_t *set0 = json_object_get(registers_before, "0");
json_object_foreach(set0, key, value) {
c->lowlevel_register_set(0, atoi(key), json_integer_value(value));
}
json_t *set1 = json_object_get(registers_before, "1");
json_object_foreach(set1, key, value) {
c->lowlevel_register_set(1, atoi(key), json_integer_value(value));
}
}
{
json_t *psw_reg = json_object_get(registers_before, "psw");
assert(psw_reg);
c->lowlevel_psw_set(json_integer_value(psw_reg));
}
{
json_t *b_pc = json_object_get(registers_before, "pc");
assert(b_pc);
c->setPC(json_integer_value(b_pc));
// TODO PS
} }
c->step_a(); c->step_a();
@ -91,39 +112,74 @@ int run_cpu_validation(const std::string & filename)
c->step_b(); c->step_b();
// validate // validate
bool err = false; {
json_t *memory_after = json_object_get(test, "memory-after"); bool err = false;
json_object_foreach(memory_before, key, value) { {
uint16_t mem_contains = b->readPhysical(atoi(key)); json_t *memory_after = json_object_get(test, "memory-after");
uint16_t should_be = json_integer_value(value); const char *key = nullptr;
json_t *value = nullptr;
json_object_foreach(memory_after, key, value) {
int key_v = atoi(key);
uint16_t mem_contains = b->readPhysical(key_v);
uint16_t should_be = json_integer_value(value);
if (mem_contains != should_be) { if (mem_contains != should_be) {
DOLOG(warning, true, "memory address %s mismatch (is: %06o, should be: %06o)", key, mem_contains, should_be); DOLOG(warning, true, "memory address %06o (%d) mismatch (is: %06o (%d), should be: %06o (%d))", key_v, key_v, mem_contains, mem_contains, should_be, should_be);
err = true; err = true;
}
}
} }
}
json_t *registers_after = json_object_get(test, "registers-after"); uint16_t psw = c->getPSW();
json_object_foreach(registers_before, key, value) {
uint16_t register_is = 0;
if (strcmp(key, "psw") == 0) json_t *const registers_after = json_object_get(test, "registers-after");
register_is = c->getPSW();
{
int set_nr = (psw >> 11) & 1;
char set[] = { char('0' + set_nr), 0x00 };
json_t *a_set = json_object_get(registers_after, set);
const char *key = nullptr;
json_t *value = nullptr;
json_object_foreach(a_set, key, value) {
uint16_t register_is = c->lowlevel_register_get(set_nr, atoi(key));
uint16_t should_be = json_integer_value(value);
if (register_is != should_be) {
DOLOG(warning, true, "set %d register %s mismatch (is: %06o, should be: %06o)", set_nr, key, register_is, should_be);
err = true;
}
}
}
{
json_t *a_pc = json_object_get(registers_after, "pc");
assert(a_pc);
uint16_t should_be_pc = json_integer_value(a_pc);
if (c->getPC() != should_be_pc) {
DOLOG(warning, true, "PC register mismatch (is: %06o, should be: %06o)", c->getPC(), should_be_pc);
err = true;
}
}
// TODO check PS
{
json_t *a_psw = json_object_get(registers_after, "psw");
assert(a_psw);
uint16_t should_be_psw = json_integer_value(a_psw);
if (should_be_psw != psw) {
DOLOG(warning, true, "PSW register mismatch (is: %06o, should be: %06o)", psw, should_be_psw);
err = true;
}
}
if (err)
DOLOG(warning, true, "%s\n", json_dumps(test, 0)); // also emit empty line(!)
else else
register_is = c->getRegister(atoi(key), rm_cur); n_ok++;
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 // clean-up
delete b; delete b;
} }