1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-01-23 10:47:51 +00:00
Gyorgy Szombathelyi fe94350806 Update MiST modules
2022-10-09 18:13:55 +02:00

406 lines
12 KiB
Verilog

// Copyright 2008, 2009 by Jakub Bednarski
//
// Extracted from Minimig gayle.v
//
// Minimig 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.
//
// Minimig 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/>.
//
//
//
// -- JB --
//
// 2008-10-06 - initial version
// 2008-10-08 - interrupt controller implemented, kickstart boots
// 2008-10-09 - working identify device command implemented (hdtoolbox detects our drive)
// - read command reads data from hardfile (fixed size and name, only one sector read size supported, workbench sees hardfile partition)
// 2008-10-10 - multiple sector transfer supported: works ok, sequential transfers with direct spi read and 28MHz CPU from 400 to 520 KB/s
// - arm firmare seekfile function very slow: seeking from start to 20MB takes 144 ms (some software improvements required)
// 2008-10-30 - write support added
// 2008-12-31 - added hdd enable
// 2009-05-24 - clean-up & renaming
// 2009-08-11 - hdd_ena enables Master & Slave drives
// 2009-11-18 - changed sector buffer size
// 2010-04-13 - changed sector buffer size
// 2010-08-10 - improved BSY signal handling
// 2022-08-18 - added packet command handling
module ide
(
input clk,
input clk_en,
input reset,
input [2:0] address_in,
input sel_secondary,
input [15:0] data_in,
output [15:0] data_out,
output data_oe,
input rd,
input hwr,
input lwr,
input sel_ide,
output reg [1:0] intreq,
input [1:0] intreq_ack, // interrupt clear
output nrdy, // fifo is not ready for reading
input [1:0] hdd0_ena, // enables Master & Slave drives on primary channel
input [1:0] hdd1_ena, // enables Master & Slave drives on secondary channel
output fifo_rd,
output fifo_wr,
// connection to the IO-Controller
output hdd_cmd_req,
output hdd_dat_req,
input [2:0] hdd_addr,
input [15:0] hdd_data_out,
output [15:0] hdd_data_in,
input hdd_wr,
input hdd_status_wr,
input hdd_data_wr,
input hdd_data_rd
);
localparam VCC = 1'b1;
localparam GND = 1'b0;
/*
0 Data
1 Error | Feature
2 SectorCount
3 SectorNumber
4 CylinderLow
5 CylinderHigh
6 Device/Head
7 Status | Command
command class:
PI (PIO In)
PO (PIO Out)
ND (No Data)
Status:
#6 - DRDY - Drive Ready
#7 - BSY - Busy
#3 - DRQ - Data Request
#0 - ERR - Error
INTRQ - Interrupt Request
*/
// address decoding signals
wire sel_tfr; // HDD task file registers select
wire sel_fifo; // HDD data port select (FIFO buffer)
wire sel_status /* synthesis keep */; // HDD status register select
wire sel_command /* synthesis keep */;// HDD command register select
// internal registers
reg block_mark; // IDE multiple block start flag
reg busy; // busy status (command processing state)
reg pio_in; // pio in command type is being processed
reg pio_out; // pio out command type is being processed
reg error; // error status (command processing failed)
reg [1:0] dev; // drive select (Primary/Secondary, Master/Slave)
wire bsy; // busy
wire drdy; // drive ready
wire drq; // data request
reg drq_d; // data request
wire err; // error
wire [7:0] status; // HDD status
// FIFO control
wire fifo_reset;
wire [15:0] fifo_data_in;
wire [15:0] fifo_data_out;
wire fifo_full;
wire fifo_empty;
wire fifo_last_out; // last word of a sector is being read
wire fifo_last_in; // last word of a sector is being written
// HDD status register
assign status = {bsy,drdy,2'b01,drq,2'b00,err};
// packet states
reg [1:0] packet_state;
localparam PACKET_IDLE = 0;
localparam PACKET_WAITCMD = 1;
localparam PACKET_PROCESSCMD = 2;
wire packet_state_change;
reg [12:0] packet_count;
wire packet_in_last;
wire packet_in;
wire packet_out;
`ifdef IDE_DEBUG
// cmd/status debug
reg [7:0] status_dbg /* synthesis noprune */;
reg [7:0] dbg_ide_cmd /* synthesis noprune */;
reg [2:0] dbg_addr /* synthesis noprune */;
reg dbg_wr /* synthesis noprune */;
reg[15:0] dbg_data_in /* synthesis noprune */;
reg[15:0] dbg_data_out /* synthesis noprune */;
always @(posedge clk) begin
status_dbg <= status;
if (clk_en) begin
dbg_wr <= 0;
if (sel_command) // set when the CPU writes command register
dbg_ide_cmd <= data_in[15:8];
if (sel_ide) begin
dbg_addr <= address_in;
dbg_wr <= hwr | lwr;
if (rd) dbg_data_out <= data_out;
if (hwr | lwr) dbg_data_in <= data_in;
end
end
end
`endif
// HDD status register bits
assign bsy = busy & ~drq;
assign drdy = ~(bsy|drq);
assign err = error;
// address decoding
assign sel_tfr = sel_ide;
assign sel_status = rd && sel_tfr && address_in==3'b111 ? VCC : GND;
assign sel_command = hwr && sel_tfr && address_in==3'b111 ? VCC : GND;
assign sel_fifo = sel_tfr && address_in==3'b000 ? VCC : GND;
//===============================================================================================//
// task file registers
reg [7:0] tfr [7:0];
wire [2:0] tfr_sel;
wire [7:0] tfr_in;
wire [7:0] tfr_out;
wire tfr_we;
reg [8:0] sector_count; // sector counter
wire sector_count_dec_in; // decrease sector counter (reads)
wire sector_count_dec_out; // decrease sector counter (writes)
always @(posedge clk)
if (clk_en) begin
if (hwr && sel_tfr && address_in == 3'b010) begin // sector count register loaded by the host
sector_count <= {1'b0, data_in[15:8]};
if (data_in[15:8] == 0) sector_count <= 9'd256;
end else if (sector_count_dec_in || sector_count_dec_out)
sector_count <= sector_count - 8'd1;
end
reg rd_old;
reg wr_old;
reg sel_fifo_old;
always @(posedge clk)
if (clk_en) begin
rd_old <= rd;
wr_old <= hwr & lwr;
sel_fifo_old <= sel_fifo;
end
assign sector_count_dec_in = pio_in & fifo_last_out & sel_fifo_old & ~rd & rd_old & packet_state == PACKET_IDLE;
assign sector_count_dec_out = pio_out & fifo_last_in & sel_fifo_old & ~hwr & ~lwr & wr_old & packet_state == PACKET_IDLE;
// task file register control
assign tfr_we = packet_in_last ? 1'b1 : bsy ? hdd_wr : sel_tfr & hwr;
assign tfr_sel = packet_in_last ? 3'd2 : bsy ? hdd_addr : address_in;
assign tfr_in = packet_in_last ? 8'h03: bsy ? hdd_data_out[7:0] : data_in[15:8];
// input multiplexer for SPI host
assign hdd_data_in = tfr_sel==0 ? fifo_data_out : {7'h0, dev[1], tfr_out};
// task file registers
always @(posedge clk)
if (clk_en) begin
if (tfr_we)
tfr[tfr_sel] <= tfr_in;
end
assign tfr_out = tfr[tfr_sel];
// master/slave drive select
always @(posedge clk)
if (clk_en) begin
if (reset)
dev <= 0;
else if (sel_tfr && address_in==6 && hwr)
dev <= {sel_secondary, data_in[12]};
end
assign packet_state_change = busy && hdd_status_wr && hdd_data_out[5];
// bytes count in a packet
always @(posedge clk)
if (clk_en) begin
if (reset)
packet_count <= 0;
else if (hdd_wr && hdd_addr == 4)
packet_count[6:0] <= hdd_data_out[7:1];
else if (hdd_wr && hdd_addr == 5)
packet_count[12:7] <= hdd_data_out[5:0];
else if (packet_state_change && packet_state == PACKET_IDLE)
packet_count <= 13'd6; // IDLE->WAITCMD transition, expect 6 words of packet command
end
// status register (write only from SPI host)
// 7 - busy status (write zero to finish command processing: allow host access to task file registers)
// 6
// 5
// 4 - intreq (used for writes only)
// 3 - drq enable for pio in (PI) command type
// 2 - drq enable for pio out (PO) command type
// 1
// 0 - error flag (remember about setting error task file register)
// command busy status
always @(posedge clk)
if (clk_en) begin
if (reset)
busy <= GND;
else if (hdd_status_wr && hdd_data_out[7] || (sector_count_dec_in && sector_count == 9'h01)) // reset by SPI host (by clearing BSY status bit)
busy <= GND;
else if (sel_command) // set when the CPU writes command register
busy <= VCC;
end
// IDE interrupt request register
always @(posedge clk)
if (clk_en) begin
drq_d <= drq;
if (reset) begin
intreq[0] <= GND;
intreq[1] <= GND;
block_mark <= GND;
end else begin
if (busy && hdd_status_wr && hdd_data_out[3])
block_mark <= VCC; // to handle IDENTIFY
if (pio_in) begin // reads
if (hdd_status_wr && hdd_data_out[4])
block_mark <= VCC;
if ((error | (!drq_d & drq)) & block_mark) begin
intreq[dev[1]] <= VCC;
block_mark <= GND;
end
if (packet_in_last) // read the last word from the packet command result
intreq[dev[1]] <= VCC;
end else if (pio_out) begin // writes
if (hdd_status_wr && hdd_data_out[4])
intreq[dev[1]] <= VCC;
end else if (hdd_status_wr && hdd_data_out[7]) // other command types completed
intreq[dev[1]] <= VCC;
else if (packet_state_change && packet_state == PACKET_IDLE) // ready to accept command packet
intreq[dev[1]] <= VCC;
if (intreq_ack[0]) intreq[0] <= GND; // cleared by the CPU
if (intreq_ack[1]) intreq[1] <= GND; // cleared by the CPU
end
end
// pio in command type
always @(posedge clk)
if (clk_en) begin
if (reset)
pio_in <= GND;
else if (drdy) // reset when processing of the current command ends
pio_in <= GND;
else if (busy && hdd_status_wr && hdd_data_out[3]) // set by SPI host
pio_in <= VCC;
end
// pio out command type
always @(posedge clk)
if (clk_en) begin
if (reset)
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[7]) // reset by SPI host when command processing completes
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[3]) // pio_in set by SPI host (during PACKET processing)
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[2]) // set by SPI host
pio_out <= VCC;
end
// packet command state machine
always @(posedge clk)
if (clk_en) begin
if (reset)
packet_state <= PACKET_IDLE;
else if (drdy) // reset when processing of the current command ends
packet_state <= PACKET_IDLE;
else if (packet_state_change) // set by SPI host
packet_state <= packet_state == PACKET_IDLE ? PACKET_WAITCMD :
packet_state == PACKET_WAITCMD ? PACKET_PROCESSCMD : packet_state;
end
assign drq = (fifo_full & pio_in) | (~fifo_full & pio_out & (sector_count != 0 || packet_out)); // HDD data request status bit
// error status
always @(posedge clk)
if (clk_en) begin
if (reset)
error <= GND;
else if (sel_command) // reset by the CPU when command register is written
error <= GND;
else if (busy && hdd_status_wr && hdd_data_out[0]) // set by SPI host
error <= VCC;
end
assign hdd_cmd_req = bsy; // bsy is set when command register is written, tells the SPI host about new command
assign hdd_dat_req = (fifo_full & pio_out); // the FIFO is full so SPI host may read it
// FIFO in/out multiplexer
assign fifo_reset = reset | sel_command | packet_state_change | packet_in_last;
assign fifo_data_in = pio_in ? hdd_data_out : data_in;
assign fifo_rd = pio_out ? hdd_data_rd : sel_fifo & rd;
assign fifo_wr = pio_in ? hdd_data_wr : sel_fifo & hwr & lwr;
assign packet_in = packet_state == PACKET_PROCESSCMD && pio_in;
assign packet_out = packet_state == PACKET_WAITCMD || (packet_state == PACKET_PROCESSCMD && pio_out);
//sector data buffer (FIFO)
ide_fifo SECBUF1
(
.clk(clk),
.clk_en(clk_en),
.reset(fifo_reset),
.data_in(fifo_data_in),
.data_out(fifo_data_out),
.rd(fifo_rd),
.wr(fifo_wr),
.packet_in(packet_in),
.packet_out(packet_out),
.packet_count(packet_count),
.packet_in_last(packet_in_last),
.full(fifo_full),
.empty(fifo_empty),
.last_out(fifo_last_out),
.last_in(fifo_last_in)
);
// fifo is not ready for reading
assign nrdy = pio_in & sel_fifo & fifo_empty;
assign data_oe = (!dev[1] && hdd0_ena[dev[0]]) || (dev[1] && hdd1_ena[dev[0]]);
//data_out multiplexer
assign data_out = sel_fifo && rd ? fifo_data_out :
sel_status ? data_oe ? {status,8'h00} : 16'h00_00 :
sel_tfr && rd ? {tfr_out,8'h00} : 16'h00_00;
//===============================================================================================//
endmodule