library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

--  Uncomment the following lines to use the declarations that are
--  provided for instantiating Xilinx primitive components.
--library UNISIM;
--use UNISIM.VComponents.all;

entity vconfig is
    Port ( RES : in std_logic;
	        nINIT_B : in std_logic;
           CLK : in std_logic;
           DONE : in std_logic;
           CCLK : out std_logic;
           CCLK_PRESCALER : in std_logic_vector(5 downto 0);
           LOAD_PRESCALER : in std_logic;
           DIN : in std_logic_vector(31 downto 0);
           DOUT : out std_logic;
           LOAD_SREG : in std_logic;
			  BUF_IS_EMPTY : inout std_logic);
end vconfig;

architecture Behavioral of vconfig is

constant nbits : integer := 32;
constant nBufs : integer := 2;

type t_config_state is (CFG_STOP, CFG_INIT, CFG_RUN);
type t_buf is array (0 to nBufs-1) of std_logic_vector (nBits-1 downto 0); 

signal REG: t_buf;
signal count, CLK_SCALE: integer range 0 to 63;
signal SHIFT_CNT: integer range 0 to nbits-1;
signal state, next_state : t_config_state;
signal SCLK, SHIFT_EN : STD_LOGIC;
signal buf_depth: integer range 0 to 2;
signal tempID, readBufId, writeBufId : integer range 0 to nBufs-1;


begin

-- 4-bit loadable serial-in and serial-out shift register
--     CLK: in STD_LOGIC;
--     DIN: in STD_LOGIC;
--     LOAD: in STD_LOGIC;
--     LOAD_DATA: in STD_LOGIC_VECTOR(3 downto 0);
--     DOUT: out STD_LOGIC; 
 

process (RES, SCLK, state, nINIT_B, DONE, REG, BUF_IS_EMPTY)
begin
   if (RES = '1') then
	   CCLK <= '1';
		SHIFT_EN <= '0';
		DOUT <= 'Z';

	else
	   case state is
		when CFG_STOP =>
		SHIFT_EN <= '0';
	   CCLK <= '1';
		if (DONE = '0' and BUF_IS_EMPTY = '0') then
		  next_state <= CFG_INIT;
		else
		  next_state <= CFG_STOP;
      end if;

		when CFG_INIT =>
      CCLK <= SCLK;
 	   next_state <= CFG_RUN;

		when CFG_RUN =>
		SHIFT_EN <= '1';
      CCLK <= SCLK;
		if (DONE = '1' or nINIT_B = '0' or BUF_IS_EMPTY = '1') then
		  next_state <= CFG_STOP;
		else
		  next_state <= CFG_RUN;
      end if;
	 	end case;
	   if (nINIT_B = '1') then
	     DOUT <= STD_LOGIC(REG(readBufId)(nbits-1));
	   else
	     DOUT <= 'Z';
	   end if;
	end if;

end process;

sreg:
  process(RES, SCLK, REG, DIN, LOAD_SREG, SHIFT_EN, SHIFT_CNT)
  begin
    if (RES = '1') then
  		BUF_IS_EMPTY <= '1';
		SHIFT_CNT <= 0;
		writeBufId <= 0;
		readBufId <= 0;
		buf_depth <= 0;

	 elsif (LOAD_SREG = '1') then			
	   REG(writeBufId) <= DIN;
	   
      if (SHIFT_EN = '0') then
        SHIFT_CNT <= nbits-1;
		end if;

		if (buf_depth = nbufs) then
  		  buf_depth <= buf_depth ;
		else
  		  buf_depth <= buf_depth + 1;
		end if;

	 else
	   if SCLK='0' and SCLK'event then
	     if SHIFT_EN = '1' then
		    REG(readBufId)(nbits-1 downto 0) <= REG(readBufId)(nbits-2 downto 0) & '0';
          if (SHIFT_CNT = 0) then
				if (buf_depth = 0) then
              writeBufId <= 0;
		        readBufId <= 0;
				else
		        SHIFT_CNT <= nbits-1;
				  tempID <= readBufID;
				  readBufID <= writeBufId;
				  writeBufId <= tempID;
				  buf_depth <= buf_depth - 1;
				end if;
			 else
  		      SHIFT_CNT <= SHIFT_CNT - 1;
			 end if;
		  end if;
		end if;
	 end if;
    if (SHIFT_CNT = 0 and buf_depth = 1) then
      BUF_IS_EMPTY <= '1';
    else
      BUF_IS_EMPTY <= '0';
	 end if;
  end process;

-- 4-bit synchronous counter with count enable, 
-- asynchronous reset
--     CLK: in STD_LOGIC;
--     RESET: in STD_LOGIC;
--     CE, LOAD, DIR: in STD_LOGIC;
--     DIN: in STD_LOGIC_VECTOR(3 downto 0);
--     COUNT: inout STD_LOGIC_VECTOR(3 downto 0);
cfg_state_gen:
   process(RES, SCLK, next_state)
	begin
      if (RES = '1') then
        state <= CFG_STOP;
	   elsif SCLK='1' and SCLK'event then
        state <= next_state;
		end if;   
   end process;


clk_prescaler: 
process (RES, CLK, CCLK_PRESCALER, LOAD_PRESCALER) 
begin
      if (RES = '1') then
        SCLK <= '1';
		  CLK_SCALE <= 1;
	     COUNT <= CLK_SCALE;

	 	elsif (LOAD_PRESCALER = '1') then
        CLK_SCALE <= CONV_INTEGER(CCLK_PRESCALER);

	   elsif CLK='1' and CLK'event then
		  if (COUNT = 1) then
		    SCLK <= not SCLK;

          COUNT <= CLK_SCALE;
		  else
          COUNT <= COUNT - 1;
		  end if;
      end if;
end process;

end Behavioral;
