-- LED96_if
-- interface for the 96 LEDs of the FPGA4U.
-- We defined four LEDs regions, three of them having an user-defined
-- intensity, set by a pulse width over 256 cycles, while one regions
-- is fixed to full intensity.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity LED96_if is
	port(
		clk   : in  std_logic;
		reset : in  std_logic;
		-- avalon slave interface
		as0_read   : in  std_logic;
		as0_write  : in  std_logic;
		as0_addr   : in  std_logic_vector(3 downto 0);
		as0_rddata : out std_logic_vector(31 downto 0);
		as0_wrdata : in  std_logic_vector(31 downto 0);
		-- LEDs
		LEDS : out std_logic_vector(95 downto 0)
		);
end entity;

architecture synth of LED96_if is
-- Register map:
-- addr  type   content
-- 00     R/W   LED31..0 to intensity 0
-- 04     R/W   LED63..32 to intensity 0
-- 08     R/W   LED95..64 to intensity 0
-- 1C     RO    intensity 0, constant to 0xFFFF
-- 1C     W     reset all layers
-- 10     R/W   LED31..0 to intensity 1
-- 14     R/W   LED63..32 to intensity 1
-- 18     R/W   LED95..64 to intensity 1
-- 1C     R/W   intensity 1
-- ...

constant C_NREGS : natural := 4;  -- number of layers
constant C_WIDTH : natural := 10; -- pulse counter width
type  t_array16 is array (natural range <>) of std_logic_vector(C_WIDTH-1 downto 0);
type  t_array96 is array (natural range <>) of std_logic_vector(95 downto 0);

signal r_pulse  : t_array16(0 to C_NREGS-1);
signal r_leds   : t_array96(0 to C_NREGS-1);
signal s_filter : std_logic_vector(0 to C_NREGS-2); -- pwm filter

signal r_read : std_logic;
signal r_addr : std_logic_vector(3 downto 0); -- log2(C_NREGS*4) bits

signal r_counter : std_logic_vector(C_WIDTH-1 downto 0);
begin

-- counter
process(clk, reset)
begin
	if (reset = '1') then
		r_counter <= (others => '0');
	elsif (rising_edge(clk)) then
		r_counter <= r_counter + 1;
	end if;
end process;

-- filters
g_filters: for i in 0 to C_NREGS-2 generate
	s_filter(i) <= '1' when r_pulse(i+1) > r_counter
	            else '0';
end generate;

-- LEDs output
process(s_filter, r_leds)
	variable v_tmp : std_logic_vector(95 downto 0);
begin
	v_tmp := r_leds(0);
	for i in 0 to C_NREGS-2 loop
		v_tmp := v_tmp or (r_leds(i+1) and (95 downto 0 => s_filter(i)));
	end loop;
	LEDS <= v_tmp;
end process;

-- avalon read, 1 cycle latency:
-- address and read are registered
process(clk, reset)
begin
	if (reset = '1') then
		r_addr <= (others => '0');
		r_read <= '0';
	elsif (rising_edge(clk)) then
		r_read <= as0_read;
		if (as0_read='1') then
			r_addr <= as0_addr;
		end if;
	end if;
end process;

process(r_addr, r_read, r_pulse, r_leds)
	variable v_tmp : std_logic_vector(95 downto 0);
begin
	as0_rddata <= (others => '0');
	if (r_read='1') then
		v_tmp := r_leds(conv_integer(r_addr(r_addr'high downto 2)));
		case r_addr(1 downto 0) is
			-- for leds
			when "00" => as0_rddata <= v_tmp(31 downto  0);
			when "01" => as0_rddata <= v_tmp(63 downto 32);
			when "10" => as0_rddata <= v_tmp(95 downto 64);
			-- for pulse input
			when "11" => as0_rddata(C_WIDTH-1 downto 0) <= r_pulse(conv_integer(r_addr(r_addr'high downto 2)));
			when others =>
		end case;
	end if;
end process;

-- avalon write
process(clk, reset)
begin
	if (reset = '1') then
		r_leds  <= (others => (others => '0'));
		r_pulse <= (others => (others => '1'));
	elsif (rising_edge(clk)) then
		if (as0_write='1') then
			case as0_addr(1 downto 0) is
				-- for LEDs
				when "00" => r_leds(conv_integer(as0_addr(as0_addr'high downto 2)))(31 downto  0) <= as0_wrdata;
				when "01" => r_leds(conv_integer(as0_addr(as0_addr'high downto 2)))(63 downto 32) <= as0_wrdata;
				when "10" => r_leds(conv_integer(as0_addr(as0_addr'high downto 2)))(95 downto 64) <= as0_wrdata;
				-- for pulse
				when "11" =>
					if (as0_addr(as0_addr'high downto 2) = 0) then -- reset leds
						r_leds <= (others => (others => '0'));
					end if;
					r_pulse(conv_integer(as0_addr(as0_addr'high downto 2))) <= as0_wrdata(C_WIDTH-1 downto 0);
					-- pulse 0 is fixed to full
					r_pulse(0) <= (others => '1');
				when others =>
			end case;
		end if;
	end if;
end process;
end architecture;