// ZX-Evo Base Configuration (c) NedoPC 2008,2009,2010,2011,2012,2013,2014
//
// DRAM controller. performs accesses to DRAM.

/*
    This file is part of ZX-Evo Base Configuration firmware.

    ZX-Evo Base Configuration firmware 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.

    ZX-Evo Base Configuration firmware is distributed in the hope that
    it will be useful, but 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.

    You should have received a copy of the GNU General Public License
    along with ZX-Evo Base Configuration firmware.
    If not, see <http://www.gnu.org/licenses/>.
*/


//
//
// state:          | RD1   | RD2   | RD3   | RD4   | WR1   | WR2   | WR3   | WR4   | RFSH1 | RFSH2 | RFSH3 | RFSH4 |
// clk: ___/```\___/```\___/```\___/```\___/```\___/```\___/```\___/```\___/```\___/```\___/```\___/```\___/```\___/```\__
//                 |      READ CYCLE               |      WRITE CYCLE              |      REFRESH CYCLE            |
// ras: ```````````````````\_______________/```````````````\_______________/```````````````````````\_______________/
// cas: ```````````````````````````\_______________/```````````````\_______________/```````\_______________/````````
// ra:                 |  row  | column|               |  row  | column|
// rd:     XXXXXXXXXXXXXXXXXXXXXXXXX<read data read|  write data write data write  |
// rwe:   `````````````````````````````````````````\_______________________________/````````````````````````````````
// req:  __/```````\_______________________/```````\________________________________________________________________
// rnw:  XX/```````\XXXXXXXXXXXXXXXXXXXXXXX\_______/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// cbeg: __________/```````\_______________________/```````\_______________________/```````\_______________________/
// rrdy: __________________________________/```````\________________________________________________________________
// addr: XX< addr  >XXXXXXXXXXXXXXXXXXXXXXX< addr  >XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//wrdata:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX< write >XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//rddata:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX< read  >XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//
// comments:
// rucas_n, rlcas_n, rras0_n, rras1_n, rwe_n could be made 'fast output register'
// ra[] couldn't be such in acex1k, because output registers could be all driven only by
//  single clock polarity (and here they are driven by negative edge, while CAS/RAS by positive)
//
// rst_n is resynced before use and acts as req inhibit. so while in reset, dram regenerates and isn't corrupted

`include "../include/tune.v"

module dram(

	input clk,
	input rst_n, // shut down accesses, remain refresh

	output reg [9:0] ra, // to the DRAM pins
	inout     [15:0] rd, // .              .
	                     // .              .
	output reg rwe_n,    // .              .
	output reg rucas_n,  // .              .
	output reg rlcas_n,  // .              .
	output reg rras0_n,  // .              .
	output reg rras1_n,  // to the DRAM pins

	input [20:0] addr, // access address of 16bit word: addr[0] selects between rras0_n and rras1_n,
	                   // addr[10:1] goes to row address, addr[20:11] goes to column address

	input req,         // request for read/write cycle
	input rnw,         // READ/nWRITE (=1: read, =0: write)

	output reg cbeg,       // cycle begin (any including refresh), can be used for synchronizing
	output reg rrdy,       // Read data ReaDY

	output reg [15:0] rddata, // data just read

	input  [15:0] wrdata, // data to be written
	input   [1:0] bsel    // positive byte select for write: bsel[0] is for wrdata[7:0], bsel[1] is for wrdata[15:8]


);

	reg [1:0] rst_sync;
	wire reset;
	wire int_req;

	reg [20:0] int_addr;
	reg [15:0] int_wrdata;
	reg  [1:0] int_bsel;


	reg rfsh_alt; // we must alternate chips in refresh cycles to lower total heating



	reg [3:0] state;
	reg [3:0] next_state;

	localparam RD1   = 0;
	localparam RD2   = 1;
	localparam RD3   = 2;
	localparam RD4   = 3;
	localparam WR1   = 4;
	localparam WR2   = 5;
	localparam WR3   = 6;
	localparam WR4   = 7;
	localparam RFSH1 = 8;
	localparam RFSH2 = 9;
	localparam RFSH3 = 10;
	localparam RFSH4 = 11;



	initial
	begin
		state = RFSH1; // for simulation only!
		rfsh_alt = 1'b0;
	end

/*
`ifdef SIMULATE
	always @(posedge clk)
	begin
		if( req && !rnw && (state==RD4 || state==WR4 || state==RFSH4) )
		begin
			$display("written word %x mask %x to address %x",wrdata&{ {8{bsel[1]}}, {8{bsel[0]}} },{ {8{bsel[1]}}, {8{bsel[0]}} },addr);
		end
	end
`endif
*/

	always @(posedge clk)
	begin
		state <= next_state;
	end

	always @*
		case( state )

		RD1:
			next_state = RD2;
		RD2:
			next_state = RD3;
		RD3:
			next_state = RD4;
		RD4:
			if( !int_req )
				next_state = RFSH1;
			else
				next_state = rnw?RD1:WR1;

		WR1:
			next_state = WR2;
		WR2:
			next_state = WR3;
		WR3:
			next_state = WR4;
		WR4:
			if( !int_req )
				next_state = RFSH1;
			else
				next_state = rnw?RD1:WR1;


		RFSH1:
			next_state = RFSH2;
		RFSH2:
			next_state = RFSH3;
		RFSH3:
			next_state = RFSH4;
		RFSH4:
			if( !int_req )
				next_state = RFSH1;
			else
				next_state = rnw?RD1:WR1;

		endcase


	// incoming data latching
	always @(posedge clk)
	begin
		if( (state==RD4) || (state==WR4) || (state==RFSH4) )
		begin
			int_addr   <= addr;
			int_wrdata <= wrdata;
			int_bsel   <= bsel;
		end
	end

	// WE control
	always @(posedge clk)
	begin
		if( (next_state==WR1) || (next_state==WR2) || (next_state==WR3) || (next_state==WR4) )
			rwe_n <= 1'b0;
		else
			rwe_n <= 1'b1;
	end


	// RAS/CAS sequencing
	always @(posedge clk)
	begin
		case( state )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RD1:
		begin
			rras0_n <= int_addr[0];
			rras1_n <= ~int_addr[0];
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RD2:
		begin
			rucas_n <= 1'b0;
			rlcas_n <= 1'b0;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RD3:
		begin
			rras0_n <= 1'b1;
			rras1_n <= 1'b1;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RD4:
		begin
			rras0_n <= 1'b1;
			rras1_n <= 1'b1;
			rucas_n <= 1'b1;
			rlcas_n <= 1'b1;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		WR1:
		begin
			rras0_n <= int_addr[0];
			rras1_n <= ~int_addr[0];
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		WR2:
		begin
			rucas_n <= ~int_bsel[1];
			rlcas_n <= ~int_bsel[0];
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		WR3:
		begin
			rras0_n <= 1'b1;
			rras1_n <= 1'b1;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		WR4:
		begin
			rras0_n <= 1'b1;
			rras1_n <= 1'b1;
			rucas_n <= 1'b1;
			rlcas_n <= 1'b1;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RFSH1:
		begin
			rucas_n <= 1'b0;
			rlcas_n <= 1'b0;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RFSH2:
		begin
			rras0_n <=  rfsh_alt;
			rras1_n <= ~rfsh_alt;

			rfsh_alt <= ~rfsh_alt;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RFSH3:
		begin
			rucas_n <= 1'b1;
			rlcas_n <= 1'b1;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		RFSH4:
		begin
			rras0_n <= 1'b1;
			rras1_n <= 1'b1;
			rucas_n <= 1'b1;
			rlcas_n <= 1'b1;
		end
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		endcase
	end


	// row/column address multiplexing
	always @(negedge clk)
	begin
		if( (state==RD1) || (state==WR1) )
			ra <= int_addr[10:1];
		else
			ra <= int_addr[20:11];
	end


	// DRAM data bus control
	assign rd = rwe_n ? 16'hZZZZ : int_wrdata;


	// read data from DRAM
	always @(posedge clk)
	begin
		if( state==RD3 )
			rddata <= rd;
	end


	// cbeg and rrdy control
	always @(posedge clk)
	begin
		if( (state==RD4) || (state==WR4) || (state==RFSH4) )
			cbeg <= 1'b1;
		else
			cbeg <= 1'b0;


            if( state==RD3 )
            	rrdy <= 1'b1;
            else
            	rrdy <= 1'b0;
	end


	// reset must be synchronous here in order to preserve
	// DRAM state while other modules reset, but we have only
	// asynchronous one globally. so we must re-synchronize it
	// and use it as 'DRAM operation enable'. when in reset,
	// controller ignores req signal and generates only refresh cycles
	always @(posedge clk)
		rst_sync[1:0] <= { rst_sync[0], ~rst_n };

	assign reset = rst_sync[1];

	assign int_req = req & (~reset);

endmodule
