module RAM2E(C14M, RCLK, PHI1, LED, nWE, nWE80, nEN80, nC07X, Ain, Din, Dout, nDOE, Vout, nVOE, CKE, nCS, nRAS, nCAS, nRWE, BA, RA, RD, DQML, DQMH); /* Clocks */ input C14M, PHI1; /* SDRAM clock output */ output RCLK; reg RCLKx0; reg RCLKx1; always @(negedge C14M) RCLKx1 <= !RCLKx1; always @(posedge C14M) RCLKx0 <= RCLKx1; assign RCLK = RCLKx0 ^ RCLKx1; /* Control inputs */ input nWE, nWE80, nEN80, nC07X; /* Activity LED */ reg LEDEN = 0; output LED; assign LED = !(!nEN80 && LEDEN && Ready); /* Address Bus */ input [7:0] Ain; // Multiplexed DRAM address input /* 6502 Data Bus */ input [7:0] Din; // 6502 data bus inputs reg DOEEN = 0; // 6502 data bus output enable from state machine output nDOE; assign nDOE = !(!nEN80 && nWE && DOEEN); // 6502 data bus output enable output reg [7:0] Dout; // 6502 data Bus output /* Video Data Bus */ output nVOE; assign nVOE = !(!PHI1); /// Video data bus output enable output reg [7:0] Vout; // Video data bus /* SDRAM */ output reg CKE = 0; output nCS; assign nCS = 0; output reg nRAS = 1, nCAS = 1, nRWE = 1; output reg [1:0] BA; output reg [11:0] RA; output reg DQML = 1, DQMH = 1; wire RDOE = !nEN80 && !nWE80; inout [7:0] RD; assign RD[7:0] = 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 CmdRWMaskSet = 0; // RAMWorks Mask register set flag // Causes RWBank to be zeroed next RWSel access reg CmdSetRWBankFFMAX = 0; //reg CmdSetRWBankFFSPI = 0; //reg CmdSetRWBankFFMXO2 = 0; reg CmdSetRWBankFFLED = 0; reg CmdLEDSet = 0; reg CmdLEDGet = 0; /* Command Sequence Detector */ reg [2:0] CS = 0; // Command sequence state reg [2:0] CmdTout = 0; // Command sequence timeout /* UFM Interface */ reg [15:7] 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 UFMRTPBusy = 0; always @(posedge C14M) UFMRTPBusy <= UFMBusy || RTPBusy; /* UFM State and User Command Triggers */ reg UFMInitDone = 0; // 1 if UFM initialization finished reg UFMReqErase = 0; // 1 if UFM requires erase reg CmdBitbangMAX = 0; // Set by user command. Loads UFM outputs next RWSel //reg CmdBitbangSPI = 0; //reg CmdBitbangMXO2 = 0; //reg CmdExecMXO2 = 0; reg CmdPrgmMAX = 0; // Set by user command. Programs UFM reg CmdEraseMAX = 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+16'h0001; // 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+4'h1; end /* UFM Control */ reg UFMProgStart; always @(posedge C14M) begin 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]==4'h6 || FS[4:1]==4'h7 || FS[4:1]==4'h8 || FS[4:1]==4'h9 || FS[4:1]==4'hA || FS[4:1]==4'hB || FS[4:1]==4'hC || FS[4:1]==4'hD || FS[4:1]==4'hE)) begin // In states CXXX-DXXX (substeps 6-E) // Shift out UFMD[15:7] (repeat 256x 9x) ARCLK <= 1'b0; // Don't clock address register 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:7] <= {UFMD[14:7], DRDOut}; end else if (!UFMInitDone && FS[15:13]==3'b110 && FS[4:1]==4'hF) begin // In states CXXX-DXXX (substep F) // Check and save mask, compute and save LEDEN DRCLK <= 1'b0; // Don't 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 // Set settings // If byte is erased (0xFF, i.e. all 1's, is erased)... if (UFMD[15:8]==8'hFF && UFMD[7]==1'b1) begin // Current UFM address is where we want to store UFMInitDone <= 1'b1; // Quit iterating ARCLK <= 1'b0; // Don't increment address register // Otherwise byte is valid setting (i.e. some bit is 0)... end else begin // Set RWMask, but if saved mask is 0x80, set for FF if (UFMD[15:8]==8'b10000000) RWMask[7:0] <= {1'b1, 7'h00}; else RWMask[7:0] <= {UFMD[15], ~UFMD[14:8]}; // Set LED setting LEDEN <= UFMD[15] ^ UFMD[7]; // If last byte in sector... if (FS[12:5]==8'hFF) begin UFMReqErase <= 1'b1; // Need to erase ARCLK <= 1'b0; // Don't increment address register end else ARCLK <= FS[0]; // Increment if not last byte end 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; // Reset UFMProgStart UFMProgStart <= 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 (CmdBitbangMAX && RWSel && S==4'hC) begin DRDIn <= Din[6]; DRCLKPulse <= Din[7]; DRCLK <= 1'b0; end else begin DRCLKPulse <= 1'b0; DRCLK <= DRCLKPulse; end if (RWSel && S==4'hC) begin // LED control if (CmdLEDSet) LEDEN <= Din[0]; // Set capacity mask if (CmdRWMaskSet) RWMask[7:0] <= {Din[7], ~Din[6:0]}; end // UFM programming sequence if (S==4'h1 && !UFMRTPBusy) begin if (!UFMProgStart) begin if (CmdPrgmMAX) begin UFMErase <= UFMReqErase; UFMProgram <= 0; UFMProgStart <= 1; end else if (CmdEraseMAX) begin UFMErase <= 1; UFMProgram <= 0; UFMProgStart <= 1; end end else begin UFMErase <= 0; UFMProgram <= !UFMErase; UFMProgStart <= 1; end 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 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 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 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 nRAS <= 1'b0; nCAS <= 1'b0; nRWE <= 1'b1; RA[10] <= 1'b0; end else begin // Otherwise send no-op // NOP 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; // Inhibit data bus output DOEEN <= 1'b0; // 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 nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h2) begin // Enable clock CKE <= 1'b1; // Activate nRAS <= 1'b0; nCAS <= 1'b1; nRWE <= 1'b1; // SDRAM bank 0, high-order row address is 0 BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Row address is as previously latched // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h3) begin // Enable clock CKE <= 1'b1; // Read nRAS <= 1'b1; nCAS <= 1'b0; nRWE <= 1'b1; // SDRAM bank 0, RA[11,9:8] don't care BA[1:0] <= 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; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h4) begin // Enable clock CKE <= 1'b1; // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h5) begin // Enable clock CKE <= 1'b1; // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h6) begin // Enable clock CKE <= 1'b1; if (FS[5:4]==0) begin // Auto-refresh nRAS <= 1'b0; nCAS <= 1'b0; nRWE <= 1'b1; end else begin // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; end // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h7) begin // Enable clock CKE <= 1'b1; // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 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; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h8) begin // Enable clock if '245 output enabled CKE <= !nEN80; // Activate if '245 output enabled nRAS <= nEN80; nCAS <= 1'b1; nRWE <= 1'b1; // SDRAM bank, RA[11:8] determine by RamWorks bank BA[1:0] <= RWBank[5:4]; RA[11:8] <= RWBank[3:0]; // Row address is as previously latched // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'h9) begin // Keep CKE same as last clock // Read/Write if '245 output enabled if (CKE) begin // Read/Write if CKE ('245 output enabled) nRAS <= 1'b1; nCAS <= 1'b0; nRWE <= nWE80; end else begin // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; end // SDRAM bank still determined by RamWorks, RA[11,9:8] don't care BA[1:0] <= 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]; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'hA) begin // Keep CKE same as last clock // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Inhibit data bus output DOEEN <= 1'b0; end else if (S==4'hB) begin // Disable clock CKE <= 1'b0; // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Enable data bus output DOEEN <= 1'b1; end else if (S==4'hC) begin // Disable clock CKE <= 1'b0; // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Enable data bus output DOEEN <= 1'b1; // RAMWorks Bank Register Select if (RWSel) begin // Latch RAMWorks bank if accessed if (CmdSetRWBankFFLED || CmdSetRWBankFFMAX || //CmdSetRWBankFFSPI || //CmdSetRWBankFFMXO2 || (CmdLEDGet && LEDEN)) 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+3'h1; else CS <= 0; // Back to beginning if it's not right if (CS==3'h6) begin // Recognize and submit command in CS6 CmdSetRWBankFFMAX <= Din[7:0]==8'hFF; //CmdSetRWBankFFSPI <= Din[7:0]==8'hFE; //CmdSetRWBankFFMXO2 <= Din[7:0]==8'hFD; CmdSetRWBankFFLED <= Din[7:0]==8'hF0; CmdRWMaskSet <= Din[7:0]==8'hE0; CmdLEDSet <= Din[7:0]==8'hE2; CmdLEDGet <= Din[7:0]==8'hE3; CmdBitbangMAX <= Din[7:0]==8'hEA; //CmdBitbangSPI <= Din[7:0]==8'hEB; //CmdBitbangMXO2 <= Din[7:0]==8'hEC; //CmdExecMXO2 <= Din[7:0]==8'hED; if (!CmdEraseMAX && !CmdPrgmMAX) begin if (Din[7:0]==8'hEE) CmdEraseMAX <= 1; if (Din[7:0]==8'hEF) CmdPrgmMAX <= 1; end end else begin // Reset command triggers CmdSetRWBankFFMAX <= 0; //CmdSetRWBankFFSPI <= 0; //CmdSetRWBankFFMXO2 <= 0; CmdSetRWBankFFLED <= 0; CmdRWMaskSet <= 0; CmdLEDSet <= 0; CmdLEDGet <= 0; CmdBitbangMAX <= 0; //CmdBitbangSPI <= 0; //CmdBitbangMXO2 <= 0; //CmdExecMXO2 <= 0; end CmdTout <= 0; // Reset command timeout if RWSel accessed end else begin CmdTout <= CmdTout+3'h1; // 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 nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 2'b00; RA[11:8] <= 4'b0000; // Mask everything DQML <= 1'b1; DQMH <= 1'b1; // Enable data bus output DOEEN <= 1'b1; end else if (S==4'hE) begin // Disable clock CKE <= 1'b0; // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 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; // Enable data bus output DOEEN <= 1'b1; end else if (S==4'hF) begin // Disable clock CKE <= 1'b0; // NOP nRAS <= 1'b1; nCAS <= 1'b1; nRWE <= 1'b1; // Don't care bank, RA[11:8] BA[1:0] <= 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; // Enable data bus output DOEEN <= 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