diff --git a/cores/mist/data_io.v b/cores/mist/data_io.v index d59c654..e9a8efd 100644 --- a/cores/mist/data_io.v +++ b/cores/mist/data_io.v @@ -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 diff --git a/cores/mist/dma.v b/cores/mist/dma.v index a0322d7..98881f4 100644 --- a/cores/mist/dma.v +++ b/cores/mist/dma.v @@ -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 diff --git a/cores/mist/mist_top.v b/cores/mist/mist_top.v index e2695a7..3a3bd46 100644 --- a/cores/mist/mist_top.v +++ b/cores/mist/mist_top.v @@ -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 ),