-------------------------------------------------------------------------
-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL
-- This file: cpu_embedded using cpu_core and rom
--
-- 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.fifo_ctrl_pkg.all;
use work.sdram_config.all;
use work.sdram_types.all;

entity sdram_ctrl_frontend is
	Generic
	(
		BL 				: natural := 2;
		f_sysclk 		: natural := 100E6;
		read_phaseshift : integer := 0;
		fifo_depth		: integer := 4
	);
	Port
	(
		sys_rst_in			: in  STD_LOGIC;
		sys_clk_in			: in  STD_LOGIC;
		sys_rst_out			: out  STD_LOGIC;
		sys_clk_out			: out  STD_LOGIC;
		sys_clk_fb			: in  STD_LOGIC;
		
		-- User interface
		busy				: out STD_LOGIC;
		en					: in  STD_LOGIC;
		r_wn				: in  STD_LOGIC;
		be					: in unsigned(SDR_DM_WIDTH-1 downto 0);
		addr				: in  user_addr_t;
		din					: in unsigned(SDR_DATA_WIDTH-1 downto 0);
		dout				: out unsigned(SDR_DATA_WIDTH-1 downto 0);
		dout_re				: in  STD_LOGIC;
		dout_vld			: out STD_LOGIC;
		
		-- SDRAM signals
		sd_clk_p			: out  STD_LOGIC;
		sd_clk_n			: out  STD_LOGIC;
		sd_cke				: out  STD_LOGIC;
		sd_cs_n				: out  STD_LOGIC;
		sd_cas_n			: out  STD_LOGIC;
		sd_ras_n			: out  STD_LOGIC;
		sd_we_n				: out  STD_LOGIC;
		sd_addr				: out  sdr_addr_t;
		sd_ba				: out  sdr_ba_t;
		sd_dm				: out unsigned(DDR_DM_WIDTH-1 downto 0);
		sd_dqs				: inout unsigned(DDR_DQS_WIDTH-1 downto 0);
		sd_data				: inout unsigned(DDR_DATA_WIDTH-1 downto 0)
		
	);
end sdram_ctrl_frontend;

architecture struct of sdram_ctrl_frontend is

	-- Number of user data words for simulation
	signal rst_out			: std_logic;  
	signal clk_out			: std_logic;  

	signal u_addr			: user_addr_t;
	signal u_tag_in			: user_tag_t;
	signal u_tag_rd			: user_tag_t;
	signal u_tag_wr			: user_tag_t;
	signal u_cmd			: user_cmd_t;
	signal u_cmd_we			: std_logic;
	signal u_busy			: std_logic;
	signal u_data_w			: unsigned(SDR_DATA_WIDTH-1 downto 0);
	signal u_dm_wr_in		: unsigned(SDR_DM_WIDTH-1 downto 0);
	signal u_dm_rd_in		: unsigned(SDR_DM_WIDTH-1 downto 0);
	signal u_dm_rd_out		: unsigned(SDR_DM_WIDTH-1 downto 0);
	signal u_data_r			: unsigned(SDR_DATA_WIDTH-1 downto 0);
	signal u_data_vld		: std_logic;
	signal u_req_wr			: std_logic;
	signal rdy				: std_logic;

	constant CAT_FIFO_WIDTH			: integer := user_cmd_t'length + user_tag_t'length + DDR_ROW_ADDR_WIDTH + DDR_BANK_WIDTH + DDR_COL_ADDR_WIDTH + SDR_DM_WIDTH;
	signal cat_fifo_din				: unsigned(CAT_FIFO_WIDTH-1 downto 0);
	signal cat_fifo_dout			: unsigned(CAT_FIFO_WIDTH-1 downto 0);
	signal cat_fifo_re				: std_logic;
	signal cat_fifo_we				: std_logic;
	signal cat_fifo_full			: std_logic;
	signal cat_fifo_empty			: std_logic;
--	signal cat_fifo_almost_full		: std_logic;
--	signal cat_fifo_almost_empty	: std_logic;

	signal write_fifo_din			: unsigned(SDR_DATA_WIDTH+SDR_DM_WIDTH-1 downto 0);
	signal write_fifo_dout			: unsigned(SDR_DATA_WIDTH+SDR_DM_WIDTH-1 downto 0);
	signal write_fifo_re			: std_logic;
	signal write_fifo_we			: std_logic;
	signal write_fifo_full			: std_logic;
	signal write_fifo_empty			: std_logic;
--	signal write_fifo_almost_full	: std_logic;
--	signal write_fifo_almost_empty	: std_logic;

	signal read_fifo_din			: unsigned(SDR_DATA_WIDTH+SDR_DM_WIDTH-1 downto 0);
	signal read_fifo_dout			: unsigned(SDR_DATA_WIDTH+SDR_DM_WIDTH-1 downto 0);
	signal read_fifo_re				: std_logic;
	signal read_fifo_we				: std_logic;
	signal read_fifo_full			: std_logic;
	signal read_fifo_empty			: std_logic;
--	signal read_fifo_almost_full	: std_logic;
--	signal read_fifo_almost_empty	: std_logic;

	alias cmd_fifo_in is cat_fifo_din(CAT_FIFO_WIDTH-1 downto CAT_FIFO_WIDTH-user_cmd_t'length);
	alias tag_fifo_in is cat_fifo_din(CAT_FIFO_WIDTH-user_cmd_t'length-1 downto CAT_FIFO_WIDTH-user_cmd_t'length-user_tag_t'length);
	alias addr_fifo_in is cat_fifo_din(CAT_FIFO_WIDTH-user_cmd_t'length-user_tag_t'length-1 downto SDR_DM_WIDTH);
	alias dm_rd_fifo_in is cat_fifo_din(SDR_DM_WIDTH-1 downto 0);
	alias cmd_fifo_out is cat_fifo_dout(CAT_FIFO_WIDTH-1 downto CAT_FIFO_WIDTH-user_cmd_t'length);
	alias tag_fifo_out is cat_fifo_dout(CAT_FIFO_WIDTH-user_cmd_t'length-1 downto CAT_FIFO_WIDTH-user_cmd_t'length-user_tag_t'length);
	alias addr_fifo_out is cat_fifo_dout(CAT_FIFO_WIDTH-user_cmd_t'length-user_tag_t'length-1 downto SDR_DM_WIDTH);
	alias dm_rd_fifo_out is cat_fifo_dout(SDR_DM_WIDTH-1 downto 0);

	alias write_fifo_dm_in is write_fifo_din(write_fifo_din'length-1 downto write_fifo_din'length-SDR_DM_WIDTH);
	alias write_fifo_data_in is write_fifo_din(write_fifo_din'length-SDR_DM_WIDTH-1 downto 0);
	alias write_fifo_dm_out is write_fifo_dout(write_fifo_dout'length-1 downto write_fifo_dout'length-SDR_DM_WIDTH);
	alias write_fifo_data_out is write_fifo_dout(write_fifo_dout'length-SDR_DM_WIDTH-1 downto 0);

	alias read_fifo_dm_in is read_fifo_din(read_fifo_din'length-1 downto read_fifo_din'length-SDR_DM_WIDTH);
	alias read_fifo_data_in is read_fifo_din(read_fifo_din'length-SDR_DM_WIDTH-1 downto 0);
	alias read_fifo_dm_out is read_fifo_dout(read_fifo_dout'length-1 downto read_fifo_dout'length-SDR_DM_WIDTH);
	alias read_fifo_data_out is read_fifo_dout(read_fifo_dout'length-SDR_DM_WIDTH-1 downto 0);
	
begin 
   
	-- Instantiate synchronous FIFO
	inst_cat_fifo: entity work.fifo_sync
	GENERIC MAP
	(
		addr_width	=> fifo_depth,
		data_width	=> CAT_FIFO_WIDTH
	)
	PORT MAP
	(
		rst			=>	rst_out,
		clk			=>	clk_out,
		we			=>	cat_fifo_we,
		re			=>	cat_fifo_re,
		fifo_full	=>	cat_fifo_full,
		fifo_empty	=>	cat_fifo_empty,
		fifo_afull	=>	open,
		fifo_aempty	=>	open,
		data_w		=>  cat_fifo_din,
		data_r		=>  cat_fifo_dout
	);

	-- Instantiate synchronous FIFO
	inst_write_fifo: entity work.fifo_sync
	GENERIC MAP
	(
		addr_width	=> fifo_depth,
		data_width	=> 2*DDR_DATA_WIDTH + SDR_DM_WIDTH
	)
	PORT MAP
	(
		rst			=>	rst_out,
		clk			=>	clk_out,
		we			=>	write_fifo_we,
		re			=>	write_fifo_re,
		fifo_full	=>	write_fifo_full,
		fifo_empty	=>	write_fifo_empty,
		fifo_afull	=>	open,
		fifo_aempty	=>	open,
		data_w		=>  write_fifo_din,
		data_r		=>  write_fifo_dout
	);

	-- Instantiate synchronous FIFO
	inst_read_fifo: entity work.fifo_sync
	GENERIC MAP
	(
		addr_width	=> fifo_depth,
		data_width	=> 2*DDR_DATA_WIDTH + SDR_DM_WIDTH
	)
	PORT MAP
	(
		rst			=>	rst_out,
		clk			=>	clk_out,
		we			=>	read_fifo_we,
		re			=>	read_fifo_re,
		fifo_full	=>	read_fifo_full,
		fifo_empty	=>	read_fifo_empty,
		fifo_afull	=>	open,
		fifo_aempty	=>	open,
		data_w		=>  read_fifo_din,
		data_r		=>  read_fifo_dout
	);

	-- DDR SDRAM Controller Core
	inst_sdram_ctrl : entity work.sdram_ctrl_top
	Generic map
	(
		BL 				=> BL,
		read_phaseshift => read_phaseshift,
		f_sysclk 		=> f_sysclk
	)
	Port map
	(
	
		sys_rst_in			=> sys_rst_in,
		sys_clk_in			=> sys_clk_in,

		sys_rst_out			=> rst_out,
		sys_clk_out			=> clk_out,
		sys_clk_fb			=> sys_clk_fb,
		
		-- User interface
		u_data_vld			=> u_data_vld,
		u_data_req_w		=> u_req_wr,
		u_data_req_r		=> open,
		u_tag_in			=> u_tag_in,
		u_tag_rd			=> u_tag_rd,
		u_tag_wr			=> u_tag_wr,
		u_busy				=> u_busy,
		u_addr				=> u_addr,
		u_cmd				=> u_cmd,
		u_cmd_we			=> u_cmd_we,
		u_data_wr			=> u_data_w,
		u_data_rd			=> u_data_r,
		u_dm_wr_in			=> u_dm_wr_in,
		u_dm_rd_in			=> u_dm_rd_in,
		u_dm_rd_out			=> u_dm_rd_out,
		
		-- SDRAM signals
		sd_clk_p			=> sd_clk_p,
		sd_clk_n			=> sd_clk_n,
		sd_cke				=> sd_cke,
		sd_cs_n				=> sd_cs_n,
		sd_cas_n			=> sd_cas_n,
		sd_ras_n			=> sd_ras_n,
		sd_we_n				=> sd_we_n,
		sd_addr				=> sd_addr,
		sd_ba				=> sd_ba,
		sd_dm				=> sd_dm,
		sd_dqs				=> sd_dqs,
		sd_data				=> sd_data
		
	);
   
------------------------------------------------------------------------------------------
	sys_rst_out			<= rst_out;
	sys_clk_out			<= clk_out;
	busy				<= not rdy;
	rdy					<= not (cat_fifo_full or write_fifo_full or read_fifo_full);
	write_fifo_we 		<= en and not r_wn and rdy;
	write_fifo_data_in	<= din;
	write_fifo_dm_in	<= not be;
	cat_fifo_we			<= en and rdy;
	cmd_fifo_in			<= UCMD_WRITE when r_wn = '0' else UCMD_READ;
	addr_fifo_in		<= addr;
	dm_rd_fifo_in		<= not be;
	tag_fifo_in			<= (others=>'0');
	dout				<= read_fifo_data_out;
	dout_vld			<= not read_fifo_empty;
	
	u_cmd_we			<= (not cat_fifo_empty) and (not u_busy);
	u_cmd				<= cmd_fifo_out;
	u_addr				<= addr_fifo_out;
	u_dm_rd_in			<= dm_rd_fifo_out;
	u_dm_wr_in			<= write_fifo_dm_out;
	u_tag_in			<= tag_fifo_out;
	
	write_fifo_re		<= u_req_wr and (not write_fifo_empty);
	cat_fifo_re			<= u_cmd_we;
	u_data_w			<= write_fifo_data_out;
	read_fifo_re 		<= (not read_fifo_empty) and dout_re;
	read_fifo_we		<= u_data_vld;
	read_fifo_data_in	<= u_data_r;	
	read_fifo_dm_in		<= u_dm_rd_out;

	
end architecture struct;
