-- 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.git.all;
use work.wishbone_types.all;

entity syscon is
    generic (
        NCPUS            : positive := 1;
	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;
	HAS_LITEETH      : boolean;
        HAS_SD_CARD      : boolean;
        HAS_SD_CARD2     : boolean;
        UART0_IS_16550   : boolean;
        HAS_UART1        : boolean
	);
    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_vector(NCPUS-1 downto 0);
	soc_reset  : out std_ulogic;
	alt_reset  : out std_ulogic;
        tb_rdp     : out std_ulogic;
        tb_frz     : out std_ulogic
	);
end entity syscon;


architecture behaviour of syscon is
    -- Register address bits
    constant SYS_REG_BITS       : positive := 6;

    -- 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) := "000000";
    constant SYS_REG_INFO	  : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000001";
    constant SYS_REG_BRAMINFO	  : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000010";
    constant SYS_REG_DRAMINFO	  : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000011";
    constant SYS_REG_CLKINFO	  : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000100";
    constant SYS_REG_CTRL	  : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000101";
    constant SYS_REG_DRAMINITINFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000110";
    constant SYS_REG_SPIFLASHINFO : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "000111";
    constant SYS_REG_UART0_INFO   : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "001000";
    constant SYS_REG_UART1_INFO   : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "001001";
    constant SYS_REG_GIT_INFO     : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "001010";
    constant SYS_REG_CPU_CTRL     : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "001011";
    constant SYS_REG_TB_CTRL      : std_ulogic_vector(SYS_REG_BITS-1 downto 0) := "001100";

    -- Muxed reg read signal
    signal reg_out	: std_ulogic_vector(63 downto 0);

    -- INFO register bits
    constant SYS_REG_INFO_HAS_UART    : integer := 0;  -- Has a UART (always set)
    constant SYS_REG_INFO_HAS_DRAM    : integer := 1;  -- Has DRAM
    constant SYS_REG_INFO_HAS_BRAM    : integer := 2;  -- Has "main" BRAM
    constant SYS_REG_INFO_HAS_SPIF    : integer := 3;  -- Has SPI flash
    constant SYS_REG_INFO_HAS_LETH    : integer := 4;  -- Has LiteEth ethernet
    constant SYS_REG_INFO_HAS_LSYS    : integer := 5;  -- Has 6-bit address syscon
    constant SYS_REG_INFO_HAS_URT1    : integer := 6;  -- Has second UART
    constant SYS_REG_INFO_HAS_ARTB    : integer := 7;  -- Has architected TB frequency
    constant SYS_REG_INFO_HAS_SDCARD  : integer := 8;  -- Has LiteSDCard SD-card interface
    constant SYS_REG_INFO_HAS_SDCARD2 : integer := 9;  -- Has 2nd LiteSDCard SD-card interface

    -- 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 := 4;
    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;
    constant SYS_REG_CTRL_ALT_RESET  : integer := 3;

    -- 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;

    -- UART0/1 info registers bits
    --
    --  0 ..31  : UART clock freq (in HZ)
    --      32  : UART is 16550 (otherwise pp)
    --

    -- GIT info register bits
    --
    --  0 ..55  : git hash (14 chars = 56 bits)
    --      63  : dirty flag
    --

    -- 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);
    signal ctrl_init_alt_reset : std_ulogic;

    -- 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 reg_uart0info : std_ulogic_vector(63 downto 0);
    signal reg_uart1info : std_ulogic_vector(63 downto 0);
    signal reg_gitinfo   : std_ulogic_vector(63 downto 0);
    signal reg_cpuctrl   : std_ulogic_vector(63 downto 0);
    signal reg_tbctrl    : 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_has_leth : std_ulogic;
    signal info_has_lsdc : std_ulogic;
    signal info_has_lsd2 : std_ulogic;
    signal info_has_urt1 : std_ulogic;
    signal info_clk      : std_ulogic_vector(39 downto 0);
    signal info_fl_off   : std_ulogic_vector(31 downto 0);
    signal uinfo_16550   : std_ulogic;
    signal uinfo_freq    : std_ulogic_vector(31 downto 0);
    signal tb_rdprot     : std_ulogic;
    signal tb_freeze     : std_ulogic;

    -- Wishbone response latch
    signal wb_rsp        : wb_io_slave_out;
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 <= not reg_cpuctrl(NCPUS-1 downto 0) when reg_ctrl(SYS_REG_CTRL_CORE_RESET) = '0'
                  else (others => '1');
    alt_reset <= reg_ctrl(SYS_REG_CTRL_ALT_RESET);


    -- 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_has_leth <= '1' when HAS_LITEETH    else '0';
    info_has_lsdc <= '1' when HAS_SD_CARD    else '0';
    info_has_lsd2 <= '1' when HAS_SD_CARD2   else '0';
    info_has_urt1 <= '1' when HAS_UART1      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,
                 SYS_REG_INFO_HAS_LETH   => info_has_leth,
                 SYS_REG_INFO_HAS_SDCARD => info_has_lsdc,
                 SYS_REG_INFO_HAS_SDCARD2 => info_has_lsd2,
                 SYS_REG_INFO_HAS_LSYS   => '1',
                 SYS_REG_INFO_HAS_URT1   => info_has_urt1,
		 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);

    -- UART info registers read composition
    uinfo_16550   <= '1' when UART0_IS_16550 else '0';
    uinfo_freq    <= std_ulogic_vector(to_unsigned(CLK_FREQ, 32));
    reg_uart0info <= (32           => uinfo_16550,
                      31 downto 0  => uinfo_freq,
                      others       => '0');
    reg_uart1info <= (32           => '1',
                      31 downto 0  => uinfo_freq,
                      others       => '0');

    -- GIT info register composition
    reg_gitinfo <= (63          => GIT_DIRTY,
                    55 downto 0 => GIT_HASH,
                    others      => '0');

    reg_cpuctrl(63 downto 8) <= std_ulogic_vector(to_unsigned(NCPUS, 56));

    reg_tbctrl <= 62x"0" & tb_rdprot & tb_freeze;

    -- Wishbone response
    wb_rsp.ack <= wishbone_in.cyc and wishbone_in.stb;
    with wishbone_in.adr(SYS_REG_BITS downto 1) 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,
        reg_uart0info   when SYS_REG_UART0_INFO,
        reg_uart1info   when SYS_REG_UART1_INFO,
        reg_gitinfo     when SYS_REG_GIT_INFO,
        reg_cpuctrl     when SYS_REG_CPU_CTRL,
        reg_tbctrl      when SYS_REG_TB_CTRL,
	(others => '0') when others;
    wb_rsp.dat   <= reg_out(63 downto 32) when wishbone_in.adr(0) = '1' else
                  reg_out(31 downto 0);
    wb_rsp.stall <= '0';

    -- Wishbone response latch
    regs_read: process(clk)
    begin
        if rising_edge(clk) then
            -- Send response from latch
            wishbone_out <= wb_rsp;
        end if;
    end process;

    -- Timebase control
    tb_rdp <= tb_rdprot;
    tb_frz <= tb_freeze;

    -- Initial state
    ctrl_init_alt_reset <= '1' when HAS_DRAM else '0';

    -- Register writes
    regs_write : process(clk)
    begin
	if rising_edge(clk) then
	    if (rst) then
		reg_ctrl <= (SYS_REG_CTRL_ALT_RESET => ctrl_init_alt_reset,
                        others => '0');
                reg_cpuctrl(7 downto 0) <= x"01";          -- enable cpu 0 only
                tb_rdprot <= '0';
                tb_freeze <= '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 downto 1) = SYS_REG_CTRL and
                        wishbone_in.adr(0) = '0' then
			reg_ctrl(SYS_REG_CTRL_BITS-1 downto 0) <=
			    wishbone_in.dat(SYS_REG_CTRL_BITS-1 downto 0);
		    end if;
                    if wishbone_in.adr(SYS_REG_BITS downto 1) = SYS_REG_CPU_CTRL and
                        wishbone_in.adr(0) = '0' and wishbone_in.sel(0) = '1' then
                        reg_cpuctrl(7 downto 0) <= wishbone_in.dat(7 downto 0);
                    end if;
                    if wishbone_in.adr(SYS_REG_BITS downto 1) = SYS_REG_TB_CTRL and
                        wishbone_in.adr(0) = '0' and wishbone_in.sel(0) = '1' then
                        tb_rdprot <= wishbone_in.dat(1);
                        tb_freeze <= wishbone_in.dat(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;
