-- I2S stereo encoder

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY i2s IS
  GENERIC (
    CLK_RATE   : natural :=50000000;
    AUDIO_DW   : natural :=16;
    AUDIO_RATE : natural :=96000);
  PORT (
    reset : IN std_logic;
    clk_sys : IN std_logic;
    half_rate : IN std_logic;

    sclk : OUT std_logic;
    lrclk : OUT std_logic;
    sdata : OUT std_logic;

    left_chan : IN std_logic_vector(AUDIO_DW-1 DOWNTO 0);
    right_chan : IN std_logic_vector(AUDIO_DW-1 DOWNTO 0)
    );  
END ENTITY i2s;

ARCHITECTURE rtl OF i2s IS
  
  FUNCTION gcd (
    CONSTANT a  : natural;
    CONSTANT b  : natural) RETURN natural IS
  BEGIN
    IF b/=0 THEN
      RETURN gcd(b,a MOD b);
    ELSE
      RETURN a;
    END IF;
  END FUNCTION gcd;
  
  CONSTANT AFREQ : natural := AUDIO_RATE * AUDIO_DW * 2 * 2;
  CONSTANT MUL   : natural := AFREQ/gcd(CLK_RATE,AFREQ);
  CONSTANT DIV   : natural :=  CLK_RATE/gcd(CLK_RATE,AFREQ);
  SIGNAL acc   : integer RANGE -MUL TO DIV-MUL :=0;
  
  SIGNAL sync,tog : std_logic;
  SIGNAL cpt : unsigned(5 DOWNTO 0);
  SIGNAL shift : std_logic_vector(AUDIO_DW*2-1 DOWNTO 0);
  
BEGIN

  ----------------------------------------------------------
  ClockGen:PROCESS (clk_sys) IS
  BEGIN
    IF rising_edge(clk_sys) THEN
      IF acc>0 THEN
        acc<=acc-MUL;
        sync<='0';
      ELSE
        acc<=acc+DIV-MUL;
        sync<='1';
      END IF;
		 IF reset='1' THEN
			sync<='0';
		 END IF;
		END IF;
  END PROCESS ClockGen;
  
  ----------------------------------------------------------
  Serial:PROCESS (clk_sys) IS
  BEGIN
    IF rising_edge(clk_sys) THEN
      IF sync='1' AND (tog='1' OR half_rate='0') THEN
        cpt<=cpt+1;
        sclk<=cpt(0);
        lrclk<=cpt(5);
        sdata<=shift(shift'left);
        IF cpt="000001" THEN
          shift<=left_chan & right_chan;
        ELSIF cpt(0)='1' THEN
          shift<=shift(shift'high-1 DOWNTO 0) & right_chan(0);
        END IF;
      END IF;
      
      tog<=tog XOR sync;
      
      IF reset='1' THEN
        tog<='0';
        cpt<=(OTHERS =>'0');
      END IF;
    END IF;

  END PROCESS Serial;
  
END ARCHITECTURE rtl;
