Created
April 19, 2012 17:42
-
-
Save kierdavis/2422551 to your computer and use it in GitHub Desktop.
DCPU-16 implementation in Verilog
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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