Basic CPU working, and some verilator testcases
parent
0e21c36caf
commit
9963c33ff6
|
@ -0,0 +1 @@
|
|||
*.swp
|
133
cpu.v
133
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
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue