From 9963c33ff6e65b2fa7325c6012220f1d53173d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergiusz=20=27q3k=27=20Baza=C5=84ski?= Date: Sat, 4 Jan 2014 13:54:08 +0100 Subject: [PATCH] Basic CPU working, and some verilator testcases --- .gitignore | 1 + cpu.v | 133 +++++++++++++++++++++++++++++++------------------- test_main.cpp | 102 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 49 deletions(-) create mode 100644 .gitignore create mode 100644 test_main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/cpu.v b/cpu.v index f565257..f64538f 100644 --- a/cpu.v +++ b/cpu.v @@ -3,23 +3,27 @@ `timescale 1ns / 1ps -module core ( +/* verilator lint_off UNDRIVEN */ +/* verilator lint_off UNUSED */ +/* verilator lint_off LITENDIAN */ +module cpu ( input reset, input cpu_clock, - input timer_clock, input [15:0] keypad ); // 4k (12 bits) of 8-bit ram - reg [7:0] ram[0:4095]; + reg [7:0] ram[0:4095]/* verilator public */; // 16 8-bit V registers - reg [7:0] v[0:15]; + reg [7:0] vr[0:15]/* verilator public */; // 16-bit I register reg [15:0] i; // 12-bit program counter - reg [11:0] pc; - // 8-bit stack pointer - reg [7:0] sp; + reg [11:0] pc/* verilator public */; + // 16-element, 12-bit stack + reg [11:0] stack [0:15]; + // 4-bit stack pointer + reg [3:0] sp; // internal cycle counter (0 - fetch) `define FETCH 0 @@ -29,35 +33,55 @@ module core ( // Instruction register reg [15:0] ins; - wire family = (ins >> 12); + wire [3:0] family = ins[15:12]; // ALU operation - wire [3:0] alu_op = ins & 4'hF; - // ALU source registers - wire [3:0] alu_x = (ins >> 8) & 4'hF; - wire [3:0] alu_y = (ins >> 4) & 4'hF; - // ALU immediate - wire [7:0] kk = ins & 8'hFF; - wire [11:0] nnn = ins & 12'hFFF; + wire [3:0] alu_op = ins[3:0]; + wire is_alu_op = (c == `EXECUTE) && (family == 8); + // Source register indexes + wire [3:0] x = ins[11:8]; + wire [3:0] y = ins[7:4]; + // Immediates + wire [7:0] kk = ins[7:0]; + wire [11:0] nnn = ins[11:0]; // Jump register - reg[15:0] jmp; + reg[11:0] jmp; // Should jump on FETCH? reg should_jmp; - always @(cpu_clock) + always @(posedge cpu_clock) begin if (!reset) begin i <= 0; pc <= 0; sp <= 0; should_jmp <= 0; + c <= 0; + + vr[0] <= 0; + vr[1] <= 0; + vr[2] <= 0; + vr[3] <= 0; + vr[4] <= 0; + vr[5] <= 0; + vr[6] <= 0; + vr[7] <= 0; + vr[8] <= 0; + vr[9] <= 0; + vr[10] <= 0; + vr[11] <= 0; + vr[12] <= 0; + vr[13] <= 0; + vr[14] <= 0; + vr[15] <= 0; end else begin case(c) `FETCH: begin - ins <= ram[pc] << 8 | ram[pc + 1]; + ins <= {ram[pc], ram[pc + 1]}; + c <= 1; end - `EXECUTE: begin + `EXECUTE: begin case (family) // Jump to address 1: begin @@ -67,87 +91,70 @@ module core ( // Call address 2: begin should_jmp <= 1; - ram[sp] <= pc; + stack[sp] <= pc; sp <= sp + 1; jmp <= nnn; end // Skip next if Vx == kk (3xkk) 3: begin - if (v[alu_x] == kk) begin + if (vr[x] == kk) begin should_jmp <= 1; jmp <= pc + 4; end end // Skip next if Vx != kk (4xkk) 4: begin - if (v[alu_x] != kk) begin + if (vr[x] != kk) begin should_jmp <= 1; jmp <= pc + 4; end end // Skip next if Vx == Vy (5xy0) 5: begin - if (v[alu_x] == v[alu_y]) begin + if (vr[x] == vr[y]) begin should_jmp <= 1; jmp <= pc + 4; end end // Vx <= kk - 6: v[alu_x] <= kk; + 6: vr[x] <= kk; // Vx += kk - 7: v[alu_x] <= v[alu_x] + kk; - // ALU operation - 8: case(alu_op) - 0: v[alu_x] <= v[alu_y]; - 1: v[alu_x] <= v[alu_x] | v[alu_y]; - 2: v[alu_x] <= v[alu_x] & v[alu_y]; - 3: v[alu_x] <= v[alu_x] ^ v[alu_y]; - 4: { v[15], v[alu_x] } <= v[alu_x] + v[alu_y]; - 5: { v[15], v[alu_x] } <= (v[alu_x] - v[alu_y]) ^ (1 << 8); - 6: begin - v[15] <= v[alu_x][0]; - v[alu_x] <= v[alu_x] >> 1; - end - 7: { v[15], v[alu_x] } <= (v[alu_y] - v[alu_x]) ^ (1 << 8); - 15: begin - v[15] <= v[alu_x][7]; - v[alu_x] <= v[alu_x] << 1; - end - endcase + 7: vr[x] <= vr[x] + kk; // Skip if Vx != Vy (9xy0) 9: begin - if (v[alu_x] != v[alu_y]) begin + if (vr[x] != vr[y]) begin should_jmp <= 1; jmp <= pc + 4; end end // Set I to nnn - 10: i <= nnn; + 10: i <= {4'h0, nnn}; // Jump to nnn + V0 11: begin should_jmp <= 1; - jmp <= nnn + v[0]; + jmp <= nnn + {4'h0, vr[0]}; end // Set Vx to random and kk // TODO: undo fair dice roll - 12: v[alu_x] <= 42 & kk; + 12: vr[x] <= 42 & kk; // TODO: draw sprites 13: begin end // Key functions 14: begin if (kk == 8'h9E) begin - if ((keypad >> alu_x) & 1) begin + if (keypad[x]) begin should_jmp <= 1; jmp <= pc + 4; end end else if (kk == 8'hA1) begin - if (((keypad >> alu_x) & 1) == 0) begin + if (~keypad[x]) begin should_jmp <= 1; jmp <= pc + 4; end end end endcase + c <= 2; end `WRITEBACK: begin if (should_jmp) begin @@ -156,6 +163,34 @@ module core ( pc <= pc + 2; end should_jmp <= 0; + c <= 0; + end + endcase + end + end + + // ALU + always @(posedge cpu_clock) + begin + if (is_alu_op) begin + case(alu_op) + 0: vr[x] <= vr[y]; + 1: vr[x] <= vr[x] | vr[y]; + 2: vr[x] <= vr[x] & vr[y]; + 3: vr[x] <= vr[x] ^ vr[y]; + 4: { vr[15], vr[x] } <= {8'b0, vr[x]} + {8'b0, vr[y]}; + 5: begin + vr[x] <= vr[x] - vr[y]; + vr[15] <= {7'b0, vr[x] >= vr[y]}; + end + 6: begin + vr[15] <= {7'b0, vr[x][0]}; + vr[x] <= vr[x] >> 1; + end + 7: { vr[15], vr[x] } <= ({8'b0, vr[y]} - {8'b0, vr[x]}) ^ (1 << 8); + 15: begin + vr[15] <= {7'b0, vr[x][7]}; + vr[x] <= vr[x] << 1; end endcase end diff --git a/test_main.cpp b/test_main.cpp new file mode 100644 index 0000000..7963f4e --- /dev/null +++ b/test_main.cpp @@ -0,0 +1,102 @@ +#include "Vcpu.h" +#include "Vcpu_cpu.h" +#include "verilated.h" + +Vcpu *top; + +void dump_regs(void) +{ + printf("-> regs:\n"); + for (int i = 0; i < 8; i++) + printf(" %i: 0x%02x\n", i, top->v->vr[i]); +} + +void step(void) +{ + top->cpu_clock = 0; + top->eval(); + top->cpu_clock = 1; + top->eval(); +} + +void step3(void) +{ + step(); step(); step(); +} + +void run_instruction(uint16_t instruction) +{ + top->v->ram[0] = instruction >> 8; + top->v->ram[1] = instruction & 0xFF; + top->v->pc = 0; +} + +void reset(void) +{ + top->reset = 0; + step3(); step3(); + top->reset = 1; +} + +void test_alu_instruction(uint8_t x, uint8_t y, uint16_t ins, uint8_t expected) +{ + printf("INS %04x: ", ins); + reset(); + top->v->vr[1] = x; + top->v->vr[2] = y; + run_instruction(ins); + step3(); + if (top->v->vr[1] != expected) + printf(" FAIL! Expected %02x, got %02x.\n", expected, top->v->vr[1]); + else + printf(" OK.\n"); +} + +void test_alu_instruction_vf(uint8_t x, uint8_t y, uint16_t ins, uint8_t expected, uint8_t evf) +{ + printf("INS %04x: ", ins); + reset(); + top->v->vr[1] = x; + top->v->vr[2] = y; + run_instruction(ins); + step3(); + if (top->v->vr[1] != expected) + printf(" FAIL! Expected %02x, got %02x.\n", expected, top->v->vr[1]); + else + { + if (top->v->vr[15] != evf) + printf(" FAIL! Expected VF %02x, got %02x.\n", evf, top->v->vr[15]); + else + printf(" OK.\n"); + } +} + +int main(int argc, char **argv) +{ + Verilated::commandArgs(argc, argv); + top = new Vcpu; + // LD + printf("LD "); + test_alu_instruction(0x00, 0x45, 0x8120, 0x45); + // OR + printf("OR "); + test_alu_instruction(0x0F, 0xF0, 0x8121, 0xFF); + // AND + printf("AND "); + test_alu_instruction(0xF0, 0x45, 0x8122, 0x40); + // XOR + printf("XOR "); + test_alu_instruction(0xFF, 0x11, 0x8123, 0xEE); + // ADD + printf("ADD (no carry) "); + test_alu_instruction_vf(0x10, 0x25, 0x8124, 0x35, 0x00); + printf("ADD (with carry) "); + test_alu_instruction_vf(0xF0, 0x43, 0x8124, 0x33, 0x01); + // SUB + printf("SUB (no borrow) "); + test_alu_instruction_vf(0x45, 0x15, 0x8125, 0x30, 0x01); + printf("SUB (zero, no borrow) "); + test_alu_instruction_vf(0x45, 0x45, 0x8125, 0x00, 0x01); + printf("SUB (borrow) "); + test_alu_instruction_vf(0x15, 0x45, 0x8125, 0xD0, 0x00); +}