LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
use std.textio.all; --  Imports the standard textio package.

use work.emac_types.all;
use work.utils_pkg.all;

ENTITY emac_top_jb IS
	Generic
	(
		f_sysclk	: real		:= 100.0;
		TX_RAM_SIZE	: natural	:= 2048;
		RX_RAM_SIZE	: natural	:= 2048
	);
	Port
	(
		CLK_I		: in  STD_LOGIC;
		RST_I		: in  STD_LOGIC;
		INT_O		: out STD_LOGIC;
		CYC_I		: in  STD_LOGIC;
		STB_I		: in  STD_LOGIC;
		SEL_I		: in  unsigned(3 downto 0);
		WE_I		: in  STD_LOGIC;
		ACK_O		: out STD_LOGIC;
		SRDY_O		: out STD_LOGIC;
		MRDY_I		: in  STD_LOGIC;
		ADDR_I		: in  unsigned(31 downto 0);
		DAT_I		: in  unsigned(31 downto 0);
		DAT_O		: out unsigned(31 downto 0);
		mii_rx_clk	: in  STD_LOGIC;
		mii_rx_dv	: in  STD_LOGIC;
		mii_rx_er	: in  STD_LOGIC;
		mii_rx		: in unsigned(7 downto 0);
		mii_tx_clk	: in  STD_LOGIC;
		mii_tx_en	: out STD_LOGIC;
		mii_tx_er	: out STD_LOGIC;
		mii_tx		: out unsigned(7 downto 0);
		mii_gtx_clk	: in  STD_LOGIC;
		mii_crs		: in  STD_LOGIC;
		mii_col		: in  STD_LOGIC
		
	);
END emac_top_jb;

ARCHITECTURE behavior OF emac_top_jb IS

	-- Signals for EMAC connections
	signal tx_ctrl_in		: tx_ctrl_in_t;
	signal tx_ctrl_out		: tx_ctrl_out_t;
	signal tx_din			: unsigned(31 downto 0);
	signal tx_din_vld		: std_logic;

	signal rx_ctrl_in		: rx_ctrl_in_t;
	signal rx_ctrl_out		: rx_ctrl_out_t;

	signal rx_dout			: unsigned(31 downto 0);
	signal rx_dout_re		: std_logic;
	
	signal rx_int_en		: std_logic;
	signal tx_int_en		: std_logic;
	signal irq_rx			: std_logic;
	signal irq_tx			: std_logic;

	signal ready			: std_logic;

	signal read				: std_logic;
	signal write			: std_logic;
	signal data_read		: std_logic;
	signal data_write		: std_logic;
	signal reg_read			: std_logic;
	signal reg_write		: std_logic;

	signal reg_addr			: unsigned(3 downto 0);
	signal tx_clk			: std_logic;
	
begin

	SRDY_O					<= CYC_I and ready;
	read					<= STB_I and CYC_I and not WE_I;
	write					<= STB_I and CYC_I and WE_I;
	data_read				<= read and ADDR_I(15);
	data_write				<= write and ADDR_I(15);
	reg_read				<= read and not ADDR_I(15);
	reg_write				<= write and not ADDR_I(15);
	reg_addr				<= ADDR_I(5 downto 2);

	ready					<= not tx_ctrl_in.pkt_alloc_en;
	
	tx_din_vld				<= data_write;
	tx_din					<= DAT_I;

	rx_dout_re				<= data_read;
	
	tx_clk					<= mii_gtx_clk when tx_ctrl_in.Gbps_en = '1' else mii_tx_clk;
	
------------------------------------------------------------------
registers_write: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			tx_ctrl_in.pkt_alloc_en		<= '0';
			tx_ctrl_in.pkt_commit_en	<= '0';
			tx_ctrl_in.reset			<= '0';
			rx_ctrl_in.pkt_free_en		<= '0';
			rx_ctrl_in.pkt_req_en		<= '0';
			rx_ctrl_in.reset			<= '0';
			if RST_I = '1' then
				rx_int_en				<= '0';
				tx_int_en				<= '0';
				tx_ctrl_in.tx_size		<= (others => '0');
				rx_ctrl_in.mac_addr		<= (X"03", X"02", X"01", X"00", X"07", X"06");
				rx_ctrl_in.promiscious	<= '0';
				tx_ctrl_in.Gbps_en		<= '0';
				rx_ctrl_in.Gbps_en		<= '0';
				tx_ctrl_in.fcs_gen_en	<= '0';
				rx_ctrl_in.fcs_chk_en	<= '0';
			elsif reg_write = '1' then
			
				-- 0x0000 .. 0x003C
				case reg_addr is

				-- 0x0000
				when "0000" =>
					rx_ctrl_in.reset			<= DAT_I(31);
					rx_ctrl_in.Gbps_en			<= DAT_I(30);
					rx_ctrl_in.fcs_chk_en		<= DAT_I(29);
					rx_ctrl_in.promiscious		<= DAT_I(23);
					rx_ctrl_in.pkt_req_en		<= DAT_I(17);
					rx_ctrl_in.pkt_free_en		<= DAT_I(16);
					rx_int_en					<= DAT_I(4);

				-- 0x0004
				-- rx_size R/O

				-- 0x0008
				when "0010" =>
					tx_ctrl_in.reset			<= DAT_I(31);
					tx_ctrl_in.Gbps_en			<= DAT_I(30);
					tx_ctrl_in.fcs_gen_en		<= DAT_I(29);
					tx_ctrl_in.pkt_alloc_en		<= DAT_I(17);
					tx_ctrl_in.pkt_commit_en	<= DAT_I(16);
					tx_int_en					<= DAT_I(4);

				-- 0x000C
				when "0011" => 
					tx_ctrl_in.tx_size			<= DAT_I(15 downto 0);

				-- 0x0010
				-- Gap

				-- 0x0014
				-- Gap

				-- 0x0018
				when "0110" =>
					rx_ctrl_in.mac_addr(0)		<= DAT_I(31 downto 24);
					rx_ctrl_in.mac_addr(1)		<= DAT_I(23 downto 16);
					rx_ctrl_in.mac_addr(2)		<= DAT_I(15 downto 8);
					rx_ctrl_in.mac_addr(3)		<= DAT_I(7 downto 0);

				-- 0x001C
				when "0111" =>
					rx_ctrl_in.mac_addr(4)		<= DAT_I(31 downto 24);
					rx_ctrl_in.mac_addr(5)		<= DAT_I(23 downto 16);

				-- 0x0020 .. 0x003C
				-- Gap
				when others => null;

				end case;
				
			end if;	
		end if;	
	end process;

registers_read: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			ACK_O					<= data_read;
			DAT_O					<= rx_dout;
			if reg_read = '1' then
				
				-- 0x0000 .. 0x003C
				case reg_addr is

				-- 0x0000
				when "0000" =>	
					ACK_O		<= ready;
					DAT_O		<= (others => '0');
					DAT_O(31)	<= rx_ctrl_out.reset_busy;
					DAT_O(30)	<= rx_ctrl_in.Gbps_en;
					DAT_O(29)	<= rx_ctrl_in.fcs_chk_en;
					DAT_O(23)	<= rx_ctrl_in.promiscious;
					DAT_O(19)	<= rx_ctrl_out.pkt_bcast;
					DAT_O(18)	<= rx_ctrl_out.pkt_mac_match;
					DAT_O(17)	<= rx_ctrl_out.pkt_valid;
					DAT_O(16)	<= rx_ctrl_out.pkt_avail;
					DAT_O(4)	<= rx_int_en;

				-- 0x0004
				when "0001" => 
					ACK_O		<= '1';
					DAT_O		<= X"0000" & rx_ctrl_out.rx_size;

				-- 0x0008
				when "0010" =>
					ACK_O		<= ready;
					DAT_O		<= (others => '0');
					DAT_O(31)	<= tx_ctrl_out.reset_busy;
					DAT_O(30)	<= tx_ctrl_in.Gbps_en;
					DAT_O(29)	<= tx_ctrl_in.fcs_gen_en;
					DAT_O(24)	<= tx_ctrl_out.pkt_done;
					DAT_O(17)	<= tx_ctrl_out.pkt_alloc_req;
					DAT_O(16)	<= tx_ctrl_out.pkt_armed;
					DAT_O(4)	<= tx_int_en;

				-- 0x000C
				when "0011" => 
					ACK_O		<= '1';
					DAT_O		<= X"0000" & tx_ctrl_out.tx_size;

				-- 0x0010
				-- Gap

				-- 0x0014
				-- Gap

				-- 0x0018
				when "0110" =>
					ACK_O		<= '1';
					DAT_O		<= rx_ctrl_in.mac_addr(0) & rx_ctrl_in.mac_addr(1) & rx_ctrl_in.mac_addr(2) & rx_ctrl_in.mac_addr(3);

				-- 0x001C
				when "0111" =>
					ACK_O		<= '1';
					DAT_O		<= rx_ctrl_in.mac_addr(4) & rx_ctrl_in.mac_addr(5) & X"0000";

				-- 0x0020 .. 0x003C
				-- Gap
				when others => null;

				end case;

			end if;
		end if;	
	end process;

irq_register:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			INT_O		<= irq_rx or irq_tx;
			irq_tx		<= tx_int_en and tx_ctrl_out.pkt_done;
			irq_rx		<= rx_int_en and rx_ctrl_out.pkt_avail;
		end if;
	end process;

inst_emac_rx : entity work.emac_rx
	GENERIC MAP
	(
		f_sysclk	=> f_sysclk,
		RAM_SIZE	=> RX_RAM_SIZE
	)
	PORT MAP
	(
		clk			=> CLK_I,
		rst			=> RST_I,
		dout_re		=> rx_dout_re,
		dout		=> rx_dout,
		ctrl_in		=> rx_ctrl_in,
		ctrl_out	=> rx_ctrl_out,
		mii_rx_clk	=> mii_rx_clk,
		mii_rx_dv	=> mii_rx_dv,
		mii_rx_er	=> mii_rx_er,
		mii_rx		=> mii_rx,
		mii_crs		=> mii_crs,
		mii_col		=> mii_col
		
	);


inst_emac_tx : entity work.emac_tx
	GENERIC MAP
	(
		f_sysclk	=> f_sysclk,
		RAM_SIZE	=> TX_RAM_SIZE
	)
	PORT MAP
	(
		clk			=> CLK_I,
		rst			=> RST_I,
		din_vld		=> tx_din_vld,
		din			=> tx_din,
		ctrl_in		=> tx_ctrl_in,
		ctrl_out	=> tx_ctrl_out,
		mii_tx_clk	=> tx_clk,
		mii_tx_en	=> mii_tx_en,
		mii_tx_er	=> mii_tx_er,
		mii_tx		=> mii_tx
		
	);


end behavior;
