221 lines
7.1 KiB
VHDL
221 lines
7.1 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- flashrom_wb.vhd: Wishbone 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/.
|
|
-- -------------------------------------------------------------------------- --
|
|
|
|
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;
|