2017-03-17 22:10:27 +01:00
|
|
|
-- -------------------------------------------------------------------------- --
|
|
|
|
-- flashrom_controller.vhd: Flashrom Controller
|
|
|
|
--
|
|
|
|
-- Copyright (C) 2017 Markus Koch <markus@notsyncing.net>
|
|
|
|
--
|
|
|
|
-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
-- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
-- -------------------------------------------------------------------------- --
|
|
|
|
|
2016-08-04 19:22:38 +02:00
|
|
|
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
|
2016-12-10 20:48:43 +01:00
|
|
|
page : in std_logic_vector(FLASHROM_PAGE_ADDR_WIDTH - 1 downto 0);
|
2016-08-04 19:22:38 +02:00
|
|
|
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
|
2016-12-04 18:00:31 +01:00
|
|
|
status : out std_logic_vector(7 downto 0); -- value of the status register (update using status_update_stb)
|
2016-08-04 19:22:38 +02:00
|
|
|
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);
|
2016-12-06 19:53:39 +01:00
|
|
|
data_in_next : out std_logic;
|
2016-08-04 19:22:38 +02:00
|
|
|
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
|
2016-12-10 20:48:43 +01:00
|
|
|
spi_sck : out std_logic; -- SPI clock
|
2016-08-04 19:22:38 +02:00
|
|
|
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
|
2016-12-06 21:24:09 +01:00
|
|
|
constant spif_max_word_length : integer := 32;
|
|
|
|
constant max_dummy_bits : integer := 63;
|
|
|
|
constant bootup_delay : integer := 4000; -- TODO: These times need to be dependent on f_clk!
|
|
|
|
constant status_reg_poll_delay : integer := 100000; -- TODO: These times need to be dependent on f_clk!
|
2016-08-04 19:22:38 +02:00
|
|
|
|
2016-12-06 21:24:09 +01:00
|
|
|
type state_t is (INIT, GETINFO, GETSTATUS, LOADPAGE, WRITEPAGE, WAIT_BUSY, IDLE);
|
2016-08-04 19:22:38 +02:00
|
|
|
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;
|
2016-12-06 19:53:39 +01:00
|
|
|
signal command_is_latched : boolean;
|
2016-08-04 19:22:38 +02:00
|
|
|
begin
|
|
|
|
spi_wp_n <= '1';
|
|
|
|
|
2017-03-01 19:08:01 +01:00
|
|
|
fancy_spi_master_inst : entity work.fancy_spi_master
|
2016-08-04 19:22:38 +02:00
|
|
|
generic map(
|
2017-03-01 19:08:01 +01:00
|
|
|
TX_WIDTH_MAX => spif_max_word_length,
|
|
|
|
RX_WIDTH_MAX => spif_max_word_length,
|
|
|
|
RX_IGNORE_MAX => max_dummy_bits + 400,
|
|
|
|
SPI_CPOL => '0',
|
|
|
|
SPI_CPHA => '0',
|
|
|
|
CKDIV => 8,
|
|
|
|
CS_INACTIVE_DELAY => 400
|
|
|
|
)
|
2016-08-04 19:22:38 +02:00
|
|
|
port map(
|
2017-03-01 19:08:01 +01:00
|
|
|
clk => clk,
|
|
|
|
rst => rst,
|
|
|
|
clr => clr,
|
|
|
|
tx_width => spif_data_in_length,
|
|
|
|
tx_enable => spif_data_in_valid,
|
|
|
|
tx_data => spif_data_in,
|
|
|
|
tx_next => spif_data_next,
|
|
|
|
rx_width => spif_data_out_length,
|
|
|
|
rx_valid => spif_data_out_valid,
|
|
|
|
rx_data => spif_data_out,
|
|
|
|
rx_ignore => spif_data_out_dummy_bits,
|
|
|
|
spi_clk => spi_sck,
|
|
|
|
spi_cs_n => spi_cs_n,
|
|
|
|
spi_mosi => spi_si,
|
|
|
|
spi_miso => spi_so
|
|
|
|
);
|
2016-08-04 19:22:38 +02:00
|
|
|
|
|
|
|
flashrom_controller_p : process(clk, rst) is
|
2016-12-06 19:53:39 +01:00
|
|
|
variable write_cnt : integer range 0 to bootup_delay;
|
2016-12-06 21:24:09 +01:00
|
|
|
variable delay_cnt : integer range 0 to status_reg_poll_delay;
|
|
|
|
|
|
|
|
variable done : boolean;
|
2016-08-04 20:59:43 +02:00
|
|
|
|
2016-08-04 19:22:38 +02:00
|
|
|
procedure default_state is
|
|
|
|
begin
|
|
|
|
spi_reset_n <= '1';
|
|
|
|
spif_data_in_valid <= '0';
|
2016-12-04 18:27:58 +01:00
|
|
|
done := false;
|
2016-12-06 19:53:39 +01:00
|
|
|
command_is_latched <= false;
|
2016-08-04 19:22:38 +02:00
|
|
|
end procedure default_state;
|
|
|
|
|
|
|
|
procedure reset_state is
|
|
|
|
begin
|
|
|
|
default_state;
|
|
|
|
state <= INIT;
|
|
|
|
spi_reset_n <= '0';
|
|
|
|
words_sent <= 0;
|
2016-12-06 19:53:39 +01:00
|
|
|
write_cnt := 0;
|
2016-08-04 19:22:38 +02:00
|
|
|
|
|
|
|
spif_data_in <= (others => '0');
|
|
|
|
spif_data_in_length <= 0;
|
|
|
|
spif_data_out_length <= 0;
|
|
|
|
spif_data_out_dummy_bits <= 0;
|
2016-08-04 20:59:43 +02:00
|
|
|
|
2016-12-06 19:53:39 +01:00
|
|
|
write_cnt := 0;
|
2016-12-06 21:24:09 +01:00
|
|
|
delay_cnt := 0;
|
2016-08-04 19:22:38 +02:00
|
|
|
end procedure reset_state;
|
2016-12-04 18:27:58 +01:00
|
|
|
|
2016-12-05 20:37:04 +01:00
|
|
|
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
|
2016-12-04 18:27:58 +01:00
|
|
|
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';
|
2016-12-05 20:37:04 +01:00
|
|
|
spif_data_in <= data_in & padBits(spif_data_in, data_in);
|
2016-12-04 18:27:58 +01:00
|
|
|
|
2016-12-06 19:53:39 +01:00
|
|
|
if write_cnt = nTxWords then
|
2016-12-04 18:27:58 +01:00
|
|
|
spif_data_in_valid <= '0';
|
|
|
|
elsif spif_data_next = '1' then
|
2016-12-06 19:53:39 +01:00
|
|
|
write_cnt := write_cnt + 1;
|
2016-12-04 18:27:58 +01:00
|
|
|
end if;
|
|
|
|
|
2016-12-06 19:53:39 +01:00
|
|
|
if spif_data_out_valid = '1' and (write_cnt = nTxWords) then
|
2016-12-04 18:27:58 +01:00
|
|
|
spif_data_in_valid <= '0';
|
|
|
|
done := true;
|
|
|
|
state <= IDLE;
|
2016-12-06 19:53:39 +01:00
|
|
|
write_cnt := 0;
|
2016-12-04 18:27:58 +01:00
|
|
|
end if;
|
|
|
|
end procedure run_command_single;
|
|
|
|
|
2016-08-04 19:22:38 +02:00
|
|
|
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;
|
2016-12-06 21:24:09 +01:00
|
|
|
if delay_cnt = bootup_delay then
|
|
|
|
delay_cnt := 0;
|
2016-12-06 19:53:39 +01:00
|
|
|
state <= GETINFO;
|
2016-08-04 20:59:43 +02:00
|
|
|
else
|
2016-12-06 21:24:09 +01:00
|
|
|
delay_cnt := delay_cnt + 1;
|
2016-08-04 20:59:43 +02:00
|
|
|
end if;
|
2016-12-04 18:27:58 +01:00
|
|
|
when GETINFO => -- TODO: In simulation I can only call this command once?!
|
2016-12-05 20:37:04 +01:00
|
|
|
run_command_single(8, 32, 8, 5, FLASHROM_COMMAND_MANUFACTURER_ID);
|
2016-12-04 18:27:58 +01:00
|
|
|
if done then
|
|
|
|
info <= spif_data_out;
|
2016-08-04 19:22:38 +02:00
|
|
|
end if;
|
2016-12-04 18:00:31 +01:00
|
|
|
when IDLE =>
|
2016-12-06 19:53:39 +01:00
|
|
|
write_cnt := 0;
|
2016-12-06 21:24:09 +01:00
|
|
|
delay_cnt := 0;
|
2016-12-04 18:00:31 +01:00
|
|
|
if status_update_stb = '1' then
|
2016-12-04 18:27:58 +01:00
|
|
|
state <= GETSTATUS;
|
2016-12-04 18:00:31 +01:00
|
|
|
end if;
|
2016-12-06 19:53:39 +01:00
|
|
|
if load_stb = '1' then
|
2016-12-05 20:37:04 +01:00
|
|
|
state <= LOADPAGE;
|
2016-12-04 18:27:58 +01:00
|
|
|
end if;
|
2016-12-06 19:53:39 +01:00
|
|
|
if sync_stb = '1' then
|
|
|
|
state <= WRITEPAGE;
|
|
|
|
end if;
|
2016-12-04 18:00:31 +01:00
|
|
|
when GETSTATUS =>
|
2016-12-05 20:37:04 +01:00
|
|
|
run_command_single(8, 8, 16, 3, FLASHROM_COMMAND_GET_STATUS);
|
2016-12-04 18:27:58 +01:00
|
|
|
if done then
|
2016-12-04 18:00:31 +01:00
|
|
|
status <= spif_data_out(7 downto 0);
|
|
|
|
end if;
|
2016-12-06 21:24:09 +01:00
|
|
|
when WAIT_BUSY =>
|
|
|
|
if delay_cnt = status_reg_poll_delay then
|
|
|
|
run_command_single(8, 8, 16, 3, FLASHROM_COMMAND_GET_STATUS);
|
|
|
|
if done then
|
|
|
|
status <= spif_data_out(7 downto 0);
|
|
|
|
delay_cnt := 0;
|
|
|
|
if spif_data_out(7) = '1' then
|
|
|
|
state <= IDLE;
|
|
|
|
else
|
|
|
|
state <= WAIT_BUSY;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
else
|
|
|
|
delay_cnt := delay_cnt + 1;
|
|
|
|
end if;
|
2016-12-05 20:37:04 +01:00
|
|
|
when LOADPAGE =>
|
2017-03-01 19:08:01 +01:00
|
|
|
run_command_single(32, 8, 40, 258, FLASHROM_COMMAND_CONT_ARRAY_READ & page);
|
2016-12-06 19:53:39 +01:00
|
|
|
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;
|
2016-12-05 20:37:04 +01:00
|
|
|
if done then
|
|
|
|
report "Load page done." severity note;
|
|
|
|
end if;
|
2016-12-06 19:53:39 +01:00
|
|
|
when WRITEPAGE =>
|
2017-03-01 19:08:01 +01:00
|
|
|
run_command_single(32, 8, 40, 257, FLASHROM_COMMAND_WRITE_THROUGH_1 & page);
|
2016-12-06 19:53:39 +01:00
|
|
|
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;
|
2016-12-06 21:24:09 +01:00
|
|
|
if done then
|
|
|
|
delay_cnt := 0;
|
|
|
|
state <= WAIT_BUSY;
|
|
|
|
end if;
|
2016-08-04 19:22:38 +02:00
|
|
|
end case;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
end process flashrom_controller_p;
|
2016-12-04 18:00:31 +01:00
|
|
|
ready <= '1' when state = IDLE else '0';
|
2016-12-05 20:37:04 +01:00
|
|
|
|
|
|
|
data_out <= spif_data_out(7 downto 0);
|
|
|
|
data_out_valid <= spif_data_out_valid when state = LOADPAGE else '0';
|
2016-12-06 19:53:39 +01:00
|
|
|
data_in_next <= spif_data_next when (state = WRITEPAGE) and command_is_latched else '0';
|
2016-08-04 19:22:38 +02:00
|
|
|
end architecture RTL;
|