177 lines
5.8 KiB
VHDL
177 lines
5.8 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;
|
|
cs_inactive_cycles : integer := 10
|
|
);
|
|
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; -- SPI clock
|
|
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;
|
|
signal prevent_retrig : boolean;
|
|
signal cs_inactive_cnt : integer range 0 to cs_inactive_cycles;
|
|
begin
|
|
toSpi : process(clk, rst) is
|
|
procedure default_state is
|
|
begin
|
|
data_next <= '0';
|
|
data_out_valid <= '0';
|
|
pseudoEdge <= false;
|
|
cs_inactive_cnt <= 0;
|
|
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 =>
|
|
cs_inactive_cnt <= cs_inactive_cnt;
|
|
prevent_retrig <= false;
|
|
delayCycle <= '0';
|
|
spi_sck <= '0';
|
|
oneBitRead <= '0';
|
|
dummy_passed <= false;
|
|
data_out <= (others => '0');
|
|
if ckDiv = clk_divider - 2 then -- ensures cs inactive time between transactions
|
|
if cs_inactive_cnt = cs_inactive_cycles then
|
|
if data_in_valid = '1' then
|
|
ckDiv <= 0;
|
|
state <= TX;
|
|
bitCounter <= 0;
|
|
bitCounterIn <= 0;
|
|
data_in_length_i <= 0;
|
|
pseudoEdge <= true;
|
|
end if;
|
|
else
|
|
ckDiv <= 0;
|
|
cs_inactive_cnt <= cs_inactive_cnt + 1;
|
|
end if;
|
|
else
|
|
ckDiv <= ckDiv + 1;
|
|
end if;
|
|
when TX =>
|
|
if data_in_valid = '0' then
|
|
prevent_retrig <= true;
|
|
end if;
|
|
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' and not prevent_retrig 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;
|
|
ckDiv <= 0;
|
|
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;
|