library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity splink is generic ( NUM_STRANDS : positive; MAX_STRAND_LEN : positive := 256 ); port ( clk : in std_logic; reset : in std_logic; 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; subtype color_t is std_logic_vector(BITS_PER_LED-1 downto 0); type led_data_t is record addr : std_logic_vector(7 downto 0); current_color : color_t; 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 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"; 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 driver_gen: for i in 0 to NUM_STRANDS-1 generate ws2812_inst: entity work.ws2812 generic map ( NUM_LEDS => MAX_STRAND_LEN, COLOR_ORDER => "GRB", T_CLK => 12.5 ns, T0H => 0.35 us, T0L => 0.8 us, T1H => 0.7 us, T1L => 0.6 us, T_RES => 80 us ) port map ( n_reset => not reset, clk => clk, led_addr => led_data_arr(i).addr, led_red => led_data_arr(i).current_color(23 downto 16), led_green => led_data_arr(i).current_color(15 downto 8), led_blue => led_data_arr(i).current_color(7 downto 0), dout => drivers(i) ); 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_data_arr(i).current_color <= strand_store(to_integer(unsigned(led_data_arr(i).addr))); if udp_valid = '1' and receive_state = DATA and active_strand = i then strand_store(pixels_received) := udp_data(23 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'; if all_strands_written then frame_number <= current_frame; clear_write_flags <= '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 <= STRAND_NUM; else receive_state <= DROP; end if; when STRAND_NUM => -- TODO udp_length, range check with MAX_STRAND_LEN num_pixels <= MAX_STRAND_LEN; -- FIXME bounds check active_strand <= to_integer(unsigned(udp_data)); receive_state <= FRAME_NUM; 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;