lw35-upgrade/display/fpga/design/spi_if.vhd

166 lines
4.2 KiB
VHDL

-- FIXME: synchronizer
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity spi_if is
port(
clk : in std_logic;
rst : in std_logic; --
-- Local memory IF
addr : out std_logic_vector(12 downto 0);
data_in : in std_logic_vector(8 downto 0);
data_we : out std_logic;
data_out : out std_logic_vector(8 downto 0); --
-- Other control signals
vsync_rq : out std_logic; --
-- SPI IF
spi_cs_n : in std_logic; -- SPI chip select, active low
spi_sck : in std_logic; -- SPI clock
spi_mosi : in std_logic; -- SPI data input
spi_miso : inout std_logic -- SPI data output
);
end entity spi_if;
architecture RTL of spi_if is
signal spi_sck_last : std_logic;
signal sr_in : std_logic_vector(3 downto 0);
signal sr_out : std_logic_vector(3 downto 0);
signal bit_cnt : unsigned(1 downto 0);
signal rx_stb : std_logic;
type state_t is (IDLE, ADDRESS, DATA, FLUSH, LOCK);
signal state : state_t;
signal word_cnt : unsigned(3 downto 0);
signal cache_nor : std_logic_vector(2 * 4 * 4 - 1 downto 0);
signal cache_extra : std_logic_vector(4 - 1 downto 0);
begin
spi_receiver : process(clk, rst) is
begin
if (rst = '1') then
spi_miso <= 'Z';
bit_cnt <= (others => '0');
sr_in <= (others => '0');
rx_stb <= '0';
spi_sck_last <= '0';
elsif (rising_edge(clk)) then
rx_stb <= '0';
spi_sck_last <= spi_sck;
if (spi_cs_n = '0') then
if (spi_sck = '1' and spi_sck_last = '0') then -- rising edge of SPI clock, write
sr_in <= sr_in(sr_in'high - 1 downto 0) & spi_mosi;
bit_cnt <= bit_cnt + 1;
if (bit_cnt = 3) then
rx_stb <= '1';
end if;
elsif (spi_sck = '0' and spi_sck_last = '1') then -- falling edge of SPI clock, read
end if;
else
spi_miso <= 'Z';
bit_cnt <= (others => '0');
sr_in <= (others => '0');
end if;
end if;
end process spi_receiver;
proc : process(clk, rst) is
begin
if (rst = '1') then
addr <= (others => '0');
data_we <= '0';
word_cnt <= (others => '0');
cache_nor <= (others => '0');
cache_extra <= (others => '0');
vsync_rq <= '0';
state <= IDLE;
elsif (rising_edge(clk)) then
data_we <= '0';
vsync_rq <= '0';
if (spi_cs_n = '0') then
case state is
when IDLE =>
if (rx_stb = '1') then
word_cnt <= word_cnt + 1;
if (word_cnt = 1) then
if (sr_in = x"0") then -- Start at addr
state <= ADDRESS;
word_cnt <= (others => '0');
addr <= (others => '0');
elsif (sr_in = x"1") then -- Continue
state <= DATA;
word_cnt <= (others => '0');
else
state <= LOCK;
end if;
end if;
end if;
when ADDRESS =>
if (rx_stb = '1') then
addr <= addr(addr'high - 4 downto 0) & sr_in;
word_cnt <= word_cnt + 1;
if (word_cnt = 3) then
state <= DATA;
word_cnt <= (others => '0');
end if;
end if;
when DATA =>
if (data_we = '1') then -- If we came from FLUSH
addr <= std_logic_vector(unsigned(addr) + 1);
end if;
if (rx_stb = '1') then
word_cnt <= word_cnt + 1;
if (word_cnt = 8) then
cache_extra <= sr_in;
state <= FLUSH;
word_cnt <= (others => '0');
data_we <= '1';
else
cache_nor <= cache_nor(cache_nor'high - 4 downto 0) & sr_in;
end if;
end if;
when FLUSH =>
addr <= std_logic_vector(unsigned(addr) + 1);
cache_nor <= cache_nor(cache_nor'high - 8 downto 0) & x"00";
cache_extra <= cache_extra(cache_extra'high - 1 downto 0) & '0';
data_we <= '1';
word_cnt <= word_cnt + 1;
if (word_cnt = 2) then
state <= DATA;
word_cnt <= (others => '0');
end if;
when LOCK =>
vsync_rq <= '1';
null;
end case;
else
state <= IDLE;
--addr <= (others => '0');
data_we <= '0';
word_cnt <= (others => '0');
end if;
end if;
end process proc;
data_out <= cache_nor(cache_nor'high downto cache_nor'high - 7) & cache_extra(cache_extra'high);
end architecture RTL;