library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use work.globals.all;

entity registers is
	 port(
		 clock_in 									: in 	std_logic;
		 reset_in 									: in 	std_logic;
		 cs_n_in 									: in 	std_logic;
		 rd_n_in 									: in 	std_logic;
		 wr_n_in 									: in 	std_logic;	 	
		 mxfe_configurator_done_in					: in	std_logic;	 
		 dac_transfer_done_in						: in	std_logic;
		 address_in 								: in 	std_logic_vector(15 downto 0);
		 data_in 									: in 	std_logic_vector(15 downto 0);
		 mxfe_register_data_in						: in	std_logic_vector( 7 downto 0);
		 data_out 									: out 	std_logic_vector(15 downto 0);
		 mxfe_configuration_data_out				: out 	std_logic_vector(23 downto 0); 	 
		 mxfe_start_serializer_out					: out	std_logic;	  
		 mxfe_configuration_fsm_clock_enable_out	: out	std_logic;
		 mxfe_write_n_or_read_out					: out	std_logic;
		 mxfe_one_n_or_two_out						: out	std_logic;	  
		 dac_data_out								: out	std_logic_vector(23 downto 0);	  
		 dac_start_serializer_out					: out	std_logic;	
		 dac_serializer_fsm_clock_enable_out		: out	std_logic;
		 drive_cpu_bus_out							: out	std_logic
	);
end registers;

architecture synthesis of registers is		 

	constant clock_divider_limit : std_logic_vector (3 downto 0) := "1001";  -- 9, N-1

	component dual_rank_synchronizer is
		port (
			clock_in	: in  std_logic;
			reset_in	: in  std_logic;
			unsynced_in	: in  std_logic;
			synced_out	: out std_logic
		);
	end component dual_rank_synchronizer;      
	
	component falling_edge_detector is
		port (
			clock_in      	: in  std_logic;
			reset_in		: in  std_logic;
			candidate_in	: in  std_logic;
			detected_out	: out std_logic
		);
	end component falling_edge_detector;		 
	
	component jk_ff is
		port (
			 clock_in 	: in 	std_logic;
			 reset_in 	: in 	std_logic;	
			 enable_in	: in	std_logic;
			 j_in 		: in 	std_logic;
			 k_in 		: in 	std_logic;
			 q_out 		: out 	std_logic
		);
	end component jk_ff; 
	
	signal		mxfe_configuration_command_register 	: std_logic_vector( 7 downto 0); 
	signal      mxfe_instruction_register				: std_logic_vector( 7 downto 0);   
	signal		mxfe_register_1							: std_logic_vector( 7 downto 0);	
	signal		mxfe_register_2							: std_logic_vector( 7 downto 0);	
	signal		dac_command_register					: std_logic_vector( 7 downto 0);
	signal		dac_data_register						: std_logic_vector(15 downto 0);
	signal 		clock_divider_counter					: std_logic_vector( 3 downto 0);
	signal		reset_clock_divider_counter				: std_logic;
	signal		synced_cs_n								: std_logic;
	signal		synced_wr_n								: std_logic;	
	signal		write_pulse								: std_logic;   
	signal		mxfe_start								: std_logic; 
	signal      mxfe_busy								: std_logic;
	signal		dac_start								: std_logic; 
	signal      dac_busy								: std_logic;	
	
begin				
	
	sync1 : dual_rank_synchronizer
		port map (
		  clock_in     	=> clock_in,
		  reset_in 		=> reset_in,
		  unsynced_in	=> cs_n_in,
		  synced_out    => synced_cs_n
		  );	
		  
	sync2 : dual_rank_synchronizer
		port map (
		  clock_in     	=> clock_in,
		  reset_in 		=> reset_in,
		  unsynced_in	=> wr_n_In,
		  synced_out    => synced_wr_n
		  );	
		  
	fed1 : falling_edge_detector
		port map (
		  clock_in		=> clock_in,
		  reset_in		=> reset_in,
		  candidate_in	=> synced_wr_n,
		  detected_out	=> write_pulse
		  );   
		  
	mxfe_busy_ff : jk_ff
		port map (
		  clock_in     	=> clock_in,
		  reset_in 		=> reset_in, 
		  enable_in		=> vdd,
		  j_in 	     	=> mxfe_start,
		  k_in      	=> mxfe_configurator_done_in,
		  q_out     	=> mxfe_busy
		);
		
	dac_busy_ff : jk_ff
		port map (
		  clock_in     	=> clock_in,
		  reset_in 		=> reset_in, 
		  enable_in		=> vdd,
		  j_in 	     	=> dac_start,
		  k_in      	=> dac_transfer_done_in,
		  q_out     	=> dac_busy
		);
		
	register_file : process (clock_in)
	begin					 
		if rising_edge(clock_in) then	   
			if reset_in = '1' then
				mxfe_configuration_command_register 	<= (others => '0');
				mxfe_instruction_register				<= (others => '0');  
				mxfe_register_1							<= (others => '0');	
				mxfe_register_2							<= (others => '0');		
				dac_command_register					<= (others => '0');
				dac_data_register						<= (others => '0');
			elsif (synced_cs_n = '0' and write_pulse = '1') then
				case address_in is		
					when MxfeConfigurationCommandRegisterAddress	=> mxfe_configuration_command_register <= data_in(7 downto 0);
					when Mxfeinstructionregisteraddress				=> mxfe_instruction_register <= data_in(7 downto 0);   
					when MxfeRegister_1_Address						=> mxfe_register_1 <= data_in(7 downto 0);	
					when MxfeRegister_2_Address						=> mxfe_register_2 <= data_in(7 downto 0);	  
					when DAC_CommandRegisterAddress					=> dac_command_register <= data_in(7 downto 0);	
					when DAC_DataRegisterAddress					=> dac_data_register <= data_in;	
					when others => null;
				end case;	
			end if;
		end if;
	end process register_file;
		
	data_out_mux : process (address_in, cs_n_in, rd_n_in, mxfe_register_data_in, 
							dac_data_register, mxfe_instruction_register, mxfe_register_1,
							mxfe_register_2, mxfe_register_data_in, mxfe_busy, dac_busy)
	begin  
		if (cs_n_in = '0' and rd_n_in = '0') then
			case address_in is
				when MxfeConfigurationStatusRegisterAddress	=> data_out <= "000000000000000" & mxfe_busy;	
				when MxfeInstructionRegisterAddress			=> data_out <= "00000000" & mxfe_instruction_register;  
				when MxfeRegister_1_Address					=> data_out <= "00000000" & mxfe_register_1;	
				when MxfeRegister_2_Address					=> data_out <= "00000000" & mxfe_register_2;
				when MxfeResultRegisterAddress 				=> data_out <= "00000000" & mxfe_register_data_in; 	 
				when DAC_StatusRegisterAddress				=> data_out <= "000000000000000" & dac_busy;
				when DAC_DataRegisterAddress				=> data_out <= dac_data_register;	
				when MotherboardFpgaRevisionRegisterAddress	=> data_out <= MotherboardFpgaChipRevision;
				when others => data_out <= x"dead";
			end case;	 
		else
			data_out <= x"0000";
		end if;
	end process data_out_mux;	 		  		 
	
	clock_divider : process (clock_in)
	begin					  
		if rising_edge (clock_in) then
			if reset_in = '1' then
				clock_divider_counter <= (others => '0');
			elsif reset_clock_divider_counter = '1' then
				clock_divider_counter <= (others => '0'); 
			else
				clock_divider_counter <= clock_divider_counter + 1;
			end if;
		end if;
	end process	clock_divider;		 
	
	drive_cpu_bus_out           <= '1' when (synced_cs_n = '0' and rd_n_in = '0') else '0';		   
	reset_clock_divider_counter <= '1' when( clock_divider_counter = clock_divider_limit) else '0'; 
	
	mxfe_start					<= '1' when (synced_cs_n = '0' and write_pulse = '1' and 
									address_in = MxfeConfigurationCommandRegisterAddress) else '0';	
	mxfe_start_serializer_out	<= mxfe_start;	
	mxfe_configuration_data_out <= mxfe_instruction_register & mxfe_register_1 & mxfe_register_2;
	mxfe_write_n_or_read_out	<= mxfe_instruction_register(7);
	mxfe_one_n_or_two_out		<= mxfe_instruction_register(6);	  
	
	dac_start					<= '1' when (synced_cs_n = '0' and write_pulse = '1' and 
									address_in = DAC_CommandRegisterAddress) else '0';	
	dac_start_serializer_out	<= dac_start;	
	dac_data_out 				<= dac_command_register & dac_data_register;	
 	mxfe_configuration_fsm_clock_enable_out	<= reset_clock_divider_counter;	  
	dac_serializer_fsm_clock_enable_out		<= reset_clock_divider_counter; 
end synthesis;
