-------------------------------------------------------------------------
-- Project: SDRAM controller
-- This file: DDR physical layer (Virtex-4 specific)
--
-- Copyright (C) 2007 J. Ahrensfeld
--
-- This program 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 program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- For questions and ideas, please contact the author at jens@jayfield.org
--
--------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

use work.sdram_const.all;
use work.sdram_config.all;
use work.sdram_types.all;

entity sdram_phy is
	Generic
	(
		F_SDRCLK_IN	 	: real		:= 100.0;
		F_SDRCLK	 	: real		:= 100.0;
		TPD_BOARD_SIM	: time		:= 0 ns
	);
	Port
	(
		rst				: in  STD_LOGIC;
		clk				: in  STD_LOGIC;
		clk_fb			: in  STD_LOGIC;
		clk0_out		: out STD_LOGIC;
		rst0_out		: out STD_LOGIC;
		phy_in			: in  phy_in_t;
		phy_out			: out phy_out_t;
		phy_ctrl		: in  phy_ctrl_t;
		part_clk		: out STD_LOGIC;
		part_dqm		: out unsigned(PART_DM_WIDTH-1 downto 0);
		part_data		: inout unsigned(PART_DATA_WIDTH-1 downto 0);
		part_ba			: out unsigned(PART_BANK_WIDTH-1 downto 0);
		part_addr		: out unsigned(PART_ADDR_WIDTH-1 downto 0);
		part_cs_n		: out STD_LOGIC;
		part_we_n		: out STD_LOGIC;
		part_cas_n		: out STD_LOGIC;
		part_ras_n		: out STD_LOGIC;
		part_cke		: out STD_LOGIC
	);
end sdram_phy;

architecture tech of sdram_phy is

	signal data_reg_r		: unsigned(PART_DATA_WIDTH-1 downto 0);
	signal data_reg_w		: unsigned(PART_DATA_WIDTH-1 downto 0);
	signal dm_reg_w			: unsigned(PART_DM_WIDTH-1 downto 0);
	signal part_ctrl_reg	: part_ctrl_t;
	signal we_reg			: std_logic;
	
	signal clk0				: std_logic;
	signal clk0_temp		: std_logic;
	signal rst0				: std_logic;
	signal clk0_rd			: std_logic;

	signal locked			: std_logic;  
	signal error			: std_logic;  
	signal reset_counter	: unsigned(7 downto 0) := (others => '1');
	signal reset			: STD_LOGIC := '1';

	signal re_cl_pipe		: unsigned(TCAS+1 downto 0);	-- +2 = number of pipe registers used here
	
	type u_tag_array_t is array (natural range 0 to TCAS+2) of user_tag_t;
	signal u_tag_pipe : u_tag_array_t;
	
	attribute KEEP : string;
	attribute KEEP of clk0		: signal is "TRUE";

	attribute KEEP of clk0_rd	: signal is "TRUE";

begin

--	clk0_out	<= clk0_temp;
	clk0_out	<= clk0_temp after 10 ps;		-- fix delta delay
	clk0		<= clk0_temp; -- after 10 ps;		-- fix delta delay
	rst0_out	<= rst0;
--	clk0_rd		<= clk_fb;			-- no feedback pin on DE0-Nano, use shifted clk1_0_out from clock gen

	phy_out.rd_data_we	<= re_cl_pipe(re_cl_pipe'left);
	phy_out.tag_rd		<= u_tag_pipe(u_tag_pipe'high);
	phy_out.tag_wr		<= u_tag_pipe(0);
	
------------------------------------------------------------------------------------------------------------------------------------------------
inst_sdram_clk : entity work.sdram_clk
	GENERIC MAP
	(
		F_SDRCLK_IN				=> F_SDRCLK_IN,
		F_SDRCLK_OUT			=> F_SDRCLK,
		clk0_out_phaseshift_ps	=> 0,
		clk1_out_phaseshift_ps	=> 3000
	)
	PORT MAP 
	(
		-- Clocks and Reset
		rst				=> rst,   -- external async reset, low active
		clk_in			=> clk,   -- system clock (e.g. 100MHz), from board
		clk_fb_in		=> clk_fb,   -- feedback clock
		clk0_0_out		=> clk0_temp,  -- System clock #0, dcm#0 output 0°
		clk1_0_out		=> clk0_rd,  -- System clock #1 (e.g. clock for DDR-SDRAM data capture), dcm#1 output 0°
		locked_out		=> locked  -- DCM locked status
	);

rst0_gen:
	process(locked, clk0)
	begin
		if locked = '0' then
			rst0			<= '1';
			reset_counter	<= (others => '1');
		elsif rising_edge(clk0) then
			if (reset_counter /= 0) then
				reset_counter <= reset_counter - 1;
			else
				rst0		<= '0';
			end if;
		end if;
	end process;
	
------------------------------------------------------------------------------------------------------------------------------------------------
utag_pipe:
	process(clk0)
	begin
		if rising_edge(clk0) then
			for i in u_tag_pipe'length-1 downto 1 loop
				u_tag_pipe(i) <= u_tag_pipe(i-1);
			end loop;	
			if phy_ctrl.utag_we = '1' then
				u_tag_pipe(0) <= phy_ctrl.u_tag;
			end if;	
		end if;
	end process;

re_pipe:
	process(clk0)
	begin
		if rising_edge(clk0) then
			if rst0 = '1' then
				re_cl_pipe	<= (others => '0');
			else
				for i in re_cl_pipe'length-1 downto 1 loop
					re_cl_pipe(i) <= re_cl_pipe(i-1);
				end loop;	
				re_cl_pipe(0) <= phy_ctrl.re;
			end if;
		end if;
	end process;
	
------------------------------------------------------------------------------------------------------------------------------------------------
-- SDRAM Clock
part_clk		<= clk0 after TPD_BOARD_SIM;

------------------------------------------------------------------------------------------------------------------------------------------------
-- Data OUT FFs
	process (clk0) is
	begin  
		if falling_edge(clk0) then
			part_data <= (others => 'Z') after TPD_BOARD_SIM;
			if we_reg = '1' then
				part_data	<= data_reg_w after TPD_BOARD_SIM;
			end if;
		end if;
	end process;

------------------------------------------------------------------------------------------------------------------------------------------------
-- Data-mask OUT DDR-FFs
	process (clk0) is
	begin  
		if falling_edge(clk0) then
			part_dqm	<= dm_reg_w after TPD_BOARD_SIM;
		end if;
	end process;

------------------------------------------------------------------------------------------------------------------------------------------------
	process (clk0) is
	begin  
		if rising_edge(clk0) then
			if rst0 = '1' then
				part_ctrl_reg.cmd		<= COMMAND(SD_DESELECT); 
				part_ctrl_reg.ba		<= (others=>'0');
				part_ctrl_reg.addr		<= (others=>'0');
				part_ctrl_reg.cke		<= '0';
			else
				part_ctrl_reg 		<= phy_ctrl.part;
				dm_reg_w			<= (others=>'0');
				if (phy_ctrl.we = '1') then
					data_reg_w			<= phy_in.wr_data;
					dm_reg_w			<= phy_in.wr_dm;
				end if;
				we_reg				<= phy_ctrl.we;
			end if;
		end if;
	end process;

	phy_out.wr_data_re	<= phy_ctrl.we;
	
	process (clk0) is
	begin  
		if falling_edge(clk0) then
			part_ras_n		<= part_ctrl_reg.cmd.ras_n after TPD_BOARD_SIM;
			part_cas_n		<= part_ctrl_reg.cmd.cas_n after TPD_BOARD_SIM;
			part_we_n 		<= part_ctrl_reg.cmd.we_n after TPD_BOARD_SIM;
			part_cs_n 		<= part_ctrl_reg.cmd.cs_n after TPD_BOARD_SIM;
			part_ba   		<= part_ctrl_reg.ba after TPD_BOARD_SIM;
			part_addr    	<= part_ctrl_reg.addr after TPD_BOARD_SIM;
			part_cke  		<= part_ctrl_reg.cke after TPD_BOARD_SIM;
		end if;
	end process;

-----------------------------------------------------------------
-- READ DATA Processing
-----------------------------------------------------------------
data_sample_stage:
	process (clk0_rd)
	begin
		if rising_edge(clk0_rd) then
			data_reg_r	<= part_data;
		end if;
	end process;

register_data_out:
	process (clk0)
	begin
		if rising_edge(clk0) then
			phy_out.rd_data		<= data_reg_r;
		end if;
	end process;

------------------------------------------------------------------------------------------
end tech;
