library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library ddr3;

library ip;
use ip.wishbone_package.all;
use ip.mor1kx_pkg.all;

entity top is
	port(
		clk_hw       : in    std_logic;
		rst_hw       : in    std_logic;

		-- GPIO
		GPIOA        : inout std_logic_vector(wishbone_data_width - 1 downto 0);

		-- JINN
		jinn_uart_rx : in    std_logic;
		jinn_uart_tx : out   std_logic;

		-- UART
		uart_rx      : in    std_logic;
		uart_tx      : out   std_logic;

		-- DDR3 RAM
		mem_a        : out   std_logic_vector(12 downto 0); -- memory.mem_a
		mem_ba       : out   std_logic_vector(2 downto 0); -- .mem_ba
		mem_ck       : out   std_logic_vector(0 downto 0); -- .mem_ck
		mem_ck_n     : out   std_logic_vector(0 downto 0); -- .mem_ck_n
		mem_cke      : out   std_logic_vector(0 downto 0); -- .mem_cke
		mem_cs_n     : out   std_logic_vector(0 downto 0); -- .mem_cs_n
		mem_dm       : out   std_logic_vector(1 downto 0); -- .mem_dm
		mem_ras_n    : out   std_logic_vector(0 downto 0); -- .mem_ras_n
		mem_cas_n    : out   std_logic_vector(0 downto 0); -- .mem_cas_n
		mem_we_n     : out   std_logic_vector(0 downto 0); -- .mem_we_n
		mem_reset_n  : out   std_logic; -- .mem_reset_n
		mem_dq       : inout std_logic_vector(15 downto 0) := (others => '0'); -- .mem_dq
		mem_dqs      : inout std_logic_vector(1 downto 0)  := (others => '0'); -- .mem_dqs
		mem_dqs_n    : inout std_logic_vector(1 downto 0)  := (others => '0'); -- .mem_dqs_n
		mem_odt      : out   std_logic_vector(0 downto 0); -- .mem_odt
		oct_rzqin    : in    std_logic  -- oct.rzqin
	);

end entity top;

architecture RTL of top is
	constant debug_baud : natural := 460800;
	constant F_CPU      : natural := 50000000;

	-- WB config
	constant masters             : natural := 3;
	constant slaves              : natural := 2;
	constant INTERCON_ID_SRAM    : natural := 0;
	constant INTERCON_ID_DDR3    : natural := 1;
	constant INTERCON_ID_GPIO    : natural := 2;
	constant INTERCON_ID_UART    : natural := 3;
	constant INTERCON_ID_NS16550 : natural := 4;

	constant in_simulation : boolean := false
	--pragma synthesis_off
	or true
--pragma synthesis_on
;
	constant in_synthesis : boolean := not in_simulation;

	signal clk               : std_logic;
	signal rst               : std_logic;
	signal interrupt         : std_logic_vector(32 - 1 downto 0);
	signal intercon_slave_i  : wishbone_v3_slave_in_vector(slaves - 1 downto 0);
	signal intercon_slave_o  : wishbone_v3_slave_out_vector(slaves - 1 downto 0);
	signal intercon_master_i : wishbone_v3_master_in_vector(masters - 1 downto 0);
	signal intercon_master_o : wishbone_v3_master_out_vector(masters - 1 downto 0);

	signal pll_locked              : std_logic;
	signal rst_ddr3_n              : std_logic;
	signal debug_o                 : debug_interface_o;
	signal debug_i                 : debug_interface_i;
	signal traceport_exec_valid_o  : std_logic;
	signal traceport_exec_pc_o     : std_logic_vector(31 downto 0);
	signal traceport_exec_insn_o   : std_logic_vector(OR1K_INSN_WIDTH - 1 downto 0);
	signal traceport_exec_wbdata_o : std_logic_vector(OPTION_OPERAND_WIDTH - 1 downto 0);
	signal traceport_exec_wbreg_o  : std_logic_vector(OPTION_RF_ADDR_WIDTH - 1 downto 0);
	signal traceport_exec_wben_o   : std_logic;
	signal mor1kx_rst              : std_logic;
	signal jinn_data_i             : std_logic_vector(7 downto 0);
	signal jinn_data_available     : std_logic;
	signal jinn_data_o             : std_logic_vector(7 downto 0);
	signal jinn_data_valid_o       : std_logic;
	signal jinn_uart_busy_i        : std_logic;

	signal avl_ready_0       : std_logic;
	signal avl_burstbegin_0  : std_logic;
	signal avl_addr_0        : std_logic_vector(24 downto 0);
	signal avl_rdata_valid_0 : std_logic;
	signal avl_rdata_0       : std_logic_vector(31 downto 0);
	signal avl_wdata_0       : std_logic_vector(31 downto 0);
	signal avl_be_0          : std_logic_vector(3 downto 0);
	signal avl_read_req_0    : std_logic;
	signal avl_write_req_0   : std_logic;
	signal avl_size_0        : std_logic_vector(2 downto 0);

	signal local_init_done   : std_logic; --             status.local_init_done
	signal local_cal_success : std_logic; --                   .local_cal_success
	signal local_cal_fail    : std_logic; --                   .local_cal_fail

	signal avl_reqEn : std_logic;
	signal writeAck  : std_logic;
	signal readAck   : std_logic;
begin
	debug_i.addr <= (others => '0');
	debug_i.dat  <= (others => '0');
	debug_i.stb  <= '1';
	debug_i.we   <= '0';

	-- System controller
	resetSync : process(clk, rst_hw) is
	begin
		if rst_hw = '0' then            -- low active
			rst        <= '1';
			rst_ddr3_n <= '0';
		elsif rising_edge(clk) then
			if pll_locked = '1' then
				rst_ddr3_n <= '1';      -- Start DDR3 Controller
				if local_init_done = '1' and local_cal_success = '1' then
					rst <= '0';         -- Start system!
				end if;
			end if;
		end if;
	end process resetSync;

	-- Clock management
	clk        <= clk_hw;
	pll_locked <= '1';

	-- SRAM
	sram_wb_inst : entity work.sram_wb
		port map(
			clk    => clk,
			rst    => rst,
			wb_in  => intercon_slave_i(INTERCON_ID_SRAM),
			wb_out => intercon_slave_o(INTERCON_ID_SRAM)
		);

	-- CPU
	interrupt <= (others => '0');
	mor1kx_vhdl_inst : entity ip.mor1kx_vhdl
		port map(
			clk                     => clk,
			rst                     => mor1kx_rst,
			data_o                  => intercon_master_o(1),
			data_i                  => intercon_master_i(1),
			inst_o                  => intercon_master_o(0),
			inst_i                  => intercon_master_i(0),
			irq_i                   => interrupt,
			debug_o                 => debug_o,
			debug_i                 => debug_i,
			traceport_exec_valid_o  => traceport_exec_valid_o,
			traceport_exec_pc_o     => traceport_exec_pc_o,
			traceport_exec_insn_o   => traceport_exec_insn_o,
			traceport_exec_wbdata_o => traceport_exec_wbdata_o,
			traceport_exec_wbreg_o  => traceport_exec_wbreg_o,
			traceport_exec_wben_o   => traceport_exec_wben_o
		);

	-- Debug interface
	jinn_inst : entity work.jinn
		port map(
			clk_i          => clk,
			rst_i          => rst,
			master_i       => intercon_master_i(2),
			master_o       => intercon_master_o(2),
			cpu_stall      => debug_i.stall,
			cpu_reset      => mor1kx_rst,
			data_i         => jinn_data_i,
			data_available => jinn_data_available,
			data_o         => jinn_data_o,
			data_valid_o   => jinn_data_valid_o,
			output_busy_i  => jinn_uart_busy_i
		);

	uart_rx_inst : entity work.uart_rx
		port map(
			clk          => clk,
			rst          => rst,
			data         => jinn_data_i,
			byte_ready   => jinn_data_available,
			error        => open,
			ckDiv        => std_logic_vector(to_unsigned(F_CPU / debug_baud - 1, 16)),
			parityEnable => '0',
			parityOdd    => '0',
			twoStopBits  => '0',
			rx           => jinn_uart_rx
		);

	uart_tx_inst : entity work.uart_tx
		port map(
			clk          => clk,
			rst          => rst,
			data         => jinn_data_o,
			byte_ready   => jinn_data_valid_o,
			busy         => jinn_uart_busy_i,
			ckDiv        => std_logic_vector(to_unsigned(F_CPU / debug_baud - 1, 16)),
			parityEnable => '0',
			parityOdd    => '0',
			twoStopBits  => '0',
			tx           => jinn_uart_tx
		);

	-- DDR3 RAM
	ddr3_inst : entity ddr3.ddr3
		port map(
			pll_ref_clk                => clk,
			global_reset_n             => rst_hw,
			soft_reset_n               => '1',
			afi_clk                    => open,
			afi_half_clk               => open,
			afi_reset_n                => open,
			afi_reset_export_n         => open,
			mem_a                      => mem_a,
			mem_ba                     => mem_ba,
			mem_ck                     => mem_ck,
			mem_ck_n                   => mem_ck_n,
			mem_cke                    => mem_cke,
			mem_cs_n                   => mem_cs_n,
			mem_dm                     => mem_dm,
			mem_ras_n                  => mem_ras_n,
			mem_cas_n                  => mem_cas_n,
			mem_we_n                   => mem_we_n,
			mem_reset_n                => mem_reset_n,
			mem_dq                     => mem_dq,
			mem_dqs                    => mem_dqs,
			mem_dqs_n                  => mem_dqs_n,
			mem_odt                    => mem_odt,
			avl_ready_0                => avl_ready_0,
			avl_burstbegin_0           => avl_burstbegin_0,
			avl_addr_0                 => avl_addr_0,
			avl_rdata_valid_0          => avl_rdata_valid_0,
			avl_rdata_0                => avl_rdata_0,
			avl_wdata_0                => avl_wdata_0,
			avl_be_0                   => avl_be_0,
			avl_read_req_0             => avl_read_req_0,
			avl_write_req_0            => avl_write_req_0,
			avl_size_0                 => avl_size_0,
			mp_cmd_clk_0_clk           => clk,
			mp_cmd_reset_n_0_reset_n   => rst_hw,
			mp_rfifo_clk_0_clk         => clk,
			mp_rfifo_reset_n_0_reset_n => rst_hw,
			mp_wfifo_clk_0_clk         => clk,
			mp_wfifo_reset_n_0_reset_n => rst_hw,
			local_init_done            => local_init_done,
			local_cal_success          => local_cal_success,
			local_cal_fail             => local_cal_fail,
			oct_rzqin                  => oct_rzqin,
			pll_mem_clk                => open,
			pll_write_clk              => open,
			pll_locked                 => open,
			pll_write_clk_pre_phy_clk  => open,
			pll_addr_cmd_clk           => open,
			pll_avl_clk                => open,
			pll_config_clk             => open,
			pll_mem_phy_clk            => open,
			afi_phy_clk                => open,
			pll_avl_phy_clk            => open,
			csr_clk                    => clk,
			csr_reset_n                => rst_hw
		);

	avl_addr_0       <= intercon_slave_i(INTERCON_ID_DDR3).ADR(26 downto 2); -- & "00";
	avl_be_0         <= intercon_slave_i(INTERCON_ID_DDR3).SEL;
	avl_burstbegin_0 <= '0';

	avl_wdata_0     <= intercon_slave_i(INTERCON_ID_DDR3).DAT;
	avl_read_req_0  <= (intercon_slave_i(INTERCON_ID_DDR3).STB and (not intercon_slave_i(INTERCON_ID_DDR3).WE)) and avl_reqEn;
	avl_write_req_0 <= (intercon_slave_i(INTERCON_ID_DDR3).STB and intercon_slave_i(INTERCON_ID_DDR3).WE) and avl_reqEn;
	avl_size_0      <= "001";

	intercon_slave_o(INTERCON_ID_DDR3).DAT <= avl_rdata_0;
	intercon_slave_o(INTERCON_ID_DDR3).ERR <= '0';
	intercon_slave_o(INTERCON_ID_DDR3).RTY <= '0';
	intercon_slave_o(INTERCON_ID_DDR3).ACK <= (readAck or writeAck) and intercon_slave_i(INTERCON_ID_DDR3).STB;

	readAck  <= avl_rdata_valid_0;      --(avl_rdata_valid_0 and not intercon_slave_i(INTERCON_ID_SRAM).WE);
	writeAck <= (intercon_slave_i(INTERCON_ID_DDR3).WE and avl_ready_0);

	wb2avl : process(clk, rst) is
	begin
		if rst = '1' then
			avl_reqEn <= '1';
		elsif rising_edge(clk) then
			if intercon_slave_i(INTERCON_ID_DDR3).STB = '1' then
				avl_reqEn <= '0';
			else
				avl_reqEn <= '1';
			end if;
		end if;
	end process wb2avl;

	-- Intercon
	crossbar_inst : entity ip.crossbar
		generic map(
			masters => masters,
			slaves  => slaves,
			async   => true
		)
		port map(
			clk      => clk,
			rst      => rst,
			slave_i  => intercon_master_o,
			slave_o  => intercon_master_i,
			master_i => intercon_slave_o,
			master_o => intercon_slave_i,
			address  => (
				INTERCON_ID_SRAM => x"00000000",
				INTERCON_ID_DDR3 => x"80000000"
			),
			mask     => (
				INTERCON_ID_SRAM => x"ffff0000",
				INTERCON_ID_DDR3 => x"f0000000"
			)
		);
end architecture RTL;
