-------------------------------------------------------------
--    _____
--   /     \	
--  /____   \____
-- / \===\   \==/
--/___\===\___\/  AVNET
--     \======/   DESIGN
--      \====/    SERVICES
--
--------------------------------------------------------------
--
-- Title       : ac97_cntl.vhd
-- Design      : 
-- Author      : James Dampare 
-- Company     : Avnet
-- Date        : 01/09/2003
---------------------------------------------------------------

LIBRARY ieee ;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
use IEEE.std_logic_unsigned.all;

entity ac97_cntl is
  port( 
      sysclk                : in  std_logic;
      reset_on              : in  std_logic; 
      reset_off             : in  std_logic;                                                 
      bit_clk               : in  std_logic;   -- 12.288 MHz 
      LED                   : out std_logic_vector(6 downto 0);
      -- UCB1400 Codec Interface
      sdata_in              : in  std_logic; 
      sdata_out             : out std_logic;
      sync_out              : out std_logic;
      ac97_reset_n          : out std_logic;
      test_data             : out std_logic_vector(15 downto 0)
 );          
end ac97_cntl;  


architecture rtl of ac97_cntl is

-----------------------------
--  Declare Components
-----------------------------

component parallel_serial
port(
   ser_clk          : in std_logic;
   ser_load         : in std_logic;
   par_data         : in std_logic_vector(255 downto 0);
   ser_data         : out std_logic
   );
end component; 

component serial_parallel 
port(
   ser_clk         : in std_logic;
   ser_data        : in std_logic;
   par_load        : in std_logic;
   par_data        : out std_logic_vector(255 downto 0)
   );
end component; 


---------------------------------
----  Internal Signals
---------------------------------

type cmd_type is  (codec_ready_check, write_register, write_addr_02, write_addr_1A, write_addr_1C,
                   write_addr_20, write_addr_2C, write_addr_32, write_addr_6A, read_register, read_addr_02, read_addr_1A, 
                   read_addr_1C, read_addr_20, read_addr_2C, read_addr_32, read_addr_6A, read_data_02, read_data_1A, 
                   read_data_1C, read_data_20, read_data_2C, read_data_32, read_data_6A, ltch_data_6A, cmp_data, test_results);
                   
signal cmd_state   : cmd_type;

---------------------------------------
-- SDATA_OUT Slot Initialization
-------------------------------------------------------------------------------
signal   slot0_out        : std_logic_vector(15 downto 0); 
signal   slot1_out        : std_logic_vector(19 downto 0);
signal   slot2_out        : std_logic_vector(19 downto 0);
constant slot3_out        : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot4_out        : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot5_out        : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot6_out        : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot7_out        : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot8_out        : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot9_out        : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot10_out       : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot11_out       : std_logic_vector(19 downto 0) := "0000" & X"0000";
constant slot12_out       : std_logic_vector(19 downto 0) := "0000" & X"0000";
--------------------------------------------------------------------------------

signal sdata_out_mux      : std_logic_vector(255 downto 0);

signal bit_counter        : integer range 0 to 255;
signal cnt_64             : std_logic;
signal cnt_128            : std_logic;

signal ser_load           : std_logic;
signal par_load           : std_logic;

signal codec_ready_bit    : std_logic;
signal data_reg_02        : std_logic_vector(15 downto 0);
signal data_reg_1A        : std_logic_vector(15 downto 0);
signal data_reg_1C        : std_logic_vector(15 downto 0);
signal data_reg_20        : std_logic_vector(15 downto 0);
signal data_reg_2C        : std_logic_vector(15 downto 0);
signal data_reg_32        : std_logic_vector(15 downto 0);
signal data_reg_6A        : std_logic_vector(15 downto 0);

signal addr_reg_02        : std_logic_vector(7 downto 0);
signal addr_reg_1A        : std_logic_vector(7 downto 0);
signal addr_reg_1C        : std_logic_vector(7 downto 0);
signal addr_reg_20        : std_logic_vector(7 downto 0);
signal addr_reg_2C        : std_logic_vector(7 downto 0);
signal addr_reg_32        : std_logic_vector(7 downto 0);
signal addr_reg_6A        : std_logic_vector(7 downto 0);

signal slot0_in           : std_logic_vector(15 downto 0); 
signal slot1_in           : std_logic_vector(19 downto 0);
signal slot2_in           : std_logic_vector(19 downto 0);
signal sdata_in_reg       : std_logic_vector(255 downto 0);

signal cmp_data_02        : std_logic;
signal cmp_data_1A        : std_logic;
signal cmp_data_1C        : std_logic;
signal cmp_data_20        : std_logic;
signal cmp_data_2C        : std_logic;
signal cmp_data_32        : std_logic;
signal cmp_data_6A        : std_logic;
signal read_data_cmp      : std_logic;

signal test_completed     : std_logic;
signal test_passed        : std_logic;

signal ac_reset           : std_logic;
signal reset              : std_logic;
signal sdata_out_int      : std_logic;
signal sync_out_int       : std_logic;

--------------------------------------------------------------------------------
	
begin 

LED(0) <= codec_ready_bit;
LED(1) <= test_passed;
LED(2) <= test_completed;
LED(3) <= ac_reset;
LED(4) <= cmp_data_1A;
LED(5) <= cmp_data_2C;
LED(6) <= cmp_data_6A;


sdata_out <= '0' when ac_reset = '1' else sdata_out_int;
sync_out  <= '0' when ac_reset = '1' else sync_out_int;
ac97_reset_n <= not(ac_reset);
reset  <= ac_reset;
test_data <= data_reg_6A;
---------------------------------------------------------------------------------

sdata_out_mux <= (slot0_out & slot1_out & slot2_out & slot3_out & slot4_out  & slot5_out  & 
                  slot6_out & slot7_out & slot8_out & slot9_out & slot10_out & slot11_out & slot12_out);

slot0_in  <= sdata_in_reg(255 downto 240);
slot1_in  <= sdata_in_reg(239 downto 220);
slot2_in  <= sdata_in_reg(219 downto 200);
------------------------------------------------------------------

------------------------------------------------------------------
-- System Reset. 
------------------------------------------------------------------
gen_ac_reset: process (sysclk)
begin
if rising_edge(sysclk) then		
   if reset_on = '1'  then
      ac_reset  <= '1';
   elsif reset_on = '0' and reset_off = '1' then
      ac_reset  <= '0';
   end if;
end if;
end process gen_ac_reset;

------------------------------------------


---------------------------------------------------------
-- Initialize slots(2:0) with their default values
-- for both write and read accesses.
---------------------------------------------------------
gen_slot0_2: process (cmd_state)
 begin
   if (cmd_state = write_addr_02) then
     slot0_out <= X"E000";
     slot1_out <= X"02000";
     slot2_out <= X"00000"; 
   elsif (cmd_state = write_addr_1A) then
     slot0_out <= X"E000";
     slot1_out <= X"1A000";
     slot2_out <= X"04040";
   elsif (cmd_state = write_addr_1C) then
     slot0_out <= X"E000";
     slot1_out <= X"1C000";
     slot2_out <= X"00000";
   elsif (cmd_state = write_addr_20) then
     slot0_out <= X"E000";
     slot1_out <= X"20000";
     slot2_out <= X"00800";
    elsif (cmd_state = write_addr_2C) then
     slot0_out <= X"E000";
     slot1_out <= X"2C000";
     slot2_out <= X"BB800";
    elsif (cmd_state = write_addr_32) then
     slot0_out <= X"E000";
     slot1_out <= X"32000";
     slot2_out <= X"BB800";
    elsif (cmd_state = write_addr_6A) then
     slot0_out <= X"E000";
     slot1_out <= X"6A000";
     slot2_out <= X"00580";
   elsif (cmd_state = read_addr_02) then
     slot0_out <= X"C000";
     slot1_out <= X"82000";
     slot2_out <= X"00000";
   elsif (cmd_state = read_addr_1A) then
     slot0_out <= X"C000";
     slot1_out <= X"9A000";
     slot2_out <= X"00000";
   elsif (cmd_state = read_addr_1C) then
     slot0_out <= X"C000";
     slot1_out <= X"9C000";
     slot2_out <= X"00000";
   elsif (cmd_state = read_addr_20) then
     slot0_out <= X"C000";
     slot1_out <= X"A0000";
     slot2_out <= X"00000";
   elsif (cmd_state = read_addr_2C) then
     slot0_out <= X"C000";
     slot1_out <= X"AC000";
     slot2_out <= X"00000";
   elsif (cmd_state = read_addr_32) then
     slot0_out <= X"C000";
     slot1_out <= X"B2000";
     slot2_out <= X"00000";
   elsif (cmd_state = read_addr_6A) then
     slot0_out <= X"C000";
     slot1_out <= X"EA000";
     slot2_out <= X"00000";
   else
     slot0_out <= X"0000";
     slot1_out <= X"00000";
     slot2_out <= X"00000";
   end if;
 end process gen_slot0_2;

----------------------------------------
-- AC-Link Bit Counter
----------------------------------------

gen_bit_count: process(bit_clk, reset)
begin
   if (reset = '1') then
      bit_counter <= 0;
   elsif rising_edge(bit_clk) then
      if (bit_counter = 255) then
         bit_counter <= 0;
      else 
         bit_counter <= bit_counter + 1 ;
      end if;
   end if;
end process gen_bit_count;
-----------------------------------------------
-- Generate control signals
----------------------------------------------

ctrl_gen: process(bit_clk)
begin
if rising_edge(bit_clk) then
   if bit_counter > 1 and bit_counter < 18  then
      sync_out_int <= '1';
   else
      sync_out_int <= '0';
   end if;

   if bit_counter = 1 then
      ser_load <= '1';
   else
      ser_load <= '0';
   end if;

   if bit_counter = 2 then
      par_load <= '1';
   else
      par_load <= '0';
   end if;

   if bit_counter = 128 then
      cnt_128 <= '1';
   else
      cnt_128 <= '0';
   end if;

   if bit_counter = 64 then
      cnt_64 <= '1';
   else
      cnt_64 <= '0';
   end if;
end if;
end process ctrl_gen; 
-----------------------------------------------
-----------------------------------------------
-- Sample Read ADDRESS/DATA
-----------------------------------------------

read_data_gen: process(reset, bit_clk)
begin
if (reset = '1') then
   codec_ready_bit <= '0';
   data_reg_02 <= (others => '1');
   data_reg_1A <= (others => '1');
   data_reg_1C <= (others => '1');
   data_reg_20 <= (others => '1');
   data_reg_2C <= (others => '1');
   data_reg_32 <= (others => '1');
   data_reg_6A <= (others => '1');
   addr_reg_02 <= (others => '1');
   addr_reg_1A <= (others => '1');
   addr_reg_1C <= (others => '1');
   addr_reg_20 <= (others => '1');
   addr_reg_2C <= (others => '1');
   addr_reg_32 <= (others => '1');
   addr_reg_6A <= (others => '1');
   
elsif rising_edge(bit_clk) then	
	
   if (cmd_state = codec_ready_check and cnt_64 = '1') then
      codec_ready_bit <= slot0_in(15);
   end if;
   
   if (cmd_state = read_addr_1A and cnt_64 = '1') then
      addr_reg_02 <= slot1_in(19 downto 12);
      data_reg_02 <= slot2_in(19 downto 4);
   end if;

   if (cmd_state = read_addr_1C and cnt_64 = '1') then
      addr_reg_1A <= slot1_in(19 downto 12);
      data_reg_1A <= slot2_in(19 downto 4);
   end if;

   if (cmd_state = read_addr_20 and cnt_64 = '1') then
      addr_reg_1C <= slot1_in(19 downto 12);
      data_reg_1C <= slot2_in(19 downto 4);
   end if;

   if (cmd_state = read_addr_2C and cnt_64 = '1') then
      addr_reg_20 <= slot1_in(19 downto 12);
      data_reg_20 <= slot2_in(19 downto 4);
   end if;

   if (cmd_state = read_addr_32 and cnt_64 = '1') then
      addr_reg_2C <= slot1_in(19 downto 12);
      data_reg_2C <= slot2_in(19 downto 4);
   end if;

   if (cmd_state = read_addr_6A and cnt_64 = '1') then
      addr_reg_32 <= slot1_in(19 downto 12);
      data_reg_32 <= slot2_in(19 downto 4);
   end if;

   if (cmd_state = ltch_data_6A and cnt_64 = '1') then
      addr_reg_6A <= slot1_in(19 downto 12);
      data_reg_6A <= slot2_in(19 downto 4);
   end if;
end if;
end process read_data_gen; 
------------------------------------------------------
-- Read Address/Data Comparator
------------------------------------------------------

gen_data_cmp: process(reset, bit_clk)
begin
if (reset = '1') then
   cmp_data_02 <= '0';
   cmp_data_1A <= '0';
   cmp_data_1C <= '0';
   cmp_data_20 <= '0';
   cmp_data_2C <= '0';
   cmp_data_32 <= '0';
   cmp_data_6A <= '0';
   read_data_cmp <= '0';
elsif rising_edge(bit_clk) then
   
   if (cmd_state = cmp_data and cnt_64 = '1') then
      if(addr_reg_02 = X"02" and data_reg_02 = X"0000") then
         cmp_data_02 <= '1';
      else
         cmp_data_02 <= '0';
      end if;

      if(addr_reg_1A = X"1A" and data_reg_1A = X"0404") then
         cmp_data_1A <= '1';
      else
         cmp_data_1A <= '0';
      end if;

      if(addr_reg_1C = X"1C" and data_reg_1C = X"0000") then
         cmp_data_1C <= '1';
      else
         cmp_data_1C <= '0';
      end if;

      if(addr_reg_20 = X"20" and data_reg_20 = X"0080") then
         cmp_data_20 <= '1';
      else
         cmp_data_20 <= '0';
      end if;

      if(addr_reg_2C = X"2C" and data_reg_2C = X"BB80") then
         cmp_data_2C <= '1';
      else
         cmp_data_2C <= '0';
      end if;

      if(addr_reg_32 = X"32" and data_reg_32 = X"BB80") then
         cmp_data_32 <= '1';
      else
         cmp_data_32 <= '0';
      end if;

      if(addr_reg_6A = X"6A" and data_reg_6A = X"0058") then
         cmp_data_6A <= '1';
      else
         cmp_data_6A <= '0';
      end if;
   end if;

   if (cmd_state = cmp_data and cnt_128 = '1') then
      if(cmp_data_02 = '1' and cmp_data_1A = '1' and cmp_data_1C = '1' and
         cmp_data_20 = '1' and cmp_data_2C = '1' and cmp_data_32 = '1' and cmp_data_6A = '1')  then
         read_data_cmp <= '1';
      else
         read_data_cmp <= '0';
      end if;
   end if;
end if;
end process gen_data_cmp;         
---------------------------------------------

---------------------------------------------
-- AC97 Controller State Machine
---------------------------------------------
cmd_sm : process (reset, bit_clk)
begin
   if (reset = '1') then
      cmd_state   <= codec_ready_check;
      test_passed <= '0';
      test_completed <= '0';
   elsif rising_edge(bit_clk) then
      case cmd_state is
         when codec_ready_check =>
            if (cnt_128 = '1' and codec_ready_bit = '1' ) then
               cmd_state <= write_register;
            else
               cmd_state <= codec_ready_check;
            end if;

         when write_register =>
            if (cnt_128 = '1') then
               cmd_state <= write_addr_02;
            else
               cmd_state <= write_register;
            end if;

         when write_addr_02 =>
            if (cnt_128 = '1') then
               cmd_state <= write_addr_1A;
            else
               cmd_state <= write_addr_02;
            end if;

         when write_addr_1A =>
            if (cnt_128 = '1') then
               cmd_state <= write_addr_1C;
            else
               cmd_state <= write_addr_1A;
            end if;
	    
         when write_addr_1C =>
            if (cnt_128 = '1') then
               cmd_state <= write_addr_20;
            else
               cmd_state <= write_addr_1C;
            end if;
		 
         when write_addr_20 =>
            if (cnt_128 = '1') then
               cmd_state <= write_addr_2C;
            else
               cmd_state <= write_addr_20;
            end if;

         when write_addr_2C =>
            if (cnt_128 = '1') then
               cmd_state <= write_addr_32;
            else
               cmd_state <= write_addr_2C;
            end if;

         when write_addr_32 =>
            if (cnt_128 = '1') then
               cmd_state <= write_addr_6A;
            else
               cmd_state <= write_addr_32;
            end if;

         when write_addr_6A =>
            if (cnt_128 = '1') then
               cmd_state <= read_register;
            else
               cmd_state <= write_addr_6A;
            end if;
			
         when read_register =>
            if (cnt_128 = '1') then
               cmd_state <= read_addr_02;
            else
               cmd_state <= read_register;
            end if;
			
         when read_addr_02 =>
            if (cnt_128 = '1') then
               cmd_state <= read_data_02;
            else
               cmd_state <= read_addr_02;
            end if;	
			
         when read_data_02 =>
            if (cnt_128 = '1') then
               cmd_state <= read_addr_1A;
            else
               cmd_state <= read_data_02;
            end if;
			
         when read_addr_1A =>
            if (cnt_128 = '1') then
               cmd_state <= read_data_1A;
            else
               cmd_state <= read_addr_1A;
            end if;	
			
         when read_data_1A =>
            if (cnt_128 = '1') then
               cmd_state <= read_addr_1C;
            else
               cmd_state <= read_data_1A;
            end if;
			
         when read_addr_1C =>
            if (cnt_128 = '1') then
               cmd_state <= read_data_1C;
            else
               cmd_state <= read_addr_1C;
            end if;	
			
         when read_data_1C =>
            if (cnt_128 = '1') then
               cmd_state <= read_addr_20;
            else
               cmd_state <= read_data_1C;
            end if;
			
         when read_addr_20 =>
            if (cnt_128 = '1') then
               cmd_state <= read_data_20;
            else
               cmd_state <= read_addr_20;
            end if;	
			
         when read_data_20 =>
            if (cnt_128 = '1') then
               cmd_state <= read_addr_2C;
            else
               cmd_state <= read_data_20;
            end if;
			
         when read_addr_2C =>
            if (cnt_128 = '1') then
               cmd_state <= read_data_2C;
            else
               cmd_state <= read_addr_2C;
            end if;	
			
         when read_data_2C =>
            if (cnt_128 = '1') then
               cmd_state <= read_addr_32;
            else
               cmd_state <= read_data_2C;
            end if;
			
         when read_addr_32 =>
            if (cnt_128 = '1') then
               cmd_state <= read_data_32;
            else
               cmd_state <= read_addr_32;
            end if;	
			
         when read_data_32 =>
            if (cnt_128 = '1') then
               cmd_state <= read_addr_6A;
            else
               cmd_state <= read_data_32;
            end if;

           when read_addr_6A =>
            if (cnt_128 = '1') then
               cmd_state <= read_data_6A;
            else
               cmd_state <= read_addr_6A;
            end if;	

         when read_data_6A =>
            if (cnt_128 = '1') then
               cmd_state <= ltch_data_6A;
            else
               cmd_state <= read_data_6A;
            end if;

         when ltch_data_6A =>
            if (cnt_128 = '1') then
               cmd_state <= cmp_data;
            else
               cmd_state <= ltch_data_6A;
            end if;

         when cmp_data =>
            if (cnt_128 = '1') then
               cmd_state <= test_results;
            else
               cmd_state <= cmp_data;
            end if;
			
         when test_results => 
            cmd_state <= test_results;
            test_completed <= '1';
            if (cnt_128 = '1') then
               if (read_data_cmp = '1') then
                  test_passed <= '1';
               else
                  test_passed <= '0';
               end if;
            end if;
      end case;
   end if;
end process cmd_sm;
------------------------------------------------

--------------------------------
--  COMPONENT Instantiations
--------------------------------

inst_par_ser: parallel_serial 
port map(
   ser_clk    => bit_clk,		 
   ser_load   => ser_load,
   par_data   => sdata_out_mux,	
   ser_data   => sdata_out_int
   );

inst_ser_par: serial_parallel 
port map(
   ser_clk    => bit_clk,		 
   ser_data   => sdata_in,
   par_load   => par_load,	
   par_data   => sdata_in_reg
   );

------------------------------------------------- 

end rtl; 
