1
0
mirror of https://github.com/antonblanchard/microwatt.git synced 2026-02-25 08:19:47 +00:00
Files
antonblanchard.microwatt/syscon.vhdl
Benjamin Herrenschmidt cc4dcb3597 spi: Add SPI Flash controller
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>
2020-06-13 00:01:48 +10:00

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;