module grom_cpu( input clk, input reset, output reg [11:0] addr, input [7:0] data_in, output reg [7:0] data_out, output reg we, output reg ioreq, output reg hlt ); reg[11:0] PC /* verilator public_flat */; // Program counter reg[7:0] IR /* verilator public_flat */; // Instruction register reg[7:0] VALUE /* verilator public_flat */; // Temp reg for storing 2nd operand reg[3:0] CS /* verilator public_flat */; // Code segment regiser reg[3:0] DS /* verilator public_flat */; // Data segment regiser reg[11:0] SP /* verilator public_flat */; // Stack pointer regiser reg[7:0] R[0:3] /* verilator public_flat */; // General purpose registers reg[11:0] FUTURE_PC /* verilator public_flat */; // PC to jump to localparam STATE_RESET /*verilator public_flat*/ = 5'b00000; localparam STATE_FETCH_PREP /*verilator public_flat*/ = 5'b00001; localparam STATE_FETCH_WAIT /*verilator public_flat*/ = 5'b00010; localparam STATE_FETCH /*verilator public_flat*/ = 5'b00011; localparam STATE_EXECUTE /*verilator public_flat*/ = 5'b00100; localparam STATE_FETCH_VALUE_PREP /*verilator public_flat*/ = 5'b00101; localparam STATE_FETCH_VALUE /*verilator public_flat*/ = 5'b00110; localparam STATE_EXECUTE_DBL /*verilator public_flat*/ = 5'b00111; localparam STATE_LOAD_VALUE /*verilator public_flat*/ = 5'b01000; localparam STATE_LOAD_VALUE_WAIT /*verilator public_flat*/ = 5'b01001; localparam STATE_ALU_RESULT_WAIT /*verilator public_flat*/ = 5'b01010; localparam STATE_ALU_RESULT /*verilator public_flat*/ = 5'b01011; localparam STATE_PUSH_PC_LOW /*verilator public_flat*/ = 5'b01100; localparam STATE_JUMP /*verilator public_flat*/ = 5'b01101; localparam STATE_RET_VALUE_WAIT /*verilator public_flat*/ = 5'b01110; localparam STATE_RET_VALUE /*verilator public_flat*/ = 5'b01111; localparam STATE_RET_VALUE_WAIT2 /*verilator public_flat*/ = 5'b10000; localparam STATE_RET_VALUE2 /*verilator public_flat*/ = 5'b10001; reg [4:0] state /* verilator public_flat */ = STATE_RESET; reg [7:0] alu_a /* verilator public_flat */; reg [7:0] alu_b /* verilator public_flat */; reg [3:0] alu_op /* verilator public_flat */; reg [1:0] RESULT_REG /* verilator public_flat */; wire [7:0] alu_res /* verilator public_flat */; wire alu_CF /* verilator public_flat */; wire alu_ZF /* verilator public_flat */; wire alu_SF /* verilator public_flat */; reg jump; alu alu(.clk(clk),.A(alu_a),.B(alu_b),.operation(alu_op),.result(alu_res),.CF(alu_CF),.ZF(alu_ZF),.SF(alu_SF)); always @(posedge clk) begin if (reset) begin state <= STATE_RESET; hlt <= 0; end else begin case (state) STATE_RESET : begin PC <= 12'h000; state <= STATE_FETCH_PREP; CS <= 4'h0; DS <= 4'h0; R[0] <= 8'h00; R[1] <= 8'h00; R[2] <= 8'h00; R[3] <= 8'h00; SP <= 12'hfff; end STATE_FETCH_PREP : begin addr <= PC; we <= 0; ioreq <= 0; state <= STATE_FETCH_WAIT; end STATE_FETCH_WAIT : begin // Sync with memory due to CLK state <= (hlt) ? STATE_FETCH_PREP : STATE_FETCH; end STATE_FETCH : begin IR <= data_in; PC <= PC + 1; state <= STATE_EXECUTE; end STATE_EXECUTE : begin `ifdef DISASSEMBLY $display(" PC %h R0 %h R1 %h R2 %h R3 %h CS %h DS %h SP %h ALU [%d %d %d]", PC, R[0], R[1], R[2], R[3], CS, DS, SP, alu_CF,alu_SF,alu_ZF); `endif if (IR[7]) begin addr <= PC; state <= STATE_FETCH_VALUE_PREP; PC <= PC + 1; end else begin case(IR[6:4]) 3'b000 : begin `ifdef DISASSEMBLY $display("MOV R%d,R%d",IR[3:2],IR[1:0]); `endif R[IR[3:2]] <= R[IR[1:0]]; state <= STATE_FETCH_PREP; end 3'b001 : begin alu_a <= R[0]; // first input R0 alu_b <= R[IR[1:0]]; RESULT_REG <= 0; // result in R0 alu_op <= { 2'b00, IR[3:2] }; state <= STATE_ALU_RESULT_WAIT; `ifdef DISASSEMBLY case(IR[3:2]) 2'b00 : begin $display("ADD R%d",IR[1:0]); end 2'b01 : begin $display("SUB R%d",IR[1:0]); end 2'b10 : begin $display("ADC R%d",IR[1:0]); end 2'b11 : begin $display("SBC R%d",IR[1:0]); end endcase `endif end 3'b010 : begin alu_a <= R[0]; // first input R0 alu_b <= R[IR[1:0]]; RESULT_REG <= 0; // result in R0 alu_op <= { 2'b01, IR[3:2] }; state <= STATE_ALU_RESULT_WAIT; `ifdef DISASSEMBLY case(IR[3:2]) 2'b00 : begin $display("AND R%d",IR[1:0]); end 2'b01 : begin $display("OR R%d",IR[1:0]); end 2'b10 : begin $display("NOT R%d",IR[1:0]); end 2'b11 : begin $display("XOR R%d",IR[1:0]); end endcase `endif end 3'b011 : begin RESULT_REG <= IR[1:0]; // result in REG // CMP and TEST are not storing result state <= IR[3] ? STATE_FETCH_PREP : STATE_ALU_RESULT_WAIT; // CMP and TEST are having first input R0, for INC and DEC is REG alu_a <= IR[3] ? R[0] : R[IR[1:0]]; // CMP and TEST are having second input REG, for INC and DEC is 1 alu_b <= IR[3] ? R[IR[1:0]] : 8'b00000001; case(IR[3:2]) 2'b00 : begin `ifdef DISASSEMBLY $display("INC R%d",IR[1:0]); `endif alu_op <= 4'b0000; // ALU_OP_ADD end 2'b01 : begin `ifdef DISASSEMBLY $display("DEC R%d",IR[1:0]); `endif alu_op <= 4'b0001; // ALU_OP_SUB end 2'b10 : begin `ifdef DISASSEMBLY $display("CMP R%d",IR[1:0]); `endif alu_op <= 4'b0001; // ALU_OP_SUB end 2'b11 : begin `ifdef DISASSEMBLY $display("TST R%d",IR[1:0]); `endif alu_op <= 4'b0100; // ALU_OP_AND end endcase end 3'b100 : begin if (IR[3]==0) begin alu_a <= R[0]; // first input R0 // no 2nd input RESULT_REG <= 0; // result in R0 alu_op <= { 1'b1, IR[2:0] }; `ifdef DISASSEMBLY case(IR[2:0]) 3'b000 : begin $display("SHL"); end 3'b001 : begin $display("SHR"); end 3'b010 : begin $display("SAL"); end 3'b011 : begin $display("SAR"); end 3'b100 : begin $display("ROL"); end 3'b101 : begin $display("ROR"); end 3'b110 : begin $display("RCL"); end 3'b111 : begin $display("RCR"); end endcase `endif state <= STATE_ALU_RESULT_WAIT; end else begin if (IR[2]==0) begin `ifdef DISASSEMBLY $display("PUSH R%d",IR[1:0]); `endif addr <= SP; we <= 1; ioreq <= 0; data_out <= R[IR[1:0]]; SP <= SP - 1; state <= STATE_FETCH_PREP; end else begin `ifdef DISASSEMBLY $display("POP R%d",IR[1:0]); `endif addr <= SP + 1; we <= 0; ioreq <= 0; RESULT_REG <= IR[1:0]; SP <= SP + 1; state <= STATE_LOAD_VALUE_WAIT; end end end 3'b101 : begin `ifdef DISASSEMBLY $display("LOAD R%d,[R%d]", IR[3:2], IR[1:0]); `endif addr <= { DS, R[IR[1:0]] }; we <= 0; ioreq <= 0; RESULT_REG <= IR[3:2]; state <= STATE_LOAD_VALUE_WAIT; end 3'b110 : begin `ifdef DISASSEMBLY $display("STORE [R%d],R%d", IR[3:2], IR[1:0]); `endif addr <= { DS, R[IR[3:2]] }; we <= 1; ioreq <= 0; data_out <= R[IR[1:0]]; state <= STATE_FETCH_PREP; end 3'b111 : begin // Special instuctions case(IR[3:2]) 2'b00 : begin CS <= R[IR[1:0]][3:0]; state <= STATE_FETCH_PREP; `ifdef DISASSEMBLY $display("MOV CS,R%d",IR[1:0]); `endif end 2'b01 : begin DS <= R[IR[1:0]][3:0]; state <= STATE_FETCH_PREP; `ifdef DISASSEMBLY $display("MOV DS,R%d",IR[1:0]); `endif end 2'b10 : begin case(IR[1:0]) 2'b00 : begin `ifdef DISASSEMBLY $display("PUSH CS"); `endif addr <= SP; we <= 1; ioreq <= 0; data_out <= { 4'b0000, CS}; SP <= SP - 1; state <= STATE_FETCH_PREP; end 2'b01 : begin `ifdef DISASSEMBLY $display("PUSH DS"); `endif addr <= SP; we <= 1; ioreq <= 0; data_out <= { 4'b0000, DS}; SP <= SP - 1; state <= STATE_FETCH_PREP; end 2'b10 : begin `ifdef DISASSEMBLY $display("Unused opcode"); `endif end 2'b11 : begin `ifdef DISASSEMBLY $display("Unused opcode"); `endif end endcase state <= STATE_FETCH_PREP; end 2'b11 : begin case(IR[1:0]) 2'b00 : begin `ifdef DISASSEMBLY $display("Unused opcode"); `endif state <= STATE_FETCH_PREP; end 2'b01 : begin `ifdef DISASSEMBLY $display("Unused opcode"); `endif state <= STATE_FETCH_PREP; end 2'b10 : begin `ifdef DISASSEMBLY $display("RET"); `endif addr <= SP + 1; we <= 0; ioreq <= 0; SP <= SP + 1; state <= STATE_RET_VALUE_WAIT; end 2'b11 : begin hlt <= 1; `ifdef DISASSEMBLY $display("HALT"); `endif state <= STATE_FETCH_PREP; end endcase end endcase end endcase end end STATE_FETCH_VALUE_PREP : begin // Sync with memory due to CLK state <= STATE_FETCH_VALUE; end STATE_FETCH_VALUE : begin VALUE <= data_in; state <= STATE_EXECUTE_DBL; end STATE_EXECUTE_DBL : begin case(IR[6:4]) 3'b000 : begin if (IR[3]==0) begin case(IR[2:0]) 3'b000 : begin `ifdef DISASSEMBLY $display("JMP %h ",{ CS, VALUE[7:0] }); `endif jump = 1; end 3'b001 : begin `ifdef DISASSEMBLY $display("JC %h ",{CS, VALUE[7:0] }); `endif jump = (alu_CF==1); end 3'b010 : begin `ifdef DISASSEMBLY $display("JNC %h ",{CS, VALUE[7:0] }); `endif jump = (alu_CF==0); end 3'b011 : begin `ifdef DISASSEMBLY $display("JM %h ",{CS, VALUE[7:0] }); `endif jump = (alu_SF==1); end 3'b100 : begin `ifdef DISASSEMBLY $display("JP %h ",{CS, VALUE[7:0] }); `endif jump = (alu_SF==0); end 3'b101 : begin `ifdef DISASSEMBLY $display("JZ %h ",{CS, VALUE[7:0] }); `endif jump = (alu_ZF==1); end 3'b110 : begin `ifdef DISASSEMBLY $display("JNZ %h ",{CS, VALUE[7:0] }); `endif jump = (alu_ZF==0); end 3'b111 : begin `ifdef DISASSEMBLY $display("Unused opcode %h",IR); `endif jump = 0; end endcase if (jump) begin PC <= { CS, VALUE[7:0] }; addr <= { CS, VALUE[7:0] }; we <= 0; ioreq <= 0; end state <= STATE_FETCH_PREP; end else begin case(IR[2:0]) 3'b000 : begin `ifdef DISASSEMBLY $display("JR %h ", PC + {VALUE[7],VALUE[7],VALUE[7],VALUE[7],VALUE[7:0]} ); `endif jump = 1; end 3'b001 : begin `ifdef DISASSEMBLY $display("JRC %h ",{CS, VALUE[7:0] }); `endif jump = (alu_CF==1); end 3'b010 : begin `ifdef DISASSEMBLY $display("JRNC %h ",{CS, VALUE[7:0] }); `endif jump = (alu_CF==0); end 3'b011 : begin `ifdef DISASSEMBLY $display("JRM %h ",{CS, VALUE[7:0] }); `endif jump = (alu_SF==1); end 3'b100 : begin `ifdef DISASSEMBLY $display("JRP %h ",{CS, VALUE[7:0] }); `endif jump = (alu_SF==0); end 3'b101 : begin `ifdef DISASSEMBLY $display("JRZ %h ",{CS, VALUE[7:0] }); `endif jump = (alu_ZF==1); end 3'b110 : begin `ifdef DISASSEMBLY $display("JRNZ %h ",{CS, VALUE[7:0] }); `endif jump = (alu_ZF==0); end 3'b111 : begin `ifdef DISASSEMBLY $display("Unused opcode %h",IR); `endif jump = 0; end endcase if (jump) begin PC <= PC + {VALUE[7],VALUE[7],VALUE[7],VALUE[7],VALUE[7:0]}; addr <= PC + {VALUE[7],VALUE[7],VALUE[7],VALUE[7],VALUE[7:0]}; we <= 0; ioreq <= 0; end state <= STATE_FETCH_PREP; end end 3'b001 : begin `ifdef DISASSEMBLY $display("JUMP %h ",{ IR[3:0], VALUE[7:0] }); `endif PC <= { IR[3:0], VALUE[7:0] }; addr <= { IR[3:0], VALUE[7:0] }; we <= 0; ioreq <= 0; state <= STATE_FETCH_PREP; end 3'b010 : begin `ifdef DISASSEMBLY $display("CALL %h ",{ IR[3:0], VALUE[7:0] }); `endif FUTURE_PC <= { IR[3:0], VALUE[7:0] }; addr <= SP; we <= 1; ioreq <= 0; data_out <= { 4'b0000, PC[11:8]}; SP <= SP - 1; state <= STATE_PUSH_PC_LOW; end 3'b011 : begin `ifdef DISASSEMBLY $display("MOV SP,%h ",{ IR[3:0], VALUE[7:0] }); `endif SP <= { IR[3:0], VALUE[7:0] }; state <= STATE_FETCH_PREP; end 3'b100 : begin `ifdef DISASSEMBLY $display("IN R%d,[0x%h]",IR[1:0], VALUE); `endif ioreq <= 1; we <= 0; addr <= { 4'b0000, VALUE }; RESULT_REG <= IR[1:0]; state <= STATE_LOAD_VALUE_WAIT; end 3'b101 : begin `ifdef DISASSEMBLY $display("OUT [0x%h],R%d",VALUE,IR[1:0]); `endif ioreq <= 1; we <= 1; addr <= { 4'b0000, VALUE }; data_out <= R[IR[1:0]]; state <= STATE_FETCH_PREP; end 3'b110 : begin // Special instuctions case(IR[1:0]) 2'b00 : begin `ifdef DISASSEMBLY $display("MOV CS,0x%h",VALUE); `endif CS <= VALUE[3:0]; state <= STATE_FETCH_PREP; end 2'b01 : begin `ifdef DISASSEMBLY $display("MOV DS,0x%h",VALUE); `endif DS <= VALUE[3:0]; state <= STATE_FETCH_PREP; end 2'b10 : begin `ifdef DISASSEMBLY $display("Unused opcode %h",IR); `endif state <= STATE_FETCH_PREP; end 2'b11 : begin `ifdef DISASSEMBLY $display("Unused opcode %h",IR); `endif state <= STATE_FETCH_PREP; end endcase end 3'b111 : begin case(IR[3:2]) 2'b00 : begin `ifdef DISASSEMBLY $display("MOV R%d,0x%h",IR[1:0],VALUE); `endif R[IR[1:0]] <= VALUE; state <= STATE_FETCH_PREP; end 2'b01 : begin `ifdef DISASSEMBLY $display("LOAD R%d,[0x%h]",IR[1:0], {DS, VALUE}); `endif addr <= { DS, VALUE }; we <= 0; ioreq <= 0; RESULT_REG <= IR[1:0]; state <= STATE_LOAD_VALUE_WAIT; end 2'b10 : begin `ifdef DISASSEMBLY $display("STORE [0x%h],R%d", {DS, VALUE}, IR[1:0]); `endif addr <= { DS, VALUE }; we <= 1; ioreq <= 0; data_out <= R[IR[1:0]]; state <= STATE_FETCH_PREP; end 2'b11 : begin `ifdef DISASSEMBLY $display("Unused opcode %h",IR); `endif state <= STATE_FETCH_PREP; end endcase end endcase end STATE_LOAD_VALUE_WAIT : begin // Sync with memory due to CLK state <= STATE_LOAD_VALUE; end STATE_LOAD_VALUE : begin R[RESULT_REG] <= data_in; we <= 0; state <= STATE_FETCH_PREP; end STATE_ALU_RESULT_WAIT : begin state <= STATE_ALU_RESULT; end STATE_ALU_RESULT : begin R[RESULT_REG] <= alu_res; state <= STATE_FETCH_PREP; end STATE_PUSH_PC_LOW : begin addr <= SP; we <= 1; ioreq <= 0; data_out <= PC[7:0]; SP <= SP - 1; state <= STATE_JUMP; end STATE_JUMP : begin `ifdef DISASSEMBLY $display("Jumping to %h",FUTURE_PC); `endif PC <= FUTURE_PC; state <= STATE_FETCH_PREP; end STATE_RET_VALUE_WAIT : begin // Sync with memory due to CLK state <= STATE_RET_VALUE; end STATE_RET_VALUE : begin FUTURE_PC <= { 4'b0000, data_in }; we <= 0; state <= STATE_RET_VALUE_WAIT2; addr <= SP + 1; we <= 0; ioreq <= 0; SP <= SP + 1; end STATE_RET_VALUE_WAIT2 : begin // Sync with memory due to CLK state <= STATE_RET_VALUE2; end STATE_RET_VALUE2 : begin FUTURE_PC <= FUTURE_PC | ({ 4'b0000, data_in } << 8); we <= 0; state <= STATE_JUMP; end default : begin state <= STATE_FETCH_PREP; end endcase end end endmodule