LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

Library UNISIM;
use UNISIM.vcomponents.all;

ENTITY ssram_frontend_wb IS
	Generic
	(
		latency			: natural := 2;		-- clock cycles
		addr_width		: natural := 20;
		data_width		: natural := 32;
		parity_width	: natural := 4
	);
	Port
	(
		-- J-Bus domain
		CLK_I			: in  STD_LOGIC;
		RST_I			: in  STD_LOGIC;
		CYC_I			: in  STD_LOGIC;
		STB_I			: in  STD_LOGIC;
		SEL_I			: in  unsigned(3 downto 0);
		WE_I			: in  STD_LOGIC;
		ACK_O			: out STD_LOGIC;
		SRDY_O			: out STD_LOGIC;
		MRDY_I			: in  STD_LOGIC;
		ADDR_I			: in  unsigned(31 downto 0);
		DAT_I			: in  unsigned(31 downto 0);
		DAT_O			: out unsigned(31 downto 0);
		
		-- Sync SRAM domain
		ssram_clk_o		: out std_logic;
		ssram_clk_fb	: in  std_logic;
		ssram_cke_n		: out std_logic;
		ssram_ce_n		: out std_logic;
		ssram_oe_n		: out std_logic;
		ssram_we_n		: out std_logic;
		ssram_adv		: out std_logic;
		ssram_mode		: out std_logic;
		ssram_zz		: out std_logic;
		ssram_a			: out unsigned(addr_width-1 downto 0);
		ssram_d			: inout unsigned(data_width-1 downto 0);
		ssram_dp		: inout unsigned(parity_width-1 downto 0);
		ssram_bw_n		: out unsigned(3 downto 0)
	);
END ssram_frontend_wb;

ARCHITECTURE behavior OF ssram_frontend_wb IS

	constant topad	: time := 3 ns;
	subtype ssram_d_t is unsigned(data_width+parity_width-1 downto 0);
	type data_pipe_t is array (latency downto 0) of ssram_d_t;

	signal ssram_clk	: std_logic;
	signal dcm_locked	: std_logic;
	signal dcm_clk0		: std_logic;
	signal dcm_clk1		: std_logic;
	signal data_en		: std_logic;
	signal drive		: std_logic;
	signal vld_pipe		: unsigned(latency downto 0);
	signal drive_pipe	: unsigned(latency downto 0);
	signal data_pipe	: data_pipe_t;
	signal ssram_dout	: ssram_d_t;
	
begin

	SRDY_O							<= CYC_I and dcm_locked;
	ACK_O							<= vld_pipe(vld_pipe'left);
	DAT_O(data_width-1 downto 0)	<= ssram_d;

	data_en							<= CYC_I and STB_I;
	ssram_dout						<= "0000" & DAT_I(data_width-1 downto 0);
	drive							<= drive_pipe(drive_pipe'left);
	
	ssram_clk_o						<= ssram_clk after topad;
	ssram_cke_n						<= not dcm_locked after topad;
	ssram_oe_n						<= not vld_pipe(vld_pipe'left-1) after topad;
	
inst_clock_out : ODDR
	generic map
	(
		DDR_CLK_EDGE => "OPPOSITE_EDGE",	-- "OPPOSITE_EDGE" or "SAME_EDGE" 
		INIT => '0',						-- Initial value for Q port ('1' or '0')
		SRTYPE => "SYNC"					-- Reset Type ("ASYNC" or "SYNC")
	) 										
	port map (
		Q	=> ssram_clk,	-- 1-bit DDR output
		C	=> dcm_clk1,	-- 1-bit clock input
		CE	=> '1',			-- 1-bit clock enable input
		D1	=> '1',			-- 1-bit data input (positive edge)
		D2	=> '0',			-- 1-bit data input (negative edge)
		R	=> '0',			-- 1-bit reset input
		S	=> '0' 			-- 1-bit set input
	);

ssram_bufg: bufg
	port map
	(
		o  => dcm_clk1,  
		i  => dcm_clk0
	);

inst_DCM_BASE_0 : DCM_BASE
	generic map (
		CLKDV_DIVIDE => 2.0, -- Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5
				   --   7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0
		CLKFX_DIVIDE => 1,   -- Can be any interger from 1 to 32
		CLKFX_MULTIPLY => 4, -- Can be any integer from 2 to 32
		CLKIN_DIVIDE_BY_2 => FALSE, -- TRUE/FALSE to enable CLKIN divide by two feature
		CLKIN_PERIOD => 10.0 , -- Specify period of input clock in ns from 1.25 to 1000.00
		CLKOUT_PHASE_SHIFT => "FIXED", -- Specify phase shift mode of NONE or FIXED
		CLK_FEEDBACK => "1X",         -- Specify clock feedback of NONE or 1X
		DCM_AUTOCALIBRATION => TRUE, -- DCM calibrartion circuitry TRUE/FALSE
		DCM_PERFORMANCE_MODE => "MAX_SPEED",   -- Can be MAX_SPEED or MAX_RANGE
		DESKEW_ADJUST => "SOURCE_SYNCHRONOUS", -- SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or
									 --   an integer from 0 to 15
		DFS_FREQUENCY_MODE => "LOW",   -- LOW or HIGH frequency mode for frequency synthesis
		DLL_FREQUENCY_MODE => "LOW",   -- LOW, HIGH, or HIGH_SER frequency mode for DLL
		DUTY_CYCLE_CORRECTION => TRUE, -- Duty cycle correction, TRUE or FALSE
		FACTORY_JF => X"F0F0",          -- FACTORY JF Values Suggested to be set to X"F0F0" 
		PHASE_SHIFT => 0, -- Amount of fixed phase shift from -255 to 1023
		STARTUP_WAIT => FALSE) -- Delay configuration DONE until DCM LOCK, TRUE/FALSE
	port map (
		CLK0 => dcm_clk0,         -- 0 degree DCM CLK ouptput
		CLK180 => open,     -- 180 degree DCM CLK output
		CLK270 => open,     -- 270 degree DCM CLK output
		CLK2X => open,       -- 2X DCM CLK output
		CLK2X180 => open, -- 2X, 180 degree DCM CLK out
		CLK90 => open,       -- 90 degree DCM CLK output
		CLKDV => open,       -- Divided DCM CLK out (CLKDV_DIVIDE)
		CLKFX => open,       -- DCM CLK synthesis out (M/D)
		CLKFX180 => open, -- 180 degree CLK synthesis out
		LOCKED => dcm_locked,     -- DCM LOCK status output
		CLKFB => ssram_clk_fb,       -- DCM clock feedback
		CLKIN => CLK_I,       -- Clock input (from IBUFG, BUFG or DCM)
		RST => RST_I            -- DCM asynchronous reset input
	);


data_valid_pipeline:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if RST_I = '1' then
				vld_pipe	<= (others => '0');
			else
				vld_pipe	<= vld_pipe(vld_pipe'left-1 downto 0) & (data_en and MRDY_I and not WE_I);
			end if;
		end if;
	end process;

data_out_pipeline:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if RST_I = '1' then
				ssram_ce_n	<= '1' after topad;
				ssram_we_n	<= '1' after topad;
				ssram_adv	<= '0' after topad;
				ssram_mode	<= '0' after topad;
				ssram_zz	<= '1' after topad;
				drive_pipe	<= (others => '0');
			elsif dcm_locked = '1' then
				ssram_ce_n	<= not data_en after topad;
				ssram_we_n	<= not (WE_I and data_en) after topad;
				ssram_adv	<= '0' after topad;
				ssram_mode	<= '0' after topad;
				ssram_zz	<= '0' after topad;
				ssram_a		<= ADDR_I(addr_width-1 downto 0) after topad;
				ssram_bw_n	<= not SEL_I after topad;		
				drive_pipe	<= drive_pipe(drive_pipe'left-1 downto 0) & (data_en and WE_I);
				data_pipe	<= data_pipe(data_pipe'left-1 downto 0) & ssram_dout;
			end if;	
		end if;
	end process;
	
	ssram_d		<= data_pipe(data_pipe'left)(data_width-1 downto 0) after topad when drive = '1' else (others => 'Z') after topad;
	ssram_dp	<= data_pipe(data_pipe'left)(data_width+parity_width-1 downto data_width) after topad when drive = '1' else (others => 'Z') after topad;
	
end behavior;
