----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:05:19 03/16/2009 
-- Design Name: 
-- Module Name:    AC97_Core - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity AC97_Core is
    Port ( 
			-- Input Ports
			TAG_IN	: 	in STD_LOGIC_VECTOR (4 downto 0);  --write the tag information for the AC97 frame
			ID			:	in STD_LOGIC_VECTOR (1 downto 0);  --AC97 codec ID
			RNW		:	in STD_LOGIC;							  --read from or write to a specific AC97 codec internal register
			ADDR		:	in STD_LOGIC_VECTOR (6 downto 0);  --AC97 codec internal register address
			DATA_WR	:	in STD_LOGIC_VECTOR (15 downto 0); --Data to write into the register
																		  --If a read operation is performed, the data sent should be '0'-s
			PCM_OUT_L:	in STD_LOGIC_VECTOR (19 downto 0); --Left channel PCM output data
			PCM_OUT_R:	in STD_LOGIC_VECTOR (19 downto 0); --Right channel PCM output data
			-- output ports
			SLOT_VALID: out STD_LOGIC_VECTOR (4 downto 0); --inicates which slot read contains valid data
         SLOT_REQ	: 	out STD_LOGIC_VECTOR (1 downto 0); --read tag/slot request bits
			ADDR_OUT	:	out STD_LOGIC_VECTOR (6 downto 0); --Address from which is read
			DATA_OUT	:	out STD_LOGIC_VECTOR (15 downto 0);--Data read from the register
			PCM_IN_L	:	out STD_LOGIC_VECTOR (17 downto 0);--left channel PCM input data;
			PCM_IN_R	:	out STD_LOGIC_VECTOR (17 downto 0);--right channel PCM output data;
			-- AC 97 link ports
			-- The bitclk signal is input for an AC97 primary codec (in the case when the XUPV2P board is used)
			-- and an output signal to the AC97 secondary codec (in the case when the XUPV2P board is used)
			-- SDATA_OUT is changed on the positive edge of BITCLK and SDATA_IN is read on the negative edge of BITCLK
			BITCLK	:	in STD_LOGIC;
			SDATA_IN	:	in STD_LOGIC; 	-- AC Link input frame signal
			SDATA_OUT:	out STD_LOGIC;	-- AC Link output frame signal
			SYNC		:	out STD_LOGIC;	-- the SYNC line determines the start of a nwe frame
												-- it should have a frequency of 48KHz, regardless of the sample rate
			RESET_n	:  out STD_LOGIC;	-- AC97 cold reset signal
			-- System and control ports
			SYSCLK 	: 	in  STD_LOGIC;	-- System Clock
			RESET		:	in STD_LOGIC;	-- Reset input
			--BITCLK_IN:	in STD_LOGIC; --should have a frequency of 12.288MHz
			LOAD		:	in STD_LOGIC;	--Load a new frame
			DONE		:  out STD_LOGIC); -- The loaded frame was transmitted
end AC97_Core;



architecture Behavioral of AC97_Core is

type STATES IS (Idle, Rise_first_SYNC, Loadreg, Shift, Done_transm);
signal STC, STN: STATES:=Idle;

-- data received from the upper level hierarchical component
signal DATAREG_IN		: STD_LOGIC_VECTOR (95 downto 0):= X"000000000000000000000000";
alias  Slot_0_IN		: STD_LOGIC_VECTOR (15 downto 0) is DATAREG_IN (95 downto 80);
alias  Slot_1_IN		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_IN (79 downto 60);
alias  Slot_2_IN		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_IN (59 downto 40);
alias  Slot_3_IN		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_IN (39 downto 20);
alias  Slot_4_IN		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_IN (19 downto  0);

-- data received from the AC97 codec
signal DATAREG_OUT	: STD_LOGIC_VECTOR (95 downto 0):= X"000000000000000000000000";
alias  Slot_0_OUT		: STD_LOGIC_VECTOR (15 downto 0) is DATAREG_OUT (95 downto 80);
alias  Slot_1_OUT		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_OUT (79 downto 60);
alias  Slot_2_OUT		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_OUT (59 downto 40);
alias  Slot_3_OUT		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_OUT (39 downto 20);
alias  Slot_4_OUT		: STD_LOGIC_VECTOR (19 downto 0) is DATAREG_OUT (19 downto  0);

-- shifts in SDATA_IN and it will load to DATAREG_OUT
signal SHIFTREG_OUT	: STD_LOGIC_VECTOR (95 downto 0):= X"000000000000000000000000"; 
-- loaded from DATAREG_IN and it will be shifted out to SDATA_OUT
signal SHIFTREG_IN	: STD_LOGIC_VECTOR (95 downto 0):= X"000000000000000000000000";

signal cntbits			: integer range 0 to 256:=0;
signal BITCLK_READ	: STD_LOGIC:='0';
--signal BITCLK_WRITE	: STD_LOGIC:='0';
signal SYNC_CMB		: STD_LOGIC:='0';
signal DONE_CMB		: STD_LOGIC:='0';
signal DONE_REG		: STD_LOGIC:='0';


signal cnt_reset		: integer range 0 to 511:=0;
signal reset_done		: STD_LOGIC:='0';
signal rise_sync		: STD_LOGIC:='0';

signal BITCLK_REG    : STD_LOGIC_VECTOR (1 downto 0):="00";
signal BITCLK_N, BITCLK_P      : STD_LOGIC;


-- makes the state machine to wait in the Loadreg state until the next positive edge of BITCLK
signal WAIT_ON_BITCLK_n: STD_LOGIC :='0';

signal BITCLK_NEGEDGE: STD_LOGIC :='0';

begin

-- create the reset counter
count_reset: process (SYSCLK, STC, cnt_reset)
begin
	if SYSCLK'EVENT AND SYSCLK = '1' then
		if RESET = '1' then cnt_reset <= 0;
		elsif cnt_reset = 511 then cnt_reset <= 511;
		else cnt_reset <= cnt_reset + 1;
      end if;
	end if;
end process count_reset;

reset_done <= '1' when cnt_reset = 511 else '0';
RESET_n <='0' when cnt_reset <= 240 else '1';

-- detect BITCLK positive and negative edges
reg_BITCLK: process (SYSCLK, BITCLK, BITCLK_REG)
begin
   if SYSCLK'EVENT and SYSCLK = '1' then
      BITCLK_REG (1) <= BITCLK_REG(0);
      BITCLK_REG(0)  <= BITCLK;
   end if;
end process reg_BITCLK;

BITCLK_P <= BITCLK_REG(0) AND (NOT BITCLK_REG(1));
BITCLK_N <= (NOT BITCLK_REG(0)) AND BITCLK_REG (1);

-- assign BITCLK and BITCLK_READ;
--BITCLK <= BITCLK_WRITE when C_USE_PRIMARY_CODEC = 0 else 'Z';
BITCLK_READ <= BITCLK; --when C_USE_PRIMARY_CODEC = 1 else BITCLK_IN;
--BITCLK_WRITE <= BITCLK_IN;

-- assign the WAIT_ON_BITCLK_n signal
--assign_wait_load: process (BITCLK_READ, STC, cnt_reset, RESET, DONE_REG)
--begin
--   if ((RESET = '1') or (DONE_REG = '1') or (cnt_reset<511) or (STC = Rise_first_SYNC) ) then 
--            WAIT_ON_BITCLK_n <= '0';
--	elsif BITCLK_READ'EVENT AND BITCLK_READ = '1' then
--         WAIT_ON_BITCLK_n <= '1';
--	end if;
--end process assign_wait_load;
--
---- assign the rise_sync signal
--assign_wait_sync_BITCLK: process (BITCLK_READ, cnt_reset, RESET, STC)
--begin
--   if ((RESET = '1') or (cnt_reset<511)) then 
--            rise_sync <= '0';
--	elsif BITCLK_READ'EVENT AND BITCLK_READ = '1' then
--         if STC = Rise_first_SYNC then
--				rise_sync <= '1';
--			end if;
--	end if;
--end process assign_wait_sync_BITCLK;

-- assign the BITCLK_NEGEDGE signal
--assign_wait_BITCLK_NEGEDGE: process (BITCLK_READ, cnt_reset, RESET, STC)
--begin
--   if ((RESET = '1') or (cnt_reset<511) or (STC = Loadreg)) then 
--            BITCLK_NEGEDGE <= '0';
--	elsif BITCLK_READ'EVENT AND BITCLK_READ = '0' then
--         if STC = Shift then
--				BITCLK_NEGEDGE <= '1';
--			end if;
--	end if;
--end process assign_wait_BITCLK_NEGEDGE;


-- state machine register process
reg_states: process (BITCLK_READ, RESET, STC)
begin
	if BITCLK_READ'EVENT AND BITCLK_READ = '1' then
		if RESET = '1' then 
				STC <= Idle;
		else
				STC <= STN;
		end if;
   end if;
end process reg_states;

-- state machine transitions process
tranz: process (STC, reset_done, BITCLK_N, BITCLK_P, WAIT_ON_BITCLK_n, cntbits, rise_sync, BITCLK_NEGEDGE)
begin
	STN <= STC;
	case STC is 
	when Idle 	      	=>	if (reset_done = '1') then STN <= Rise_first_SYNC; end if;
	when Rise_first_SYNC	=> STN <= Loadreg;
	when Loadreg 	   	=> STN <= Shift;
	when Shift	      	=> if ((cntbits = 255)) then STN <= Done_Transm; end if;
	when Done_transm		=> STN <= Shift;
	when others       	=> STN <= Idle;
	end case;
end process tranz;

-- Load the input data register when the LOAD signal is asserted
-- checking register overload should be done in the upper level hierarchy
-- see the codec datasheet for details
load_datareg_in: process (SYSCLK, LOAD, TAG_IN, ID, RNW, ADDR,
                                 DATA_WR, PCM_OUT_L, PCM_OUT_R, DONE_REG)
begin
	if SYSCLK'EVENT and SYSCLK = '1' then
		if LOAD = '1' OR DONE_REG = '1' then
			Slot_0_IN <= TAG_IN & X"00" & '0' & ID ;
			Slot_1_IN <= RNW & ADDR & X"000";
			if RNW = '1' then 
					Slot_2_IN <= (others => '0');
			else
					Slot_2_IN <= DATA_WR & X"0";
         end if;
			Slot_3_IN <= PCM_OUT_L;
			Slot_4_IN <= PCM_OUT_R;
		end if;
	end if;
end process load_datareg_in;

--Load and shift the SHIFTREG_IN register
load_shiftreg: process (BITCLK_READ, STC, DATAREG_IN, DONE_REG)
begin
	if BITCLK_READ'EVENT and BITCLK_READ = '1' then
         if STC = Loadreg or STC = Done_transm then
               SHIFTREG_IN <= DATAREG_IN;
         elsif STC = Shift then
               SHIFTREG_IN <= SHIFTREG_IN (94 downto 0) & '0';
         end if;
	end if;
end process load_shiftreg;

SDATA_OUT <= SHIFTREG_IN (95);

-- Load the output data register when the DONE signal is asserted
-- see the codec datasheet for details
load_datareg_out: process (SYSCLK, DONE_REG, SHIFTREG_OUT)
begin
	if SYSCLK'EVENT and SYSCLK = '1' then
		if DONE_REG = '1' then
         DATAREG_OUT <= SHIFTREG_OUT;
      end if;
	end if;
end process load_datareg_out;

SLOT_VALID  <= Slot_0_OUT (15 downto 11);
ADDR_OUT	   <= Slot_1_OUT (18 downto 12);
SLOT_REQ    <= Slot_1_OUT (11 downto 10);
DATA_OUT    <= Slot_2_OUT (19 downto 4);
PCM_IN_L    <= Slot_3_OUT (19 downto 2);
PCM_IN_R    <= Slot_4_OUT (19 downto 2);

--Shift in the SHIFTREG_OUT register
shift_in: process (BITCLK_READ, STC, DATAREG_IN, SDATA_IN)
begin
        if ((STC = Loadreg) or (STC = Rise_first_SYNC)) then
               SHIFTREG_OUT <= (others => '0');
               cntbits <= 0;
         elsif BITCLK_READ'EVENT AND BITCLK_READ='0' then -- on the falling edge!
            if STC = Idle then
                  cntbits <= 0;
            elsif ((STC = Shift) and (cntbits < 96)) then
                  SHIFTREG_OUT <= SHIFTREG_OUT (94 downto 0) & SDATA_IN;
                  cntbits <= cntbits+1;
            else 
                  if cntbits = 255 then cntbits <= 0;
                  else cntbits <= cntbits+1;
                  end if;
            end if;
         end if;
end process shift_in;

SYNC_CMB <= '1' when ((STC= Shift AND cntbits <= 14 ) OR (STC = Shift AND cntbits > 254) OR 
							(STC = Done_transm) or (STC = Rise_first_SYNC) or (STC = loadreg)) else '0';

DONE_CMB <= '1' when STC = Done_transm else '0';

--register SYNC_CMB
register_sync_cmb: process (BITCLK_READ, SYNC_CMB)
begin
	if BITCLK_READ'EVENT and BITCLK_READ = '1' then
--		if BITCLK_P = '1' then		
         SYNC <= SYNC_CMB;
--      end if;
   end if;
end process register_sync_cmb;

-- register DONE_CMB
DONE_REG <= DONE_CMB AND BITCLK_N;
register_done: process (SYSCLK, DONE_REG)
begin
   if SYSCLK'EVENT AND SYSCLK = '1' then
      DONE <= DONE_REG;
   end if;
end process register_done;

--DONE<=DONE_REG;


end Behavioral;


