From 52a70e7931426ca35dff688745870508950cc691 Mon Sep 17 00:00:00 2001 From: Sam Stevens Date: Wed, 24 Oct 2018 21:59:51 +0100 Subject: [PATCH] Implemented vm memory using external 23LCV512 chip. Todo: implement extended registers --- arduino/arduino.ino | 44 ++++++++++++++- arduino/console.cpp | 10 ++-- arduino/memory.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++++ arduino/memory.h | 57 ++++++++++++++++++++ arduino/vm.c | 7 ++- arduino/vm.h | 5 +- 6 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 arduino/memory.cpp create mode 100644 arduino/memory.h diff --git a/arduino/arduino.ino b/arduino/arduino.ino index ae72dc9..50f8236 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -21,16 +21,48 @@ limitations under the License. #include "vm.h" #include "console.h" +#include "memory.h" VM vm; Console console(&vm); +//Expecting a Microchip 23LCV512 connected over MOSI/MISO/MCLK +Memory mem(20000000, MSBFIRST, SPI_MODE0, 4); + void setup() { + //Setup VM vm_reset(&vm); vm.error = vm_print_error; vm.syscall = vm_syscall; - Serial.begin(1200); + vm.readAddr = vm_read_addr; + vm.writeAddr = vm_write_addr; + + //Start serial + Serial.begin(9600); console.setSerial(&Serial); + + //Init memory + SPI.begin(); + mem.setSize(VM_MEM_SIZE); + if (!mem.init()) { + Serial.println("Memory failed to init!"); + } else { + mem.writeRange(0x55, 0, VM_MEM_SIZE); +#ifdef TEST_DESTRUCTIVE + Serial.print("Testing "); + Serial.print(mem.getSize(), DEC); + Serial.print(" bytes memory... "); + unsigned long startTime = millis(); + if (mem.test()) { + Serial.println("OK"); + } else { + Serial.println("ERROR!"); + } + Serial.print("Tested in "); + Serial.print(millis() - startTime, DEC); + Serial.println("ms"); +#endif + } } void loop() { @@ -45,7 +77,8 @@ void vm_print_error(uint8_t err) { Serial.println("Halted. PC misaligned."); break; case VM_ERR_UNKNOWN_OP: - Serial.println("Halted. Unknown instruction. (this should never happen)"); + //Will only happen if an instruction is not handled in the vm + Serial.println("Halted. Unknown instruction."); break; case VM_ERR_OUT_OF_BOUNDS: Serial.println("Halted. Operation out of bounds."); @@ -80,3 +113,10 @@ uint8_t vm_syscall(VM* vm, uint8_t callno, uint8_t imm) { return 0; } +uint8_t vm_read_addr(uint16_t addr) { + return mem.read(addr); +} +void vm_write_addr(uint16_t addr, uint8_t data) { + mem.write(addr, data); +} + diff --git a/arduino/console.cpp b/arduino/console.cpp index 4629fcc..5cfb898 100644 --- a/arduino/console.cpp +++ b/arduino/console.cpp @@ -83,7 +83,7 @@ void Console::printMemory(uint16_t from, uint16_t to) { serial->print(i, HEX); serial->print(" "); } - uint8_t m = this->vm->M[i]; + uint8_t m = this->vm->readAddr(i); if (m < 0x10) { serial->print("0"); } @@ -95,8 +95,8 @@ void Console::printMemory(uint16_t from, uint16_t to) { void Console::loop() { HardwareSerial * serial = this->serial; - int i; - + uint32_t i; + switch(this->state) { case CONSOLE_NONE: serial->println("Press Enter to activate console."); @@ -155,7 +155,7 @@ void Console::loop() { case CONSOLE_CLEAR: vm_reset(this->vm); for(i = 0; i < VM_MEM_SIZE; i++) { - this->vm->M[i] = 0; + this->vm->writeAddr(i, 0); } serial->println("VM Reset & Memory Cleared"); this->state = CONSOLE_ACTIVATE; @@ -315,7 +315,7 @@ void Console::stateExamine() { for(int i = 0; i < x.length(); i += 2) { value = this->hexToDec(x.substring(i, i+2)); if (value >= 0 && value <= 255) { - this->vm->M[location++] = value; + this->vm->writeAddr(location++, value); } else { serial->println(i); serial->println(x.substring(i, i+2)); diff --git a/arduino/memory.cpp b/arduino/memory.cpp new file mode 100644 index 0000000..b131aa9 --- /dev/null +++ b/arduino/memory.cpp @@ -0,0 +1,127 @@ +/* +Copyright 2018 Sam Stevens + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + */ + +#include "memory.h" + +Memory::Memory(uint32_t clock, uint8_t bitOrder, uint8_t dataMode, uint8_t csPin) { + //Set cs pin high + this->csPin = csPin; + pinMode(csPin, OUTPUT); + digitalWrite(csPin, HIGH); + //Default to 64k ram + this->memSize = 0xFFFF; + //Store spi setting + this->setting = SPISettings(clock, bitOrder, dataMode); +} + +void Memory::start() { + digitalWrite(this->csPin, LOW); + SPI.beginTransaction(this->setting); +} +void Memory::end() { + SPI.endTransaction(); + digitalWrite(this->csPin, HIGH); +} + +bool Memory::init() { + //Set mode to sequential access (should be default mode) + this->start(); + SPI.transfer(INSTR_WRMR); + SPI.transfer(MODE_SEQ); + this->end(); + + //Verify that it was set + this->start(); + SPI.transfer(INSTR_RDMR); + uint8_t mode = SPI.transfer(0); + this->end(); + + return mode == MODE_SEQ; +} + +uint16_t Memory::getSize() { + return this->memSize; +} + +void Memory::setSize(uint16_t memSize) { + this->memSize = memSize; +} + +uint8_t Memory::read(uint16_t addr) { + this->start(); + SPI.transfer(INSTR_READ); + SPI.transfer16(addr); + uint8_t data = SPI.transfer(0); + this->end(); + return data; +} + +void Memory::write(uint16_t addr, uint8_t data) { + this->start(); + SPI.transfer(INSTR_WRITE); + SPI.transfer16(addr); + SPI.transfer(data); + this->end(); +} + +void Memory::writeRange(uint8_t data, uint16_t from, uint16_t to) { + if (from > to) { + return; + } + this->start(); + SPI.transfer(INSTR_WRITE); + SPI.transfer16(from); + for(uint32_t i = from; i < to; i++) { + SPI.transfer(data); + } + this->end(); +} + +bool Memory::verifyRange(uint8_t data, uint16_t from, uint16_t to) { + if (from > to) { + return false; + } + this->start(); + SPI.transfer(INSTR_READ); + SPI.transfer16(from); + bool ok = true; + for(uint32_t i = from; i < to; i++) { + if (SPI.transfer(0) != data) { + ok = false; + break; + } + } + this->end(); + return ok; +} + +bool Memory::test() { + //write 0x55 (01010101) + this->writeRange(0x55, 0, this->memSize); + //verify + if (!this->verifyRange(0x55, 0, this->memSize)) { + return false; + } + + //write zeros + this->writeRange(0, 0, this->memSize); + //verify zeros + if (!this->verifyRange(0, 0, this->memSize)) { + return false; + } + return true; +} + diff --git a/arduino/memory.h b/arduino/memory.h new file mode 100644 index 0000000..9e2aba9 --- /dev/null +++ b/arduino/memory.h @@ -0,0 +1,57 @@ +/* +Copyright 2018 Sam Stevens + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + */ + +#ifndef UVM_MEMORY_H +#define UVM_MEMORY_H + +#include + +enum MemoryMode_t { + MODE_BYTE = 0x00, + MODE_PAGE = 0x80, + MODE_SEQ = 0x40 +}; +typedef enum MemoryMode_t MemoryMode; + +enum MemoryInstruction_t { + INSTR_READ = 0x03, + INSTR_WRITE = 0x02, + INSTR_EDIO = 0x3B, + INSTR_RESIO = 0xFF, + INSTR_RDMR = 0x05, + INSTR_WRMR = 0x01 +}; +typedef enum MemoryInstruction_t MemoryInstruction; + +class Memory { + SPISettings setting; + uint8_t csPin; + uint16_t memSize; + void start(); + void end(); + public: + Memory(uint32_t clock, uint8_t bitOrder, uint8_t dataMode, uint8_t csPin); + uint16_t getSize(); + void setSize(uint16_t memSize); + bool init(); + uint8_t read(uint16_t addr); + void write(uint16_t addr, uint8_t data); + void writeRange(uint8_t data, uint16_t from, uint16_t to) ; + bool verifyRange(uint8_t data, uint16_t from, uint16_t to); + bool test(); +}; + +#endif //UVM_MEMORY_H diff --git a/arduino/vm.c b/arduino/vm.c index 8196192..f300ec6 100644 --- a/arduino/vm.c +++ b/arduino/vm.c @@ -36,7 +36,6 @@ void vm_reset(VM *vm) { void vm_clear(VM *vm) { vm_reset(vm); - memset(&vm->M, 0, VM_MEM_SIZE); } inline uint8_t vm_get_r(VM *vm, uint8_t r) { @@ -99,7 +98,7 @@ void vm_step(VM *vm) { vm->halted = true; return; } - uint16_t raw = (vm->M[PC] << 8) + vm->M[PC + 1]; + uint16_t raw = (vm->readAddr(PC) << 8) + vm->readAddr(PC + 1); vm->PC += 2; inst.op = (uint8_t) (raw >> 12); @@ -118,7 +117,7 @@ void vm_step(VM *vm) { temp16 = x; temp16 += inst.imm; if (temp16 < VM_MEM_SIZE) { - d = vm->M[temp16]; + d = vm->readAddr(temp16); } else { if (vm->error != NULL) { vm->error(VM_ERR_OUT_OF_BOUNDS); @@ -132,7 +131,7 @@ void vm_step(VM *vm) { temp16 = vm_get_r(vm, inst.rd); temp16 += inst.imm; if (temp16 < VM_MEM_SIZE) { - vm->M[temp16] = x; + vm->writeAddr(temp16, x); } else { if (vm->error != NULL) { vm->error(VM_ERR_OUT_OF_BOUNDS); diff --git a/arduino/vm.h b/arduino/vm.h index 31d7cc9..8988413 100644 --- a/arduino/vm.h +++ b/arduino/vm.h @@ -20,7 +20,7 @@ limitations under the License. #include #include -#define VM_MEM_SIZE 256 +#define VM_MEM_SIZE 0xFFFF #define VM_REG_SIZE 16 #define OP_HLT 0x0 @@ -60,9 +60,10 @@ typedef struct VM_Instruction_t VM_Instruction; struct VM_t { uint8_t R[VM_REG_SIZE]; uint8_t PC; - uint8_t M[VM_MEM_SIZE]; uint8_t carry; bool halted; + uint8_t (*readAddr)(uint16_t addr); + void (*writeAddr)(uint16_t addr, uint8_t data); uint8_t (*syscall)(struct VM_t* vm, uint8_t callno, uint8_t imm); void (*error)(uint8_t err); };