Basic CPU working, and some verilator testcases

master
q3k 2014-01-04 13:54:08 +01:00
parent 0e21c36caf
commit 9963c33ff6
3 changed files with 187 additions and 49 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.swp

133
cpu.v
View File

@ -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

102
test_main.cpp Normal file
View File

@ -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);
}