module RAM2GS(PHI2, MAin, CROW, Din, Dout,
              nCCAS, nCRAS, nFWE, LED,
              RBA, RA, RD, nRCS, RCLK, RCLKout, 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;
    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 in/out */
    input RCLK;
    output RCLKout;
    ODDRXE rclk_oddr(.D0(1'b0), .D1(1'b1), 
        .SCLK(RCLK), .RST(1'b0), .Q(RCLKout));
    
    /* 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 wb_rst;
    reg wb_cyc_stb;
	reg wb_req;
    reg wb_we;
    reg [7:0] wb_adr;
    reg [7:0] wb_dati;
	wire wb_ack;
    wire [7:0] wb_dato;
	wire ufm_irq;
    REFB ufmefb (
        .wb_clk_i(RCLK),
        .wb_rst_i(wb_rst),
        .wb_cyc_i(wb_cyc_stb),
        .wb_stb_i(wb_cyc_stb),
        .wb_we_i(wb_we),
        .wb_adr_i(wb_adr),
        .wb_dat_i(wb_dati),
        .wb_dat_o(wb_dato),
        .wb_ack_o(wb_ack),
		.wbc_ufm_irq(ufm_irq));
	
    /* UFM Command Interface */
    reg C1Submitted = 0;
    reg ADSubmitted = 0;
    reg CmdEnable = 0;
    reg CmdValid = 0;
    reg Cmdn8MEGEN = 0;
    reg CmdLEDEN = 0;
    reg CmdUFMWrite = 0;
    reg CmdUFMShift = 0;
    reg CmdUFMData = 0;
    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;
            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;
            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]);
                CmdValid <= 1'b0;
            end else if (Din[7:4]==4'h0) begin // Unsupported type 
                XOR8MEG <= 0;
                CmdValid <= 1'b0;
            end else if (Din[7:4]==4'h1) begin
                CmdLEDEN <= Din[1];
                Cmdn8MEGEN <= !Din[0];
                CmdUFMShift <= 1'b0;
                CmdUFMWrite <= 1'b0;
                CmdValid <= 1'b1;
            end else if (Din[7:4]==4'h2) begin
                // Reserved for MAX commands
                CmdValid <= 1'b0;
            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
                CmdValid <= 1'b0;
            end else if (Din[7:4]==4'h3 &&  Din[3]) begin
                // LCMXO2 commands
                // Din[1] - Shift when low, execute when high
                // Din[0] - Shift data
                CmdUFMShift <= !Din[1];
                CmdUFMWrite <= Din[1:0] == 2'b10;
				CmdUFMData <= Din[0];
                CmdLEDEN <= LEDEN;
                Cmdn8MEGEN <= n8MEGEN;
                CmdValid <= 1'b1;
            end else CmdValid <= 1'b0;
        end else CmdValid <= 1'b0;
    end

    /* UFM Control */
    always @(posedge RCLK) begin
        if (!InitReady && FS[17:15]==3'h0) begin
            wb_rst <= !FS[14];
            wb_we <= 1'b0;
			wb_cyc_stb <= 1'b0;
			wb_req <= 1'b0;
            wb_adr[7:0] <= 8'h00;
            wb_dati[7:0] <= 8'h00;
        end else if (!InitReady && FS[17:15]==3'h1) begin
            wb_rst <= 1'b0;
			
			if (FS[8:0]==0) wb_cyc_stb <= 0;
			else if (FS[8:0]==1 && wb_req) wb_cyc_stb <= 1;
			else if (wb_ack) wb_cyc_stb <= 0;
            case (FS[14:9])
                0: begin // Open frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h80;
					wb_req <= 1;
                end 1: begin // Enable configuration interface - command
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h74;
					wb_req <= 1;
                end 2: begin // Enable configuration interface - operand 1/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h08;
					wb_req <= 1;
                end 3: begin // Enable configuration interface - operand 2/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 4: begin // Enable configuration interface - operand 3/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 5: begin // Close frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;

                end 6: begin // Open frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h80;
					wb_req <= 1;
                end 7: begin // Poll status register - command
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h3C;
					wb_req <= 1;
                end 8: begin // Poll status register - operand 1/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 9: begin // Poll status register - operand 2/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 10: begin // Poll status register - operand 3/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 11, 12, 13, 14: begin // Read status register 1-4
                    wb_we <= 1'b0;
                    wb_adr[7:0] <= 8'h73;
                    wb_dati[7:0] <= 8'h3C;
					wb_req <= 1;
                end 15: begin // Close frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;

                end 16: begin // Open frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h80;
					wb_req <= 1;
                end 17: begin // Set UFM address - command
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'hB4;
					wb_req <= 1;
                end 18: begin // Set UFM address - operand 1/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 19: begin // Set UFM address - operand 2/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 20: begin // Set UFM address - operand 3/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 21: begin // Set UFM address - data 1/4
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h40;
					wb_req <= 1;
                end 22: begin // Set UFM address - data 2/4
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 23: begin // Set UFM address - data 3/4
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 24: begin // Set UFM address - data 4/4
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 190;
					wb_req <= 1;
                end 25: begin // Close frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;

                end 26: begin // Open frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h80;
					wb_req <= 1;
                end 27: begin // Read UFM page - command
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'hCA;
					wb_req <= 1;
                end 28: begin // Read UFM page - operand 1/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h10;
					wb_req <= 1;
                end 29: begin // Read UFM page - operand 2/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 30: begin // Read UFM page - operand 3/3
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h01;
					wb_req <= 1;
                end 31: begin // Read UFM page - data 0
                    wb_we <= 1'b0;
                    wb_adr[7:0] <= 8'h73;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                    if (wb_ack) begin
                        LEDEN <= wb_dato[1];
                        n8MEGEN <= wb_dato[0];
                    end
                end     32, 33, 34,
                    35, 36, 37, 38,
                    39, 40, 41, 42,
                    43, 44, 45, 46: begin // Read UFM page - data 1-15
                    wb_we <= 1'b0;
                    wb_adr[7:0] <= 8'h73;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 47: begin // Close frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;

                end 48: begin // Open frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h80;
					wb_req <= 1;
                end 49: begin // Disable configuration interface - command
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h26;
					wb_req <= 1;
                end 50: begin // Disable configuration interface - operand 1/2
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 51: begin // Disable configuration interface - operand 2/2
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end 52: begin // Close frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;

                end 53: begin // Open frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h80;
					wb_req <= 1;
                end 54: begin // Bypass - command
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h71;
                    wb_dati[7:0] <= 8'hFF;
					wb_req <= 1;
                end 55: begin // Close frame
                    wb_we <= 1'b1;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 1;
                end default: begin
                    wb_we <= 1'b0;
                    wb_adr[7:0] <= 8'h70;
                    wb_dati[7:0] <= 8'h00;
					wb_req <= 0;
                end
            endcase
        end else if (!InitReady) begin
            wb_rst <= 1'b0;
            wb_cyc_stb <= 1'b0;
			wb_req <= 1'b0;
            wb_we <= 1'b0;
            wb_adr[7:0] <= 8'h00;
            wb_dati[7:0] <= 8'h00;
        end else if (!PHI2r2 && PHI2r3 && CmdValid) begin
            wb_rst <= 1'b0;
			wb_req <= 1'b0;
            // Set user command signals after PHI2 falls
            LEDEN <= CmdLEDEN;
            n8MEGEN <= Cmdn8MEGEN;
            if (CmdUFMShift) begin
                wb_adr[7:0] <= { wb_adr[6:0], wb_dati[7] };
                wb_dati[7:0] <= { wb_dati[6:0], wb_we };
                wb_we <= CmdUFMData;
            end
            wb_cyc_stb <= CmdUFMWrite;
        end else wb_cyc_stb <= 0;
    end
endmodule
