`default_nettype none module cpu ( nrst, clk, nmi, irq, o_led, o_state); `include "parameters.vh" localparam STATE_START_1 = 4'hc; localparam STATE_START_2 = 4'hd; localparam STATE_START_3 = 4'he; localparam STATE_START_4 = 4'hf; localparam STATE_START_0 = 4'hb; localparam STATE_FETCH = 0; localparam STATE_EXECUTE = 1; localparam STATE_EXECUTE_ZPX = 2; localparam STATE_EXECUTE_ZP = 3; localparam STATE_FETCH_DATA_2 = 4; localparam STATE_EXECUTE_DATA_2 = 5; localparam STATE_BRANCH = 7; localparam READ_SOURCE_NONE = 2'h0, READ_SOURCE_MEM = 2'h1, READ_SOURCE_ALU = 2'h2; input wire nrst; input wire clk; input wire nmi; input wire irq; output wire [5:0] o_led; output wire [31:0] o_state; reg [7:0] X; reg [7:0] Y; reg [7:0] A; reg [15:0] PC; reg [7:0] S; reg [7:0] P; reg [7:0] tmp; reg [3:0] state; reg [7:0] instr; wire [4:0] address_code; wire [7:0] decoded_instr; wire rst = !nrst; logic mem_wr; wire [7:0] mem_data; wire [15:0] mem_addr_eff; logic [7:0] mem_data_wr; logic [2:0] mem_sel; logic [7:0] mem_aux_addr; logic [7:0] mem_aux_addr_lo; logic [7:0] mem_aux_addr_hi; logic [7:0] alu_op_1; logic [7:0] alu_op_2; logic alu_op_c; logic [2:0] alu_op_sel; wire [7:0] alu_op_result; logic alu_n; logic alu_z; logic alu_v; logic alu_c; reg [7:0] data; reg [15:0] mem_addr; reg [2:0] mem_addr_offset; logic [1:0] pending_a_read; logic [1:0] pending_x_read; logic [1:0] pending_y_read; logic pending_rel_branch; wire is_arith; initial A = 0; initial X = 0; initial Y = 0; initial P = 0; initial S = 8'hff; initial mem_data_wr = 0; initial mem_wr = 0; assign mem_addr_eff = mem_addr; initial pending_a_read = 0; initial pending_x_read = 0; initial pending_y_read = 0; initial pending_rel_branch = 0; initial mem_aux_addr = 0; assign o_state[3:0] = state; assign o_state[31:24] = A; assign o_state[23:16] = X; assign is_arith = (decoded_instr == I_ADC); alu alui( .A(alu_op_1), .B(alu_op_2), .C(alu_op_c), .sel(alu_op_sel), .result(alu_op_result), .r_n(alu_n), .r_z(alu_z), .r_v(alu_v), .r_c(alu_c)); decoder decoder ( .clk(clk), .rst(rst), .instr((state == STATE_EXECUTE ? mem_data : instr)), .address_code(address_code), .decoded(decoded_instr) ); mio mioi(.clk(clk), .i_data(mem_data_wr), .i_addr(mem_addr_eff), .i_wr(mem_wr), .o_data(mem_data), .o_led(o_led)); dmux dmuxi( .sel(mem_sel), .i_pc(PC), .i_a(A), .i_x(X), .i_y(Y), .i_sp(S), .i_aux({mem_aux_addr_hi, mem_aux_addr_lo}), .o_mux(mem_addr)); initial state = STATE_FETCH; initial PC = 0; initial mem_sel = 0; always_comb begin mem_aux_addr_lo = mem_aux_addr; mem_aux_addr_hi = 0; if (address_code == ADDR_MODE_ABSOLUTE) begin mem_aux_addr_hi = mem_data; end else if (address_code == ADDR_MODE_ZP) begin mem_aux_addr_lo = mem_data; end else if (address_code == ADDR_MODE_INDEXED_ZP) begin mem_aux_addr_lo = mem_aux_addr; end end always_comb begin alu_op_1 = 0; alu_op_2 = 0; alu_op_c = 0; alu_op_sel = 0; if (decoded_instr == I_DEX) begin alu_op_1 = X; alu_op_2 = 1'b1; alu_op_c = 1'b1; alu_op_sel = OP_SUB; end else if (decoded_instr == I_DEY) begin alu_op_1 = Y; alu_op_2 = 1'b1; alu_op_c = 1'b1; alu_op_sel = OP_SUB; end else if (address_code == ADDR_MODE_IMMEDIATE || address_code == ADDR_MODE_ABSOLUTE || address_code == ADDR_MODE_ZP || address_code == ADDR_MODE_INDEXED_ZP) begin alu_op_1 = A; alu_op_2 = mem_data; if (decoded_instr == I_EOR) begin alu_op_c = 0; alu_op_sel = OP_EOR; end else if (decoded_instr == I_AND) begin alu_op_c = 0; alu_op_sel = OP_AND; end else if (decoded_instr == I_ORA) begin alu_op_c = 0; alu_op_sel = OP_OR; end else if (decoded_instr == I_ADC) begin alu_op_c = P[P_C]; alu_op_sel = OP_ADC; end end end always @(posedge clk) if (state == STATE_FETCH) begin if (pending_a_read == READ_SOURCE_MEM) begin A <= mem_data; P[P_Z] <= (mem_data == 0); P[P_N] <= mem_data[7]; end else if (pending_a_read == READ_SOURCE_ALU) begin A <= alu_op_result; P[P_Z] <= alu_z; P[P_N] <= alu_n; if (is_arith) begin P[P_C] <= alu_c; P[P_V] <= alu_v; end end pending_a_read <= READ_SOURCE_NONE; if (pending_x_read == READ_SOURCE_MEM) begin X <= mem_data; P[P_Z] <= (mem_data == 0); P[P_N] <= mem_data[7]; end else if (pending_x_read == READ_SOURCE_ALU) begin X <= alu_op_result; P[P_Z] <= alu_z; P[P_N] <= alu_n; end pending_x_read <= READ_SOURCE_NONE; if (pending_y_read == READ_SOURCE_MEM) begin Y <= mem_data; P[P_Z] <= (mem_data == 0); P[P_N] <= mem_data[7]; end else if (pending_y_read == READ_SOURCE_ALU) begin Y <= alu_op_result; P[P_Z] <= alu_z; P[P_N] <= alu_n; end pending_y_read <= READ_SOURCE_NONE; PC <= PC + 1; state <= STATE_EXECUTE; end else if (state == STATE_EXECUTE) begin instr <= mem_data; if (address_code == ADDR_MODE_ABSOLUTE) begin PC <= PC + 1; state <= STATE_FETCH_DATA_2; end else if (address_code == ADDR_MODE_IMMEDIATE) begin if (decoded_instr == I_LDX) begin pending_x_read <= READ_SOURCE_MEM; end else if (decoded_instr == I_LDA) begin pending_a_read <= READ_SOURCE_MEM; end else if (decoded_instr == I_EOR || decoded_instr == I_AND || decoded_instr == I_ORA || decoded_instr == I_ADC) begin pending_a_read <= READ_SOURCE_ALU; end state <= STATE_FETCH; PC <= PC + 1; end else if (address_code == ADDR_MODE_ZP) begin mem_sel <= DMUX_AUX; state <= STATE_EXECUTE_ZP; PC <= PC + 1; end else if (address_code == ADDR_MODE_INDEXED_ZP) begin mem_sel <= DMUX_AUX; state <= STATE_EXECUTE_ZPX; PC <= PC + 1; end else if (address_code == ADDR_MODE_IMPLIED) begin if (decoded_instr == I_DEX) begin pending_x_read <= READ_SOURCE_ALU; state <= STATE_FETCH; end else if (decoded_instr == I_DEY) begin pending_y_read <= READ_SOURCE_ALU; state <= STATE_FETCH; end end else if (decoded_instr == I_BNE) begin if(!P[P_Z]) begin pending_rel_branch = 1'b1; state <= STATE_BRANCH; end else begin state <= STATE_FETCH; PC <= PC + 1; end end end else if (state == STATE_EXECUTE_ZPX) begin mem_aux_addr <= mem_data + X; state <= STATE_EXECUTE_ZP; end else if (state == STATE_EXECUTE_ZP) begin if (address_code == ADDR_MODE_ZP) begin mem_aux_addr <= mem_data; end mem_sel <= DMUX_PC; pending_a_read <= READ_SOURCE_ALU; state <= STATE_FETCH; end else if (state == STATE_BRANCH) begin if (pending_rel_branch) begin PC <= PC + {{8{mem_data[7]}}, mem_data[7:0]} + 1'b1; end pending_rel_branch <= 0; state <= STATE_FETCH; end else if (state == STATE_FETCH_DATA_2) begin if (address_code == ADDR_MODE_ABSOLUTE) begin mem_aux_addr[7:0] <= mem_data; mem_sel <= DMUX_AUX; state <= STATE_EXECUTE_DATA_2; PC <= PC + 1'b1; end end else if (state == STATE_EXECUTE_DATA_2) begin if (address_code == ADDR_MODE_ABSOLUTE) begin mem_sel <= DMUX_PC; state <= STATE_FETCH; if (decoded_instr == I_EOR || decoded_instr == I_AND || decoded_instr == I_ORA || decoded_instr == I_ADC) begin pending_a_read <= READ_SOURCE_ALU; end else if(decoded_instr == I_JMP) begin PC <= mem_addr; end end end always_comb begin mem_data_wr = 0; mem_wr = 0; if (state == STATE_EXECUTE_DATA_2) begin if (address_code == ADDR_MODE_ABSOLUTE) begin if(decoded_instr == I_STA) begin mem_data_wr = A; mem_wr = 1'b1; end end end else if (state == STATE_EXECUTE_ZP) begin if(decoded_instr == I_STA) begin mem_data_wr = A; mem_wr = 1'b1; end end end `ifdef FORMAL logic f_past_valid; logic f2_past_valid; logic f3_past_valid; logic f4_past_valid; logic f5_past_valid; initial f_past_valid = 0; initial f2_past_valid = 0; initial f3_past_valid = 0; initial f4_past_valid = 0; initial f5_past_valid = 0; always @(posedge clk) f_past_valid <= 1; always @(posedge clk) if (f_past_valid) f2_past_valid <= 1; always @(posedge clk) if (f2_past_valid) f3_past_valid <= 1; always @(posedge clk) if (f3_past_valid) f4_past_valid <= 1; always @(posedge clk) if (f4_past_valid) f5_past_valid <= 1; always @(posedge clk) if (f2_past_valid && state == STATE_EXECUTE) if ($past(decoded_instr) == I_DEX) begin assert(X == $past(X, 3) - 8'd1); assert($past(PC) == $past(PC, 3) + 16'd1); assert(P[P_Z] == (X == 0)); assert(P[P_N] == (X[7] == 1)); assert($stable(P[P_C])); assert($stable(P[P_V])); assert($stable(P[P_B])); assert($stable(P[P_D])); assert($stable(P[P_I])); end always @(posedge clk) if (f2_past_valid && state == STATE_EXECUTE) if ($past(decoded_instr) == I_DEY) begin assert(Y == $past(Y, 3) - 8'd1); assert($past(PC) == $past(PC, 3) + 16'd1); assert(P[P_Z] == (Y == 0)); assert(P[P_N] == (Y[7] == 1)); assert($stable(P[P_C])); assert($stable(P[P_V])); assert($stable(P[P_B])); assert($stable(P[P_D])); assert($stable(P[P_I])); end always @(posedge clk) if (f2_past_valid && state == STATE_EXECUTE) if ($past(decoded_instr) == I_LDX && $past(address_code) == ADDR_MODE_IMMEDIATE) begin assert(X == $past(mem_data)); assert(P[P_Z] == (X == 0)); assert(P[P_N] == (X[7] == 1)); if (f3_past_valid) begin assert($past(PC) == $past(PC, 3) + 16'd2); end end always @(posedge clk) if (f2_past_valid && state == STATE_EXECUTE) if ($past(decoded_instr) == I_LDA && $past(address_code) == ADDR_MODE_IMMEDIATE) begin assert(A == $past(mem_data)); assert(P[P_Z] == (A == 0)); assert(P[P_N] == (A[7] == 1)); if (f3_past_valid) begin assert($past(PC) == $past(PC, 3) + 16'd2); end end logic [15:0] f_abs_address; initial f_abs_address = 0; always_comb begin f_abs_address = 0; if (f_past_valid) begin f_abs_address[15:8] = mem_data; f_abs_address[7:0] = mem_aux_addr; end end always @(posedge clk) if (f2_past_valid && state == STATE_EXECUTE_DATA_2) if ($past(decoded_instr) == I_STA && $past(address_code) == ADDR_MODE_ABSOLUTE) begin assert(mem_wr == 1'b1); assert(mem_data_wr == A); assert(f_abs_address == mem_addr_eff); if (f3_past_valid) begin assert(PC == $past(PC, 3) + 16'd3); end end always @(posedge clk) if (f4_past_valid && state == STATE_EXECUTE && $past(state) == STATE_FETCH && ($past(state, 2) == STATE_EXECUTE_DATA_2 || $past(state, 2) == STATE_EXECUTE_ZP || $past(state, 2) == STATE_EXECUTE)) begin if ($past(decoded_instr, 2) == I_EOR) assert(A == ($past(A) ^ $past(mem_data))); else if ($past(decoded_instr, 2) == I_AND) assert(A == ($past(A) & $past(mem_data))); else if ($past(decoded_instr, 2) == I_ORA) assert(A == ($past(A) | $past(mem_data))); else if ($past(decoded_instr, 2) == I_ADC) assert(A == ($past(A) + $past(mem_data) + $past(P[P_C]))); else assume(0); assert($past(mem_wr, 2) == 1'b0); if ($past(address_code, 2) == ADDR_MODE_ABSOLUTE) assert($past(f_abs_address, 2) == $past(mem_addr_eff, 2)); else if ($past(address_code, 2) == ADDR_MODE_ZP) assert($past(mem_data, 2) == $past(mem_addr_eff, 2)); else if ($past(address_code, 2) == ADDR_MODE_INDEXED_ZP) assert((($past(mem_data, 3) + $past(X, 3)) & 16'hff) == $past(mem_addr_eff, 2)); assert(P[P_Z] == (A == 0)); assert(P[P_N] == (A[7] == 1)); if ($past(decoded_instr) == I_ADC) begin assert(P[P_C] == ({1'b0, $past(A)} + {1'b0, $past(mem_data)} + {8'b0, $past(P[P_C])} >= 9'h100)); assert(P[P_V] == (($past(A[7]) ^ A[7]) & ($past(mem_data[7]) ^ A[7]))); end else begin assert(P[P_C] == $past(P[P_C], 2)); assert(P[P_V] == $past(P[P_V], 2)); end assert(P[P_B] == $past(P[P_B], 2)); assert(P[P_D] == $past(P[P_D], 2)); assert(P[P_I] == $past(P[P_I], 2)); assert(S == $past(S, 2)); assert(X == $past(X, 2)); assert(Y == $past(Y, 2)); if (f3_past_valid) begin if ($past(address_code, 2) == ADDR_MODE_ABSOLUTE) assert($past(PC, 2) == $past(PC, 5) + 16'd3); else if ($past(address_code, 2) == ADDR_MODE_ZP) assert($past(PC, 2) == $past(PC, 4) + 16'd2); else if ($past(address_code, 2) == ADDR_MODE_INDEXED_ZP) assert($past(PC, 2) == $past(PC, 5) + 16'd2); else if ($past(address_code, 2) == ADDR_MODE_IMMEDIATE) assert($past(PC, 1) == $past(PC, 3) + 16'd2); end end always @(posedge clk) if (f2_past_valid && state == STATE_FETCH) if ($past(decoded_instr) == I_BNE && $past(address_code) == ADDR_MODE_RELATIVE) begin if ($past(state) == STATE_BRANCH) begin assert(PC == $past(PC) + {{8{$past(mem_data[7])}}, mem_data[7:0]} + 1'b1); assert(!$past(P[P_Z])); end else begin assert(PC == $past(PC) + 1'b1); assert($past(P[P_Z])); end end always @(posedge clk) if (f3_past_valid && state == STATE_FETCH) if ($past(decoded_instr) == I_JMP && $past(address_code) == ADDR_MODE_ABSOLUTE) begin assert(PC == $past(f_abs_address)); assert(P == $past(P, 3)); assert(S == $past(S, 3)); assert(A == $past(A, 3)); assert(X == $past(X, 3)); assert(Y == $past(Y, 3)); end always @(posedge clk) if (f_past_valid) assume(state != $past(state)); always @(posedge clk) cover(state == STATE_EXECUTE); `endif endmodule