mor1kx-bemicrocv/cores/flashrom-wb/flashrom_spi.vhd

158 lines
5.1 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity flashrom_spi is
generic(
clk_divider : integer range 2 to 9999 := 2;
max_word_length : integer := 32;
max_dummy_bits : integer := 8
);
port(
clk : in std_logic;
rst : in std_logic;
clr : in std_logic;
-- SPI flash hardware signals
spi_si : out std_logic; -- SPI serial in
spi_so : in std_logic; -- SPI serial out
spi_sck : out std_logic; -- Create clock using PLL, then supply to chip and this module
spi_cs_n : out std_logic; -- SPI chip select
-- Logic interface
data_in_valid : in std_logic; -- Data to Flash ROM
data_in : in std_logic_vector(max_word_length - 1 downto 0);
data_in_length : in integer range 0 to max_word_length;
data_next : out std_logic;
data_out : out std_logic_vector(max_word_length - 1 downto 0);
data_out_valid : out std_logic; -- Data from Flash ROM
data_out_length : in integer range 0 to max_word_length;
data_out_dummy_bits : in integer range 0 to max_dummy_bits;
transmission_active : out std_logic
);
end entity flashrom_spi;
architecture RTL of flashrom_spi is
type txstate_t is (IDLE, TX);
signal state : txstate_t;
signal ckDiv : integer range 0 to clk_divider - 2;
signal shiftreg : std_logic_vector(max_word_length - 1 downto 0);
signal bitCounter : integer range 0 to max_word_length - 1;
signal bitCounterIn : integer range 0 to max_word_length + max_dummy_bits - 1; -- TODO: Actually this must count until the higher of the two
signal data_in_length_i : integer range 0 to max_word_length;
signal data_out_length_i : integer range 0 to max_word_length;
signal delayCycle : std_logic;
signal oneBitRead : std_logic;
signal pseudoEdge : boolean;
signal dummy_passed : boolean;
begin
toSpi : process(clk, rst) is
procedure default_state is
begin
data_next <= '0';
data_out_valid <= '0';
pseudoEdge <= false;
end procedure default_state;
procedure reset_state is
begin
state <= IDLE;
spi_sck <= '0';
shiftreg <= (others => '0');
bitCounter <= 0;
bitCounterIn <= 0;
data_out <= (others => '0');
delayCycle <= '0';
oneBitRead <= '0';
dummy_passed <= false;
default_state;
end procedure reset_state;
begin
if rst = '1' then
reset_state;
elsif rising_edge(clk) then
default_state;
if clr = '1' then
reset_state;
else
case state is
when IDLE =>
delayCycle <= '0';
spi_sck <= '0';
oneBitRead <= '0';
dummy_passed <= false;
data_out <= (others => '0');
if data_in_valid = '1' then
state <= TX;
bitCounter <= 0;
bitCounterIn <= 0;
data_in_length_i <= 0;
pseudoEdge <= true;
end if;
ckDiv <= 0;
when TX =>
if ckDiv = clk_divider - 2 or pseudoEdge then
ckDiv <= 0;
if not pseudoEdge then
spi_sck <= not spi_sck;
end if;
if spi_sck = '0' and bitCounter = data_in_length_i and data_in_valid = '0' then
-- Prevent last clock cycle (make it a "passive" one)
spi_sck <= '0';
pseudoEdge <= true;
end if;
if spi_sck = '1' or pseudoEdge then -- falling edge -> provide data
if bitCounter = data_in_length_i then
bitCounter <= 0;
if data_in_valid = '1' then
shiftreg <= data_in;
data_in_length_i <= data_in_length - 1;
data_out_length_i <= data_out_length - 1;
data_next <= '1';
else
delayCycle <= '1';
end if;
else
bitCounter <= bitCounter + 1;
shiftreg <= shiftreg(shiftreg'high - 1 downto 0) & '0';
end if;
--else -- spi_sck = '1' (falling edge)
data_out <= data_out(data_out'high - 1 downto 0) & spi_so;
if bitCounterIn = 0 then
if dummy_passed then
data_out_valid <= '1';
end if;
end if;
if not dummy_passed then
if bitCounterIn = data_out_dummy_bits then
dummy_passed <= true;
bitCounterIn <= 1;
else
bitCounterIn <= bitCounterIn + 1;
end if;
else
if bitCounterIn = data_out_length_i then
bitCounterIn <= 0;
else
bitCounterIn <= bitCounterIn + 1;
end if;
if delayCycle = '1' then
spi_sck <= '0';
state <= IDLE;
end if;
end if;
end if;
else
ckDiv <= ckDiv + 1;
end if;
end case;
end if;
end if;
end process toSpi;
spi_si <= shiftreg(shiftreg'high);
spi_cs_n <= '0' when state = TX else '1';
transmission_active <= '1' when state = TX else '0';
end architecture RTL;