Ported AO486 floppy controller.

This commit is contained in:
kitune-san
2022-09-24 10:27:00 +09:00
parent fc23647a13
commit 2eb18fc780
6 changed files with 1279 additions and 12 deletions

49
PCXT.sv
View File

@@ -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;

View File

@@ -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 (

View File

@@ -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;

878
rtl/common/floppy.v Normal file
View File

@@ -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

87
rtl/common/simple_fifo.v Normal file
View File

@@ -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

93
rtl/hps_ext.v Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////
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