From e21415b367e08a865e11bc208819fdc67747b98a Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Sun, 16 Oct 2016 11:40:21 +0200 Subject: [PATCH] Initial commit --- README.MD | 12 ++ bench/bench_ws2812b_controller.vhd | 90 +++++++++ bench/bench_ws2812b_phy.vhd | 83 +++++++++ design/top.vhd | 128 +++++++++++++ design/ws2812b_controller.vhd | 146 +++++++++++++++ design/ws2812b_gamma.vhd | 285 +++++++++++++++++++++++++++++ design/ws2812b_phy.vhd | 165 +++++++++++++++++ 7 files changed, 909 insertions(+) create mode 100644 README.MD create mode 100644 bench/bench_ws2812b_controller.vhd create mode 100644 bench/bench_ws2812b_phy.vhd create mode 100644 design/top.vhd create mode 100644 design/ws2812b_controller.vhd create mode 100644 design/ws2812b_gamma.vhd create mode 100644 design/ws2812b_phy.vhd diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..9fb9fef --- /dev/null +++ b/README.MD @@ -0,0 +1,12 @@ +# WS2812B CONTROLLER FOR FPGAS +A controller for the WorldSemi WS2812B RGB LEDs written in plain VHDL. + +## Entity description +### ws2812b_phy +Low level driver for the WorldSemi WS2812B RGB LEDs. Handles bit timing and command separation. + +### ws2812b_controller +WS2812B Controller to render picture data from SRAM. + +## License +This work is released under the Mozilla Public License (MPL) Version 2. It may be interpreted in the same way the Open Hardware Description License (OHDL) by Julius Baxter does. Read more about it here: http://juliusbaxter.net/ohdl/ohdl.txt diff --git a/bench/bench_ws2812b_controller.vhd b/bench/bench_ws2812b_controller.vhd new file mode 100644 index 0000000..b094cb6 --- /dev/null +++ b/bench/bench_ws2812b_controller.vhd @@ -0,0 +1,90 @@ +-- ---------------------------------------------------------------------------- +-- WS2812B CONTROLLER FOR FPGAS +-- ---------------------------------------------------------------------------- +-- bench_ws2812b_controller.vhd : Testbench for the SRAM controller module +-- : to drive the WS2812B LEDs. +-- ---------------------------------------------------------------------------- +-- Author : Markus Koch +-- Contributors : None +-- Created on : 2016/10/16 +-- License : Mozilla Public License (MPL) Version 2 +-- ---------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +library design; +use design.all; + +entity bench_ws2812b_controller is +end entity bench_ws2812b_controller; + +architecture RTL of bench_ws2812b_controller is + constant length : integer := 4; + + signal clk : std_logic; + signal rst : std_logic; + signal so : std_logic; + signal addr : std_logic_vector(integer(ceil(log2(real(length - 1)))) downto 0); + signal data_red : std_logic_vector(7 downto 0); + signal data_green : std_logic_vector(7 downto 0); + signal data_blue : std_logic_vector(7 downto 0); + signal dataOut_red : std_logic_vector(7 downto 0); + signal dataOut_green : std_logic_vector(7 downto 0); + signal dataOut_blue : std_logic_vector(7 downto 0); + signal we : std_logic; + signal render : std_logic; + signal vsync : std_logic; + +begin + ws2812b_controller_inst : entity design.ws2812b_controller + generic map( + length => length, + f_clk => 100000000) + port map( + clk => clk, + rst => rst, + so => so, + addr => addr, + data_red => data_red, + data_green => data_green, + data_blue => data_blue, + dataOut_red => dataOut_red, + dataOut_green => dataOut_green, + dataOut_blue => dataOut_blue, + we => we, + render => render, + vsync => vsync + ); + + clock_driver : process + constant period : time := 10 ns; + begin + clk <= '0'; + wait for period / 2; + clk <= '1'; + wait for period / 2; + end process clock_driver; + + test : process is + begin + rst <= '1'; + addr <= (others => '0'); + data_red <= (others => '0'); + data_green <= (others => '0'); + data_blue <= (others => '0'); + render <= '0'; + wait for 20 ns; + + rst <= '0'; + wait for 60 ns; + render <= '1'; + wait for 20 ns; + render <= '0'; + + wait; + end process test; + +end architecture RTL; diff --git a/bench/bench_ws2812b_phy.vhd b/bench/bench_ws2812b_phy.vhd new file mode 100644 index 0000000..758ed3f --- /dev/null +++ b/bench/bench_ws2812b_phy.vhd @@ -0,0 +1,83 @@ +-- ---------------------------------------------------------------------------- +-- WS2812B CONTROLLER FOR FPGAS +-- ---------------------------------------------------------------------------- +-- bench_ws2812b_phy.vhd : Development testbench for the WS2812B phy module. +-- ---------------------------------------------------------------------------- +-- Author : Markus Koch +-- Contributors : None +-- Created on : 2016/10/16 +-- License : Mozilla Public License (MPL) Version 2 +-- ---------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library design; +use design.all; + +entity bench_ws2812b_phy is +end entity bench_ws2812b_phy; + +architecture RTL of bench_ws2812b_phy is + signal clk : std_logic; + signal rst : std_logic; + signal so : std_logic; + signal pixData_red : std_logic_vector(7 downto 0); + signal pixData_green : std_logic_vector(7 downto 0); + signal pixData_blue : std_logic_vector(7 downto 0); + signal pixData_valid : std_logic; + signal pixData_next : std_logic; + +begin + clock_driver : process + constant period : time := 10 ns; + begin + clk <= '0'; + wait for period / 2; + clk <= '1'; + wait for period / 2; + end process clock_driver; + + ws2812b_phy_inst : entity design.ws2812b_phy + generic map( + f_clk => 100000000 + ) + port map( + clk => clk, + rst => rst, + so => so, + pixData_red => pixData_red, + pixData_green => pixData_green, + pixData_blue => pixData_blue, + pixData_valid => pixData_valid, + pixData_next => pixData_next + ); + + test : process is + begin + pixData_red <= x"00"; + pixData_green <= x"80"; + pixData_blue <= x"01"; + pixData_valid <= '0'; + rst <= '1'; + wait for 20 ns; + rst <= '0'; + wait for 20 ns; + pixData_valid <= '1'; + + wait until pixData_next = '1'; -- Pix 0 + wait until pixData_next = '1'; -- Pix 1 + pixData_valid <= '0'; + wait until pixData_next = '1'; -- Reset ack + + pixData_valid <= '1'; + wait until pixData_next = '1'; -- Pix 0 + wait until pixData_next = '1'; -- Pix 1 + pixData_valid <= '0'; + wait until pixData_next = '1'; -- Reset ack + + wait; + end process test; + +end architecture RTL; diff --git a/design/top.vhd b/design/top.vhd new file mode 100644 index 0000000..ac635d0 --- /dev/null +++ b/design/top.vhd @@ -0,0 +1,128 @@ +-- ---------------------------------------------------------------------------- +-- WS2812B CONTROLLER FOR FPGAS +-- ---------------------------------------------------------------------------- +-- top.vhd : Top level design to test the WS2812B controller modules. +-- : Real demo applications coming soon. +-- ---------------------------------------------------------------------------- +-- Author : Markus Koch +-- Contributors : None +-- Created on : 2016/10/16 +-- License : Mozilla Public License (MPL) Version 2 +-- ---------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity top is + port( + clk : in std_logic; + rst_hw : in std_logic; + btn_n : in std_logic; + so : out std_logic + ); +end entity top; + +architecture RTL of top is + constant length : integer := 120; + + signal rst : std_logic; + signal addr : std_logic_vector(integer(ceil(log2(real(length - 1)))) downto 0); + signal data_red : std_logic_vector(7 downto 0); + signal data_green : std_logic_vector(7 downto 0); + signal data_blue : std_logic_vector(7 downto 0); + signal dataOut_red : std_logic_vector(7 downto 0); + signal dataOut_green : std_logic_vector(7 downto 0); + signal dataOut_blue : std_logic_vector(7 downto 0); + signal we : std_logic; + signal render : std_logic; + signal vsync : std_logic; + signal done : std_logic; + signal foo : std_logic_vector(1 downto 0); +begin + rst <= not rst_hw; + + foo <= addr(1 downto 0); + ws2812b_controller_inst : entity work.ws2812b_controller + generic map( + length => length, + f_clk => 50000000 + ) + port map( + clk => clk, + rst => rst, + so => so, + addr => addr, + data_red => data_red, + data_green => data_green, + data_blue => data_blue, + dataOut_red => dataOut_red, + dataOut_green => dataOut_green, + dataOut_blue => dataOut_blue, + we => we, + render => render, + vsync => vsync + ); + + prog : process(clk, rst) is + variable colRot : unsigned(1 downto 0); + variable c2 : integer range 0 to 25000000; + begin + if rst = '1' then + addr <= (others => '1'); + data_red <= (others => '0'); + data_green <= (others => '0'); + data_blue <= (others => '0'); + we <= '0'; + done <= '1'; + c2 := 0; + colRot := "00"; + render <= '0'; + elsif rising_edge(clk) then + we <= '0'; + render <= '0'; + if done = '0' then + addr <= std_logic_vector(unsigned(addr) + 1); + if to_integer(unsigned(addr)) = length - 1 then + done <= '1'; + render <= '1'; + end if; + + if unsigned(foo) = colRot then + data_red <= (others => '1'); + data_green <= (others => '0'); + data_blue <= (others => '0'); + elsif unsigned(foo) = colRot + 1 then + data_red <= (others => '0'); + data_green <= (others => '1'); + data_blue <= (others => '0'); + elsif unsigned(foo) = colRot + 2 then + data_red <= (others => '0'); + data_green <= (others => '0'); + data_blue <= (others => '1'); + else + data_red <= std_logic_vector(to_unsigned(127, 8)); + data_green <= std_logic_vector(to_unsigned(127, 8)); + data_blue <= (others => '0'); + end if; + + if (btn_n = '0') then + data_red <= (others => '1'); + data_green <= (others => '1'); + data_blue <= (others => '1'); + end if; + we <= '1'; + else + if c2 = 10000000 then + done <= '0'; + c2 := 0; + colRot := colRot + 1; + else + c2 := c2 + 1; + end if; + end if; + end if; + end process prog; + +end architecture RTL; diff --git a/design/ws2812b_controller.vhd b/design/ws2812b_controller.vhd new file mode 100644 index 0000000..75ed42f --- /dev/null +++ b/design/ws2812b_controller.vhd @@ -0,0 +1,146 @@ +-- ---------------------------------------------------------------------------- +-- WS2812B CONTROLLER FOR FPGAS +-- ---------------------------------------------------------------------------- +-- ws2812b_controller.vhd : WS2812B Controller to render picture data from +-- : SRAM. +-- ---------------------------------------------------------------------------- +-- Author : Markus Koch +-- Contributors : None +-- Created on : 2016/10/16 +-- License : Mozilla Public License (MPL) Version 2 +-- ---------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity ws2812b_controller is + generic( + length : integer := 16; -- Amount of LEDs on the link + f_clk : natural := 50000000; + T0H : real := 0.00000035; + T1H : real := 0.0000009; + T0L : real := 0.0000009; + T1L : real := 0.00000035; + DEL : real := 0.0000001; -- Must be bigger than others + RES : real := 0.0000050 + ); + port( + clk : in std_logic; + rst : in std_logic; + -- Hardware Connection + so : out std_logic; -- Serial output to WS2812B + -- Data Link + addr : in std_logic_vector(integer(ceil(log2(real(length - 1)))) downto 0); -- Address of the LED + data_red : in std_logic_vector(7 downto 0); + data_green : in std_logic_vector(7 downto 0); + data_blue : in std_logic_vector(7 downto 0); + dataOut_red : out std_logic_vector(7 downto 0); + dataOut_green : out std_logic_vector(7 downto 0); + dataOut_blue : out std_logic_vector(7 downto 0); + we : in std_logic; -- Write to RAM + render : in std_logic; -- Send data to LEDs + vsync : out std_logic -- Finished sending data out + ); +end entity ws2812b_controller; + +architecture RTL of ws2812b_controller is + type memory_t is array (length - 1 downto 0) of std_logic_vector(23 downto 0); + signal memory : memory_t; + signal rdaddr : std_logic_vector(integer(ceil(log2(real(length - 1)))) downto 0); + type state_t is (IDLE, PRESENT, WAITEMPTY); + signal state : state_t; + signal pixData_red : std_logic_vector(7 downto 0); + signal pixData_green : std_logic_vector(7 downto 0); + signal pixData_blue : std_logic_vector(7 downto 0); + signal pixData_valid : std_logic; + signal pixData_next : std_logic; +begin + -- ----------------------- + -- Bit Timing Driver + -- ----------------------- + ws2812b_phy_inst : entity work.ws2812b_phy + generic map( + f_clk => f_clk, + T0H => T0H, + T1H => T1H, + T0L => T0L, + T1L => T1L, + DEL => DEL, + RES => RES + ) + port map( + clk => clk, + rst => rst, + so => so, + pixData_red => pixData_red, + pixData_green => pixData_green, + pixData_blue => pixData_blue, + pixData_valid => pixData_valid, + pixData_next => pixData_next + ); + + -- ----------------------- + -- Memory Interface + -- ----------------------- + mem_writer : process(rst, clk) is + begin + if rst = '1' then + dataOut_red <= (others => '0'); + dataOut_green <= (others => '0'); + dataOut_blue <= (others => '0'); + elsif rising_edge(clk) then + dataOut_red <= memory(to_integer(unsigned(addr)))(23 downto 16); + dataOut_green <= memory(to_integer(unsigned(addr)))(15 downto 8); + dataOut_blue <= memory(to_integer(unsigned(addr)))(7 downto 0); + if we = '1' then + memory(to_integer(unsigned(addr))) <= data_red & data_green & data_blue; + end if; + end if; + end process mem_writer; + + -- ----------------------- + -- Main Controller FSM + -- ----------------------- + main : process(rst, clk) is + begin + if rst = '1' then + rdaddr <= (others => '0'); + state <= IDLE; + vsync <= '0'; + elsif rising_edge(clk) then + vsync <= '0'; + case state is + when IDLE => + rdaddr <= (others => '0'); + if render = '1' then + report "SIZE=" & integer'image(integer(ceil(log2(real(length - 1))))); + state <= PRESENT; + end if; + when PRESENT => + if pixData_next = '1' then + if to_integer(unsigned(rdaddr)) = length - 1 then + rdaddr <= (others => '0'); + state <= WAITEMPTY; + vsync <= '1'; + else + rdaddr <= std_logic_vector(unsigned(rdaddr) + 1); + end if; + end if; + when WAITEMPTY => + rdaddr <= (others => '0'); + if pixData_next = '1' then + state <= IDLE; + end if; + end case; + end if; + end process main; + + pixData_valid <= '1' when state = PRESENT else '0'; + + pixData_red <= memory(to_integer(unsigned(rdaddr)))(23 downto 16); + pixData_green <= memory(to_integer(unsigned(rdaddr)))(15 downto 8); + pixData_blue <= memory(to_integer(unsigned(rdaddr)))(7 downto 0); + +end architecture RTL; diff --git a/design/ws2812b_gamma.vhd b/design/ws2812b_gamma.vhd new file mode 100644 index 0000000..52e7691 --- /dev/null +++ b/design/ws2812b_gamma.vhd @@ -0,0 +1,285 @@ +-- ---------------------------------------------------------------------------- +-- WS2812B CONTROLLER FOR FPGAS +-- ---------------------------------------------------------------------------- +-- ws2812b_gamma.vhd : Gamma correction RAM for the WS2812B LEDs. +-- : Not yet implemented. +-- ---------------------------------------------------------------------------- +-- Author : Markus Koch +-- Contributors : None +-- Created on : 2016/10/16 +-- License : Mozilla Public License (MPL) Version 2 +-- ---------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity ws2812b_gamma is + port( + clk : in std_logic; + rst : in std_logic + ); +end entity ws2812b_gamma; + +architecture RTL of ws2812b_gamma is + type gamma_table_t is array (0 to 255) of std_logic_vector(7 downto 0); + constant gamme_table : gamma_table_t := ( + 0 => std_logic_vector(to_unsigned(0, 8)), + 1 => std_logic_vector(to_unsigned(0, 8)), + 2 => std_logic_vector(to_unsigned(0, 8)), + 3 => std_logic_vector(to_unsigned(0, 8)), + 4 => std_logic_vector(to_unsigned(0, 8)), + 5 => std_logic_vector(to_unsigned(0, 8)), + 6 => std_logic_vector(to_unsigned(0, 8)), + 7 => std_logic_vector(to_unsigned(0, 8)), + 8 => std_logic_vector(to_unsigned(0, 8)), + 9 => std_logic_vector(to_unsigned(0, 8)), + 10 => std_logic_vector(to_unsigned(0, 8)), + 11 => std_logic_vector(to_unsigned(0, 8)), + 12 => std_logic_vector(to_unsigned(0, 8)), + 13 => std_logic_vector(to_unsigned(0, 8)), + 14 => std_logic_vector(to_unsigned(0, 8)), + 15 => std_logic_vector(to_unsigned(0, 8)), + 16 => std_logic_vector(to_unsigned(0, 8)), + 17 => std_logic_vector(to_unsigned(0, 8)), + 18 => std_logic_vector(to_unsigned(0, 8)), + 19 => std_logic_vector(to_unsigned(0, 8)), + 20 => std_logic_vector(to_unsigned(0, 8)), + 21 => std_logic_vector(to_unsigned(0, 8)), + 22 => std_logic_vector(to_unsigned(1, 8)), + 23 => std_logic_vector(to_unsigned(1, 8)), + 24 => std_logic_vector(to_unsigned(1, 8)), + 25 => std_logic_vector(to_unsigned(1, 8)), + 26 => std_logic_vector(to_unsigned(1, 8)), + 27 => std_logic_vector(to_unsigned(1, 8)), + 28 => std_logic_vector(to_unsigned(1, 8)), + 29 => std_logic_vector(to_unsigned(2, 8)), + 30 => std_logic_vector(to_unsigned(2, 8)), + 31 => std_logic_vector(to_unsigned(2, 8)), + 32 => std_logic_vector(to_unsigned(2, 8)), + 33 => std_logic_vector(to_unsigned(2, 8)), + 34 => std_logic_vector(to_unsigned(2, 8)), + 35 => std_logic_vector(to_unsigned(3, 8)), + 36 => std_logic_vector(to_unsigned(3, 8)), + 37 => std_logic_vector(to_unsigned(3, 8)), + 38 => std_logic_vector(to_unsigned(3, 8)), + 39 => std_logic_vector(to_unsigned(3, 8)), + 40 => std_logic_vector(to_unsigned(4, 8)), + 41 => std_logic_vector(to_unsigned(4, 8)), + 42 => std_logic_vector(to_unsigned(4, 8)), + 43 => std_logic_vector(to_unsigned(4, 8)), + 44 => std_logic_vector(to_unsigned(5, 8)), + 45 => std_logic_vector(to_unsigned(5, 8)), + 46 => std_logic_vector(to_unsigned(5, 8)), + 47 => std_logic_vector(to_unsigned(5, 8)), + 48 => std_logic_vector(to_unsigned(6, 8)), + 49 => std_logic_vector(to_unsigned(6, 8)), + 50 => std_logic_vector(to_unsigned(6, 8)), + 51 => std_logic_vector(to_unsigned(7, 8)), + 52 => std_logic_vector(to_unsigned(7, 8)), + 53 => std_logic_vector(to_unsigned(7, 8)), + 54 => std_logic_vector(to_unsigned(8, 8)), + 55 => std_logic_vector(to_unsigned(8, 8)), + 56 => std_logic_vector(to_unsigned(8, 8)), + 57 => std_logic_vector(to_unsigned(9, 8)), + 58 => std_logic_vector(to_unsigned(9, 8)), + 59 => std_logic_vector(to_unsigned(9, 8)), + 60 => std_logic_vector(to_unsigned(10, 8)), + 61 => std_logic_vector(to_unsigned(10, 8)), + 62 => std_logic_vector(to_unsigned(11, 8)), + 63 => std_logic_vector(to_unsigned(11, 8)), + 64 => std_logic_vector(to_unsigned(11, 8)), + 65 => std_logic_vector(to_unsigned(12, 8)), + 66 => std_logic_vector(to_unsigned(12, 8)), + 67 => std_logic_vector(to_unsigned(13, 8)), + 68 => std_logic_vector(to_unsigned(13, 8)), + 69 => std_logic_vector(to_unsigned(13, 8)), + 70 => std_logic_vector(to_unsigned(14, 8)), + 71 => std_logic_vector(to_unsigned(14, 8)), + 72 => std_logic_vector(to_unsigned(15, 8)), + 73 => std_logic_vector(to_unsigned(15, 8)), + 74 => std_logic_vector(to_unsigned(16, 8)), + 75 => std_logic_vector(to_unsigned(16, 8)), + 76 => std_logic_vector(to_unsigned(17, 8)), + 77 => std_logic_vector(to_unsigned(17, 8)), + 78 => std_logic_vector(to_unsigned(18, 8)), + 79 => std_logic_vector(to_unsigned(18, 8)), + 80 => std_logic_vector(to_unsigned(19, 8)), + 81 => std_logic_vector(to_unsigned(19, 8)), + 82 => std_logic_vector(to_unsigned(20, 8)), + 83 => std_logic_vector(to_unsigned(21, 8)), + 84 => std_logic_vector(to_unsigned(21, 8)), + 85 => std_logic_vector(to_unsigned(22, 8)), + 86 => std_logic_vector(to_unsigned(22, 8)), + 87 => std_logic_vector(to_unsigned(23, 8)), + 88 => std_logic_vector(to_unsigned(23, 8)), + 89 => std_logic_vector(to_unsigned(24, 8)), + 90 => std_logic_vector(to_unsigned(25, 8)), + 91 => std_logic_vector(to_unsigned(25, 8)), + 92 => std_logic_vector(to_unsigned(26, 8)), + 93 => std_logic_vector(to_unsigned(27, 8)), + 94 => std_logic_vector(to_unsigned(27, 8)), + 95 => std_logic_vector(to_unsigned(28, 8)), + 96 => std_logic_vector(to_unsigned(29, 8)), + 97 => std_logic_vector(to_unsigned(29, 8)), + 98 => std_logic_vector(to_unsigned(30, 8)), + 99 => std_logic_vector(to_unsigned(31, 8)), + 100 => std_logic_vector(to_unsigned(31, 8)), + 101 => std_logic_vector(to_unsigned(32, 8)), + 102 => std_logic_vector(to_unsigned(33, 8)), + 103 => std_logic_vector(to_unsigned(34, 8)), + 104 => std_logic_vector(to_unsigned(34, 8)), + 105 => std_logic_vector(to_unsigned(35, 8)), + 106 => std_logic_vector(to_unsigned(36, 8)), + 107 => std_logic_vector(to_unsigned(37, 8)), + 108 => std_logic_vector(to_unsigned(37, 8)), + 109 => std_logic_vector(to_unsigned(38, 8)), + 110 => std_logic_vector(to_unsigned(39, 8)), + 111 => std_logic_vector(to_unsigned(40, 8)), + 112 => std_logic_vector(to_unsigned(40, 8)), + 113 => std_logic_vector(to_unsigned(41, 8)), + 114 => std_logic_vector(to_unsigned(42, 8)), + 115 => std_logic_vector(to_unsigned(43, 8)), + 116 => std_logic_vector(to_unsigned(44, 8)), + 117 => std_logic_vector(to_unsigned(45, 8)), + 118 => std_logic_vector(to_unsigned(46, 8)), + 119 => std_logic_vector(to_unsigned(46, 8)), + 120 => std_logic_vector(to_unsigned(47, 8)), + 121 => std_logic_vector(to_unsigned(48, 8)), + 122 => std_logic_vector(to_unsigned(49, 8)), + 123 => std_logic_vector(to_unsigned(50, 8)), + 124 => std_logic_vector(to_unsigned(51, 8)), + 125 => std_logic_vector(to_unsigned(52, 8)), + 126 => std_logic_vector(to_unsigned(53, 8)), + 127 => std_logic_vector(to_unsigned(54, 8)), + 128 => std_logic_vector(to_unsigned(55, 8)), + 129 => std_logic_vector(to_unsigned(56, 8)), + 130 => std_logic_vector(to_unsigned(57, 8)), + 131 => std_logic_vector(to_unsigned(58, 8)), + 132 => std_logic_vector(to_unsigned(59, 8)), + 133 => std_logic_vector(to_unsigned(60, 8)), + 134 => std_logic_vector(to_unsigned(61, 8)), + 135 => std_logic_vector(to_unsigned(62, 8)), + 136 => std_logic_vector(to_unsigned(63, 8)), + 137 => std_logic_vector(to_unsigned(64, 8)), + 138 => std_logic_vector(to_unsigned(65, 8)), + 139 => std_logic_vector(to_unsigned(66, 8)), + 140 => std_logic_vector(to_unsigned(67, 8)), + 141 => std_logic_vector(to_unsigned(68, 8)), + 142 => std_logic_vector(to_unsigned(69, 8)), + 143 => std_logic_vector(to_unsigned(70, 8)), + 144 => std_logic_vector(to_unsigned(71, 8)), + 145 => std_logic_vector(to_unsigned(72, 8)), + 146 => std_logic_vector(to_unsigned(73, 8)), + 147 => std_logic_vector(to_unsigned(74, 8)), + 148 => std_logic_vector(to_unsigned(76, 8)), + 149 => std_logic_vector(to_unsigned(77, 8)), + 150 => std_logic_vector(to_unsigned(78, 8)), + 151 => std_logic_vector(to_unsigned(79, 8)), + 152 => std_logic_vector(to_unsigned(80, 8)), + 153 => std_logic_vector(to_unsigned(81, 8)), + 154 => std_logic_vector(to_unsigned(83, 8)), + 155 => std_logic_vector(to_unsigned(84, 8)), + 156 => std_logic_vector(to_unsigned(85, 8)), + 157 => std_logic_vector(to_unsigned(86, 8)), + 158 => std_logic_vector(to_unsigned(88, 8)), + 159 => std_logic_vector(to_unsigned(89, 8)), + 160 => std_logic_vector(to_unsigned(90, 8)), + 161 => std_logic_vector(to_unsigned(91, 8)), + 162 => std_logic_vector(to_unsigned(93, 8)), + 163 => std_logic_vector(to_unsigned(94, 8)), + 164 => std_logic_vector(to_unsigned(95, 8)), + 165 => std_logic_vector(to_unsigned(96, 8)), + 166 => std_logic_vector(to_unsigned(98, 8)), + 167 => std_logic_vector(to_unsigned(99, 8)), + 168 => std_logic_vector(to_unsigned(100, 8)), + 169 => std_logic_vector(to_unsigned(102, 8)), + 170 => std_logic_vector(to_unsigned(103, 8)), + 171 => std_logic_vector(to_unsigned(104, 8)), + 172 => std_logic_vector(to_unsigned(106, 8)), + 173 => std_logic_vector(to_unsigned(107, 8)), + 174 => std_logic_vector(to_unsigned(109, 8)), + 175 => std_logic_vector(to_unsigned(110, 8)), + 176 => std_logic_vector(to_unsigned(111, 8)), + 177 => std_logic_vector(to_unsigned(113, 8)), + 178 => std_logic_vector(to_unsigned(114, 8)), + 179 => std_logic_vector(to_unsigned(116, 8)), + 180 => std_logic_vector(to_unsigned(117, 8)), + 181 => std_logic_vector(to_unsigned(119, 8)), + 182 => std_logic_vector(to_unsigned(120, 8)), + 183 => std_logic_vector(to_unsigned(121, 8)), + 184 => std_logic_vector(to_unsigned(123, 8)), + 185 => std_logic_vector(to_unsigned(124, 8)), + 186 => std_logic_vector(to_unsigned(126, 8)), + 187 => std_logic_vector(to_unsigned(128, 8)), + 188 => std_logic_vector(to_unsigned(129, 8)), + 189 => std_logic_vector(to_unsigned(131, 8)), + 190 => std_logic_vector(to_unsigned(132, 8)), + 191 => std_logic_vector(to_unsigned(134, 8)), + 192 => std_logic_vector(to_unsigned(135, 8)), + 193 => std_logic_vector(to_unsigned(137, 8)), + 194 => std_logic_vector(to_unsigned(138, 8)), + 195 => std_logic_vector(to_unsigned(140, 8)), + 196 => std_logic_vector(to_unsigned(142, 8)), + 197 => std_logic_vector(to_unsigned(143, 8)), + 198 => std_logic_vector(to_unsigned(145, 8)), + 199 => std_logic_vector(to_unsigned(146, 8)), + 200 => std_logic_vector(to_unsigned(148, 8)), + 201 => std_logic_vector(to_unsigned(150, 8)), + 202 => std_logic_vector(to_unsigned(151, 8)), + 203 => std_logic_vector(to_unsigned(153, 8)), + 204 => std_logic_vector(to_unsigned(155, 8)), + 205 => std_logic_vector(to_unsigned(157, 8)), + 206 => std_logic_vector(to_unsigned(158, 8)), + 207 => std_logic_vector(to_unsigned(160, 8)), + 208 => std_logic_vector(to_unsigned(162, 8)), + 209 => std_logic_vector(to_unsigned(163, 8)), + 210 => std_logic_vector(to_unsigned(165, 8)), + 211 => std_logic_vector(to_unsigned(167, 8)), + 212 => std_logic_vector(to_unsigned(169, 8)), + 213 => std_logic_vector(to_unsigned(170, 8)), + 214 => std_logic_vector(to_unsigned(172, 8)), + 215 => std_logic_vector(to_unsigned(174, 8)), + 216 => std_logic_vector(to_unsigned(176, 8)), + 217 => std_logic_vector(to_unsigned(178, 8)), + 218 => std_logic_vector(to_unsigned(179, 8)), + 219 => std_logic_vector(to_unsigned(181, 8)), + 220 => std_logic_vector(to_unsigned(183, 8)), + 221 => std_logic_vector(to_unsigned(185, 8)), + 222 => std_logic_vector(to_unsigned(187, 8)), + 223 => std_logic_vector(to_unsigned(189, 8)), + 224 => std_logic_vector(to_unsigned(191, 8)), + 225 => std_logic_vector(to_unsigned(193, 8)), + 226 => std_logic_vector(to_unsigned(194, 8)), + 227 => std_logic_vector(to_unsigned(196, 8)), + 228 => std_logic_vector(to_unsigned(198, 8)), + 229 => std_logic_vector(to_unsigned(200, 8)), + 230 => std_logic_vector(to_unsigned(202, 8)), + 231 => std_logic_vector(to_unsigned(204, 8)), + 232 => std_logic_vector(to_unsigned(206, 8)), + 233 => std_logic_vector(to_unsigned(208, 8)), + 234 => std_logic_vector(to_unsigned(210, 8)), + 235 => std_logic_vector(to_unsigned(212, 8)), + 236 => std_logic_vector(to_unsigned(214, 8)), + 237 => std_logic_vector(to_unsigned(216, 8)), + 238 => std_logic_vector(to_unsigned(218, 8)), + 239 => std_logic_vector(to_unsigned(220, 8)), + 240 => std_logic_vector(to_unsigned(222, 8)), + 241 => std_logic_vector(to_unsigned(224, 8)), + 242 => std_logic_vector(to_unsigned(227, 8)), + 243 => std_logic_vector(to_unsigned(229, 8)), + 244 => std_logic_vector(to_unsigned(231, 8)), + 245 => std_logic_vector(to_unsigned(233, 8)), + 246 => std_logic_vector(to_unsigned(235, 8)), + 247 => std_logic_vector(to_unsigned(237, 8)), + 248 => std_logic_vector(to_unsigned(239, 8)), + 249 => std_logic_vector(to_unsigned(241, 8)), + 250 => std_logic_vector(to_unsigned(244, 8)), + 251 => std_logic_vector(to_unsigned(246, 8)), + 252 => std_logic_vector(to_unsigned(248, 8)), + 253 => std_logic_vector(to_unsigned(250, 8)), + 254 => std_logic_vector(to_unsigned(252, 8)), + 255 => std_logic_vector(to_unsigned(255, 8)) + ); + begin + end architecture RTL; diff --git a/design/ws2812b_phy.vhd b/design/ws2812b_phy.vhd new file mode 100644 index 0000000..1acad26 --- /dev/null +++ b/design/ws2812b_phy.vhd @@ -0,0 +1,165 @@ +-- ---------------------------------------------------------------------------- +-- WS2812B CONTROLLER FOR FPGAS +-- ---------------------------------------------------------------------------- +-- ws2812b_phy.vhd : Low level driver for the WorldSemi WS2812B RGB LEDs. +-- Handles bit timing and command separation. +-- ---------------------------------------------------------------------------- +-- Author : Markus Koch +-- Contributors : None +-- Created on : 2016/10/16 +-- License : Mozilla Public License (MPL) Version 2 +-- ---------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity ws2812b_phy is + generic( + f_clk : natural := 50000000; + T0H : real := 0.00000035; + T1H : real := 0.0000009; + T0L : real := 0.0000009; + T1L : real := 0.00000035; + DEL : real := 0.0000000; -- 0.0000001 + RES : real := 0.00005000 -- Must be bigger than others + ); + port( + -- Global Signals + clk : in std_logic; -- System clock @ f_clk + rst : in std_logic; -- Asynchronous reset + -- Hardware Connection + so : out std_logic; -- Serial output to WS2812B + -- Data Link + pixData_red : in std_logic_vector(7 downto 0); + pixData_green : in std_logic_vector(7 downto 0); + pixData_blue : in std_logic_vector(7 downto 0); + pixData_valid : in std_logic; + pixData_next : out std_logic + ); +end entity ws2812b_phy; + +architecture RTL of ws2812b_phy is + -- WS2812B Bit Encoder Signals and Definitions + constant CYC_T0H : natural := natural(T0H / (real(1) / real(f_clk))) - 1; + constant CYC_T1H : natural := natural(T1H / (real(1) / real(f_clk))) - 1; + constant CYC_T0L : natural := natural(T0L / (real(1) / real(f_clk))) - 1; + constant CYC_T1L : natural := natural(T1L / (real(1) / real(f_clk))) - 1; + constant CYC_DEL : natural := natural(DEL / (real(1) / real(f_clk))) - 1; + constant CYC_RES : natural := natural(RES / (real(1) / real(f_clk))) - 1; + + type state_t is (HIGH, LOW); + signal bitState : state_t; + + signal bitCnt : integer range 0 to CYC_RES; -- Timing counter + signal bitData_i : std_logic; + signal bitData : std_logic_vector(1 downto 0); -- 00: send 0
01: send 1
10: send reset
11: send led-separator + signal bitData_valid : std_logic; -- Applied data is valid -> TX request (keep valid until data_next) + signal bitData_next : std_logic; -- Apply next bit or release valid to terminate transmission + + -- Serializer Signals and Definitions + signal shiftreg : std_logic_vector(23 downto 0); + signal pixCnt : integer range 0 to 25; +begin + -- ----------------------- + -- WS2812B Bit Encoder + -- ----------------------- + bitEncoder : process(rst, clk) is + begin + if rst = '1' then + bitCnt <= 0; + bitState <= LOW; + bitData_next <= '0'; + elsif rising_edge(clk) then + bitData_next <= '0'; + if bitCnt /= 0 then + bitCnt <= bitCnt - 1; + end if; + case bitState is + when HIGH => + if bitCnt = 0 then + bitState <= LOW; + if bitData_i = '0' then + bitCnt <= CYC_T0L; + else + bitCnt <= CYC_T1L; + end if; + end if; + when LOW => + if bitCnt = 0 then + if bitData_valid = '1' then + bitData_next <= '1'; + bitData_i <= bitData(0); + if bitData(0) = '0' then + bitCnt <= CYC_T0H; + else + bitCnt <= CYC_T1H; + end if; + if bitData(1) = '0' then + bitState <= HIGH; + else + if bitData(0) = '0' then + bitCnt <= CYC_RES; + else + bitCnt <= CYC_DEL; + end if; + bitState <= LOW; + end if; + end if; + end if; + end case; + end if; + end process bitEncoder; + + so <= '1' when bitState = HIGH else '0'; + + -- ----------------------- + -- Pixel Data Serializer + -- ----------------------- + pixSerializer : process(rst, clk) is + begin + if rst = '1' then + bitData_valid <= '0'; + pixData_next <= '0'; + pixCnt <= 0; + elsif rising_edge(clk) then + pixData_next <= '0'; + if bitData_next = '1' then + pixCnt <= pixCnt - 1; + if pixCnt = 2 then -- End of data + bitData(1) <= '1'; -- Control sequence + if pixData_valid = '1' then + shiftreg(23) <= '1'; -- Trigger DEL sequence + report "WS2812B: Send DELAY" severity note; + else + shiftreg(23) <= '0'; -- Trigger RES sequence + report "WS2812B: Send RESET" severity note; + pixData_next <= '1'; -- Acknowledge that the reset has been latched + end if; + elsif pixCnt = 1 then -- End of control + bitData_valid <= '0'; + else + shiftreg <= shiftreg(22 downto 0) & '0'; + end if; + end if; + if pixCnt = 0 then -- End of DEL + pixCnt <= pixCnt; + if pixData_valid = '1' then + report "WS2812B: Latch pixel data" severity note; + pixData_next <= '1'; + shiftreg <= pixData_green & pixData_red & pixData_blue; + bitData_valid <= '1'; + pixCnt <= 25; + bitData(1) <= '0'; -- Data bit + -- else + -- bitData(1) <= '1'; -- Control sequence + -- shiftreg(23) <= '1'; -- Trigger RES sequence + end if; + end if; + + end if; + end process pixSerializer; + + bitData(0) <= shiftreg(23); + +end architecture RTL;