ws2812b-spi/spi_slave.vhd

113 lines
3.2 KiB
VHDL

-------------------------------------------------------------------------------
-- Title : simple SPI slave
-- Project :
-------------------------------------------------------------------------------
-- File : spi_slave.vhd
-- Author : Mario Hüttel <mario.huettel@gmx.net>
-- 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');
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;
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_int <= shift_reg(7);
miso <= miso_int when cs = '0' else 'Z';
end architecture RTL;