-------------------------------------------------------------------------------
-- CSEE 4840 Embedded System Design
-- Jaycam
-- Yaniv Schiller - yhs2001@columbia.edu
-- Avrum Tilman - amt77@columbia.edu
-- Josh Weinberg - jmw211@columbia.edu
-------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

--  Uncomment the following lines to use the declarations that are
--  provided for instantiating Xilinx primitive components.
library UNISIM;
use UNISIM.VComponents.all;

entity opb_cntrl is
  generic (
    C_OPB_AWIDTH    : integer := 32;
    C_OPB_DWIDTH    : integer := 32;
    C_BASEADDR      : std_logic_vector := X"2000_0000";
    C_HIGHADDR      : std_logic_vector := X"2000_00FF"
    );

    Port ( 	OPB_Clk : in std_logic;
				OPB_Rst : in std_logic;
				OPB_ABus : in std_logic_vector (31 downto 0);
				OPB_BE : in std_logic_vector (3 downto 0);
				OPB_DBus : in std_logic_vector (31 downto 0);
				OPB_RNW : in std_logic;
				OPB_select : in std_logic;
				OPB_seqAddr : in std_logic;
				pixel_clock : in std_logic;
           			io_clock: in std_logic;
				UIO_DBus : out std_logic_vector (31 downto 0);
				UIO_errAck : out std_logic;
				UIO_retry : out std_logic;
				UIO_toutSup : out std_logic;
				UIO_xferAck : out std_logic;
				PB_A : out std_logic_vector (19 downto 0);
				PB_UB_N : out std_logic; 
				PB_LB_N : out std_logic;
				PB_WE_N : out std_logic;
				PB_OE_N : out std_logic;
				RAM_CE_N : out std_logic;
				ETHERNET_CS_N : out std_logic;
			  	ETHERNET_RDY : in std_logic;
			  	ETHERNET_IREQ : in std_logic;
			  	ETHERNET_IOCS16_N : in std_logic;
          
				PB_D : inout std_logic_vector (15 downto 0)
	 		   );
end opb_cntrl;

architecture Behavioral of opb_cntrl is

signal addr_mux : std_logic_vector(19 downto 0);
signal video_addr : std_logic_vector (19 downto 0);
signal video_data : std_logic_vector (15 downto 0);
signal video_req : std_logic;
signal video_ce : std_logic;
signal i : integer;
signal cs : std_logic;

signal fullword, eth_io, read_early, write_early : std_logic ;
signal videocycle, amuxsel, hihalf : std_logic;
signal rce0, rce1, rreset : std_logic;
signal xfer : std_logic;
signal wr_req, rd_req, bus_req : std_logic;
signal pb_rd, pb_wr : std_logic;

signal sram_ce : std_logic;
signal ethernet_ce : std_logic;


signal rnw, ram_we, ram_oe, oe : std_logic;

signal addr : std_logic_vector (23 downto 0); 	

signal be : std_logic_vector (3 downto 0);
signal pb_bytesel : std_logic_vector (1 downto 0);

signal wdata : std_logic_vector (31 downto 0);
signal wdata_mux : std_logic_vector (15 downto 0);

signal rdata : std_logic_vector (15 downto 0);	-- register data read - FDRE



component memoryctrl 
Port ( 	  rst : in std_logic;
           clk : in std_logic;
           cs : in std_logic; -- any of my devices selected
           opb_select : in std_logic; -- original select	
           rnw : in std_logic;
           eth_io : in std_logic;
           fullword : in std_logic;
           read_early : out std_logic;
           write_early : out std_logic;
           videocycle : out std_logic;
           hihalf : out std_logic;
           wr_req : out std_logic;
           rd_req : out std_logic;
           bus_req : out std_logic;
           xfer : out std_logic;
           ce0 : out std_logic;
           ce1 : out std_logic;
           rres : out std_logic;
	   ram_output_enable : out std_logic;
	   ram_WE : out std_logic;
	   sram_ce : in std_logic);
end component;

component pad_io 
    Port ( sys_clk : in std_logic;
           io_clock : in std_logic;
           read_early : in std_logic;
           write_early : in std_logic;
           rst : in std_logic;
           PB_A : out std_logic_vector(19 downto 0);
           PB_UB_N : out std_logic;
           PB_LB_N : out std_logic;
           PB_WE_N : out std_logic;
           PB_OE_N : out std_logic;
           RAM_CE_N : out std_logic;
           ETHERNET_CS_N : out std_logic;
           ETHERNET_RDY : in std_logic;
           ETHERNET_IREQ : in std_logic;
           ETHERNET_IOCS16_N : in std_logic;
           PB_D : inout std_logic_vector(15 downto 0);
           
           pb_addr : in std_logic_vector(19 downto 0);
           pb_ub : in std_logic;
           pb_lb : in std_logic;
           pb_wr : in std_logic;
           pb_rd : in std_logic;
           ram_ce : in std_logic;
	   ethernet_ce : in std_logic;
           pb_dread : out std_logic_vector(15 downto 0);
           pb_dwrite : in std_logic_vector(15 downto 0));
end component;


begin



-- the controller state machine
memoryctrl1 : memoryctrl
port map	(	rst => OPB_Rst,
			clk => OPB_Clk,
			cs => cs, 
			opb_select => OPB_select, 
			rnw => rnw, 
				
			fullword => fullword,
			eth_io =>eth_io,
			read_early => read_early,
			write_early => write_early,
			videocycle => videocycle, 
			hihalf => hihalf, 
			wr_req => wr_req, 
			rd_req => rd_req,
                        bus_req => bus_req,
                        
			xfer => xfer,
			ce0 => rce0, 
			ce1 => rce1, 
			rres => rreset,
			
			
	   		ram_output_enable => ram_oe,
		        ram_WE => ram_we,
	   		sram_ce => sram_ce
			);

-- PADS

pad_io1 : pad_io
port map	(	sys_clk => OPB_Clk,
			io_clock => io_clock,
                        read_early => read_early,
                        write_early => write_early,
			rst => OPB_Rst,
			PB_A => PB_A,
			PB_UB_N => PB_UB_N,	
			PB_LB_N => PB_LB_N,
			PB_WE_N => PB_WE_N,
			PB_OE_N => PB_OE_N, 
			RAM_CE_N => RAM_CE_N,
			ETHERNET_CS_N => ETHERNET_CS_N,
		  	ETHERNET_RDY => ETHERNET_RDY,
		  	ETHERNET_IREQ => ETHERNET_IREQ,
		  	ETHERNET_IOCS16_N => ETHERNET_IOCS16_N,
			PB_D => PB_D,

			pb_addr => addr_mux,
			pb_wr => pb_wr,
			pb_rd => oe,
			pb_ub => pb_bytesel(1),
			pb_lb => pb_bytesel(0),
			ram_ce => sram_ce,
			ethernet_ce => ethernet_ce,

			pb_dread  => rdata,
			pb_dwrite => wdata_mux);


--	amuxsel <= videocycle;
	video_ce <= '0';
	video_req <= '0';

	addr_mux <= (addr(20 downto 2)) & (addr(1) or hihalf);
	
	fullword <= be(2) and be(0);

	wdata_mux <= wdata(15 downto 0) when ((addr(1) or hihalf) = '1') else wdata(31 downto 16);




pb_rd <= rd_req;
pb_wr <= wr_req;
oe <= ram_oe when (sram_ce='1') else pb_rd;
--ram_oe active low

cs <= OPB_select when OPB_ABus(31 downto 23) = "000000001" else '0';
sram_ce <= '1' when addr(22 downto 21)="10" else '0';
ethernet_ce <= '1' when addr(22 downto 21) ="01" else '0';
eth_io <= '1' when addr(22 downto 21) /= "10" else '0';



process (OPB_Clk,	OPB_Rst)
begin

-- register rw
	if OPB_Clk'event and OPB_Clk ='1' then
	
		if OPB_Rst = '1' then
			rnw <= '0'; 
		else 
			rnw <= OPB_RNW;
		end if;
	
	end if;

-- register adresses A23 .. A0
	if OPB_Clk'event and OPB_Clk ='1' then
	   for i in 0 to 23 loop
			if OPB_Rst = '1' then
  				addr(i) <= '0';
			else
				addr(i) <= OPB_ABus(i);
			end if;	
   	end loop;
	end if;
			
	-- register BE
	if OPB_Clk'event and OPB_Clk ='1' then
			if OPB_Rst = '1' then
  				be <= "0000";
			else
				be <= OPB_BE;
			end if;
	end if;

-- register data write
	if OPB_Clk'event and OPB_Clk ='1' then
	   for i in 0 to 31 loop
			if OPB_Rst = '1' then
  				wdata(i) <= '0';
			else
				wdata(i) <= OPB_DBus(i);
			end if;		
   	end loop;
	end if;

-- the fun begins
-- ce0/ce1 enables writing MSB (low) / LSB (high) halves

--always @(posedge OPB_Rst or posedge OPB_Clk) begin (synchrounous or asynchronous reset??) 

  		for i in 0 to 15 loop
			if OPB_Rst = '1' then
				UIO_DBus(i) <= '0';
			
			elsif OPB_Clk'event and OPB_Clk ='1' then 
				if rreset = '1' then
					UIO_DBus(i) <= '0';
				elsif (rce1 or rce0) = '1' or ram_oe='0' then
					UIO_DBus(i) <= rdata(i);
				end if;
			end if;
		end loop;

		for i in 16 to 31 loop 
			if OPB_Rst = '1' then
				UIO_DBus(i) <= '0';
			elsif OPB_Clk'event and OPB_Clk ='1' then 

				if rreset = '1' then
					UIO_DBus(i) <= '0';
				elsif rce0 = '1' then
					UIO_DBus(i) <= rdata(i-16);
				end if;
			end if;
		end loop;



end process;

-- tie unused to ground
UIO_errAck <= '0';
UIO_retry <= '0';
UIO_toutSup <= '0';

UIO_xferAck <= xfer;


end Behavioral;
