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(
    --offchip data pins
    PB_D : inout std_logic_vector(15 downto 0) := X"0000";
    -- 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
  
    );
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 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(4 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);  -- data to send to slndbus
  signal tristate : std_logic_vector(15 downto 0):=X"0000";  -- tristate driver ctrl vector
  signal cfgdatabit : std_logic;
begin  -- Behavioral

  Sln_errAck <='0';
  Sln_retry <= '0';
  Sln_toutSup <= '0';
  
  -- pad offchip OPB data connections:
 G: for n in 0 to 15 generate 
 begin
   dataiobuf : IOBUF_F_24 port map ( 
     O => data_out(n), --to offchip OPB_Dbus
     IO => PB_D(n), 
     I => data_in(n), --from offchip OPB_Dbus 
     T => tristate(n)); --read ctrl regs
 end generate G;

 cs <= OPB_select when OPB_ABus(31 downto 16) = X"FEFE" else '0';
-- codec config 
--  process(OPB_Clk, OPB_Rst)
--  begin
--    if OPB_Rst = '1' then
--      cs_config <= '0';
--      data_out <= X"0000";
--    elsif OPB_Clk'event and OPB_Clk = '1'  then
     --if OPB_ABus = X"CCCC" then
     --  cs_config <= '1';
     --  config_cnt <= "00000";
     --  config_reg <= OPB_DBus(15 downto 0);
     --  tristate(2) <= '0'; 
     --end if;
--      if OPB_ABus = X"CCCB" then
--        cs_config <= OPB_DBus(0);
--      elsif OPB_ABus = X"CCCC" and cs_config = '1' then
--        data_out(0) <= OPB_Dbus(0);      --fake clock generated by C
--      elsif OPB_ABus = X"CCCD" and cs_config = '1' then      
--        data_out(1) <= OPB_Dbus(0);      -- data stream from C
--      elsif OPB_ABus = X"CCCE" and cs_config = '1' then
--        cfgdatabit <= data_in(2);          --input from ctrl regs
--      end if;
       

--    end if;
--  end process;
  
 AU_CSN_N <= not cs_config;
 tristate(2) <= cs_config;
 
-- send config to codec
--  process(cs_config)
--  begin
--    if cs_config = '1' and cs = '1' then
--      data_out(0) <= not audio_clk;
--      data_out(1) <= config_reg(15-conv_integer(config_cnt));  --reverse
--                                                               --string from                                                              --microblaze 
--      rdata(15-conv_integer(config_cnt)) <= data_in(2);  --read ctrl regs into
--                                                         --this vector(sent to
--                                                         --Sln_DBus), but
--                                                         --reversed 
--      if config_cnt = "10000" then
--        cs_config <= '0';
--        tristate(2) <= '1';
--      end if;
--    else
--      data_out <= X"0000";
--    end if;
  
--  end process;

--clock dividers to create proper clocks for the codec:
process(OPB_Clk, OPB_Rst)
begin
	if OPB_Rst = '1' then 
		clkcnt <= "00000";
	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 <= not audio_clk; -- BCLK is antiphase (as to sample in the middle
                          -- of data)


process(audio_clk, OPB_Rst)
begin
    
  if OPB_rst = '1' then
    bit_cnt <= "00000";
    lrck <= '0';
    config_cnt <= "00000";
  elsif audio_clk'event and audio_clk='1' then
    config_cnt <= config_cnt + 1;       --only for config clock
    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)
  begin
    
  if audio_clk'event and audio_clk = '0' then
      adc_reg(conv_integer(bit_cnt)) <= AU_SDTO0;
  end if;

AU_SDTI <= AU_SDTO0;
end process;

process(lrck)
  begin
    if lrck'event and lrck = '0' then
      adc_reg_lowerhalf <= adc_reg(15 downto 0);            
    end if;
  end process;

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

process(OPB_Rst, OPB_Clk)
  begin
    if OPB_rst = '1' then
      rdata <= X"00000000";
      cs_config <= '0';
      data_out <= X"0000";
    elsif OPB_Clk'event and OPB_Clk = '1' and cs = '1' then
--        if OPB_ABus(15 downto 0) = X"FFFC" then
--          cs_data <= '1';
--          rdata(15 downto 0) <= X"0001";
--        elsif OPB_ABus(15 downto 0) = X"0000" then
--          rdata(15 downto 0) <= adc_reg_lowerhalf_rev;  --leftchannel data
--          cs_data <= '0';
--        elsif OPB_ABus(15 downto 0) = X"CCCB" then
--          cs_config <= OPB_DBus(0);
--        elsif OPB_ABus(15 downto 0) = X"CCCC" and cs_config = '1' then
--          data_out(0) <= OPB_Dbus(0);      --fake clock generated by C
         
--        elsif OPB_ABus(15 downto 0) = X"CCCD" and cs_config = '1' then      
--          data_out(1) <= OPB_Dbus(0);      -- data stream from C
--          --config_reg <= OPB_Dbus(15 downto 0);
--        elsif OPB_ABus(15 downto 0) = X"CCCE" and cs_config = '1' then
--          rdata(0) <= data_in(2);          --input from ctrl regs
--          rdata(15 downto 1) <= "000000000000000";
--        else 
--          rdata(15 downto 0) <= X"0000";          
--        end if; 
     rdata(31 downto 16) <= X"0000"; 
    end if;
end process;

Sln_DBus <= rdata when cs = '1' else X"0000_0000";

datafsm_seq : process(OPB_clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then then
      d_state <= d_idle;
    elsif OPB_clk'event and OPB_clk = '1' then
      d_state <= d_next;
    end if;
  end process datafsm_seq;

datafsm_comb : process(OPB_Rst, d_state)
  begin
    case d_state is
      when idle =>
        if OPB_ABus = X"1111" then
          d_next <= poll;
        end if;
      when poll =>
        if poll = '0' then
          d_next <= req;
        elsif poll = '1' then
          d_next <= idle;
        end if;
      when req =>
        if OPB_ABus = X"2222" then
          rdata <= parallel_out;
          d_next <= ack;
        else
          d_next <= req;
        end if;
      when ack =>
        rdata <= parallel_out;
        Sln_xferAck <= '1';
        d_next <= idle;
      when others => idle;
    end case;
  end process datafsm_comb;

-- ctrlfsm_seq : process(audio_clk,OPB_Rst)
--   begin
--     if OPB_Rst = '1' then
--       present_state <= idle;
--     elsif audio_clk'event and audio_clk = '0' then
--       present_state <= next_state;
--     end if;
--     if cs_config = '1' then
--       data_out(0) <= 
--     end if;
-- end process ctrlfsm_seq;

-- ctrlfsm_comb: process(OPB_Rst, present_state)
--   begin
-- case present_state is
--   when idle =>
--     if cs_config = '1' then
--       next_state <= s1;
--     else
--       next_state <= idle;
--     end if;
--   when s1 =>
--     data_out(
--   when others => idle;
-- end case;

--   end process ctrlfsm_comb;

end Behavioral;
