module RAM4GS(PHI2, MAin, CROW, Din, Dout, nCCAS, nCRAS, nFWE, RBA, RA, RD, nRCS, RCLK, RCKE, nRWE, nRRAS, nRCAS, RDQMH, RDQML); /* 65816 Phase 2 Clock */ input PHI2; /* Async. DRAM Control Inputs */ input nCCAS, nCRAS; /* Synchronized PHI2 and DRAM signals */ reg PHI2r, PHI2r2, PHI2r3; reg RASr, RASr2, RASr3; reg CASr, CASr2, CASr3; reg FWEr; reg CBR; /* 65816 Data */ input [7:0] Din; output [7:0] Dout = RD[7:0]; /* Latched 65816 Bank Address */ reg [7:0] Bank; /* Async. DRAM Address Bus */ input [1:0] CROW; input [9:0] MAin; input nFWE; reg n8MEGEN = 0; reg XOR8MEG = 0; /* SDRAM Clock */ input RCLK; /* SDRAM */ reg RCKEEN; output reg RCKE = 0; output reg nRCS = 1, nRRAS = 1, nRCAS = 1, nRWE = 1; output reg [1:0] RBA; reg nRowColSel; reg RA11; reg RA10; reg [9:0] RowA; output [11:0] RA; assign RA[11] = RA11; assign RA[10] = RA10; assign RA[9:0] = ~nRowColSel ? RowA[9:0] : MAin[9:0]; output RDQML = ~nRowColSel ? 1'b1 : ~MAin[9]; output RDQMH = ~nRowColSel ? 1'b1 : MAin[9]; reg [7:0] WRD; inout [7:0] RD = (~nCCAS & ~nFWE) ? WRD[7:0] : 8'bZ; /* UFM Interface */ reg UFMD = 0; // UFM data register bit 15 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 reg UFMOscEN = 0; // UFM oscillator enable 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 (UFMOscEN), .program (UFMProgram), .busy (UFMBusy), .drdout (DRDOut), .osc (UFMOsc), .rtpbusy (RTPBusy)); reg UFMBusyReg = 0; // UFMBusy registered to sync with RCLK reg RTPBusyReg = 0; // RTPBusy registered to sync with RCLK /* UFM State */ reg UFMInitDone = 0; // 1 if UFM initialization finished reg UFMReqErase = 0; // 1 if UFM requires erase /* UFM Command Interface */ reg C1Submitted = 0; reg ADSubmitted = 0; reg CmdEnable = 0; reg CmdSubmitted = 0; reg Cmdn8MEGEN = 0; reg CmdDRCLK = 0; reg CmdDRDIn = 0; reg CmdUFMErase = 0; // Set by user command. Programs UFM reg CmdUFMPrgm = 0; // Set by user command. Erases UFM wire ADWR = Bank[7:0]==8'hFB & MAin[7:0]==8'hFF & ~nFWE; wire C1WR = Bank[7:0]==8'hFB & MAin[7:0]==8'hFE & ~nFWE; wire CMDWR = Bank[7:0]==8'hFB & MAin[7:0]==8'hFD & ~nFWE; /* State Counters */ reg InitReady = 0; // 1 if ready for init sequence reg Ready = 0; // 1 if done with init sequence reg [1:0] S = 0; // post-RAS State counter reg [17:0] FS = 0; // Fast init state counter reg [3:0] IS = 0; // Init state counter reg WriteDone; /* Synchronize PHI2, RAS, CAS */ always @(posedge RCLK) begin PHI2r <= PHI2; PHI2r2 <= PHI2r; PHI2r3 <= PHI2r2; RASr <= ~nCRAS; RASr2 <= RASr; RASr3 <= RASr2; CASr <= ~nCCAS; CASr2 <= CASr; CASr3 <= CASr2; end /* Latch 65816 bank when PHI2 rises */ always @(posedge PHI2) begin if (Ready) RA11 <= (Din[6] & ~n8MEGEN) ^ XOR8MEG; // Set RA11 else RA11 <= 1'b0; // Reserved in mode register Bank[7:0] <= Din[7:0]; // Latch bank end /* Latch bank address, row address, WE, and CAS when RAS falls */ always @(negedge nCRAS) begin if (Ready) begin RBA[1:0] <= CROW[1:0]; RowA[9:0] <= MAin[9:0]; end else begin RBA[1:0] <= 2'b00; // Reserved in mode register RowA[9] <= 1'b1; // "1" for single write mode RowA[8] <= 1'b0; // Reserved RowA[7] <= 1'b0; // "0" for not test mode RowA[6:4] <= 3'b010; // "2" for CAS latency 2 RowA[3] <= 1'b0; // "0" for sequential burst (not used) RowA[2:0] <= 3'b000; // "0" for burst length 1 (no burst) end FWEr <= ~nFWE; CBR <= ~nCCAS; end /* Latch write data when CAS falls */ always @(negedge nCCAS) begin WRD[7:0] <= Din[7:0]; end /* State counter from RAS */ always @(posedge RCLK) begin if (~RASr2) S <= 0; else if (S==2'h3) S <= 2'h3; else S <= S+1; end /* Init state counter */ always @(posedge RCLK) begin // Wait ~4.178ms (at 62.5 MHz) before starting init sequence FS <= FS+1; if (FS[17:10] == 8'hFF) InitReady <= 1'b1; end /* SDRAM CKE */ always @(posedge RCLK) begin // Only 1 LUT4 allowed for this function! RCKE <= ((RASr | RASr2) & RCKEEN) | (~RASr2 & RASr3); end /* SDRAM command */ always @(posedge RCLK) begin if (Ready) begin if (S==0) begin if (RASr2) begin if (CBR) begin // AREF nRCS <= 1'b0; nRRAS <= 1'b0; nRCAS <= 1'b0; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care end else begin // ACT nRCS <= 1'b0; nRRAS <= 1'b0; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // Bank RA10 consistently "1" end // Enable clock only for reads RCKEEN <= ~CBR & ~FWEr; end else if (RCKE) begin // PCall nRCS <= 1'b0; nRRAS <= 1'b0; nRCAS <= 1'b1; nRWE <= 1'b0; RA10 <= 1'b1; // "all" RCKEEN <= 1'b1; end else begin // NOP nRCS <= 1'b1; nRRAS <= 1'b1; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care RCKEEN <= 1'b1; end nRowColSel <= 1'b0; // Select registered row addres end else if (S==1) begin // NOP nRCS <= 1'b1; nRRAS <= 1'b1; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care nRowColSel <= 1'b1; // Select asynchronous column address RCKEEN <= ~CBR; // Disable clock if refresh cycle end else if (S==2) begin if (~FWEr & ~CBR) begin // RD nRCS <= 1'b0; nRRAS <= 1'b1; nRCAS <= 1'b0; nRWE <= 1'b1; RA10 <= 1'b1; // Auto-precharge end else begin // NOP nRCS <= 1'b1; nRRAS <= 1'b1; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care end nRowColSel <= 1'b1; // Select asynchronous column address RCKEEN <= ~CBR & FWEr; // Enable clock only for writes end else if (S==3) begin if (CASr2 & ~CASr3 & ~CBR & FWEr) begin // WR nRCS <= 1'b0; nRRAS <= 1'b1; nRCAS <= 1'b0; nRWE <= 1'b0; RA10 <= 1'b1; // Auto-precharge end else begin // NOP nRCS <= 1'b1; nRRAS <= 1'b1; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care end nRowColSel <= ~(~FWEr | CASr3 | CBR); RCKEEN <= ~(~FWEr | CASr2 | CBR); end end else if (InitReady) begin if (S==0 & RASr2) begin if (IS==0) begin // NOP nRCS <= 1'b1; nRRAS <= 1'b1; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care end else if (IS==1) begin // PC all nRCS <= 1'b0; nRRAS <= 1'b0; nRCAS <= 1'b1; nRWE <= 1'b0; RA10 <= 1'b1; // "all" end else if (IS==9) begin // Load mode register nRCS <= 1'b0; nRRAS <= 1'b0; nRCAS <= 1'b0; nRWE <= 1'b0; RA10 <= 1'b0; // Reserved in mode register end else begin // AREF nRCS <= 1'b0; nRRAS <= 1'b0; nRCAS <= 1'b0; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care end IS <= IS+1; end else begin // NOP nRCS <= 1'b1; nRRAS <= 1'b1; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care end if (S==3 & ~RASr2 & IS==15) Ready <= 1'b1; nRowColSel <= 1'b0; // Select registered row address RCKEEN <= 1'b1; end else begin // NOP nRCS <= 1'b1; nRRAS <= 1'b1; nRCAS <= 1'b1; nRWE <= 1'b1; RA10 <= 1'b1; // RA10 is don't care nRowColSel <= 1'b0; // Select registered row address RCKEEN <= 1'b0; end end /* Submit command when PHI2 falls */ always @(negedge PHI2) begin // Magic number check if (C1WR & Din[7:0]==8'hC1) begin // "C1" magic number if (ADSubmitted) begin CmdEnable <= 1'b1; UFMOscEN <= 1'b1; end C1Submitted <= 1'b1; ADSubmitted <= 1'b0; end else if (ADWR & Din[7:0]==8'hAD) begin // "AD" magic number if (C1Submitted) begin CmdEnable <= 1'b1; UFMOscEN <= 1'b1; end ADSubmitted <= 1'b1; C1Submitted <= 1'b0; end else if (C1WR | ADWR) begin // wrong magic number submitted CmdEnable <= 1'b0; C1Submitted <= 1'b0; ADSubmitted <= 1'b0; end else if (CMDWR) CmdEnable <= 1'b0; // Submit command if (CMDWR & CmdEnable) begin if (Din[7:4]==4'h0) begin XOR8MEG <= Din[0]; end else if (Din[7:4]==4'h1) begin Cmdn8MEGEN <= ~Din[0]; CmdSubmitted <= 1'b1; end else if (Din[7:4]==4'h2) begin Cmdn8MEGEN <= n8MEGEN; CmdUFMErase <= Din[3]; CmdUFMPrgm <= Din[2]; CmdDRCLK <= Din[1]; CmdDRDIn <= Din[0]; CmdSubmitted <= 1'b1; end end end /* UFM Control */ always @(posedge RCLK) begin if (~Ready) begin if (~UFMInitDone & FS[17:16]==2'b00) begin // Shift 0 into address register ARCLK <= FS[3]; // Clock address register ARShift <= 1'b1; // Shift 0 into address register DRCLK <= 1'b0; // Don't clock data register DRDIn <= 1'b0; // DRDIn is don't care DRShift <= 1'b0; // DRShift is don't care end else if (~UFMInitDone & FS[17:16]==2'b01 & FS[7:4]==4'h0) begin // Parallel transfer UFM data to shift register ARCLK <= 1'b0; // Don't clock address register ARShift <= 1'b0; // ARShift is don't care DRCLK <= FS[3]; // Clock data register DRDIn <= 1'b0; // DRDIn is don't care DRShift <= 1'b0; // Parallel transfer to data register end else if (~UFMInitDone & FS[17:16]==2'b01 & FS[7:4]==4'h4) begin // Shift UFM ARCLK <= 1'b0; // Don't clock address register ARShift <= 1'b0; // ARShift is don't care DRCLK <= FS[3]; // Clock data register DRDIn <= 1'b0; // DRDIn is don't care DRShift <= 1'b1; // Shift data register // Capture bit 15 of this UFM word in UFMD register if (FS[3:0]==4'h7) UFMD <= DRDOut; end else if (~UFMInitDone & FS[17:16]==2'b01 & FS[7:4]==4'h5) begin // Check saved capacity entry if (UFMD) UFMInitDone <= 1'b1; // If erased, quit iterating else begin // If valid setting here n8MEGEN <= ~DRDOut; // Set capacity setting // If last byte in sector, mark need to erase if (FS[15:8]==8'hFF) begin UFMReqErase <= 1'b1; // Mark need to wrap around UFMInitDone <= 1'b1; // Quit iterating end end end else if (~UFMInitDone & FS[17:16]==2'b01 & FS[7:4]==4'h6) begin // Increment UFM address ARCLK <= FS[3]; // Clock address register ARShift <= 1'b0; // Increment UFM address DRCLK <= 1'b0; // Don't clock data register DRDIn <= 1'b0; // DRDIn is don't care DRShift <= 1'b0; // DRShift is don't care end else if (FS[17:16]==2'b10 & UFMReqErase) begin // Shift 0 into address register ARCLK <= FS[3]; // Clock address register ARShift <= 1'b1; // Shift 0 into address register DRCLK <= 1'b0; // Don't clock data register DRDIn <= 1'b0; // DRDIn is don't care DRShift <= 1'b0; // DRShift is don't care end else begin // Don't do anything with UFM ARCLK <= 1'b0; // Don't clock address register ARShift <= 1'b0; // ARShift is don't care DRCLK <= 1'b0; // Don't clock data register DRDIn <= 1'b0; // DRDIn is don't care DRShift <= 1'b0; // DRShift is don't care end // Don't erase or program UFM during initialization UFMErase <= 1'b0; UFMProgram <= 1'b0; end else begin // Can only shift UFM data register now ARCLK <= 1'b0; ARShift <= 1'b0; DRShift <= 1'b1; // Set user command signals after PHI2 falls if (~PHI2r2 & PHI2r3 & CmdSubmitted) begin n8MEGEN <= Cmdn8MEGEN; DRCLK <= CmdDRCLK; DRDIn <= CmdDRDIn; end // UFM programming sequence if (CmdUFMPrgm | CmdUFMErase) begin if (~UFMBusyReg & ~RTPBusyReg) begin if (UFMReqErase | CmdUFMErase) UFMErase <= 1'b1; else if (CmdUFMPrgm) UFMProgram <= 1'b1; end else if (UFMBusyReg) UFMReqErase <= 1'b0; end end end endmodule