------Guitar Effector------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity gDSP is

  generic (
    C_OPB_AWIDTH : integer                   := 32;
    C_OPB_DWIDTH : integer                   := 32;
    C_BASEADDR   : std_logic_vector(0 to 31) := X"00000000";
    C_HIGHADDR   : std_logic_vector(0 to 31) := X"FFFFFFFF"
    );

  port (
    OPB_Clk     : in  std_logic;
    OPB_Rst     : in  std_logic;
    OPB_ABus    : in  std_logic_vector(0 to C_OPB_AWIDTH-1);  --(31:0)
    OPB_BE      : in  std_logic_vector(0 to C_OPB_DWIDTH/8-1);--(3:0)
    OPB_DBus    : in  std_logic_vector(0 to C_OPB_DWIDTH-1);  --(31:0)
    OPB_RNW     : in  std_logic;
    OPB_select  : in  std_logic;
    OPB_seqAddr : in  std_logic;        -- Sequential Address
    Sln_DBus    : out std_logic_vector(0 to C_OPB_DWIDTH-1);  --(31:0)
    Sln_errAck  : out std_logic;        -- (unused)
    Sln_retry   : out std_logic;        -- (unused)
    Sln_toutSup : out std_logic;        -- Timeout suppress
    Sln_xferAck : out std_logic;        -- Transfer acknowledge

    SRAM_CE     : out std_logic;        --sram chip enable (active low)
    SRAM_OE     : out std_logic;        --sram output enable (active low)
    SRAM_WE     : out std_logic;        --sram write enable (active low)
    SRAM_UB     : out std_logic;        --sram enable upper-byte(active low)
    SRAM_LB     : out std_logic;        --sram enable lower-byte(active low)
    PB_A        : out std_logic_vector(17 downto 0);  --sram 18 bit address
    PB_D        : inout std_ulogic_vector(15 downto 0);  --sram 16 bit data


    au_mclk     : out std_logic;
    au_lrclk    : out std_logic;
    au_bclk     : out std_logic;
    au_sdti     : out std_logic;
    au_sdto0    : in std_logic;
    au_cs       : out std_logic
    );
end gDSP;

architecture Behavioral of gDSP is

  component wr_audio_control
    port (
    clk_in: in std_logic;               -->200ns clock
    init: in std_logic;                 --pulse input
    done: out std_logic;
    D_in: in std_logic_vector(15 downto 0);        --control data
    cntrl_out: out std_logic);
  end component;

  component OBUF_F_24
    port (
      O : out STD_ULOGIC;               -- the pin
      I : in  STD_ULOGIC);              -- signal to pin
  end component;

  component IOBUF_F_24
    port (
      O : out STD_ULOGIC;               -- signal from pin
      IO : inout STD_ULOGIC;            -- the pin
      I : in  STD_ULOGIC;               -- signal to pin
      T : in STD_ULOGIC);               -- 1-drive IO with I
  end component;

  component ak4565
    port (
      clk      : in  std_logic;
      rst      : in  std_logic;
      mclk     : out std_logic;
      bclk     : out std_logic;
      lrclk    : out std_logic;
      sdti     : out std_logic;
      sdto0    : in  std_logic;
      csn      : out std_logic;
      cclk     : out std_logic;
      cdti     : out std_logic;

      adcdone : out std_logic;  --AD indicator
      dacload : out std_logic;  --DA indicator
      adc_dtout : out std_logic_vector(15 downto 0);  --data out to fpga
      dac_dtin  : in  std_logic_vector(15 downto 0);  --parallelload data from fpga
      c_datain : in std_logic_vector(15 downto 0);  --parallelload control from fpga
      c_wr     : in std_logic :='0';  --write to control
      c_done   : out std_logic  --write control done
      );
  end component;

  component distortion
    port(clk: in STD_LOGIC;
    rst: in STD_LOGIC;
    din:in std_logic_vector(15 downto 0);
    dout:out std_logic_vector(15 downto 0)
    );
  end component;

  component SRAM_FX
  port (
		clk:in std_logic; --50Mhz system clk
		rst:in std_logic;

		feedback: in std_logic;
		effect_sel: in std_logic;
    		delay_len: in std_logic_vector(17 downto 0);  --must be in 2's comp negative, this reduce unnessessary computation in vhdl
		trigger: in std_logic;
		done: out std_logic;

		snd_in: in std_logic_vector(15 downto 0);
		snd_out:out std_logic_vector(15 downto 0);

		sram_rnw: out std_logic;
		sram_addr: out std_logic_vector(17 downto 0);
		sram_dt_wr: out std_logic_vector(15 downto 0);
		sram_dt_rd: in std_logic_vector(15 downto 0)
		);
  end component;


  signal ABus: std_logic_vector(17 downto 0);
  signal pbDIn: std_logic_vector(15 downto 0);

  signal tri_state : std_logic;

  signal mclk : std_logic;  --12.5Mhz
  signal bclk : std_logic;  --3.125Mhz
  signal lrclk: std_logic;  --48.8Khz
  signal cclk: std_logic;

  signal sdti: std_logic;
  signal latch16bit,dist_out,echo_out,delay_out,snd_output: std_logic_vector(15 downto 0) := x"0000";

  signal adcdone : std_logic;
  signal dacload : std_logic;
  signal adc_dtout : std_logic_vector(15 downto 0);  --data out to fpga
  signal dac_dtin  : std_logic_vector(15 downto 0) :=x"0000";  --parallelload data from fpga
  signal c_datain : std_logic_vector(15 downto 0) :="1110000011100111";  --parallelload control from fpga
  signal csn : std_logic;
  signal c_wr     : std_logic;
  signal c_done   : std_logic;

  signal feedback, effect_sel: std_logic;
  signal en_echoproc,trigger,echobegin: std_logic;
  signal echodone: std_logic;
 -- signal echoout: std_logic_vector(15 downto 0);
  signal sram_rnw: std_logic;
  signal sram_addr: std_logic_vector(17 downto 0);
  signal sram_dt_wr: std_logic_vector(15 downto 0);
  signal sram_dt_rd: std_logic_vector(15 downto 0);

  signal initcnt:std_logic_vector(15 downto 0):=X"0000";  --has to wait > 4128/fs=90ms for initialization
  constant pre_init : std_logic_vector(1 downto 0):="00";
  constant c_wr_wait: std_logic_vector(1 downto 0):="01";
  constant wait_done: std_logic_vector(1 downto 0):="10";
  constant norm : std_logic_vector(1 downto 0):="11";
  signal c_state, n_state :  std_logic_vector(1 downto 0):="00";
  signal c_serial_data: std_logic;


----------------------------OPB SIGNALS
---------------------------PART FOR C PROGRAM CHOICE OF EFFECT ______________
  signal RNW : std_logic;--read not write
  --signal ABus : std_logic_vector(0 to 15);
  signal chip_select : std_logic;
  signal output_enable : std_logic;
  signal WE, RST : std_logic;

  -- Critical: Sln_xferAck is generated directly from state bit 0!
  constant STATE_BITS : integer := 3;
  constant Idle     : std_logic_vector(0 to STATE_BITS-1) := "000";
  constant Selected : std_logic_vector(0 to STATE_BITS-1) := "001";
  constant Read     : std_logic_vector(0 to STATE_BITS-1) := "011";
  constant Xfer     : std_logic_vector(0 to STATE_BITS-1) := "111";

  signal present_state, next_state : std_logic_vector(0 to STATE_BITS-1);

  signal DI: std_logic_vector(0 to 15);
  signal fx_sel:std_logic_vector(0 to 2);
  --------------------------END OPB SIGNALS------------------------------------

----------------------------------------------------------------------------------
----------------------------------------------------------------------------------
-----------BEGIN ARCHITECTURE------------------------------------------------------
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
begin
------Audio Interface Signals and Buffering--------------------------------------------------

    au_mclk<=mclk;  -----VERY IMPORTANT---- mclk must be sync with LRCLK
    au_bclk<=bclk;
    au_lrclk<=lrclk;
    au_sdti <=sdti;
    au_cs<=csn;

------SRAM pin assignment-----------------------------------------------------------------------
    SRAM_CE <='0' when c_state=norm else  -- *** must enable SRAM!!!
              '1';
    SRAM_OE <='0';
    SRAM_WE <=sram_rnw;
    SRAM_UB <='0';
    SRAM_LB <='0';

    gen1: for m in 0 to 17 generate
    sramAddrpin:OBUF_F_24 port map (O=>PB_A(m), I=>ABus(m));
    end generate;

    gen2: for m in 0 to 15 generate
    sramDatapin:IOBUF_F_24 port map (O=>sram_dt_rd(m),IO=>PB_D(m), I=>pbDIn(m), T=>tri_state );
    end generate;
    tri_state <=sram_rnw when c_state=norm else
                '0'; --always writing


    ABus<=sram_addr(17 downto 0);
    pbDIn(15 downto 2) <=sram_dt_wr(15 downto 2) when c_state=norm else
                     (others => '0');
    pbDIn(0)<=sram_dt_wr(0) when c_state=norm else
              cclk;
    pbDIn(1)<=sram_dt_wr(1) when c_state=norm else
              c_serial_data;

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

    ak:ak4565 port map(clk =>OPB_Clk,
      rst      =>OPB_Rst,
      mclk     =>mclk,
      bclk     =>bclk,
      lrclk    =>lrclk,
      sdti     =>sdti,
      sdto0    =>au_sdto0,
      csn      =>csn,
      cclk     =>cclk,
      cdti     =>c_serial_data,
      adcdone =>adcdone,
      dacload =>dacload,
      adc_dtout => adc_dtout,
      dac_dtin  => dac_dtin,
      c_datain =>c_datain,
      c_wr    =>c_wr,
      c_done  =>c_done);

      ak_distort:distortion port map(
      clk=>OPB_Clk,
      rst =>OPB_Rst,
      din => latch16bit,
      dout=> dist_out
      );

    echobegin<=trigger when c_state = norm else
                '0';
  delaygen:SRAM_FX port map(
		clk=>OPB_Clk,
		rst=>OPB_Rst,
    		feedback=>feedback,
		effect_sel=>effect_sel,
    		delay_len=>"000101110111000000",
		trigger=>echobegin,
		done=>echodone,
		snd_in=>latch16bit,
		snd_out=>echo_out,
		sram_rnw=>sram_rnw,
		sram_addr=>sram_addr,
		sram_dt_wr=>sram_dt_wr,
		sram_dt_rd=>sram_dt_rd
		);

    ---latch output data----------------------
    process (OPB_Clk,OPB_Rst)
    begin
      if OPB_Rst = '1' then
        latch16bit<=x"0000";
        dac_dtin<=x"0000";
      elsif OPB_clk'event and OPB_clk ='1' then
        if adcdone = '1' and dacload = '1' then
          latch16bit(15 downto 0)<=adc_dtout(15 downto 0);
          dac_dtin(15 downto 0)<=snd_output(15 downto 0);
        end if;
      end if;
    end process;

    --- generate effect trigger-----------------
    process (OPB_Clk,OPB_Rst)
    begin
      if OPB_Rst = '1' then
        trigger<='0';
        en_echoproc<='0';
      elsif OPB_clk'event and OPB_clk ='1' then
        if adcdone = '1' and dacload = '1' then
          if echodone='0' and en_echoproc='1' then
            trigger<='1';
          else
            en_echoproc<='0';
            trigger<='0';
          end if;
        else
          en_echoproc<='1';
        end if;
      end if;
    end process;


 ----control initialization-------
 --op0 op1 op2 a0 a1 a2 a3 a4 d0 d1 d2 d3 d4 d5 d6 d7
 c_datain<=   "1110000000100000";

 process (bclk, OPB_Rst)
 begin
   if OPB_Rst = '1' then
      c_state <= pre_init;
   elsif bclk'event and bclk='1' then
      c_state<=n_state;
   end if;
 end process;

 process (lrclk, OPB_Rst)
 begin
   if OPB_Rst = '1' then
      initcnt <= X"0000";
   elsif lrclk'event and lrclk='1' then
      initcnt <= initcnt + 1;
   end if;
 end process;

process (initcnt(15),c_state,c_done)
begin
  c_wr<='0';
  case c_state is
  when pre_init =>
  --wait for initcnt
    if initcnt(15) = '1' then
      n_state<=c_wr_wait;
    else
      n_state<=pre_init;
    end if;
  when c_wr_wait =>
  --hold c_wr for 1 cycle
      c_wr<='1';
      n_state<=wait_done;
  when wait_done =>
      if c_done='1' then
        --normal operation
        n_state<=norm;
      else
        n_state<=wait_done;
      end if;
  when norm =>
    n_state<=norm;

  when others =>
    n_state<=pre_init;
  end case;
end process;


register_opb_inputs: process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      DI <= (others => '0');
      --ABus <= (others => '0');
      RNW <= '0';
    elsif OPB_Clk'event and OPB_Clk = '1' then
      DI <= OPB_DBus(0 to 15);
      --ABus <= OPB_ABus(C_OPB_AWIDTH-3-(RAM_AWIDTH-1) to C_OPB_AWIDTH-3);
      RNW <= OPB_RNW;
    end if;
  end process register_opb_inputs;

register_opb_outputs: process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      Sln_DBus(0 to 15) <= (others => '0');
    elsif OPB_Clk'event and OPB_Clk = '1' then
     if output_enable = '1' then
        Sln_DBus(0 to 15) <= fx_sel &"0000000000000"; --test purposes for now
     else
       Sln_DBus(0 to 15) <= (others => '0');
     end if;
    end if;
  end process register_opb_outputs;

  -- Unused outputs
  Sln_errAck  <= '0';
  Sln_retry   <= '0';
  Sln_toutSup <= '0';
  Sln_DBus(16 to 31) <= (others => '0');

  chip_select <=
    '1' when OPB_select = '1' and OPB_ABus(0 to 31) = x"FEFF1001"
     else '0';

--latch accumulator --for testing
another : process(OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      fx_sel<="000";
    elsif OPB_Clk'event and OPB_Clk = '1' then
	if WE= '1' then
	   fx_sel <= DI(0 to 2);
	end if;
    end if;
  end process another;


  -- Sequential part of the FSM
  fsm_seq : process(OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      present_state <= Idle;
    elsif OPB_Clk'event and OPB_Clk = '1' then
      present_state <= next_state;
    end if;
  end process fsm_seq;

  -- Combinational part of the FSM
  fsm_comb : process(OPB_Rst, present_state, chip_select, OPB_Select, RNW)
  begin
    RST <= '1';                         -- Default values
    WE <= '0';
    output_enable <= '0';
    if OPB_RST = '1' then
      next_state <= Idle;
    else
      case present_state is
        when Idle =>
          if chip_select = '1' then
            next_state <= Selected;
          else
            next_state <= Idle;
          end if;

        when Selected =>
          if OPB_Select = '1' then
            if RNW = '1' then
              RST <= '0';
              next_state <= Read;
            else
              WE <= '1';
              next_state <= Xfer;
            end if;
          else
            next_state <= Idle;
          end if;

        when Read =>
          if OPB_Select = '1' then
            output_enable <= '1';
            next_state <= Xfer;
          else
            next_state <= Idle;
          end if;

        -- State encoding is critical here: xfer must only be true here
        when Xfer =>
          next_state <= Idle;

        when others =>
          next_state <= Idle;
      end case;
    end if;
  end process fsm_comb;

  Sln_xferAck <= present_state(0);


snd_output<=latch16bit(15 downto 0) when fx_sel="000" else
	    dist_out(15 downto 0) when fx_sel="001" else
	    echo_out(15 downto 0);

feedback<=fx_sel(1);
effect_sel<=fx_sel(2);

end Behavioral;