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

entity opb_ak4565 is

  generic(
    C_OPB_AWIDTH    : integer := 32;
    C_OPB_DWIDTH    : integer := 32;
    C_BASEADDR      : std_logic_vector(31 downto 0) := X"FEFE_0000";
    C_HIGHADDR      : std_logic_vector(31 downto 0) := X"FEFE_FFFF"
    );
  
  port(
	--ins
    OPB_Clk : in std_logic;
    OPB_Rst : in std_logic;
    OPB_ABus : in std_logic_vector (C_OPB_AWIDTH-1 downto 0);
    OPB_BE : in std_logic_vector (3 downto 0);
    OPB_DBus : in std_logic_vector (C_OPB_DWIDTH-1 downto 0);
    OPB_RNW : in std_logic;
    OPB_select : in std_logic;
    OPB_seqAddr : in std_logic;

    AU_SDTO0 : in std_logic;

    --outs
    AU_CSN_N : out std_logic;
    AU_BCLK : out std_logic;
    AU_MCLK : out std_logic;
    AU_LRCK : out std_logic;
    AU_SDTI : out std_logic;
            
    
    Sln_DBus : out std_logic_vector (C_OPB_DWIDTH-1 downto 0);
    Sln_errAck : out std_logic;
    Sln_retry : out std_logic;
    Sln_toutSup : out std_logic;
    Sln_xferAck : out std_logic;

    Interrupt : out std_logic

    );
end opb_ak4565;

architecture Behavioral of opb_ak4565 is

  component IOBUF_F_24
    port (
      O : out STD_ULOGIC;
      IO : inout STD_ULOGIC;
      I : in STD_ULOGIC;
      T : in STD_ULOGIC);
  end component;

  signal RNW : std_logic;
  signal ABus : std_logic_vector(C_OPB_AWIDTH-1 downto 0);
  signal DBus : std_logic_vector(C_OPB_DWIDTH-1 downto 0);
  
  
  
  signal data_out,data_in : std_logic_vector(15 downto 0);  -- offchip data signals
  signal cs_data, cs_config, cs, readctrlregs : std_logic:= '0';  -- chip selects
  signal config_clk, config_in : std_logic;  -- config signals
  signal audio_clk,lrck : std_logic;         -- internal clock sigs
  signal clkcnt : std_logic_vector(9 downto 0);  -- divider counter
  signal bit_cnt : std_logic_vector(4 downto 0);-- counter for which bit
                                              --(0-32) that we're writing now
  signal adc_reg: std_logic_vector(31 downto 0);
  signal adc_reg_lowerhalf : std_logic_vector(15 downto 0);
  signal adc_reg_lowerhalf_rev : std_logic_vector(15 downto 0);
  signal config_reg: std_logic_vector(15 downto 0);
  signal config_cnt : std_logic_vector(4 downto 0):= "00000";  -- config counter
  signal parallel_out : std_logic_vector(15 downto 0); -- to be sent to microblaze
  signal rdata : std_logic_vector(31 downto 0):=X"0000_0000";  -- data to send to slndbus
  signal tristate : std_logic_vector(15 downto 0):=X"0000";  -- tristate driver ctrl vector
  signal cfgdatabit : std_logic;
  signal poll : std_logic;
  signal state : std_logic_vector(3 downto 0);      -- one hot state variable
  signal xferAck : std_logic;
  signal sampleToWrite : std_logic_vector(15 downto 0);  -- for audio output
  signal sampleToWrite_rev : std_logic_vector(15 downto 0);  -- for audio output
  signal serialToWrite : std_logic;     -- serial stream for audio output
  signal sample_cnt : std_logic_vector(2 downto 0);
  signal itrpt : std_logic := '0';
begin  -- Behavioral

  Interrupt <= itrpt;
  Sln_errAck <='0';
  Sln_retry <= '0';
  Sln_toutSup <= '0';

  cs <= OPB_select when OPB_ABus(31 downto 16) = X"FEFE" else '0';

  AU_CSN_N <= '1';
 
--clock dividers to create proper clocks for the codec:
process(OPB_Clk, OPB_Rst)
begin
	if OPB_Rst = '1' then 
		clkcnt <= "0000000000";
	elsif OPB_clk'event and OPB_clk='1' then
		clkcnt <= clkcnt + 1;                
	end if;
end process;
AU_MCLK <= clkcnt(1); -- master clock, 12.5 MHz
audio_clk <= clkcnt(4); -- this is the serial clock, 1.5625 Mhz
--AU_BCLK <= audio_clk;
AU_BCLK <= not audio_clk; -- BCLK is antiphase (as to sample in the middle
                          -- of data)

process(OPB_Clk, OPB_Rst, clkcnt, sample_cnt)
  begin
    if OPB_Rst = '1' then
      itrpt <= '0';
    elsif(OPB_Clk'event and OPB_Clk = '1') then
      if(clkcnt = "0100010000" and sample_cnt = "101") then  --was 110
        itrpt <= '1';   
        sample_cnt <= "000";
      elsif (clkcnt = "0100000000") then
        sample_cnt <= sample_cnt + 1;
      else
        itrpt <= '0';     
      end if;
    end if;
  end process;
  
process(audio_clk, OPB_Rst)
begin
    
  if OPB_rst = '1' then
    bit_cnt <= "00000";
    lrck <= '0';
  elsif audio_clk'event and audio_clk='1' then
    bit_cnt <= bit_cnt + 1;
    lrck <= not bit_cnt(4);
  end if; 
end process;

AU_LRCK <= lrck; --channel selection clock, 48.828 kHz


--fill register with the serial bits of data, send the left channel (bottom 16
--bits to the microblaze via parallel_out signal
process(audio_clk,bit_cnt,lrck)
  begin    
  if audio_clk'event and audio_clk = '0' then
      adc_reg(conv_integer(bit_cnt - 1)) <= AU_SDTO0;
  end if;
  if audio_clk'event and audio_clk = '1' then
    if( (bit_cnt) < "10000" ) then
      serialToWrite <= sampleToWrite_rev(conv_integer(bit_cnt));
    else
      serialToWrite <= '0';
    end if;
  end if;
end process;
AU_SDTI <= serialToWrite;



process(audio_clk)
  begin
    if audio_clk'event and audio_clk = '0' and bit_cnt = "10000" then
      adc_reg_lowerhalf <= adc_reg(15 downto 0);            
    end if;

end process;

process (adc_reg_lowerhalf,sampleToWrite) begin
        for i in 0 to 15 loop
		adc_reg_lowerhalf_rev(i)<=adc_reg_lowerhalf(15-i);
	end loop;
        for j in 0 to 15 loop
		sampleToWrite_rev(j)<=sampleToWrite(15-j);
	end loop;
end process;

--rdata(31 downto 16) <= X"0000";
Sln_DBus <= rdata when cs = '1' and OPB_RNW = '1' else X"0000_0000";

--FSMish thing:
--states: selected  (0001)
--        ready poll(0010)
--        ready data(0100)
--        xfer      (1000)
--        write     (1111)
--        idle      (0000)

poll_and_read : process(OPB_Clk,OPB_Rst, state, poll, OPB_RNW, cs,OPB_ABus, bit_cnt, clkcnt)
  begin
    if(OPB_Rst = '1') then
      xferAck <= '0';
      rdata(15 downto 0) <= X"0000";
      state <= "0000";             
    elsif(OPB_Clk'event and OPB_Clk = '1') then
      if (cs = '1' ) then
        case state is
          
          when "0000" => 
            xferAck <= '0';
            rdata <= X"0000_0000";
            if(OPB_RNW = '1') then
              state <= "0001";   --selected + reading
            elsif OPB_ABus(15 downto 0) = X"2222" then
              state <= "1111";   --writing
            else                
              state <= "0000";
            end if;
            
          when  "0001" =>
            if(OPB_ABus(15 downto 0) = X"0000") then
              state <= "0100";
            else
              state <= "0000";
            end if;
	           
          when "0100" =>
            xferAck <= '0';
            --rdata(25 downto 16) <= clkcnt(9 downto 0);-- adc_reg_lowerhalf_rev;
            rdata(31 downto 16) <= adc_reg_lowerhalf_rev; --RAJ - 15 downto 0
            state <= "1000"; --go to xfer
        
          when "1000" =>
            xferAck <= '1';            
            state <= "0000";

          when "1111" =>
            sampleToWrite <= OPB_DBus(15 downto 0);
	    state <= "1000";

            
          when others =>
           null;
        end case;
      else
        xferAck <='0';
        state <= "0000";	

      end if; --end if cs=1
    end if; --end if OPB_RST
end process poll_and_read;
Sln_xferAck <= xferAck;

end Behavioral;
