hp-saturn/saturn_bus_controller.v
Raphael Jacquot c62d562008 make it so that execution of bus programs happen
in the same cycle as the instruction
modify the way jump and rtn are handled
add some registers to the debugger
2019-03-14 16:37:51 +01:00

352 lines
No EOL
11 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
module saturn_bus_controller (
i_clk,
i_clk_en,
i_reset,
i_phases,
i_phase,
i_cycle_ctr,
o_bus_clk_en,
o_bus_is_data,
o_bus_nibble_out,
i_bus_nibble_in,
o_debug_cycle,
o_instr_decoded,
o_char_to_send,
o_char_counter,
o_char_valid,
o_char_send,
i_serial_busy,
o_halt
);
input wire [0:0] i_clk;
input wire [0:0] i_clk_en;
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;
output reg [0:0] o_bus_clk_en;
output reg [0:0] o_bus_is_data;
output reg [3:0] o_bus_nibble_out;
input wire [3:0] i_bus_nibble_in;
output wire [0:0] o_debug_cycle;
output wire [0:0] o_instr_decoded;
assign o_instr_decoded = dec_instr_decoded;
output wire [7:0] o_char_to_send;
output wire [9:0] o_char_counter;
output wire [0:0] o_char_valid;
output wire [0:0] o_char_send;
input wire [0:0] i_serial_busy;
output wire [0:0] o_halt;
/**************************************************************************************************
*
* master control unit
*
*************************************************************************************************/
saturn_control_unit control_unit (
.i_clk (i_clk),
.i_clk_en (bus_clk_en),
.i_reset (i_reset),
.i_phases (i_phases),
.i_phase (i_phase),
.i_cycle_ctr (i_cycle_ctr),
.i_bus_busy (bus_busy),
.o_program_address (ctrl_unit_prog_addr),
.i_program_address (bus_prog_addr),
.o_program_data (ctrl_unit_prog_data),
.o_no_read (ctrl_unit_no_read),
.i_nibble (i_bus_nibble_in),
.o_error (ctrl_unit_error),
.o_alu_busy (alu_busy),
.o_exec_unit_busy (exec_unit_busy),
/* debugger interface */
.o_current_pc (ctrl_current_pc),
.o_reg_alu_mode (ctrl_reg_alu_mode),
.o_reg_carry (ctrl_reg_carry),
.o_reg_hst (ctrl_reg_hst),
.o_reg_st (ctrl_reg_st),
.o_reg_p (ctrl_reg_p),
.i_dbg_register (dbg_register),
.i_dbg_reg_ptr (dbg_reg_ptr),
.o_dbg_reg_nibble (ctrl_reg_nibble),
.i_dbg_rstk_ptr (dbg_rstk_ptr),
.o_dbg_rstk_val (ctrl_rstk_val),
.o_reg_rstk_ptr (ctrl_reg_rstk_ptr),
.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 [0:0] ctrl_unit_error;
wire [4:0] ctrl_unit_prog_addr;
wire [4:0] ctrl_unit_prog_data;
wire [0:0] ctrl_unit_no_read;
wire [0:0] alu_busy;
wire [0:0] exec_unit_busy;
/* debugger insterface */
wire [19:0] ctrl_current_pc;
wire [0:0] ctrl_reg_alu_mode;
wire [0:0] ctrl_reg_carry;
wire [3:0] ctrl_reg_hst;
wire [15:0] ctrl_reg_st;
wire [3:0] ctrl_reg_p;
wire [3:0] ctrl_reg_nibble;
wire [19:0] ctrl_rstk_val;
wire [2:0] ctrl_reg_rstk_ptr;
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;
/**************************************************************************************************
*
* debugger module
*
*************************************************************************************************/
saturn_debugger debugger (
.i_clk (i_clk),
.i_clk_en (i_clk_en),
.i_reset (i_reset),
.i_phases (i_phases),
.i_phase (i_phase),
.i_cycle_ctr (i_cycle_ctr),
.o_debug_cycle (dbg_debug_cycle),
.i_alu_busy (alu_busy),
.i_exec_unit_busy (exec_unit_busy),
/* debugger interface */
.i_current_pc (ctrl_current_pc),
.i_reg_alu_mode (ctrl_reg_alu_mode),
.i_reg_carry (ctrl_reg_carry),
.i_reg_hst (ctrl_reg_hst),
.i_reg_st (ctrl_reg_st),
.i_reg_p (ctrl_reg_p),
.o_dbg_register (dbg_register),
.o_dbg_reg_ptr (dbg_reg_ptr),
.i_dbg_reg_nibble (ctrl_reg_nibble),
.o_dbg_rstk_ptr (dbg_rstk_ptr),
.i_dbg_rstk_val (ctrl_rstk_val),
.i_reg_rstk_ptr (ctrl_reg_rstk_ptr),
.i_alu_reg_dest (dec_alu_reg_dest),
.i_alu_reg_src_1 (dec_alu_reg_src_1),
.i_alu_reg_src_2 (dec_alu_reg_src_2),
.i_alu_imm_value (dec_alu_imm_value),
.i_alu_opcode (dec_alu_opcode),
.i_instr_type (dec_instr_type),
.i_instr_decoded (dec_instr_decoded),
.i_instr_execute (dec_instr_execute),
.i_bus_busy (bus_busy),
.o_char_to_send (o_char_to_send),
.o_char_counter (o_char_counter),
.o_char_valid (o_char_valid),
.o_char_send (o_char_send),
.i_serial_busy (i_serial_busy),
.i_bus_debug (dbg_bus_info),
.i_bus_action (dbg_bus_action),
.i_bus_data (dbg_bus_data)
);
wire [4:0] dbg_register;
wire [3:0] dbg_reg_ptr;
wire [2:0] dbg_rstk_ptr;
wire [0:0] dbg_debug_cycle;
assign o_debug_cycle = dbg_debug_cycle;
reg [0:0] dbg_bus_info;
reg [1:0] dbg_bus_action;
reg [3:0] dbg_bus_data;
/**************************************************************************************************
*
* the bus controller module
*
*************************************************************************************************/
/*
* local registers
*/
reg [0:0] bus_error;
reg [0:0] bus_busy;
wire [0:0] bus_clk_en = !o_debug_cycle && i_clk_en;
/*
* program list for the bus controller
* this is used for the control unit to send the bus controller
* the list of things that need to be done for long sequences
*/
reg [4:0] bus_prog_addr;
wire [0:0] more_to_write;
assign more_to_write = (bus_prog_addr != ctrl_unit_prog_addr);
/*
* this should come from the debugger
*/
assign o_halt = bus_error || ctrl_unit_error;
initial begin
dbg_bus_info = 1'b0;
dbg_bus_action = 2'b00;
dbg_bus_data = 4'h0;
bus_error = 1'b0;
bus_prog_addr = 5'd0;
bus_busy = 1'b1;
end
// wire [0:0] bus_read_valid = bus_clk_en && i_phases[2] && !bus_busy && !alu_busy;
// wire [0:0] bus_write_valid = bus_clk_en && i_phases[1] && bus_busy && !alu_busy;
/*
* bus chronograms
*
* The bus works on a 4 phase system
*
*/
always @(posedge i_clk) begin
dbg_bus_action <= 2'b11;
dbg_bus_data <= 4'h0;
if (bus_clk_en && !alu_busy) begin
case (i_phases)
4'b0001:
begin
/*
* in this phase, we can send a command or data from the processor
*/
// $display("BUSCTRL %0d: [%d] cycle start", i_phase, i_cycle_ctr);
if (more_to_write) begin
$write("BUSCTRL %0d: [%d] %0d|%0d : %5b ", i_phase, i_cycle_ctr,
bus_prog_addr, ctrl_unit_prog_addr, ctrl_unit_prog_data);
if (ctrl_unit_prog_data[4]) $write("CMD : ");
else $write("DATA : ");
$write("%h\n", ctrl_unit_prog_data[3:0]);
bus_prog_addr <= bus_prog_addr + 5'b1;
o_bus_is_data <= !ctrl_unit_prog_data[4];
o_bus_nibble_out <= ctrl_unit_prog_data[3:0];
o_bus_clk_en <= 1'b1;
bus_busy <= 1'b1;
/* data for the debugger */
dbg_bus_info <= 1'b1;
dbg_bus_action <= { 1'b0, ctrl_unit_prog_data[4]};
dbg_bus_data <= ctrl_unit_prog_data[3:0];
end
/*
* nothing to send, see if we can read, and do it
*/
if (!more_to_write && !ctrl_unit_no_read) begin
// $display("BUSCTRL %0d: [%d] setting up read", i_phase, i_cycle_ctr);
o_bus_is_data <= 1'b1;
o_bus_clk_en <= 1'b1;
end
end
4'b0010:
begin
/*
* this phase is reserved for reading data from the bus
*/
if (o_bus_clk_en) begin
// $display("BUSCTRL %0d: [%d] lowering bus clock_en", i_phase, i_cycle_ctr);
o_bus_clk_en <= 1'b0;
end
/* memories write to the bus in this state */
end
4'b0100:
begin
/*
* this phase is when the instruction decoder does it's job
*/
if (!more_to_write && bus_busy) begin
$display("BUSCTRL %0d: [%d] done sending the entire program", i_phase, i_cycle_ctr);
bus_busy <= 1'b0;
end
/* at that poing, weread data in for the debugger */
if (!bus_busy && !alu_busy) begin
dbg_bus_info <= 1'b1;
dbg_bus_action <= 2'b10;
dbg_bus_data <= i_bus_nibble_in;
$display("BUSCTRL %0d: [%d] READ %h", i_phase, i_cycle_ctr, i_bus_nibble_in);
end
end
4'b1000:
begin
/*
* instructions that can be handled in one clock are done here, otherwise, we start the ALU
*/
end
default: begin end // other states should not exist
endcase
end
if (dbg_bus_info)
dbg_bus_info <= 1'b0;
if (i_reset) begin
bus_error <= 1'b0;
bus_prog_addr <= 5'd0;
bus_busy <= 1'b1;
end
end
endmodule