VHDL coding tips and tricks: Synthesizable Clocked Square Root Calculator In VHDL

## Tuesday, December 8, 2020

### Synthesizable Clocked Square Root Calculator In VHDL

Long back I had shared VHDL function for finding the square root of a number. This function too was synthesisable, but as it was a function, it was purely combinatorial. If you want to find the square root of a relatively larger number, then the resource usage was very high.

In such cases, it makes sense to use a clocked design. Such a clocked design enables us to reuse one set of resources over and over. The advantage of such a design is that it uses far less resources while the disadvantage being the low speed.

For example, in the design I have shared in this post, to find the square root of a N-bit number, you need to wait N/2 clock cycles.

The code is written based on Figure (8) from this paper: A New Non-Restoring Square Root Algorithm and Its VLSI Implementations.

The codes are well commented, so I wont write much about how it works here. Please refer to the block diagram from the paper in case you have some doubts.

Let me share the codes now:

### square_root.vhd:

--Synthesisable Design for Finding Square root of a number.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity square_root is
generic(N : integer := 32);
port (
Clk : in std_logic;     --Clock
rst : in std_logic;     --Asynchronous active high reset.
input : in unsigned(N-1 downto 0);  --this is the number for which we want to find square root.
done : out std_logic;   --This signal goes high when output is ready
sq_root : out unsigned(N/2-1 downto 0)  --square root of 'input'
);
end square_root;

architecture Behav of square_root is

begin

SQROOT_PROC : process(Clk,rst)
variable a : unsigned(N-1 downto 0);  --original input.
variable left,right,r : unsigned(N/2+1 downto 0):=(others => '0');  --input to adder/sub.r-remainder.
variable q : unsigned(N/2-1 downto 0) := (others => '0');  --result.
variable i : integer := 0;  --index of the loop.
begin
if(rst = '1'then  --reset the variables.
done <= '0';
sq_root <= (others => '0');
i := 0;
a := (others => '0');
left := (others => '0');
right := (others => '0');
r := (others => '0');
q := (others => '0');
elsif(rising_edge(Clk)) then
--Before we start the first clock cycle get the 'input' to the variable 'a'.
if(i = 0then
a := input;
done <= '0';    --reset 'done' signal.
i := i+1;   --increment the loop index.
elsif(i < N/2then --keep incrementing the loop index.
i := i+1;
end if;
--These statements below are derived from the block diagram.
right := q & r(N/2+1) & '1';
left := r(N/2-1 downto 0) & a(N-1 downto N-2);
a := a(N-3 downto 0) & "00";  --shifting left by 2 bit.
if ( r(N/2+1) = '1'then   --add or subtract as per this bit.
r := left + right;
else
r := left - right;
end if;
q := q(N/2-2 downto 0) & (not r(N/2+1));
if(i = N/2then    --This means the max value of loop index has reached.
done <= '1';    --make 'done' high because output is ready.
i := 0--reset loop index for beginning the next cycle.
sq_root <= q;   --assign 'q' to the output port.
--reset other signals for using in the next cycle.
left := (others => '0');
right := (others => '0');
r := (others => '0');
q := (others => '0');
end if;
end if;
end process;

end architecture;

### Testbench: tb.vhd

--Testbench for out square root calculator design.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

--empty entity as its a testbench
entity tb is
end tb;

architecture sim of tb is

--Declare the component which we want to test.
component square_root is
generic(N : integer := 32);
port (
Clk : in std_logic;
rst : in std_logic;
input : in unsigned(N-1 downto 0);
done : out std_logic;
sq_root : out unsigned(N/2-1 downto 0)
);
end component;

constant clk_period : time := 10 ns;    --set the clock period for simulation.
constant N : integer := 16;    --width of the input.
signal Clk,rst,done : std_logic := '0';
signal input : unsigned(N-1 downto 0) := (others => '0');
signal sq_root : unsigned(N/2-1 downto 0) := (others => '0');
signal error : integer := 0;    --this indicates the number of errors encountered during simulation.

begin

Clk <= not Clk after clk_period / 2;    --generate clock by toggling 'Clk'.

--entity instantiation.
DUT : entity work.square_root generic map(N => N)
port map(Clk,rst,input,done,sq_root);

--Apply the inputs to the design and check if the results are correct.
--The number of inputs for which the results were wrongly calculated are counted by 'error'.
SEQUENCER_PROC : process
variable actual_result,i : integer := 0;
begin
--First we apply reset input for one clock period.
rst <= '1';
wait for clk_period;
rst <= '0';
--Test the design for all the combination of inputs.
--Since we have (2^16)-1 inputs, we test all of them one by one.
while(i <= 2**N-1loop
input <= to_unsigned(i,N);  --convert 'i' from integer to unsigned format.
wait until done='1';    --wait until the 'done' output signal goes high.
wait until falling_edge(Clk);   --we sample the output at the falling edge of the clock.
actual_result := integer(floor(sqrt(real(i)))); --Calculate the actual result.
--if actual result and calculated result are different increment 'error' by 1.
if (actual_result /= to_integer(sq_root)) then
error <= error + 1;
end if
i := i+1;   --increment the loop index.
end loop;
reset <= '1';   --all inputs are tested. Apply reset
input <= (others => '0');   --reset the 'input'
wait;
end process;

end architecture;

### Simulation Waveform from ModelSim:

To reach the end of the testbench, you need to simulate only for 5.5 msec of simulation time.