commit 660765e3325ae94c74d1c929875da9a2f8c1964c Author: Mario Hüttel Date: Sun Apr 15 20:33:13 2018 +0200 implemented first draft diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90a5f0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*~ + diff --git a/bench.vhd b/bench.vhd new file mode 100644 index 0000000..f81e6bd --- /dev/null +++ b/bench.vhd @@ -0,0 +1,187 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity bench is + +end entity bench; + +architecture sim of bench is + signal clk : std_logic; + signal rst_hw : std_logic; + signal sck : std_logic; + signal mosi : std_logic; + signal cs : std_logic; + signal ready : std_logic; + signal ws_out : std_logic; +begin -- architecture sim + + top_1 : entity work.top + port map ( + clk => clk, + rst_hw => rst_hw, + sck => sck, + mosi => mosi, + cs => cs, + ready => ready, + ws_out => ws_out); + + clk_gen : process is + begin + clk <= '0'; + wait for 10 ns; + clk <= '1'; + wait for 10 ns; + end process clk_gen; + + rst_gen : process is + begin + rst_hw <= '1'; + wait for 15 ns; + rst_hw <= '0'; + wait; + end process rst_gen; + + sck_gen : process is + begin + sck <= '0'; + wait for 40 ns; + sck <= '1'; + wait for 40 ns; + end process sck_gen; + + send_spi : process is + procedure send24(dat : in std_logic_vector(23 downto 0)) is + variable data : std_logic_vector(dat'range); + begin + cs <= '1'; + if ready /= '1' then + wait until ready = '1'; + end if; + wait for 30 ns; + data := dat; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '0'; + mosi <= data(23); + data := data(22 downto 0) & '0'; + wait until falling_edge(sck); + cs <= '1'; + end send24; + begin + cs <= '1'; + wait for 50 ns; + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + send24(x"FF0055"); +send24(x"CCFFAA"); + wait; + end process send_spi; +end architecture sim; diff --git a/fifo.vhd b/fifo.vhd new file mode 100644 index 0000000..38e50c0 --- /dev/null +++ b/fifo.vhd @@ -0,0 +1,96 @@ +-- Thanks to: http://www.deathbylogic.com/2013/07/vhdl-standard-fifo/ +library IEEE; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; + +entity STD_FIFO is + Generic ( + DATA_WIDTH : positive := 8; + FIFO_DEPTH : positive := 256 + ); + Port ( + CLK : in STD_LOGIC; + RST : in STD_LOGIC; + WriteEn : in STD_LOGIC; + DataIn : in STD_LOGIC_VECTOR (DATA_WIDTH - 1 downto 0); + ReadEn : in STD_LOGIC; + DataOut : out STD_LOGIC_VECTOR (DATA_WIDTH - 1 downto 0); + Empty : out STD_LOGIC; + Full : out STD_LOGIC + ); +end STD_FIFO; + +architecture Behavioral of STD_FIFO is + +begin + + -- Memory Pointer Process + fifo_proc : process (CLK) + type FIFO_Memory is array (0 to FIFO_DEPTH - 1) of STD_LOGIC_VECTOR (DATA_WIDTH - 1 downto 0); + variable Memory : FIFO_Memory; + + variable Head : natural range 0 to FIFO_DEPTH - 1; + variable Tail : natural range 0 to FIFO_DEPTH - 1; + + variable Looped : boolean; + begin + if rising_edge(CLK) then + if RST = '1' then + Head := 0; + Tail := 0; + + Looped := false; + + Full <= '0'; + Empty <= '1'; + else + if (ReadEn = '1') then + if ((Looped = true) or (Head /= Tail)) then + -- Update data output + DataOut <= Memory(Tail); + + -- Update Tail pointer as needed + if (Tail = FIFO_DEPTH - 1) then + Tail := 0; + + Looped := false; + else + Tail := Tail + 1; + end if; + + + end if; + end if; + + if (WriteEn = '1') then + if ((Looped = false) or (Head /= Tail)) then + -- Write Data to Memory + Memory(Head) := DataIn; + + -- Increment Head pointer as needed + if (Head = FIFO_DEPTH - 1) then + Head := 0; + + Looped := true; + else + Head := Head + 1; + end if; + end if; + end if; + + -- Update Empty and Full flags + if (Head = Tail) then + if Looped then + Full <= '1'; + else + Empty <= '1'; + end if; + else + Empty <= '0'; + Full <= '0'; + end if; + end if; + end if; + end process; + +end Behavioral; diff --git a/spi_slave.vhd b/spi_slave.vhd new file mode 100644 index 0000000..81b1e11 --- /dev/null +++ b/spi_slave.vhd @@ -0,0 +1,113 @@ +------------------------------------------------------------------------------- +-- Title : simple SPI slave +-- Project : +------------------------------------------------------------------------------- +-- File : spi_slave.vhd +-- Author : Mario Hüttel +-- Company : +-- Created : 2018-04-15 +-- Last update: 2018-04-15 +-- Platform : +-- Standard : VHDL'93/02 +------------------------------------------------------------------------------- +-- Description: This SPI slaves samples the SPI's sck line +-- Therefore freq(clk) > 2.5 * freq(sck) +------------------------------------------------------------------------------- +-- Copyright (c) 2018 +------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity spi_slave is + generic ( + DAT_WIDTH : natural := 8); + port ( + clk : in std_logic; + rst : in std_logic; + cs : in std_logic; + sck : in std_logic; + miso : out std_logic; + mosi : in std_logic; + dat_o : out std_logic_vector(DAT_WIDTH - 1 downto 0); + dat_i : in std_logic_vector(DAT_WIDTH - 1 downto 0); + dat_o_strb : out std_logic; + dat_i_ack : out std_logic); + +end entity spi_slave; + + +architecture RTL of spi_slave is + signal sck_old : std_logic; + signal sck_sync : std_logic; + signal mosi_sync : std_logic; + signal cs_sync : std_logic; + signal pos_edge : std_logic; + signal neg_edge : std_logic; + signal miso_int : std_logic; + signal cnt : integer range 0 to DAT_WIDTH-1; + signal shift_reg : std_logic_vector(DAT_WIDTH -1 downto 0); +begin -- architecture RTL + + sync : process (clk, rst) is + begin -- process sck_sync + if rst = '1' then -- asynchronous reset (active high) + sck_sync <= '0'; + mosi_sync <= '0'; + cs_sync <= '0'; + elsif rising_edge(clk) then -- rising clock edge + cs_sync <= cs; + mosi_sync <= mosi; + sck_sync <= sck; + end if; + end process sync; + + edge_detector : process (clk, rst) is + begin -- process edge_detector + if rst = '1' then -- asynchronous reset (active high) + pos_edge <= '0'; + neg_edge <= '0'; + elsif rising_edge(clk) then -- rising clock edge + neg_edge <= '0'; + pos_edge <= '0'; + sck_old <= sck_sync; + if sck_old = '1' and sck_sync = '0' then + neg_edge <= '1'; + elsif sck_old = '0' and sck_sync = '1' then + pos_edge <= '1'; + end if; + end if; + end process edge_detector; + + shifter : process (clk, rst) is + begin -- process shifter + if rst = '1' then -- asynchronous reset (active high) + shift_reg <= (others => '0'); + dat_i_ack <= '0'; + dat_o <= (others => '0'); + miso_int <= '0'; + cnt <= 0; + elsif rising_edge(clk) then -- rising clock edge + dat_i_ack <= '0'; + dat_o_strb <= '0'; + if pos_edge = '1' and cs_sync = '0' then + shift_reg <= shift_reg(DAT_WIDTH-2 downto 0) & mosi_sync; + miso_int <= shift_reg(7); + if cnt < DAT_WIDTH-1 then + cnt <= cnt + 1; + else + cnt <= 0; + end if; + if cnt = DAT_WIDTH - 1 then + dat_o <= shift_reg(DAT_WIDTH -2 downto 0) & mosi_sync; + dat_o_strb <= '1'; + shift_reg <= dat_i; + dat_i_ack <= '1'; + end if; + end if; + end if; + end process shifter; + + miso <= miso_int when cs = '0' else 'Z'; + +end architecture RTL; diff --git a/top.vhd b/top.vhd new file mode 100644 index 0000000..1fe916c --- /dev/null +++ b/top.vhd @@ -0,0 +1,184 @@ +------------------------------------------------------------------------------- +-- Title : Top Level of SPI => WS2812b +-- Project : +------------------------------------------------------------------------------- +-- File : top.vhd +-- Author : Mario Hüttel +-- Company : +-- Created : 2018-04-15 +-- Last update: 2018-04-15 +-- Platform : +-- Standard : VHDL'93/02 +------------------------------------------------------------------------------- +-- Description: +------------------------------------------------------------------------------- +-- Copyright (c) 2018 GPLv2 +------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity top is + + port ( + clk : in std_logic; + rst_hw : in std_logic; + sck : in std_logic; + mosi : in std_logic; + cs : in std_logic; + ready : out std_logic; -- goes high when at least 10 LEDs can + -- be accepted + ws_out : out std_logic); + +end entity top; + +architecture RTL of top is + constant FIFO_DEPTH : positive := 20; + + signal rst : std_logic; + signal fifo_empty : std_logic; + signal fifo_full : std_logic; + signal fifo_wr : std_logic; + signal fifo_rd : std_logic; + signal fifo_i : std_logic_vector(23 downto 0); + signal fifo_o : std_logic_vector(23 downto 0); + signal spi_dat_o : std_logic_vector(23 downto 0); + signal spi_dat_o_strb : std_logic; + signal fifo_fill_cnt : integer range 0 to FIFO_DEPTH; + signal ws_strb : std_logic; + signal red : unsigned(7 downto 0); + signal green : unsigned(7 downto 0); + signal blue : unsigned(7 downto 0); + signal ws_busy : std_logic; + signal fifo_inc : std_logic; + signal fifo_dec : std_logic; + signal fifo_data_req : std_logic; + signal fifo_data_avail : std_logic; +begin -- architecture RTL + + reset_sync : process (clk, rst_hw) is + begin -- process reset_sync + if rst_hw = '1' then -- asynchronous reset (active high) + rst <= '1'; + elsif rising_edge(clk) then -- rising clock edge + rst <= rst_hw; + end if; + end process reset_sync; + + STD_FIFO_1 : entity work.STD_FIFO + generic map ( + DATA_WIDTH => 24, + FIFO_DEPTH => FIFO_DEPTH) -- 20 LEDs FIFO depth + port map ( + CLK => clk, + RST => rst, + WriteEn => fifo_wr, + DataIn => fifo_i, + ReadEn => fifo_rd, + DataOut => fifo_o, + Empty => fifo_empty, + Full => fifo_full); + + spi_slave_1 : entity work.spi_slave + generic map ( + DAT_WIDTH => 24) + port map ( + clk => clk, + rst => rst, + cs => cs, + sck => sck, + miso => open, + mosi => mosi, + dat_o => spi_dat_o, + dat_i => (others => '0'), + dat_o_strb => spi_dat_o_strb, + dat_i_ack => open); + + ws2812bphy_1 : entity work.ws2812bphy + generic map ( + HIGH1 => 40, + LOW1 => 23, + HIGH0 => 20, + LOW0 => 43) + port map ( + clk => clk, + rst => rst, + busy => ws_busy, + ws_out => ws_out, + strb => ws_strb, + red => red, + green => green, + blue => blue); + + fill_cnt_proc : process (clk, rst) is + variable inc_dec : std_logic_vector(1 downto 0); + begin -- process fill_cnt_proc + if rst = '1' then -- asynchronous reset (active high) + fifo_fill_cnt <= 0; + elsif rising_edge(clk) then -- rising clock edge + inc_dec := fifo_inc & fifo_dec; + if inc_dec = "10" then + fifo_fill_cnt <= fifo_fill_cnt + 1; + elsif inc_dec = "01" then + fifo_fill_cnt <= fifo_fill_cnt -1; + end if; + end if; + end process fill_cnt_proc; + + ready <= '1' when fifo_fill_cnt <= 10 else '0'; + + spi2fifo_proc : process (clk, rst) is + begin -- process spi2fifo_proc + if rst = '1' then -- asynchronous reset (active high) + fifo_inc <= '0'; + fifo_wr <= '0'; + fifo_i <= (others => '0'); + elsif rising_edge(clk) then -- rising clock edge + fifo_wr <= '0'; + fifo_inc <= '0'; + if spi_dat_o_strb = '1' and fifo_full /= '1' then + fifo_wr <= '1'; + fifo_i <= spi_dat_o; + fifo_inc <= '1'; + end if; + end if; + end process spi2fifo_proc; + + fifo2ws_proc : process (clk, rst) is + begin -- process fifo2ws_proc + if rst = '1' then -- asynchronous reset (active high) + fifo_rd <= '0'; + fifo_dec <= '0'; + ws_strb <= '0'; + red <= x"00"; + blue <= x"00"; + green <= x"00"; + fifo_data_req <= '0'; + fifo_data_avail <= '0'; + elsif rising_edge(clk) then -- rising clock edge + fifo_rd <= '0'; + ws_strb <= '0'; + fifo_dec <= '0'; + -- TODO: Write following lines as statemachine + if fifo_empty = '0' and fifo_data_req = '0' and fifo_data_avail = '0' then + fifo_data_req <= '1'; + fifo_rd <= '1'; + fifo_dec <= '1'; + end if; + if fifo_data_req = '1' then + fifo_data_avail <= '1'; + fifo_data_req <= '0'; + end if; + if ws_busy = '0' and ws_strb = '0' and fifo_data_avail = '1' then + red <= unsigned(fifo_o(23 downto 16)); + green <= unsigned(fifo_o(15 downto 8)); + blue <= unsigned(fifo_o(7 downto 0)); + ws_strb <= '1'; + fifo_data_avail <= '0'; + end if; + end if; + end process fifo2ws_proc; + +end architecture RTL; + + diff --git a/ws2812/ws2812bphy.vhd b/ws2812/ws2812bphy.vhd new file mode 100644 index 0000000..df9c06a --- /dev/null +++ b/ws2812/ws2812bphy.vhd @@ -0,0 +1,121 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity ws2812bphy is + generic( + HIGH1 : integer := 40; + LOW1 : integer := 23; + HIGH0 : integer := 20; + LOW0 : integer := 43); + port( + clk : in std_logic; + rst : in std_logic; + busy : out std_logic; + ws_out : out std_logic; + strb : in std_logic; + red : in unsigned(7 downto 0); + green : in unsigned(7 downto 0); + blue : in unsigned(7 downto 0)); +end entity ws2812bphy; + +architecture RTL of ws2812bphy is + + type ws_output_state_t is (IDLE, TRANSMITTING, INTERLEDDELAY); + signal ws_output_state : ws_output_state_t; + + type bitstate_t is (LOW, HIGH); + signal bitstate : bitstate_t; + +begin + ws_output : process(clk, rst) is + variable counter : integer range 0 to 255 := 0; + + variable color_vector : std_logic_vector(23 downto 0); + variable bitnum : integer range 0 to 23; + begin + if rst = '1' then + ws_output_state <= IDLE; + color_vector := (others => '0'); + counter := 0; + bitstate <= LOW; + bitnum := 0; + elsif rising_edge(clk) then + case ws_output_state is + when IDLE => + bitstate <= LOW; + if strb = '1' then + ws_output_state <= TRANSMITTING; + bitnum := 23; + color_vector := std_logic_vector(green) & std_logic_vector(red) & std_logic_vector(blue); + counter := 0; + bitstate <= HIGH; + end if; + when TRANSMITTING => + case bitstate is + when HIGH => + if color_vector(bitnum) = '1' then + if counter < HIGH1 - 1 then + counter := counter + 1; + else + bitstate <= LOW; + counter := 0; + end if; + else + if counter < HIGH0 -1 then + counter := counter + 1; + else + bitstate <= LOW; + counter := 0; + end if; + end if; + when LOW => + if color_vector(bitnum) = '1' then + if counter < LOW1 -1 then + counter := counter + 1; + else + bitstate <= HIGH; + counter := 0; + if bitnum = 0 then + ws_output_state <= INTERLEDDELAY; + counter := 0; + bitstate <= LOW; + else + bitnum := bitnum - 1; + end if; + end if; + else + if counter < LOW0 - 1 then + counter := counter + 1; + else + bitstate <= HIGH; + counter := 0; + + if bitnum = 0 then + ws_output_state <= INTERLEDDELAY; + counter := 0; + bitstate <= LOW; + else + bitnum := bitnum - 1; + end if; + + end if; + end if; + + end case; + + when INTERLEDDELAY => + if counter < HIGH1+LOW1+LOW1 then + counter := counter + 1; + else + counter := 0; + ws_output_state <= IDLE; + end if; + + end case; + end if; + end process ws_output; + busy <= '0' when ws_output_state = IDLE else '1'; + ws_out <= '1' when bitstate = HIGH else '0'; + +end architecture RTL;