mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-26 23:54:15 +01:00
442 lines
13 KiB
Systemverilog
442 lines
13 KiB
Systemverilog
module n64_pi (
|
|
input clk,
|
|
input reset,
|
|
|
|
mem_bus.controller mem_bus,
|
|
n64_reg_bus.controller reg_bus,
|
|
|
|
n64_scb.pi n64_scb,
|
|
|
|
input n64_reset,
|
|
input n64_nmi,
|
|
input n64_pi_alel,
|
|
input n64_pi_aleh,
|
|
input n64_pi_read,
|
|
input n64_pi_write,
|
|
inout [15:0] n64_pi_ad
|
|
);
|
|
|
|
// Control signals and input synchronization
|
|
|
|
logic [1:0] n64_reset_ff;
|
|
logic [1:0] n64_nmi_ff;
|
|
logic [2:0] n64_pi_alel_ff;
|
|
logic [2:0] n64_pi_aleh_ff;
|
|
logic [1:0] n64_pi_read_ff;
|
|
logic [2:0] n64_pi_write_ff;
|
|
|
|
always_ff @(posedge clk) begin
|
|
n64_reset_ff <= {n64_reset_ff[0], n64_reset};
|
|
n64_nmi_ff <= {n64_nmi_ff[0], n64_nmi};
|
|
n64_pi_aleh_ff <= {n64_pi_aleh_ff[1:0], n64_pi_aleh};
|
|
n64_pi_alel_ff <= {n64_pi_alel_ff[1:0], n64_pi_alel};
|
|
n64_pi_read_ff <= {n64_pi_read_ff[0], n64_pi_read};
|
|
n64_pi_write_ff <= {n64_pi_write_ff[1:0], n64_pi_write};
|
|
end
|
|
|
|
logic pi_reset;
|
|
logic pi_nmi;
|
|
logic pi_aleh;
|
|
logic pi_alel;
|
|
logic pi_read;
|
|
logic pi_write;
|
|
|
|
always_comb begin
|
|
pi_reset = n64_reset_ff[1];
|
|
pi_nmi = n64_nmi_ff[1];
|
|
pi_aleh = n64_pi_aleh_ff[2];
|
|
pi_alel = n64_pi_alel_ff[2];
|
|
pi_read = n64_pi_read_ff[1];
|
|
pi_write = n64_pi_write_ff[2];
|
|
end
|
|
|
|
|
|
// PI bus state and event generator
|
|
|
|
typedef enum bit [1:0] {
|
|
PI_MODE_IDLE = 2'b10,
|
|
PI_MODE_HIGH = 2'b11,
|
|
PI_MODE_LOW = 2'b01,
|
|
PI_MODE_VALID = 2'b00
|
|
} e_pi_mode;
|
|
|
|
typedef enum bit [1:0] {
|
|
PORT_NONE,
|
|
PORT_MEM,
|
|
PORT_REG
|
|
} e_port;
|
|
|
|
e_pi_mode pi_mode;
|
|
|
|
e_port read_port;
|
|
e_port write_port;
|
|
|
|
always_comb begin
|
|
pi_mode = e_pi_mode'({pi_aleh, pi_alel});
|
|
end
|
|
|
|
logic last_reset;
|
|
logic last_nmi;
|
|
e_pi_mode last_pi_mode;
|
|
logic last_read;
|
|
logic last_write;
|
|
|
|
always_ff @(posedge clk) begin
|
|
last_reset <= pi_reset;
|
|
last_nmi <= pi_nmi;
|
|
last_pi_mode <= pi_mode;
|
|
last_read <= pi_read;
|
|
last_write <= pi_write;
|
|
end
|
|
|
|
logic aleh_op;
|
|
logic alel_op;
|
|
logic read_op;
|
|
logic write_op;
|
|
logic end_op;
|
|
|
|
always_comb begin
|
|
n64_scb.n64_reset = !last_reset && pi_reset;
|
|
n64_scb.n64_nmi = !last_nmi && pi_nmi;
|
|
aleh_op = pi_reset && (last_pi_mode != PI_MODE_HIGH) && (pi_mode == PI_MODE_HIGH);
|
|
alel_op = pi_reset && (last_pi_mode == PI_MODE_HIGH) && (pi_mode == PI_MODE_LOW);
|
|
read_op = pi_reset && (pi_mode == PI_MODE_VALID) && (read_port != PORT_NONE) && (last_read && !pi_read);
|
|
write_op = pi_reset && (pi_mode == PI_MODE_VALID) && (write_port != PORT_NONE) && (last_write && !pi_write);
|
|
end_op = pi_reset && (last_pi_mode == PI_MODE_VALID) && (pi_mode != PI_MODE_VALID);
|
|
end
|
|
|
|
|
|
// Input and output data sampling
|
|
|
|
logic n64_pi_ad_oe;
|
|
logic [15:0] n64_pi_ad_out;
|
|
logic [15:0] n64_pi_dq_in;
|
|
logic [15:0] n64_pi_dq_out;
|
|
|
|
assign n64_pi_ad = n64_pi_ad_oe ? n64_pi_ad_out : 16'hZZZZ;
|
|
|
|
always_ff @(posedge clk) begin
|
|
n64_pi_ad_oe <= pi_reset && (pi_mode == PI_MODE_VALID) && !last_read && (read_port != PORT_NONE);
|
|
n64_pi_ad_out <= n64_pi_dq_out;
|
|
n64_pi_dq_in <= n64_pi_ad;
|
|
end
|
|
|
|
|
|
// Address decoding
|
|
|
|
const bit [31:0] DDIPL_OFFSET = 32'h03BC_0000;
|
|
const bit [31:0] SAVE_OFFSET = 32'h03FE_0000;
|
|
const bit [31:0] FLASH_OFFSET = 32'h0400_0000;
|
|
const bit [31:0] BOOTLOADER_OFFSET = 32'h04E0_0000;
|
|
const bit [31:0] SHADOW_OFFSET = 32'h04FE_0000;
|
|
const bit [31:0] BUFFER_OFFSET = 32'h0500_0000;
|
|
|
|
logic [31:0] mem_offset;
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (reset) begin
|
|
read_port <= PORT_NONE;
|
|
write_port <= PORT_NONE;
|
|
reg_bus.dd_select <= 1'b0;
|
|
reg_bus.flashram_select <= 1'b0;
|
|
reg_bus.cfg_select <= 1'b0;
|
|
end else if (aleh_op) begin
|
|
read_port <= PORT_NONE;
|
|
write_port <= PORT_NONE;
|
|
mem_offset <= 32'd0;
|
|
reg_bus.dd_select <= 1'b0;
|
|
reg_bus.flashram_select <= 1'b0;
|
|
reg_bus.cfg_select <= 1'b0;
|
|
|
|
if (n64_scb.dd_enabled) begin
|
|
if (n64_pi_dq_in == 16'h0500) begin
|
|
read_port <= PORT_REG;
|
|
write_port <= PORT_REG;
|
|
reg_bus.dd_select <= 1'b1;
|
|
end
|
|
end
|
|
|
|
if (n64_scb.ddipl_enabled) begin
|
|
if (n64_pi_dq_in >= 16'h0600 && n64_pi_dq_in < 16'h0640) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= PORT_NONE;
|
|
mem_offset <= (-32'h0600_0000) + DDIPL_OFFSET;
|
|
end
|
|
end
|
|
|
|
if (n64_scb.flashram_enabled) begin
|
|
if (n64_pi_dq_in >= 16'h0800 && n64_pi_dq_in < 16'h0802) begin
|
|
read_port <= PORT_REG;
|
|
write_port <= PORT_REG;
|
|
mem_offset <= (-32'h0800_0000) + SAVE_OFFSET;
|
|
reg_bus.flashram_select <= 1'b1;
|
|
if (n64_scb.flashram_read_mode) begin
|
|
read_port <= PORT_MEM;
|
|
end
|
|
end
|
|
end else if (n64_scb.sram_enabled) begin
|
|
if (n64_scb.sram_banked) begin
|
|
if (n64_pi_dq_in >= 16'h0800 && n64_pi_dq_in < 16'h0810) begin
|
|
if (n64_pi_dq_in[3:2] != 2'b11 && n64_pi_dq_in[1:0] == 2'b00) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= PORT_MEM;
|
|
mem_offset <= (-32'h0800_0000) - {n64_pi_dq_in[3:2], 18'd0} + {n64_pi_dq_in[3:2], 15'd0} + SAVE_OFFSET;
|
|
end
|
|
end
|
|
end else begin
|
|
if (n64_pi_dq_in >= 16'h0800 && n64_pi_dq_in < 16'h0802) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= PORT_MEM;
|
|
mem_offset <= (-32'h0800_0000) + SAVE_OFFSET;
|
|
end
|
|
end
|
|
end
|
|
|
|
if (n64_scb.bootloader_enabled) begin
|
|
if (n64_pi_dq_in >= 16'h1000 && n64_pi_dq_in < 16'h101C) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= PORT_NONE;
|
|
mem_offset <= (-32'h1000_0000) + BOOTLOADER_OFFSET;
|
|
end
|
|
end else begin
|
|
if (n64_pi_dq_in >= 16'h1000 && n64_pi_dq_in < 16'h1400) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= n64_scb.rom_write_enabled ? PORT_MEM : PORT_NONE;
|
|
mem_offset <= (-32'h1000_0000);
|
|
end
|
|
end
|
|
|
|
if (n64_scb.rom_shadow_enabled) begin
|
|
if (n64_pi_dq_in >= 16'h13FE && n64_pi_dq_in < 16'h1400) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= PORT_NONE;
|
|
mem_offset <= (-32'h13FE_0000) + SHADOW_OFFSET;
|
|
end
|
|
end
|
|
|
|
if (n64_scb.rom_extended_enabled) begin
|
|
if (n64_pi_dq_in >= 16'h1400 && n64_pi_dq_in < 16'h14E0) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= PORT_NONE;
|
|
mem_offset <= (-32'h1400_0000) + FLASH_OFFSET;
|
|
end
|
|
end
|
|
|
|
if (n64_scb.cfg_unlock) begin
|
|
if (n64_pi_dq_in >= 16'h1FFE && n64_pi_dq_in < 16'h1FFF) begin
|
|
read_port <= PORT_MEM;
|
|
write_port <= PORT_MEM;
|
|
mem_offset <= (-32'h1FFE_0000) + BUFFER_OFFSET;
|
|
end
|
|
end
|
|
|
|
if (n64_pi_dq_in >= 16'h1FFF && n64_pi_dq_in < 16'h2000) begin
|
|
read_port <= n64_scb.cfg_unlock ? PORT_REG : PORT_NONE;
|
|
write_port <= PORT_REG;
|
|
reg_bus.cfg_select <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Mem bus read FIFO controller
|
|
|
|
logic read_fifo_full;
|
|
logic read_fifo_write;
|
|
logic [15:0] read_fifo_wdata;
|
|
|
|
logic read_fifo_empty;
|
|
logic read_fifo_read;
|
|
logic [15:0] read_fifo_rdata;
|
|
|
|
logic read_fifo_wait;
|
|
|
|
n64_pi_fifo read_fifo_inst (
|
|
.clk(clk),
|
|
.reset(reset),
|
|
|
|
.flush(reset || !pi_reset || alel_op),
|
|
|
|
.full(read_fifo_full),
|
|
.write(read_fifo_write),
|
|
.wdata(read_fifo_wdata),
|
|
|
|
.empty(read_fifo_empty),
|
|
.read(read_fifo_read),
|
|
.rdata(read_fifo_rdata)
|
|
);
|
|
|
|
always_ff @(posedge clk) begin
|
|
read_fifo_read <= 1'b0;
|
|
|
|
if (reset || !pi_reset || alel_op) begin
|
|
read_fifo_wait <= 1'b0;
|
|
end
|
|
|
|
if (read_port == PORT_MEM) begin
|
|
if (read_op) begin
|
|
if (read_fifo_empty) begin
|
|
read_fifo_wait <= 1'b1;
|
|
end else begin
|
|
read_fifo_read <= 1'b1;
|
|
n64_pi_dq_out <= read_fifo_rdata;
|
|
end
|
|
end
|
|
|
|
if (!read_fifo_empty && read_fifo_wait) begin
|
|
read_fifo_read <= 1'b1;
|
|
read_fifo_wait <= 1'b0;
|
|
n64_pi_dq_out <= read_fifo_rdata;
|
|
end
|
|
end
|
|
|
|
if (read_port == PORT_REG) begin
|
|
if (read_op) begin
|
|
n64_pi_dq_out <= reg_bus.rdata;
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Mem bus write FIFO controller
|
|
|
|
logic write_fifo_full;
|
|
logic write_fifo_write;
|
|
logic [15:0] write_fifo_wdata;
|
|
|
|
logic write_fifo_empty;
|
|
logic write_fifo_read;
|
|
logic [15:0] write_fifo_rdata;
|
|
|
|
logic write_fifo_wait;
|
|
|
|
n64_pi_fifo write_fifo_inst (
|
|
.clk(clk),
|
|
.reset(reset),
|
|
|
|
.flush(reset),
|
|
|
|
.full(write_fifo_full),
|
|
.write(write_fifo_write),
|
|
.wdata(write_fifo_wdata),
|
|
|
|
.empty(write_fifo_empty),
|
|
.read(write_fifo_read),
|
|
.rdata(write_fifo_rdata)
|
|
);
|
|
|
|
always_ff @(posedge clk) begin
|
|
write_fifo_write <= 1'b0;
|
|
|
|
if (reset) begin
|
|
write_fifo_wait <= 1'b0;
|
|
end
|
|
|
|
if (write_port == PORT_MEM) begin
|
|
if (write_op) begin
|
|
if (write_fifo_full) begin
|
|
write_fifo_wait <= 1'b1;
|
|
end else begin
|
|
write_fifo_write <= 1'b1;
|
|
write_fifo_wdata <= n64_pi_dq_in;
|
|
end
|
|
end
|
|
|
|
if (!write_fifo_full && write_fifo_wait) begin
|
|
write_fifo_write <= 1'b1;
|
|
write_fifo_wait <= 1'b0;
|
|
write_fifo_wdata <= n64_pi_dq_in;
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Mem bus controller
|
|
|
|
logic [31:0] starting_address;
|
|
logic load_starting_address;
|
|
logic read_enabled;
|
|
logic first_write_op;
|
|
|
|
always_ff @(posedge clk) begin
|
|
write_fifo_read <= 1'b0;
|
|
load_starting_address <= 1'b0;
|
|
|
|
if (reset || !pi_reset) begin
|
|
mem_bus.request <= 1'b0;
|
|
read_enabled <= 1'b0;
|
|
end else begin
|
|
if (aleh_op) begin
|
|
starting_address[31:16] <= n64_pi_dq_in;
|
|
end
|
|
|
|
if (alel_op) begin
|
|
starting_address <= {starting_address[31:16], n64_pi_dq_in} + mem_offset;
|
|
load_starting_address <= 1'b1;
|
|
read_enabled <= 1'b1;
|
|
first_write_op <= 1'b1;
|
|
end
|
|
|
|
if (load_starting_address) begin
|
|
mem_bus.address <= starting_address;
|
|
end
|
|
|
|
if (!mem_bus.request) begin
|
|
if ((write_port == PORT_MEM) && !write_fifo_empty) begin
|
|
mem_bus.request <= 1'b1;
|
|
mem_bus.write <= 1'b1;
|
|
mem_bus.wdata <= write_fifo_rdata;
|
|
write_fifo_read <= 1'b1;
|
|
read_enabled <= 1'b0;
|
|
if (first_write_op) begin
|
|
mem_bus.address <= starting_address;
|
|
first_write_op <= 1'b0;
|
|
end
|
|
end else if ((read_port == PORT_MEM) && !read_fifo_full && read_enabled) begin
|
|
mem_bus.request <= 1'b1;
|
|
mem_bus.write <= 1'b0;
|
|
end
|
|
end
|
|
|
|
if (mem_bus.ack) begin
|
|
mem_bus.request <= 1'b0;
|
|
mem_bus.address[16:0] <= mem_bus.address[16:0] + 2'd2;
|
|
end
|
|
|
|
if (end_op) begin
|
|
read_enabled <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
always_comb begin
|
|
read_fifo_write = !mem_bus.write && mem_bus.ack;
|
|
read_fifo_wdata = mem_bus.rdata;
|
|
mem_bus.wmask = 2'b11;
|
|
end
|
|
|
|
|
|
// Reg bus controller
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (aleh_op) begin
|
|
reg_bus.address[16] <= n64_pi_dq_in[0];
|
|
end
|
|
|
|
if (alel_op) begin
|
|
reg_bus.address[15:0] <= n64_pi_dq_in;
|
|
end
|
|
|
|
if (read_op || write_op) begin
|
|
reg_bus.address <= reg_bus.address + 2'd2;
|
|
end
|
|
end
|
|
|
|
always_comb begin
|
|
reg_bus.read = read_op && (read_port == PORT_REG);
|
|
reg_bus.write = write_op && (write_port == PORT_REG);
|
|
reg_bus.wdata = n64_pi_dq_in;
|
|
end
|
|
|
|
endmodule
|