mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-02-25 08:19:47 +00:00
This adds an SPI flash controller which supports direct memory-mapped access to the flash along with a manual mode to send commands. The direct mode can be set via generic to default to single wire or quad mode. The controller supports normal, dual and quad accesses with configurable commands, clock divider, dummy clocks etc... The SPI clock can be an even divider of sys_clk starting at 2 (so max 50Mhz with our typical Arty designs). A flash offset is carried via generics to syscon to tell SW about which portion of the flash is reserved for the FPGA bitfile. There is currently no plumbing to make the CPU reset past that address (TBD). Note: Operating at 50Mhz has proven unreliable without adding some delay to the sampling of the input data. I'm working in improving this, in the meantime, I'm leaving the default set at 25 Mhz. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
178 lines
6.6 KiB
VHDL
178 lines
6.6 KiB
VHDL
-- syscon module, a bunch of misc global system control MMIO registers
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library work;
|
|
use work.wishbone_types.all;
|
|
|
|
entity syscon is
|
|
generic (
|
|
SIG_VALUE : std_ulogic_vector(63 downto 0) := x"f00daa5500010001";
|
|
CLK_FREQ : integer;
|
|
HAS_UART : boolean;
|
|
HAS_DRAM : boolean;
|
|
BRAM_SIZE : integer;
|
|
DRAM_SIZE : integer;
|
|
DRAM_INIT_SIZE : integer;
|
|
HAS_SPI_FLASH : boolean;
|
|
SPI_FLASH_OFFSET : integer
|
|
);
|
|
port (
|
|
clk : in std_ulogic;
|
|
rst : in std_ulogic;
|
|
|
|
-- Wishbone ports:
|
|
wishbone_in : in wb_io_master_out;
|
|
wishbone_out : out wb_io_slave_out;
|
|
|
|
-- System control ports
|
|
dram_at_0 : out std_ulogic;
|
|
core_reset : out std_ulogic;
|
|
soc_reset : out std_ulogic
|
|
);
|
|
end entity syscon;
|
|
|
|
|
|
architecture behaviour of syscon is
|
|
-- Register address bits
|
|
constant SYS_REG_BITS : positive := 3;
|
|
|
|
-- Register addresses (matches wishbone addr downto 3, ie, 8 bytes per reg)
|
|
constant SYS_REG_SIG : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000";
|
|
constant SYS_REG_INFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "001";
|
|
constant SYS_REG_BRAMINFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "010";
|
|
constant SYS_REG_DRAMINFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "011";
|
|
constant SYS_REG_CLKINFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "100";
|
|
constant SYS_REG_CTRL : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "101";
|
|
constant SYS_REG_DRAMINITINFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "110";
|
|
constant SYS_REG_SPIFLASHINFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "111";
|
|
|
|
-- Muxed reg read signal
|
|
signal reg_out : std_ulogic_vector(63 downto 0);
|
|
|
|
-- INFO register bits
|
|
constant SYS_REG_INFO_HAS_UART : integer := 0;
|
|
constant SYS_REG_INFO_HAS_DRAM : integer := 1;
|
|
constant SYS_REG_INFO_HAS_BRAM : integer := 2;
|
|
constant SYS_REG_INFO_HAS_SPIF : integer := 3;
|
|
|
|
-- BRAMINFO contains the BRAM size in the bottom 52 bits
|
|
-- DRAMINFO contains the DRAM size if any in the bottom 52 bits
|
|
-- (both have reserved top bits for future use)
|
|
-- CLKINFO contains the CLK frequency is HZ in the bottom 40 bits
|
|
|
|
-- CTRL register bits
|
|
constant SYS_REG_CTRL_BITS : positive := 3;
|
|
constant SYS_REG_CTRL_DRAM_AT_0 : integer := 0;
|
|
constant SYS_REG_CTRL_CORE_RESET : integer := 1;
|
|
constant SYS_REG_CTRL_SOC_RESET : integer := 2;
|
|
|
|
-- SPI Info register bits
|
|
--
|
|
-- Top 32-bit is flash offset which is the amount of flash
|
|
-- reserved for the FPGA bitfile if any
|
|
constant SYS_REG_SPI_INFO_IS_FLASH : integer := 0;
|
|
|
|
-- Ctrl register
|
|
signal reg_ctrl : std_ulogic_vector(SYS_REG_CTRL_BITS-1 downto 0);
|
|
signal reg_ctrl_out : std_ulogic_vector(63 downto 0);
|
|
|
|
-- Others
|
|
signal reg_info : std_ulogic_vector(63 downto 0);
|
|
signal reg_braminfo : std_ulogic_vector(63 downto 0);
|
|
signal reg_draminfo : std_ulogic_vector(63 downto 0);
|
|
signal reg_dramiinfo : std_ulogic_vector(63 downto 0);
|
|
signal reg_clkinfo : std_ulogic_vector(63 downto 0);
|
|
signal reg_spiinfo : std_ulogic_vector(63 downto 0);
|
|
signal info_has_dram : std_ulogic;
|
|
signal info_has_bram : std_ulogic;
|
|
signal info_has_uart : std_ulogic;
|
|
signal info_has_spif : std_ulogic;
|
|
signal info_clk : std_ulogic_vector(39 downto 0);
|
|
signal info_fl_off : std_ulogic_vector(31 downto 0);
|
|
begin
|
|
|
|
-- Generated output signals
|
|
dram_at_0 <= '1' when BRAM_SIZE = 0 else reg_ctrl(SYS_REG_CTRL_DRAM_AT_0);
|
|
soc_reset <= reg_ctrl(SYS_REG_CTRL_SOC_RESET);
|
|
core_reset <= reg_ctrl(SYS_REG_CTRL_CORE_RESET);
|
|
|
|
-- All register accesses are single cycle
|
|
wishbone_out.ack <= wishbone_in.cyc and wishbone_in.stb;
|
|
wishbone_out.stall <= '0';
|
|
|
|
-- Info register is hard wired
|
|
info_has_uart <= '1' when HAS_UART else '0';
|
|
info_has_dram <= '1' when HAS_DRAM else '0';
|
|
info_has_bram <= '1' when BRAM_SIZE /= 0 else '0';
|
|
info_has_spif <= '1' when HAS_SPI_FLASH else '0';
|
|
info_clk <= std_ulogic_vector(to_unsigned(CLK_FREQ, 40));
|
|
reg_info <= (SYS_REG_INFO_HAS_UART => info_has_uart,
|
|
SYS_REG_INFO_HAS_DRAM => info_has_dram,
|
|
SYS_REG_INFO_HAS_BRAM => info_has_bram,
|
|
SYS_REG_INFO_HAS_SPIF => info_has_spif,
|
|
others => '0');
|
|
reg_braminfo <= x"000" & std_ulogic_vector(to_unsigned(BRAM_SIZE, 52));
|
|
reg_draminfo <= x"000" & std_ulogic_vector(to_unsigned(DRAM_SIZE, 52)) when HAS_DRAM
|
|
else (others => '0');
|
|
reg_dramiinfo <= x"000" & std_ulogic_vector(to_unsigned(DRAM_INIT_SIZE, 52)) when HAS_DRAM
|
|
else (others => '0');
|
|
reg_clkinfo <= (39 downto 0 => info_clk,
|
|
others => '0');
|
|
info_fl_off <= std_ulogic_vector(to_unsigned(SPI_FLASH_OFFSET, 32));
|
|
reg_spiinfo <= (31 downto 0 => info_fl_off,
|
|
others => '0');
|
|
|
|
-- Control register read composition
|
|
reg_ctrl_out <= (63 downto SYS_REG_CTRL_BITS => '0',
|
|
SYS_REG_CTRL_BITS-1 downto 0 => reg_ctrl);
|
|
|
|
-- Register read mux
|
|
with wishbone_in.adr(SYS_REG_BITS+2 downto 3) select reg_out <=
|
|
SIG_VALUE when SYS_REG_SIG,
|
|
reg_info when SYS_REG_INFO,
|
|
reg_braminfo when SYS_REG_BRAMINFO,
|
|
reg_draminfo when SYS_REG_DRAMINFO,
|
|
reg_dramiinfo when SYS_REG_DRAMINITINFO,
|
|
reg_clkinfo when SYS_REG_CLKINFO,
|
|
reg_ctrl_out when SYS_REG_CTRL,
|
|
reg_spiinfo when SYS_REG_SPIFLASHINFO,
|
|
(others => '0') when others;
|
|
wishbone_out.dat <= reg_out(63 downto 32) when wishbone_in.adr(2) = '1' else
|
|
reg_out(31 downto 0);
|
|
|
|
-- Register writes
|
|
regs_write: process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
if (rst) then
|
|
reg_ctrl <= (others => '0');
|
|
else
|
|
if wishbone_in.cyc and wishbone_in.stb and wishbone_in.we then
|
|
-- Change this if CTRL ever has more than 32 bits
|
|
if wishbone_in.adr(SYS_REG_BITS+2 downto 3) = SYS_REG_CTRL and
|
|
wishbone_in.adr(2) = '0' then
|
|
reg_ctrl(SYS_REG_CTRL_BITS-1 downto 0) <=
|
|
wishbone_in.dat(SYS_REG_CTRL_BITS-1 downto 0);
|
|
end if;
|
|
end if;
|
|
|
|
-- Reset auto-clear
|
|
if reg_ctrl(SYS_REG_CTRL_SOC_RESET) = '1' then
|
|
reg_ctrl(SYS_REG_CTRL_SOC_RESET) <= '0';
|
|
end if;
|
|
if reg_ctrl(SYS_REG_CTRL_CORE_RESET) = '1' then
|
|
reg_ctrl(SYS_REG_CTRL_CORE_RESET) <= '0';
|
|
end if;
|
|
|
|
-- If BRAM doesn't exist, force DRAM at 0
|
|
if BRAM_SIZE = 0 then
|
|
reg_ctrl(SYS_REG_CTRL_DRAM_AT_0) <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end architecture behaviour;
|