rgboard/design/top.vhd

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;