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 "vm.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
VM vm;
|
VM vm;
|
||||||
Console console(&vm);
|
Console console(&vm);
|
||||||
|
|
||||||
|
//Expecting a Microchip 23LCV512 connected over MOSI/MISO/MCLK
|
||||||
|
Memory mem(20000000, MSBFIRST, SPI_MODE0, 4);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
//Setup VM
|
||||||
vm_reset(&vm);
|
vm_reset(&vm);
|
||||||
vm.error = vm_print_error;
|
vm.error = vm_print_error;
|
||||||
vm.syscall = vm_syscall;
|
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);
|
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() {
|
void loop() {
|
||||||
@@ -45,7 +77,8 @@ void vm_print_error(uint8_t err) {
|
|||||||
Serial.println("Halted. PC misaligned.");
|
Serial.println("Halted. PC misaligned.");
|
||||||
break;
|
break;
|
||||||
case VM_ERR_UNKNOWN_OP:
|
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;
|
break;
|
||||||
case VM_ERR_OUT_OF_BOUNDS:
|
case VM_ERR_OUT_OF_BOUNDS:
|
||||||
Serial.println("Halted. Operation 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;
|
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(i, HEX);
|
||||||
serial->print(" ");
|
serial->print(" ");
|
||||||
}
|
}
|
||||||
uint8_t m = this->vm->M[i];
|
uint8_t m = this->vm->readAddr(i);
|
||||||
if (m < 0x10) {
|
if (m < 0x10) {
|
||||||
serial->print("0");
|
serial->print("0");
|
||||||
}
|
}
|
||||||
@@ -95,8 +95,8 @@ void Console::printMemory(uint16_t from, uint16_t to) {
|
|||||||
|
|
||||||
void Console::loop() {
|
void Console::loop() {
|
||||||
HardwareSerial * serial = this->serial;
|
HardwareSerial * serial = this->serial;
|
||||||
int i;
|
uint32_t i;
|
||||||
|
|
||||||
switch(this->state) {
|
switch(this->state) {
|
||||||
case CONSOLE_NONE:
|
case CONSOLE_NONE:
|
||||||
serial->println("Press Enter to activate console.");
|
serial->println("Press Enter to activate console.");
|
||||||
@@ -155,7 +155,7 @@ void Console::loop() {
|
|||||||
case CONSOLE_CLEAR:
|
case CONSOLE_CLEAR:
|
||||||
vm_reset(this->vm);
|
vm_reset(this->vm);
|
||||||
for(i = 0; i < VM_MEM_SIZE; i++) {
|
for(i = 0; i < VM_MEM_SIZE; i++) {
|
||||||
this->vm->M[i] = 0;
|
this->vm->writeAddr(i, 0);
|
||||||
}
|
}
|
||||||
serial->println("VM Reset & Memory Cleared");
|
serial->println("VM Reset & Memory Cleared");
|
||||||
this->state = CONSOLE_ACTIVATE;
|
this->state = CONSOLE_ACTIVATE;
|
||||||
@@ -315,7 +315,7 @@ void Console::stateExamine() {
|
|||||||
for(int i = 0; i < x.length(); i += 2) {
|
for(int i = 0; i < x.length(); i += 2) {
|
||||||
value = this->hexToDec(x.substring(i, i+2));
|
value = this->hexToDec(x.substring(i, i+2));
|
||||||
if (value >= 0 && value <= 255) {
|
if (value >= 0 && value <= 255) {
|
||||||
this->vm->M[location++] = value;
|
this->vm->writeAddr(location++, value);
|
||||||
} else {
|
} else {
|
||||||
serial->println(i);
|
serial->println(i);
|
||||||
serial->println(x.substring(i, i+2));
|
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) {
|
void vm_clear(VM *vm) {
|
||||||
vm_reset(vm);
|
vm_reset(vm);
|
||||||
memset(&vm->M, 0, VM_MEM_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t vm_get_r(VM *vm, uint8_t r) {
|
inline uint8_t vm_get_r(VM *vm, uint8_t r) {
|
||||||
@@ -99,7 +98,7 @@ void vm_step(VM *vm) {
|
|||||||
vm->halted = true;
|
vm->halted = true;
|
||||||
return;
|
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;
|
vm->PC += 2;
|
||||||
inst.op = (uint8_t) (raw >> 12);
|
inst.op = (uint8_t) (raw >> 12);
|
||||||
|
|
||||||
@@ -118,7 +117,7 @@ void vm_step(VM *vm) {
|
|||||||
temp16 = x;
|
temp16 = x;
|
||||||
temp16 += inst.imm;
|
temp16 += inst.imm;
|
||||||
if (temp16 < VM_MEM_SIZE) {
|
if (temp16 < VM_MEM_SIZE) {
|
||||||
d = vm->M[temp16];
|
d = vm->readAddr(temp16);
|
||||||
} else {
|
} else {
|
||||||
if (vm->error != NULL) {
|
if (vm->error != NULL) {
|
||||||
vm->error(VM_ERR_OUT_OF_BOUNDS);
|
vm->error(VM_ERR_OUT_OF_BOUNDS);
|
||||||
@@ -132,7 +131,7 @@ void vm_step(VM *vm) {
|
|||||||
temp16 = vm_get_r(vm, inst.rd);
|
temp16 = vm_get_r(vm, inst.rd);
|
||||||
temp16 += inst.imm;
|
temp16 += inst.imm;
|
||||||
if (temp16 < VM_MEM_SIZE) {
|
if (temp16 < VM_MEM_SIZE) {
|
||||||
vm->M[temp16] = x;
|
vm->writeAddr(temp16, x);
|
||||||
} else {
|
} else {
|
||||||
if (vm->error != NULL) {
|
if (vm->error != NULL) {
|
||||||
vm->error(VM_ERR_OUT_OF_BOUNDS);
|
vm->error(VM_ERR_OUT_OF_BOUNDS);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ limitations under the License.
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define VM_MEM_SIZE 256
|
#define VM_MEM_SIZE 0xFFFF
|
||||||
#define VM_REG_SIZE 16
|
#define VM_REG_SIZE 16
|
||||||
|
|
||||||
#define OP_HLT 0x0
|
#define OP_HLT 0x0
|
||||||
@@ -60,9 +60,10 @@ typedef struct VM_Instruction_t VM_Instruction;
|
|||||||
struct VM_t {
|
struct VM_t {
|
||||||
uint8_t R[VM_REG_SIZE];
|
uint8_t R[VM_REG_SIZE];
|
||||||
uint8_t PC;
|
uint8_t PC;
|
||||||
uint8_t M[VM_MEM_SIZE];
|
|
||||||
uint8_t carry;
|
uint8_t carry;
|
||||||
bool halted;
|
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);
|
uint8_t (*syscall)(struct VM_t* vm, uint8_t callno, uint8_t imm);
|
||||||
void (*error)(uint8_t err);
|
void (*error)(uint8_t err);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user