module RAM2E(C14M, PHI1, nWE, nWE80, nEN80, nC07X, Ain, Din, Dout, nDOE, Vout, nVOE, CKE, nCS, nRAS, nCAS, nRWE, BA, RA, RD, DQML, DQMH); /* Clocks */ input C14M, PHI1; /* Control inputs */ input nWE, nWE80, nEN80, nC07X; /* Delay for EN80 signal */ //output DelayOut = 1'b0; //input DelayIn; wire EN80 = ~nEN80; /* Address Bus */ input [7:0] Ain; // Multiplexed DRAM address input /* 6502 Data Bus */ input [7:0] Din; // 6502 data bus inputs output nDOE = ~(EN80 & nWE); // 6502 data bus output enable output reg [7:0] Dout; // 6502 data Bus output /* Video Data Bus */ output nVOE = ~(~PHI1); /// Video data bus output enable output reg [7:0] Vout; // Video data bus /* SDRAM */ output reg CKE = 0; output reg nCS = 1, nRAS = 1, nCAS = 1, nRWE = 1; output reg [1:0] BA; output reg [11:0] RA; output reg DQML = 1, DQMH = 1; wire RDOE = EN80 & ~nWE80; inout [7:0] RD = RDOE ? Din[7:0] : 8'bZ; /* RAMWorks Bank Register and Capacity Mask */ reg [7:0] RWBank = 0; // RAMWorks bank register reg [7:0] RWMask = 0; // RAMWorks bank reg. capacity mask reg RWSel = 0; // RAMWorks bank register select reg RWMaskSet = 0; // RAMWorks Mask register set flag reg SetRWBankFF = 0; // Causes RWBank to be zeroed next RWSel access /* Command Sequence Detector */ reg [2:0] CS = 0; // Command sequence state reg [2:0] CmdTout = 0; // Command sequence timeout /* UFM Interface */ reg [15:8] UFMD = 0; // *Parallel* UFM data register reg ARCLK = 0; // UFM address register clock // UFM address register data input tied to 0 reg ARShift = 0; // 1 to Shift UFM address in, 0 to increment reg DRCLK = 0; // UFM data register clock reg DRDIn = 0; // UFM data register input reg DRShift = 0; // 1 to shift UFM out, 0 to load from current address reg UFMErase = 0; // Rising edge starts erase. UFM+RTP must not be busy reg UFMProgram = 0; // Rising edge starts program. UFM+RTP must not be busy wire UFMBusy; // 1 if UFM is doing user operation. Asynchronous wire RTPBusy; // 1 if real-time programming in progress. Asynchronous wire DRDOut; // UFM data output // UFM oscillator always enabled wire UFMOsc; // UFM oscillator output (3.3-5.5 MHz) UFM UFM_inst ( // UFM IP block (for Altera MAX II and MAX V) .arclk (ARCLK), .ardin (1'b0), .arshft (ARShift), .drclk (DRCLK), .drdin (DRDIn), .drshft (DRShift), .erase (UFMErase), .oscena (1'b1), .program (UFMProgram), .busy (UFMBusy), .drdout (DRDOut), .osc (UFMOsc), .rtpbusy (RTPBusy)); reg UFMBusyReg = 0; // UFMBusy registered to sync with C14M reg RTPBusyReg = 0; // RTPBusy registered to sync with C14M /* UFM State & User Command Triggers */ reg UFMInitDone = 0; // 1 if UFM initialization finished reg UFMReqErase = 0; // 1 if UFM requires erase reg UFMBitbang = 0; // Set by user command. Loads UFM outputs next RWSel reg UFMPrgmEN = 0; // Set by user command. Programs UFM reg UFMEraseEN = 0; // Set by user command. Erases UFM reg DRCLKPulse = 0; // Set by user command. Causes DRCLK pulse next C14M /* State Counters */ reg PHI1reg = 0; // Saved PHI1 at last rising clock edge reg Ready = 0; // 1 if done with init sequence (S0) and enter S1-S15 reg [15:0] FS = 0; // Fast state counter reg [3:0] S = 0; // IIe State counter /* State Counters */ always @(posedge C14M) begin // Increment fast state counter FS <= FS+1; // Synchronize Apple state counter to S1 when just entering PHI1 PHI1reg <= PHI1; // Save old PHI1 S <= (PHI1 & ~PHI1reg & Ready) ? 4'h1 : S==4'h0 ? 4'h0 : S==4'hF ? 4'hF : S+1; end /* UFM Control */ always @(posedge C14M) begin // Synchronize asynchronous UFM signals UFMBusyReg <= UFMBusy; RTPBusyReg <= RTPBusy; if (S==4'h0) begin if ((FS[15:13]==3'b101) | (FS[15:13]==3'b111 & UFMReqErase)) begin // In states AXXX-BXXX and also EXXX-FXXX if erase/wrap req'd // shift in 0's to address register ARCLK <= FS[0]; // Clock address register DRCLK <= 1'b0; // Don't clock data register ARShift <= 1'b1; // Shift address registers DRDIn <= 1'b0; // Don't care DRDIn DRShift <= 1'b0; // Don't care DRDShift end else if (~UFMInitDone & FS[15:13]==3'b110 & FS[4:1]==4'h4) begin // In states CXXX-DXXX (substep 4) // Xfer to data reg (repeat 256x 1x) ARCLK <= 1'b0; // Don't clock address register DRCLK <= FS[0]; // Clock data register ARShift <= 1'b0; // Don't care ARShift DRDIn <= 1'b0; // Don't care DRDIn DRShift <= 1'b0; // Don't care DRShift end else if (~UFMInitDone & FS[15:13]==3'b110 & FS[4]==1'b1) begin // In states CXXX-DXXX (substeps 8-F) // Save UFM D15-8, shift out D14-7 (repeat 256x 8x) DRCLK <= FS[0]; // Clock data register ARShift <= 1'b0; // ARShift is 0 because we want to increment DRDIn <= 1'b0; // Don't care what to shift into data register DRShift <= 1'b1; // Shift data register // Shift into UFMD if (FS[0]) UFMD[15:8] <= {UFMD[14:8], DRDOut}; // Compare and store mask if (FS[4:1]==4'hF) begin ARCLK <= FS[0]; // Clock address register to increment // If byte is erased (0xFF, i.e. all 1's, is erased)... if (UFMD[14:8]==7'b1111111 & DRDOut==1'b1) begin // Current UFM address is where we want to store UFMInitDone <= 1'b1; // Quit iterating // Otherwise byte is valid setting (i.e. some bit is 0)... end else begin // Set RWMask, but if saved mask is 0x80, store ~0xFF if (UFMD[14:8]==7'b1000000 & DRDOut==1'b0) begin RWMask[7:0] <= {1'b1, ~7'h7F}; end else RWMask[7:0] <= {UFMD[14], ~UFMD[13:8], ~DRDOut}; // If last byte in sector... if (FS[12:5]==8'hFF) begin UFMReqErase <= 1'b1; // Mark need to erase end end end else ARCLK <= 1'b0; // Don't clock address register end else begin ARCLK <= 1'b0; DRCLK <= 1'b0; ARShift <= 1'b0; DRDIn <= 1'b0; DRShift <= 1'b0; end // Don't erase or program UFM during initialization UFMErase <= 1'b0; UFMProgram <= 1'b0; // Keep DRCLK pulse control disabled during init DRCLKPulse <= 1'b0; end else begin // Can only shift UFM data register now ARCLK <= 1'b0; ARShift <= 1'b0; DRShift <= 1'b1; // UFM bitbang control if (UFMBitbang & CS==3'h7 & RWSel & S==4'hC) begin DRDIn <= Din[6]; DRCLKPulse <= Din[7]; DRCLK <= 1'b0; end else begin DRCLKPulse <= 1'b0; DRCLK <= DRCLKPulse; end // Set capacity mask if (RWMaskSet & RWSel & S==4'hC) RWMask[7:0] <= {Din[7], ~Din[6:0]}; // UFM programming sequence if (UFMPrgmEN | UFMEraseEN) begin if (~UFMBusyReg & ~RTPBusyReg) begin if (UFMReqErase | UFMEraseEN) UFMErase <= 1'b1; else if (UFMPrgmEN) UFMProgram <= 1'b1; end else if (UFMBusyReg) UFMReqErase <= 1'b0; end end end /* SDRAM Control */ always @(posedge C14M) begin if (S==4'h0) begin // SDRAM initialization if (FS[15:0]==16'hFFC0) begin // Precharge All nCS <= 1'b0; nRAS <= 1'b0; nCAS <= 1'b1; nRWE <= 1'b0; RA[10] <= 1'b1; // "all" end else if (FS[15:4]==16'hFFD & FS[0]==1'b0) begin // Repeat 8x // Auto-refresh nCS <= 1'b0; nRAS <= 1'b0; nCAS <= 1'b0; nRWE <= 1'b1; RA[10] <= 1'b0; end else if (FS[15:0]==16'hFFE8) begin // Set Mode Register nCS <= 1'b0; nRAS <= 1'b0; nCAS <= 1'b0; nRWE <= 1'b0; RA[10] <= 1'b0; // Reserved in mode register end else if (FS[15:4]==12'hFFF & FS[0]==1'b0) begin // Repeat 8x // Auto-refresh nCS <= 1'b0; nRAS <= 1'b0; nCAS <= 1'b0; nRWE <= 1'b1; RA[10] <= 1'b0; end else begin // Otherwise send no-op // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; RA[10] <= 1'b0; end // Enable SDRAM clock after 65,280 cycles (~4.56ms) CKE <= FS[15:8] == 8'hFF; // Mode register contents BA[1:0] <= 2'b00; // Reserved RA[11] <= 1'b0; // Reserved // RA[10] set above ^ RA[9] <= 1'b1; // "1" for single write mode RA[8] <= 1'b0; // Reserved RA[7] <= 1'b0; // "0" for not test mode RA[6:4] <= 3'b010; // "2" for CAS latency 2 RA[3] <= 1'b0; // "0" for sequential burst (not used) RA[2:0] <= 3'b000; // "0" for burst length 1 (no burst) // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Begin normal operation after 128k init cycles (~9.15ms) if (FS == 16'hFFFF) Ready <= 1'b1; end else if (S==4'h1) begin // Enable clock CKE <= 1'b1; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'h2) begin // Enable clock CKE <= 1'b1; // Activate nCS <= 1'b0; nRAS <= 1'b0; nCAS <= 1'b1; nRWE <= 1'b1; // SDRAM bank 0, high-order row address is 0 BA <= 2'b00; RA[11:8] <= 4'b0000; // Row address is as previously latched // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'h3) begin // Enable clock CKE <= 1'b1; // Read nCS <= 1'b0; nRAS <= 1'b1; nCAS <= 1'b0; nRWE <= 1'b1; // SDRAM bank 0, RA[11,9:8] don't care BA <= 2'b00; RA[11] <= 1'b0; RA[10] <= 1'b1; // (A10 set to auto-precharge) RA[9] <= 1'b0; RA[8] <= 1'b0; // Latch column address for read command RA[7:0] <= Ain[7:0]; // Read low byte (high byte is +4MB in ramworks) DQML <= 1'b0; DQMH <= 1'b1; end else if (S==4'h4) begin // Enable clock CKE <= 1'b1; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'h5) begin // Enable clock CKE <= 1'b1; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'h6) begin // Enable clock CKE <= 1'b1; if (FS[6:4]==0) begin // Auto-refresh nCS <= 1'b0; nRAS <= 1'b0; nCAS <= 1'b0; nRWE <= 1'b1; end else begin // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; end // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'h7) begin // Enable clock CKE <= 1'b1; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Latch row address for activate command RA[7:0] <= Ain[7:0]; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'h8) begin // Enable clock CKE <= 1'b1; // Activate nCS <= 1'b0; nRAS <= 1'b0; nCAS <= 1'b1; nRWE <= 1'b1; // SDRAM bank, RA[11:8] determine by RamWorks bank BA <= RWBank[5:4]; RA[11:8] <= RWBank[3:0]; // Row address is as previously latched // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'h9) begin // Enable clock CKE <= 1'b1; // Read/Write nCS <= 1'b0; nRAS <= 1'b1; nCAS <= 1'b0; nRWE <= nWE80; // SDRAM bank still determined by RamWorks, RA[11,9:8] don't care BA <= RWBank[5:4]; RA[11] <= 1'b0; RA[10] <= 1'b1; // (A10 set to auto-precharge) RA[9] <= 1'b0; RA[8] <= RWBank[7]; // Latch column address for R/W command RA[7:0] <= Ain[7:0]; // Latch RAMWorks low nybble write select using old row address RWSel <= RA[0] & ~RA[3] & ~nWE & ~nC07X; // Mask according to RAMWorks bank (high byte is +4MB) DQML <= RWBank[6]; DQMH <= ~RWBank[6]; end else if (S==4'hA) begin // Enable clock CKE <= 1'b1; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'hB) begin // Disable clock CKE <= 1'b0; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'hC) begin // Disable clock CKE <= 1'b0; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // RAMWorks Bank Register Select if (RWSel) begin // Latch RAMWorks bank if accessed if (SetRWBankFF) RWBank <= 8'hFF; else RWBank <= Din[7:0] & {RWMask[7], ~RWMask[6:0]}; // Recognize command sequence and advance CS state if ((CS==3'h0 & Din[7:0]==8'hFF) | (CS==3'h1 & Din[7:0]==8'h00) | (CS==3'h2 & Din[7:0]==8'h55) | (CS==3'h3 & Din[7:0]==8'hAA) | (CS==3'h4 & Din[7:0]==8'hC1) | (CS==3'h5 & Din[7:0]==8'hAD) | CS==3'h6 | CS==3'h7) CS <= CS+1; else CS <= 0; // Back to beginning if it's not right if (CS==3'h6) begin // Recognize and submit command in CS6 SetRWBankFF <= Din[7:0]==8'hFF; if (Din[7:0]==8'hEF) UFMPrgmEN <= 1'b1; if (Din[7:0]==8'hEE) UFMEraseEN <= 1'b1; UFMBitbang <= Din[7:0]==8'hEA; RWMaskSet <= Din[7:0]==8'hE0; end else begin // Reset command triggers SetRWBankFF <= 1'b0; UFMBitbang <= 1'b0; RWMaskSet <= 1'b0; end CmdTout <= 0; // Reset command timeout if RWSel accessed end else begin CmdTout <= CmdTout+1; // Increment command timeout // If command sequence times out, reset sequence state if (CmdTout==3'h7) CS <= 0; end end else if (S==4'hD) begin // Disable clock CKE <= 1'b0; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'hE) begin // Disable clock CKE <= 1'b0; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Latch row address for next video read RA[7:0] <= Ain[7:0]; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end else if (S==4'hF) begin // Disable clock CKE <= 1'b0; // NOP nCS <= 1'b1; nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA <= 2'b00; RA[11:8] <= 4'b0000; // Latch row address for next video read RA[7:0] <= Ain[7:0]; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; end end always @(negedge C14M) begin // Latch video and read data outputs if (S==4'h6) Vout[7:0] <= RD[7:0]; if (S==4'hC) Dout[7:0] <= RD[7:0]; end endmodule