diff --git a/vhdl/splink.vhdl b/vhdl/splink.vhdl index be3dbc1..0b5a1da 100644 --- a/vhdl/splink.vhdl +++ b/vhdl/splink.vhdl @@ -34,6 +34,14 @@ architecture a of splink is 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 frame_number: unsigned(31 downto 0); + signal pixels_received: natural range 0 to MAX_STRAND_LEN-1; + + type receive_state_t is (FRAME_NUM, STRAND_NUM, DATA, DROP); + signal receive_state : receive_state_t; begin driver_gen: for i in 0 to NUM_STRANDS-1 generate ws2812_inst: entity work.ws2812 @@ -61,66 +69,78 @@ begin 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 frame_done then + led_data_arr(i).was_written <= '0'; + end if; + end if; + end process; end generate; - writer: process(clk) - type strand_store_t is array(0 to MAX_STRAND_LEN-1) of color_t; - type strand_stores_t is array(0 to NUM_STRANDS-1) of strand_store_t; - variable strand_stores: strand_stores_t; + process(led_data_arr) + begin + frame_done <= '1'; - variable active_strand: natural range 0 to NUM_STRANDS-1; - variable packet_len: natural range 1 to MAX_STRAND_LEN; - variable frame_number: unsigned(31 downto 0); - variable store_counter: natural range 0 to MAX_STRAND_LEN-1; + for i in 0 to NUM_STRANDS-1 loop + if not led_data_arr(i).was_written then + frame_done <= '0'; + end if; + end loop; + end process; - type receive_state_t is (FRAME_NUM, STRAND_NUM, DATA, DROP); - variable receive_state : receive_state_t; + fsm: process(clk) begin if rising_edge(clk) then - frame_done <= '0'; + if reset then + receive_state <= STRAND_NUM; + elsif udp_valid then + if udp_last then + -- always resynchronize to start of packet + receive_state <= STRAND_NUM; + end if; - for i in 0 to NUM_STRANDS-1 loop - led_data_arr(i).current_color <= strand_stores(i)(to_integer(unsigned(led_data_arr(i).addr))); - end loop; - - if udp_valid then case receive_state is when STRAND_NUM => -- TODO udp_length, range check with MAX_STRAND_LEN - packet_len := MAX_STRAND_LEN; + num_pixels <= MAX_STRAND_LEN; -- FIXME bounds check - active_strand := to_integer(unsigned(udp_data)); + active_strand <= to_integer(unsigned(udp_data)); - receive_state := FRAME_NUM; + receive_state <= FRAME_NUM; when FRAME_NUM => - frame_number := unsigned(udp_data); + frame_number <= unsigned(udp_data); - store_counter := 0; - receive_state := DATA; + pixels_received <= 0; + receive_state <= DATA; when DATA => - strand_stores(active_strand)(store_counter) := udp_data(23 downto 0); - - if store_counter = packet_len then - if udp_last then - --led_data_arr(active_strand).was_written <= '1'; - else - -- packet too long - receive_state := DROP; - end if; - else - store_counter := store_counter + 1; + 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; - - if udp_last then - receive_state := STRAND_NUM; - end if; end if; end if; end process;