library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.ser_if_pack.all;
entity ser_if is
port(
    reset_z                                   : in      std_logic;
    scan_mode                                 : in      std_logic;
     ser_clk                                  :   in    std_logic;
     ser_clk_z                                :   in    std_logic;
     ser_enable_z                             :   in    std_logic;
     ser_data_in                              :   in    std_logic;
     ser_data_out                             :   out   std_logic;
     ser_data_out_en                          :   out   std_logic;
     read_mode                                : in std_logic;
     burst_mode                               : in std_logic;
--     pi_ser_if_fsm_proc_reset_z               : in std_logic;
-- Memory interface
    mem_clk                                  : out std_logic;
    mem_wz                        : out std_logic; -- 0 write , 1 read
    mem_rdata                           : in std_logic_vector(DW-1 downto 0);
    mem_raddr                           : out std_logic_vector(AW-1 downto 0);
    mem_wdata                           : out std_logic_vector(DW-1 downto 0);
    mem_waddr                           : out std_logic_vector(AW-1 downto 0)
    );
end entity;
architecture behav of ser_if is
component tiboxv_clk_icg
port (
             en                                 : in std_logic;
             te                                 : in std_logic;
             clk                              : in std_logic;
             clkout                           : out std_logic
     );
end component;
-- SPI state machine
type ser_if_fsm_type is (addr_dec, write_dec, read_src);
signal curr_state               : ser_if_fsm_type;
signal   bit_count                  :   unsigned(BW-1 downto 0);
signal   shift_register             :   std_logic_vector(DW-2 downto   0);
signal   addr_reg                   :   std_logic_vector(AW-1 downto   0);
signal   com_rdata                  :   std_logic_vector(DW-1 downto   0);
signal   ser_data_out_shift_reg     :   std_logic_vector(DW-2 downto   0);
signal int_addr                     : integer range 0 to 255;
signal sig_read_mode                      : std_logic;
signal sig_burst_mode                 : std_logic;
signal   ser_data_out_int              : std_logic;
signal     sig_mem_clk             :std_logic;
signal     sig_mem_wz            :std_logic; -- 0 write , 1 read
signal     sig_mem_w_en          :std_logic;
signal     sig_mem_rdata         :std_logic_vector(DW-1 downto 0);
signal     sig_mem_raddr         :std_logic_vector(AW-1 downto 0);
signal     sig_mem_wdata         :std_logic_vector(DW-1 downto 0);
--signal     sig_mem_waddr             :std_logic_vector(AW-1 downto 0);
signal ser_if_fsm_proc_reset_z     : std_logic;
begin    -- behav
-- address int conversion
int_addr                <= to_integer(unsigned(addr_reg));
com_rdata               <= mem_rdata;
-- Internal reset for serial interface
ser_if_fsm_proc_reset_z <= '0'            when ((reset_z='0') or (ser_enable_z='1'
and scan_mode='0')) else
                           '1';
-- address int conversion
int_addr                <= to_integer(unsigned(addr_reg));
com_rdata               <= mem_rdata;
-- Internal reset for serial interface
ser_if_fsm_proc_reset_z <= '0'            when ((reset_z='0') or (ser_enable_z='1'
and scan_mode='0')) else
                           '1';
--sig_read_mode               <='1';
-----------------------------------------------------------------------------
-- SPI fsm
--sig_read_mode               <='1';
-----------------------------------------------------------------------------
-- SPI fsm
-----------------------------------------------------------------------------
process(ser_clk,ser_if_fsm_proc_reset_z)
    variable addr_var : std_logic_vector(AW-1 downto 0);
begin -- process ser_if_fsm_proc
      if(ser_if_fsm_proc_reset_z='0') then
          curr_state      <= addr_dec;
          bit_count       <= (others=>'0');
          shift_register <= (others=>'0');
          addr_reg        <= (others=>'0');
        -- Sync oper. All MSB first
      elsif(ser_clk'event and ser_clk='1') then -- rising clock edge
          shift_register(0)               <= ser_data_in;
          shift_register(DW-2 downto 1)   <= shift_register(DW-3 downto 0);
          case curr_state is
              -- Decode ADDR : AW-bit addr
              when addr_dec =>
                      if(bit_count=to_unsigned(AW-1,BW)) then
                          -- only last AW-2 bits of address field are considered
                          bit_count      <= (others=>'0');
                          addr_var       := shift_register(AW-2 downto 0) &
ser_data_in;
                          addr_reg        <= addr_var;
                          if(addr_var/=std_logic_vector(to_unsigned(0,AW-2)) and
read_mode='1') then
                               curr_state <= read_src;
                          else
                              curr_state <= write_dec;
                          end if;
                      else
                          bit_count <= bit_count + 1;
                      end if;
              -- Decode WRITE : DW-bit, single
              when write_dec =>
                  if(bit_count=to_unsigned(DW-1,BW)) then
                      bit_count <= (others=>'0');
                      if(burst_mode='0') then
                          curr_state <= addr_dec;
                          addr_reg    <= (others=>'0');
                      else -- continous mode. Format is A D D D D ....
                          -- In burst mode, once we try to write R/W bit for reading,
...
                        -- ... we will always be in write mode only as the sequence
would be A D D D D D D D and ...
                        -- ... we would be held up in write mode. To avoid this, we
have to jump to 'addr_dec' state ...
                         -- ... once we write into R/W bit. So the final sequence
would be A D A D D D D D D ....
                         if(int_addr=0) then
                              curr_state <= addr_dec;
                              addr_reg   <= (others=>'0');
                         else
                              curr_state <= write_dec;
                              addr_reg   <= std_logic_vector(unsigned(addr_reg) + 1);
                         end if;
                     end if;
                else
                     bit_count <= bit_count + 1;
                end if;
             -- Source READ : DW-bits, single
             when read_src =>
                 if(bit_count=to_unsigned(DW-1,BW)) then
                     curr_state <= addr_dec;
                     bit_count <= (others=>'0');
                     if(burst_mode='0') then
                         curr_state <= addr_dec;
                         addr_reg   <= (others=>'0');
                     else                            -- continous mode A D D D
D ....
                         curr_state <= read_src;
                     addr_reg   <= std_logic_vector(unsigned(addr_reg) + 1);
                     end if;
                 else
                     bit_count <= bit_count + 1;
                 end if;
             -- Error condition - Go to ADDR_DEC
             when others =>
                 curr_state <= addr_dec;
        end case;
    end if;
end process;
-- Memory controls
mem_waddr                   <= addr_reg;
mem_raddr                   <= addr_reg;
sig_mem_wz                  <='1'          when (read_mode='1' )
else
                             '0'          when (read_mode='0' and
bit_count=to_unsigned(DW-1,BW) and curr_state=write_dec) else
                             '1';
mem_wz                     <= sig_mem_wz;
sig_mem_w_en               <= not(sig_mem_wz);
mem_clk_icg : tiboxv_clk_icg
port map (
             en           => sig_mem_w_en,
             te           => scan_mode,
             clk        => ser_clk,
             clkout     => mem_clk
         );
--mem_clk <= ser_clk;
-- mem_waddr        <= sig_mem_waddr;
 mem_wdata        <= shift_register & ser_data_in;
-----------------------------------------------------------------------------
-- Ser Read is pumped out at the neg edge
-----------------------------------------------------------------------------
process(ser_clk_z,ser_if_fsm_proc_reset_z)
begin
    if(ser_if_fsm_proc_reset_z='0') then
        ser_data_out_en         <= '0';
    elsif(ser_clk_z'event and ser_clk_z='1') then
        if(curr_state=read_src) then
             ser_data_out_en    <= '1';
        else
             ser_data_out_en    <= '0';
        end if;
    end if;
end process;
process(ser_clk_z, reset_z)
begin
    if(reset_z='0') then
        ser_data_out_int          <= '0';
        ser_data_out_shift_reg <= (others=>'0');
    elsif(ser_clk_z'event and ser_clk_z='1') then
        if(curr_state=read_src) then
            if(bit_count=to_unsigned(0,BW)) then
                 ser_data_out_int         <= com_rdata(DW-1);
                 ser_data_out_shift_reg <= com_rdata(DW-2 downto 0);
            else
                 ser_data_out_int                         <=
ser_data_out_shift_reg(DW-2);
                 ser_data_out_shift_reg(DW-2 downto 1)    <=
ser_data_out_shift_reg(DW-3 downto 0);
            end if;
        end if;
    end if;
end process;
ser_data_out <= ser_data_out_int;
end behav;