`timescale 1ns / 100ps /* * NCR5380 SCSI module for minimigmac. * * Located on high data bus, but writes are done at odd addresses as * LDS is used as WR signals or something like that on a Mac Plus. * * We don't care here and just ignore which side was used. We also don't * implement the interrupt logic (well at least we don't route it to the * CPU like a real Mac) and we do all sort of simplifications such as only * supporting initiator mode * * This is implementation is really just a gateway to the backbus, the * microcontroller can observe SCSI line states and exchange data. * * Notes: * * - Arbitration - * * We do not support external arbitration & reselection, thus we always * win it. Thus our arbitration logic is as simple as reflecting the * arbitration bit into ICR:AIP and never setting ICR:LA, no timing * to care much about. * * Note: We also don't wait for a bus free phase, so we assume SW will * note try to arbitrate while BSY is still asserted by the target, if * that was to happen, I could use a latch to delay arbitration to * finding that BSY is false and checking SEL. * * - Bus reset - * * We don't do anything special, just report the state of the line. We * might end up needing to latch it to give a chance for the uC to pick * it up if it's asserted for tfoo short (for ex. the uC is in the middle * of a data transfer). * * - Busy monitoring - * * Not implemented... might need to add, ROM doesn't ues it but MacOS * might * * - Interrupts - * * Nothing implemented, BSR:IRQ never set, ROM doesn't use it, but * MacOS might ... Select Enable Register is not implemented * * - Data transfers * * In dumb mode, it's all manual assertion of the various lines on * both side. However, there's a problem with the Mac ROM when doing * that. * * When doing a read, the ROM does not wait for REQ to go down after the * last byte has been read. If the PIC is slow enough to react, that menans * that the subsequent call to SCSIComplete, will incorrectly match that REQ * for the one of the status phase, and get a phase mismatch. This can happen * with my SPI protocol which is somewhat sub optimal for alternating reads * and writes * * So the PIC has to be fast enough at lowering REQ when ACK gets set ... * or always use automatic mode: * * Automatic mode: when in that mode, REQ/ACK is handled in HW and the * separate _scsi_dreq line is used to pace the PIC as follow: * * - On reads (target -> initiator) * * * Signal din_full set set when PIC writes to IDATA, * causes REQ to be asserted * * ACK (either manual or auto-generated by DMA mode) * clears din_full (and thus de-asserts REQ) * * _scsi_dreq is asserted based on a flip flop that * gets set when data is read by host (DACK) and set * when REQ is asserted (IDATA is written). This only * happens while automatic mode is enabled. * * - On writes (initiator -> target) * * * /* Read registers */ `define RREG_CDR 3'h0 /* Current SCSI data */ `define RREG_ICR 3'h1 /* Initiator Command */ `define RREG_MR 3'h2 /* Mode register */ `define RREG_TCR 3'h3 /* Target Command */ `define RREG_CSR 3'h4 /* SCSI bus status */ `define RREG_BSR 3'h5 /* Bus and status */ `define RREG_IDR 3'h6 /* Input data */ `define RREG_RST 3'h7 /* Reset */ /* Write registers */ `define WREG_ODR 3'h0 /* Ouptut data */ `define WREG_ICR 3'h1 /* Initiator Command */ `define WREG_MR 3'h2 /* Mode register */ `define WREG_TCR 3'h3 /* Target Command */ `define WREG_SER 3'h4 /* Select Enable */ `define WREG_DMAS 3'h5 /* Start DMA Send */ `define WREG_DMATR 3'h6 /* Start DMA Target receive */ `define WREG_IDMAR 3'h7 /* Start DMA Initiator receive */ /* Backbus interface */ `define BB_REG_LINES 0 /* Read only, line states set by initiator */ `define BB_LINES_BSY 7 `define BB_LINES_SEL 6 `define BB_LINES_ACK 5 `define BB_LINES_ATN 4 `define BB_LINES_RST 3 `define BB_REG_ASSERT 1 /* RW, assert lines by target */ `define BB_ASSERT_BSY 7 `define BB_ASSERT_SEL 6 `define BB_ASSERT_CD 5 `define BB_ASSERT_IO 4 `define BB_ASSERT_MSG 3 `define BB_ASSERT_REQ 2 `define BB_ASSERT_RST 1 `define BB_ASSERT_AUTO 0 /* Automatic mode */ `define BB_REG_ODATA 2 `define BB_REG_IDATA 3 `define BB_REG_ACNT_HI 4 `define BB_REG_ACNT_LO 5 /* MR bit numbers */ `define MR_DMA_MODE 1 `define MR_ARB 0 /* ICR bit numbers */ `define ICR_A_RST 7 `define ICR_TEST_MODE 6 `define ICR_DIFF_ENBL 5 `define ICR_A_ACK 4 `define ICR_A_BSY 3 `define ICR_A_SEL 2 `define ICR_A_ATN 1 `define ICR_A_DATA 0 /* TCR bit numbers */ `define TCR_A_REQ 3 `define TCR_A_MSG 2 `define TCR_A_CD 1 `define TCR_A_IO 0 module ncr5380(input sysclk, input reset, /* Bus interface. 3-bit address, to be wired * appropriately upstream (to A4..A6) plus one * more bit (A9) wired as dack. */ input bus_cs, input bus_we, output bus_ack, input bus_phase, input [2:0] bus_rs, input dack, input [7:0] wdata, output [7:0] rdata, /* Backbus interface */ input [5:0] bb_addr, input [7:0] bb_wdata, output [7:0] bb_rdata, input bb_strobe, input bb_wr, /* External request line */ output scsi_hshake ); /* Bus interface signals */ wire reg_rd; wire reg_wr; wire dma_rd; wire dma_wr; wire bus_hold; /* Mode Register */ reg [7:0] mr; /* Initiator Command Register */ wire icr_aip; wire icr_la; wire [7:0] icr_read; reg [7:0] icr; /* Target Command Register */ reg [3:0] tcr; /* Backbus asserts */ reg [7:0] bb_assert; /* These are the simulated SCSI bus lines, we use positive * polarity to simplify things */ wire scsi_bsy; wire scsi_sel; wire scsi_ack; wire scsi_atn; wire scsi_cd; wire scsi_io; wire scsi_msg; wire scsi_req; wire scsi_rst; /* What backbus sees of the above */ wire [7:0] bb_lines; wire bb_reg_rd; wire bb_reg_wr; wire bb_reg_lines; wire bb_reg_assert; wire bb_reg_odata; wire bb_reg_idata; wire bb_reg_acnt_hi; wire bb_reg_acnt_lo; /* SCSI bus status register */ wire [7:0] csr; /* Bus and Status register */ wire bsr_eodma; wire bsr_dmarq; wire bsr_perr; wire bsr_irq; wire bsr_pmatch; wire bsr_berr; wire [7:0] bsr; /* Current data register and logic */ wire [7:0] cur_data; wire out_en; /* Data in and out latches and associated * control logic for DMA */ reg [7:0] din; reg [7:0] dout; reg dphase; reg dma_en; /* BB automatic mode */ reg autoreq; reg [15:0] autocnt; wire autoreg; reg autodrop; reg [7:0] dout_latch; /* --- Main host-side interface --- */ /* Register & DMA accesses decodes */ assign dma_rd = bus_cs & bus_phase & dack & ~bus_we; assign dma_wr = bus_cs & bus_phase & dack & bus_we; assign reg_rd = bus_cs & bus_phase & ~dack & ~bus_we; assign reg_wr = bus_cs & bus_phase & ~dack & bus_we; /* Hold bus to phase 0 on DMA accesses if necessary, this * makes "blind" transfers 100% reliable since I really don't * know if my SPI interface is fast enough */ assign bus_hold = dack & (scsi_ack | ~scsi_req) & dma_en; assign bus_ack = bus_cs & ~bus_hold; /* System bus reads * * Q. to HW dudes... I don't need to test reg_rd everywhere, I can * just let the mux toggle all the time, but would that consume * more power or it's so ridiculous we don't care ? */ assign rdata = dma_rd ? cur_data : reg_rd && bus_rs == `RREG_CDR ? cur_data : reg_rd && bus_rs == `RREG_ICR ? icr_read : reg_rd && bus_rs == `RREG_MR ? mr : reg_rd && bus_rs == `RREG_TCR ? { 4'h0 | tcr } : reg_rd && bus_rs == `RREG_CSR ? csr : reg_rd && bus_rs == `RREG_BSR ? bsr : reg_rd && bus_rs == `RREG_IDR ? cur_data : reg_rd && bus_rs == `RREG_RST ? 8'hff : 8'hff; /* DMA handhsaking logic. Two phase logic, in phase 0 * DRQ follows SCSI _REQ until we see DACK. In phase 1 * we just wait for SCSI _REQ to go down and go back to * phase 0. We assert SCSI _ACK in phase 1. */ always@(posedge sysclk or posedge reset) begin if (reset) begin dphase <= 0; end else begin if (!dma_en) begin dphase <= 0; end else if (dphase == 0) begin /* Be careful to do that in bus phase 1, * not phase 0, or we would incorrectly * assert bus_hold and lock up the system */ if ((dma_rd || dma_wr) && scsi_req) begin dphase <= 1; end end else if (!scsi_req) begin dphase <= 0; end end end /* Data out latch (in DMA mode, this is one cycle after we've * asserted ACK) */ always@(posedge sysclk or posedge reset) begin if (reset) dout <= 8'haa; else if ((reg_wr && bus_rs == `WREG_ODR) || dma_wr) dout <= wdata; end /* Current data register. Simplified logic: We loop back the * output data if we are asserting the bus, else we get the * input latch */ assign cur_data = out_en ? dout : din; /* Logic for "asserting the bus" simplified */ assign out_en = icr[`ICR_A_DATA] | mr[`MR_ARB]; /* ICR read wires */ assign icr_read = { icr[`ICR_A_RST], icr_aip, icr_la, icr[`ICR_A_ACK], icr[`ICR_A_BSY], icr[`ICR_A_SEL], icr[`ICR_A_ATN], icr[`ICR_A_DATA] }; /* ICR write */ always@(posedge sysclk or posedge reset) begin if (reset) begin icr <= 0; end else if (reg_wr && (bus_rs == `WREG_ICR)) begin icr <= wdata; end end /* MR write */ always@(posedge sysclk or posedge reset) begin if (reset) mr <= 8'b0; else if (reg_wr && (bus_rs == `WREG_MR)) mr <= wdata; end /* TCR write */ always@(posedge sysclk or posedge reset) begin if (reset) tcr <= 4'b0; else if (reg_wr && (bus_rs == `WREG_TCR)) tcr <= wdata[3:0]; end /* DMA start send & receive registers. We currently ignore * the direction. */ always@(posedge sysclk or posedge reset) begin if (reset) begin dma_en <= 0; end else begin if (!mr[`MR_DMA_MODE]) begin dma_en <= 0; end else if (reg_wr && (bus_rs == `WREG_DMAS)) begin dma_en <= 1; end else if (reg_wr && (bus_rs == `WREG_IDMAR)) begin dma_en <= 1; end end end /* CSR (read only). We don't do parity */ assign csr = { scsi_rst, scsi_bsy, scsi_req, scsi_msg, scsi_cd, scsi_io, scsi_sel, 1'b0 }; /* BSR (read only). We don't do a few things... */ assign bsr_eodma = 0; /* We don't do EOP */ assign bsr_dmarq = scsi_req & ~dphase & dma_en; assign bsr_perr = 0; /* We don't do parity */ assign bsr_irq = 0; /* XXX ? Does MacOS use this ? */ assign bsr_pmatch = tcr[`TCR_A_MSG] == scsi_msg && tcr[`TCR_A_CD ] == scsi_cd && tcr[`TCR_A_IO ] == scsi_io; assign bsr_berr = 0; /* XXX ? Does MacOS use this ? */ assign bsr = { bsr_eodma, bsr_dmarq, bsr_perr, bsr_irq, bsr_pmatch, bsr_berr, scsi_atn, scsi_ack }; /* --- Simulated SCSI Signals --- */ /* BSY logic (simplified arbitration, see notes) */ assign scsi_bsy = icr[`ICR_A_BSY] | bb_assert[`BB_ASSERT_BSY] | mr[`MR_ARB]; /* Remains of simplified arbitration logic */ assign icr_aip = mr[`MR_ARB]; assign icr_la = 0; /* Other ORed SCSI signals */ assign scsi_sel = icr[`ICR_A_SEL] | bb_assert[`BB_ASSERT_SEL]; assign scsi_rst = icr[`ICR_A_RST] | bb_assert[`BB_ASSERT_RST]; assign scsi_ack = icr[`ICR_A_ACK] | dphase; assign scsi_atn = icr[`ICR_A_ATN]; /* Other trivial lines set by target */ assign scsi_cd = bb_assert[`BB_ASSERT_CD]; assign scsi_io = bb_assert[`BB_ASSERT_IO]; assign scsi_msg = bb_assert[`BB_ASSERT_MSG]; assign scsi_req = bb_assert[`BB_ASSERT_REQ] | autoreq; /* --- Backbus interface --- */ assign bb_reg_rd = bb_strobe & ~bb_wr; assign bb_reg_wr = bb_strobe & bb_wr; assign bb_reg_lines = bb_addr[3:0] == `BB_REG_LINES; assign bb_reg_assert = bb_addr[3:0] == `BB_REG_ASSERT; assign bb_reg_odata = bb_addr[3:0] == `BB_REG_ODATA; assign bb_reg_idata = bb_addr[3:0] == `BB_REG_IDATA; assign bb_reg_acnt_hi = bb_addr[3:0] == `BB_REG_ACNT_HI; assign bb_reg_acnt_lo = bb_addr[3:0] == `BB_REG_ACNT_LO; /* Backbus lines, ie, pseudo SCSI bus state seen by the * microcontroller. Note that they are positive logic unlike * the real SCSI lines. We only care about the lines that are * not asserted exclusively by the target */ assign bb_lines = { scsi_bsy, scsi_sel, scsi_ack, scsi_atn, scsi_rst, 3'b0 }; /* Backbus register reads */ assign bb_rdata = bb_reg_lines ? bb_lines : bb_reg_assert ? bb_assert : bb_reg_odata ? (bb_assert[`BB_ASSERT_AUTO] ? dout_latch : dout) : bb_reg_idata ? din : 0; /* External handshake logic. We assert it for now when SEL is * asserted in ICR. Or in auto mode, when both req and ack are * low */ assign scsi_hshake = icr[`ICR_A_SEL] | icr[`ICR_A_RST] | (bb_assert[`BB_ASSERT_AUTO] & ~(scsi_ack | scsi_req)); /* Data in latch */ always@(posedge sysclk or posedge reset) begin if (reset) din <= 8'h55; else if (bb_reg_wr && bb_reg_idata) din <= bb_wdata; end /* BB assert register write */ always@(posedge sysclk or posedge reset) begin if (reset) bb_assert <= 0; else if (bb_reg_wr && bb_reg_assert) bb_assert <= bb_wdata; end /* --- Backbus "automatic" mode logic crackpot --- */ /* autocnt register write & decrement */ always@(posedge sysclk or posedge reset) begin if (reset) autocnt <= 0; else if (bb_reg_wr && bb_reg_acnt_hi) autocnt[15:8] <= bb_wdata; else if (bb_reg_wr && bb_reg_acnt_lo) autocnt[7:0] <= bb_wdata; else if (autoreg) autocnt <= autocnt - 1; end /* Hack to workaround SPI interface giving us two strobes * on the first byte of reads. We use this latch to drop * the first one. */ always@(posedge sysclk or posedge reset) begin if (reset) autodrop <= 0; else begin if (bb_reg_wr && bb_reg_acnt_lo) autodrop <= 1; else if (bb_strobe) autodrop <= 0; end end /* An automatic register access happens when auto mode is * enabled, autocnt is non-0 and we access the idata or * odata register */ assign autoreg = bb_assert[`BB_ASSERT_AUTO] & bb_strobe & (bb_reg_idata | (bb_reg_odata & ~autodrop)) & (autocnt != 0); /* Automatic mode, toggle REQ automagically during * data phases if there are still bytes to go */ always@(posedge sysclk or posedge reset) begin if (reset) autoreq <= 0; else begin if (!bb_assert[`BB_ASSERT_AUTO]) autoreq <= 0; else if (autoreg) autoreq <= 1; else if (scsi_ack) autoreq <= 0; end end /* In auto mode writes, we are slightly off as REQ is * asserted by a read done by the PIC so we need to * latch the data when ack is asserted so it's still * valid by the time the PIC reads it. To simplify * things we always latch on ACK */ always@(posedge sysclk or posedge reset) begin if (reset) dout_latch <= 0; else begin if (scsi_ack) dout_latch <= dout; end end endmodule