hp-saturn/saturn_control_unit.v
Raphaël Jacquot eeb5150159 add the beginnings of a PC and RSTK handler
fix bad maths in the rom-gx-r module
wire in the PC in the debugger and the control unit
add an execute flag, to start execution of partially
decoded instructions that need reading data from the
instruction stream
2019-03-03 09:33:42 +01:00

340 lines
9.4 KiB
Verilog

/*
(c) Raphaël Jacquot 2019
This file is part of hp_saturn.
hp_saturn is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
hp_saturn is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <https://www.gnu.org/licenses/>.
*/
`default_nettype none
`include "saturn_def_buscmd.v"
`include "saturn_def_alu.v"
module saturn_control_unit (
i_clk,
i_reset,
i_phases,
i_phase,
i_cycle_ctr,
i_debug_cycle,
i_bus_busy,
o_program_data,
o_program_address,
i_program_address,
o_no_read,
i_nibble,
o_error,
/* debugger interface */
o_current_pc,
o_alu_reg_dest,
o_alu_reg_src_1,
o_alu_reg_src_2,
o_alu_imm_value,
o_alu_opcode,
o_instr_type,
o_instr_decoded
);
input wire [0:0] i_clk;
input wire [0:0] i_reset;
input wire [3:0] i_phases;
input wire [1:0] i_phase;
input wire [31:0] i_cycle_ctr;
input wire [0:0] i_debug_cycle;
input wire [0:0] i_bus_busy;
output wire [4:0] o_program_data;
output wire [4:0] o_program_address;
input wire [4:0] i_program_address;
output reg [0:0] o_no_read;
input wire [3:0] i_nibble;
output wire [0:0] o_error;
assign o_error = control_unit_error;
/* debugger interface */
output wire [19:0] o_current_pc;
output wire [4:0] o_alu_reg_dest;
output wire [4:0] o_alu_reg_src_1;
output wire [4:0] o_alu_reg_src_2;
output wire [3:0] o_alu_imm_value;
output wire [4:0] o_alu_opcode;
output wire [3:0] o_instr_type;
output wire [0:0] o_instr_decoded;
assign o_current_pc = reg_PC;
assign o_alu_reg_dest = dec_alu_reg_dest;
assign o_alu_reg_src_1 = dec_alu_reg_src_1;
assign o_alu_reg_src_2 = dec_alu_reg_src_2;
assign o_alu_imm_value = dec_alu_imm_value;
assign o_alu_opcode = dec_alu_opcode;
assign o_instr_type = dec_instr_type;
assign o_instr_decoded = dec_instr_decoded;
/**************************************************************************************************
*
* decoder module
*
*************************************************************************************************/
saturn_inst_decoder instruction_decoder(
.i_clk (i_clk),
.i_reset (i_reset),
.i_phases (i_phases),
.i_phase (i_phase),
.i_cycle_ctr (i_cycle_ctr),
.i_debug_cycle (i_debug_cycle),
.i_bus_busy (i_bus_busy),
.i_nibble (i_nibble),
.i_reg_p (reg_P),
.i_current_pc (reg_PC),
.o_alu_reg_dest (dec_alu_reg_dest),
.o_alu_reg_src_1 (dec_alu_reg_src_1),
.o_alu_reg_src_2 (dec_alu_reg_src_2),
.o_alu_imm_value (dec_alu_imm_value),
.o_alu_opcode (dec_alu_opcode),
.o_instr_type (dec_instr_type),
.o_instr_decoded (dec_instr_decoded),
.o_instr_execute (dec_instr_execute)
);
wire [4:0] dec_alu_reg_dest;
wire [4:0] dec_alu_reg_src_1;
wire [4:0] dec_alu_reg_src_2;
wire [3:0] dec_alu_imm_value;
wire [4:0] dec_alu_opcode;
wire [3:0] dec_instr_type;
wire [0:0] dec_instr_decoded;
wire [0:0] dec_instr_execute;
/*
* wires for decode shortcuts
*/
wire [0:0] reg_dest_p;
wire [0:0] reg_src_1_imm;
wire [0:0] aluop_copy;
assign reg_dest_p = (dec_alu_reg_dest == `ALU_REG_P);
assign reg_src_1_imm = (dec_alu_reg_src_1 == `ALU_REG_IMM);
assign aluop_copy = (dec_alu_opcode == `ALU_OP_COPY);
wire [0:0] inst_alu_p_eq_n;
wire [0:0] inst_alu_other;
assign inst_alu_p_eq_n = aluop_copy && reg_dest_p && reg_src_1_imm;
assign inst_alu_other = !(inst_alu_p_eq_n);
/**************************************************************************************************
*
* registers module (contains A, B, C, D, R0, R1, R2, R3, R4)
*
*************************************************************************************************/
/**************************************************************************************************
*
* PC and RSTK module
*
*************************************************************************************************/
saturn_regs_pc_rstk regs_pc_rstk (
.i_clk (i_clk),
.i_reset (i_reset),
.i_phases (i_phases),
.i_phase (i_phase),
.i_cycle_ctr (i_cycle_ctr),
.i_debug_cycle (i_debug_cycle),
.i_bus_busy (i_bus_busy),
.i_nibble (i_nibble),
.o_current_pc (reg_PC)
);
/**************************************************************************************************
*
* other processor registers
*
*************************************************************************************************/
reg [3:0] reg_P;
wire [19:0] reg_PC;
/**************************************************************************************************
*
* the control unit
*
*************************************************************************************************/
reg [0:0] control_unit_error;
reg [0:0] just_reset;
reg [0:0] control_unit_ready;
reg [4:0] bus_program[0:31];
reg [4:0] bus_prog_addr;
reg [2:0] addr_nibble_ptr;
assign o_program_data = bus_program[i_program_address];
assign o_program_address = bus_prog_addr;
initial begin
/* control variables */
o_no_read = 1'b0;
control_unit_error = 1'b0;
just_reset = 1'b1;
control_unit_ready = 1'b0;
bus_prog_addr = 5'd0;
addr_nibble_ptr = 3'd0;
/* registers */
reg_P = 4'b0;
end
always @(posedge i_clk) begin
/************************
*
* we're just starting, load the PC into the controller and modules
* this could also be used when loading the PC on jumps, need to identify conditions
*
*/
if (!i_debug_cycle && just_reset && i_phases[3]) begin
/* this happend right after reset */
`ifdef SIM
if (just_reset) begin
$display("CTRL %0d: [%d] we are in the control unit", i_phase, i_cycle_ctr);
`endif
just_reset <= 1'b0;
end
/* this loads the PC to the modules */
bus_program[bus_prog_addr] <= {1'b1, `BUSCMD_LOAD_PC };
`ifdef SIM
$display("CTRL %0d: [%d] pushing LOAD_PC command to pos %d", i_phase, i_cycle_ctr, bus_prog_addr);
`endif
addr_nibble_ptr <= 3'b0;
bus_prog_addr <= bus_prog_addr + 1;
end
/* loop to fill the initial PC value in the program */
if (!i_debug_cycle && !control_unit_ready && (bus_prog_addr != 5'b0)) begin
/*
* this should load the actual PC values...
*/
bus_program[bus_prog_addr] <= {1'b0, reg_PC[addr_nibble_ptr*4+:4]};
addr_nibble_ptr <= addr_nibble_ptr + 3'd1;
bus_prog_addr <= bus_prog_addr + 5'd1;
`ifdef SIM
$write("CTRL %0d: [%d] pushing ADDR : prog[%0d] <= PC[%0d] (%h)", i_phase, i_cycle_ctr,
bus_prog_addr, addr_nibble_ptr, {1'b0, reg_PC[addr_nibble_ptr*4+:4]});
`endif
if (bus_prog_addr == 5'd5) begin
control_unit_ready <= 1'b1;
`ifdef SIM
$write(" done");
`endif
end
`ifdef SIM
$write("\n");
`endif
end
/************************
*
* main execution loop
*
*/
if (!i_debug_cycle && control_unit_ready && !i_bus_busy) begin
// `ifdef SIM
// $display("CTRL %0d: [%d] starting to do things", i_phase, i_cycle_ctr);
// `endif
if (i_cycle_ctr == 10) begin
control_unit_error <= 1'b1;
$display("CTRL %0d: [%d] enough cycles for now", i_phase, i_cycle_ctr);
end
if (i_phases[2]) begin
$display("CTRL %0d: [%d] interpreting %h", i_phase, i_cycle_ctr, i_nibble);
end
if (i_phases[3] && dec_instr_execute) begin
case (dec_instr_type)
`INSTR_TYPE_NOP: begin
$display("CTRL %0d: [%d] NOP instruction", i_phase, i_cycle_ctr);
end
`INSTR_TYPE_ALU: begin
$display("CTRL %0d: [%d] ALU instruction", i_phase, i_cycle_ctr);
/*
* treat special cases
*/
if (inst_alu_p_eq_n) begin
$display("CTRL %0d: [%d] exec : P= %h", i_phase, i_cycle_ctr, dec_alu_imm_value);
reg_P <= dec_alu_imm_value;
end
/*
* the general case
*/
end
default: begin
$display("CTRL %0d: [%d] unsupported instruction", i_phase, i_cycle_ctr);
end
endcase
end
end
if (i_reset) begin
/* control variables */
o_no_read <= 1'b0;
control_unit_error <= 1'b0;
just_reset <= 1'b1;
control_unit_ready <= 1'b0;
bus_prog_addr <= 5'd0;
addr_nibble_ptr <= 3'd0;
/* registers */
reg_P <= 4'b0;
end
end
endmodule