-------------------------------------------------------------------------
-- 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_const.all;
use work.sdram_config.all;
use work.sdram_types.all;

entity sdram_ctrl_frontend_wb64 is
	Generic
	(
		F_SYSCLK		: real		:= 100.0;
		F_SDRCLK	 	: real		:= 100.0;
		FIFO_DEPTH		: integer	:= 4
	);
	Port
	(
		RST_I				: in  STD_LOGIC;
		CLK_I				: in  STD_LOGIC;
		SDRAM_RST0			: in  STD_LOGIC;
		SDRAM_CLK0			: in  STD_LOGIC;
		
		CYC_I				: in  STD_LOGIC;
		STB_I				: in  STD_LOGIC;
		SEL_I				: in  unsigned(BUS_DM_WIDTH-1 downto 0);
		WE_I				: in  STD_LOGIC;
		ACK_O				: out STD_LOGIC;
		MRDY_I				: in  STD_LOGIC;
		SRDY_O				: out STD_LOGIC;
		ADDR_I				: in  unsigned(31 downto 0);
		DAT_I				: in  unsigned(BUS_DATA_WIDTH-1 downto 0);
		DAT_O				: out unsigned(BUS_DATA_WIDTH-1 downto 0);

		-- PHY interface
		phy_ctrl			: out phy_ctrl_t;
		phy_in				: out phy_in_t;
		phy_out				: in  phy_out_t

	);
end sdram_ctrl_frontend_wb64;

architecture struct of sdram_ctrl_frontend_wb64 is

	signal u_addr			: user_addr_t;
	signal u_tag_in			: user_tag_t;
	signal u_cmd			: user_cmd_t;
	signal u_cmd_we			: std_logic;
	signal u_busy			: std_logic;
	signal rdy				: std_logic;

	constant CAT_FIFO_WIDTH			: integer := user_cmd_t'length + user_tag_t'length + PART_ROW_ADDR_WIDTH + PART_BANK_WIDTH + PART_COL_ADDR_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 write_fifo_din			: unsigned(BUS_DATA_WIDTH+BUS_DM_WIDTH-1 downto 0);
	signal write_fifo_dout			: unsigned(BUS_DATA_WIDTH+BUS_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 read_fifo_din			: unsigned(BUS_DATA_WIDTH-1 downto 0);
	signal read_fifo_dout			: unsigned(BUS_DATA_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;

	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 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 0);

	alias write_fifo_dm_in is write_fifo_din(BUS_DATA_WIDTH+BUS_DM_WIDTH-1 downto BUS_DATA_WIDTH);
	alias write_fifo_data_in is write_fifo_din(BUS_DATA_WIDTH-1 downto 0);
	alias write_fifo_dm_out is write_fifo_dout(BUS_DATA_WIDTH+BUS_DM_WIDTH-1 downto BUS_DATA_WIDTH);
	alias write_fifo_data_out is write_fifo_dout(BUS_DATA_WIDTH-1 downto 0);

	alias read_fifo_data_in is read_fifo_din(BUS_DATA_WIDTH-1 downto 0);
	alias read_fifo_data_out is read_fifo_dout(BUS_DATA_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_I,
		clk			=>	CLK_I,
		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_async
	GENERIC MAP
	(
		addr_width	=> FIFO_DEPTH,
		data_width	=> write_fifo_din'length
	)
	PORT MAP
	(
		rst			=>	SDRAM_RST0,
		clk_w		=>	CLK_I,
		clk_r		=>	SDRAM_CLK0,
		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_async
	GENERIC MAP
	(
		addr_width	=> FIFO_DEPTH,
		data_width	=> read_fifo_din'length
	)
	PORT MAP
	(
		rst			=>	SDRAM_RST0,
		clk_w		=>	SDRAM_CLK0,
		clk_r		=>	CLK_I,
		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_top : entity work.sdram_ctrl_top
	Generic map
	(
		F_SYSCLK 		=> F_SYSCLK,
		F_SDRCLK		=> F_SDRCLK,
		FIFO_DEPTH		=> FIFO_DEPTH
	)
	Port map
	(
	
		sys_rst_in			=> RST_I,
		sys_clk_in			=> CLK_I,

		sdram_rst0			=> SDRAM_RST0,
		sdram_clk0			=> SDRAM_CLK0,
		
		-- User interface
		u_tag				=> u_tag_in,
		u_busy				=> u_busy,
		u_addr				=> u_addr,
		u_cmd				=> u_cmd,
		u_cmd_we			=> u_cmd_we,
		
		-- Phy control interface
		phy_ctrl			=> phy_ctrl
		
	);

------------------------------------------------------------------------------------------
	SRDY_O				<= rdy;
	ACK_O				<= not (read_fifo_empty);
	DAT_O				<= read_fifo_data_out;
	rdy					<= not (cat_fifo_full or write_fifo_full or read_fifo_full) and CYC_I;
	write_fifo_we 		<= STB_I and WE_I and rdy;
	write_fifo_data_in	<= DAT_I;
	write_fifo_dm_in	<= not SEL_I;
	cat_fifo_we			<= STB_I and rdy;
	cmd_fifo_in			<= UCMD_WRITE when WE_I = '1' else UCMD_READ;
	addr_fifo_in		<= ADDR_I(25 downto 3) & "0";
	tag_fifo_in			<= "000" & ADDR_I(2);
	
	u_cmd_we			<= (not cat_fifo_empty);
	u_cmd				<= cmd_fifo_out;
	u_addr				<= addr_fifo_out;
	phy_in.wr_dm		<= (write_fifo_dm_out(BUS_DATA_WIDTH+BUS_DM_WIDTH-5 downto BUS_DATA_WIDTH) & write_fifo_dm_out(BUS_DATA_WIDTH+BUS_DM_WIDTH-1 downto BUS_DATA_WIDTH+4)) when phy_out.tag_wr(0) = '1' else write_fifo_dm_out(BUS_DATA_WIDTH+BUS_DM_WIDTH-1 downto BUS_DATA_WIDTH);
	u_tag_in			<= tag_fifo_out;
	phy_in.wr_data		<= (write_fifo_data_out(BUS_DATA_WIDTH/2-1 downto 0) & write_fifo_data_out(BUS_DATA_WIDTH-1 downto BUS_DATA_WIDTH/2)) when phy_out.tag_wr(0) = '1' else write_fifo_data_out(BUS_DATA_WIDTH-1 downto 0);
	
	write_fifo_re		<= phy_out.wr_data_re and (not write_fifo_empty);
	cat_fifo_re			<= u_cmd_we and (not u_busy);
	read_fifo_re 		<= (not read_fifo_empty) and MRDY_I;
	read_fifo_we		<= phy_out.rd_data_we;
	read_fifo_data_in	<= (phy_out.rd_data(BUS_DATA_WIDTH/2-1 downto 0) & phy_out.rd_data(BUS_DATA_WIDTH-1 downto BUS_DATA_WIDTH/2)) when phy_out.tag_rd(0) = '1' else phy_out.rd_data(BUS_DATA_WIDTH-1 downto 0);	

end architecture struct;
