Skip to content

Instantly share code, notes, and snippets.

@kierdavis
Created April 19, 2012 17:42
Show Gist options
  • Save kierdavis/2422551 to your computer and use it in GitHub Desktop.
Save kierdavis/2422551 to your computer and use it in GitHub Desktop.
DCPU-16 implementation in Verilog
/*
General state machine operation:
ST_FETCH - Load instruction word
ST_LOADAREF - Decode operand_a into a_type and a_value components
ST_LOADA - Load a using a_type and a_value
ST_LOADB - Decode operand_b and load b
ST_DECODE - Distribute to ST_opcode
ST_opcode - Modify b, using a
ST_STORE - Store b into a's location using a_type and a_value
*/
module DCPU16(sys_clk, sys_rst);
parameter ST_FETCH = 5'h00;
parameter ST_FETCH_INCRPC = 5'h01;
parameter ST_LOADAREF = 5'h02;
parameter ST_LOADAREF_INCRPC = 5'h03;
parameter ST_LOADAREF_INCRSP = 5'h04;
parameter ST_LOADA = 5'h05;
parameter ST_LOADB = 5'h06;
parameter ST_LOADB_INCRPC = 5'h07;
parameter ST_LOADB_LOADMEM_INCRPC = 5'h08;
parameter ST_LOADB_INCRSP = 5'h09;
parameter ST_DECODE = 5'h0A;
parameter ST_CPU_ERR = 5'h0B;
parameter ST_JSR = 5'h0C;
parameter ST_JSR_1 = 5'h0D;
parameter ST_IFE = 5'h0E;
parameter ST_IFN = 5'h0F;
parameter ST_IFG = 5'h10;
parameter ST_IFB = 5'h11;
parameter ST_ALU = 5'h12;
parameter ST_ALU_BUFFER = 5'h13;
parameter ST_STORE = 5'h14;
parameter ST_SKIP = 5'h15;
parameter ERR_NONE = 4'd0;
parameter ERR_INVALID_EXT_OPCODE = 4'd1;
parameter ERR_INVALID_BASIC_OPCODE = 4'd2;
parameter ERR_STATE_MACHINE_CONFUSED = 4'd3;
parameter ERR_STORE_IMM = 4'd4;
parameter ERR_STORE_POP = 4'd5;
parameter ERR_LOAD_PUSH = 4'd6;
parameter ERR_INVALID_OPERAND_A = 4'd7;
parameter ERR_INVALID_OPERAND_B = 4'd8;
parameter ERR_INVALID_A_TYPE = 4'd9;
parameter OP_IMM = 3'd0;
parameter OP_REG = 3'd1;
parameter OP_MEM = 3'd2;
parameter OP_SP = 3'd3;
parameter OP_PC = 3'd4;
parameter OP_O = 3'd5;
input sys_clk;
input sys_rst;
wire sys_clk;
wire sys_rst;
reg [15:0] pc;
reg [15:0] sp;
reg [15:0] o;
wire [15:0] pc_plus_one = pc + 16'h1;
reg [15:0] pc_buffer;
wire [15:0] sp_plus_one = sp + 16'h1;
wire [15:0] sp_minus_one = sp - 16'h1;
reg [15:0] sp_buffer;
reg [15:0] regs[0:7];
reg [15:0] memory[0:65535];
/*
initial begin
regs[0] <= 16'h0000;
pc <= 16'h0000;
state <= ST_FETCH;
// 7c01 0030 7de1 0100 0020
memory[16'h0000] <= 16'h7C01;
memory[16'h0001] <= 16'h0030;
memory[16'h0002] <= 16'h7DE1;
memory[16'h0003] <= 16'h1000;
memory[16'h0004] <= 16'h0020;
memory[16'h0005] <= 16'h7803;
memory[16'h0006] <= 16'h1000;
end
*/
reg [15:0] instruction;
wire [3:0] opcode = instruction[3:0];
wire [5:0] operand_a = instruction[9:4];
wire [5:0] operand_b = instruction[15:10];
reg [2:0] a_type;
reg [15:0] a_value;
reg [15:0] a;
reg [15:0] b;
reg [15:0] b_addr;
reg [15:0] b_buffer;
reg [4:0] state;
reg [4:0] newstate;
reg [3:0] err;
wire [15:0] alu_q;
wire [15:0] alu_o;
reg skip;
always @(posedge sys_clk) begin
case (state)
ST_FETCH: begin
instruction <= memory[pc]; // Fetch the instruction
pc_buffer <= pc_plus_one; // Get ready to incr PC
newstate <= ST_FETCH_INCRPC;
end
ST_FETCH_INCRPC: begin
pc <= pc_buffer; // Increment PC
if (opcode == 4'd0) begin // Don't even bother loading A
newstate <= ST_LOADB;
end
else begin
newstate <= ST_LOADAREF;
end
end
ST_LOADAREF: begin // Start loading A
if (operand_a[5] == 1'b1) begin
err <= ERR_STORE_IMM;
newstate <= ST_CPU_ERR;
end
else if (operand_a[5:3] == 3'b000) begin
a_type <= OP_REG;
a_value <= {13'd0, operand_a[2:0]};
newstate <= ST_LOADA;
end
else if (operand_a[5:3] == 3'b001) begin
a_type <= OP_MEM;
a_value <= regs[operand_a[2:0]];
newstate <= ST_LOADA;
end
else if (operand_a[5:3] == 3'b010) begin
a_type <= OP_MEM;
a_value <= memory[pc] + regs[operand_a[2:0]];
pc_buffer <= pc_plus_one;
newstate <= ST_LOADAREF_INCRPC;
end
else if (operand_a == 6'b011000) begin
err <= ERR_STORE_POP;
newstate <= ST_CPU_ERR;
end
else if (operand_a == 6'b011001) begin
a_type <= OP_MEM;
a_value <= sp;
newstate <= ST_LOADA;
end
else if (operand_a == 6'b011010) begin
a_type <= OP_MEM;
a_value <= sp_minus_one;
sp_buffer <= sp_minus_one;
newstate <= ST_LOADAREF_INCRSP;
end
else if (operand_a == 6'b011011) begin
a_type <= OP_SP;
newstate <= ST_LOADA;
end
else if (operand_a == 6'b011100) begin
a_type <= OP_PC;
newstate <= ST_LOADA;
end
else if (operand_a == 6'b011101) begin
a_type <= OP_O;
newstate <= ST_LOADA;
end
else if (operand_a == 6'b011110) begin
a_type <= OP_MEM;
a_value <= memory[pc];
pc_buffer <= pc_plus_one;
newstate <= ST_LOADAREF_INCRPC;
end
else if (operand_a == 6'b011111) begin
err <= ERR_STORE_IMM;
newstate <= ST_CPU_ERR;
end
else begin
err <= ERR_INVALID_OPERAND_A;
newstate <= ST_CPU_ERR;
end
end
ST_LOADAREF_INCRPC: begin
pc <= pc_buffer;
newstate <= ST_LOADA;
end
ST_LOADAREF_INCRSP: begin
sp <= sp_buffer;
newstate <= ST_LOADA;
end
ST_LOADA: begin
case (a_type)
OP_IMM: begin
a <= a_value;
newstate <= ST_LOADB;
end
OP_REG: begin
a <= regs[a_value[2:0]];
newstate <= ST_LOADB;
end
OP_MEM: begin
a <= memory[a_value];
newstate <= ST_LOADB;
end
OP_SP: begin
a <= sp;
newstate <= ST_LOADB;
end
OP_PC: begin
a <= pc;
newstate <= ST_LOADB;
end
OP_O: begin
a <= o;
newstate <= ST_LOADB;
end
default: begin
err <= ERR_INVALID_A_TYPE;
newstate <= ST_CPU_ERR;
end
endcase
end
ST_LOADB: begin
if (operand_b[5] == 1'b1) begin
b <= {{11{operand_b[4]}}, operand_b[4:0]};
newstate <= ST_DECODE;
end
else if (operand_b[5:3] == 3'b000) begin
b <= regs[operand_b[2:0]];
newstate <= ST_DECODE;
end
else if (operand_b[5:3] == 3'b001) begin
b <= memory[regs[operand_b[2:0]]];
newstate <= ST_DECODE;
end
else if (operand_b[5:3] == 3'b010) begin
b_addr <= memory[pc] + regs[operand_b[2:0]];
pc_buffer <= pc_plus_one;
newstate <= ST_LOADB_LOADMEM_INCRPC;
end
else if (operand_b == 6'b011000) begin
b <= memory[sp];
sp_buffer <= sp_plus_one;
newstate <= ST_LOADB_INCRSP;
end
else if (operand_b == 6'b011001) begin
b <= memory[sp];
newstate <= ST_DECODE;
end
else if (operand_b == 6'b011010) begin
err <= ERR_LOAD_PUSH;
newstate <= ST_CPU_ERR;
end
else if (operand_b == 6'b011011) begin
b <= sp;
newstate <= ST_DECODE;
end
else if (operand_b == 6'b011100) begin
b <= pc;
newstate <= ST_DECODE;
end
else if (operand_b == 6'b011101) begin
b <= o;
newstate <= ST_DECODE;
end
else if (operand_b == 6'b011110) begin
b_addr <= memory[pc];
pc_buffer <= pc_plus_one;
newstate <= ST_LOADB_LOADMEM_INCRPC;
end
else if (operand_b == 6'b011111) begin
b <= memory[pc];
pc_buffer <= pc_plus_one;
newstate <= ST_LOADB_INCRPC;
end
else begin
err <= ERR_INVALID_OPERAND_B;
newstate <= ST_CPU_ERR;
end
end
ST_LOADB_INCRPC: begin
pc <= pc_buffer;
newstate <= ST_DECODE;
end
ST_LOADB_LOADMEM_INCRPC: begin
b <= memory[b_addr];
pc <= pc_buffer;
newstate <= ST_DECODE;
end
ST_LOADB_INCRSP: begin
sp <= sp_buffer;
newstate <= ST_DECODE;
end
ST_DECODE: begin
if (skip) begin
newstate <= ST_SKIP;
end
else begin
case (opcode)
4'h0: case (operand_a)
6'h00: newstate <= ST_FETCH;
6'h01: newstate <= ST_JSR;
default: begin
err <= ERR_INVALID_EXT_OPCODE;
newstate <= ST_CPU_ERR;
end
endcase
4'h1: newstate <= ST_STORE;
4'h2: newstate <= ST_ALU;
4'h3: newstate <= ST_ALU;
4'h4: newstate <= ST_ALU;
4'h5: newstate <= ST_ALU;
4'h6: newstate <= ST_ALU;
4'h7: newstate <= ST_ALU;
4'h8: newstate <= ST_ALU;
4'h9: newstate <= ST_ALU;
4'hA: newstate <= ST_ALU;
4'hB: newstate <= ST_ALU;
4'hC: newstate <= ST_IFE;
4'hD: newstate <= ST_IFN;
4'hE: newstate <= ST_IFG;
4'hF: newstate <= ST_IFB;
default: begin
err <= ERR_INVALID_BASIC_OPCODE;
newstate <= ST_CPU_ERR;
end
endcase
end
end
ST_CPU_ERR: begin
newstate <= ST_CPU_ERR;
end
ST_JSR: begin
memory[sp_minus_one] <= pc;
sp_buffer <= sp_minus_one;
newstate <= ST_JSR_1;
end
ST_JSR_1: begin
sp <= sp_buffer;
pc <= b;
newstate <= ST_FETCH;
end
ST_ALU: begin
b_buffer <= alu_q;
o <= alu_o;
newstate <= ST_ALU_BUFFER;
end
ST_ALU_BUFFER: begin
b <= b_buffer;
newstate <= ST_STORE;
end
ST_IFE: begin
// a - b == 0
if (|alu_q) begin
skip <= 1'd1;
end
newstate <= ST_FETCH;
end
ST_IFN: begin
// a - b != 0
if (!(|alu_q)) begin
skip <= 1'd1;
end
newstate <= ST_FETCH;
end
ST_IFG: begin
// a - b != 0 && (a - b)[15] == 0 (not zero and not negative i.e. positive)
if (!(|alu_q) || alu_q[15]) begin
skip <= 1'd1;
end
newstate <= ST_FETCH;
end
ST_IFB: begin
// a & b != 0
if (!(|alu_q)) begin
skip <= 1'd1;
end
newstate <= ST_FETCH;
end
ST_STORE: begin
case (a_type)
OP_IMM: begin
err <= ERR_STORE_IMM;
newstate <= ST_CPU_ERR;
end
OP_REG: begin
regs[a_value[2:0]] <= b;
newstate <= ST_FETCH;
end
OP_MEM: begin
memory[a_value] <= a;
newstate <= ST_FETCH;
end
OP_SP: begin
sp <= a;
newstate <= ST_FETCH;
end
OP_PC: begin
pc <= a;
newstate <= ST_FETCH;
end
OP_O: begin
o <= a;
newstate <= ST_FETCH;
end
default: begin
err <= ERR_INVALID_A_TYPE;
newstate <= ST_CPU_ERR;
end
endcase
end
ST_SKIP: begin
skip <= 1'd0;
newstate <= ST_FETCH;
end
default: begin
//err <= ERR_STATE_MACHINE_CONFUSED;
//newstate <= ST_CPU_ERR;
newstate <= ST_FETCH;
end
endcase
end
always @(negedge sys_clk) begin
state <= newstate;
end
ALU alu(.a(a), .b(b), .q(alu_q), .o(alu_o), .mode(opcode));
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment