Search code examples
vhdlsquare-root

My VDHL code runs incorrectly - square root in vhdl


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

entity RaccinCarreSequentielle is

generic(    
    N: natural:= 16
    );
port(
    X: unsigned(2*N-1 downto 0);
    reset : in std_logic; --clear signal
    clk : in std_logic;
    state_done :  in std_logic; --start
    result_done : out std_logic;
    result :      out unsigned(2*N-1 downto 0)
    );
end entity;

architecture state_machine_raccincarre_arc of RaccinCarreSequentielle is
    type state is (s0_wait,s1_init,s2_calcul,s3_fini);
    signal pre_state,next_state: state;
begin
    state_register:process(reset,clk)
    begin
        if reset = '1' then
            pre_state <= s0_wait;
        elsif rising_edge(clk) then
            pre_state <= next_state;
        end if;
    end process state_register;

state_machine: process(pre_state,state_done)
    variable rx,rz,rv : unsigned(2*N-1 downto 0);
    variable ri: unsigned(N-1 downto 0);
begin
    case pre_state is
        when s0_wait =>
            if state_done = '1' then
                next_state <= s1_init;
            else 
                next_state <= s0_wait;
            end if;
        when s1_init => 
            next_state <= s2_calcul;
            rx := x;
            rz := (others => '0');
            rv := (2*N-2 => '1', others => '0');
            ri := to_unsigned(N-1,N);
        when s2_calcul =>
            if ri > 0 then
                    ri := ri - 1;
                    rz := rz + rv;
                    if rx > rz then
                         rx := rx - rz;
                         rz := rz + rv;
                    else 
                         rz := rz - rv;
                    end if;
                    rz := '0' & rz(2*N-1 downto 1);
                    rv := "00" & rv(2*N-1 downto 2);
                    next_state <= s2_calcul;
                 else
                    next_state <= s3_fini;
            end if;
        when s3_fini =>
            result <= rz;           
            if state_done = '0' then
                next_state <= s0_wait;
            else 
                next_state <= s3_fini;
            end if;
        when others =>
            null;
    end case;
end process state_machine;
                
result_proc: process(pre_state)
begin
    if pre_state = s3_fini then
        result_done <= '1';
    else 
        result_done <= '0';
    end if;
end process result_proc;
end architecture; 

When i use the for loop inside state2, my code will run correctly and my result is good. For example when i want to find the square root of 255, i will have 15. But when I dont want to use For loop in state2_calcul as you see. So i did a if statement to reduce the variable RI each time i go to state2 like below. I did a simulation but the state always stops at state2, it can not go through like my link.

https://ibb.co/6HVWNHC

when s2_calcul =>
    if ri > 0 then
            ri := ri - 1;
            rz := rz + rv;
            if rx > rz then
                 rx := rx - rz;
                 rz := rz + rv;
            else 
                 rz := rz - rv;
            end if;
            rz := '0' & rz(2*N-1 downto 1);
            rv := "00" & rv(2*N-1 downto 2);
            next_state <= s2_calcul;
         else
            next_state <= s3_fini;
    end if;

I think that problem is the nested IF, but when i change that, nothing change. Could someone helps me explain that problem and how can i solve it. thanks


Solution

  • You cannot memorize anything in a combinatorial part of your design. So you need registers for ri, rx, rv and rz. These registers are somehow part of your global state which is indeed a combination of them, and of the pre_state register.

    Let's continue with the style you already use: one synchronous process for the registers, another for the combinatorial part, and a next_xxx signal for the input of each xxx register.

    architecture state_machine_raccincarre_arc of RaccinCarreSequentielle is
        type state is (s0_wait, s1_init, s2_calcul, s3_fini);
        signal pre_state, next_state: state;
        signal rx, rz, rv, next_rx, next_rz, next_rv: unsigned(2*N-1 downto 0);
        signal ri, next_ri: unsigned(N-1 downto 0);
    begin
        state_register:process(reset,clk)
        begin
            if reset = '1' then
                pre_state <= s0_wait;
                rx <= (others => '0');
                rz <= (others => '0');
                ri <= (others => '0');
                rv <= (others => '0');
            elsif rising_edge(clk) then
                pre_state <= next_state;
                rx <= next_rx;
                rz <= next_rz;
                ri <= next_ri;
                rv <= next_rv;
            end if;
        end process state_register;
    
        state_machine: process(pre_state, state_done, x, rx, rz, rv, ri)
            variable tmpz : unsigned(2*N-1 downto 0);
        begin
            next_state <= pre_state;
            next_rx    <= rx;
            next_rz    <= rz;
            next_rv    <= rv;
            next_ri    <= ri;
            tmpz       := (others => '0');
            case pre_state is
                when s0_wait =>
                    if state_done = '1' then
                        next_state <= s1_init;
                    end if;
                when s1_init =>
                    next_state <= s2_calcul;
                    next_rx <= x;
                    next_rz <= (others => '0');
                    next_rv <= (others => '0');
                    next_rv(2*N-2) <= '1';
                    next_ri <= to_unsigned(N-1,N);
                when s2_calcul =>
                    if ri > 0 then
                        next_ri <= ri - 1;
                        tmpz := rz + rv;
                        if rx > tmpz then
                            next_rx <= rx - tmpz;
                            tmpz := tmpz + rv;
                        else
                            tmpz := tmpz - rv;
                        end if;
                        next_rz <= '0' & tmpz(2*N-1 downto 1);
                        next_rv <= "00" & rv(2*N-1 downto 2);
                    else
                        next_state <= s3_fini;
                    end if;
                when s3_fini =>
                    if state_done = '0' then
                        next_state <= s0_wait;
                    end if;
            end case;
        end process state_machine;
    
        result <= rz;
        result_done <= '1' when pre_state = s3_fini else '0';
    end architecture;
    

    See? The sensitivity list of the combinatorial process contains all signals that the process reads, the signals it assigns are always assigned (thanks to the default assignments at the very beginning), and the only variable is always assigned before it is used (thanks also to the default assignment at the very beginning).

    Note that, thanks to the default assignments at the very beginning, there is no risk to get latches at synthesis. By default these:

    next_xxx <= xxx;
    

    assignments say that by default the xxx register shall not change. An interesting side effect is that you do not need any more some of your else statements. You can replace:

            when s0_wait =>
                if state_done = '1' then
                    next_state <= s1_init;
                else 
                    next_state <= s0_wait;
                end if;
    

    by:

            when s0_wait =>
                if state_done = '1' then
                    next_state <= s1_init;
                end if;
    

    because the else clause is already what happens by default.

    Of course, it could be that it does not work exactly as you would like because the structure is totally different. But at least this should be a good starting point for your debugging. Just remember the main principles of combinatorial processes for synthesis:

    1. the sensitivity list must contain all signals that the process reads (the all keyword of the VHDL 2008 standard is helpful, if your tools support it),
    2. the signals it assigns (its outputs) must be assigned each time the process resumes (default assignments at the very beginning can be handy for beginners),
    3. the variables must always be assigned before they are used (default assignments at the very beginning can be handy for beginners).

    Be careful, these 3 golden rules are not that easy to check, especially in complicated nested if and case statements. This is why I always suggest to beginners to use the trick of default assignments at the very beginning. Rules #2 and #3 become trivial. For rule #1, if your tools support the all keyword of the VHDL 2008 standard, use it for all combinatorial processes (not for synchronous processes):

    state_machine: process(all)