----------------------------------------------------------------------------------------------------
-- Component   : squareRoot_pipe
-- Author      : pwkolas
----------------------------------------------------------------------------------------------------
-- File        : squareRoot_pipe.vhd
-- Mod. Date   : XX.XX.XXXX
-- Version     : 1.00
----------------------------------------------------------------------------------------------------
-- Description : Square root calculator.
--               Based on
--               "A New Non-Restoring Square Root Algorithm and Its VLSI Implementations"
--
----------------------------------------------------------------------------------------------------
-- Modification History :
--
----------------------------------------------------------------------------------------------------
-- Comments :
--
----------------------------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity squareRoot_pipe is
   generic (
      G_DATA_W : integer := 32
      );
   port (
      clk     : in  std_logic;
      rst     : in  std_logic;
      iv_data : in  std_logic_vector(G_DATA_W-1 downto 0);
      ov_res  : out std_logic_vector((G_DATA_W/2)-1 downto 0)
      );
end entity squareRoot_pipe;

architecture squareRoot_pipe_rtl of squareRoot_pipe is

   constant C_ALU_W  : integer := ((G_DATA_W/2) + 2);
   constant C_PIPE_L : integer := G_DATA_W/2;
   constant C_OFFSET : integer := 3;    -- width of start vectors going  to ALU

   type t_arr_pipe_x_data is array (C_PIPE_L-1 downto 0) of unsigned(G_DATA_W-1 downto 0);
   signal a_data : t_arr_pipe_x_data;   -- (D)
   signal a_R    : t_arr_pipe_x_data;   -- (R)

   type t_arr_pipe_x_alu is array (C_PIPE_L-1 downto 0) of unsigned(C_ALU_W-1 downto 0);

   type t_arr_pipe_x_res is array (C_PIPE_L-1 downto 0) of unsigned(G_DATA_W/2-1 downto 0);
   signal a_Q : t_arr_pipe_x_res;       -- (ALU Q out)

   signal nextOp : std_logic_vector(C_PIPE_L-1 downto 0);

begin
   sqrt_p : process (clk, rst)
      variable va_AluInR : t_arr_pipe_x_alu;  -- (ALU R in)
      variable va_AluInQ : t_arr_pipe_x_alu;  -- (ALU Q in)
      variable va_AluOut : t_arr_pipe_x_alu;  -- (ALU Q out)
   begin
      if (rst = '1') then
         a_data    <= (others => (others => '0'));
         a_R       <= (others => (others => '0'));
         a_Q       <= (others => (others => '0'));
         va_AluInR := (others => (others => '0'));
         va_AluInQ := (others => (others => '0'));
         va_AluOut := (others => (others => '0'));
         nextOp    <= (others => '0');
      elsif rising_edge(clk) then
         -- stage 0 start conditions, ALU inputs
         va_AluInR(0)             := (others => '0');
         va_AluInR(0)(1 downto 0) := unsigned(iv_data(G_DATA_W-1 downto G_DATA_W-1-1));
         va_AluInQ(0)             := (others => '0');
         va_AluInQ(0)(0)          := '1';

         -- stage 0 calculations
         va_AluOut(0) := va_AluInR(0) - va_AluInQ(0);

         -- stage 0 result registers, ALU output
         a_data(0)                              <= shift_left(unsigned(iv_data), 2);
         a_R(0)                                 <= (others => '0');
         a_R(0)(G_DATA_W-1 downto G_DATA_W-1-1) <= va_AluOut(0)(1 downto 0);
         a_Q(0)                                 <= (others => '0');
         a_Q(0)(0)                              <= not va_AluOut(0)(2);
         nextOp(0)                              <= not va_AluOut(0)(2);

         -- next stages
         for i in 1 to C_PIPE_L-1 loop
            -- prepare inputs for next stage
            va_AluInR(i)                            := (others => '0');
            va_AluInR(i)(C_OFFSET+i-1 downto 2)     := a_R(i-1)(G_DATA_W-(i-1)-1 downto G_DATA_W-(2*i));
            va_AluInR(i)(2-1 downto 0)              := a_data(i-1)(G_DATA_W-1 downto G_DATA_W-1-1);
            va_AluInQ(i)                            := (others => '0');
            va_AluInQ(i)(C_OFFSET+(i-1)-1 downto 2) := a_Q(i-1)(i-1 downto 0);
            va_AluInQ(i)(1)                         := not a_Q(i-1)(0);
            va_AluInQ(i)(0)                         := '1';

            -- ALU ADD/SUB
            if (nextOp(i-1) = '1') then
               va_AluOut(i) := va_AluInR(i) - va_AluInQ(i);
            else
               va_AluOut(i) := va_AluInR(i) + va_AluInQ(i);
            end if;

            -- result registers
            a_data(i)                                    <= shift_left(unsigned(a_data(i-1)), 2);
            a_R(i)                                       <= (others => '0');
            a_R(i)(G_DATA_W-i-1 downto G_DATA_W-2*(i+1)) <= va_AluOut(i)(i+1 downto 0);
            a_Q(i)                                       <= shift_left(unsigned(a_Q(i-1)), 1);
            a_Q(i)(0)                                    <= not va_AluOut(i)(i+2);
            nextOp(i)                                    <= not va_AluOut(i)(i+2);

         end loop;
      end if;
   end process;

   ov_res <= std_logic_vector(a_Q(C_PIPE_L-1));

end architecture squareRoot_pipe_rtl;
