svgb/rtl/cpu/ctrl.sv
Koray Yanik fda176d3b5 Complete rewrite from scratch, bootstrap WIP
Rewrite to use several bus multiplexers, resulting into a less messy
microarchitecture (hopefully). Some more room for cleanup though...

Supports every instruction from the bootstrap rom, more or less.
LY hacked at 0x90 to progress through vsync instantly.
No cartridge is present yet, so we will always fail checksum test and lock up.
2023-10-01 23:00:56 +01:00

181 lines
4.1 KiB
Systemverilog

`include "cpu_pkg.svh"
import cpu_pkg::*;
module ctrl (
input logic clk,
input logic nreset,
input logic [ 7:0] rdata_i,
output logic instr_valid_o,
output logic [ 7:0] instr_o [2:0],
output logic pc_we_o, // update PC always
output logic pc_we_cc_o, // update PC conditionally
output pc_src_t pc_src_o,
output cc_t cc_o,
output reg8_t reg8_src_o,
output reg8_t reg8_dest_o,
output reg16_t reg16_sel_o,
output alu_ctrl_t alu_ctrl_o,
output logic alu16_inc_o,
output logic alu16_dec_o,
output addr_src_t addr_src_o,
output bus_ctrl_t bus_x_ctrl_o,
output bus_ctrl_t bus_y_ctrl_o,
output bus16_ctrl_t bus_p_ctrl_o,
output bus16_ctrl_t bus_q_ctrl_o
);
typedef enum {
ST_RESET,
ST_ADDR,
ST_FETCH0,
ST_FETCH1,
ST_FETCH2,
ST_EXEC1,
ST_EXEC2,
ST_EXEC3
} state_t;
logic halt;
logic instr_undef;
logic halted_r;
logic state_en;
state_t state_r;
state_t state_next;
logic state_ex;
logic instr_we [2:0];
logic [ 7:0] instr_r [2:0];
logic [ 7:0] instr [2:0];
logic req_instr1;
logic req_instr2;
logic req_ex2;
logic req_ex3;
addr_src_t instr_addr_src;
logic is_ex1;
logic is_ex2;
logic is_ex3;
logic incr_pc;
logic idec_pc_we;
logic idec_pc_we_cc;
pc_src_t idec_pc_src;
idec idec_inst (
.instr_i (instr),
.is_ex1_i (is_ex1),
.is_ex2_i (is_ex2),
.is_ex3_i (is_ex3),
.req_instr1_o (req_instr1),
.req_instr2_o (req_instr2),
.req_ex2_o (req_ex2),
.req_ex3_o (req_ex3),
.reg8_src_o (reg8_src_o),
.reg8_dest_o (reg8_dest_o),
.reg16_sel_o (reg16_sel_o),
.bus_x_ctrl_o (bus_x_ctrl_o),
.bus_y_ctrl_o (bus_y_ctrl_o),
.bus_p_ctrl_o (bus_p_ctrl_o),
.bus_q_ctrl_o (bus_q_ctrl_o),
.alu_ctrl_o (alu_ctrl_o),
.alu16_inc_o (alu16_inc_o),
.alu16_dec_o (alu16_dec_o),
.addr_src_o (instr_addr_src),
.pc_we_o (idec_pc_we),
.pc_we_cc_o (idec_pc_we_cc),
.cc_o (cc_o),
.pc_src_o (idec_pc_src),
.instr_undef_o (instr_undef)
);
assign is_ex1 = state_r == ST_EXEC1;
assign is_ex2 = state_r == ST_EXEC2;
assign is_ex3 = state_r == ST_EXEC3;
assign halt = instr_undef & is_ex1;
assign state_en = ~halted_r;
always_ff @(posedge clk or negedge nreset) begin
if (!nreset)
state_r <= ST_RESET;
else if (state_en)
state_r <= state_next;
end
always_ff @(posedge clk or negedge nreset) begin
if (!nreset)
halted_r <= 1'b0;
else if (halt)
halted_r <= 1'b1;
end
generate for (genvar i = 0; i < 3; i++) begin : gen_instr_r
always_ff @(posedge clk or negedge nreset) begin
if (!nreset)
instr_r[i] <= 8'h0;
else if (instr_we[i])
instr_r[i] <= rdata_i;
end
end endgenerate
assign instr_we[0] = (state_r == ST_FETCH0);
assign instr_we[1] = (state_r == ST_FETCH1);
assign instr_we[2] = (state_r == ST_FETCH2);
always_comb begin
case (state_r)
ST_RESET: state_next = ST_ADDR;
ST_ADDR: state_next = ST_FETCH0;
ST_FETCH0: state_next = req_instr1 ? ST_FETCH1 : ST_EXEC1;
ST_FETCH1: state_next = req_instr2 ? ST_FETCH2 : ST_EXEC1;
ST_FETCH2: state_next = ST_EXEC1;
ST_EXEC1: state_next = req_ex2 ? ST_EXEC2 : ST_ADDR;
ST_EXEC2: state_next = req_ex3 ? ST_EXEC3 : ST_ADDR;
ST_EXEC3: state_next = ST_ADDR;
endcase
end
assign state_ex = is_ex1 | is_ex2 | is_ex3;
assign incr_pc = (state_r == ST_ADDR) |
(state_r == ST_FETCH0 & req_instr1) |
(state_r == ST_FETCH1 & req_instr2);
assign pc_we_o = state_ex ? idec_pc_we : incr_pc;
assign pc_we_cc_o = state_ex & idec_pc_we_cc;
assign pc_src_o = state_ex ? idec_pc_src : PC_SRC_INC;
assign instr_valid_o = state_ex & ~instr_undef;
assign instr[0] = (state_r == ST_FETCH0) ? rdata_i : instr_r[0];
assign instr[1] = (state_r == ST_FETCH1) ? rdata_i : instr_r[1];
assign instr[2] = (state_r == ST_FETCH2) ? rdata_i : instr_r[2];
assign instr_o = instr;
assign addr_src_o = state_ex ? instr_addr_src : ADDR_SRC_PC;
endmodule : ctrl