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

199 lines
7.0 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.flashrom_pkg.all;
entity flashrom_controller is
port(
clk : in std_logic;
rst : in std_logic;
clr : in std_logic;
-- Control IF
ready : out std_logic; -- The controller is ready to accept commands
page : in std_logic_vector(FLASHROM_ADDR_WIDTH - 1 downto 0);
sync_stb : in std_logic; -- Synchronize current memory page with chip, only sampled when ready
load_stb : in std_logic; -- Load page into local buffer, only sampled when ready
status_update_stb : in std_logic; -- Update status vector
status : out std_logic_vector(7 downto 0); -- value of the status register (update using status_update_stb)
info : out std_logic_vector(31 downto 0); -- value of the information register (updated on reset)
-- Data IF
data_in : in std_logic_vector(7 downto 0);
data_in_next : out std_logic;
data_out : out std_logic_vector(7 downto 0);
data_out_valid : out 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_reset_n : out std_logic; -- SPI hard reset
spi_cs_n : out std_logic; -- SPI chip select
spi_wp_n : out std_logic -- SPI write protect
);
end entity flashrom_controller;
architecture RTL of flashrom_controller is
constant spif_max_word_length : integer := 32;
constant max_dummy_bits : integer := 63;
constant bootup_delay : integer := 4000;
type state_t is (INIT, GETINFO, GETSTATUS, LOADPAGE, WRITEPAGE, IDLE);
signal state : state_t;
signal spif_data_in_valid : std_logic;
signal spif_data_in : std_logic_vector(spif_max_word_length - 1 downto 0);
signal spif_data_in_length : integer range 0 to spif_max_word_length;
signal spif_data_next : std_logic;
signal spif_data_out : std_logic_vector(spif_max_word_length - 1 downto 0);
signal spif_data_out_valid : std_logic;
signal spif_data_out_length : integer range 0 to spif_max_word_length;
signal words_sent : integer range 0 to 511;
signal spif_data_out_dummy_bits : integer range 0 to max_dummy_bits;
signal spif_transmission_active : std_logic;
signal command_is_latched : boolean;
begin
spi_wp_n <= '1';
flashrom_spi_inst : entity work.flashrom_spi
generic map(
clk_divider => 4,
max_word_length => spif_max_word_length,
max_dummy_bits => max_dummy_bits)
port map(
clk => clk,
rst => rst,
clr => clr,
spi_si => spi_si,
spi_so => spi_so,
spi_sck => spi_sck,
spi_cs_n => spi_cs_n,
data_in_valid => spif_data_in_valid,
data_in => spif_data_in,
data_in_length => spif_data_in_length,
data_next => spif_data_next,
data_out => spif_data_out,
data_out_valid => spif_data_out_valid,
data_out_length => spif_data_out_length,
data_out_dummy_bits => spif_data_out_dummy_bits,
transmission_active => spif_transmission_active);
flashrom_controller_p : process(clk, rst) is
variable write_cnt : integer range 0 to bootup_delay;
variable done : boolean;
procedure default_state is
begin
spi_reset_n <= '1';
spif_data_in_valid <= '0';
done := false;
command_is_latched <= false;
end procedure default_state;
procedure reset_state is
begin
default_state;
state <= INIT;
spi_reset_n <= '0';
words_sent <= 0;
write_cnt := 0;
spif_data_in <= (others => '0');
spif_data_in_length <= 0;
spif_data_out_length <= 0;
spif_data_out_dummy_bits <= 0;
write_cnt := 0;
end procedure reset_state;
procedure run_command_single(constant data_in_length : integer; constant data_out_length : integer; constant data_out_dummy_bits : integer; constant nTxWords : integer; constant data_in : std_logic_vector) is
begin
spif_data_in_length <= data_in_length;
spif_data_out_length <= data_out_length;
spif_data_out_dummy_bits <= data_out_dummy_bits;
spif_data_in_valid <= '1';
spif_data_in <= data_in & padBits(spif_data_in, data_in);
if write_cnt = nTxWords then
spif_data_in_valid <= '0';
elsif spif_data_next = '1' then
write_cnt := write_cnt + 1;
end if;
if spif_data_out_valid = '1' and (write_cnt = nTxWords) then
spif_data_in_valid <= '0';
done := true;
state <= IDLE;
write_cnt := 0;
end if;
end procedure run_command_single;
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 INIT =>
words_sent <= 0;
if write_cnt = bootup_delay then
write_cnt := 0;
state <= GETINFO;
else
write_cnt := write_cnt + 1;
end if;
when GETINFO => -- TODO: In simulation I can only call this command once?!
run_command_single(8, 32, 8, 5, FLASHROM_COMMAND_MANUFACTURER_ID);
if done then
info <= spif_data_out;
end if;
when IDLE =>
write_cnt := 0;
if status_update_stb = '1' then
state <= GETSTATUS;
end if;
if load_stb = '1' then
state <= LOADPAGE;
end if;
if sync_stb = '1' then
state <= WRITEPAGE;
end if;
when GETSTATUS =>
run_command_single(8, 8, 16, 3, FLASHROM_COMMAND_GET_STATUS);
if done then
status <= spif_data_out(7 downto 0);
end if;
when LOADPAGE =>
run_command_single(32, 8, 34, 258, FLASHROM_COMMAND_CONT_ARRAY_READ & page);
if spif_data_next = '1' or command_is_latched then
command_is_latched <= true;
spif_data_in_length <= 8;
spif_data_in <= (others => '0');
end if;
if done then
report "Load page done." severity note;
end if;
when WRITEPAGE =>
run_command_single(32, 8, 34, 257, FLASHROM_COMMAND_WRITE_THROUGH_1 & page);
if spif_data_next = '1' or command_is_latched then
command_is_latched <= true;
spif_data_in_length <= 8;
spif_data_in <= data_in & padBits(spif_data_in, data_in);
end if;
end case;
end if;
end if;
end process flashrom_controller_p;
ready <= '1' when state = IDLE else '0';
data_out <= spif_data_out(7 downto 0);
data_out_valid <= spif_data_out_valid when state = LOADPAGE else '0';
data_in_next <= spif_data_next when (state = WRITEPAGE) and command_is_latched else '0';
end architecture RTL;