module RAM2GS(PHI2, MAin, CROW, Din, Dout, nCCAS, nCRAS, nFWE, LED, 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; /* Activity LED */ reg LEDEN = 0; output LED; assign LED = !(!nCRAS && !CBR && LEDEN && Ready); /* 65816 Data */ input [7:0] Din; output [7:0] Dout; assign Dout[7:0] = 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, RDQMH; assign RDQML = !nRowColSel ? 1'b1 : !MAin[9]; assign RDQMH = !nRowColSel ? 1'b1 : MAin[9]; reg [7:0] WRD; inout [7:0] RD; assign RD[7:0] = (!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 UFMBusyAsync; // 1 if UFM is doing user operation. Asynchronous wire RTPBusyAsync; // 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 (UFMBusyAsync), .drdout (DRDOut), .osc (UFMOsc), .rtpbusy (RTPBusyAsync)); // UFMBusy registered to sync with RCLK reg UFMBusyReg0; always @(posedge RCLK) UFMBusyReg0 <= UFMBusyAsync; // RTPBusy registered to sync with RCLK reg RTPBusyReg0; always @(posedge RCLK) RTPBusyReg0 <= RTPBusyAsync; // UFMRTPBusy ORs both reg UFMRTPBusy; always @(posedge RCLK) UFMRTPBusy <= UFMBusyReg0 || RTPBusyReg0; /* 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 CmdLEDEN = 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 /* 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+2'h1; end /* Init state counter */ always @(posedge RCLK) begin // Wait ~4.178ms (at 62.5 MHz) before starting init sequence FS <= FS+18'h1; 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+4'h1; 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 && Din[3:2]==2'b00) begin // MAX w/LED // if (Din[7:4]==4'h0) begin // MAX w/o LED // if (Din[7:4]==4'h0 && Din[3:2]==2'b01) begin // LCMXO / iCE40 / AGM // if (Din[7:4]==4'h0 && Din[3:2]==2'b10) begin // LCMXO2 XOR8MEG <= Din[0] && !(LEDEN && Din[1]); end else if (Din[7:4]==4'h0) begin // Unsupported type XOR8MEG <= 0; end else if (Din[7:4]==4'h1) begin CmdLEDEN <= Din[1]; Cmdn8MEGEN <= !Din[0]; CmdSubmitted <= 1'b1; end else if (Din[7:4]==4'h2) begin // MAX commands CmdLEDEN <= LEDEN; Cmdn8MEGEN <= n8MEGEN; if (!CmdUFMPrgm && !CmdUFMErase) begin CmdUFMErase <= Din[3]; CmdUFMPrgm <= Din[2]; end CmdDRCLK <= Din[1]; CmdDRDIn <= Din[0]; CmdSubmitted <= 1'b1; end else if (Din[7:4]==4'h3 && !Din[3]) begin // Reserved for SPI (LCMXO, iCE40) commands // Din[2] - CS // Din[1] - SCK // Din[0] - SDI end else if (Din[7:4]==4'h3 && Din[3]) begin // Reserved for LCMXO2 commands // Din[1] - Shift when high, execute when low // Din[0] - Shift data end end end /* UFM command synchronization */ reg CmdUFMPrgmSync; always @(posedge RCLK) CmdUFMPrgmSync <= CmdUFMPrgm; reg CmdUFMEraseSync; always @(posedge RCLK) CmdUFMEraseSync <= CmdUFMErase; /* UFM Control */ reg UFMProgStart; 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 data 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'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 // Shift UFM data 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'b1; // Shift data register // If valid setting here, set capacity setting to UFMD[14] if (FS[3:0]==4'h7 && !UFMD) n8MEGEN <= !DRDOut; end else if (!UFMInitDone && FS[17:16]==2'b01 && FS[7:4]==4'h6) begin if (UFMD) UFMInitDone <= 1'b1; // If erased, quit iterating else begin // If valid setting here LEDEN <= !DRDOut; // LED enabled if UFMD[13]==0 // 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 // 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; UFMProgStart <= 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 LEDEN <= CmdLEDEN; n8MEGEN <= Cmdn8MEGEN; DRCLK <= CmdDRCLK; DRDIn <= CmdDRDIn; end // UFM programming sequence if (FS[6:0]==0) begin if (!UFMProgStart && !UFMRTPBusy) begin if (CmdUFMPrgmSync) begin UFMErase <= UFMReqErase || CmdUFMEraseSync; UFMProgStart <= 1'b1; end else if (CmdUFMEraseSync) UFMErase <= 1'b1; end else if (UFMProgStart && !UFMRTPBusy) begin UFMErase <= 1'b0; if (!UFMErase) UFMProgram <= 1'b1; end end end end endmodule