SummerCart64/fw/rtl/mcu/mcu_top.sv

821 lines
24 KiB
Systemverilog
Raw Normal View History

2022-05-15 15:47:12 +02:00
module mcu_top (
input clk,
input reset,
2022-07-22 12:43:53 +02:00
n64_scb.controller n64_scb,
dd_scb.controller dd_scb,
2022-05-15 15:47:12 +02:00
usb_scb.controller usb_scb,
dma_scb.controller usb_dma_scb,
sd_scb.controller sd_scb,
dma_scb.controller sd_dma_scb,
flash_scb.controller flash_scb,
2022-08-01 23:30:40 +02:00
vendor_scb.controller vendor_scb,
2022-05-15 15:47:12 +02:00
fifo_bus.controller fifo_bus,
mem_bus.controller mem_bus,
input sd_det,
input button,
output logic mcu_int,
input mcu_clk,
input mcu_cs,
input mcu_mosi,
output mcu_miso
);
// Button input synchronization
logic [2:0] sd_det_ff;
logic [2:0] button_ff;
always_ff @(posedge clk) begin
sd_det_ff <= {sd_det_ff[1:0], sd_det};
button_ff <= {button_ff[1:0], button};
end
// MCU <-> FPGA transport
logic frame_start;
logic data_ready;
logic [7:0] rdata;
logic [7:0] wdata;
mcu_spi mcu_spi_inst (
.clk(clk),
.reset(reset),
.frame_start(frame_start),
.data_ready(data_ready),
.rx_data(rdata),
.tx_data(wdata),
.mcu_clk(mcu_clk),
.mcu_cs(mcu_cs),
.mcu_mosi(mcu_mosi),
.mcu_miso(mcu_miso)
);
// Protocol controller
const bit [7:0] FPGA_ID = 8'h64;
typedef enum bit [1:0] {
PHASE_CMD,
PHASE_ADDRESS,
PHASE_DATA,
PHASE_NOP
} phase_e;
typedef enum bit [7:0] {
CMD_IDENTIFY,
CMD_REG_READ,
CMD_REG_WRITE,
CMD_MEM_READ,
CMD_MEM_WRITE,
CMD_USB_STATUS,
CMD_USB_READ,
CMD_USB_WRITE
2022-05-15 15:47:12 +02:00
} cmd_e;
phase_e phase;
cmd_e cmd;
logic [1:0] counter;
logic [7:0] address;
logic reg_read;
logic reg_write;
logic [31:0] reg_rdata;
logic [31:0] reg_wdata;
logic mem_read;
logic mem_write;
logic [15:0] mem_rdata;
logic [15:0] mem_wdata;
logic mem_word_select;
always_ff @(posedge clk) begin
fifo_bus.rx_read <= 1'b0;
fifo_bus.tx_write <= 1'b0;
reg_read <= 1'b0;
reg_write <= 1'b0;
mem_read <= 1'b0;
mem_write <= 1'b0;
if (reset) begin
end else begin
if (frame_start) begin
counter <= 2'd0;
phase <= PHASE_CMD;
end
if (reg_read || reg_write || (mem_word_select && (mem_read || mem_write))) begin
address <= address + 1'd1;
end
if (data_ready) begin
case (phase)
PHASE_CMD: begin
cmd <= cmd_e'(rdata);
phase <= PHASE_ADDRESS;
if (rdata == CMD_USB_STATUS) begin
phase <= PHASE_NOP;
end
if (rdata == CMD_USB_READ) begin
fifo_bus.rx_read <= 1'b1;
phase <= PHASE_DATA;
end
if (rdata == CMD_USB_WRITE) begin
phase <= PHASE_DATA;
end
end
PHASE_ADDRESS: begin
address <= rdata;
phase <= PHASE_DATA;
if (cmd == CMD_REG_READ) begin
reg_read <= 1'b1;
end
if (cmd == CMD_MEM_READ) begin
mem_read <= 1'b1;
mem_word_select <= 1'b0;
end
end
PHASE_DATA: begin
counter <= counter + 1'd1;
if (cmd == CMD_REG_READ) begin
if (counter == 2'd3) begin
reg_read <= 1'd1;
end
end
if (cmd == CMD_REG_WRITE) begin
case (counter)
2'd0: reg_wdata[7:0] <= rdata;
2'd1: reg_wdata[15:8] <= rdata;
2'd2: reg_wdata[23:16] <= rdata;
2'd3: reg_wdata[31:24] <= rdata;
endcase
if (counter == 2'd3) begin
reg_write <= 1'd1;
end
end
if (cmd == CMD_MEM_READ) begin
if (counter[0]) begin
mem_read <= 1'b1;
mem_word_select <= ~mem_word_select;
end
end
if (cmd == CMD_MEM_WRITE) begin
case (counter[0])
1'd0: mem_wdata[15:8] <= rdata;
1'd1: mem_wdata[7:0] <= rdata;
endcase
if (counter[0]) begin
mem_write <= 1'b1;
mem_word_select <= counter[1];
end
end
if (cmd == CMD_USB_READ) begin
phase <= PHASE_NOP;
end
if (cmd == CMD_USB_WRITE) begin
fifo_bus.tx_write <= 1'b1;
fifo_bus.tx_wdata <= rdata;
phase <= PHASE_NOP;
end
end
PHASE_NOP: begin end
endcase
end
end
end
always_comb begin
wdata = 8'h00;
case (cmd)
CMD_IDENTIFY: begin
wdata = FPGA_ID;
end
CMD_REG_READ: begin
case (counter)
2'd0: wdata = reg_rdata[7:0];
2'd1: wdata = reg_rdata[15:8];
2'd2: wdata = reg_rdata[23:16];
2'd3: wdata = reg_rdata[31:24];
endcase
end
CMD_REG_WRITE: begin
wdata = 8'h00;
end
CMD_MEM_READ: begin
case (counter[0])
1'd0: wdata = mem_rdata[15:8];
1'd1: wdata = mem_rdata[7:0];
endcase
end
CMD_MEM_WRITE: begin
wdata = 8'h00;
end
CMD_USB_STATUS: begin
wdata = {6'd0, ~fifo_bus.tx_full, ~fifo_bus.rx_empty};
end
CMD_USB_READ: begin
wdata = fifo_bus.rx_rdata;
end
CMD_USB_WRITE: begin
wdata = 8'h00;
end
endcase
end
// Mem bus controller
logic [15:0] mem_buffer [0:511];
logic mem_start;
logic mem_stop;
logic mem_direction;
logic [8:0] mem_length;
logic [31:0] mem_address;
logic mem_busy;
logic mem_stop_pending;
logic [8:0] mem_counter;
always_ff @(posedge clk) begin
if (reset) begin
mem_busy <= 1'b0;
mem_stop_pending <= 1'b0;
mem_bus.request <= 1'b0;
end else begin
if (mem_read) begin
mem_rdata <= mem_buffer[{address, mem_word_select}];
end
if (mem_write) begin
mem_buffer[{address, mem_word_select}] <= mem_wdata;
end
if (mem_stop) begin
mem_stop_pending <= mem_busy;
end else if (mem_start && !mem_busy) begin
mem_bus.write <= mem_direction;
mem_bus.address <= mem_address;
mem_busy <= 1'b1;
mem_counter <= 9'd0;
end
if (mem_busy) begin
if (!mem_bus.request) begin
mem_bus.request <= 1'b1;
mem_bus.wdata <= mem_buffer[mem_counter];
end
if (mem_bus.ack) begin
mem_bus.request <= 1'b0;
mem_bus.address <= mem_bus.address + 2'd2;
mem_counter <= mem_counter + 1'd1;
if (!mem_bus.write) begin
mem_buffer[mem_counter] <= mem_bus.rdata;
end
if ((mem_counter == mem_length) || mem_stop_pending) begin
mem_busy <= 1'b0;
mem_stop_pending <= 1'b0;
end
end
end
end
end
always_comb begin
mem_bus.wmask = 2'b11;
end
// Register list
typedef enum bit [7:0] {
REG_STATUS,
REG_MEM_ADDRESS,
REG_MEM_SCR,
REG_USB_SCR,
REG_USB_DMA_ADDRESS,
REG_USB_DMA_LENGTH,
REG_USB_DMA_SCR,
REG_CFG_SCR,
REG_CFG_DATA_0,
REG_CFG_DATA_1,
REG_CFG_CMD,
REG_CFG_VERSION,
REG_FLASHRAM_SCR,
REG_FLASH_SCR,
REG_RTC_SCR,
REG_RTC_TIME_0,
REG_RTC_TIME_1,
REG_SD_SCR,
REG_SD_ARG,
REG_SD_CMD,
REG_SD_RSP_0,
REG_SD_RSP_1,
REG_SD_RSP_2,
REG_SD_RSP_3,
REG_SD_DAT,
REG_SD_DMA_ADDRESS,
REG_SD_DMA_LENGTH,
2022-07-22 12:43:53 +02:00
REG_SD_DMA_SCR,
REG_DD_SCR,
REG_DD_CMD_DATA,
REG_DD_HEAD_TRACK,
REG_DD_SECTOR_INFO,
2022-08-01 23:30:40 +02:00
REG_DD_DRIVE_ID,
REG_VENDOR_SCR,
REG_VENDOR_DATA
2022-05-15 15:47:12 +02:00
} reg_address_e;
logic bootloader_skip;
assign n64_scb.cfg_version = 32'h53437632;
2022-07-22 12:43:53 +02:00
logic dd_bm_ack;
2022-05-15 15:47:12 +02:00
// Register read logic
always_ff @(posedge clk) begin
if (reg_read) begin
reg_rdata <= 32'd0;
case (address)
REG_STATUS: begin
reg_rdata <= {
29'd0,
2022-05-15 15:47:12 +02:00
n64_scb.cfg_pending,
~sd_det_ff[2],
~button_ff[2]
2022-05-15 15:47:12 +02:00
};
end
REG_MEM_ADDRESS: begin
reg_rdata <= mem_address;
end
REG_MEM_SCR: begin
reg_rdata <= {
28'd0,
mem_busy,
3'b000
};
end
REG_USB_SCR: begin
reg_rdata <= {
28'd0,
usb_scb.reset_pending,
~fifo_bus.tx_full,
~fifo_bus.rx_empty,
1'b0
};
end
REG_USB_DMA_ADDRESS: begin
reg_rdata <= {
5'd0,
usb_dma_scb.starting_address
};
end
REG_USB_DMA_LENGTH: begin
reg_rdata <= {
5'd0,
usb_dma_scb.transfer_length
};
end
REG_USB_DMA_SCR: begin
reg_rdata <= {
28'd0,
usb_dma_scb.busy,
usb_dma_scb.direction,
2'b00
};
end
REG_CFG_SCR: begin
reg_rdata <= {
2022-09-02 22:48:47 +02:00
20'd0,
n64_scb.rom_extended_enabled,
2022-05-15 15:47:12 +02:00
n64_scb.eeprom_16k_mode,
n64_scb.eeprom_enabled,
2022-07-01 18:25:39 +02:00
n64_scb.ddipl_enabled,
2022-05-15 15:47:12 +02:00
n64_scb.dd_enabled,
n64_scb.flashram_enabled,
n64_scb.sram_banked,
n64_scb.sram_enabled,
n64_scb.rom_shadow_enabled,
n64_scb.rom_write_enabled,
bootloader_skip,
n64_scb.bootloader_enabled
};
end
REG_CFG_DATA_0: begin
reg_rdata <= n64_scb.cfg_rdata[0];
end
REG_CFG_DATA_1: begin
reg_rdata <= n64_scb.cfg_rdata[1];
end
REG_CFG_CMD: begin
reg_rdata <= {
24'd0,
n64_scb.cfg_cmd
};
end
REG_CFG_VERSION: begin
reg_rdata <= n64_scb.cfg_version;
end
REG_FLASHRAM_SCR: begin
reg_rdata <= {
18'd0,
n64_scb.flashram_write_or_erase,
n64_scb.flashram_sector_or_all,
n64_scb.flashram_sector,
n64_scb.flashram_pending,
1'b0
};
end
REG_FLASH_SCR: begin
reg_rdata <= {
31'd0,
flash_scb.erase_pending
};
end
REG_RTC_SCR: begin
reg_rdata <= {
24'h525443,
7'd0,
2022-05-15 15:47:12 +02:00
n64_scb.rtc_pending
};
end
REG_RTC_TIME_0: begin
reg_rdata <= {
5'd0, n64_scb.rtc_rdata[28:26],
2'd0, n64_scb.rtc_rdata[19:14],
1'd0, n64_scb.rtc_rdata[13:7],
1'd0, n64_scb.rtc_rdata[6:0]
};
end
REG_RTC_TIME_1: begin
reg_rdata <= {
8'd0,
n64_scb.rtc_rdata[41:34],
3'd0, n64_scb.rtc_rdata[33:29],
2'd0, n64_scb.rtc_rdata[25:20]
};
end
REG_SD_SCR: begin
reg_rdata <= {
30'd0,
sd_scb.clock_mode
};
end
REG_SD_DMA_ADDRESS: begin
reg_rdata <= {
5'd0,
sd_dma_scb.starting_address
};
end
REG_SD_DMA_LENGTH: begin
reg_rdata <= {
5'd0,
sd_dma_scb.transfer_length
};
end
REG_SD_DMA_SCR: begin
reg_rdata <= {
28'd0,
sd_dma_scb.busy,
sd_dma_scb.direction,
2'b00
};
end
2022-07-22 12:43:53 +02:00
REG_DD_SCR: begin
reg_rdata <= {
14'd0,
dd_bm_ack,
dd_scb.bm_micro_error,
dd_scb.bm_transfer_c2,
dd_scb.bm_transfer_data,
dd_scb.bm_transfer_blocks,
dd_scb.bm_transfer_mode,
1'b0,
dd_scb.bm_stop_pending,
1'b0,
dd_scb.bm_start_pending,
dd_scb.disk_changed,
dd_scb.disk_inserted,
1'b0,
dd_scb.bm_pending,
1'b0,
dd_scb.cmd_pending,
1'b0,
dd_scb.hard_reset
};
end
REG_DD_CMD_DATA: begin
reg_rdata <= {8'd0, dd_scb.cmd, dd_scb.data};
end
REG_DD_HEAD_TRACK: begin
reg_rdata <= {18'd0, dd_scb.index_lock, dd_scb.head_track};
end
REG_DD_SECTOR_INFO: begin
reg_rdata <= {
dd_scb.sectors_in_block,
dd_scb.sector_size_full,
dd_scb.sector_size,
dd_scb.sector_num
};
end
2022-08-01 23:30:40 +02:00
REG_VENDOR_SCR: begin
reg_rdata <= vendor_scb.control_rdata;
end
REG_VENDOR_DATA: begin
reg_rdata <= vendor_scb.data_rdata;
end
2022-05-15 15:47:12 +02:00
endcase
end
end
// Register write logic
always_ff @(posedge clk) begin
mem_start <= 1'b0;
mem_stop <= 1'b0;
usb_scb.write_buffer_flush <= 1'b0;
usb_scb.reset_ack <= 1'b0;
usb_scb.fifo_flush <= 1'b0;
usb_dma_scb.start <= 1'b0;
usb_dma_scb.stop <= 1'b0;
sd_dma_scb.start <= 1'b0;
sd_dma_scb.stop <= 1'b0;
n64_scb.cfg_done <= 1'b0;
2022-08-30 00:33:26 +02:00
n64_scb.cfg_error <= 1'b0;
2022-05-15 15:47:12 +02:00
n64_scb.cfg_irq <= 1'b0;
n64_scb.flashram_done <= 1'b0;
n64_scb.rtc_done <= 1'b0;
2022-07-22 12:43:53 +02:00
dd_scb.hard_reset_clear <= 1'b0;
dd_scb.cmd_ready <= 1'b0;
dd_scb.bm_start_clear <= 1'b0;
dd_scb.bm_stop_clear <= 1'b0;
dd_scb.bm_clear <= 1'b0;
dd_scb.bm_ready <= 1'b0;
2022-08-01 23:30:40 +02:00
vendor_scb.control_valid <= 1'b0;
2022-05-15 15:47:12 +02:00
if (n64_scb.n64_nmi) begin
n64_scb.bootloader_enabled <= !bootloader_skip;
end
if (flash_scb.erase_done) begin
flash_scb.erase_pending <= 1'b0;
end
2022-07-22 12:43:53 +02:00
if (dd_scb.bm_interrupt_ack) begin
dd_bm_ack <= 1'b1;
end
2022-05-15 15:47:12 +02:00
if (reset) begin
mcu_int <= 1'b0;
sd_scb.clock_mode <= 2'd0;
2022-09-02 22:48:47 +02:00
n64_scb.rom_extended_enabled <= 1'b0;
2022-05-15 15:47:12 +02:00
n64_scb.eeprom_16k_mode <= 1'b0;
n64_scb.eeprom_enabled <= 1'b0;
n64_scb.dd_enabled <= 1'b0;
2022-07-01 18:25:39 +02:00
n64_scb.ddipl_enabled <= 1'b0;
2022-05-15 15:47:12 +02:00
n64_scb.flashram_enabled <= 1'b0;
n64_scb.sram_banked <= 1'b0;
n64_scb.sram_enabled <= 1'b0;
n64_scb.rom_shadow_enabled <= 1'b0;
n64_scb.rom_write_enabled <= 1'b0;
bootloader_skip <= 1'b0;
n64_scb.bootloader_enabled <= 1'b1;
flash_scb.erase_pending <= 1'b0;
2022-07-22 12:43:53 +02:00
dd_bm_ack <= 1'b0;
2022-07-30 19:39:49 +02:00
n64_scb.rtc_wdata_valid <= 1'b0;
2022-05-15 15:47:12 +02:00
end else if (reg_write) begin
case (address)
REG_STATUS: begin end
REG_MEM_ADDRESS: begin
mem_address <= reg_wdata;
end
REG_MEM_SCR: begin
{
mem_length,
mem_direction,
mem_stop,
mem_start
} <= {(reg_wdata[14:5] - 1'd1), reg_wdata[2:0]};
end
REG_USB_SCR: begin
{
usb_scb.write_buffer_flush,
usb_scb.reset_ack,
usb_scb.fifo_flush
} <= {reg_wdata[5:4], reg_wdata[0]};
end
REG_USB_DMA_ADDRESS: begin
usb_dma_scb.starting_address <= reg_wdata[26:0];
end
REG_USB_DMA_LENGTH: begin
usb_dma_scb.transfer_length <= reg_wdata[26:0];
end
REG_USB_DMA_SCR: begin
{
usb_dma_scb.direction,
usb_dma_scb.stop,
usb_dma_scb.start
} <= reg_wdata[2:0];
end
REG_CFG_SCR: begin
{
2022-09-02 22:48:47 +02:00
n64_scb.rom_extended_enabled,
2022-05-15 15:47:12 +02:00
n64_scb.eeprom_16k_mode,
n64_scb.eeprom_enabled,
2022-07-01 18:25:39 +02:00
n64_scb.ddipl_enabled,
2022-05-15 15:47:12 +02:00
n64_scb.dd_enabled,
n64_scb.flashram_enabled,
n64_scb.sram_banked,
n64_scb.sram_enabled,
n64_scb.rom_shadow_enabled,
n64_scb.rom_write_enabled,
bootloader_skip,
n64_scb.bootloader_enabled
2022-09-02 22:48:47 +02:00
} <= reg_wdata[11:0];
2022-05-15 15:47:12 +02:00
end
REG_CFG_DATA_0: begin
n64_scb.cfg_wdata[0] <= reg_wdata;
end
REG_CFG_DATA_1: begin
n64_scb.cfg_wdata[1] <= reg_wdata;
end
REG_CFG_CMD: begin
{
n64_scb.cfg_irq,
n64_scb.cfg_error,
n64_scb.cfg_done
} <= reg_wdata[2:0];
end
REG_FLASHRAM_SCR: begin
n64_scb.flashram_done <= reg_wdata[0];
end
REG_FLASH_SCR: begin
flash_scb.erase_pending <= 1'b1;
flash_scb.erase_block <= reg_wdata[23:16];
end
REG_RTC_SCR: begin
n64_scb.rtc_done <= reg_wdata[1];
end
REG_RTC_TIME_0: begin
2022-07-30 19:39:49 +02:00
n64_scb.rtc_wdata_valid <= 1'b0;
n64_scb.rtc_wdata[28:26] <= reg_wdata[26:24];
n64_scb.rtc_wdata[19:14] <= reg_wdata[21:16];
n64_scb.rtc_wdata[13:7] <= reg_wdata[14:8];
n64_scb.rtc_wdata[6:0] <= reg_wdata[6:0];
2022-05-15 15:47:12 +02:00
end
REG_RTC_TIME_1: begin
2022-07-30 19:39:49 +02:00
n64_scb.rtc_wdata_valid <= 1'b1;
2022-05-15 15:47:12 +02:00
n64_scb.rtc_wdata[41:34] <= reg_wdata[23:16];
n64_scb.rtc_wdata[33:29] <= reg_wdata[12:8];
n64_scb.rtc_wdata[25:20] <= reg_wdata[5:0];
end
REG_SD_SCR: begin
sd_scb.clock_mode <= reg_wdata[1:0];
end
REG_SD_DMA_ADDRESS: begin
sd_dma_scb.starting_address <= reg_wdata[26:0];
end
REG_SD_DMA_LENGTH: begin
sd_dma_scb.transfer_length <= reg_wdata[26:0];
end
REG_SD_DMA_SCR: begin
{
sd_dma_scb.direction,
sd_dma_scb.stop,
sd_dma_scb.start
} <= reg_wdata[2:0];
end
2022-07-22 12:43:53 +02:00
REG_DD_SCR: begin
dd_scb.bm_clear <= reg_wdata[19];
if (reg_wdata[18]) begin
dd_bm_ack <= 1'b0;
end
dd_scb.bm_micro_error <= reg_wdata[16];
dd_scb.bm_transfer_c2 <= reg_wdata[15];
dd_scb.bm_transfer_data <= reg_wdata[14];
dd_scb.bm_stop_clear <= reg_wdata[11];
dd_scb.bm_start_clear <= reg_wdata[9];
dd_scb.disk_changed <= reg_wdata[7];
dd_scb.disk_inserted <= reg_wdata[6];
dd_scb.bm_ready <= reg_wdata[5];
dd_scb.cmd_ready <= reg_wdata[3];
dd_scb.hard_reset_clear <= reg_wdata[1];
end
REG_DD_CMD_DATA: begin
dd_scb.cmd_data <= reg_wdata[15:0];
end
REG_DD_HEAD_TRACK: begin
{dd_scb.index_lock, dd_scb.head_track} <= reg_wdata[13:0];
end
REG_DD_DRIVE_ID: begin
dd_scb.drive_id <= reg_wdata[15:0];
end
2022-08-01 23:30:40 +02:00
REG_VENDOR_SCR: begin
vendor_scb.control_valid <= 1'b1;
vendor_scb.control_wdata <= reg_wdata;
end
REG_VENDOR_DATA: begin
vendor_scb.data_wdata <= reg_wdata;
end
2022-05-15 15:47:12 +02:00
endcase
end
end
endmodule