`timescale 1ns / 1ps
/*
    Copyright (C) 2021, David Henderson using elements (C) 2019 Stephen J Leary
    All rights reserved.
    
    This is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This is distributed WITHOUT ANY WARRANTY; without even the implied warranty
	 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    <http://www.gnu.org/licenses/>
*/

module DFB_SDRAM(

	input RST,
	input RAMCLK,
	input [31:0] A,
	input AS,
	input [1:0] SIZE,
	input RW,
	
	output RAMCLKE,
	output [12:0] MA,
	output [1:0] BA,
	output [3:0] DQM,
	output RAS,
	output CAS,
	output ALTUCS,
	output ALTLCS,
	output ALTUWE,
	output ALTLWE,
	output DSACK,
	
	output [2:0] CYCLE // informational
);

	// Command structure based on TF330 source (C) 2019 Stephen J Leary
	// { RAS, CAS, WE }
	localparam CMD_NOP             = 3'b111; //  7
	localparam CMD_BURST_TERMINATE = 3'b110; //  6
	localparam CMD_READ            = 3'b101; //  5
	localparam CMD_WRITE           = 3'b100; //  4
	localparam CMD_ACTIVE          = 3'b011; //  3
	localparam CMD_PRECHARGE       = 3'b010; //  2
	localparam CMD_AUTO_REFRESH    = 3'b001; //  1
	localparam CMD_LOAD_MODE       = 3'b000; //  0 	

	localparam MODE 					= 13'b0000000100000; // single read per burst, cas delay of 2
	
	localparam CYCLE_IDLE = 2'b00;
	localparam CYCLE_REFRESH = 2'b01;
	localparam CYCLE_ACCESS = 2'b10;

	reg [2:0] cmd = CMD_NOP;
	reg [12:0] counter = 'd0;
	reg initialised = 1'b0;
	reg [12:0] ma = 'd0;
	reg [3:0] dqm = 4'b1111;
	reg [1:0] ba = 'd0;
	reg clke	= 1'b0;
	reg refresh_pending = 1'b0;
	reg [3:0] cycle = 'd0;
	reg [1:0] cycle_type = 'd0;
	reg dsack = 1'b1;


	always @( negedge RAMCLK or negedge RST ) begin
	
	
		if( ~RST ) begin
			cmd <= CMD_NOP;
			counter <= 'd0;
			initialised <= 'd0;
			ma <= MODE;
			ba <= 'd0;
			dqm <= 'd15;
			clke <= 1'b0;
			dsack <= 1'b1;
		end
		else begin
			counter <= counter + 'd1;
			if( ~initialised ) begin // perform init
				// choose the counter values depending on your clock
				clke <= 1'b1;
				if( counter == 'd6000 ) begin // > 100us @ 32MHz
					cmd <= CMD_PRECHARGE;
					ma[10] <= 1'b1; // all banks
				end
				else if( counter == 'd6100 || counter == 'd6300 ) begin // refresh before and after load moad
					cmd <= CMD_AUTO_REFRESH;
				end
				else if( counter == 'd6200 ) begin	// load mode
					cmd <= CMD_LOAD_MODE;
					ma <= MODE;
				end
				else if( counter == 'd6310 ) begin
					initialised <= 'd1;
				end
				else begin
					cmd <= CMD_NOP;
				end
			end
			else begin
				// choose the counter values depending on your clock
//				if( counter[7:0] == 8'hFF ) begin // 32MHz
				if( counter[6:0] == 7'h7F ) begin // 16MHz
					refresh_pending <= 1'b1;
				end
				
				if( cycle_type == CYCLE_IDLE ) begin
					clke <= 1'b1;
					cmd <= CMD_NOP;
					if( ~AS ) begin
						cycle_type <= CYCLE_ACCESS;
					end
					else if( refresh_pending ) begin
						cycle_type <= CYCLE_REFRESH;
					end
					cycle <= 'd0;
				end
				else begin
					// DQM logic based on TF330 source (C) 2019 Stephen J Leary
					dqm[3] <= ~RW & (A[1] | A[0]);
					dqm[2] <= ~RW & ((~SIZE[1] & SIZE[0] & ~A[0]) | A[1]);
					dqm[1] <= ~RW & ((SIZE[1] & ~SIZE[0] & ~A[1] & ~A[0]) | (~SIZE[1] & SIZE[0] & ~A[1]) |(A[1] & A[0]));
					dqm[0] <= ~RW & ((~SIZE[1] & SIZE[0] & ~A[1] ) | (~SIZE[1] & SIZE[0] & ~A[0] ) | (SIZE[1] & ~A[1] & ~A[0] ) | (SIZE[1] & ~SIZE[0] & ~A[1]));
				
					if( cycle_type == CYCLE_REFRESH ) begin // handle refresh
						case( cycle )
							'd1: begin
								cmd <= CMD_AUTO_REFRESH;
								refresh_pending <= 1'b0;
							end
							'd3: begin
								cycle_type <= CYCLE_IDLE;
							end
							default: begin
								cmd <= CMD_NOP;
							end
						endcase
					end
					
					if( cycle_type == CYCLE_ACCESS ) begin // handle access
						/* The cycle counts in the case statement here can be adjusted depending on target
						/* frequency. Consecutive cycle RAS/CAS access is likely safe up to 50MHz
						/*/
						case( cycle )
							'd0: begin // open a row
								ma<= A[24:12];
								ba <= A[26:25];
								cmd <= CMD_ACTIVE;
							end
							'd1: begin // access a column
								ma[9:0] <= A[11:2];
								ma[12:10] <= 3'b001;
								cmd <= RW ? CMD_READ : CMD_WRITE;
								dsack <= RW ? 1'b1 : 1'b0; // for a write, we can send DSACK immediately
							end
							'd2: begin 
								cmd <= CMD_NOP;
							end
							'd3: begin // read data should be valid at CMD_READ + 2 CLK (for CAS Delay setting of 2)
								cmd <= CMD_NOP;
								if( RW ) begin // only applicable on reads -- hold the clock to preserve data out until AS goes high
									clke <= 1'b0;								
								end
								dsack <= 1'b0;
							end
							default: begin
								cmd <= CMD_NOP;
							end
						endcase
						
						if( AS == 1'b1 ) begin // end or abort
							if( dsack == 1'b1 ) begin // this is an abort rather than a normal termination -- we've not asserted dsack
								refresh_pending <= 1'b1; // request a refresh to as a simple way to clear up
							end
							dsack <= 1'b1;
							clke <= 1'b1; // re-enable the clock
							cycle_type <= CYCLE_IDLE;
						end
					end
					
					if( cycle != 'hF ) begin // don't overflow
						cycle <= cycle + 'd1; // increment the cycle counter when in a real cycle
					end
				end	
			end
		end

	end




	assign RAMCLKE = clke;
	assign MA = ma;
	assign BA = ba;
	assign DQM = dqm;
	assign RAS = cmd[2];
	assign CAS = cmd[1];
	assign ALTUCS = 1'b0;
	assign ALTLCS = 1'b0;
	assign ALTUWE = cmd[0];
	assign ALTLWE = cmd[0];
	assign DSACK = dsack;
	
	assign CYCLE = cycle[2:0];
endmodule

