library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all; library ws2812b; use ws2812b.all; use work.top_pkg.all; entity top is generic( f_clk : natural := 24_000_000; ROWS : natural := 7; COLUMNS : natural := 64 ); port( clk_hw : in std_logic; -- HW clock rst_hw : in std_logic; -- Hardware reset (active low) uart_rx : in std_logic; -- UART RX pin ws2812_out : out std_logic_vector(ROWS - 1 downto 0) -- WS2812B data line ); end entity top; architecture RTL of top is constant RAM_WIDTH : natural := 8 * 3 * ROWS; constant RAM_DEPTH : natural := integer(ceil(log2(real(COLUMNS)))); constant UART_CKDIV : std_logic_vector(15 downto 0) := std_logic_vector(to_unsigned(f_clk / 115200, 16)); signal clk : std_logic; signal rst : std_logic; signal pixData_red : color_vector(ROWS - 1 downto 0); signal pixData_green : color_vector(ROWS - 1 downto 0); signal pixData_blue : color_vector(ROWS - 1 downto 0); signal pixData_valid : std_logic_vector(ROWS - 1 downto 0); signal pixData_next : std_logic_vector(ROWS - 1 downto 0); signal uart_rx_data : std_logic_vector(7 downto 0); signal uart_rx_stb : std_logic; signal uart_rx_error : std_logic; type controller_state_t is (IDLE, ADDR, LENGTH, DATA); signal controller_state : controller_state_t; signal controller_ram_addr : std_logic_vector(RAM_DEPTH - 1 downto 0); signal controller_ram_din : std_logic_vector(RAM_WIDTH - 1 downto 0); signal controller_ram_dout : std_logic_vector(RAM_WIDTH - 1 downto 0); signal controller_ram_we : std_logic; signal sr : std_logic_vector(controller_ram_din'range); signal byte_cnt : integer range 0 to ROWS * 3 - 1; signal leds_left : integer range 0 to 2**RAM_DEPTH - 1; signal render_stb : std_logic; type renderer_state_t is (IDLE, RENDER, LOCK); signal renderer_state : renderer_state_t; signal renderer_addr : std_logic_vector(RAM_DEPTH - 1 downto 0); signal renderer_data : std_logic_vector(RAM_WIDTH - 1 downto 0); begin clk <= clk_hw; reset_gen : process(clk, rst_hw) is variable tmp : std_logic; begin if (rst_hw = '0') then tmp := '1'; rst <= '1'; elsif (rising_edge(clk)) then rst <= tmp; tmp := '0'; end if; end process reset_gen; ws2812b_phys : for i in 0 to ROWS - 1 generate ws2812b_phy_inst : entity ws2812b.ws2812b_phy generic map( f_clk => f_clk ) port map( clk => clk, rst => rst, so => ws2812_out(i), pixData_red => pixData_red(i), pixData_green => pixData_green(i), pixData_blue => pixData_blue(i), pixData_valid => pixData_valid(i), pixData_next => pixData_next(i) ); end generate ws2812b_phys; -- Upon receiving ```renderer_stb```, it sends the data in memory out to the LEDs renderer : process(clk, rst) is procedure set_all_valid(state : std_logic) is begin for i in 0 to ROWS - 1 loop pixData_valid(i) <= state; end loop; end procedure set_all_valid; begin if (rst = '1') then renderer_state <= IDLE; renderer_addr <= (others => '0'); set_all_valid('0'); elsif (rising_edge(clk)) then case renderer_state is when IDLE => set_all_valid('0'); renderer_addr <= (others => '0'); -- Note: We need to have been in this state for at least one cycle if (render_stb = '1') then set_all_valid('1'); renderer_state <= RENDER; end if; when RENDER => if (pixData_next(0) = '1') then -- TODO: This expects all PHYs to run synchronously renderer_addr <= std_logic_vector(unsigned(renderer_addr) + 1); if (unsigned(renderer_addr) = (COLUMNS - 1)) then set_all_valid('0'); renderer_state <= LOCK; end if; end if; when LOCK => set_all_valid('0'); if (pixData_next(0) = '1') then renderer_state <= IDLE; end if; end case; end if; end process renderer; pixData_connections : for i in 0 to ROWS - 1 generate pixData_red(i) <= renderer_data(i * 24 + 7 downto i * 24); pixData_green(i) <= renderer_data(i * 24 + 15 downto i * 24 + 8); pixData_blue(i) <= renderer_data(i * 24 + 23 downto i * 24 + 16); end generate pixData_connections; ram_inst : entity work.ram generic map( WIDTH => RAM_WIDTH, DEPTH => RAM_DEPTH ) port map( clk => clk, addr_a => controller_ram_addr, data_out_a => controller_ram_dout, data_in_a => controller_ram_din, we_a => controller_ram_we, addr_b => renderer_addr, data_out_b => renderer_data ); controller : process(clk, rst) is variable sr_v : std_logic_vector(sr'range); begin if (rst = '1') then sr <= (others => '0'); controller_state <= IDLE; byte_cnt <= 0; render_stb <= '0'; controller_ram_addr <= (others => '0'); controller_ram_we <= '0'; elsif (rising_edge(clk)) then render_stb <= '0'; controller_ram_we <= '0'; if (uart_rx_stb = '1') then sr_v := sr(sr'high - uart_rx_data'length downto sr'low) & uart_rx_data; sr <= sr_v; end if; case controller_state is when IDLE => byte_cnt <= 0; if (uart_rx_stb = '1') then if (uart_rx_data = x"00") then controller_state <= ADDR; elsif (uart_rx_data = x"01") then render_stb <= '1'; controller_state <= IDLE; end if; end if; when ADDR => if (uart_rx_stb = '1') then if (byte_cnt = 1) then controller_ram_addr <= std_logic_vector(to_unsigned(to_integer(unsigned(sr_v(15 downto 0)) * 3), controller_ram_addr'length)); controller_state <= LENGTH; byte_cnt <= 0; else byte_cnt <= byte_cnt + 1; end if; end if; when LENGTH => if (uart_rx_stb = '1') then if (byte_cnt = 1) then leds_left <= to_integer(unsigned(sr_v(15 downto 0))) - 1; controller_state <= DATA; byte_cnt <= 0; else byte_cnt <= byte_cnt + 1; end if; end if; when DATA => if (controller_ram_we = '1') then controller_ram_addr <= std_logic_vector(unsigned(controller_ram_addr) + 1); end if; if (uart_rx_stb = '1') then if (byte_cnt = ROWS * 3 - 1) then byte_cnt <= 0; controller_ram_we <= '1'; if (leds_left = 0) then controller_state <= IDLE; else leds_left <= leds_left - 1; end if; else byte_cnt <= byte_cnt + 1; end if; end if; end case; end if; end process controller; controller_ram_din <= sr; uart_rx_inst : entity work.uart_rx port map( clk => clk, rst => rst, data => uart_rx_data, byte_ready => uart_rx_stb, error => uart_rx_error, ckDiv => UART_CKDIV, parityEnable => '0', parityOdd => '0', twoStopBits => '0', rx => uart_rx ); end architecture RTL;