1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-02-16 20:30:54 +00:00

ACSI harddisk support

This commit is contained in:
harbaum
2013-04-01 19:33:53 +00:00
parent 2c28c1dd95
commit 153a50467a
3 changed files with 212 additions and 26 deletions

View File

@@ -12,11 +12,16 @@ module data_io (
input ss,
output sdo,
// dma interface
// dma status interface
output [4:0] dma_idx,
input [7:0] dma_data,
output reg dma_ack,
// acsi data interface
input acsi_out_available,
output reg acsi_out_strobe,
input [9:0] acsi_out_data,
// ram interface
output reg [2:0] state, // state bits required to drive the sdram host
output [22:0] addr,
@@ -67,7 +72,6 @@ reg [15:0] txData;
assign sdo = txData[15];
always@(negedge sck) begin
// memory read
if(cmd == 3) begin
if(cnt == 8)
@@ -83,6 +87,19 @@ always@(negedge sck) begin
else
txData[15:1] <= txData[14:0];
end
// acsi daza read
// send alternating "data available flag + address" and "data"
if(cmd == 7) begin
if(cnt == 8) begin
txData[15:8] <= { 5'd0, acsi_out_data[9:8], acsi_out_available };
acsi_out_strobe <= 1'b0;
end else if(cnt == 16) begin
txData[15:8] <= acsi_out_data[7:0];
acsi_out_strobe <= 1'b1;
end else
txData[15:1] <= txData[14:0];
end
end
always@(posedge sck, posedge ss) begin

View File

@@ -12,6 +12,7 @@ module dma (
// output to mfp
output irq,
output reg br,
// input from system config
input fdc_wr_prot,
@@ -21,6 +22,11 @@ module dma (
output reg [7:0] dio_data,
input dio_ack,
// interface to fifo for acsi commands
output acsi_data_out_available,
input acsi_strobe_out,
output [9:0] acsi_data_out, // 2 bit a0/a1 + 8 bit data
// input from psg
input drv_side,
input [1:0] drv_sel
@@ -33,23 +39,42 @@ always @(dio_idx, base, scnt, fdc_cmd, fdc_track, fdc_sector,
dio_data = 8'h00;
case (dio_idx)
// DMA status
0: dio_data = base[23:16];
1: dio_data = base[15:8];
2: dio_data = base[7:0];
3: dio_data = scnt;
// FDC status
4: dio_data = fdc_cmd;
5: dio_data = fdc_track;
6: dio_data = fdc_sector;
7: dio_data = fdc_data;
8: dio_data = { 4'b0000, drv_sel, drv_side, fdc_busy };
8: dio_data = { 3'b000, acsi_busy, drv_sel, drv_side, fdc_busy != 0 };
// ACSI status
9: dio_data = { acsi_target, acsi_cmd };
10: dio_data = acsi_cmd_parms[0];
11: dio_data = acsi_cmd_parms[1];
12: dio_data = acsi_cmd_parms[2];
13: dio_data = acsi_cmd_parms[3];
14: dio_data = acsi_cmd_parms[4];
default: dio_data = 8'h00;
endcase
end
// ------------------ cpu interface --------------------
reg fdc_busy;
// fdc_busy is a counter. counts down from 2 to 0. stays at 3 since that
// means that the fdc is waiting for the arm io controller
reg [1:0] fdc_busy;
assign irq = !fdc_busy;
// route FDC or ACSI irq out on irq pin
assign irq = fdc_irq || acsi_irq;
// fdc irq is set at end of busy phase and cleared by fdc status register read
reg fdc_irq;
// detect cpu read access on fdc status register (4:4 == 00 -> fdc controller access)
wire fdc_status_read = sel && rw && (addr == 3'h2) && (mode[4:3] == 2'b00);
reg [15:0] mode;
@@ -73,7 +98,7 @@ reg step_dir;
wire [7:0] fdc_status;
assign fdc_status = {
!(motor_on == 0), fdc_wr_prot, 3'b000,
(fdc_cmd[7]==1'b0)?track0:1'b0, 1'b0, fdc_busy };
(fdc_cmd[7]==1'b0)?track0:1'b0, 1'b0, fdc_busy != 0 };
wire [15:0] dma_status;
assign dma_status = { 14'd0, !(scnt == 0), 1'b1 }; // bit 0 = 1: DMA_OK
@@ -82,7 +107,7 @@ assign dma_status = { 14'd0, !(scnt == 0), 1'b1 }; // bit 0 = 1: DMA_OK
reg [15:0] motor_on;
always @(sel, rw, addr, mode, base, fdc_data, fdc_sector, fdc_status, fdc_track,
dma_status, scnt) begin
dma_status, scnt, acsi_target) begin
dout = 16'h0000;
if(sel && rw) begin
@@ -99,6 +124,10 @@ always @(sel, rw, addr, mode, base, fdc_data, fdc_sector, fdc_status, fdc_track,
if(mode[2:1] == 2'b11) // data register
dout = { 8'h00, fdc_data };
end
if(mode[3] == 1'b1) begin
// acsi status register [3]==1 -> BUSY
dout = { 8'h00, acsi_target, 5'h00 }; // status "ok"
end
end
if(addr == 3'h3)
@@ -117,11 +146,43 @@ always @(sel, rw, addr, mode, base, fdc_data, fdc_sector, fdc_status, fdc_track,
dout = { 8'h00, base[7:0] };
end
end
// -------------- fifo to send acsi data to io controller -------------
assign acsi_data_out_available = (readPout != writePout);
assign acsi_data_out = fifoOut[readPout];
localparam FIFO_ADDR_BITS = 8;
localparam FIFO_DEPTH = (1 << FIFO_ADDR_BITS);
reg [9:0] fifoOut [FIFO_DEPTH-1:0];
reg [FIFO_ADDR_BITS-1:0] writePout, readPout;
reg acsi_strobe_outD, acsi_strobe_outD2;
always @(posedge clk) begin
acsi_strobe_outD <= acsi_strobe_out;
acsi_strobe_outD2 <= acsi_strobe_outD;
if(reset)
readPout <= 0;
else
if(acsi_strobe_outD && !acsi_strobe_outD2)
readPout <= readPout + 8'd1;
end
// --------------- acsi handling --------------------
reg [2:0] acsi_target;
reg [4:0] acsi_cmd;
reg [2:0] acsi_byte_counter;
reg [7:0] acsi_cmd_parms [4:0];
reg acsi_busy;
reg acsi_irq;
// acsi status register is read (clears interrupt)
wire acsi_status_read = sel && rw && (mode[4:3] == 2'b01);
reg dio_ackD, dio_ackD2;
always @(posedge clk)
dio_ackD <= dio_ack;
always @(negedge clk) begin
if(reset) begin
mode <= 16'd0;
@@ -131,16 +192,51 @@ always @(negedge clk) begin
fdc_data <= 8'd0;
base <= 24'h000000;
scnt <= 8'h00;
fdc_busy <= 1'b0;
fdc_busy <= 2'd0;
motor_on <= 16'd0;
writePout <= 0;
fdc_irq <= 1'b0;
acsi_target <= 3'd0;
acsi_cmd <= 5'd0;
acsi_irq <= 1'b0;
acsi_busy <= 1'b0;
br <= 1'b0;
end else begin
// acknowledge comes from io controller
// rising edge on ack -> clear busy flag
dio_ackD2 <= dio_ackD;
if(dio_ackD && !dio_ackD2) begin
br <= 1'b0; // release bus
scnt <= 8'h00; // all sectors transmitted
fdc_busy <= 1'b0;
// fdc_busy == 3 -> fdc waiting for io controller
if(fdc_busy == 3)
fdc_busy <= 2'd1; // jump to end of busy phase
// acsi_busy -> acsi waiting for io controller
if(acsi_busy) begin
acsi_irq <= 1'b1; // set acsi irq
acsi_busy <= 1'd0;
end
end
// fdc is ending busy phase
if(fdc_busy == 1)
fdc_irq <= 1'b1;
// cpu is reading status register -> clear fdc irq
if(fdc_status_read)
fdc_irq <= 1'b0;
// cpu is reading status register -> clear fdc irq
if(acsi_status_read)
acsi_irq <= 1'b0;
// if fdc is busy (==0), but not blocked by io controller (==15)
// then count it down
if((fdc_busy != 0) && (fdc_busy != 3))
fdc_busy <= fdc_busy - 2'd1;
// let "motor" run for some time
if(motor_on != 0)
motor_on <= motor_on - 16'd1;
@@ -152,6 +248,7 @@ always @(negedge clk) begin
if(mode[3] == 1'b0) begin
// fdc register write
if(mode[2:1] == 2'b00) begin // command register
fdc_busy <= 2'd2;
fdc_cmd <= din[7:0];
// all TYPE I and TYPE II commands start the motor
@@ -185,28 +282,28 @@ always @(negedge clk) begin
// ------------- TYPE II commands -------------
if(din[7:5] == 3'b100) // read sector
fdc_busy <= 1'b1;
fdc_busy <= 2'd3;
if(din[7:5] == 3'b101) // write sector
if(!fdc_wr_prot)
fdc_busy <= 1'b1;
fdc_busy <= 2'd3;
// ------------- TYPE III commands ------------
// these aren't supported yet
// if(din[7:4] == 4'b1100) // read address
// fdc_busy <= 1'b1;
// fdc_busy <= 2'd3;
// if(din[7:4] == 4'b1110) // read track
// fdc_busy <= 1'b1;
// fdc_busy <= 2'd3;
// if(din[7:4] == 4'b1111) // write track
// if(!fdc_wr_prot)
// fdc_busy <= 1'b1;
// fdc_busy <= 2'd3;
// ------------- TYPE IV commands -------------
if(din[7:4] == 4'b1101) // force intrerupt
fdc_busy <= 1'b0;
fdc_busy <= 2'd1;
end if(mode[2:1] == 2'b01) // track register
fdc_track <= din[7:0];
@@ -214,6 +311,37 @@ always @(negedge clk) begin
fdc_sector <= din[7:0];
if(mode[2:1] == 2'b11) // data register
fdc_data <= din[7:0];
end else begin
// acsi register access
if(!mode[1]) begin
// mode[1] == 0 -> first command byte
acsi_target <= din[7:5];
acsi_cmd <= din[4:0];
acsi_byte_counter <= 3'd0;
// only acsi 0 is supported
if(din[7:5] == 3'd0)
acsi_irq <= 1'b1;
end else begin
// further bytes
acsi_cmd_parms[acsi_byte_counter] <= din[7:0];
acsi_byte_counter <= acsi_byte_counter + 3'd1;
// only acsi 0 is supported
if(acsi_target == 3'd0) begin
// auto-ack first 5 bytes
if(acsi_byte_counter < 4)
acsi_irq <= 1'b1;
else begin
br <= 1'b1; // request bus
acsi_busy <= 1'b1; // request io cntroller
end
end
end
// acsi: write data in output buffer
fifoOut[writePout] <= { mode[2:1], din[7:0] };
writePout <= writePout + 8'd1;
end
end

View File

@@ -58,7 +58,26 @@ wire io_dtack = vreg_sel || mmu_sel || mfp_sel || mfp_iack ||
// TOS versions cope with that, TOS versions with blitter support need this to work as this is
// required to properly detect that a blitter is not present.
// a bus error is now generated once no dtack is seen for 63 clock cycles.
wire tg68_berr = (dtack_timeout == 6'd63); // || cpu_write_illegal;
wire tg68_berr = (dtack_timeout == 5'd31); // || cpu_write_illegal;
// count bus errors for debugging purposes. we can thus trigger for a
// certain bus error
reg [3:0] berr_cnt_out;
reg [3:0] berr_cnt;
always @(posedge clk_8) begin
if(reset) begin
berr_cnt <= 4'd0;
end else begin
berr_cnt_out <= 4'd0;
if(tg68_berr) begin
berr_cnt_out <= berr_cnt + 4'd1;
berr_cnt <= berr_cnt + 4'd1;
end
end
end
// the following is just to prevent optimization
assign UART_TX = (berr_cnt_out == 4'd0);
wire cpu_write = cpu_cycle && cpu2io && data_strobe && !tg68_rw;
@@ -67,18 +86,24 @@ wire cpu_write = cpu_cycle && cpu2io && data_strobe && !tg68_rw;
wire cpu_write_illegal = cpu_write &&
(tg68_adr[23:3] === 21'd0); // the first two long words $0 and $4
reg [5:0] dtack_timeout;
reg [4:0] dtack_timeout;
always @(posedge clk_8) begin
if(reset) begin
dtack_timeout <= 6'd0;
dtack_timeout <= 5'd0;
end else begin
if(!tg68_dtack)
dtack_timeout <= 6'd0;
else if(dtack_timeout != 6'd63)
dtack_timeout <= dtack_timeout + 6'd1;
if(!tg68_dtack || br)
dtack_timeout <= 5'd0;
else if(dtack_timeout != 5'd31)
dtack_timeout <= dtack_timeout + 5'd1;
end
end
// no tristate busses exist inside the FPGA. so bus request doesn't do
// much more than halting the cpu by suppressing dtack
wire br = dma_br; // dma is only other bus master (yet)
wire dma_br;
// request interrupt ack from mfp for IPL == 6
wire mfp_iack;
assign mfp_iack = cpu_cycle && cpu2iack && data_strobe && (tg68_adr[3:1] == 3'b110);
@@ -293,6 +318,11 @@ wire [7:0 ]dma_dio_data;
// floppy_sel is active low
wire wr_prot = (floppy_sel == 2'b01)?system_ctrl[7]:system_ctrl[6];
// acsi interface
wire data_from_acsi_available;
wire strobe_from_acsi;
wire [9:0] data_from_acsi;
dma dma (
// cpu interface
.clk (clk_8 ),
@@ -306,6 +336,7 @@ dma dma (
.dout (dma_data_out),
.irq (dma_irq ),
.br (dma_br ),
// system control interface
.fdc_wr_prot (wr_prot),
@@ -315,6 +346,11 @@ dma dma (
.dio_data (dma_dio_data),
.dio_ack (dma_dio_ack ),
// acsi interface
.acsi_data_out_available (data_from_acsi_available),
.acsi_strobe_out (strobe_from_acsi),
.acsi_data_out (data_from_acsi),
// floppy interface
.drv_sel (floppy_sel ),
.drv_side (floppy_side )
@@ -543,7 +579,7 @@ wire data_strobe;
assign data_strobe = ~tg68_uds || ~tg68_lds;
// generate dtack (for st ram only and rom), TODO: no dtack for rom write
assign tg68_dtack = ~((cpu2mem && data_strobe && cpu_cycle) || io_dtack );
assign tg68_dtack = ~(((cpu2mem && data_strobe && cpu_cycle) || io_dtack ) && !br);
wire ram_oe;
assign ram_oe = video_cycle?~video_read:
@@ -668,11 +704,16 @@ data_io data_io (
.ss (SPI_SS2 ),
.sdo (data_io_sdo ),
// dma interface
// dma status interface
.dma_idx (dma_dio_idx ),
.dma_data (dma_dio_data ),
.dma_ack (dma_dio_ack ),
// acsi interface
.acsi_out_available (data_from_acsi_available ),
.acsi_out_strobe (strobe_from_acsi ),
.acsi_out_data (data_from_acsi ),
// ram interface
.state (host_state ),
.addr (host_addr ),