393 lines
8.1 KiB
Verilog
393 lines
8.1 KiB
Verilog
module cpu(
|
|
input wire clk,
|
|
input wire reset,
|
|
output reg[7:0] out
|
|
);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Opcodes
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
parameter OP_NOP = 4'b0000;
|
|
parameter OP_LDA = 4'b0001;
|
|
parameter OP_ADD = 4'b0010;
|
|
parameter OP_SUB = 4'b0011;
|
|
parameter OP_STA = 4'b0100;
|
|
parameter OP_LDI = 4'b0101;
|
|
parameter OP_JMP = 4'b0110;
|
|
parameter OP_JC = 4'b0111;
|
|
parameter OP_JZ = 4'b1000;
|
|
parameter OP_OUT = 4'b1110;
|
|
parameter OP_HLT = 4'b1111;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Control Signals
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Halt
|
|
reg ctrl_ht;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_HLT && stage == 2)
|
|
ctrl_ht <= 1;
|
|
else
|
|
ctrl_ht <= 0;
|
|
end
|
|
|
|
// Memory Address Register In
|
|
reg ctrl_mi;
|
|
always @(negedge clk) begin
|
|
if (stage == 0)
|
|
ctrl_mi <= 1;
|
|
else if (ir[7:4] == OP_LDA && stage == 2)
|
|
ctrl_mi <= 1;
|
|
else if (ir[7:4] == OP_ADD && stage == 2)
|
|
ctrl_mi <= 1;
|
|
else if (ir[7:4] == OP_SUB && stage == 2)
|
|
ctrl_mi <= 1;
|
|
else if (ir[7:4] == OP_STA && stage == 2)
|
|
ctrl_mi <= 1;
|
|
else
|
|
ctrl_mi <= 0;
|
|
end
|
|
|
|
// RAM In
|
|
reg ctrl_ri;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_STA && stage == 3)
|
|
ctrl_ri <= 1;
|
|
else
|
|
ctrl_ri <= 0;
|
|
end
|
|
|
|
// RAM Out
|
|
reg ctrl_ro;
|
|
always @(negedge clk) begin
|
|
if (stage == 1)
|
|
ctrl_ro <= 1;
|
|
else if (ir[7:4] == OP_LDA && stage == 3)
|
|
ctrl_ro <= 1;
|
|
else if (ir[7:4] == OP_ADD && stage == 3)
|
|
ctrl_ro <= 1;
|
|
else if (ir[7:4] == OP_SUB && stage == 3)
|
|
ctrl_ro <= 1;
|
|
else
|
|
ctrl_ro <= 0;
|
|
end
|
|
|
|
// Instruction Register Out
|
|
reg ctrl_io;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_LDA && stage == 2)
|
|
ctrl_io <= 1;
|
|
else if (ir[7:4] == OP_LDI && stage == 2)
|
|
ctrl_io <= 1;
|
|
else if (ir[7:4] == OP_ADD && stage == 2)
|
|
ctrl_io <= 1;
|
|
else if (ir[7:4] == OP_SUB && stage == 2)
|
|
ctrl_io <= 1;
|
|
else if (ir[7:4] == OP_STA && stage == 2)
|
|
ctrl_io <= 1;
|
|
else if (ir[7:4] == OP_JMP && stage == 2)
|
|
ctrl_io <= 1;
|
|
else if (ir[7:4] == OP_JC && stage == 2)
|
|
ctrl_io <= 1;
|
|
else if (ir[7:4] == OP_JZ && stage == 2)
|
|
ctrl_io <= 1;
|
|
else
|
|
ctrl_io <= 0;
|
|
end
|
|
|
|
// Instruction Register In
|
|
reg ctrl_ii;
|
|
always @(negedge clk) begin
|
|
if (stage == 1)
|
|
ctrl_ii <= 1;
|
|
else
|
|
ctrl_ii <= 0;
|
|
end
|
|
|
|
// A Register In
|
|
reg ctrl_ai;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_LDI && stage == 2)
|
|
ctrl_ai <= 1;
|
|
else if (ir[7:4] == OP_LDA && stage == 3)
|
|
ctrl_ai <= 1;
|
|
else if (ir[7:4] == OP_ADD && stage == 4)
|
|
ctrl_ai <= 1;
|
|
else if (ir[7:4] == OP_SUB && stage == 4)
|
|
ctrl_ai <= 1;
|
|
else
|
|
ctrl_ai <= 0;
|
|
end
|
|
|
|
// A Register Out
|
|
reg ctrl_ao;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_STA && stage == 3)
|
|
ctrl_ao <= 1;
|
|
else if (ir[7:4] == OP_OUT && stage == 2)
|
|
ctrl_ao <= 1;
|
|
else
|
|
ctrl_ao <= 0;
|
|
end
|
|
|
|
// Sum Out
|
|
reg ctrl_eo;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_ADD && stage == 4)
|
|
ctrl_eo <= 1;
|
|
else if (ir[7:4] == OP_SUB && stage == 4)
|
|
ctrl_eo <= 1;
|
|
else
|
|
ctrl_eo <= 0;
|
|
end
|
|
|
|
// Subtract
|
|
reg ctrl_su;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_SUB && stage == 4)
|
|
ctrl_su <= 1;
|
|
else
|
|
ctrl_su <= 0;
|
|
end
|
|
|
|
// B Register In
|
|
reg ctrl_bi;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_ADD && stage == 3)
|
|
ctrl_bi <= 1;
|
|
else if (ir[7:4] == OP_SUB && stage == 3)
|
|
ctrl_bi <= 1;
|
|
else
|
|
ctrl_bi <= 0;
|
|
end
|
|
|
|
// Output Register In
|
|
reg ctrl_oi;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_OUT && stage == 2)
|
|
ctrl_oi <= 1;
|
|
else
|
|
ctrl_oi <= 0;
|
|
end
|
|
|
|
// Counter Enable
|
|
reg ctrl_ce;
|
|
always @(negedge clk) begin
|
|
if (stage == 1)
|
|
ctrl_ce <= 1;
|
|
else
|
|
ctrl_ce <= 0;
|
|
end
|
|
|
|
// Counter Out
|
|
reg ctrl_co;
|
|
always @(negedge clk) begin
|
|
// Always in Stage 0
|
|
if (stage == 0)
|
|
ctrl_co <= 1;
|
|
else
|
|
ctrl_co <= 0;
|
|
end
|
|
|
|
// Jump
|
|
reg ctrl_jp;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_JMP && stage == 2)
|
|
ctrl_jp <= 1;
|
|
else if (ir[7:4] == OP_JC && stage == 2 && flags[FLAG_C] == 1)
|
|
ctrl_jp <= 1;
|
|
else if (ir[7:4] == OP_JZ && stage == 2 && flags[FLAG_Z] == 1)
|
|
ctrl_jp <= 1;
|
|
else
|
|
ctrl_jp <= 0;
|
|
end
|
|
|
|
// Flags Register In
|
|
reg ctrl_fi;
|
|
always @(negedge clk) begin
|
|
if (ir[7:4] == OP_ADD && stage == 4)
|
|
ctrl_fi <= 1;
|
|
else if (ir[7:4] == OP_SUB && stage == 4)
|
|
ctrl_fi <= 1;
|
|
else
|
|
ctrl_fi <= 0;
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Bus
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
wire[7:0] bus;
|
|
assign bus =
|
|
ctrl_co ? pc :
|
|
ctrl_ro ? mem[mar] :
|
|
ctrl_io ? ir[3:0] :
|
|
ctrl_ao ? a_reg :
|
|
ctrl_eo ? alu :
|
|
8'b0;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Program Counter
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg[3:0] pc;
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
pc <= 0;
|
|
else if (ctrl_ce)
|
|
pc <= pc + 1;
|
|
else if (ctrl_jp)
|
|
pc <= bus[3:0];
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Instruction Step Counter
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg[2:0] stage;
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
stage <= 0;
|
|
else if (stage == 5 || ctrl_jp)
|
|
stage <= 0;
|
|
else if (ctrl_ht || stage == 6)
|
|
// For a halt, put it into a stage it can never get out of
|
|
stage <= 6;
|
|
else
|
|
stage <= stage + 1;
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Memory Address Register
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg[3:0] mar;
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
mar <= 0;
|
|
else if (ctrl_mi)
|
|
mar <= bus[3:0];
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Memory
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg[7:0] mem[16];
|
|
always @(posedge clk) begin
|
|
if (ctrl_ri)
|
|
mem[mar] <= bus;
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Instruction Register
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg[7:0] ir;
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
ir <= 0;
|
|
else if (ctrl_ii)
|
|
ir <= bus;
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ALU
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg[7:0] a_reg;
|
|
reg[7:0] b_reg;
|
|
wire[7:0] b_reg_out;
|
|
wire[8:0] alu;
|
|
wire flag_z, flag_c;
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
a_reg <= 0;
|
|
else if (ctrl_ai)
|
|
a_reg <= bus;
|
|
end
|
|
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
b_reg <= 0;
|
|
else if (ctrl_bi)
|
|
b_reg <= bus;
|
|
end
|
|
|
|
// Zero flag is set if ALU is zero
|
|
assign flag_z = (alu[7:0] == 0) ? 1 : 0;
|
|
|
|
// Use twos-complement for subtraction
|
|
assign b_reg_out = ctrl_su ? ~b_reg + 1 : b_reg;
|
|
|
|
// Carry flag is set if there's an overflow into bit 8 of the ALU
|
|
assign flag_c = alu[8];
|
|
|
|
assign alu = a_reg + b_reg_out;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Flags Register
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
parameter FLAG_C = 1;
|
|
parameter FLAG_Z = 0;
|
|
|
|
reg[1:0] flags;
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
flags <= 0;
|
|
else if (ctrl_fi)
|
|
flags <= {flag_c, flag_z};
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Output Register
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
always @(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
out <= 0;
|
|
else if (ctrl_oi)
|
|
out <= bus;
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Program to Run
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
initial begin
|
|
mem[0] = {OP_OUT, 4'b0};
|
|
mem[1] = {OP_ADD, 4'hF};
|
|
mem[2] = {OP_JC, 4'h4};
|
|
mem[3] = {OP_JMP, 4'h0};
|
|
mem[4] = {OP_SUB, 4'hF};
|
|
mem[5] = {OP_OUT, 4'h0};
|
|
mem[6] = {OP_JZ, 4'h0};
|
|
mem[7] = {OP_JMP, 4'h4};
|
|
mem[8] = {OP_NOP, 4'h0};
|
|
mem[9] = {OP_NOP, 4'h0};
|
|
mem[10] = {OP_NOP, 4'h0};
|
|
mem[11] = {OP_NOP, 4'h0};
|
|
mem[12] = {OP_NOP, 4'h0};
|
|
mem[13] = {OP_NOP, 4'h0};
|
|
mem[14] = {OP_NOP, 4'h0};
|
|
mem[15] = {8'h01}; // DATA = 1
|
|
end
|
|
|
|
endmodule
|
|
|