From 2eb18fc780763bdf24cf957d5fefbbb991a39d2c Mon Sep 17 00:00:00 2001 From: kitune-san <19359213+kitune-san@users.noreply.github.com> Date: Sat, 24 Sep 2022 10:27:00 +0900 Subject: [PATCH] Ported AO486 floppy controller. --- PCXT.sv | 49 +- rtl/KFPC-XT/HDL/Chipset.sv | 29 +- rtl/KFPC-XT/HDL/Peripherals.sv | 155 +++++- rtl/common/floppy.v | 878 +++++++++++++++++++++++++++++++++ rtl/common/simple_fifo.v | 87 ++++ rtl/hps_ext.v | 93 ++++ 6 files changed, 1279 insertions(+), 12 deletions(-) create mode 100644 rtl/common/floppy.v create mode 100644 rtl/common/simple_fifo.v create mode 100644 rtl/hps_ext.v diff --git a/PCXT.sv b/PCXT.sv index 699894a..f7ef283 100644 --- a/PCXT.sv +++ b/PCXT.sv @@ -218,11 +218,11 @@ localparam CONF_STR = { "O7,Splash Screen,Yes,No;", "-;", "P1,FDD & HDD;", + "P1S0,IMGIMAVFD,Floppy A:;", + "P1S1,IMGIMAVFD,Floppy B:;", + "P1OQR,Write Protect,None,A:,B:,A: & B:;", "P1-;", - "P1S1,IMGIMA,FDD Image:;", - "P1S0,IMG,HDD Image:;", - "P1-;", - "P1OJK,Write Protect,None,FDD,HDD,FDD & HDD;", + "P1S2,IMG,HDD Image:;", "P1-;", "P1OLM,Speed,115200,230400,460800,921600;", "P1-;", @@ -313,7 +313,7 @@ hps_io #(.CONF_STR(CONF_STR), .PS2DIV(2000), .PS2WE(1), .WIDE(1)) hps_io ( .clk_sys(clk_chipset), .HPS_BUS(HPS_BUS), - .EXT_BUS(), + .EXT_BUS(EXT_BUS), .gamma_bus(gamma_bus), .forced_scandoubler(forced_scandoubler), @@ -358,6 +358,31 @@ hps_io #(.CONF_STR(CONF_STR), .PS2DIV(2000), .PS2WE(1), .WIDE(1)) hps_io .ioctl_wait(ioctl_wait) ); + +wire [15:0] mgmt_din; +wire [15:0] mgmt_dout; +wire [15:0] mgmt_addr; +wire mgmt_rd; +wire mgmt_wr; +wire [7:0] mgmt_req; +assign mgmt_req[5:0] = 6'b000000; + +wire [35:0] EXT_BUS; +hps_ext hps_ext +( + .clk_sys(clk_chipset), + .EXT_BUS(EXT_BUS), + + .ext_din(mgmt_din), + .ext_dout(mgmt_dout), + .ext_addr(mgmt_addr), + .ext_rd(mgmt_rd), + .ext_wr(mgmt_wr), + + .ext_req(mgmt_req), + .ext_hotswap(2'b00) +); + /////////////////////// CLOCKS /////////////////////////////// wire clk_sys; @@ -745,8 +770,12 @@ end end end + ////////////////////////////////////////////////////////////////// + reg [27:0] cur_rate; + always @(posedge CLK_50M) cur_rate <= 50000000; + wire [5:0] r, g, b; reg [7:0] raux_cga, gaux_cga, baux_cga; reg [7:0] raux_mda, gaux_mda, baux_mda; @@ -942,7 +971,15 @@ end .ems_enabled (~status[11]), .ems_address (status[13:12]), .bios_protect_flag (bios_protect_flag), - .bios_writable (status[31:30]) + .bios_writable (status[31:30]), + .mgmt_readdata (mgmt_din), + .mgmt_writedata (mgmt_dout), + .mgmt_address (mgmt_addr), + .mgmt_write (mgmt_wr), + .mgmt_read (mgmt_rd), + .clock_rate (cur_rate), + .floppy_wp (status[27:26]), + .fdd_request (mgmt_req[7:6]) ); wire [15:0] SDRAM_DQ_IN; diff --git a/rtl/KFPC-XT/HDL/Chipset.sv b/rtl/KFPC-XT/HDL/Chipset.sv index 575910e..86df53e 100644 --- a/rtl/KFPC-XT/HDL/Chipset.sv +++ b/rtl/KFPC-XT/HDL/Chipset.sv @@ -122,7 +122,16 @@ module CHIPSET ( input logic [1:0] ems_address, // BIOS input logic bios_protect_flag, - input logic [2:0] bios_writable + input logic [2:0] bios_writable, + // FDD + input logic [15:0] mgmt_address, + input logic mgmt_read, + output logic [15:0] mgmt_readdata, + input logic mgmt_write, + input logic [15:0] mgmt_writedata, + input logic [27:0] clock_rate, + input logic [1:0] floppy_wp, + output logic [1:0] fdd_request ); logic dma_ready; @@ -150,6 +159,8 @@ module CHIPSET ( logic ems_b3; logic ems_b4; logic tandy_snd_rdy; + logic fdd_dma_req; + always_ff @(posedge clock) begin if (reset) @@ -219,7 +230,7 @@ module CHIPSET ( .memory_write_n_direction (memory_write_n_direction), .no_command_state (no_command_state), .ext_access_request (ext_access_request), - .dma_request ({dma_request[3:1], DRQ0}), + .dma_request ({dma_request[3], fdd_dma_req, dma_request[1], DRQ0}), .dma_acknowledge_n (dma_acknowledge_n), .address_enable_n (address_enable_n), .terminal_count_n (terminal_count_n) @@ -228,6 +239,7 @@ module CHIPSET ( PERIPHERALS u_PERIPHERALS ( .clock (clock), .clk_sys (clk_sys), + .cpu_clock (cpu_clock), .clk_uart (clk_uart), .peripheral_clock (peripheral_clock), .turbo_mode (turbo_mode), @@ -302,7 +314,18 @@ module CHIPSET ( .ems_b2 (ems_b2), .ems_b3 (ems_b3), .ems_b4 (ems_b4), - .bios_writable (bios_writable) + .bios_writable (bios_writable), + .mgmt_address (mgmt_address), + .mgmt_read (mgmt_read), + .mgmt_readdata (mgmt_readdata), + .mgmt_write (mgmt_write), + .mgmt_writedata (mgmt_writedata), + .clock_rate (clock_rate), + .floppy_wp (floppy_wp), + .fdd_request (fdd_request), + .fdd_dma_req (fdd_dma_req), + .fdd_dma_ack (~dma_acknowledge_n[2]), + .terminal_count (terminal_count_n) ); RAM u_RAM ( diff --git a/rtl/KFPC-XT/HDL/Peripherals.sv b/rtl/KFPC-XT/HDL/Peripherals.sv index 4f62ac4..6ec319d 100644 --- a/rtl/KFPC-XT/HDL/Peripherals.sv +++ b/rtl/KFPC-XT/HDL/Peripherals.sv @@ -7,6 +7,7 @@ module PERIPHERALS #( ) ( input logic clock, input logic clk_sys, + input logic cpu_clock, input logic peripheral_clock, input logic [1:0] turbo_mode, input logic reset, @@ -91,7 +92,19 @@ module PERIPHERALS #( output logic ems_b2, output logic ems_b3, output logic ems_b4, - input logic [2:0] bios_writable + input logic [2:0] bios_writable, + // FDD + input logic [15:0] mgmt_address, + input logic mgmt_read, + output logic [15:0] mgmt_readdata, + input logic mgmt_write, + input logic [15:0] mgmt_writedata, + input logic [27:0] clock_rate, + input logic [1:0] floppy_wp, + output logic [1:0] fdd_request, + output logic fdd_dma_req, + input logic fdd_dma_ack, + input logic terminal_count ); wire grph_mode; @@ -99,6 +112,23 @@ module PERIPHERALS #( assign tandy_16_gfx = (tandy_video & grph_mode & hres_mode); + + // + // CPU clock edge + // + logic prev_cpu_clock; + + always_ff @(posedge clock, posedge reset) begin + if (reset) + prev_cpu_clock <= 1'b0; + else + prev_cpu_clock <= cpu_clock; + end + + wire cpu_clock_posedge = ~prev_cpu_clock & cpu_clock; + wire cpu_clock_negedge = prev_cpu_clock & ~cpu_clock; + + // // chip select // @@ -150,6 +180,7 @@ module PERIPHERALS #( assign ems_b3 = (~iorq && ena_ems[2] && (address[19:14] == {ems_page_address, 2'b10})); // A8000h - C8000h - D8000h assign ems_b4 = (~iorq && ena_ems[3] && (address[19:14] == {ems_page_address, 2'b11})); // AC000h - CC000h - DC000h + wire floppy0_select_n = ~(~address_enable_n && (({address[15:2], 2'd0} == 16'h03F0) || ({address[15:1], 1'd0} == 16'h03F4) || ({address[15:0]} == 16'h03F7))); logic [1:0] ems_access_address; logic ems_write_enable; @@ -189,6 +220,7 @@ module PERIPHERALS #( logic timer_interrupt; logic keybord_interrupt; logic uart_interrupt; + logic fdd_interrupt; logic [7:0] interrupt_data_bus_out; KF8259 u_KF8259 ( @@ -211,8 +243,13 @@ module PERIPHERALS #( //.slave_program_or_enable_buffer (), .interrupt_acknowledge_n (interrupt_acknowledge_n), .interrupt_to_cpu (interrupt_to_cpu), - .interrupt_request ({interrupt_request[7:5], uart_interrupt, interrupt_request[3:2], - keybord_interrupt, timer_interrupt}) + .interrupt_request ({interrupt_request[7], + fdd_interrupt, + interrupt_request[5], + uart_interrupt, + interrupt_request[3:2], + keybord_interrupt, + timer_interrupt}) ); // @@ -791,6 +828,114 @@ module PERIPHERALS #( ); + // + // FDC + // + logic mgmt_fdd_cs; + logic [7:0] write_to_fdd; + logic [2:0] fdd_io_address; + logic fdd_io_read; + logic fdd_io_read_1; + logic fdd_io_write; + logic [7:0] fdd_readdata_wire; + logic [7:0] fdd_dma_readdata; + logic [7:0] fdd_readdata; + logic fdd_dma_req_wire; + logic fdd_dma_write_ack; + logic fdd_dma_read_ack; + logic prev_fdd_dma_write_ack; + logic prev_fdd_dma_read_ack; + logic fdd_dma_rw_ack; + logic fdd_dma_tc; + logic prev_fdd_dma_tc; + + assign mgmt_fdd_cs = (mgmt_address[15:8] == 8'hF2); + + always_ff @(posedge clock) begin + if (~io_write_n) + write_to_fdd <= internal_data_bus; + else + write_to_fdd <= write_to_fdd; + end + + always_ff @(posedge clock) begin + fdd_io_address <= address[2:0]; + fdd_io_read <= ~io_read_n & prev_io_read_n & ~floppy0_select_n; + fdd_io_read_1 <= fdd_io_read; + fdd_io_write <= io_write_n & ~prev_io_write_n & ~floppy0_select_n; + end + + assign fdd_dma_write_ack = fdd_dma_ack & ~io_write_n; + assign fdd_dma_read_ack = fdd_dma_ack & ~io_read_n; + + always_ff @(posedge clock) begin + prev_fdd_dma_write_ack <= fdd_dma_write_ack; + prev_fdd_dma_read_ack <= fdd_dma_read_ack; + end + + assign fdd_dma_rw_ack = (~fdd_dma_read_ack & prev_fdd_dma_read_ack) || (~fdd_dma_write_ack & prev_fdd_dma_write_ack); + + assign fdd_dma_tc = terminal_count & fdd_dma_ack; + + always_ff @(posedge clock) begin + prev_fdd_dma_tc <= fdd_dma_tc; + end + + floppy floppy ( + .clk (clock), + .rst_n (~reset), + + //dma + .dma_req (fdd_dma_req_wire), + .dma_ack (fdd_dma_rw_ack), + .dma_tc (prev_fdd_dma_tc & fdd_dma_rw_ack), + .dma_readdata (write_to_fdd), + .dma_writedata (fdd_dma_readdata), + + //irq + .irq (fdd_interrupt), + + //io buf + .io_address (fdd_io_address), + .io_read (fdd_io_read), + .io_readdata (fdd_readdata_wire), + .io_write (fdd_io_write), + .io_writedata (write_to_fdd), + +// .fdd0_inserted (), + + .mgmt_address (mgmt_address[3:0]), + .mgmt_fddn (mgmt_address[7]), + .mgmt_write (mgmt_write & mgmt_fdd_cs), + .mgmt_writedata (mgmt_writedata), + .mgmt_read (mgmt_read & mgmt_fdd_cs), + .mgmt_readdata (mgmt_readdata), + + .wp (floppy_wp), + + .clock_rate (clock_rate), + + .request (fdd_request) + ); + + always_ff @(posedge clock) begin + if (fdd_dma_ack) + fdd_dma_req <= 1'b0; + else if (cpu_clock_negedge) + fdd_dma_req <= fdd_dma_req_wire; + else + fdd_dma_req <= fdd_dma_req; + end + + always_ff @(posedge clock) begin + if ((fdd_io_read_1) && (~address_enable_n)) + fdd_readdata <= fdd_readdata_wire; + else if ((~io_read_n) && (fdd_dma_ack)) + fdd_readdata <= fdd_dma_readdata; + else + fdd_readdata <= fdd_readdata; + end + // // KFTVGA @@ -896,6 +1041,10 @@ module PERIPHERALS #( data_bus_out_from_chipset <= 1'b1; data_bus_out <= joy_data; end + else if ((~floppy0_select_n || fdd_dma_read_ack) && (~io_read_n)) begin + data_bus_out_from_chipset <= 1'b1; + data_bus_out <= fdd_readdata; + end else begin data_bus_out_from_chipset <= 1'b0; data_bus_out <= 8'b00000000; diff --git a/rtl/common/floppy.v b/rtl/common/floppy.v new file mode 100644 index 0000000..2d8d08a --- /dev/null +++ b/rtl/common/floppy.v @@ -0,0 +1,878 @@ +/* + * Copyright (c) 2014, Aleksander Osman + * Copyright (c) 2020, Alexey Melnikov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module floppy +( + input clk, + input rst_n, + + //dma + output dma_req, + input dma_ack, + input dma_tc, + input [7:0] dma_readdata, + output [7:0] dma_writedata, + + //irq + output reg irq, + + //io buf + input [2:0] io_address, + input io_read, + output reg [7:0] io_readdata, + input io_write, + input [7:0] io_writedata, + + output fdd0_inserted, + //management + /* + 0x00.[0]: media present + 0x01.[0]: media writeprotect + 0x02.[7:0]: media cylinders + 0x03.[7:0]: media sectors per track + 0x04.[31:0]: media total sector count + 0x05.[1:0]: media heads + */ + input [3:0] mgmt_address, + input mgmt_fddn, + input mgmt_write, + input [15:0] mgmt_writedata, + input mgmt_read, + output [15:0] mgmt_readdata, + + input [1:0] wp, + + input [27:0] clock_rate, + + output [1:0] request +); + +reg [27:0] clk_rate; +always @(posedge clk) clk_rate <= clock_rate; + +//------------------------------------------------------------------------------ media management + +assign mgmt_readdata = (!mgmt_address) ? {selected_drive[0], sd_sector[14:0]} : (&mgmt_address) ? fifo_readdata : 16'd1; +assign request = (state == S_SD_READ_WAIT_FOR_DATA || state == S_SD_WRITE_WAIT_FOR_EMPTY_FIFO || state == S_SD_FORMAT_WAIT_FOR_FILL) ? + {cmd_write_normal_in_progress | cmd_format_in_progress, cmd_read_normal_in_progress} : 2'b00; + +reg media_present[2]; +always @(posedge clk) if(mgmt_write && mgmt_address == 4'd0) media_present[mgmt_fddn] <= mgmt_writedata[0]; + +assign fdd0_inserted = media_present[0]; + +reg [1:0] wp_sys; +always @(posedge clk) if(mgmt_write && mgmt_address == 4'd1) wp_sys[mgmt_fddn] <= mgmt_writedata[0]; +wire [1:0] media_writeprotected = wp_sys | wp; + +(* ramstyle = "logic" *) reg [7:0] media_cylinders[2]; +always @(posedge clk) if(mgmt_write && mgmt_address == 4'd2) media_cylinders[mgmt_fddn] <= mgmt_writedata[7:0]; + +(* ramstyle = "logic" *) reg [7:0] media_sectors_per_track[2]; +always @(posedge clk) if(mgmt_write && mgmt_address == 4'd3) media_sectors_per_track[mgmt_fddn] <= mgmt_writedata[7:0]; + +(* ramstyle = "logic" *) reg [15:0] media_sector_count[2]; +always @(posedge clk) if(mgmt_write && mgmt_address == 4'd4 && ~mgmt_fddn) media_sector_count[0] <= mgmt_writedata; +always @(posedge clk) if(mgmt_write && mgmt_address == 4'd4 && mgmt_fddn) media_sector_count[1] <= mgmt_writedata; + +(* ramstyle = "logic" *) reg [1:0] media_heads[2]; +always @(posedge clk) if(mgmt_write && mgmt_address == 4'd5) media_heads[mgmt_fddn] <= mgmt_writedata[1:0]; + +wire fifo_read = mgmt_read && &mgmt_address; +wire fifo_write = mgmt_write && &mgmt_address; + +//------------------------------------------------------------------------------ io read + +wire ndma_read = io_read && io_address == 3'd5 && execute_ndma && cmd_read_normal_in_progress; +wire ndma_write = io_write && io_address == 3'd5 && execute_ndma && (cmd_write_normal_in_progress || cmd_format_in_progress); + +wire [7:0] io_readdata_prepare = + (io_address == 3'd2) ? { 2'b0, motor_enable[1], motor_enable[0], dma_irq_enable, enable, selected_drive } : //digital output register + (io_address == 3'd4) ? { datareg_ready, transfer_to_cpu, execute_ndma, busy, in_seek_mode } : //main status reg + (ndma_read) ? fifo_q : + (io_address == 3'd5) ? reply[7:0] : + (io_address == 3'd7) ? { change[selected_drive[0]], 7'h7F } : + 8'd0; + +always @(posedge clk) io_readdata <= io_readdata_prepare; + +//------------------------------------------------------------------------------ + +wire sw_reset = + (io_write && io_address == 3'h2 && io_writedata[2] == 1'b0 && enable) || + (io_write && io_address == 3'h4 && io_writedata[7]); + +reg selected_drive_r; +always @(posedge clk) selected_drive_r <= selected_drive[0]; + +wire [1:0] selected_drive = {1'b0, rst_n & +( + (io_write && io_address == 3'h2) ? io_writedata[0] : + (cmd_recalibrate_start) ? io_writedata[0] : + (cmd_seek_start) ? command[0] : + (cmd_read_id_start) ? io_writedata[0] : + (cmd_format_track_start) ? command[24] : + (cmd_read_write_start) ? command[48] : + selected_drive_r +)}; + +reg motor_enable[2]; +reg old_motor_enable[2]; +always @(posedge clk) begin + if(~rst_n) motor_enable[0] <= 1'b0; + else if(io_write && io_address == 3'h2) motor_enable[0] <= io_writedata[4]; + old_motor_enable[0] <= motor_enable[0]; +end + +always @(posedge clk) begin + if(~rst_n) motor_enable[1] <= 1'b0; + else if(io_write && io_address == 3'h2) motor_enable[1] <= io_writedata[5]; + old_motor_enable[1] <= motor_enable[1]; +end + +reg dma_irq_enable; +always @(posedge clk) begin + if(~rst_n) dma_irq_enable <= 1'b1; + else if(io_write && io_address == 3'h2) dma_irq_enable <= io_writedata[3]; +end + +reg enable; +always @(posedge clk) begin + if(~rst_n) enable <= 1'b1; + else if(io_write && io_address == 3'h2) enable <= io_writedata[2]; +end + +reg [1:0] data_rate; +always @(posedge clk) begin + if(~rst_n) data_rate <= 2'b10; + else if(io_write && io_address == 3'h4) data_rate <= io_writedata[1:0]; + else if(io_write && io_address == 3'h7) data_rate <= io_writedata[1:0]; +end + +reg datareg_ready; +always @(posedge clk) begin + if(~rst_n | sw_reset) datareg_ready <= 1'b1; + + else if(cmd_read_write_ok_at_start) datareg_ready <= 1'b0; + else if(cmd_read_id_ok_at_start) datareg_ready <= 1'b0; + else if(cmd_format_ok_at_start) datareg_ready <= 1'b0; + + else if(execute_ndma && state == S_WAIT_FOR_EMPTY_READ_FIFO && ndma_read) datareg_ready <= 1'b0; + else if(execute_ndma && state == S_WAIT_FOR_EMPTY_READ_FIFO && ~fifo_empty) datareg_ready <= 1'b1; + + else if(execute_ndma && state == S_WAIT_FOR_FULL_WRITE_FIFO && ndma_write) datareg_ready <= 1'b0; + else if(execute_ndma && state == S_WAIT_FOR_FULL_WRITE_FIFO) datareg_ready <= ~fifo_full; + + else if(execute_ndma && state == S_WAIT_FOR_FORMAT_INPUT && ndma_write) datareg_ready <= 1'b0; + else if(execute_ndma && state == S_WAIT_FOR_FORMAT_INPUT) datareg_ready <= format_data_count < 3'd4; + + else if(enter_result_phase) datareg_ready <= 1'b1; +end + +reg execute_ndma; +always @(posedge clk) begin + if(~rst_n) execute_ndma <= 1'b0; + else if(cmd_read_write_ok_at_start && ndma) execute_ndma <= 1'b1; + else if(cmd_format_ok_at_start && ndma) execute_ndma <= 1'b1; + else if(enter_result_phase) execute_ndma <= 1'b0; +end + +reg [2:0] ndma_irq_to; +always @(posedge clk) begin + if(ndma_read | ndma_write | ~execute_ndma) ndma_irq_to <= 0; + else if(~&ndma_irq_to) ndma_irq_to <= ndma_irq_to + 1'd1; +end + +reg ndma_irq; +always @(posedge clk) begin + if(~rst_n | sw_reset) ndma_irq <= 1'b0; + + else if(~execute_ndma) ndma_irq <= 1'b0; + + else if(state == S_WAIT_FOR_EMPTY_READ_FIFO && ndma_read) ndma_irq <= 1'b0; + else if(state == S_WAIT_FOR_EMPTY_READ_FIFO && ~fifo_empty && &ndma_irq_to) ndma_irq <= 1'b1; + + else if(state == S_WAIT_FOR_FULL_WRITE_FIFO && ndma_write) ndma_irq <= 1'b0; + else if(state == S_WAIT_FOR_FULL_WRITE_FIFO && ~fifo_full && &ndma_irq_to) ndma_irq <= 1'b1; + + else if(state == S_WAIT_FOR_FORMAT_INPUT && ndma_write) ndma_irq <= 1'b0; + else if(state == S_WAIT_FOR_FORMAT_INPUT && format_data_count < 3'd4 && &ndma_irq_to) ndma_irq <= 1'b1; +end + +reg transfer_to_cpu; +always @(posedge clk) begin + if(~rst_n | sw_reset) transfer_to_cpu <= 1'b0; + else if(command_first && ~enter_result_phase) transfer_to_cpu <= 1'b0; + else if(execute_ndma && state == S_WAIT_FOR_EMPTY_READ_FIFO) transfer_to_cpu <= 1'b1; + else if(enter_result_phase) transfer_to_cpu <= 1'b1; + else if(io_read && io_address == 3'd5 && reply_left == 4'd1) transfer_to_cpu <= 1'b0; +end + +reg busy; +always @(posedge clk) begin + if(~rst_n | sw_reset) busy <= 1'b0; + else if(command_first) busy <= 1'b1; + else if(cmd_recalibrate_start) busy <= 1'b0; + else if(cmd_seek_start) busy <= 1'b0; + else if(cmd_specify_start) busy <= 1'b0; + else if(cmd_configure_mode_start) busy <= 1'b0; + else if(cmd_perpendicular_mode_start) busy <= 1'b0; + else if(enter_result_phase) busy <= 1'b1; + else if(io_read && io_address == 3'd5 && reply_left == 4'd1) busy <= 1'b0; +end + +reg change[2]; +always @(posedge clk) begin + if(~rst_n) change[0] <= 1'b1; + else if(~media_present[0]) change[0] <= 1'b1; + else if(reset_changeline && ~selected_drive[0] && media_present[0]) change[0] <= 1'b0; +end + +always @(posedge clk) begin + if(~rst_n) change[1] <= 1'b1; + else if(~media_present[1]) change[1] <= 1'b1; + else if(reset_changeline && selected_drive[0] && media_present[1]) change[1] <= 1'b0; +end + +reg [3:0] in_seek_mode; +always @(posedge clk) begin + if(~rst_n | sw_reset) in_seek_mode <= 4'b0000; + else if(cmd_recalibrate_start) in_seek_mode <= 4'b0001 << io_writedata[0]; + else if(cmd_seek_start) in_seek_mode <= 4'b0001 << command[0]; +end + +//------------------------------------------------------------------------------ + +wire command_first = io_write && io_address == 3'h5 && state == S_IDLE && !command_left && ~busy; +wire command_next = io_write && io_address == 3'h5 && state == S_IDLE && command_left; + +reg [71:0] command; +always @(posedge clk) begin + if(~rst_n) command <= 72'd0; + else if(command_first) command <= io_writedata; + else if(command_next) command <= { command[63:0], io_writedata }; +end + +wire [3:0] command_at_first = + (io_writedata[4:0] == 5'h03) ? 4'd2 : //specify command + (io_writedata[4:0] == 5'h04) ? 4'd1 : //get status + (io_writedata[4:0] == 5'h07) ? 4'd1 : //recalibrate + (io_writedata[4:0] == 5'h0F) ? 4'd2 : //seek + (io_writedata[4:0] == 5'h0A) ? 4'd1 : //read ID + (io_writedata[4:0] == 5'h0D) ? 4'd5 : //format track + (io_writedata[4:0] == 5'h05) ? 4'd8 : //write normal data + (io_writedata[4:0] == 5'h06) ? 4'd8 : //read normal data + (io_writedata[4:0] == 5'h12) ? 4'd1 : //perpendicular mode (Enhanced) + (io_writedata[4:0] == 5'h13) ? 4'd3 : //configure command (Enhanced) + 4'd0; + +reg [3:0] command_size; +always @(posedge clk) begin + if(~rst_n) command_size <= 4'd0; + else if(command_first && command_at_first) command_size <= command_at_first; +end + +reg [3:0] command_left; +always @(posedge clk) begin + if(~rst_n) command_left <= 4'd0; + else if(command_first && command_at_first) command_left <= command_at_first; + else if(command_next) command_left <= command_left - 4'd1; +end + +reg [7:0] pending_command; +always @(posedge clk) begin + if(~rst_n | sw_reset) pending_command <= 8'h00; + else if(cmd_read_write_ok_at_start) pending_command <= command[63:56]; + else if(cmd_read_id_ok_at_start) pending_command <= 8'h0A; + else if(cmd_format_ok_at_start) pending_command <= command[39:32]; + else if(cmd_recalibrate_start) pending_command <= 8'h07; + else if(enter_result_phase) pending_command <= 8'h00; +end + +wire cmd_sense_interrupt_status_start = command_first && io_writedata[4:0] == 5'h08; //enters result phase +wire cmd_dump_registers_start = command_first && io_writedata[4:0] == 5'h0E; //enters result phase +wire cmd_version_start = command_first && io_writedata[4:0] == 5'h10; //enters result phase +wire cmd_unlock_start = command_first && io_writedata == 8'h14; //enters result phase +wire cmd_lock_start = command_first && io_writedata == 8'h94; //enters result phase + +wire cmd_specify_start = command_size == 4'd2 && command_next && command_left == 4'd1 && command[12:8] == 5'h03; //immediate finish +wire cmd_get_status_start = command_size == 4'd1 && command_next && command_left == 4'd1 && command[4:0] == 5'h04; //enters result phase +wire cmd_recalibrate_start = command_size == 4'd1 && command_next && command_left == 4'd1 && command[4:0] == 5'h07; //interrupt after delay +wire cmd_seek_start = command_size == 4'd2 && command_next && command_left == 4'd1 && command[12:8] == 5'h0F; //interrupt after delay +wire cmd_read_id_start = command_size == 4'd1 && command_next && command_left == 4'd1 && command[4:0] == 5'h0A; //enters result phase +wire cmd_format_track_start = command_size == 4'd5 && command_next && command_left == 4'd1 && command[36:32] == 5'h0D; //enters result pahse +wire cmd_write_normal_start = command_size == 4'd8 && command_next && command_left == 4'd1 && command[60:56] == 5'h05; //enters result phase +wire cmd_read_normal_start = command_size == 4'd8 && command_next && command_left == 4'd1 && command[60:56] == 5'h06; //enters result phase + +wire cmd_perpendicular_mode_start = command_size == 4'd1 && command_next && command_left == 4'd1 && command[4:0] == 5'h12; //immediate finish +wire cmd_configure_mode_start = command_size == 4'd3 && command_next && command_left == 4'd1 && command[20:16] == 5'h13; //immediate finish + +wire cmd_invalid_start = command_first && + io_writedata[4:0] != 5'h03 && + io_writedata[4:0] != 5'h04 && + io_writedata[4:0] != 5'h05 && + io_writedata[4:0] != 5'h06 && + io_writedata[4:0] != 5'h07 && + io_writedata[4:0] != 5'h08 && + io_writedata[4:0] != 5'h0A && + io_writedata[4:0] != 5'h0D && + io_writedata[4:0] != 5'h0E && + io_writedata[4:0] != 5'h0F && + io_writedata[4:0] != 5'h10 && + io_writedata[4:0] != 5'h12 && + io_writedata[4:0] != 5'h13 && + io_writedata[4:0] != 5'h14; + +wire cmd_read_write_start = cmd_read_normal_start || cmd_write_normal_start; + +wire cmd_read_normal_in_progress = pending_command[4:0] == 5'h06; +wire cmd_write_normal_in_progress = pending_command[4:0] == 5'h05; +wire cmd_format_in_progress = pending_command[4:0] == 5'h0D; +wire cmd_recalibrate_in_progress = pending_command[4:0] == 5'h07; +wire cmd_read_id_in_progress = pending_command[4:0] == 5'h0A; + +wire enter_result_phase = + cmd_invalid_start || cmd_sense_interrupt_status_start || cmd_dump_registers_start || cmd_version_start || cmd_unlock_start || cmd_lock_start || + (cmd_read_write_start && (cmd_read_write_incorrect_head_at_start || cmd_read_write_incorrect_sector_at_start || cmd_write_and_writeprotected_at_start)) || + (state == S_CHECK_TC && (cmd_read_write_finish || cmd_format_finish)) || + (cmd_format_track_start && cmd_format_writeprotected_at_start) || + (state == S_WAIT_FOR_FORMAT_INPUT && cmd_format_in_input_finish) || + cmd_get_status_start || + cmd_read_id_finished; + +wire raise_interrupt = dma_irq_enable && ( + (cmd_read_write_start && (cmd_read_write_incorrect_head_at_start || cmd_read_write_incorrect_sector_at_start)) || + (cmd_write_normal_start && cmd_write_and_writeprotected_at_start) || + (state == S_CHECK_TC && (cmd_read_write_finish || cmd_format_finish)) || + (cmd_format_track_start && cmd_format_writeprotected_at_start) || + (state == S_WAIT_FOR_FORMAT_INPUT && cmd_format_in_input_finish) || + delay_last_cycle || + cmd_read_id_finished +); + +wire reset_changeline = + (cmd_read_write_ok_at_start) || + (state == S_UPDATE_SECTOR && increment_cylinder) || + (cmd_recalibrate_start && cylinder[selected_drive[0]] != 8'd0) || + (cmd_seek_start && cylinder[selected_drive[0]] != io_writedata) || + (old_motor_enable[selected_drive[0]] & ~motor_enable[selected_drive[0]]); // on-off-on trick to clear the change status in win98 + + +//------------------------------------------------------------------------------ cmd: read / write + +wire cmd_read_write_hang_at_start = + ~motor_enable[selected_drive[0]] || //motor off + ~media_present[selected_drive[0]] || //no media + command[23:16] != 8'h02 || //invalid sector size + command[47:40] >= media_cylinders[selected_drive[0]]; //invalid cylinder + +wire cmd_read_write_incorrect_head_at_start = motor_enable[selected_drive[0]] && (command[50] != command[32] || (command[32] && media_heads[selected_drive[0]] == 2'd1)); +wire cmd_read_write_incorrect_sector_at_start = ~cmd_read_write_hang_at_start && (command[31:24] > media_sectors_per_track[selected_drive[0]] || command[31:24] > command[15:8]); +wire cmd_write_and_writeprotected_at_start = ~cmd_read_write_hang_at_start && ~cmd_read_write_incorrect_sector_at_start && cmd_write_normal_start && media_writeprotected[selected_drive[0]]; + +wire cmd_read_write_ok_at_start = + cmd_read_write_start && ~cmd_read_write_hang_at_start && ~cmd_read_write_incorrect_head_at_start && ~cmd_read_write_incorrect_sector_at_start && ~cmd_write_and_writeprotected_at_start; + +reg cmd_read_write_multitrack; +always @(posedge clk) begin + if(~rst_n) cmd_read_write_multitrack <= 1'b0; + else if(cmd_read_write_ok_at_start) cmd_read_write_multitrack <= command[63]; +end + +wire cmd_read_write_finish = + (cmd_read_normal_in_progress || cmd_write_normal_in_progress) && + ((~execute_ndma && dma_has_terminated) || (execute_ndma && cmd_read_write_was_ndma_terminal)); + +reg cmd_read_write_was_ndma_terminal; +always @(posedge clk) begin + if(~rst_n) cmd_read_write_was_ndma_terminal <= 1'd0; + else if(state == S_UPDATE_SECTOR && sector[selected_drive[0]] == eot[selected_drive[0]] && + {1'b0, head[selected_drive[0]] } == (media_heads[selected_drive[0]] - 2'd1)) cmd_read_write_was_ndma_terminal <= 1'd1; + else if(state == S_UPDATE_SECTOR) cmd_read_write_was_ndma_terminal <= 1'd0; +end + +//------------------------------------------------------------------------------ cmd: read id + +wire cmd_read_id_hang_at_start = + ~motor_enable[selected_drive[0]] || //motor off + ~media_present[selected_drive[0]]; //no media + +wire cmd_read_id_ok_at_start = cmd_read_id_start && ~cmd_read_id_hang_at_start; +wire cmd_read_id_finished = state == S_WAIT && !command_wait_counter && cmd_read_id_in_progress; + +//------------------------------------------------------------------------------ cmd: specify + +reg [3:0] specify_srt; +always @(posedge clk) begin + if(~rst_n) specify_srt <= 4'd0; + else if(cmd_specify_start) specify_srt <= command[7:4]; +end + +reg [3:0] specify_hut; +always @(posedge clk) begin + if(~rst_n) specify_hut <= 4'b0; + else if(cmd_specify_start) specify_hut <= command[3:0]; +end + +reg [6:0] specify_hlt; +always @(posedge clk) begin + if(~rst_n) specify_hlt <= 7'b0; + else if(cmd_specify_start) specify_hlt <= io_writedata[7:1]; +end + +reg ndma; +always @(posedge clk) begin + if(~rst_n) ndma <= 1'b0; + else if(cmd_specify_start) ndma <= io_writedata[0]; +end + +//------------------------------------------------------------------------------ cmd: sense interrupt status + +always @(posedge clk) begin + reg old_enable; + + old_enable <= enable; + + if(~rst_n | sw_reset) irq <= 1'b0; + else if(~old_enable & enable) irq <= 1'b1; + else if(ndma_write | ndma_read) irq <= 1'b0; + else if(ndma_irq | raise_interrupt) irq <= 1'b1; + else if(io_read && io_address == 3'd5 && ~ndma_read) irq <= 1'b0; +end + +reg [2:0] reset_sensei; +always @(posedge clk) begin + if(~rst_n) reset_sensei <= 3'd0; + else if(sw_reset) reset_sensei <= 3'd4; + else if(raise_interrupt) reset_sensei <= 3'd0; + else if(cmd_sense_interrupt_status_start && reset_sensei) reset_sensei <= reset_sensei - 3'd1; +end + +wire [1:0] reset_sensei_drive = + (reset_sensei == 3'd4) ? 2'd0 : + (reset_sensei == 3'd3) ? 2'd1 : + (reset_sensei == 3'd2) ? 2'd2 : + 2'd3; + +reg pending_interrupt; +always @(posedge clk) begin + if(~rst_n) pending_interrupt <= 1'b0; + else if(raise_interrupt) pending_interrupt <= 1'b1; + else if(~irq) pending_interrupt <= 1'b0; +end + +reg pending_interrupt_last; +always @(posedge clk) pending_interrupt_last <= pending_interrupt; + +//------------------------------------------------------------------------------ cmd: recalibrate / seek + +reg [7:0] delay_steps; +always @(posedge clk) begin + if(~rst_n) delay_steps <= 8'd0; + else if(cmd_recalibrate_start) delay_steps <= (cylinder[selected_drive[0]] == 8'd0)? 8'd0 : cylinder[selected_drive[0]] - 8'd1; + else if(cmd_seek_start) delay_steps <= (cylinder[selected_drive[0]] == io_writedata)? 8'd0 : (cylinder[selected_drive[0]] > io_writedata)? cylinder[selected_drive[0]] - io_writedata - 8'd1 : io_writedata - cylinder[selected_drive[0]] - 8'd1; + else if(!delay_rate && !delay_srt && delay_steps) delay_steps <= delay_steps - 8'd1; +end + +reg [3:0] delay_srt; +always @(posedge clk) begin + if(~rst_n) delay_srt <= 4'd0; + else if(cmd_recalibrate_start) delay_srt <= specify_srt; + else if(cmd_seek_start) delay_srt <= specify_srt; + else if(!delay_rate && delay_srt) delay_srt <= delay_srt - 4'd1; + else if(!delay_rate && delay_steps) delay_srt <= specify_srt; +end + +wire [27:0] delay_adder = (data_rate == 2'd0)? 28'd1000 : (data_rate == 2'd1)? 28'd600 : (data_rate == 2'd2)? 28'd500 : 28'd2000; + +reg [27:0] delay_adder_r; +always @(posedge clk) begin + if(cmd_recalibrate_start) delay_adder_r <= delay_adder; + else if(cmd_seek_start) delay_adder_r <= delay_adder; + else if(delay_srt || delay_steps) delay_adder_r <= delay_adder; +end + +reg [27:0] delay_rate; +always @(posedge clk) begin + if(~rst_n) delay_rate <= 0; + else if(cmd_recalibrate_start) delay_rate <= delay_adder; + else if(cmd_seek_start) delay_rate <= delay_adder; + else if(delay_rate >= clk_rate) delay_rate <= 1; + else if(delay_rate == 1) delay_rate <= 0; + else if(delay_rate) delay_rate <= delay_rate + delay_adder_r; + else if(delay_srt || delay_steps) delay_rate <= delay_adder; +end + +wire delay_last_cycle = !delay_steps && !delay_srt && delay_rate == 16'd1; + +reg [7:0] status_reg0_temp; +always @(posedge clk) begin + if(~rst_n) status_reg0_temp <= 8'd0; + else if(pending_interrupt && ~pending_interrupt_last) status_reg0_temp <= reply[7:0]; + else if(cmd_sense_interrupt_status_start && reset_sensei) status_reg0_temp <= { 4'hC, 2'b00, reset_sensei_drive }; +end + +//------------------------------------------------------------------------------ cmd: configure / lock / unlock + +reg [7:0] config_config; +always @(posedge clk) begin + if(~rst_n) config_config <= 8'h20; + else if(cmd_configure_mode_start) config_config <= command[7:0]; +end + +reg [7:0] config_pretrk; +always @(posedge clk) begin + if(~rst_n) config_pretrk <= 8'd0; + else if(cmd_configure_mode_start) config_pretrk <= io_writedata; +end + +reg [7:0] perp_mode; +always @(posedge clk) begin + if(~rst_n) perp_mode <= 8'd0; + else if(cmd_perpendicular_mode_start) perp_mode <= io_writedata; +end + +reg lock; +always @(posedge clk) begin + if(~rst_n) lock <= 1'd0; + else if(cmd_unlock_start) lock <= 1'd0; + else if(cmd_lock_start) lock <= 1'd1; +end + +//------------------------------------------------------------------------------ cmd: format + +wire cmd_format_writeprotected_at_start = ~cmd_format_hang_on_start && cmd_format_track_start && media_writeprotected[selected_drive[0]]; + +wire cmd_format_hang_on_start = + ~motor_enable[selected_drive[0]] || //motor off + ~media_present[selected_drive[0]] || //no media + command[23:16] != 8'h02 || //invalid sector size + command[15:8] != media_sectors_per_track[selected_drive[0]]; //invalid secotr count + +wire cmd_format_ok_at_start = cmd_format_track_start && ~cmd_format_writeprotected_at_start && ~cmd_format_hang_on_start; + +reg [31:0] format_data; +always @(posedge clk) begin + if(~rst_n) format_data <= 32'd0; + else if(ndma_write && format_data_count < 3'd4) format_data <= { format_data[23:0], io_writedata }; + else if(dma_ack && format_data_count < 3'd4) format_data <= { format_data[23:0], dma_readdata }; +end + +reg [2:0] format_data_count; +always @(posedge clk) begin + if(~rst_n) format_data_count <= 3'd0; + else if(state != S_WAIT_FOR_FORMAT_INPUT) format_data_count <= 3'd0; + else if((ndma_write || dma_ack) && format_data_count < 3'd4) format_data_count <= format_data_count + 3'd1; +end + +reg [7:0] format_filler_byte; +always @(posedge clk) begin + if(~rst_n) format_filler_byte <= 8'd0; + else if(cmd_format_ok_at_start) format_filler_byte <= io_writedata; +end + +reg [7:0] format_sector_count; +always @(posedge clk) begin + if(~rst_n) format_sector_count <= 8'd0; + else if(cmd_format_ok_at_start) format_sector_count <= command[15:8]; + else if(state == S_SD_FORMAT_WAIT_FOR_FILL && &format_counter && fifo_read) format_sector_count <= format_sector_count - 8'd1; +end + +wire cmd_format_in_input_finish = ~execute_ndma && dma_has_terminated; + +wire cmd_format_finish = cmd_format_in_progress && ( + cmd_format_in_input_finish || + (execute_ndma && !format_sector_count) +); + +//------------------------------------------------------------------------------ reply + +reg [3:0] reply_left; +always @(posedge clk) begin + if(~rst_n | sw_reset) reply_left <= 4'd0; + else if(cmd_invalid_start) reply_left <= 4'd1; + else if(cmd_read_write_start && cmd_read_write_incorrect_head_at_start) reply_left <= 4'd7; + else if(cmd_read_write_start && cmd_read_write_incorrect_sector_at_start) reply_left <= 4'd7; + else if(cmd_write_normal_start && cmd_write_and_writeprotected_at_start) reply_left <= 4'd7; + else if(cmd_format_track_start && cmd_format_writeprotected_at_start) reply_left <= 4'd7; + else if(state == S_CHECK_TC && (cmd_read_write_finish || cmd_format_finish)) reply_left <= 4'd7; + else if(state == S_WAIT_FOR_FORMAT_INPUT && cmd_format_in_input_finish) reply_left <= 4'd7; + else if(cmd_read_id_finished) reply_left <= 4'd7; + else if(cmd_get_status_start) reply_left <= 4'd1; + else if(cmd_sense_interrupt_status_start) reply_left <= 4'd2; + else if(cmd_dump_registers_start) reply_left <= 4'd10; + else if(cmd_version_start) reply_left <= 4'd1; + else if(cmd_unlock_start || cmd_lock_start) reply_left <= 4'd1; + else if(io_read && io_address == 3'h5 && reply_left) reply_left <= reply_left - 3'd1; +end + +reg [79:0] reply; +always @(posedge clk) begin + if(~rst_n | sw_reset) reply <= 80'd0; + else if(cmd_invalid_start) reply <= { reply[79:8], 8'h80 }; + else if(delay_last_cycle && cmd_recalibrate_in_progress) reply <= { reply[79:8], 8'h20 | { 6'd0, selected_drive } | ((~motor_enable[selected_drive[0]])? 8'h50 : 8'h00) }; + else if(delay_last_cycle) reply <= { reply[79:8], 8'h20 | { 5'd0, head[selected_drive[0]], selected_drive } }; + else if(cmd_read_write_start && cmd_read_write_incorrect_head_at_start) reply <= { 24'd0, 8'd2, sector[selected_drive[0]], 7'b0,head[selected_drive[0]], cylinder[selected_drive[0]], 8'h00, 8'h04, (8'h40 | { 5'd0, head[selected_drive[0]], selected_drive }) }; + else if(cmd_read_write_start && cmd_read_write_incorrect_sector_at_start) reply <= { 24'd0, 8'd2, command[31:24], 7'b0,command[32], command[47:40], 8'h00, 8'h04, (8'h40 | { 5'd0, command[32], selected_drive }) }; + else if(cmd_write_normal_start && cmd_write_and_writeprotected_at_start) reply <= { 24'd0, 8'd2, command[31:24], 7'b0,command[32], command[47:40], 8'h31, 8'h27, (8'h40 | { 5'd0, command[32], selected_drive }) }; + else if(cmd_format_track_start && cmd_format_writeprotected_at_start) reply <= { 24'd0, 8'd2, sector[selected_drive[0]], 7'b0,command[26], cylinder[selected_drive[0]], 8'h31, 8'h27, (8'h40 | { 5'd0, command[26], selected_drive }) }; + else if(state == S_CHECK_TC && cmd_read_write_finish) reply <= { 24'd0, 8'd2, sector[selected_drive[0]], 7'b0,head[selected_drive[0]], cylinder[selected_drive[0]], 8'h00, 8'h00, (8'h00 | { 5'd0, head[selected_drive[0]], selected_drive }) }; + else if(state == S_CHECK_TC && cmd_format_finish) reply <= { 24'd0, 8'd2, sector[selected_drive[0]], 7'b0,head[selected_drive[0]], cylinder[selected_drive[0]], 8'h00, 8'h00, (8'h00 | { 5'd0, head[selected_drive[0]], selected_drive }) }; + else if(state == S_WAIT_FOR_FORMAT_INPUT && cmd_format_in_input_finish) reply <= { 24'd0, 8'd2, sector[selected_drive[0]], 7'b0,head[selected_drive[0]], cylinder[selected_drive[0]], 8'h00, 8'h00, (8'h40 | { 5'd0, head[selected_drive[0]], selected_drive }) }; + else if(cmd_read_id_finished) reply <= { 24'd0, 8'd2, sector[selected_drive[0]], 7'b0,head[selected_drive[0]], cylinder[selected_drive[0]], 8'h00, 8'h00, (8'h00 | { 5'd0, head[selected_drive[0]], selected_drive }) }; + else if(cmd_get_status_start) reply <= { 72'd0, 1'b0, media_writeprotected[io_writedata[0]], 1'b1, !cylinder[io_writedata[0]], 1'b1, io_writedata[2], 1'b0, io_writedata[0] }; + else if(cmd_sense_interrupt_status_start && reset_sensei) reply <= { 64'd0, cylinder[selected_drive[0]], 4'hC, 2'b00, reset_sensei_drive }; + else if(cmd_sense_interrupt_status_start && pending_interrupt) reply <= { 64'd0, cylinder[selected_drive[0]], status_reg0_temp }; + else if(cmd_sense_interrupt_status_start && ~pending_interrupt) reply <= { 64'd0, cylinder[selected_drive[0]], 8'h80 }; + else if(cmd_dump_registers_start) reply <= { config_pretrk, config_config, lock, perp_mode[6:0], eot[selected_drive[0]], + specify_hlt, ndma, specify_srt, specify_hut, 8'h0, 8'h0, 8'h0, cylinder[selected_drive[0]] }; + else if(cmd_version_start) reply <= { 72'd0, 8'h90 }; + else if(cmd_unlock_start) reply <= 80'd0; + else if(cmd_lock_start) reply <= { 72'd0, 8'h10 }; + else if(io_read && io_address == 3'h5) reply <= { 8'd0, reply[79:8] }; +end + +//------------------------------------------------------------------------------ state + +localparam [3:0] S_IDLE = 0; + +localparam [3:0] S_PREPARE_COUNT = 1; +localparam [3:0] S_COUNT_LOGICAL = 2; + +localparam [3:0] S_PREPARE = 3; + +localparam [3:0] S_SD_CONTROL = 4; +localparam [3:0] S_SD_READ_WAIT_FOR_DATA = 5; +localparam [3:0] S_WAIT_FOR_EMPTY_READ_FIFO = 6; + +localparam [3:0] S_UPDATE_SECTOR = 7; +localparam [3:0] S_CHECK_TC = 8; +localparam [3:0] S_WAIT = 9; + +localparam [3:0] S_WAIT_FOR_FULL_WRITE_FIFO = 10; +localparam [3:0] S_SD_WRITE_WAIT_FOR_EMPTY_FIFO = 11; + +localparam [3:0] S_WAIT_FOR_FORMAT_INPUT = 12; +localparam [3:0] S_SD_FORMAT_WAIT_FOR_FILL = 13; + +reg [3:0] state; +always @(posedge clk) begin + if(~rst_n) state <= S_IDLE; + + //start read/write + else if(state == S_IDLE && cmd_read_write_ok_at_start) state <= S_PREPARE_COUNT; + + //read + else if(state == S_COUNT_LOGICAL && !mult_b && cmd_read_normal_in_progress) state <= S_PREPARE; + //sd + else if(state == S_SD_CONTROL && cmd_read_normal_in_progress) state <= S_SD_READ_WAIT_FOR_DATA; + else if(state == S_SD_READ_WAIT_FOR_DATA && fifo_full) state <= S_WAIT_FOR_EMPTY_READ_FIFO; + else if(state == S_WAIT_FOR_EMPTY_READ_FIFO && fifo_empty) state <= S_UPDATE_SECTOR; + + //write + else if(state == S_COUNT_LOGICAL && !mult_b && cmd_write_normal_in_progress) state <= S_WAIT_FOR_FULL_WRITE_FIFO; + else if(state == S_WAIT_FOR_FULL_WRITE_FIFO && fifo_full) state <= S_PREPARE; + //sd + else if(state == S_SD_CONTROL && cmd_write_normal_in_progress) state <= S_SD_WRITE_WAIT_FOR_EMPTY_FIFO; + else if(state == S_SD_WRITE_WAIT_FOR_EMPTY_FIFO && fifo_empty) state <= S_UPDATE_SECTOR; + + //format + else if(state == S_IDLE && cmd_format_ok_at_start) state <= S_WAIT_FOR_FORMAT_INPUT; + else if(state == S_WAIT_FOR_FORMAT_INPUT && cmd_format_in_input_finish) state <= S_IDLE; + else if(state == S_WAIT_FOR_FORMAT_INPUT && format_data_count == 3'd4) state <= S_PREPARE_COUNT; + //count + else if(state == S_COUNT_LOGICAL && !mult_b && cmd_format_in_progress) state <= S_PREPARE; + //sd + else if(state == S_SD_CONTROL && cmd_format_in_progress) state <= S_SD_FORMAT_WAIT_FOR_FILL; + else if(state == S_SD_FORMAT_WAIT_FOR_FILL && &format_counter && fifo_read) state <= S_WAIT; + + //read id + else if(state == S_IDLE && cmd_read_id_ok_at_start) state <= S_WAIT; + else if(state == S_WAIT && !command_wait_counter && cmd_read_id_in_progress) state <= S_IDLE; + + //count + else if(state == S_PREPARE_COUNT) state <= S_COUNT_LOGICAL; + + //sd read/write + else if(state == S_PREPARE) state <= S_SD_CONTROL; + + //update read/write/format + else if(state == S_UPDATE_SECTOR) state <= S_WAIT; + else if(state == S_WAIT && !command_wait_counter && ~cmd_read_id_in_progress) state <= S_CHECK_TC; + else if(state == S_CHECK_TC && (cmd_read_write_finish || cmd_format_finish)) state <= S_IDLE; + else if(state == S_CHECK_TC && cmd_format_in_progress) state <= S_WAIT_FOR_FORMAT_INPUT; + else if(state == S_CHECK_TC) state <= S_PREPARE_COUNT; +end + +reg [15:0] command_wait_counter; +always @(posedge clk) begin + if(~rst_n) command_wait_counter <= 0; + else if(state != S_WAIT) command_wait_counter <= 4000; // was calculated floppy_wait_cycles but was buggy, so use fixed wait time + else if(state == S_WAIT && command_wait_counter) command_wait_counter <= command_wait_counter - 16'd1; +end + +//------------------------------------------------------------------------------ count logical sector + +reg [15:0] mult_a; //sectors per track * heads +always @(posedge clk) begin + if(~rst_n) mult_a <= 16'd0; + else if(state == S_PREPARE_COUNT) mult_a <= (media_heads[selected_drive[0]] == 2'd2) ? { 7'd0, media_sectors_per_track[selected_drive[0]], 1'b0 } : { 8'b0, media_sectors_per_track[selected_drive[0]] }; + else if(state == S_COUNT_LOGICAL) mult_a <= { mult_a[14:0], 1'b0 }; +end + +reg [7:0] mult_b; //cylinder +always @(posedge clk) begin + if(~rst_n) mult_b <= 8'd0; + else if(state == S_PREPARE_COUNT) mult_b <= cylinder[selected_drive[0]]; + else if(state == S_COUNT_LOGICAL) mult_b <= { 1'b0, mult_b[7:1] }; +end + +reg [15:0] logical_sector; +always @(posedge clk) begin + if(~rst_n) logical_sector <= 16'd0; + else if(state == S_PREPARE_COUNT) logical_sector <= (head[selected_drive[0]] ? {8'd0, media_sectors_per_track[selected_drive[0]]} : 16'd0) + {8'd0, sector[selected_drive[0]]} - 16'd1; + else if(state == S_COUNT_LOGICAL && mult_b[0]) logical_sector <= logical_sector + mult_a; +end + +//------------------------------------------------------------------------------ location + +wire increment_only_sector = sector[selected_drive[0]] < eot[selected_drive[0]] && sector[selected_drive[0]] < media_sectors_per_track[selected_drive[0]]; +wire increment_cylinder = ~increment_only_sector && (~cmd_read_write_multitrack || head[selected_drive[0]] == 1'b1); + +(* ramstyle = "logic" *) reg [7:0] cylinder[2]; +always @(posedge clk) begin + if(~rst_n | sw_reset) begin cylinder[0] <= 8'd0; cylinder[1] <= 8'd0; end + else if(cmd_read_write_start && (cmd_read_write_incorrect_sector_at_start || cmd_write_and_writeprotected_at_start)) cylinder[selected_drive[0]] <= command[47:40]; + else if(cmd_read_write_ok_at_start) cylinder[selected_drive[0]] <= command[47:40]; + else if(cmd_recalibrate_start) cylinder[selected_drive[0]] <= 8'd0; + else if(cmd_seek_start) cylinder[selected_drive[0]] <= io_writedata; + else if(state == S_UPDATE_SECTOR && increment_cylinder) cylinder[selected_drive[0]] <= (cylinder[selected_drive[0]] >= media_cylinders[selected_drive[0]])? media_cylinders[selected_drive[0]] - 8'd1 : cylinder[selected_drive[0]] + 8'd1; + else if(state == S_WAIT_FOR_FORMAT_INPUT && format_data_count == 3'd4) cylinder[selected_drive[0]] <= format_data[31:24]; +end + +reg head[2]; +always @(posedge clk) begin + if(~rst_n | sw_reset) begin head[0] <= 1'd0; head[1] <= 1'd0; end + else if(cmd_read_write_start && (cmd_read_write_incorrect_sector_at_start || cmd_write_and_writeprotected_at_start)) head[selected_drive[0]] <= command[32]; + else if(cmd_format_track_start && cmd_format_writeprotected_at_start) head[selected_drive[0]] <= command[26]; + else if(cmd_read_write_ok_at_start) head[selected_drive[0]] <= command[32]; + else if(cmd_format_ok_at_start) head[selected_drive[0]] <= command[26]; + else if(cmd_get_status_start) head[selected_drive[0]] <= io_writedata[2]; + else if(cmd_seek_start) head[selected_drive[0]] <= command[2]; + else if(cmd_read_id_start) head[selected_drive[0]] <= io_writedata[2]; + else if(state == S_UPDATE_SECTOR && ~increment_only_sector && cmd_read_write_multitrack) head[selected_drive[0]] <= ~head[selected_drive[0]]; +end + +(* ramstyle = "logic" *) reg [7:0] sector[2]; +always @(posedge clk) begin + if(~rst_n | sw_reset) begin sector[0] <= 8'd1; sector[1] <= 8'd1; end + else if(cmd_read_write_start && (cmd_read_write_incorrect_sector_at_start || cmd_write_and_writeprotected_at_start)) sector[selected_drive[0]] <= command[31:24]; + else if(cmd_read_write_ok_at_start) sector[selected_drive[0]] <= command[31:24]; + else if(state == S_UPDATE_SECTOR && increment_only_sector) sector[selected_drive[0]] <= sector[selected_drive[0]] + 8'd1; + else if(state == S_UPDATE_SECTOR && ~increment_only_sector) sector[selected_drive[0]] <= 8'd1; + else if(state == S_WAIT_FOR_FORMAT_INPUT && format_data_count == 3'd4) sector[selected_drive[0]] <= format_data[15:8]; +end + +(* ramstyle = "logic" *) reg [7:0] eot[2]; +always @(posedge clk) begin + if(~rst_n | sw_reset) begin eot[0] <= 8'd0; eot[1] <= 8'd0; end + else if(cmd_read_write_ok_at_start) eot[selected_drive[0]] <= (command[15:8] == 8'd0)? media_sectors_per_track[selected_drive[0]] : command[15:8]; +end + +//------------------------------------------------------------------------------ sd + +reg [8:0] format_counter; +always @(posedge clk) begin + if(state == S_IDLE) format_counter <= 9'd0; + else if(fifo_read) format_counter <= format_counter + 9'd1; +end + +reg [15:0] sd_sector; +always @(posedge clk) begin + if(~rst_n) sd_sector <= 16'd0; + else if(state == S_PREPARE) sd_sector <= (logical_sector >= media_sector_count[selected_drive[0]])? media_sector_count[selected_drive[0]] - 1'd1 : logical_sector; +end + +//------------------------------------------------------------------------------ dma + +assign dma_writedata = fifo_q; + +assign dma_req = ~execute_ndma && ~dma_has_terminated && dma_irq_enable && ~dma_ack && ( + (cmd_read_normal_in_progress && ~fifo_empty && state == S_WAIT_FOR_EMPTY_READ_FIFO) || + (cmd_write_normal_in_progress && ~fifo_full && state == S_WAIT_FOR_FULL_WRITE_FIFO) || + (cmd_format_in_progress && format_data_count < 3'd4 && state == S_WAIT_FOR_FORMAT_INPUT) +); + +reg dma_has_terminated; +always @(posedge clk) begin + if(~rst_n) dma_has_terminated <= 1'd0; + else if(state == S_IDLE) dma_has_terminated <= 1'd0; + else if(dma_tc) dma_has_terminated <= 1'd1; +end + +//------------------------------------------------------------------------------ fifo + +wire [9:0] fifo_count; +wire fifo_empty; +wire fifo_full = fifo_count[9]; +wire [7:0] fifo_q; + +reg [7:0] fifo_readdata; +always @(posedge clk) begin + if(~rst_n) fifo_readdata <= 8'b0; + else if(cmd_format_in_progress) fifo_readdata <= format_filler_byte; + else fifo_readdata <= fifo_q; +end + +wire fifo_from_pc = (state == S_WAIT_FOR_FULL_WRITE_FIFO); +wire fifo_to_pc = (state == S_WAIT_FOR_EMPTY_READ_FIFO); +wire fifo_pc_wr = ((ndma_write || (~execute_ndma && dma_ack) || (~execute_ndma && dma_has_terminated)) && ~fifo_full); +wire fifo_pc_rd = (ndma_read || (~execute_ndma && dma_ack)); + +simple_fifo #( + .width (8), + .widthu (10) +) +fifo_to_floppy_inst ( + .clk (clk), + .rst_n (rst_n), + + .sclr (state == S_IDLE), + + .data (fifo_from_pc ? (execute_ndma ? io_writedata : dma_has_terminated ? 8'h00 : dma_readdata) : mgmt_writedata[7:0]), + .wrreq (fifo_from_pc ? fifo_pc_wr : fifo_write), + + .rdreq (fifo_to_pc ? fifo_pc_rd : fifo_read), + .q (fifo_q), + + .empty (fifo_empty), + .usedw (fifo_count) +); + +//------------------------------------------------------------------------------ + +endmodule diff --git a/rtl/common/simple_fifo.v b/rtl/common/simple_fifo.v new file mode 100644 index 0000000..7e2da82 --- /dev/null +++ b/rtl/common/simple_fifo.v @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, Aleksander Osman + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module simple_fifo +#( + parameter width = 1, + parameter widthu = 1 +) +( + input clk, + input rst_n, + input sclr, + + input rdreq, + input wrreq, + input [width-1:0] data, + + output empty, + output reg full, + output [width-1:0] q, + output reg [widthu-1:0] usedw +); + + +reg [width-1:0] mem [(2**widthu)-1:0]; + +reg [widthu-1:0] rd_index = 0; +reg [widthu-1:0] wr_index = 0; + +assign q = mem[rd_index]; +assign empty= usedw == 0 && ~(full); + +always @(posedge clk) begin + if(rst_n == 1'b0) rd_index <= 0; + else if(sclr) rd_index <= 0; + else if(rdreq && ~(empty)) rd_index <= rd_index + { {widthu-1{1'b0}}, 1'b1 }; +end + +always @(posedge clk) begin + if(rst_n == 1'b0) wr_index <= 0; + else if(sclr) wr_index <= 0; + else if(wrreq && (~(full) || rdreq)) wr_index <= wr_index + { {widthu-1{1'b0}}, 1'b1 }; +end + +always @(posedge clk) begin + if(wrreq && (~(full) || rdreq)) mem[wr_index] <= data; +end + +always @(posedge clk) begin + if(rst_n == 1'b0) full <= 1'b0; + else if(sclr) full <= 1'b0; + else if(rdreq && ~(wrreq) && full) full <= 1'b0; + else if(~(rdreq) && wrreq && ~(full) && usedw == (2**widthu)-1) full <= 1'b1; +end + +always @(posedge clk) begin + if(rst_n == 1'b0) usedw <= 0; + else if(sclr) usedw <= 0; + else if(rdreq && ~(wrreq) && ~(empty)) usedw <= usedw - { {widthu-1{1'b0}}, 1'b1 }; + else if(~(rdreq) && wrreq && ~(full)) usedw <= usedw + { {widthu-1{1'b0}}, 1'b1 }; + else if(rdreq && wrreq && empty) usedw <= { {widthu-1{1'b0}}, 1'b1 }; +end + +endmodule diff --git a/rtl/hps_ext.v b/rtl/hps_ext.v new file mode 100644 index 0000000..0282c62 --- /dev/null +++ b/rtl/hps_ext.v @@ -0,0 +1,93 @@ +// +// hps_ext for ao486 +// +// Copyright (c) 2020 Alexey Melnikov +// +// This source file 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 +// (at your option) any later version. +// +// This source file 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 this program. If not, see . +// +/////////////////////////////////////////////////////////////////////// + +module hps_ext +( + input clk_sys, + inout [35:0] EXT_BUS, + + input [15:0] ext_din, + output reg [15:0] ext_dout, + output reg [15:0] ext_addr, + output reg ext_rd, + output reg ext_wr, + + output reg ext_midi, + input [7:0] ext_req, + input [1:0] ext_hotswap +); + +assign EXT_BUS[15:0] = io_dout; +wire [15:0] io_din = EXT_BUS[31:16]; +assign EXT_BUS[32] = dout_en; +wire io_strobe = EXT_BUS[33]; +wire io_enable = |EXT_BUS[35:34]; + +localparam EXT_CMD_MIN = 'h61; +localparam EXT_CMD_MAX = 'h63; + +reg [15:0] io_dout; +reg dout_en = 0; +reg [2:0] byte_cnt; + +always@(posedge clk_sys) begin + reg [15:0] cmd; + + {ext_rd, ext_wr} <= 0; + if((ext_rd | ext_wr) & ~&ext_addr[7:0]) ext_addr <= ext_addr + 1'd1; + + if(~io_enable) begin + byte_cnt <= 0; + io_dout <= 0; + dout_en <= 0; + end + else begin + if(io_strobe) begin + + ext_dout <= io_din; + + io_dout <= 0; + if(~&byte_cnt) byte_cnt <= byte_cnt + 1'd1; + + if(byte_cnt == 1) ext_addr <= io_din; + + if(byte_cnt == 0) begin + cmd <= io_din; + dout_en <= (io_din >= EXT_CMD_MIN && io_din <= EXT_CMD_MAX); + io_dout <= {4'hE, 2'b00, ext_hotswap, ext_req}; + end + else begin + case(cmd) + 'h61: if(byte_cnt >= 3) begin + ext_wr <= 1; + end + + 'h62: if(byte_cnt >= 3) begin + io_dout <= ext_din; + ext_rd <= 1; + end + 'h63: if(byte_cnt == 1) ext_midi <= io_din[7]; + endcase + end + end + end +end + +endmodule