253 lines
6.9 KiB
VHDL
253 lines
6.9 KiB
VHDL
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;
|