--345678901234567890123456789012345678901234567890123456789012345678901234567890
--       1         2         3         4         5         6         7     
-- Title:     Silicon Lab Si5341 Clock Generator Configuration Design
-- Engineer:  Evgeny Shumilov <eugene.shumilov@gmail.com>
-- Company:   For HiTechGlobal
-- Project:   HTG-ZRF8
-- File name: si5341_gen_cfg.vhd
--------------------------------------------------------------------------------
-- Purpose:   Configures Si5341 Clock Generators on Start-up
--------------------------------------------------------------------------------
-- Simulator: Xilinx Vivado
-- Synthesis: Xilinx Vivado
--------------------------------------------------------------------------------
-- Revision:  1.00
-- Modification date: 20/10/2018
-- Limitation: Design requires  12.5MHz input clock. 
--             Change COUNT_DIV value for other bus frequency.
-- Notes: 
--------------------------------------------------------------------------------
-- Modifications List:
--
--
--------------------------------------------------------------------------------


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
--use IEEE.std_logic_signed.all;
USE ieee.numeric_std.all;
use IEEE.std_logic_misc.all;

library unisim;
use unisim.vcomponents.all;

use work.global_types.all;

entity si5341_gen_cfg is
  generic (
    COUNT_DIV                         : integer range 0 to 1023 := 512; -- sysclk divide coefficient (Valid Values: 2 to 1023)
    SIZ5341                           : integer range 0 to 511 := 511;  -- Real Address/Data Array Size (Valid Values: 1 to 511)
    ADR5341                           : addr_si5341_type := (others => (others => '0'));
    DAT5341                           : data_si5341_type := (others => (others => '0'));
    simulation_mode                   : std_logic  := '0'
    );  
  port (
  -- System signals
    CLK                               : in std_logic;       -- main clock
    RST                               : in std_logic;       -- system reset

  -- External Operation Control
    START                             : in std_logic;       -- Run Start-Up Clock Genrators Configuration

  -- Status Output
    i2c_mux_access_ok_out             : out std_logic;
    si5341_access_ok_out              : out std_logic;
    si5341_config_done_out            : out std_logic;
    si5341_config_error_out           : out std_logic;
    
  -- I2C Bus
    I_SDA_I                           : in std_logic;
    O_SDA_O                           : out std_logic;
    O_SDA_T                           : out std_logic;

    I_SCL_I                           : in std_logic;
    O_SCL_O                           : out std_logic;
    O_SCL_T                           : out std_logic
  );
end entity si5341_gen_cfg;

architecture si5341_gen_cfg_arch of si5341_gen_cfg is

component i2c
  generic (
  count_div : integer range 0 to 1023:= 512   -- sysclk divide coefficient (2 to 1023 max)
  );  
  port (
  ADDR_IN   : in std_logic_vector (7 downto 0); -- word address
  DAT_IN    : in std_logic_vector(7 downto 0);  -- write data
  DEV_ADDR  : in std_logic_vector (6 downto 0); -- device address
  CONTINUE  : in std_logic;       -- continue read operation from ADDR_IN
  AP_EN   : in std_logic; -- Enable Address Phase During Write
  WR_OP   : in std_logic;       -- write operation WRITE ONE WORD <= '0', RD <= '1'
  WR_EN   : in std_logic;       -- enable write ADDR_IN, DAT_IN, ID_ADDR, WR_OP
  CLK   : in std_logic;       -- main clock
  RST   : in std_logic;       -- system reset
  
  I2C_RDATA : out std_logic_vector(7 downto 0); --  i2c read data
  ACK_L   : out std_logic;      --  acknowledge WR_EN (active '0')
--  SDA   : inout std_logic;      --  i2c data
--  SCL   : out std_logic;      --  i2c CLK

I_SDA_I     : in std_logic;
O_SDA_O     : out std_logic;
O_SDA_T     : out std_logic;

I_SCL_I    : in std_logic;
O_SCL_O    : out std_logic;
O_SCL_T    : out std_logic;

  I2C_RDY   : out std_logic;      --  i2c ready
  I2C_ACT   : out std_logic;      --  i2c cycle active
  ACK_ERR   : out std_logic       --  i2c(ack) error
  );
end component;


constant sim_timescale  : time := 1 ns;


type fsm_state                is (IDLE, SI5341_NEW, SI5341_NEW_WR, SI5341_NEW_INC, FINISH, ERROR);

signal state_r                : fsm_state := IDLE;

signal i2c_rdata              : std_logic_vector(7 downto 0);

signal si5341_adr_arr         : addr_si5341_type := ADR5341;
signal si5341_dat_arr         : data_si5341_type := DAT5341;

signal dev_addr               : std_logic_vector(6 downto 0);

signal new_adr                : std_logic_vector(7 downto 0);
signal cur_addr               : integer range 0 to 511 := 0;
signal inc_adr                : std_logic;
signal new_da                 : std_logic_vector(6 downto 0):= (others => '0');
signal addr                   : std_logic_vector(8 downto 0);
signal new_dat                : std_logic_vector(7 downto 0);
signal new_wr                 : std_logic;
signal new_op                 : std_logic;
signal new_apen               : std_logic := '0';
signal ap_en                  : std_logic;

signal dat_in                 : std_logic_vector(7 downto 0);

signal word_addr              : std_logic_vector(7 downto 0);
signal we_i2c                 : std_logic;
signal wr_i2c                 : std_logic;

signal ack_err                : std_logic;
signal i2c_rdy                : std_logic;
signal i2c_act                : std_logic;
signal CONTINUE               : std_logic;
signal ACK_L                  : std_logic;

signal i2c_busy               : std_logic;
signal i2c_act_fall           : std_logic := '0';
signal i2c_act_rg             : std_logic := '0';

signal addr_itg               : integer range 0 to 511;
signal si5341_size            : integer range 0 to 511;
signal si5341_data            : std_logic_vector(7 downto 0);
signal si5341_addr            : std_logic_vector(15 downto 0);
signal si5341_addr_h          : std_logic_vector(7 downto 0);
signal si5341_addr_l          : std_logic_vector(7 downto 0);

signal state_test_r           : std_logic_vector(3 downto 0) := (others => '0');

  signal i2c_mux_access_ok_r    : std_logic := '0';
  signal si5341_access_ok_r     : std_logic := '0';
  signal si5341_config_done_r   : std_logic := '0';
  signal si5341_config_error_r  : std_logic := '0';
  

begin

  addr_itg      <= CONV_INTEGER(cur_addr);
  si5341_addr   <= si5341_adr_arr(addr_itg);
  si5341_data   <= si5341_dat_arr(addr_itg);
  si5341_size   <= SIZ5341;
  si5341_addr_h <= si5341_addr(15 downto 8); -- Page Number
  si5341_addr_l <= si5341_addr( 7 downto 0); -- Byte Address

  i2c_mux_access_ok_out   <= i2c_mux_access_ok_r;
  si5341_access_ok_out    <= si5341_access_ok_r;
  si5341_config_done_out  <= si5341_config_done_r;
  si5341_config_error_out <= si5341_config_error_r;
  
  process(state_r) 
  begin
    case state_r is
      when IDLE =>              state_test_r    <= "0000";
      when SI5341_NEW =>        state_test_r    <= "0011";
      when SI5341_NEW_WR =>     state_test_r    <= "0101";
      when SI5341_NEW_INC =>    state_test_r    <= "0110";
      when FINISH =>            state_test_r    <= "1000";
      when ERROR =>             state_test_r    <= "1001";
      when others =>            state_test_r    <= "1111";      
    end case;
  end process;
  
--  i_ila_1 : entity work.ila_1
--    port map (
--      clk                         => CLK,
--      probe0                      => state_test_r,        --4
--      probe1                      => new_da,              --7
--      probe2                      => new_dat,             --8
--      probe3(0)                   => new_wr,              --1     
--      probe4(0)                   => i2c_busy,            --1     
--      probe7(0)                   => i2c_rdy,            --1
--      probe8(0)                   => i2c_act,            --1       
--      probe9(0)                   => ack_err,             --1      
--      probe10(0)                   => START,             --1    
--      probe13                      => new_adr         -- 8  
--    );

---------------------------------------------------------------------------------------------
-- I2C Mudule
---------------------------------------------------------------------------------------------

  SPD_READ_UNIT: i2c
    generic map(
      count_div               => COUNT_DIV        -- sysclk divide coafficien (2 to 1023 max)
      ) 
    port map(
      ADDR_IN                 => word_addr,       -- word address
      DAT_IN                  => DAT_IN,        -- write data
      DEV_ADDR                => dev_addr,        -- device address
      CONTINUE                => CONTINUE,        -- continue read operation from ADDR_IN
      AP_EN                   => ap_en, -- Enable Address Phase During Write
      WR_OP                   => wr_i2c,        -- write operation WRITE ONE WORD <= '0', RD <= '1'
      WR_EN                   => we_i2c,        -- enable write ADDR_IN, DAT_IN, ID_ADDR, WR_OP
      CLK                     => CLK,         -- main clock
      RST                     => RST,         -- system reset

      I2C_RDATA               => i2c_rdata,       --  i2c read data
      ACK_L                   => ACK_L,       --  acknowledge WR_EN (active '0')

      I_SDA_I                 => I_SDA_I,
      O_SDA_O                 => O_SDA_O,
      O_SDA_T                 => O_SDA_T,

      I_SCL_I                 => I_SCL_I,
      O_SCL_O                 => O_SCL_O,
      O_SCL_T                 => O_SCL_T,

      I2C_RDY                 => i2c_rdy,       --  i2c ready
      I2C_ACT                 => i2c_act,       --  i2c cycle active
      ACK_ERR                 => ack_err        --  i2c(ack) error
      );               
          
  CONTINUE  <= '0';
  word_addr <= new_adr;
  dev_addr  <= new_da;
  we_i2c    <= new_wr;
  wr_i2c    <= new_op;
  dat_in    <= new_dat;
  ap_en     <= new_apen;

  I2C_BUSY_RG: process (RST, CLK)
  begin
  if RST = '1' then
    i2c_busy                  <= '0';
  elsif rising_edge(CLK) then
    if i2c_act_fall = '1' then
      i2c_busy                <= '0';
    elsif new_wr = '1' then
      i2c_busy                <= '1';
    end if;
    i2c_act_rg                <= i2c_act;
    i2c_act_fall              <= i2c_act_rg and not i2c_act;
  end if;
  end process;

  process(CLK, RST)
  begin
    if (RST = '1') then
      i2c_mux_access_ok_r       <= '0';
      si5341_access_ok_r        <= '0';
      si5341_config_done_r      <= '0';
      si5341_config_error_r     <= '0';
    
      cur_addr                  <= 0;
      state_r                   <= IDLE;
    elsif (rising_edge(CLK)) then
      if inc_adr = '1' then
        cur_addr <=  cur_addr + 1;
      end if;

      case state_r is

        when IDLE =>  --0
          new_adr               <= si5341_addr_l;
          new_dat               <= X"55";  -- selects SC0/SD0 from I2C Mux  U22 on ZCU208
          new_op                <= '0';
          inc_adr               <= '0';
          if (START = '1') then 
            new_da              <= "1101010"; -- 6Ah - Si5332 Access 
            new_apen            <= '1';
            new_wr              <= '0';
            i2c_mux_access_ok_r <= '1';
            state_r             <= SI5341_NEW;
          else
            new_wr              <= '0';
            state_r             <= IDLE;
          end if;

        when SI5341_NEW =>    --3
          new_adr               <= si5341_addr_l;
          new_dat               <= si5341_data;
          new_op                <= '0';
          new_wr                <= '0';
          inc_adr               <= '0';
          if (cur_addr = si5341_size) then 
            state_r             <= FINISH;
          else 
            state_r             <= SI5341_NEW_WR;
          end if;

    --     Write Register Value
        when SI5341_NEW_WR =>    --5
          new_adr               <= si5341_addr_l;
          new_dat               <= si5341_data;
          new_op                <= '0';
          inc_adr               <= '0';
          if (i2c_busy = '0') then 
            if (ack_err = '1') then
              state_r           <= ERROR;
              new_wr            <= '0';
            else
              si5341_access_ok_r    <= '1';
              new_wr                <= '1';
              state_r               <= SI5341_NEW_INC;
            end if;
          else 
            new_wr                  <= '0';
            state_r                 <= SI5341_NEW_WR;
          end if;

    --     Increment Counter
        when SI5341_NEW_INC =>   --6
          new_adr               <= si5341_addr_l;
          new_dat               <= si5341_data;
          new_op                <= '0';
          new_wr                <= '0';
          if (i2c_busy = '0') then 
            if ack_err = '1' then
              inc_adr           <= '0';
              state_r           <= ERROR;
            else
              inc_adr           <= '1';
              state_r           <= SI5341_NEW;
            end if;
          else 
            inc_adr             <= '0';
            state_r             <= SI5341_NEW_INC;
          end if;

        when FINISH =>     --8
          new_adr               <= X"00";
          new_dat               <= X"00";
          new_op                <= '0';
          new_wr                <= '0';
          inc_adr               <= '0';
          si5341_config_done_r  <= '1';
          state_r               <= FINISH;

        when ERROR =>      --9
          new_adr               <= X"00";
          new_dat               <= X"00";
          new_op                <= '0';
          new_wr                <= '0';
          inc_adr               <= '0';
          si5341_config_error_r <= '1';
          state_r               <= ERROR;

        when others =>  --f
          new_adr               <= X"00";
          new_dat               <= X"00";
          new_op                <= '0';
          new_wr                <= '0';
          inc_adr               <= '0';
          state_r               <= IDLE;

      end case;
    end if;
  end process;     

          
end si5341_gen_cfg_arch;