-- -------------------------------------------------------------------------- -- -- flashrom_wb.vhd: Wishbone Flashrom Controller -- -- Copyright (C) 2017 Markus Koch -- -- 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/. -- -------------------------------------------------------------------------- -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; library ip; use ip.wishbone_package.all; use ip.all; use work.flashrom_pkg.all; entity flashrom_wb is port( clk : in std_logic; rst : in std_logic; clr : in std_logic; -- Wishbone wb_in : in wishbone_v3_slave_in; wb_out : out wishbone_v3_slave_out; dbg_allow_write : 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_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_wb; architecture rtl of flashrom_wb is -- Controller Signals signal current_page : std_logic_vector(FLASHROM_PAGE_ADDR_WIDTH - 1 downto 0); signal ready : std_logic; signal sync_stb : std_logic; signal load_stb : std_logic; signal status_update_stb : std_logic; signal status : std_logic_vector(7 downto 0); signal info : std_logic_vector(31 downto 0); signal data_in : std_logic_vector(7 downto 0); signal data_in_next : std_logic; signal data_out : std_logic_vector(7 downto 0); signal data_out_valid : std_logic; -- signal bootup_complete : std_logic; type state_t is (WAITCON, IDLE, LOADPAGE, WRITEPAGE); signal state : state_t; -- signal requested_page : std_logic_vector(current_page'range); signal requested_byte : std_logic_vector(7 downto 0); signal dirty : std_logic; -- Cache Memory type cache_t is array (0 to 63) of std_logic_vector(31 downto 0); signal cache : cache_t; signal cache_addr : std_logic_vector(7 downto 0); signal cache_control_addr : unsigned(7 downto 0); signal cache_we : std_logic; signal cache_dOut : std_logic_vector(31 downto 0); signal cache_dIn : std_logic_vector(31 downto 0); signal data_in_shift : std_logic_vector(31 downto 0); signal delay_cycle : integer range 0 to 1; begin flashrom_controller_inst : entity work.flashrom_controller port map( clk => clk, rst => rst, clr => clr, page => current_page, ready => ready, sync_stb => sync_stb, load_stb => load_stb, status_update_stb => status_update_stb, status => status, info => info, data_in => data_in, data_in_next => data_in_next, data_out => data_out, data_out_valid => data_out_valid, spi_si => spi_si, spi_so => spi_so, spi_sck => spi_sck, spi_reset_n => spi_reset_n, spi_cs_n => spi_cs_n, spi_wp_n => spi_wp_n ); wb_ctrl : process(clk, rst) is procedure default_state is begin load_stb <= '0'; sync_stb <= '0'; wb_out.ACK <= '0'; cache_we <= '0'; end procedure default_state; procedure reset_state is begin default_state; state <= WAITCON; bootup_complete <= '0'; dirty <= '0'; cache_control_addr <= (others => '0'); delay_cycle <= 1; 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 WAITCON => if ready = '1' then state <= IDLE; end if; when IDLE => if (delay_cycle /= 0) then delay_cycle <= delay_cycle - 1; end if; if ready = '1' and delay_cycle = 0 and bootup_complete = '1' then if wb_in.CYC = '1' and wb_in.STB = '1' and wb_out.ACK <= '0' then -- wb_out.ACK ensures empty period between accesses if (requested_page /= current_page) then -- Page swap required if (dirty = '0') then state <= LOADPAGE; load_stb <= '1'; cache_control_addr <= (others => '1'); -- Wraparound... end if; if (dirty = '1') then state <= WRITEPAGE; sync_stb <= '1'; cache_control_addr <= to_unsigned(1, cache_control_addr'length); end if; else -- Same page wb_out.ACK <= '1'; if wb_in.WE = '1' then dirty <= dbg_allow_write; -- TODO: SWITCH BACK TO '1' TO ALLOW WRITES! cache_dIn <= wb_in.DAT; cache_we <= '1'; cache_control_addr <= unsigned(wb_in.ADR(7 downto 0)); end if; end if; end if; end if; if (bootup_complete = '0') then state <= LOADPAGE; load_stb <= '1'; cache_control_addr <= (others => '1'); -- Wraparound... end if; when LOADPAGE => bootup_complete <= '1'; current_page <= requested_page; if data_out_valid = '1' then cache_dIn <= cache_dIn(23 downto 0) & data_out; cache_control_addr <= cache_control_addr + 1; cache_we <= '1'; if cache_control_addr = 254 then state <= IDLE; delay_cycle <= 1; end if; end if; when WRITEPAGE => if cache_control_addr = 1 then data_in_shift <= cache_dOut; end if; if data_in_next = '1' then if cache_control_addr(1 downto 0) = "00" then data_in_shift <= cache_dOut; else data_in_shift <= data_in_shift(23 downto 0) & x"00"; end if; cache_control_addr <= cache_control_addr + 1; if cache_control_addr = 0 then state <= IDLE; dirty <= '0'; delay_cycle <= 1; -- TODO: 1 should be enough end if; end if; end case; end if; end if; end process wb_ctrl; requested_page <= wb_in.ADR(FLASHROM_PAGE_ADDR_WIDTH + 8 - 1 downto 8); requested_byte <= wb_in.ADR(7 downto 0); data_in <= data_in_shift(31 downto 24); -- Cache Memory Controller cache_mem_p : process(clk, rst) is begin if rst = '1' then cache_dOut <= (others => '0'); elsif rising_edge(clk) then cache_dOut <= cache(to_integer(unsigned(cache_addr(cache_addr'length - 1 downto 2)))); if cache_we = '1' then cache(to_integer(unsigned(cache_addr(cache_addr'length - 1 downto 2)))) <= cache_dIn; end if; end if; end process cache_mem_p; cache_addr <= wb_in.ADR(7 downto 0) when (state = IDLE and cache_we = '0') else std_logic_vector(cache_control_addr); wb_out.DAT <= cache_dOut; status_update_stb <= '0'; end architecture rtl;