hp-saturn/saturn_bus_ctrl.v

459 lines
12 KiB
Coq
Raw Normal View History

2019-02-16 22:38:44 +01:00
`ifndef _SATURN_BUS_CTRL
`define _SATURN_BUS_CTRL
`include "def-clocks.v"
`include "def-buscmd.v"
module saturn_bus_ctrl (
// basic stuff
i_clk,
i_reset,
i_cycle_ctr,
i_en_bus_send,
i_en_bus_recv,
2019-02-17 08:35:26 +01:00
i_en_bus_ecmd,
2019-02-16 22:38:44 +01:00
i_stalled,
i_read_stall,
o_stalled_by_bus,
// bus i/o
i_bus_data,
o_bus_data,
o_bus_strobe,
o_bus_cmd_data,
// interface to the rest of the machine
i_alu_pc,
i_address,
i_load_pc,
i_cmd_load_dp,
i_read_pc,
2019-02-18 07:43:36 +01:00
i_cmd_dp_read,
i_cmd_dp_write,
2019-02-17 15:03:36 +01:00
i_cmd_reset,
i_cmd_config,
2019-02-16 22:38:44 +01:00
i_nibble,
o_nibble
);
input wire [0:0] i_clk;
input wire [0:0] i_reset;
input wire [31:0] i_cycle_ctr;
input wire [0:0] i_en_bus_send;
input wire [0:0] i_en_bus_recv;
2019-02-17 08:35:26 +01:00
input wire [0:0] i_en_bus_ecmd;
2019-02-16 22:38:44 +01:00
input wire [0:0] i_stalled;
input wire [0:0] i_read_stall;
2019-02-17 08:35:26 +01:00
2019-02-16 22:38:44 +01:00
output reg [0:0] o_stalled_by_bus;
input wire [3:0] i_bus_data;
output reg [3:0] o_bus_data;
output reg [0:0] o_bus_strobe;
output reg [0:0] o_bus_cmd_data;
input wire [19:0] i_alu_pc;
input wire [19:0] i_address;
input wire [0:0] i_load_pc;
input wire [0:0] i_cmd_load_dp;
input wire [0:0] i_read_pc;
2019-02-18 07:43:36 +01:00
input wire [0:0] i_cmd_dp_read;
input wire [0:0] i_cmd_dp_write;
2019-02-17 15:03:36 +01:00
input wire [0:0] i_cmd_reset;
input wire [0:0] i_cmd_config;
2019-02-16 22:38:44 +01:00
input wire [3:0] i_nibble;
output reg [3:0] o_nibble;
2019-02-18 11:36:28 +01:00
/******************************************************************************
*
* clocking enables
*
****************************************************************************/
wire en_bus_send;
wire en_bus_recv;
wire en_bus_ecmd;
2019-02-16 22:38:44 +01:00
assign en_bus_send = i_en_bus_send && !i_stalled;
assign en_bus_recv = i_en_bus_recv && !i_stalled;
2019-02-17 08:35:26 +01:00
assign en_bus_ecmd = i_en_bus_ecmd && !i_stalled;
2019-02-16 22:38:44 +01:00
2019-02-18 11:36:28 +01:00
/******************************************************************************
*
* state machine events
*
****************************************************************************/
2019-02-18 11:36:28 +01:00
// declarations
reg [0:0] cmd_pc_read_s;
2019-02-18 07:43:36 +01:00
reg [0:0] dp_read_s;
reg [0:0] cmd_dp_write_s;
reg [0:0] cmd_load_dp_s;
reg [0:0] cmd_config_s;
reg [0:0] cmd_reset_s;
2019-02-18 11:36:28 +01:00
wire [0:0] addr_s;
wire [0:0] do_cmd_pc_read;
wire [0:0] do_display_stalled;
wire [0:0] do_cmd_load_dp;
2019-02-18 07:43:36 +01:00
wire [0:0] do_dp_read_data;
wire [0:0] do_dp_read_data_sc;
wire [0:0] do_dp_read_data_uc;
wire [0:0] do_pc_read_after_dp_read;
wire [0:0] do_cleanup_after_dp_read;
wire [0:0] do_cmd_dp_write;
wire [0:0] do_dp_write_data;
wire [0:0] do_pc_read_after_dp_write;
wire [0:0] cmd_load_dp_dp_write_uc;
wire [0:0] do_cmd_config;
wire [0:0] do_pc_read_after_config;
wire [0:0] cmd_config_sc;
wire [0:0] cmd_config_uc;
wire [0:0] do_cmd_reset;
wire [0:0] do_pc_read_after_reset;
wire [0:0] cmd_reset_sc;
wire [0:0] cmd_reset_uc;
wire [0:0] do_unstall;
2019-02-18 07:43:36 +01:00
wire [0:0] do_cleanup;
2019-02-18 11:36:28 +01:00
// assigns
assign addr_s = addr_cnt == 5;
assign do_cmd_load_dp = i_cmd_load_dp && !cmd_load_dp_s;
2019-02-18 07:43:36 +01:00
assign do_dp_read_data = i_cmd_dp_read && cmd_load_dp_s && (addr_s || dp_read_s);
assign do_dp_read_data_sc = i_cmd_dp_read && cmd_load_dp_s && addr_s && !dp_read_s;
assign do_dp_read_data_uc = i_cmd_dp_read && cmd_load_dp_s && addr_s && dp_read_s;
assign do_pc_read_after_dp_read = i_read_stall && !i_cmd_dp_read && cmd_load_dp_s && !addr_s && dp_read_s;
assign do_cleanup_after_dp_read = !i_read_stall && !i_cmd_dp_read && cmd_load_dp_s && !addr_s && dp_read_s;
assign do_cmd_dp_write = i_cmd_dp_write && cmd_load_dp_s && addr_s && !cmd_dp_write_s;
assign do_dp_write_data = i_cmd_dp_write && cmd_load_dp_s && addr_s && cmd_dp_write_s;
assign do_pc_read_after_dp_write = !i_cmd_dp_write && cmd_load_dp_s && cmd_dp_write_s;
assign cmd_load_dp_dp_write_uc = cmd_load_dp_s && cmd_dp_write_s && cmd_pc_read_s;
assign do_cmd_config = i_cmd_config && !cmd_config_s;
assign do_pc_read_after_config = i_cmd_config && cmd_config_s && addr_s;
assign cmd_config_sc = !o_stalled_by_bus && i_cmd_config && !cmd_config_s;
assign cmd_config_uc = cmd_config_s && cmd_pc_read_s;
assign do_cmd_reset = i_cmd_reset && !cmd_reset_s;
assign do_pc_read_after_reset = i_cmd_reset && cmd_reset_s;
assign cmd_reset_sc = !o_stalled_by_bus && i_cmd_reset && !cmd_reset_s;
assign cmd_reset_uc = cmd_reset_s && cmd_pc_read_s;
assign do_cmd_pc_read = !cmd_pc_read_s &&
2019-02-18 07:43:36 +01:00
(do_pc_read_after_dp_read ||
do_pc_read_after_dp_write ||
do_pc_read_after_config ||
do_pc_read_after_reset);
2019-02-18 11:36:28 +01:00
assign do_display_stalled = en_bus_recv && i_read_stall && !o_stalled_by_bus &&
!(do_cmd_pc_read ||
2019-02-18 07:43:36 +01:00
do_dp_read_data ||
do_pc_read_after_dp_read ||
do_cmd_dp_write ||
do_dp_write_data ||
do_pc_read_after_dp_write);
2019-02-18 07:43:36 +01:00
assign do_unstall = do_dp_read_data_uc ||
cmd_load_dp_dp_write_uc ||
cmd_config_uc ||
cmd_reset_uc;
assign do_cleanup = do_cleanup_after_dp_read ||
cmd_load_dp_dp_write_uc ||
cmd_config_uc ||
cmd_reset_uc;
2019-02-18 11:36:28 +01:00
/******************************************************************************
*
* test rom
*
****************************************************************************/
2019-02-16 22:38:44 +01:00
`ifdef SIM
`define ROMBITS 20
`else
2019-02-18 07:43:36 +01:00
`define ROMBITS 16
2019-02-16 22:38:44 +01:00
`endif
reg [3:0] rom [0:2**`ROMBITS-1];
2019-02-18 11:36:28 +01:00
wire [0:0] last_cmd_pc_read;
wire [0:0] last_cmd_dp_read;
wire [0:0] do_read_from_bus;
wire [0:0] use_pc_as_pointer;
wire [`ROMBITS-1:0] read_pointer;
assign last_cmd_pc_read = (last_cmd == `BUSCMD_PC_READ);
assign last_cmd_dp_read = (last_cmd == `BUSCMD_DP_READ);
assign do_read_from_bus = en_bus_recv && (!i_read_stall || do_dp_read_data) && (last_cmd_pc_read || last_cmd_dp_read);
assign use_pc_as_pointer = last_cmd_pc_read ;
assign read_pointer = use_pc_as_pointer?local_pc[`ROMBITS-1:0]:local_dp[`ROMBITS-1:0];
/******************************************************************************
*
* the controller itself
*
****************************************************************************/
2019-02-16 22:38:44 +01:00
initial begin
2019-02-18 11:36:28 +01:00
// `ifdef SIM
$readmemh("rom-gx-r.hex", rom, 0, 2**`ROMBITS-1);
2019-02-16 22:38:44 +01:00
// $readmemh( "testrom-2.hex", rom);
2019-02-18 11:36:28 +01:00
`ifdef SIM
2019-02-18 07:43:36 +01:00
// $monitor({"o_stalled_by_bus %b | i_read_stall %b | i_cmd_dp_read %b |",
// " cmd_load_dp_s %b | addr_s %b | dp_read_s %b |do_dp_read_data %b"},
// o_stalled_by_bus, i_read_stall, i_cmd_dp_read, cmd_load_dp_s,
// addr_s, dp_read_s, do_dp_read_data);
2019-02-16 22:38:44 +01:00
`endif
end
reg [3:0] last_cmd;
reg [2:0] addr_cnt;
reg [0:0] send_addr;
2019-02-17 08:35:26 +01:00
reg [19:0] local_pc;
reg [19:0] local_dp;
2019-02-16 22:38:44 +01:00
always @(posedge i_clk) begin
2019-02-17 08:35:26 +01:00
if (i_reset) begin
2019-02-17 15:03:36 +01:00
last_cmd <= 0;
2019-02-16 22:38:44 +01:00
o_stalled_by_bus <= 0;
2019-02-17 08:35:26 +01:00
o_bus_strobe <= 0;
o_bus_cmd_data <= 1; // 1 is the default level
addr_cnt <= 0;
2019-02-17 15:03:36 +01:00
send_addr <= 0;
cmd_pc_read_s <= 0;
2019-02-18 07:43:36 +01:00
dp_read_s <= 0;
cmd_dp_write_s <= 0;
cmd_load_dp_s <= 0;
cmd_config_s <= 0;
cmd_reset_s <= 0;
2019-02-17 08:35:26 +01:00
end
2019-02-16 22:38:44 +01:00
2019-02-17 08:35:26 +01:00
/*
*
* sending commands or data to the bus
*
*/
2019-02-16 22:38:44 +01:00
if (en_bus_send) begin
2019-02-17 08:35:26 +01:00
/*
* reset flags
*/
2019-02-18 07:43:36 +01:00
if (do_cleanup) begin
// $display("--------------------------------------------- BUS STATES CLEANUP");
cmd_pc_read_s <= 0;
2019-02-18 07:43:36 +01:00
dp_read_s <= 0;
cmd_dp_write_s <= 0;
cmd_load_dp_s <= 0;
cmd_config_s <= 0;
cmd_reset_s <= 0;
end
/*
* send the PC_READ command to restore the instruction flow
* after a data transfer
*/
if (do_cmd_pc_read) begin
$display("BUS_SEND %0d: [%d] PC_READ", `PH_BUS_SEND, i_cycle_ctr);
o_bus_data <= `BUSCMD_PC_READ;
last_cmd <= `BUSCMD_PC_READ;
cmd_pc_read_s <= 1;
o_bus_strobe <= 1;
end
if ((last_cmd == `BUSCMD_PC_READ) && !i_read_stall)
o_bus_strobe <= 1;
if (do_cmd_dp_write) begin
$display("BUS_SEND %0d: [%d] DP_WRITE", `PH_BUS_SEND, i_cycle_ctr);
o_bus_data <= `BUSCMD_DP_WRITE;
last_cmd <= `BUSCMD_DP_WRITE;
cmd_dp_write_s <= 1;
o_bus_strobe <= 1;
end
/*
* Sending LOAD_PC or LOAD_DP
*/
2019-02-16 22:38:44 +01:00
if (i_load_pc) begin
$display("BUS_SEND %0d: [%d] LOAD_PC %h", `PH_BUS_SEND, i_cycle_ctr, i_address);
2019-02-17 08:35:26 +01:00
o_bus_data <= `BUSCMD_LOAD_PC;
last_cmd <= `BUSCMD_LOAD_PC;
2019-02-17 08:35:26 +01:00
end
if (do_cmd_load_dp) begin
$display("BUS_SEND %0d: [%d] LOAD_DP %h", `PH_BUS_SEND, i_cycle_ctr, i_address);
2019-02-17 08:35:26 +01:00
o_bus_data <= `BUSCMD_LOAD_DP;
last_cmd <= `BUSCMD_LOAD_DP;
cmd_load_dp_s <= 1;
2019-02-17 08:35:26 +01:00
end
2019-02-16 22:38:44 +01:00
if (do_cmd_config) begin
$display("BUS_SEND %0d: [%d] CONFIGURE %h", `PH_BUS_SEND, i_cycle_ctr, i_address);
o_bus_data <= `BUSCMD_CONFIGURE;
last_cmd <= `BUSCMD_CONFIGURE;
cmd_config_s <= 1;
end
if (do_cmd_reset) begin
$display("BUS_SEND %0d: [%d] RESET", `PH_BUS_SEND, i_cycle_ctr);
o_bus_data <= `BUSCMD_RESET;
last_cmd <= `BUSCMD_RESET;
cmd_reset_s <= 1;
o_bus_strobe <= 1;
end
// configure loop to send i_address to the bus
// used for LOAD_PC, LOAD_DP, CONFIGURE,
if (i_load_pc || do_cmd_load_dp || do_cmd_config) begin
2019-02-16 22:38:44 +01:00
o_stalled_by_bus <= 1;
o_bus_cmd_data <= 0;
addr_cnt <= 0;
send_addr <= 1;
o_bus_strobe <= 1;
2019-02-16 22:38:44 +01:00
end
2019-02-17 08:35:26 +01:00
// sending address bits
2019-02-17 08:35:26 +01:00
if (send_addr) begin
2019-02-18 11:36:28 +01:00
`ifdef SIM
$display("BUS_SEND %0d: [%d] addr[%0d] %h =>",
`PH_BUS_SEND, i_cycle_ctr, addr_cnt, i_address[addr_cnt*4+:4]);
2019-02-18 11:36:28 +01:00
`endif
2019-02-17 08:35:26 +01:00
o_bus_data <= i_address[addr_cnt*4+:4];
addr_cnt <= addr_cnt + 1;
o_bus_strobe <= 1;
2019-02-17 08:35:26 +01:00
end
2019-02-18 07:43:36 +01:00
/*
* nothing to do for reading data
*/
/*
* writing data to the bus,
* send DP_WRITE first if necessary
*/
if (do_dp_write_data) begin
2019-02-18 07:43:36 +01:00
if (last_cmd == `BUSCMD_DP_WRITE) begin
$display("BUS_SEND %0d: [%d] WRITE %h =>", `PH_BUS_SEND, i_cycle_ctr, i_nibble);
o_bus_data <= i_nibble;
2019-02-17 08:35:26 +01:00
o_bus_strobe <= 1;
end
end
end
2019-02-16 22:38:44 +01:00
2019-02-17 08:35:26 +01:00
/*
*
* reading data from the bus
*
*/
2019-02-18 11:36:28 +01:00
if (do_read_from_bus) begin
`ifdef SIM
$display("BUS_RECV %0d: [%d] <= READ(%s) [%5h] %h",
`PH_BUS_RECV, i_cycle_ctr, use_pc_as_pointer?"PC":"DP",
read_pointer, rom[read_pointer]);
`endif
o_nibble <= rom[read_pointer];
if (use_pc_as_pointer) local_pc <= local_pc + 1;
else begin
local_dp <= local_dp + 1;
dp_read_s <= 1;
end
end
if (do_display_stalled) begin
$display("BUS_RECV %0d: [%d] STALLED", `PH_BUS_RECV, i_cycle_ctr);
2019-02-18 11:36:28 +01:00
end
2019-02-16 22:38:44 +01:00
2019-02-17 08:35:26 +01:00
/*
*
* resets the bus automatically
*
*/
2019-02-18 11:36:28 +01:00
if (en_bus_recv) begin
2019-02-16 22:38:44 +01:00
o_bus_strobe <= 0;
2019-02-17 08:35:26 +01:00
o_bus_cmd_data <= 1;
2019-02-17 15:03:36 +01:00
end
2019-02-18 11:36:28 +01:00
if (en_bus_ecmd) begin
// stalling and unstalling stuff
2019-02-18 07:43:36 +01:00
if (do_dp_read_data_sc || cmd_reset_sc || cmd_config_sc) begin
// $display("-------------------------------------- STALL");
o_stalled_by_bus <= 1;
end
if (do_unstall) begin
2019-02-18 07:43:36 +01:00
// $display("-------------------------------------- NO - STALL");
o_stalled_by_bus <= 0;
addr_cnt <= 0;
end
if (addr_s) begin
send_addr <= 0;
end
2019-02-17 15:03:36 +01:00
// command automatic switchover
case (last_cmd)
2019-02-18 11:36:28 +01:00
`BUSCMD_NOP: begin end
2019-02-17 15:03:36 +01:00
`BUSCMD_LOAD_PC,
`BUSCMD_LOAD_DP:
2019-02-18 11:36:28 +01:00
if (send_addr && addr_s) begin
// reset the addr count for next time
$display("BUS_ECMD %0d: [%d] <= %s_READ mode",
2019-02-17 15:03:36 +01:00
`PH_BUS_ECMD, i_cycle_ctr,
(last_cmd == `BUSCMD_LOAD_PC)?"PC":"DP");
2019-02-17 15:03:36 +01:00
last_cmd <= (last_cmd == `BUSCMD_LOAD_PC)?`BUSCMD_PC_READ:`BUSCMD_DP_READ;
case (last_cmd)
`BUSCMD_LOAD_PC: local_pc <= i_address;
`BUSCMD_LOAD_DP: local_dp <= i_address;
endcase
send_addr <= 0;
2019-02-18 07:43:36 +01:00
if (!do_dp_read_data) begin
// $display("-------------------------------------- NO - STALL (auto-change mode)");
o_stalled_by_bus <= 0;
end
2019-02-17 15:03:36 +01:00
end
`BUSCMD_PC_READ: begin end
2019-02-18 07:43:36 +01:00
`BUSCMD_DP_READ: begin end
`BUSCMD_DP_WRITE: begin end
`BUSCMD_CONFIGURE: begin end
`BUSCMD_RESET: begin end
default: $display("------------ UNHANDLED BUSCMD %h", last_cmd);
2019-02-17 15:03:36 +01:00
endcase
2019-02-17 08:35:26 +01:00
end
2019-02-17 15:03:36 +01:00
2019-02-16 22:38:44 +01:00
end
endmodule
`endif