-- See the file "LICENSE" for the full license governing this code. --

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.lt16x32_global.all;
use work.wishbone.all;
use work.config.all;

entity wb_scrolling is
	generic(
		memaddr		:	generic_addr_type; --:= CFG_BADR_SCR;
		addrmask	:	generic_mask_type --:= CFG_MADR_SCR;
	);
	port(
		clk		: in  std_logic;
		rst		: in  std_logic;
		wslvi	:	in	wb_slv_in_type;
		wslvo	:	out	wb_slv_out_type;

        anodes : out std_logic_vector(7 downto 0);
        cathodes : out std_logic_vector(7 downto 0)
	);
end wb_scrolling;

architecture Behavioral of wb_scrolling is

    signal cnt_start : std_logic;
    signal cnt_done : std_logic;
    signal cnt_value : std_logic_vector(31 downto 0);

    signal buffer_clear : std_logic;
    signal buffer_write : std_logic;
    signal buffer_data : std_logic_vector(4 downto 0);
    signal buffer_elements : std_logic_vector(4 downto 0);

    signal seg_data : std_logic_vector(3 downto 0);
    signal seg_off : std_logic;
    signal seg_shift : std_logic;
    signal seg_write : std_logic;
    signal seg_clear : std_logic;   

    signal on_off : std_logic;

    signal next_char : std_logic;
    signal hex_char : std_logic_vector(4 downto 0);

    signal data_out : std_logic_vector(63 downto 0);
    signal data_in : std_logic_vector(63 downto 0);
    signal data_in_changed : std_logic;
    
    signal ack : std_logic;

    component scrolling_timer is
        port(
            clk : in std_logic;
            rst : in std_logic;
            cnt_start : in std_logic;
            cnt_done : out std_logic;
            cnt_value : in std_logic_vector(31 downto 0)
        );
    end component;

    component scrolling_buffer
    port(
        clk : in std_logic;
        rst : in std_logic;
        buffer_clear : in std_logic;
        buffer_write : in std_logic;
        buffer_data : in std_logic_vector(4 downto 0);
        next_char : in std_logic;
        hex_char : out std_logic_vector(4 downto 0);
        elements : out std_logic_vector(4 downto 0)
    );
    end component;

    component scrolling_controller
    port(
        clk : in std_logic;
        rst : in std_logic;
        on_off : in std_logic;
        cnt_start : out std_logic;
        cnt_done : in std_logic;
        next_char : out std_logic;
        hex_char : in std_logic_vector(4 downto 0);
        seg_data : out std_logic_vector(3 downto 0);
        seg_off : out std_logic;
        seg_shift : out std_logic;
        seg_write : out std_logic;
        seg_clear : out std_logic;
        buffer_elements : in std_logic_vector(4 downto 0)
    );
    end component;

    component seven_segment_display is
		port(
            clk		: in  std_logic;
            rst		: in  std_logic;
            seg_data : in std_logic_vector(3 downto 0);
            seg_off : in std_logic;
            seg_shift : in std_logic;
            seg_write : in std_logic;
            seg_clear : in std_logic;   
            anodes : out std_logic_vector(7 downto 0);
            cathodes : out std_logic_vector(7 downto 0)
		);
	end component;

begin

	timer: scrolling_timer
        port map(
			clk => clk,
            rst => rst,
            cnt_start => cnt_start,
            cnt_done => cnt_done,
            cnt_value => cnt_value
    );

    buf: scrolling_buffer
    port map(
        clk => clk,
        rst => rst,
        buffer_clear => buffer_clear,
        buffer_write => buffer_write,
        buffer_data => buffer_data,
        next_char => next_char,
        hex_char => hex_char,
        elements => buffer_elements
    );

    controller: scrolling_controller
    port map(
        clk => clk,
        rst => rst,
        on_off => on_off,
        cnt_start => cnt_start,
        cnt_done => cnt_done,
        next_char => next_char,
        hex_char => hex_char,
        seg_data => seg_data,
        seg_off => seg_off,
        seg_shift => seg_shift,
        seg_write => seg_write,
        seg_clear => seg_clear,
        buffer_elements => buffer_elements
    );

    seven_segment: seven_segment_display
    port map(
        clk => clk,
        rst	=> rst,
        seg_data => seg_data,
        seg_off => seg_off,
        seg_shift => seg_shift,
        seg_write => seg_write,
        seg_clear => seg_clear,

        anodes => anodes,
        cathodes => cathodes
    );

    process(clk)
	begin
		if clk'event and clk='1' then
			if rst = '1' then
				ack	<= '0';
                data_in <= (others => '0');
                data_in_changed <= '0';
			else
                data_in <= (others => '0');
                data_in_changed <= '0';
                
				if wslvi.stb = '1' and wslvi.cyc = '1' then
				    if wslvi.we='0' then
                        -- data_out will have the correct value
                    else
                        -- Write enable
                        data_in_changed <= '1';
                        
                        if wslvi.adr(2) = '0' then 
                            data_in(31 downto 0) <= dec_wb_dat(wslvi.sel,wslvi.dat);
                        else
                            data_in(63 downto 32) <= dec_wb_dat(wslvi.sel,wslvi.dat);
                        end if;
                    end if;
				
					if ack = '0' then
						ack	<= '1';
					else
						ack	<= '0';
					end if;
				else
					ack <= '0';
				end if;
			end if;
		end if;
	end process;

    process(clk)
	begin
		if clk'event and clk='1' then
			if rst = '1' then
				cnt_value <= (others => '0');
                buffer_write <= '0';
                buffer_clear <= '0';
                buffer_data <= (others => '0');
                on_off <= '0';
			else
			    buffer_write <= '0';
                buffer_clear <= '0';
                buffer_data <= (others => '0');
                on_off <= '0';
                
                if data_in_changed = '1' and ack = '1' then
                    if wslvi.adr(2) = '1' then
                        cnt_value <= data_in(63 downto 32);
                    else
                        buffer_write <= data_in(24);
                        buffer_clear <= data_in(8);
                        buffer_data <= data_in(20 downto 16);
                        on_off <= data_in(0);
                    end if;
                end if;
			end if;
		end if;
	end process;
    
    data_out(31 downto 25) <= (others => '0');
    data_out(24) <= buffer_write;
    data_out(23 downto 21) <= (others => '0');
    data_out(20 downto 16) <= buffer_data;
    data_out(15 downto 9) <= (others => '0');
    data_out(8) <= buffer_clear;
    data_out(7 downto 1) <= (others => '0');
    data_out(0) <= on_off;

    data_out(63 downto 32) <= cnt_value;

	wslvo.dat <=
        data_out(31 downto 0) when wslvi.adr(2) = '0' else
        data_out(63 downto 32) when wslvi.adr(2) = '1';

	wslvo.ack	<= ack;
	wslvo.wbcfg	<= wb_membar(memaddr, addrmask);

end Behavioral;
