library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.ws2812_pkg.color_t; use work.ws2812_pkg.colors_vector; entity splink is generic ( NUM_STRANDS : positive; MAX_STRAND_LEN : positive := 256 ); port ( clk : in std_logic; reset : in std_logic; udp_length : in std_logic_vector(15 downto 0); udp_valid : in std_logic; udp_last : in std_logic; udp_data : in std_logic_vector(31 downto 0); frame_number : out unsigned(31 downto 0); drivers : out std_logic_vector(NUM_STRANDS-1 downto 0) ); end entity; architecture a of splink is constant BITS_PER_LED: natural := 24; signal led_addr : std_logic_vector(7 downto 0); signal led_colors : colors_vector(NUM_STRANDS-1 downto 0); type led_data_t is record was_written : std_logic; end record; type led_data_arr_t is array(0 to NUM_STRANDS-1) of led_data_t; signal led_data_arr : led_data_arr_t; signal active_strand: natural range 0 to NUM_STRANDS-1; signal num_pixels: natural range 1 to MAX_STRAND_LEN; signal current_frame: unsigned(31 downto 0); signal pixels_received: natural range 0 to MAX_STRAND_LEN-1; signal run : std_logic; signal clear_write_flags : std_logic; signal all_strands_written : std_logic; signal some_strands_written : std_logic; -- "PIXL" constant MAGIC_NUMBER : std_logic_vector(31 downto 0) := x"5049584c"; -- magic + frame num + strand num (4 bytes each) constant HEADER_LEN : natural := 12; type receive_state_t is (MAGIC, FRAME_NUM, STRAND_NUM, DATA, DROP); constant RESET_STATE : receive_state_t := MAGIC; signal receive_state : receive_state_t; begin ws2812_inst: entity work.ws2812_parallel generic map ( NUM_DRIVERS => NUM_STRANDS, NUM_LEDS => MAX_STRAND_LEN, COLOR_ORDER => "GRB", T_CLK => 12.5 ns, T0H => 0.35 us, T0L => 0.9 us, T1H => 0.7 us, T1L => 0.55 us, T_RES => 80 us ) port map ( n_reset => not reset, clk => clk, run => run, led_addr => led_addr, led_colors => led_colors, dout => drivers ); driver_gen: for i in 0 to NUM_STRANDS-1 generate writer: process(clk) type strand_store_t is array(0 to MAX_STRAND_LEN-1) of color_t; variable strand_store: strand_store_t; begin if rising_edge(clk) then led_colors(i) <= strand_store(to_integer(unsigned(led_addr))); if udp_valid = '1' and receive_state = DATA and active_strand = i then strand_store(pixels_received) := ( red => udp_data(23 downto 16), green => udp_data(15 downto 8), blue => udp_data(7 downto 0) ); if pixels_received = num_pixels - 1 and udp_last = '1' then led_data_arr(i).was_written <= '1'; end if; end if; if clear_write_flags then led_data_arr(i).was_written <= '0'; end if; end if; end process; end generate; process(led_data_arr) begin all_strands_written <= '1'; some_strands_written <= '0'; for i in 0 to NUM_STRANDS-1 loop if led_data_arr(i).was_written then some_strands_written <= '1'; else all_strands_written <= '0'; end if; end loop; end process; fsm: process(clk) begin if rising_edge(clk) then clear_write_flags <= '0'; run <= '0'; if all_strands_written then frame_number <= current_frame; clear_write_flags <= '1'; run <= '1'; end if; if reset then clear_write_flags <= '1'; receive_state <= RESET_STATE; elsif udp_valid then if udp_last then -- always resynchronize to start of packet receive_state <= RESET_STATE; end if; case receive_state is when MAGIC => if udp_data /= MAGIC_NUMBER then receive_state <= DROP; else if (unsigned(udp_length) - HEADER_LEN) / 4 > MAX_STRAND_LEN then receive_state <= DROP; else num_pixels <= (to_integer(unsigned(udp_length)) - HEADER_LEN) / 4; receive_state <= STRAND_NUM; end if; end if; when STRAND_NUM => if unsigned(udp_data) >= NUM_STRANDS then receive_state <= DROP; else active_strand <= to_integer(unsigned(udp_data)); receive_state <= FRAME_NUM; end if; when FRAME_NUM => if not some_strands_written then current_frame <= unsigned(udp_data); elsif current_frame /= unsigned(udp_data) then current_frame <= unsigned(udp_data); clear_write_flags <= '1'; end if; pixels_received <= 0; receive_state <= DATA; when DATA => if pixels_received /= num_pixels - 1 then pixels_received <= pixels_received + 1; elsif not udp_last then -- packet too long receive_state <= DROP; end if; when DROP => -- wait until udp_last end case; end if; end if; end process; end architecture;