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_PAGE_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; -- SPI clock 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; -- 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! type state_t is (INIT, GETINFO, GETSTATUS, LOADPAGE, WRITEPAGE, WAIT_BUSY, 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 command_is_latched : boolean; begin spi_wp_n <= '1'; fancy_spi_master_inst : entity work.fancy_spi_master generic map( 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 ) port map( 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 ); flashrom_controller_p : process(clk, rst) is variable write_cnt : integer range 0 to bootup_delay; variable delay_cnt : integer range 0 to status_reg_poll_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; delay_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 delay_cnt = bootup_delay then delay_cnt := 0; state <= GETINFO; else delay_cnt := delay_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; delay_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 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; when LOADPAGE => run_command_single(32, 8, 40, 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, 40, 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; if done then delay_cnt := 0; state <= WAIT_BUSY; 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;