library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;

library work;
use work.wishbone_types.all;
use work.sim_console.all;

entity litedram_wrapper is
    generic (
	DRAM_ABITS     : positive;
	DRAM_ALINES    : positive
	);
    port(
	-- LiteDRAM generates the system clock and reset
	-- from the input clkin
	clk_in        : in std_ulogic;
	rst           : in std_ulogic;
	system_clk    : out std_ulogic;
	system_reset  : out std_ulogic;
	core_alt_reset : out std_ulogic;
	pll_locked    : out std_ulogic;

	-- Wishbone ports:
	wb_in         : in wishbone_master_out;
	wb_out        : out wishbone_slave_out;
	wb_is_ctrl    : in std_ulogic;
	wb_is_init    : in std_ulogic;

	-- Init core serial debug
	serial_tx     : out std_ulogic;
	serial_rx     : in std_ulogic;

	-- Misc
	init_done     : out std_ulogic;
	init_error    : out std_ulogic;

	-- DRAM wires
	ddram_a       : out std_ulogic_vector(DRAM_ALINES-1 downto 0);
	ddram_ba      : out std_ulogic_vector(2 downto 0);
	ddram_ras_n   : out std_ulogic;
	ddram_cas_n   : out std_ulogic;
	ddram_we_n    : out std_ulogic;
	ddram_cs_n    : out std_ulogic;
	ddram_dm      : out std_ulogic_vector(1 downto 0);
	ddram_dq      : inout std_ulogic_vector(15 downto 0);
	ddram_dqs_p   : inout std_ulogic_vector(1 downto 0);
	ddram_dqs_n   : inout std_ulogic_vector(1 downto 0);
	ddram_clk_p   : out std_ulogic;
	ddram_clk_n   : out std_ulogic;
	ddram_cke     : out std_ulogic;
	ddram_odt     : out std_ulogic;
	ddram_reset_n : out std_ulogic
	);
end entity litedram_wrapper;

architecture behaviour of litedram_wrapper is

    component litedram_core port (
	clk                    : in std_ulogic;
	rst                    : in std_ulogic;
	serial_tx              : out std_ulogic;
	serial_rx              : in std_ulogic;
	pll_locked             : out std_ulogic;
	ddram_a                : out std_ulogic_vector(DRAM_ALINES-1 downto 0);
	ddram_ba               : out std_ulogic_vector(2 downto 0);
	ddram_ras_n            : out std_ulogic;
	ddram_cas_n            : out std_ulogic;
	ddram_we_n             : out std_ulogic;
	ddram_cs_n             : out std_ulogic;
	ddram_dm               : out std_ulogic_vector(1 downto 0);
	ddram_dq               : inout std_ulogic_vector(15 downto 0);
	ddram_dqs_p            : inout std_ulogic_vector(1 downto 0);
	ddram_dqs_n            : inout std_ulogic_vector(1 downto 0);
	ddram_clk_p            : out std_ulogic;
	ddram_clk_n            : out std_ulogic;
	ddram_cke              : out std_ulogic;
	ddram_odt              : out std_ulogic;
	ddram_reset_n          : out std_ulogic;
	init_done              : out std_ulogic;
	init_error             : out std_ulogic;
	user_clk               : out std_ulogic;
	user_rst               : out std_ulogic;
	user_port_native_0_cmd_valid   : in std_ulogic;
	user_port_native_0_cmd_ready   : out std_ulogic;
	user_port_native_0_cmd_we      : in std_ulogic;
	user_port_native_0_cmd_addr    : in std_ulogic_vector(DRAM_ABITS-1 downto 0);
	user_port_native_0_wdata_valid : in std_ulogic;
	user_port_native_0_wdata_ready : out std_ulogic;
	user_port_native_0_wdata_we    : in std_ulogic_vector(15 downto 0);
	user_port_native_0_wdata_data  : in std_ulogic_vector(127 downto 0);
	user_port_native_0_rdata_valid : out std_ulogic;
	user_port_native_0_rdata_ready : in std_ulogic;
	user_port_native_0_rdata_data  : out std_ulogic_vector(127 downto 0)
	);
    end component;
    
    signal user_port0_cmd_valid		: std_ulogic;
    signal user_port0_cmd_ready		: std_ulogic;
    signal user_port0_cmd_we		: std_ulogic;
    signal user_port0_cmd_addr		: std_ulogic_vector(DRAM_ABITS-1 downto 0);
    signal user_port0_wdata_valid	: std_ulogic;
    signal user_port0_wdata_ready	: std_ulogic;
    signal user_port0_wdata_we		: std_ulogic_vector(15 downto 0);
    signal user_port0_wdata_data	: std_ulogic_vector(127 downto 0);
    signal user_port0_rdata_valid	: std_ulogic;
    signal user_port0_rdata_ready	: std_ulogic;
    signal user_port0_rdata_data	: std_ulogic_vector(127 downto 0);

    signal ad3                          : std_ulogic;

    signal dram_user_reset              : std_ulogic;

    type state_t is (CMD, MWRITE, MREAD);
    signal state : state_t;

begin

    -- Address bit 3 selects the top or bottom half of the data
    -- bus (64-bit wishbone vs. 128-bit DRAM interface)
    --
    ad3 <= wb_in.adr(3);

    -- DRAM interface signals
    user_port0_cmd_valid <= (wb_in.cyc and wb_in.stb and not wb_is_ctrl and not wb_is_init)
			    when state = CMD else '0';
    user_port0_cmd_we <= wb_in.we when state = CMD else '0';
    user_port0_wdata_valid <= '1' when state = MWRITE else '0';
    user_port0_rdata_ready <= '1' when state = MREAD else '0';
    user_port0_cmd_addr <= wb_in.adr(DRAM_ABITS+3 downto 4);
    user_port0_wdata_data <= wb_in.dat & wb_in.dat;
    user_port0_wdata_we <= wb_in.sel & "00000000" when ad3 = '1' else
			   "00000000" & wb_in.sel;

    -- Wishbone out signals. CSR and init memory do nothing, just ack
    wb_out.ack <= '1' when (wb_is_ctrl = '1' or wb_is_init = '1') else
		  user_port0_wdata_ready when state = MWRITE else
		  user_port0_rdata_valid when state = MREAD else '0';
    wb_out.dat <= (others => '0') when (wb_is_ctrl = '1' or wb_is_init = '1') else
		  user_port0_rdata_data(127 downto 64) when ad3 = '1' else
		  user_port0_rdata_data(63 downto 0);
    wb_out.stall <= '0' when wb_in.cyc = '0' else not wb_out.ack;

    -- Reset, lift it when init done, no alt core reset
    system_reset <= dram_user_reset or not init_done;
    core_alt_reset <= '0';

    -- State machine
    sm: process(system_clk)
    begin
	
	if rising_edge(system_clk) then
	    if dram_user_reset = '1' then
		state <= CMD;
	    else
		case state is
		when CMD =>
		    if (user_port0_cmd_ready and user_port0_cmd_valid) = '1' then
			state <= MWRITE when wb_in.we = '1' else MREAD;
		    end if;
		when MWRITE =>
		    if user_port0_wdata_ready = '1' then
			state <= CMD;
		    end if;
		when MREAD =>
		    if user_port0_rdata_valid = '1' then
			state <= CMD;
		    end if;
		end case;
	    end if;
	end if;
    end process;

    litedram: litedram_core
	port map(
	    clk => clk_in,
	    rst => rst,
	    serial_tx => serial_tx,
	    serial_rx => serial_rx,
	    pll_locked => pll_locked,
	    ddram_a => ddram_a,
	    ddram_ba => ddram_ba,
	    ddram_ras_n => ddram_ras_n,
	    ddram_cas_n => ddram_cas_n,
	    ddram_we_n => ddram_we_n,
	    ddram_cs_n => ddram_cs_n,
	    ddram_dm => ddram_dm,
	    ddram_dq => ddram_dq,
	    ddram_dqs_p => ddram_dqs_p,
	    ddram_dqs_n => ddram_dqs_n,
	    ddram_clk_p => ddram_clk_p,
	    ddram_clk_n => ddram_clk_n,
	    ddram_cke => ddram_cke,
	    ddram_odt => ddram_odt,
	    ddram_reset_n => ddram_reset_n,
	    init_done => init_done,
	    init_error => init_error,
	    user_clk => system_clk,
	    user_rst => dram_user_reset,
	    user_port_native_0_cmd_valid => user_port0_cmd_valid,
	    user_port_native_0_cmd_ready => user_port0_cmd_ready,
	    user_port_native_0_cmd_we => user_port0_cmd_we,
	    user_port_native_0_cmd_addr => user_port0_cmd_addr,
	    user_port_native_0_wdata_valid => user_port0_wdata_valid,
	    user_port_native_0_wdata_ready => user_port0_wdata_ready,
	    user_port_native_0_wdata_we => user_port0_wdata_we,
	    user_port_native_0_wdata_data => user_port0_wdata_data,
	    user_port_native_0_rdata_valid => user_port0_rdata_valid,
	    user_port_native_0_rdata_ready => user_port0_rdata_ready,
	    user_port_native_0_rdata_data => user_port0_rdata_data
	    );

end architecture behaviour;
