Implemented vm memory using external 23LCV512 chip.
Todo: implement extended registers
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
127
arduino/memory.cpp
Normal file
127
arduino/memory.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
57
arduino/memory.h
Normal file
57
arduino/memory.h
Normal file
@@ -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 <SPI.h>
|
||||
|
||||
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
|
||||
@@ -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);
|
||||
|
||||
@@ -20,7 +20,7 @@ limitations under the License.
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user