From 9231d71e3999665602c709965f893946866e9f17 Mon Sep 17 00:00:00 2001 From: "till.harbaum@googlemail.com" Date: Thu, 22 Jan 2015 07:46:09 +0000 Subject: [PATCH] ATari ST DMA rewritten git-svn-id: file:///Users/ces/dev/mist-subversion/trunk@762 8578a998-a533-420b-88ed-89baca3f5bf4 --- cores/mist/acia.v | 38 +- cores/mist/acsi.v | 120 ++++ cores/mist/data_io.v | 199 ------- cores/mist/dma.v | 1087 ++++++++++++++++++++++++++----------- cores/mist/fdc.v | 271 +++++++++ cores/mist/io_fifo.v | 3 + cores/mist/mfp_timer.v | 6 +- cores/mist/mist.qsf | 3 +- cores/mist/mist_top.v | 202 +++---- cores/mist/ste_joystick.v | 1 - cores/mist/user_io.v | 20 +- cores/mist/video.v | 1 - 12 files changed, 1307 insertions(+), 644 deletions(-) create mode 100644 cores/mist/acsi.v delete mode 100644 cores/mist/data_io.v create mode 100644 cores/mist/fdc.v diff --git a/cores/mist/acia.v b/cores/mist/acia.v index 06f43e1..12f619b 100644 --- a/cores/mist/acia.v +++ b/cores/mist/acia.v @@ -97,30 +97,23 @@ reg ikbd_cpu_data_read; reg [7:0] ikbd_cr; reg [7:0] midi_cr; -reg [15:0] ikbd_rx_counter /* synthesis noprune */; - always @(negedge clk) begin if(reset) begin readTimer <= 14'd0; - ikbd_rx_counter <= 16'd0; end else begin if(readTimer > 0) readTimer <= readTimer - 14'd1; // read on ikbd data register ikbd_cpu_data_read <= 1'b0; - if(sel && ~ds && rw && (addr == 2'd1)) begin + if(sel && ~ds && rw && (addr == 2'd1)) ikbd_cpu_data_read <= 1'b1; - ikbd_rx_counter <= ikbd_rx_counter + 16'd1; - end - if(ikbd_cpu_data_read && ikbd_rx_data_available) begin // Some programs (e.g. bolo) need a pause between two ikbd bytes. // The ikbd runs at 7812.5 bit/s 1 start + 8 data + 1 stop bit. // One byte is 1/718.25 seconds. A pause of ~1ms is thus required // 8000000/718.25 = 11138.18 -// readTimer <= 14'd11138; readTimer <= 14'd15000; end end @@ -128,6 +121,7 @@ end // ------------------ cpu interface -------------------- +wire [7:0] ikbd_status = { ikbd_irq, 6'b000001, cpu_ikbd_rx_data_available}; wire [7:0] ikbd_rx_data; wire ikbd_rx_data_available; @@ -149,11 +143,11 @@ always @(sel, ds, rw, addr, ikbd_rx_data_available, ikbd_rx_data, ikbd_irq, if(sel && ~ds && rw) begin // keyboard acia read - if(addr == 2'd0) dout = { ikbd_irq, 6'b000001, cpu_ikbd_rx_data_available}; + if(addr == 2'd0) dout = ikbd_status; if(addr == 2'd1) dout = ikbd_rx_data; // midi acia read - if(addr == 2'd2) dout = { midi_irq, 5'b00000, midi_tx_empty, midi_rx_data_available}; + if(addr == 2'd2) dout = midi_status; if(addr == 2'd3) dout = midi_rx_data; end end @@ -162,6 +156,9 @@ end wire midi_irq = (midi_cr[7] && midi_rx_data_available) || // rx irq ((midi_cr[6:5] == 2'b01) && midi_tx_empty); // tx irq +wire [7:0] midi_status = { midi_irq, 1'b0 /* parity err */, midi_rx_overrun, midi_rx_frame_error, + 2'b00 /* CTS & DCD */, midi_tx_empty, midi_rx_data_available}; + // MIDI runs at 31250bit/s which is exactly 1/256 of the 8Mhz system clock // 8MHz/256 = 31250Hz -> MIDI bit rate @@ -171,10 +168,11 @@ always @(posedge clk) // --------------------------- midi receiver ----------------------------- reg [7:0] midi_rx_cnt; // bit + sub-bit counter -reg [8:0] midi_rx_shift_reg; // shift register used during reception +reg [7:0] midi_rx_shift_reg; // shift register used during reception reg [7:0] midi_rx_data; reg [3:0] midi_rx_filter; // filter to reduce noise reg midi_rx_frame_error; +reg midi_rx_overrun; reg midi_rx_data_available; reg midi_in_filtered; @@ -183,17 +181,23 @@ always @(negedge clk) begin midi_rx_cnt <= 8'd0; midi_rx_data_available <= 1'b0; midi_rx_filter <= 4'b1111; + midi_rx_overrun <= 1'b0; + midi_rx_frame_error <= 1'b0; end else begin // read on midi data register - if(sel && ~ds && rw && (addr == 2'd3)) + if(sel && ~ds && rw && (addr == 2'd3)) begin midi_rx_data_available <= 1'b0; // read on midi data clears rx status + midi_rx_overrun <= 1'b0; + end // midi acia master reset if(midi_cr[1:0] == 2'b11) begin midi_rx_cnt <= 8'd0; midi_rx_data_available <= 1'b0; midi_rx_filter <= 4'b1111; + midi_rx_overrun <= 1'b0; + midi_rx_frame_error <= 1'b0; end // 1/16 system clock == 16 times midi clock @@ -218,20 +222,24 @@ always @(negedge clk) begin // received a bit if(midi_rx_cnt[3:0] == 4'd0) begin // in the middle of the bit -> shift new bit into msb - midi_rx_shift_reg <= { midi_in_filtered, midi_rx_shift_reg[8:1] }; + midi_rx_shift_reg <= { midi_in_filtered, midi_rx_shift_reg[7:1] }; end // receiving last (stop) bit if(midi_rx_cnt[7:0] == 8'd1) begin if(midi_in_filtered == 1'b1) begin // copy data into rx register - midi_rx_data <= midi_rx_shift_reg[8:1]; // pure data w/o start and stop bits + midi_rx_data <= midi_rx_shift_reg; // pure data w/o start and stop bits midi_rx_data_available <= 1'b1; midi_rx_frame_error <= 1'b0; end else // report frame error via status register midi_rx_frame_error <= 1'b1; - + + // data hasn't been read yet? -> overrun + if(midi_rx_data_available) + midi_rx_overrun <= 1'b1; + end end end diff --git a/cores/mist/acsi.v b/cores/mist/acsi.v new file mode 100644 index 0000000..54dc12d --- /dev/null +++ b/cores/mist/acsi.v @@ -0,0 +1,120 @@ +// acsi.v +// +// Atari ST ACSI implementation for the MIST baord +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2015 Till Harbaum +// +// 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 acsi ( + // clocks and system interface + input clk, + input reset, + + input [7:0] enable, + + input dma_ack, // IO controller answers request + input dma_nak, // IO controller rejects request + input [7:0] dma_status, + + + input [2:0] status_sel, + output [7:0] status_byte, + + // cpu interface + input [1:0] cpu_addr, + input cpu_sel, + input cpu_rw, + input [7:0] cpu_din, + output [7:0] cpu_dout, + + output reg irq +); + +// acsi always returns dma status on cpu_read +assign cpu_dout = dma_status; + +reg [2:0] target; +reg [4:0] cmd; +reg [2:0] byte_counter; +reg [7:0] cmd_parms [4:0]; +reg busy; + +// acsi status as reported to the io controller +assign status_byte = + (status_sel == 0)?{ target, cmd }: + (status_sel == 1)?cmd_parms[0]: + (status_sel == 2)?cmd_parms[1]: + (status_sel == 3)?cmd_parms[2]: + (status_sel == 4)?cmd_parms[3]: + (status_sel == 5)?cmd_parms[4]: + (status_sel == 6)?{ 7'b0000000, busy }: + 8'h00; + +// CPU write interface +always @(negedge clk) begin + if(reset) begin + target <= 3'd0; + cmd <= 5'd0; + irq <= 1'b0; + busy <= 1'b0; + end else begin + + // DMA transfer has been ack'd by io controller + if(dma_ack && busy) begin + irq <= 1'b1; // set acsi irq + busy <= 1'd0; + end + + // DMA transfer has been rejected by io controller (no such device) + if(dma_nak) + busy <= 1'd0; + + // cpu is accessing acsi bus -> clear acsi irq + // status itself is returned by the io controller with the dma_ack. + if(cpu_sel) + irq <= 1'b0; + + // acsi register access + if(cpu_sel && !cpu_rw) begin + if(!cpu_addr[0]) begin + // a0 == 0 -> first command byte + target <= cpu_din[7:5]; + cmd <= cpu_din[4:0]; + byte_counter <= 3'd0; + + // check if this acsi device is enabled + if(enable[cpu_din[7:5]] == 1'b1) + irq <= 1'b1; + end else begin + // further bytes + cmd_parms[byte_counter] <= cpu_din[7:0]; + byte_counter <= byte_counter + 3'd1; + + // check if this acsi device is enabled + if(enable[target] == 1'b1) begin + // auto-ack first 5 bytes + if(byte_counter < 4) + irq <= 1'b1; + else + busy <= 1'b1; // request io cntroller + end + end + end + end +end + +endmodule // acsi diff --git a/cores/mist/data_io.v b/cores/mist/data_io.v deleted file mode 100644 index d3ea79a..0000000 --- a/cores/mist/data_io.v +++ /dev/null @@ -1,199 +0,0 @@ -// SPI data client (rom, floppy, harddisk io) - -module data_io ( - // clocks - input clk_8, - input reset, - input [1:0] bus_cycle, - output reg [31:0] ctrl_out, - - // spi interface - input sdi, - input sck, - input ss, - output sdo, - - // dma status interface - output [4:0] dma_idx, - input [7:0] dma_data, - output reg dma_ack, - output reg br, - - // horizontal and vertical screen adjustments - output reg [15:0] video_adj, - - // ram interface - output reg read, - output reg write, - output [22:0] addr, - output reg [15:0] data_out, // write data register - input [15:0] data_in -); - -assign dma_idx = bcnt; - -reg [4:0] cnt; // bit counter (counting spi bits, rolling from 23 to 8) -reg [4:0] bcnt; // payload byte counter -reg [14:0] sbuf; // receive buffer (buffer used to assemble spi bytes/words) -reg [7:0] cmd; // command byte (first byte of spi transmission) -reg [30:0] addrR;// address register (word address for memory transfers) -reg writeCmd; // write request received via SPI -reg writeD; // write synchonized to 8Mhz clock -reg writeD2; // synchronized write delayed by one 8Mhz clock -reg readCmd; // read request received via SPI -reg readD; // read synchonized to 8Mhz clock -reg readD2; // synchronized read delayed by one 8Mhz clock -reg [15:0] ram_data; // latch for incoming ram data -reg brI; // signals to bring br into local clock domain - -// during write the address needs to be decremented by one as the -// address auto increment takes place at the beginning of each transfer -assign addr = (cmd==2)?(addrR[22:0]-23'd1):addrR[22:0]; - -// latch bus cycle to have it stable at the end of the cycle (rising edge of clk8) -reg [1:0] bus_cycle_L; -always @(negedge clk_8) begin - bus_cycle_L <= bus_cycle; - - if(bus_cycle == 0) - br <= brI; -end - -// generate state signals required to control the sdram host interface -always @(posedge clk_8) begin - // start io transfers clock cycles after bus_cycle 0 - // (after the cpu cycle) - writeD <= writeCmd && ((bus_cycle_L == 3) || writeD); - writeD2 <= writeD; - readD <= readCmd && ((bus_cycle_L == 3) || readD); - readD2 <= readD; - - // at the end of a read cycle latch the incoming ram data for later spi transmission - if(read) ram_data <= data_in; - - if(reset) begin - read <= 1'b0; - write <= 1'b0; - end else begin - if(writeD && ~writeD2) begin - write <= 1'b1; - read <= 1'b0; - end else if(readD && ~readD2) begin - write <= 1'b0; - read <= 1'b1; - end else begin - write <= 1'b0; - read <= 1'b0; - end - end -end - -reg [15:0] txData; -assign sdo = txData[15]; - -always@(negedge sck) begin - // memory read - if(cmd == 3) begin - if(cnt == 8) - txData <= ram_data; - else - txData[15:1] <= txData[14:0]; - end - - // dma status read - if(cmd == 5) begin - if((cnt == 8) || (cnt == 16)) - txData[15:8] <= dma_data; - else - txData[15:1] <= txData[14:0]; - end -end - - -always@(posedge sck, posedge ss) begin - if(ss == 1'b1) begin - cnt <= 5'd0; - bcnt <= 4'd0; - writeCmd <= 1'b0; - readCmd <= 1'b0; - dma_ack <= 1'b0; - end else begin - dma_ack <= 1'b0; - sbuf <= { sbuf[13:0], sdi}; - - // 0:7 is command, 8:15 and 16:23 is payload bytes - if(cnt < 5'd23) - cnt <= cnt + 5'd1; - else - cnt <= 5'd8; - - // count payload bytes - if((cnt == 15) || (cnt == 23)) - bcnt <= bcnt + 4'd1; - - if(cnt == 5'd7) begin - cmd <= {sbuf[6:0], sdi}; - - // send ack - if({sbuf[6:0], sdi } == 8'd6) - dma_ack <= 1'b1; - - // request bus - if({sbuf[6:0], sdi } == 8'd7) - brI <= 1'b1; - - // release bus - if({sbuf[6:0], sdi } == 8'd8) - brI <= 1'b0; - - // if we can see a read coming initiate sdram read transfer asap - if({sbuf[6:0], sdi } == 8'd3) - readCmd <= 1; - end - - // handle "payload" - if(cnt >= 8) begin - - // set address - if(cmd == 1) - addrR <= { addrR[29:0], sdi}; - - // write ram - if(cmd == 2) begin - if(cnt == 5'd16) - writeCmd <= 1'b0; - - if(cnt == 5'd23) begin - data_out <= { sbuf, sdi }; - addrR <= addrR + 31'b1; - writeCmd <= 1'b1; - end - end - - // read ram - if(cmd == 3) begin - if(cnt == 16) - readCmd <= 0; - - if(cnt == 23) begin - addrR <= addrR + 31'b1; - readCmd <= 1; - end - end - - // set control register (32 bits written in 2 * 16 bits) - if((cmd == 4) && (cnt == 5'd23)) begin - if(bcnt < 2) - ctrl_out[31:16] <= { sbuf, sdi }; - else - ctrl_out[15:0] <= { sbuf, sdi }; - end - - // set video offsets - if((cmd == 9) && (cnt == 5'd23)) - video_adj <= { sbuf, sdi }; - end - end -end - -endmodule \ No newline at end of file diff --git a/cores/mist/dma.v b/cores/mist/dma.v index bef7d97..952121a 100644 --- a/cores/mist/dma.v +++ b/cores/mist/dma.v @@ -1,332 +1,791 @@ +// dma.v +// +// Atari ST dma engine for the MIST baord +// http://code.google.com/p/mist-board/ +// +// This file implements a SPI client which can write data +// into any memory region. This is used to upload rom +// images as well as implementation the DMA functionality +// of the Atari ST DMA controller. +// +// This also implements the video adjustment. This has nothing +// to do with dma and should happen in user_io instead. +// But now it's here and moving it is not worth the effort. +// +// Copyright (c) 2014 Till Harbaum +// +// 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 . +// + +// TODO: +// - Allow DMA transfers to ROM only for IO controller initiated transfers + +// ##############DMA/WD1772 Disk controller ########### +// -------+-----+-----------------------------------------------------+---------- +// $FF8600| |Reserved | +// $FF8602| |Reserved | +// $FF8604|word |FDC access/sector count |R/W +// $FF8606|word |DMA mode/status BIT 2 1 0|R +// | |Condition of FDC DATA REQUEST signal -----------' | || +// | |0 - sector count null,1 - not null ---------------' || +// | |0 - no error, 1 - DMA error ------------------------'| +// $FF8606|word |DMA mode/status BIT 8 7 6 . 4 3 2 1 .|W +// | |0 - read FDC/HDC,1 - write ---------' | | | | | | | | +// | |0 - HDC access,1 - FDC access --------' | | | | | | | +// | |0 - DMA on,1 - no DMA ------------------' | | | | | | +// | |Reserved ---------------------------------' | | | | | +// | |0 - FDC reg,1 - sector count reg -----------' | | | | +// | |0 - FDC access,1 - HDC access ----------------' | | | +// | |0 - pin A1 low, 1 - pin A1 high ----------------' | | +// | |0 - pin A0 low, 1 - pin A0 high ------------------' | +// $FF8609|byte |DMA base and counter (High byte) |R/W +// $FF860B|byte |DMA base and counter (Mid byte) |R/W +// $FF860D|byte |DMA base and counter (Low byte) |R/W +// | |Note: write address from low toward high byte | + module dma ( - // cpu register interface - input clk, - input reset, - input [15:0] din, - input sel, - input [2:0] addr, - input uds, - input lds, - input rw, - output reg [15:0] dout, - - // output to mfp - output irq, - - // input from system config - input fdc_wr_prot, - input [7:0] acsi_enable, - - // connection to data_io (arm controller spi interface) - input [4:0] dio_idx, - output reg [7:0] dio_data, - input dio_ack, - - // input from psg - input drv_side, - input [1:0] drv_sel + // clocks and system interface + input clk, + input reset, + input [1:0] bus_cycle, + input turbo, + output irq, + + // (this really doesn't belong here ...) + output reg [31:0] ctrl_out, + // horizontal and vertical screen adjustments + output reg [15:0] video_adj, + + // cpu interface + input [15:0] cpu_din, + input cpu_sel, + input [2:0] cpu_addr, + input cpu_uds, + input cpu_lds, + input cpu_rw, + output reg [15:0] cpu_dout, + + // spi interface + input sdi, + input sck, + input ss, + output sdo, + + // additional fdc control signals provided by PSG and OSD + input fdc_wr_prot, + input drv_side, + input [1:0] drv_sel, + // additional acsi control signals provided by OSD + input [7:0] acsi_enable, + + // ram interface for dma engine + output ram_br, + output reg ram_read, + output reg ram_write, + output [22:0] ram_addr, + output [15:0] ram_dout, + input [15:0] ram_din ); -// ------------- data_io (arm controller) interface ------ - -always @(dio_idx, base, scnt, fdc_cmd, fdc_track, fdc_sector, - fdc_data, drv_sel, drv_side, fdc_busy, acsi_busy, acsi_target, acsi_cmd) begin - 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 = { 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 +assign irq = fdc_irq || acsi_irq; + +// for debug: count irqs +reg [7:0] fdc_irq_count; +always @(posedge fdc_irq or posedge reset) begin + if(reset) fdc_irq_count <= 8'd0; + else fdc_irq_count <= fdc_irq_count + 8'd1; end -// ------------------ cpu interface -------------------- -// 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; +reg [7:0] acsi_irq_count; +always @(posedge acsi_irq or posedge reset) begin + if(reset) acsi_irq_count <= 8'd0; + else acsi_irq_count <= acsi_irq_count + 8'd1; +end -// route FDC or ACSI irq out on irq pin -assign irq = fdc_irq || acsi_irq; +// filter spi clock. the 8 bit gate delay is ~2.5ns in total +wire [7:0] spi_sck_D = { spi_sck_D[6:0], sck } /* synthesis keep */; +wire spi_sck = (spi_sck && spi_sck_D != 8'h00) || + (!spi_sck && spi_sck_D == 8'hff); -// 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; - -// fdc registers -reg [7:0] fdc_cmd; -reg [7:0] fdc_track; -reg [7:0] fdc_sector; -reg [7:0] fdc_data; - -// dma base address register and sector counter -reg [23:0] base; -reg [7:0] scnt; - -// virtual head is over track 0 -wire track0; -assign track0 = (fdc_track == 8'd0); - -reg step_dir; - -// status byte returned by the fdc when reading register 0 -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 != 0 }; - -wire [15:0] dma_status; -assign dma_status = { 14'd0, !(scnt == 0), 1'b1 }; // bit 0 = 1: DMA_OK - -// timer to simulate motor-on -reg [15:0] motor_on; - -always @(sel, rw, addr, mode, base, fdc_data, fdc_sector, fdc_status, fdc_track, - dma_status, scnt, acsi_target, acsi_busy) begin - dout = 16'h0000; - - if(sel && rw) begin - if((addr == 3'h2) && (mode[4] == 1'b0)) begin - // controller access register - if(mode[3] == 1'b0) begin - // fdc - if(mode[2:1] == 2'b00) // status register - dout = { 8'h00, fdc_status }; - if(mode[2:1] == 2'b01) // track register - dout = { 8'h00, fdc_track }; - if(mode[2:1] == 2'b10) // sector register - dout = { 8'h00, fdc_sector }; - 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, 1'b0, acsi_busy, 3'h0 }; // status "ok" - dout = { 8'h00, 3'b000, 1'b0, acsi_busy, 3'h0 }; // status "ok" - end - end - - if(addr == 3'h3) - dout = dma_status; - - // sector count register - if((addr == 3'h2) && (mode[4] == 1'b1)) - dout = { 8'h00, scnt }; - - // dma base address read back - if(addr == 3'h4) - dout = { 8'h00, base[23:16] }; - if(addr == 3'h5) - dout = { 8'h00, base[15:8] }; - if(addr == 3'h6) - dout = { 8'h00, base[7:0] }; - end -end +reg [5:0] cnt; // bit counter (counting spi bits, rolling from 39 to 8) +reg [4:0] bcnt; // payload byte counter +reg [30:0] sbuf; // receive buffer (buffer used to assemble spi bytes/words) +reg [7:0] cmd; // command byte (first byte of spi transmission) + +// word address given bei io controller +reg data_io_addr_strobe; -// --------------- 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; +// dma sector count and mode registers +reg [7:0] dma_scnt; +reg [15:0] dma_mode; -// acsi status register is access (clears interrupt) -wire acsi_status_read = sel && (addr == 3'h2) && (mode[4:3] == 2'b01); +// ============= FDC submodule ============ -reg dio_ackD, dio_ackD2; -always @(posedge clk) begin - dio_ackD <= dio_ack; +// select signal for the fdc controller registers +wire fdc_reg_sel = cpu_sel && !cpu_lds && (cpu_addr == 3'h2) && (dma_mode[4:3] == 2'b00); +wire fdc_irq; +wire [7:0] fdc_status_byte; +wire [7:0] fdc_dout; + +fdc fdc( .clk ( clk ), + .reset ( reset ), + + .irq ( fdc_irq ), + + // external floppy control signals + .drv_sel ( drv_sel ), + .drv_side ( drv_side ), + .wr_prot ( fdc_wr_prot ), + + // signals from/to io controller + .dma_ack ( dma_ack ), + .status_sel ( bcnt-4'd4 ), + .status_byte ( fdc_status_byte ), + + // cpu interfaces, passed trough dma in st + .cpu_sel ( fdc_reg_sel ), + .cpu_addr ( dma_mode[2:1] ), + .cpu_rw ( cpu_rw ), + .cpu_din ( cpu_din[7:0] ), + .cpu_dout ( fdc_dout ) +); + +// ============= ACSI submodule ============ + +// select signal for the acsi controller access (write only, status comes from io controller) +wire acsi_reg_sel = cpu_sel && !cpu_lds && (cpu_addr == 3'h2) && (dma_mode[4:3] == 2'b01); +wire acsi_irq; +wire [7:0] acsi_status_byte; +wire [7:0] acsi_dout; + +acsi acsi(.clk ( clk ), + .reset ( reset ), + + .irq ( acsi_irq ), + + // acsi target enable + .enable ( acsi_enable ), + + // signals from/to io controller + .dma_ack ( dma_ack ), + .dma_nak ( dma_nak ), + .dma_status ( dma_status ), + + .status_sel ( bcnt-4'd9 ), + .status_byte ( acsi_status_byte ), + + // cpu interface, passed through dma in st + .cpu_sel ( acsi_reg_sel ), + .cpu_addr ( dma_mode[2:1] ), + .cpu_rw ( cpu_rw ), + .cpu_din ( cpu_din[7:0] ), + .cpu_dout ( acsi_dout ) +); + +// ============= CPU read interface ============ +always @(cpu_sel, cpu_rw, cpu_addr, dma_mode, dma_addr, dma_scnt) begin + cpu_dout = 16'h0000; + + if(cpu_sel && cpu_rw) begin + + // register $ff8604 + if(cpu_addr == 3'h2) begin + if(dma_mode[4] == 1'b0) begin + // controller access register + if(dma_mode[3] == 1'b0) + cpu_dout = { 8'h00, fdc_dout }; + else + cpu_dout = { 8'h00, acsi_dout }; + end else + cpu_dout = { 8'h00, dma_scnt }; // sector count register + end + + // DMA status register $ff8606 + // bit 0 = 1: DMA_OK, bit 2 = state of FDC DRQ + if(cpu_addr == 3'h3) cpu_dout = { 14'd0, dma_scnt != 0, 1'b1 }; + + // dma address read back at $ff8609-$ff860d + if(cpu_addr == 3'h4) cpu_dout = { 8'h00, dma_addr[22:15] }; + if(cpu_addr == 3'h5) cpu_dout = { 8'h00, dma_addr[14:7] }; + if(cpu_addr == 3'h6) cpu_dout = { 8'h00, dma_addr[6:0], 1'b0 }; + end end - + +// ============= CPU write interface ============ +// flags indicating the cpu is writing something. valid on rising edge +reg cpu_address_write_strobe; +reg cpu_scnt_write_strobe; +reg cpu_mode_write_strobe; +reg cpu_dma_mode_direction_toggle; + always @(negedge clk) begin if(reset) begin - mode <= 16'd0; - fdc_cmd <= 8'd0; - fdc_track <= 8'd0; - fdc_sector <= 8'd0; - fdc_data <= 8'd0; - base <= 24'h000000; - scnt <= 8'h00; - fdc_busy <= 2'd0; - motor_on <= 16'd0; - fdc_irq <= 1'b0; - acsi_target <= 3'd0; - acsi_cmd <= 5'd0; - acsi_irq <= 1'b0; - acsi_busy <= 1'b0; + cpu_address_write_strobe <= 1'b0; + cpu_scnt_write_strobe <= 1'b0; + cpu_mode_write_strobe <= 1'b0; + cpu_dma_mode_direction_toggle <= 1'b0; + end else begin + cpu_address_write_strobe <= 1'b0; + cpu_scnt_write_strobe <= 1'b0; + cpu_mode_write_strobe <= 1'b0; + cpu_dma_mode_direction_toggle <= 1'b0; + + // cpu writes ... + if(cpu_sel && !cpu_rw && !cpu_lds) begin + + // ... sector count register + if((cpu_addr == 3'h2) && (dma_mode[4] == 1'b1)) + cpu_scnt_write_strobe <= 1'b1; + + // ... dma mode register + if(cpu_addr == 3'h3) begin + dma_mode <= cpu_din; + cpu_mode_write_strobe <= 1'b1; + + // check if cpu toggles direction bit (bit 8) + if(dma_mode[8] != cpu_din[8]) + cpu_dma_mode_direction_toggle <= 1'b1; + end + + // ... dma address + if((cpu_addr == 3'h4) || (cpu_addr == 3'h5) || (cpu_addr == 3'h6)) + // trigger address engine latch + cpu_address_write_strobe <= 1'b1; + end + end +end + + +// SPI data io and strobe +// TODO: these don't belong here +reg [15:0] io_data_in; +reg io_data_in_strobe; // data from IOC ready to be stored in fifo +reg io_data_out_strobe; +reg io_data_out_inc_strobe; + +// ======================== BUS cycle handler =================== + +// specify which bus cycles to use +wire cycle_advance = (bus_cycle == 2'd0) || (turbo && (bus_cycle == 2'd2)); +wire cycle_io = (bus_cycle == 2'd1) || (turbo && (bus_cycle == 2'd3)); + +// latch bus cycle information to use at the end of the cycle (posedge clk) +reg cycle_advance_L, cycle_io_L; +always @(negedge clk) begin + cycle_advance_L <= cycle_advance; + cycle_io_L <= cycle_io; +end + +// ======================================================================= +// ============================= DMA FIFO ================================ +// ======================================================================= + +// 32 byte dma fifo (actually a 16 word fifo) +reg [15:0] fifo [15:0]; +reg [3:0] fifo_wptr; // word pointers +reg [3:0] fifo_rptr; +wire [3:0] fifo_ptr_diff = fifo_wptr - fifo_rptr; +reg [2:0] fifo_read_cnt; // fifo read transfer word counter +reg [2:0] fifo_write_cnt; // fifo write transfer word counter + +// fifo is ready to be re-filled if it is empty +// -> we don't use this as the fifo is not refilled fast enough in non-turbo +wire fifo_not_full = fifo_ptr_diff < 4'd7; // (turbo?4'd1:4'd2); +// fifo is considered full if 8 words (16 bytes) are present +wire fifo_full = (fifo_wptr - fifo_rptr) > 4'd7; + +// Reset fifo via the dma mode direction bit toggling or when +// IO controller sets address +wire fifo_reset = cpu_dma_mode_direction_toggle || data_io_addr_strobe; + +reg fifo_read_in_progress, fifo_write_in_progress; +assign ram_br = fifo_read_in_progress || fifo_write_in_progress || ioc_br_clk; + +reg ioc_br_clk; +always @(negedge clk) + if(cycle_advance) + ioc_br_clk <= ioc_br; + +// ============= FIFO WRITE ENGINE ================== +// rising edge after ram has been read +reg ram_read_done; +always @(posedge clk) ram_read_done <= ram_read; + +// state machine for DMA ram read access +// this runs on negative clk to generate a proper bus request +// in the middle of the advance cycle + +// start condition for fifo write +wire fifo_write_start = dma_in_progress && dma_direction_out && + fifo_write_cnt == 0 && fifo_not_full; + +always @(negedge clk or posedge fifo_reset) begin + if(fifo_reset == 1'b1) begin + fifo_write_cnt <= 3'd0; + fifo_write_in_progress <= 1'b0; + end else begin + if(cycle_advance) begin + // start dma read engine if 8 more words can be stored + if(fifo_write_start) begin + fifo_write_cnt <= 3'd7; + fifo_write_in_progress <= 1'b1; + end else begin + if(fifo_write_cnt != 0) + fifo_write_cnt <= fifo_write_cnt - 3'd1; + else + fifo_write_in_progress <= 1'b0; + end + end + end +end + +// ram control signals need to be stable over the whole 8 Mhz cycle +always @(posedge clk) + ram_read <= (cycle_advance_L && fifo_write_in_progress)?1'b1:1'b0; + +wire io_data_in_strobe_clk; +spi2clk io_data_in_strobe2clk ( + .clk ( clk ), + .in ( io_data_in_strobe ), + .out ( io_data_in_strobe_clk ) +); + +wire [15:0] fifo_data_in = dma_direction_out?ram_din:io_data_in; +wire fifo_data_in_strobe = dma_direction_out?ram_read_done:io_data_in_strobe_clk; + +// write to fifo on rising edge of fifo_data_in_strobe +always @(posedge fifo_data_in_strobe or posedge fifo_reset) begin + if(fifo_reset == 1'b1) + fifo_wptr <= 4'd0; + else begin + fifo[fifo_wptr] <= fifo_data_in; + fifo_wptr <= fifo_wptr + 4'd1; + end +end + +// ============= FIFO READ ENGINE ================== + +// start condition for fifo read +wire fifo_read_start = dma_in_progress && !dma_direction_out && + fifo_read_cnt == 0 && fifo_full; + +// state machine for DMA ram write access +always @(negedge clk or posedge fifo_reset) begin + if(fifo_reset == 1'b1) begin + // not reading from fifo, not writing into ram + fifo_read_cnt <= 3'd0; + fifo_read_in_progress <= 1'b0; + end else begin + if(cycle_advance) begin + // start dma read engine if 8 more words can be stored + if(fifo_read_start) begin + fifo_read_cnt <= 3'd7; + fifo_read_in_progress <= 1'b1; + end else begin + if(fifo_read_cnt != 0) + fifo_read_cnt <= fifo_read_cnt - 3'd1; + else + fifo_read_in_progress <= 1'b0; + end + end + end +end + +// ram control signals need to be stable over the whole 8 Mhz cycle +always @(posedge clk) + ram_write <= (cycle_advance_L && fifo_read_in_progress)?1'b1:1'b0; + +// a signal half a 8mhz cycle earlier than ram_write to prefetch the data +// from the fifo right before ram write +wire fifo_read_prep = fifo_read_start || (fifo_read_cnt != 0); + +reg ram_write_prep; +always @(negedge clk) + ram_write_prep <= (cycle_advance && fifo_read_prep)?1'b1:1'b0; + +// Bring data out strobe from SPI clock domain into DMAs local clock +// domain to make sure fifo write and read run on the same clock and +// signals derived from the fifo counters are thus glitch free. This +// delays the generation of the fifos "not full" signal slighlty which +// in turn requires reloading the fifo even if it still contains one +// word in non-turbo (2MHz). Waiting for the fifo to become empty +// would not reload the first word from memory fast enough. In turbo +// (4Mhz) mode this is no problem and the first word from memory +// is being read before it has to be ready for SPI transmission +wire io_data_out_inc_strobe_clk; +spi2clk io_data_out_inc_strobe2clk ( + .clk ( clk ), + .in ( io_data_out_inc_strobe ), + .out ( io_data_out_inc_strobe_clk ) +); + +reg [15:0] fifo_data_out; +wire fifo_data_out_strobe = dma_direction_out?io_data_out_strobe:ram_write_prep; +wire fifo_data_out_strobe_clk = dma_direction_out?io_data_out_inc_strobe_clk:ram_write; + +always @(posedge fifo_data_out_strobe) + fifo_data_out <= fifo[fifo_rptr]; + +always @(posedge fifo_data_out_strobe_clk or posedge fifo_reset) begin + if(fifo_reset == 1'b1) fifo_rptr <= 4'd0; + else fifo_rptr <= fifo_rptr + 4'd1; +end + +// use fifo output directly as ram data +assign ram_dout = fifo_data_out; + +// ========================================================================== +// =============================== internal registers ======================= +// ========================================================================== + +// ================================ DMA sector count ======================== +// - register is decremented by one after 512 bytes being transferred +// - cpu can write this register directly +// - io controller can write this via the set_address command + +// CPU write access to even addresses +wire cpu_write = cpu_sel && !cpu_rw && !cpu_lds; + +// Delay the write_strobe a little bit as sector_cnt is included in +// dma_scnt_write_strobe and in turn resets sector_strobe. As a result +// sector_strobe would be a very short spike only. +wire dma_scnt_write_strobe_clk; +spi2clk dma_scnt_write_strobe2clk ( + .clk ( !clk ), + .in ( dma_scnt_write_strobe ), + .out ( dma_scnt_write_strobe_clk ) +); + +// keep track of bytes to decrement sector count register +// after 512 bytes (256 words) +reg [7:0] word_cnt; +reg sector_done; +reg sector_strobe; +reg sector_strobe_prepare; + +always @(negedge clk or posedge dma_scnt_write_strobe_clk) begin + if(dma_scnt_write_strobe_clk) begin + word_cnt <= 8'd0; + sector_strobe_prepare <= 1'b0; + sector_strobe <= 1'b0; + sector_done <= 1'b0; end else begin - - dio_ackD2 <= dio_ackD; - if(dio_ackD && !dio_ackD2) begin - scnt <= 8'h00; // all sectors transmitted - - // 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 acsi 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; - - // dma control and mode register - if(sel && ~rw) begin - if(~lds && (addr == 3'h2) && (mode[4] == 1'b0)) begin - // controller access register - 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 - if((din[7] == 1'b0) || (din[7:6] == 2'b10)) - motor_on <= 16'hffff; - - // ------------- TYPE I commands ------------- - - if(din[7:4] == 4'b0000) // RESTORE - fdc_track <= 8'd0; - - if(din[7:4] == 4'b0001) // SEEK - fdc_track <= fdc_data; - - if(din[7:4] == 4'b0011) begin // STEP with update flag - if(step_dir == 1'b1) - fdc_track <= fdc_track + 8'd1; - else - fdc_track <= fdc_track - 8'd1; - end - - if(din[7:4] == 4'b0101) begin // STEP-IN with update flag - step_dir <= 1'b1; - fdc_track <= fdc_track + 8'd1; - end - - if(din[7:4] == 4'b0111) begin // STEP-OUT with update flag - step_dir <= 1'b0; - fdc_track <= fdc_track - 8'd1; - end - - // ------------- TYPE II commands ------------- - if(din[7:5] == 3'b100) begin // read sector - fdc_busy <= 2'd3; - end - - if(din[7:5] == 3'b101) // write sector - if(!fdc_wr_prot) - fdc_busy <= 2'd3; - - // ------------- TYPE III commands ------------ - - if(din[7:4] == 4'b1100) // read address - fdc_busy <= 2'd3; - - if(din[7:4] == 4'b1110) // read track - fdc_busy <= 2'd3; - - if(din[7:4] == 4'b1111) // write track - if(!fdc_wr_prot) - fdc_busy <= 2'd3; - - // ------------- TYPE IV commands ------------- - if(din[7:4] == 4'b1101) // force intrerupt - fdc_busy <= 2'd1; - - end if(mode[2:1] == 2'b01) // track register - fdc_track <= din[7:0]; - if(mode[2:1] == 2'b10) // sector register - 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; - - // check if this acsi device is enabled - if(acsi_enable[din[7:5]] == 1'b1) - 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; - - // check if this acsi device is enabled - if(acsi_enable[acsi_target] == 1'b1) begin - // auto-ack first 5 bytes - if(acsi_byte_counter < 4) - acsi_irq <= 1'b1; - else begin - acsi_busy <= 1'b1; // request io cntroller - end - end - end - end - end - - // sector count register - if(~lds && (addr == 3'h2) && (mode[4] == 1'b1)) - scnt <= din[7:0]; - - if(addr == 3'h3) - mode <= din; - - if(~lds && (addr == 3'h4)) - base[23:16] <= din[7:0]; - if(~lds && (addr == 3'h5)) - base[15:8] <= din[7:0]; - if(~lds && (addr == 3'h6)) - base[7:0] <= din[7:0]; + if(cycle_io) begin + sector_strobe_prepare <= 1'b0; + sector_strobe <= 1'b0; + + // wait a little after the last word + if(sector_done) begin + sector_done <= 1'b0; + sector_strobe_prepare <= 1'b1; + // trigger scnt decrement + sector_strobe <= 1'b1; + end end + + // and ram read or write increases the word counter by one + if(ram_write || ram_read) begin + word_cnt <= word_cnt + 8'd1; + if(word_cnt == 255) begin + sector_done <= 1'b1; + // give multiplexor some time ahead ... + sector_strobe_prepare <= 1'b1; + end + end + end +end + +// cpu and io controller can write the scnt register and it's decremented +// after 512 bytes +wire dma_scnt_write_strobe = + cpu_scnt_write_strobe || data_io_addr_strobe || sector_strobe; +wire fifo_in_progress = fifo_read_in_progress || fifo_write_in_progress; +wire cpu_writes_scnt = cpu_write && (cpu_addr == 3'h2) && (dma_mode[4] == 1'b1); +// sector counter doesn't count below 0 +wire [7:0] dma_scnt_dec = (dma_scnt != 0)?(dma_scnt-8'd1):8'd0; +// multiplex new sector count data +wire [7:0] dma_scnt_next = sector_strobe_prepare?dma_scnt_dec: + cpu_writes_scnt?cpu_din[7:0]:sbuf[30:23]; + +// cpu or io controller set the sector count register +always @(posedge dma_scnt_write_strobe) + dma_scnt <= dma_scnt_next; + +// DMA in progress flag: +// - cpu writing the sector count register starts the DMA engine if +// dma enable bit 6 in mode register is clear +// - io controller setting the address starts the dma engine +// - changing sector count to 0 (cpu, io controller or counter) stops DMA +// - cpu writing toggling dma direction stops dma +reg dma_in_progress; +wire dma_stop = cpu_dma_mode_direction_toggle; + +// dma can be started if sector is not zero and if dma is enabled +// by a zero in bit 6 of dma mode register +wire cpu_starts_dma = cpu_writes_scnt && (cpu_din[7:0] != 0) && !dma_mode[6]; +wire ioc_starts_dma = ioc_writes_addr && (sbuf[30:23] != 0); + +always @(posedge dma_scnt_write_strobe or posedge dma_stop) begin + if(dma_stop) dma_in_progress <= 1'b0; + else dma_in_progress <= cpu_starts_dma || ioc_starts_dma || (sector_strobe && dma_scnt_next != 0); +end + +// ========================== DMA direction flag ============================ +reg dma_direction_out; // == 1 when transferring from fpga to io controller +wire cpu_writes_mode = cpu_write && (cpu_addr == 3'h3); +wire dma_direction_set = data_io_addr_strobe || cpu_dma_mode_direction_toggle; + +// bit 8 == 0 -> dma read -> dma_direction_out, io ctrl address bit 23 = dir +wire dma_direction_out_next = cpu_writes_mode?cpu_din[8]:sbuf[22]; + +// cpu or io controller set the dma direction +always @(posedge dma_direction_set) + dma_direction_out <= dma_direction_out_next; + +// ================================= DMA address ============================ +// address can be changed through three events: +// - cpu writes single address bytes into three registers +// - io controller writes address via spi +// - dma engine runs and address is incremented + +// dma address is stored in three seperate registers as +// otherwise verilator complains about signals having multiple driving blocks +reg [7:0] dma_addr_h; +reg [7:0] dma_addr_m; +reg [6:0] dma_addr_l; +wire [22:0] dma_addr = { dma_addr_h, dma_addr_m, dma_addr_l }; + +reg dma_addr_inc; +always @(posedge clk) + dma_addr_inc <= ram_write || ram_read; + +wire cpu_writes_addr = cpu_write && ((cpu_addr == 6) || (cpu_addr == 5) || (cpu_addr == 4)); +wire ioc_writes_addr = (ss == 0) && (cmd == MIST_SET_ADDRESS); +wire dma_addr_write_strobe = dma_addr_inc || data_io_addr_strobe; + +// address to be set by next write strobe +wire [22:0] dma_addr_next = + cpu_writes_addr?{ cpu_din[7:0], cpu_din[7:0], cpu_din[7:1] }: + ioc_writes_addr?{ sbuf[21:0], sdi }: + (dma_addr + 23'd1); + +// dma address low byte +wire dma_addr_write_strobe_l = dma_addr_write_strobe || (cpu_address_write_strobe && (cpu_addr == 3'h6)); +always @(posedge dma_addr_write_strobe_l) + dma_addr_l <= dma_addr_next[6:0]; + +// dma address mid byte +wire dma_addr_write_strobe_m = dma_addr_write_strobe || (cpu_address_write_strobe && (cpu_addr == 3'h5)); +always @(posedge dma_addr_write_strobe_m) + dma_addr_m <= dma_addr_next[14:7]; + +// dma address hi byte +wire dma_addr_write_strobe_h = dma_addr_write_strobe || (cpu_address_write_strobe && (cpu_addr == 3'h4)); +always @(posedge dma_addr_write_strobe_h) + dma_addr_h <= dma_addr_next[22:15]; + +// dma address is used directly to address the ram +assign ram_addr = dma_addr; + +// dma status interface +reg [7:0] dma_status; // sent with ack, only used by acsi +reg io_dma_ack, io_dma_nak; + +wire dma_ack, dma_nak; +spi2clk dma_ack2clk ( + .clk ( clk ), + .in ( io_dma_ack ), + .out ( dma_ack ) +); + +spi2clk dma_nak2clk ( + .clk ( clk ), + .in ( io_dma_nak ), + .out ( dma_nak ) +); + +// dma status byte as signalled to the io controller +wire [7:0] dma_io_status = + (bcnt == 0)?dma_addr[22:15]: + (bcnt == 1)?dma_addr[14:7]: + (bcnt == 2)?{ dma_addr[6:0], dma_direction_out }: + (bcnt == 3)?dma_scnt: + // 5 bytes FDC status + ((bcnt >= 4)&&(bcnt <= 8))?fdc_status_byte: + // 7 bytes ACSI status + ((bcnt >= 9)&&(bcnt <= 15))?acsi_status_byte: + // DMA debug signals + (bcnt == 16)?8'ha5: + (bcnt == 17)?{ fifo_rptr, fifo_wptr}: + (bcnt == 18)?{ 4'd0, fdc_irq, acsi_irq, ioc_br_clk, dma_in_progress }: + (bcnt == 19)?dma_status: + (bcnt == 20)?dma_mode[8:1]: + (bcnt == 21)?fdc_irq_count: + (bcnt == 22)?acsi_irq_count: + 8'h00; + +// ==================================================================== +// ===================== SPI client to IO controller ================== +// ==================================================================== + +// the following must match the codes in tos.h +localparam MIST_SET_ADDRESS = 8'h01; +localparam MIST_WRITE_MEMORY = 8'h02; +localparam MIST_READ_MEMORY = 8'h03; +localparam MIST_SET_CONTROL = 8'h04; +localparam MIST_GET_DMASTATE = 8'h05; // reads state of dma and floppy controller +localparam MIST_ACK_DMA = 8'h06; // acknowledge a dma command +localparam MIST_SET_VADJ = 8'h09; +localparam MIST_NAK_DMA = 8'h0a; // reject a dma command + +// ===================== SPI transmitter ================== + +reg [3:0] sdo_bit; +always @(negedge sck) + sdo_bit <= 4'd7 - cnt[3:0]; + +wire sdo_fifo = fifo_data_out[sdo_bit]; +wire sdo_dmastate = dma_io_status[sdo_bit[2:0]]; + +assign sdo = (cmd==MIST_READ_MEMORY)?sdo_fifo:sdo_dmastate; + +// ===================== SPI receiver ================== + +reg ioc_br = 1'b0; + +always@(posedge spi_sck, posedge ss) begin + if(ss == 1'b1) begin + cmd <= 8'd0; + cnt <= 6'd0; + bcnt <= 4'd0; + io_dma_ack <= 1'b0; + io_dma_nak <= 1'b0; + io_data_in_strobe <= 1'b0; + io_data_out_strobe <= 1'b0; + io_data_out_inc_strobe <= 1'b0; + data_io_addr_strobe <= 1'b0; + end else begin + io_dma_ack <= 1'b0; + io_dma_nak <= 1'b0; + io_data_in_strobe <= 1'b0; + io_data_out_strobe <= 1'b0; + io_data_out_inc_strobe <= 1'b0; + data_io_addr_strobe <= 1'b0; + + // shift bits in. stop shifting after payload + if(cmd == MIST_SET_ADDRESS) begin + // set address is 8+32 bits + if(cnt < 39) + sbuf <= { sbuf[29:0], sdi}; + end else if(cmd == MIST_WRITE_MEMORY) begin + if((cnt != 23) && (cnt != 39)) + sbuf <= { sbuf[29:0], sdi}; + end else + sbuf <= { sbuf[29:0], sdi}; + + // 0:7 is command, 8:15 and 16:23 is payload bytes + if(cnt < 39) + cnt <= cnt + 6'd1; + else + cnt <= 6'd8; + + // count payload bytes + if((cnt == 15) || (cnt == 23) || (cnt == 31) || (cnt == 39)) + bcnt <= bcnt + 4'd1; + + if(cnt == 5'd7) begin + cmd <= {sbuf[6:0], sdi}; + + if({sbuf[6:0], sdi } == 8'h07) + ioc_br <= 1'b1; + + if({sbuf[6:0], sdi } == 8'h08) + ioc_br <= 1'b0; + + // send nak + if({sbuf[6:0], sdi } == MIST_NAK_DMA) + io_dma_nak <= 1'b1; + + // read first byte from fifo once the read command is recognized + if({sbuf[6:0], sdi } == MIST_READ_MEMORY) + io_data_out_strobe <= 1'b1; + end + + // handle "payload" + + // set address + if((cmd == MIST_SET_ADDRESS) && (cnt == 39)) + data_io_addr_strobe <= 1'b1; + + // read ram + if(cmd == MIST_READ_MEMORY) begin + // read word from fifo + if((cnt == 23)||(cnt == 39)) + io_data_out_strobe <= 1'b1; + + // increment fifo read pointer + if((cnt == 8)||(cnt == 24)) + io_data_out_inc_strobe <= 1'b1; + end + + // write ram + if(cmd == MIST_WRITE_MEMORY) begin + // received one word, store it in fifo + if((cnt == 23)||(cnt == 39)) begin + io_data_in <= { sbuf[14:0], sdi }; + io_data_in_strobe <= 1'b1; + end + end + + // dma_ack + if((cmd == MIST_ACK_DMA) && (cnt == 15)) begin + io_dma_ack <= 1'b1; + dma_status <= { sbuf[6:0], sdi }; + end + + // set control register + if((cmd == MIST_SET_CONTROL) && (cnt == 39)) + ctrl_out <= { sbuf, sdi }; + + // set video offsets + if((cmd == MIST_SET_VADJ) && (cnt == 5'd23)) + video_adj <= { sbuf[14:0], sdi }; end end + +endmodule + +// =========================================================== +// Module used to bring rising edges into the clk clock domain +// =========================================================== +module spi2clk ( + input in, + input clk, + output reg out +); + +// set latch on rising edge of input signal. Clear it on rising edge +// of output signal +reg latch; +always @(posedge in or posedge out) begin + if(out) latch <= 1'b0; + else latch <= 1'b1; +end + +// move latched signal to output. This in turn clears the latch, +// so out will be reset in the next clk edge +always @(posedge clk) + out <= latch; -endmodule \ No newline at end of file +endmodule // masking diff --git a/cores/mist/fdc.v b/cores/mist/fdc.v new file mode 100644 index 0000000..260dc16 --- /dev/null +++ b/cores/mist/fdc.v @@ -0,0 +1,271 @@ +// fdc.v +// +// Atari ST floppy implementation for the MIST baord +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2014 Till Harbaum +// +// 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 fdc ( + // clocks and system interface + input clk, + input reset, + + // write protection of currently selected floppy + input [1:0] drv_sel, + input drv_side, + input wr_prot, + + input dma_ack, + input [2:0] status_sel, + output [7:0] status_byte, + + // cpu interface + input [1:0] cpu_addr, + input cpu_sel, + input cpu_rw, + input [7:0] cpu_din, + output reg [7:0] cpu_dout, + + output reg irq +); + +// 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 +localparam STATE_IDLE = 2'd0; +localparam STATE_IRQ = 2'd1; +localparam STATE_INT_WAIT = 2'd2; +localparam STATE_IO_WAIT = 2'd3; + +reg [1:0] state; // fdc busy state + +// the fdc registers +reg [7:0] cmd; // write only +reg [7:0] track; +reg [7:0] sector; +reg [7:0] data; + +// fdc status as reported to the io controller +assign status_byte = + (status_sel == 0)?cmd: + (status_sel == 1)?track: + (status_sel == 2)?sector: + (status_sel == 3)?data: + (status_sel == 4)?{ 4'b0000, drv_sel, drv_side, state == STATE_IO_WAIT }: + 8'h00; + +reg step_dir; + +reg [31:0] delay; + +wire cmd_type_1 = (cmd[7] == 1'b0); +wire cmd_type_2 = (cmd[7:6] == 2'b10); + +// ---------------- floppy motor simulation ------------- + +// timer to simulate motor-on. This runs for x/8000000 seconds after each command +reg motor_start; +reg [31:0] motor_on_counter; +wire motor_on = (motor_on_counter != 0); + +// motor_on_counter > 16000000 means the motor is spinning up +wire motor_spin_up_done = motor_on && (motor_on_counter <= 16000000); + +always @(posedge clk or posedge motor_start) begin + if(motor_start) + // motor runs for 2 seconds if it was already on. it rus for one + // more second if if wasn't on yet (spin up) + motor_on_counter <= motor_on?32'd16000000:32'd24000000; + else begin + // let "motor" run + if(motor_on_counter != 0) + motor_on_counter <= motor_on_counter - 32'd1; + end +end + +// -------------- index pulse generation ---------------- + +// floppy rotates at 300rpm = 5rps -> generate 5 index pulses per second +wire index_pulse = index_pulse_cnt > 32'd1500000; // 1/16 rotation +reg [31:0] index_pulse_cnt; +always @(posedge clk) begin + if(!motor_on) + index_pulse_cnt <= 32'd0; + else begin + if(index_pulse_cnt != 0) + index_pulse_cnt <= index_pulse_cnt - 32'd1; + else + index_pulse_cnt <= 32'd1600000; // 8000000/5 + end +end + +// status byte returned by the fdc when reading register 0 +wire [7:0] status = { + motor_on, + wr_prot, + cmd_type_1?motor_spin_up_done:1'b0, + 2'b00 /* track not found/crc err */, + cmd_type_1?(track == 0):1'b0, + cmd_type_1?index_pulse:(state!=STATE_IDLE), + state != STATE_IDLE +}; + +// CPU register read +always @(cpu_sel, cpu_addr, cpu_rw) begin + cpu_dout = 8'h00; + + if(cpu_sel && cpu_rw) begin + case(cpu_addr) + 0: cpu_dout = status; + 1: cpu_dout = track; + 2: cpu_dout = sector; + 3: cpu_dout = data; + endcase + end +end + +// CPU register write +always @(negedge clk or posedge reset) begin + if(reset) begin + // clear internal registers + cmd <= 8'h00; + track <= 8'h00; + sector <= 8'h00; + data <= 8'h00; + + // reset state machines and counters + state <= STATE_IDLE; + irq <= 1'b0; + motor_start <= 1'b0; + delay <= 32'd0; + + end else begin + motor_start <= 1'b0; + + // DMA transfer has been ack'd by io controller + if(dma_ack) begin + // fdc waiting for io controller + if(state == STATE_IO_WAIT) + state <= STATE_IRQ; // jump to end of busy phase + end + + // fdc may be waiting internally (e.g. for step completion) + if(state == STATE_INT_WAIT) begin + // count down and go into irq state if done + if(delay != 0) + delay <= delay - 32'd1; + else + state <= STATE_IRQ; + end + + // fdc is ending busy phase + if(state == STATE_IRQ) begin + irq <= 1'b1; + state <= STATE_IDLE; + end + + // cpu is reading status register or writing command register -> clear fdc irq + if(cpu_sel && (cpu_addr == 0)) + irq <= 1'b0; + + if(cpu_sel && !cpu_rw) begin + // fdc register write + if(cpu_addr == 0) begin // command register + cmd <= cpu_din; + state <= STATE_INT_WAIT; + delay <= 31'd0; + + // all TYPE I and TYPE II commands start the motor + if((cpu_din[7] == 1'b0) || (cpu_din[7:6] == 2'b10)) + motor_start <= 1'b1; + + // ------------- TYPE I commands ------------- + if(cpu_din[7:4] == 4'b0000) begin // RESTORE + track <= 8'd0; + delay <= 31'd2000000; // 250ms delay + end + + if(cpu_din[7:4] == 4'b0001) begin // SEEK + track <= data; + delay <= 31'd200000; // 25ms delay + end + + if(cpu_din[7:3] == 3'b001) begin // STEP + delay <= 31'd20000; // 2.5ms delay + if(cpu_din[4]) // update flag + track <= (step_dir == 1)?(track + 8'd1):(track - 8'd1); + end + + if(cpu_din[7:5] == 3'b010) begin // STEP-IN + delay <= 31'd20000; // 2.5ms delay + step_dir <= 1'b1; + if(cpu_din[4]) // update flag + track <= track + 8'd1; + end + + if(cpu_din[7:5] == 3'b011) begin // STEP-OUT + delay <= 31'd20000; // 2.5ms delay + step_dir <= 1'b0; + if(cpu_din[4]) // update flag + track <= track - 8'd1; + end + + // ------------- TYPE II commands ------------- + if(cpu_din[7:5] == 3'b100) begin // read sector + state <= STATE_IO_WAIT; + end + + if(cpu_din[7:5] == 3'b101) // write sector + if(!wr_prot) + state <= STATE_IO_WAIT; + + // ------------- TYPE III commands ------------ + + if(cpu_din[7:4] == 4'b1100) // read address + state <= STATE_IO_WAIT; + + if(cpu_din[7:4] == 4'b1110) // read track + state <= STATE_IO_WAIT; + + if(cpu_din[7:4] == 4'b1111) // write track + if(!wr_prot) + state <= STATE_IO_WAIT; + + // ------------- TYPE IV commands ------------- + if(cpu_din[7:4] == 4'b1101) begin // force intrerupt + if(cpu_din[3:0] == 4'b0000) + state <= STATE_IDLE; // immediately + else + state <= STATE_IRQ; // with irq + end + end // if (cpu_addr == 0) + + if(cpu_addr == 1) // track register + track <= cpu_din; + + if(cpu_addr == 2) // sector register + sector <= cpu_din; + + if(cpu_addr == 3) // data register + data <= cpu_din; + + end // if (cpu_sel && !cpu_rw) + end // else: !if(reset) +end // always @ (negedge clk or posedge reset) + +endmodule + + diff --git a/cores/mist/io_fifo.v b/cores/mist/io_fifo.v index 7475256..b2425ee 100644 --- a/cores/mist/io_fifo.v +++ b/cores/mist/io_fifo.v @@ -19,6 +19,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// TODO: Make this truly async +// http://www.asic-world.com/examples/verilog/asyn_fifo.html + module io_fifo #( parameter DATA_WIDTH = 8, parameter DEPTH = 4 diff --git a/cores/mist/mfp_timer.v b/cores/mist/mfp_timer.v index 5aff949..435405f 100644 --- a/cores/mist/mfp_timer.v +++ b/cores/mist/mfp_timer.v @@ -90,8 +90,10 @@ always @(negedge CLK) begin // then write the data to the appropriate register. if(DAT_WE) begin data <= DAT_I; - down_counter <= DAT_I; - end + // the counter itself is only loaded here if it's stopped + if(!started) + down_counter <= DAT_I; + end if(CTRL_WE) begin control <= CTRL_I[3:0]; diff --git a/cores/mist/mist.qsf b/cores/mist/mist.qsf index 8dadc87..99b032b 100644 --- a/cores/mist/mist.qsf +++ b/cores/mist/mist.qsf @@ -327,7 +327,8 @@ set_global_assignment -name VERILOG_FILE viking.v set_global_assignment -name VERILOG_FILE video_modes.v set_global_assignment -name VERILOG_FILE io_fifo.v set_global_assignment -name VERILOG_FILE osd.v -set_global_assignment -name VERILOG_FILE data_io.v +set_global_assignment -name VERILOG_FILE fdc.v +set_global_assignment -name VERILOG_FILE acsi.v set_global_assignment -name VERILOG_FILE mfp.v set_global_assignment -name VERILOG_FILE dma.v set_global_assignment -name VERILOG_FILE sigma_delta_dac.v diff --git a/cores/mist/mist_top.v b/cores/mist/mist_top.v index 9f6586d..fae28b7 100644 --- a/cores/mist/mist_top.v +++ b/cores/mist/mist_top.v @@ -1,7 +1,7 @@ /********************************************/ /* */ /********************************************/ - + module mist_top ( // clock inputsxque input wire [ 2-1:0] CLOCK_27, // 27 MHz @@ -130,8 +130,8 @@ always @(posedge clk_32 or posedge berr_reset) begin 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 = data_io_br || blitter_br; // dma/blitter are only other bus masters +// much more than halting the cpu by suppressing +wire br = dma_br || blitter_br; // dma/blitter are only other bus masters // request interrupt ack from mfp for IPL == 6. This must not be done by // combinatorics as it must be glitch free since the mfp does things on the @@ -172,8 +172,11 @@ wire rom_sel_all = ethernec_present && cpu_cycle && tg68_as && tg68_rw && ({tg68 wire [1:0] rom_sel = { rom_sel_all && tg68_adr[16], rom_sel_all && !tg68_adr[16] }; wire [15:0] rom_data_out; -// mmu 8 bit interface at $ff8000 - $ff8001 -wire mmu_sel = io_sel && ({tg68_adr[15:1], 1'd0} == 16'h8000); +// mmu 8 bit interface at $ff8000 - $ff800d +wire mmu_sel = io_sel && + (({tg68_adr[15:3], 3'd0} == 16'h8000) || // ff8000-ff8007 + ({tg68_adr[15:2], 2'd0} == 16'h8008) || // ff8008-ff800b + ({tg68_adr[15:1], 1'd0} == 16'h800c)); // ff800c-ff800d wire [7:0] mmu_data_out; // mega ste cache controller 8 bit interface at $ff8e20 - $ff8e21 @@ -185,8 +188,14 @@ wire [7:0] mste_ctrl_data_out; // (requierd to enable Mega STE cpu speed/cache control) wire vme_sel = !steroids && mste && io_sel && ({tg68_adr[15:4], 4'd0} == 16'h8e00); -// video controller 16 bit interface at $ff8200 - $ff827f -wire vreg_sel = io_sel && ({tg68_adr[15:7], 7'd0} == 16'h8200); +// shifter 16 bit interface at $ff8200 - $ff820d and $ff8240 - $ff827f +// STE shifter also has registers at ff820f and ff8265 +wire vreg_sel = io_sel && ( + ({tg68_adr[15:3], 3'd0} == 16'h8200) || // ff8200-ff8207 + ({tg68_adr[15:2], 2'd0} == 16'h8208) || // ff8208-ff820b + ({tg68_adr[15:1], 1'd0} == 16'h820c) || // ff820c-ff820d + (({tg68_adr[15:1], 1'd0} == 16'h820e) && ste) || // ff820e-ff820f (STE) + ({tg68_adr[15:6], 6'd0} == 16'h8240)); // ff8240-ff827f wire [15:0] vreg_data_out; // ste joystick 16 bit interface at $ff9200 - $ff923f @@ -197,24 +206,29 @@ wire [15:0] ste_joy_data_out; wire ste_dma_snd_sel = ste && io_sel && ({tg68_adr[15:6], 6'd0} == 16'h8900); wire [15:0] ste_dma_snd_data_out; -// mfp 8 bit interface at $fffa00 - $fffa3f -wire mfp_sel = io_sel && ({tg68_adr[15:6], 6'd0} == 16'hfa00); +// mfp 8 bit interface at $fffa00 - $fffa3f, odd byte and word only +wire mfp_sel = io_sel && (tg68_lds == 1'd0) && ({tg68_adr[15:6], 6'd0} == 16'hfa00); wire [7:0] mfp_data_out; -// acia 8 bit interface at $fffc00 - $fffc07 -wire acia_sel = io_sel && ({tg68_adr[15:8], 8'd0} == 16'hfc00); +// acia 8 bit interface at $fffc00 - $fffdff +wire acia_sel = io_sel && ({tg68_adr[15:9], 9'd0} == 16'hfc00); // fffc00-fffdff wire [7:0] acia_data_out; // blitter 16 bit interface at $ff8a00 - $ff8a3f, STE always has a blitter -wire blitter_sel = (system_ctrl[19] || ste) && io_sel && ({tg68_adr[15:8], 8'd0} == 16'h8a00); +wire blitter_sel = (system_ctrl[19] || ste) && io_sel && ({tg68_adr[15:6], 6'd0} == 16'h8a00); wire [15:0] blitter_data_out; -// psg 8 bit interface at $ff8800 - $ff8803 +// psg 8 bit interface at $ff8800 - $ff88ff wire psg_sel = io_sel && ({tg68_adr[15:8], 8'd0} == 16'h8800); wire [7:0] psg_data_out; -// dma 16 bit interface at $ff8600 - $ff860f -wire dma_sel = io_sel && ({tg68_adr[15:4], 4'd0} == 16'h8600); +// dma 16 bit interface at $ff8604 - $ff8607 (word only) and $ff8606 - $ff860d (STE - ff860f) +wire word_access = (tg68_uds == 1'd0) && (tg68_lds == 1'd0); +wire dma_sel = io_sel && ( + (({tg68_adr[15:2], 2'd0} == 16'h8604) && word_access) || // ff8604-ff8607 word only + ({tg68_adr[15:2], 2'd0} == 16'h8608) || // ff8608-ff860b + ({tg68_adr[15:1], 1'd0} == 16'h860c) || // ff860c-ff860d + (({tg68_adr[15:1], 1'd0} == 16'h860e) && ste)); // ff860e-ff860f (hdmode, STE) wire [15:0] dma_data_out; // de-multiplex the various io data output ports into one @@ -288,7 +302,6 @@ wire [5:0] shifter_r, shifter_g, shifter_b; video video ( .clk (clk_32 ), - .clk27 (CLOCK_27[0]), .bus_cycle (bus_cycle ), // spi for OSD @@ -464,7 +477,7 @@ blitter blitter ( .bm_read (blitter_master_read), .bm_data_in (ram_data_out), - .br_in (data_io_br ), + .br_in (dma_br ), .br_out (blitter_br ), .bg (blitter_bg ), .irq (blitter_irq ), @@ -636,39 +649,53 @@ YM2149 ym2149 ( .CLK8 ( clk_8 ) // 8 MHz CPU bus clock ); -wire dma_dio_ack; -wire [4:0] dma_dio_idx; -wire [7:0] dma_dio_data; - // floppy_sel is active low wire wr_prot = (floppy_sel == 2'b01)?system_ctrl[7]:system_ctrl[6]; +wire [22:0] dma_addr; +wire [15:0] dma_dout; +wire dma_write, dma_read; +wire dma_br; dma dma ( - // cpu interface - .clk (clk_8 ), - .reset (reset ), - .din (tg68_dat_out[15:0]), - .sel (dma_sel ), - .addr (tg68_adr[3:1]), - .uds (tg68_uds ), - .lds (tg68_lds ), - .rw (tg68_rw ), - .dout (dma_data_out), + // system interface + .clk (clk_8 ), + .reset (reset ), + .bus_cycle (bus_cycle ), + .irq (dma_irq ), + .turbo (1'b0 ), - .irq (dma_irq ), + .ctrl_out (system_ctrl ), + .video_adj (video_adj ), + + // spi + .sdi (SPI_DI ), + .sck (SPI_SCK ), + .ss (SPI_SS2 ), + .sdo (dma_sdo ), - // system control interface + // cpu interface + .cpu_din (tg68_dat_out[15:0]), + .cpu_sel (dma_sel ), + .cpu_addr (tg68_adr[3:1]), + .cpu_uds (tg68_uds ), + .cpu_lds (tg68_lds ), + .cpu_rw (tg68_rw ), + .cpu_dout (dma_data_out), + + // additional signals for floppy/acsi interface .fdc_wr_prot (wr_prot), + .drv_sel (floppy_sel ), + .drv_side (floppy_side ), .acsi_enable (system_ctrl[17:10]), - - // data_io (arm controller imterface) - .dio_idx (dma_dio_idx ), - .dio_data (dma_dio_data), - .dio_ack (dma_dio_ack ), - // floppy interface - .drv_sel (floppy_sel ), - .drv_side (floppy_side ) + + // ram interface + .ram_br (dma_br ), + .ram_read (dma_read ), + .ram_write (dma_write ), + .ram_addr (dma_addr ), + .ram_dout (dma_dout ), + .ram_din (ram_data_out ) ); wire [1:0] floppy_sel; @@ -681,7 +708,7 @@ wire clk_8; wire clk_32; wire clk_128; wire clk_mfp; - + // use pll clock clock ( .areset (1'b0 ), // async reset input @@ -694,23 +721,9 @@ clock clock ( ); //// 8MHz clock //// -reg [3:0] clk_cnt; +reg [1:0] clk_cnt; reg [1:0] bus_cycle; -always @ (posedge clk_32, negedge pll_locked) begin - if (!pll_locked) begin - clk_cnt <= #1 4'b0010; - bus_cycle <= 2'd0; - end else begin - clk_cnt <= #1 clk_cnt + 4'd1; - if(clk_cnt[1:0] == 2'd1) begin - bus_cycle <= bus_cycle + 2'd1; - end - end -end - -assign clk_8 = clk_cnt[1]; - // MFP clock // required: 2.4576 MHz // derived from 27MHZ: 27*74/824 = 2.457525 MHz => 0.003% error @@ -722,6 +735,19 @@ pll_mfp1 pll_mfp1 ( .c0 (clk_mfp ) // output clock c0 (2.457627MHz) ); +always @ (posedge clk_32, negedge pll_locked) begin + if (!pll_locked) begin + clk_cnt <= 2'd2; + bus_cycle <= 2'd0; + end else begin + clk_cnt <= clk_cnt + 2'd1; + if(clk_cnt == 2'd1) + bus_cycle <= bus_cycle + 2'd1; + end +end + +assign clk_8 = clk_cnt[1]; + // bus cycle counter for debugging reg [31:0] cycle_counter /* synthesis noprune */; always @ (posedge clk_8) begin @@ -838,14 +864,19 @@ always @(posedge clk_32) // the TG68 core works on the rising clock edge. We thus prepare everything // on the falling clock edge +reg brD; always @(negedge clk_32) begin + brD <= br; // delay bus request by one cycle. This only has an effect in + // STEroids mode as otherwise br changes in those cycles not used by + // CPU + // default: cpu does not run clkena <= 1'b0; iCacheStore <= 1'b0; dCacheStore <= 1'b0; cacheUpdate <= 1'b0; - if(br || reset) + if(br || brD || reset) tg68_as <= 1'b0; else begin // run cpu if it owns the bus @@ -1059,7 +1090,7 @@ wire cpu2io = (tg68_adr[23:16] == 8'hff); // irq ack happens wire cpu2iack = (tg68_fc == 3'b111); - + // generate dtack (for st ram and rom on read, no dtack for rom write) assign tg68_dtack = ((cpu2mem && cpu_cycle && tg68_as) || io_dtack ) && !br; @@ -1067,6 +1098,9 @@ assign tg68_dtack = ((cpu2mem && cpu_cycle && tg68_as) || io_dtack ) && !br; /* ------------------------------- bus multiplexer ------------------------------ */ /* ------------------------------------------------------------------------------ */ +wire dma_has_bus = dma_br; +wire blitter_has_bus = blitter_br; + // singnal indicating if cpu should use a second cpu slot for 16Mhz like operation // steroids (STEroid) always runs at max speed wire second_cpu_slot = (mste && enable_16mhz) || steroids; @@ -1079,27 +1113,27 @@ wire viking_cycle = (bus_cycle == 2); // ----------------- RAM address -------------- wire [22:0] video_cycle_addr = (st_hs && ste)?ste_dma_snd_addr:video_address; -wire [22:0] cpu_cycle_addr = data_io_br?data_io_addr:(blitter_br?blitter_master_addr:tg68_adr[23:1]); +wire [22:0] cpu_cycle_addr = dma_has_bus?dma_addr:(blitter_has_bus?blitter_master_addr:tg68_adr[23:1]); wire [22:0] ram_address = viking_cycle?viking_address:(video_cycle?video_cycle_addr:cpu_cycle_addr); // ----------------- RAM read ----------------- // memory access during the video cycle is shared between video and ste_dma_snd wire video_cycle_oe = (st_hs && ste)?ste_dma_snd_read:video_read; // memory access during the cpu cycle is shared between blitter and cpu -wire cpu_cycle_oe = data_io_br?data_io_read:(blitter_br?blitter_master_read:(cpu_cycle && tg68_as && tg68_rw && cpu2mem)); +wire cpu_cycle_oe = dma_has_bus?dma_read:(blitter_has_bus?blitter_master_read:(cpu_cycle && tg68_as && tg68_rw && cpu2mem)); wire ram_oe = viking_cycle?viking_read:(video_cycle?video_cycle_oe:(cpu_cycle?cpu_cycle_oe:1'b0)); // ----------------- RAM write ----------------- -wire cpu_cycle_wr = data_io_br?data_io_write:(blitter_br?blitter_master_write:(cpu_cycle && tg68_as && ~tg68_rw && cpu2ram)); +wire cpu_cycle_wr = dma_has_bus?dma_write:(blitter_has_bus?blitter_master_write:(cpu_cycle && tg68_as && ~tg68_rw && cpu2ram)); wire ram_wr = (viking_cycle||video_cycle)?1'b0:(cpu_cycle?cpu_cycle_wr:1'b0); wire [15:0] ram_data_out; wire [15:0] system_data_out = cpu2mem?ram_data_out:io_data_out; -wire [15:0] ram_data_in = data_io_br?data_io_dout:(blitter_br?blitter_master_data_out:tg68_dat_out); +wire [15:0] ram_data_in = dma_has_bus?dma_dout:(blitter_has_bus?blitter_master_data_out:tg68_dat_out); // data strobe -wire ram_uds = video_cycle?1'b1:((blitter_br||data_io_br)?1'b1:~tg68_uds); -wire ram_lds = video_cycle?1'b1:((blitter_br||data_io_br)?1'b1:~tg68_lds); +wire ram_uds = video_cycle?1'b1:((br && brD)?1'b1:~tg68_uds); +wire ram_lds = video_cycle?1'b1:((br && brD)?1'b1:~tg68_lds); // sdram controller has 64 bit output wire [63:0] ram_data_out_64; @@ -1143,11 +1177,11 @@ sdram sdram ( // multiplex spi_do, drive it from user_io if that's selected, drive // it from minimig if it's selected and leave it open else (also // to be able to monitor sd card data directly) -wire data_io_sdo; +wire dma_sdo; wire user_io_sdo; assign SPI_DO = (CONF_DATA0 == 1'b0)?user_io_sdo: - ((SPI_SS2 == 1'b0)?data_io_sdo:1'bZ); + ((SPI_SS2 == 1'b0)?dma_sdo:1'bZ); wire [31:0] system_ctrl; @@ -1241,40 +1275,6 @@ user_io user_io( .CORE_TYPE (8'ha3) // mist core id ); -wire [22:0] data_io_addr; -wire [15:0] data_io_dout; -wire data_io_write, data_io_read; -wire data_io_br; - -data_io data_io ( - // system control - .clk_8 (clk_8 ), - .reset (init ), - .bus_cycle (bus_cycle ), - .ctrl_out (system_ctrl ), - - // spi - .sdi (SPI_DI ), - .sck (SPI_SCK ), - .ss (SPI_SS2 ), - .sdo (data_io_sdo ), - - .video_adj (video_adj ), - - // dma status interface - .dma_idx (dma_dio_idx ), - .dma_data (dma_dio_data ), - .dma_ack (dma_dio_ack ), - .br (data_io_br ), - - // ram interface - .read (data_io_read ), - .write (data_io_write ), - .addr (data_io_addr ), - .data_out (data_io_dout ), - .data_in (ram_data_out ) -); - endmodule diff --git a/cores/mist/ste_joystick.v b/cores/mist/ste_joystick.v index 0ccb343..c62ba4f 100644 --- a/cores/mist/ste_joystick.v +++ b/cores/mist/ste_joystick.v @@ -5,7 +5,6 @@ // http://code.google.com/p/mist-board/ // // Copyright (c) 2013 Till Harbaum -// Modified by Juan Carlos González Amestoy. // // 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 diff --git a/cores/mist/user_io.v b/cores/mist/user_io.v index 3ca5f63..b681c9c 100644 --- a/cores/mist/user_io.v +++ b/cores/mist/user_io.v @@ -61,19 +61,19 @@ module user_io( wire [7:0] spi_sck_D = { spi_sck_D[6:0], SPI_CLK } /* synthesis keep */; wire spi_sck = (spi_sck && spi_sck_D != 8'h00) || (!spi_sck && spi_sck_D == 8'hff); - reg [1:0] byte_cnt; - reg [6:0] sbuf; - reg [7:0] cmd; - reg [3:0] bit_cnt; // 0..15 - reg [3:0] but_sw; +reg [1:0] byte_cnt; +reg [6:0] sbuf; +reg [7:0] cmd; +reg [3:0] bit_cnt; // 0..15 +reg [3:0] but_sw; - // counter runs 0..7,8..15,8..15,8..15 - wire [2:0] tx_bit = ~(bit_cnt[2:0]); +// counter runs 0..7,8..15,8..15,8..15 +wire [2:0] tx_bit = ~(bit_cnt[2:0]); - assign BUTTONS = but_sw[1:0]; - assign SWITCHES = but_sw[3:2]; +assign BUTTONS = but_sw[1:0]; +assign SWITCHES = but_sw[3:2]; - always@(negedge spi_sck) begin +always@(negedge spi_sck) begin if(bit_cnt <= 7) SPI_MISO <= CORE_TYPE[7-bit_cnt]; else begin diff --git a/cores/mist/video.v b/cores/mist/video.v index c013a8c..a510fc6 100644 --- a/cores/mist/video.v +++ b/cores/mist/video.v @@ -23,7 +23,6 @@ module video ( // system interface input clk, // 31.875 MHz - input clk27, // 27.000 Mhz input [1:0] bus_cycle, // bus-cycle for sync // SPI interface for OSD