-- -----------------------------------------------------------------------------
-- Copyright (c) 2013 Benjamin Krill <benjamin@krll.de>
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.
-- -----------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.dvi_package.all;

ENTITY sig_read_tb IS
END sig_read_tb;

ARCHITECTURE rtl OF sig_read_tb IS
	constant CLK_PERIOD : time := 10 ns;
	constant CLK_PERIOD2: time := 5 ns;
	signal clk          : std_logic;
	signal ddr2_clk     : std_logic;
	signal rst          : std_logic;
	signal rst_n        : std_logic;

	signal en_stb_i     : std_logic;
	signal hsync_o      : std_logic;
	signal vsync_o      : std_logic;
	signal color_en_o   : std_logic;
	signal color_o      : color_t(COLOR_CNT-1 downto 0);
	signal ddr2_rd_cmd_en_o         : std_logic;
	signal ddr2_rd_cmd_instr_o      : std_logic_vector( 2 downto 0);
	signal ddr2_rd_cmd_bl_o         : std_logic_vector( 5 downto 0);
	signal ddr2_rd_cmd_byte_addr_o  : std_logic_vector(29 downto 0);
	signal ddr2_rd_cmd_empty_i      : std_logic;
	signal ddr2_rd_cmd_full_i       : std_logic;
	signal ddr2_rd_en_o          : std_logic;
	signal ddr2_rd_data_i        : std_logic_vector(31 downto 0);
	signal ddr2_rd_full_i        : std_logic;
	signal ddr2_rd_empty_i       : std_logic;
	signal ddr2_rd_count_i       : std_logic_vector( 6 downto 0);
	signal ddr2_rd_overflow_i    : std_logic;
	signal ddr2_rd_error_i       : std_logic;
	signal ddr2_wr_cmd_en_o         : std_logic;
	signal ddr2_wr_cmd_instr_o      : std_logic_vector( 2 downto 0);
	signal ddr2_wr_cmd_bl_o         : std_logic_vector( 5 downto 0);
	signal ddr2_wr_cmd_byte_addr_o  : std_logic_vector(29 downto 0);
	signal ddr2_wr_cmd_empty_i      : std_logic;
	signal ddr2_wr_cmd_full_i       : std_logic;
	signal ddr2_wr_en_o          : std_logic;
	signal ddr2_wr_mask_o        : std_logic_vector( 3 downto 0);
	signal ddr2_wr_data_o        : std_logic_vector(31 downto 0);
	signal ddr2_wr_full_i        : std_logic;
	signal ddr2_wr_empty_i       : std_logic;
	signal ddr2_wr_count_i       : std_logic_vector( 6 downto 0);
	signal ddr2_wr_underrun_i    : std_logic;
	signal ddr2_wr_error_i       : std_logic;
	signal wr_cnt                : unsigned( 7 downto 0);
	signal wr_lat                : unsigned( 7 downto 0);

	signal en_stb_iq    : std_logic;
	signal hsync_oq     : std_logic;
	signal vsync_oq     : std_logic;
	signal color_en_oq  : std_logic;
	signal color_oq     : color_t(COLOR_CNT-1 downto 0);

	signal req_cnt      : unsigned(15 downto 0);
	signal req_save_cnt : unsigned(15 downto 0);
	signal lat_cnt      : unsigned(15 downto 0);
	signal latency_done : std_logic;
	signal latency      : std_logic;
BEGIN
	rst   <= transport '1', '0' after ( 4 * CLK_PERIOD);
	rst_n <= not rst;

	clock: process
	begin
		clk <= '1', '0' after CLK_PERIOD/2;
		wait for CLK_PERIOD;
	end process;
	ddr_clk: process
	begin
		ddr2_clk <= '1', '0' after CLK_PERIOD2/2;
		wait for CLK_PERIOD2;
	end process;

	reg: process begin
	wait until rising_edge(clk);
		en_stb_iq   <= en_stb_i;
		hsync_o     <= hsync_oq;
		vsync_o     <= vsync_oq;
		color_o     <= color_oq;
		color_en_o  <= color_en_oq;
	end process;

	dut_writ: entity work.sig_write
	port map (
		clk                   => clk,
		rst_n                 => rst_n,
		ctrl_disable_wr       => '0',

		ddr2_clk              => ddr2_clk,
		ddr2_cmd_en_o         => ddr2_wr_cmd_en_o,
		ddr2_cmd_instr_o      => ddr2_wr_cmd_instr_o,
		ddr2_cmd_bl_o         => ddr2_wr_cmd_bl_o,
		ddr2_cmd_byte_addr_o  => ddr2_wr_cmd_byte_addr_o,
		ddr2_cmd_empty_i      => ddr2_wr_cmd_empty_i,
		ddr2_cmd_full_i       => ddr2_wr_cmd_full_i,
		ddr2_wr_en_o          => ddr2_wr_en_o,
		ddr2_wr_mask_o        => ddr2_wr_mask_o,
		ddr2_wr_data_o        => ddr2_wr_data_o,
		ddr2_wr_full_i        => ddr2_wr_full_i,
		ddr2_wr_empty_i       => ddr2_wr_empty_i,
		ddr2_wr_count_i       => ddr2_wr_count_i,
		ddr2_wr_underrun_i    => ddr2_wr_underrun_i,
		ddr2_wr_error_i       => ddr2_wr_error_i,

		hsync_i    => hsync_o,
		vsync_i    => vsync_o,
		color_en_i => color_en_o,
		color_i    => color_o
	);

	mem_wr: process begin
		ddr2_wr_cmd_empty_i   <= '1';
		ddr2_wr_underrun_i    <= '0';
		ddr2_wr_error_i       <= '0';
		wait;
	end process;

	ddr2_wr_empty_i <= '1' when wr_cnt = 0 else '0';
	ddr2_wr_full_i  <= wr_cnt(7);
	ddr2_wr_count_i <= std_logic_vector(wr_cnt(6 downto 0));
	ddr2_wr_cmd_full_i <= '1' when wr_lat /= x"00" else '0';
	mem_wr_l: process (ddr2_clk, rst_n)
	begin
	if rst_n = '0' then
		wr_cnt      <= (others => '0');
		wr_lat      <= (others => '0');
	elsif rising_edge(ddr2_clk) then
		if wr_lat <= x"10" and wr_lat > x"00" and ddr2_wr_en_o = '1' then
			wr_cnt <= wr_cnt - x"03";
		elsif wr_lat <= x"10" and wr_lat > x"00" then
			wr_cnt <= wr_cnt - x"04";
		elsif ddr2_wr_en_o = '1' then
			wr_cnt <= wr_cnt + "1";
		end if;

		if ddr2_wr_cmd_en_o = '1' then
			wr_lat <= x"11";
		end if;

		if wr_lat /= x"00" then
			wr_lat <= wr_lat - "1";
		end if;
	end if;
	end process;

	dut_read: entity work.sig_read
	generic map (
		H_ACTIVE_PIXEL => x"500",
		H_BLANKING     => x"198",
		H_SYNC_WIDTH   => x"070",
		H_SYNC_OFFSET  => x"030",
		V_ACTIVE_LINES => x"400",
		V_BLANKING     => x"02a",
		V_SYNC_WIDTH   => x"01",
		V_SYNC_OFFSET  => x"03"
	)
	port map (
		clk        => clk,
		rst_n      => rst_n,

		ddr2_clk              => ddr2_clk,
		ddr2_cmd_en_o         => ddr2_rd_cmd_en_o,
		ddr2_cmd_instr_o      => ddr2_rd_cmd_instr_o,
		ddr2_cmd_bl_o         => ddr2_rd_cmd_bl_o,
		ddr2_cmd_byte_addr_o  => ddr2_rd_cmd_byte_addr_o,
		ddr2_cmd_empty_i      => ddr2_rd_cmd_empty_i,
		ddr2_cmd_full_i       => ddr2_rd_cmd_full_i,
		ddr2_rd_en_o          => ddr2_rd_en_o,
		ddr2_rd_data_i        => ddr2_rd_data_i,
		ddr2_rd_full_i        => ddr2_rd_full_i,
		ddr2_rd_empty_i       => ddr2_rd_empty_i,
		ddr2_rd_count_i       => ddr2_rd_count_i,
		ddr2_rd_overflow_i    => ddr2_rd_overflow_i,
		ddr2_rd_error_i       => ddr2_rd_error_i,

		en_stb_i   => en_stb_iq,
		hsync_o    => hsync_oq,
		vsync_o    => vsync_oq,
		color_en_o => color_en_oq,
		color_o    => color_oq
	);

	beh: process begin
		en_stb_i <= '0';
		wait for 20*CLK_PERIOD;
		en_stb_i <= '1';
		--wait for CLK_PERIOD;
		--en_stb_i <= '0';
		wait;
	end process;

	mem_rd: process begin
		ddr2_rd_cmd_empty_i   <= '1';
		ddr2_rd_data_i        <= (others => '0');
		ddr2_rd_overflow_i    <= '0';
		ddr2_rd_error_i       <= '0';
		wait;
	end process;
	ddr2_rd_count_i    <= std_logic_vector(req_cnt(6 downto 0));
	ddr2_rd_empty_i    <= '0' when      req_cnt /= x"0000" else '1';
	ddr2_rd_cmd_full_i <= '1' when req_save_cnt /= x"0000" else '0';
	ddr2_rd_full_i     <= '1' when      req_cnt  = x"0040" else '0';

	latency_done <= '1' when lat_cnt = x"0001" else '0';
	mem_rd_l: process (ddr2_clk, rst_n)
	begin
	if rst_n = '0' then
		req_cnt      <= (others => '0');
		req_save_cnt <= (others => '0');
		lat_cnt      <= (others => '0');
		latency      <= '1';
	elsif rising_edge(ddr2_clk) then
		if ddr2_rd_cmd_en_o = '1' then
			req_save_cnt <= x"00" & "00" & unsigned(ddr2_rd_cmd_bl_o) + "1";
			lat_cnt      <= x"0020";
		elsif latency_done = '1' then
			req_save_cnt <= (others => '0');
		end if;

		if lat_cnt /= x"0000" then
			lat_cnt <= lat_cnt - "1";
		end if;

		if latency_done = '1' and ddr2_rd_en_o = '1' then
			req_cnt <= req_cnt + req_save_cnt - "1";
		elsif latency_done = '1' then
			req_cnt <= req_cnt + req_save_cnt;
		elsif ddr2_rd_en_o = '1' and req_cnt /= x"0000" then
			req_cnt <= req_cnt - "1";
		end if;
	end if;
	end process;
end rtl;

configuration sig_read_tb_rtl_cfg of sig_read_tb is
  for rtl
  end for;
end sig_read_tb_rtl_cfg;
