Xilinx iterate error: Loop has iterated 64 times. Use "set -loop_iteration_limit XX" to iterate more

77 Views Asked by At

I want to describe a binary to bcd converter with the follow code:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity MAIN is
    Port (
        BIN : in STD_LOGIC_VECTOR(7 downto 0);
        BCDu : out STD_LOGIC_VECTOR(3 downto 0);
        BCDd : out STD_LOGIC_VECTOR(3 downto 0);
        BCDc : out STD_LOGIC_VECTOR(3 downto 0)
    );
end entity MAIN;

architecture Behavioral of MAIN is
begin
    process(BIN)
        variable aux : natural := to_integer(unsigned(BIN));
        variable uni : natural := 0;
        variable dec : natural := 0;
        variable cent : natural := 0;
        
    begin
        if(aux < 256) then
            while(aux > 99) loop
                aux := aux - 100;
                cent := cent + 1;
            end loop;

            while(aux > 9) loop
                aux := aux - 10;
                dec := dec + 1;
            end loop;
        end if;

        uni := aux;

        BCDu <= std_logic_vector(to_unsigned(uni, 4));
        BCDd <= std_logic_vector(to_unsigned(dec, 4));
        BCDc <= std_logic_vector(to_unsigned(cent, 4));
    end process; 
end architecture Behavioral;

But the follow error appears:

Loop has iterated 64 times. Use "set -loop_iteration_limit XX" to iterate more.

I don't know why that error appears, I have two whiles and together it has 7 iterations.

2

There are 2 best solutions below

1
On

VHDL is a hardware description language, not a procedural language. While in simulation the steps of the loop are executed one by one, on hardware the loop is unrolled such that the whole entire thing is computed in parallel. In your case, the loops probably have to have gates dedicated for all possible values of x from 0 to 255, since the synthesis engine isn’t smart enough to realize that the loops only run up to 7 times. This could likely be solved by precomputing the loop iterations in another variable. A similar solution would be to use division and modulus to eliminate the loops entirely.

However, the best solution would be to use 7 chained single-step BCD conversion modules implemented without a process to minimize the number of gates used. In each of those modules, if the 4 bit input is at least 5, then 3 is added to the output, and it’s output is shifted by one bit such that it introduces one new bit of the original input before the next stage. So the first module operates on 3 bits to produce 4, and you only use a module when there are at least three bits past the previous module. 5 and 3 are the special numbers in this because after the next shift, 5 becomes 10 and 3 becomes the 6 extra needed to make 10 equal to 16, the base of 4-bit numbers (hexadecimal). Adding 3 carries the extra 10 over to the next digit, since a binary 16 becomes a 1 in the next digit.

0
On

See 18429 - XST - "ERROR:Xst:1312 - Loop has iterated 64 times. Use "set -loop_iteration_limit XX" to iterate more.". Here the Xilinx Synthesis Technology synthesis tool is complaining about one or both of the while loops using aux to determine when to exit the loop. Synthesis unrolls loops and without aux having a constraint (that is different for each loop) the range of aux is that of subtype NATURAL at worst and the numerical binary value of BIN at best (256 values and still larger than 64).

Originally while loops were not supported for VHDL synthesis at all, now days some synthesis tools attempt to extract a finite number of repetitions from unrolled loops using the values evaluated from the loops sequence of statements. Here we see it isn't very smart and support for while loops introduces a portability issue between synthesis tools, lacking standardization.

We can switch to the use of for loops based on knowledge about the value range derived from BIN for each of the loop statements:

architecture behave of main is

begin
    process (bin)
        variable aux:   natural; -- CHANGED WAS := to_integer(unsigned(bin));
        variable uni:   natural := 0;  -- initial values are assigned at
        variable dec:   natural := 0;  -- elaboration, which occurs once
        variable cent:  natural := 0;  -- for a process statement
    begin
        aux := to_integer(unsigned(bin));  -- CHANGED for every event on bin
        cent := 0;                         --      ditto
        dec := 0;                          --      ditto
        -- if aux < 256 then  -- CHANGED this is never FALSE binary bin 0 to 255
            -- while aux > 99 loop  -- CHANGED you could constraint aux
            --     aux := aux - 100;
            --     cent := cent + 1;
            --end loop;
            for i in 0 to 1 loop -- CHANGED or use a for loop.
                if aux > 99 then -- While loop not supported universally
                    aux := aux - 100;
                    cent := cent + 1;
                end if;
            end loop;
            -- while aux > 9 loop -- CHANGED how would you re-constrain aux?
            --     aux := aux - 10;
            --     dec := dec + 1;
            -- end loop;
            for i in 0 to 8 loop  -- CHANGED for loop
                if aux > 9 then
                    aux := aux - 10;
                    dec := dec + 1;
                end if;
            end loop;
        -- end if;

        uni := aux;

        bcdu <= std_logic_vector(to_unsigned(uni, 4));
        bcdd <= std_logic_vector(to_unsigned(dec, 4));
        bcdc <= std_logic_vector(to_unsigned(cent, 4));
    end process;

end architecture behave;

This actually implies the worst case number of loop iterations of the two loop statements collective is for the decimal value 199 as a binary value of BIN.

That would represent one iteration for the first loop subtracting 100 from aux and nine iterations of the second loop subtracting 10, or ten interations in total.

There are also some changes to support simulation. Process declarative items the variable objects of type NATURAL aux, cent and dec are only elaborated (and initialized once. The resumption of simulation caused by an event on BIN would require these three variables be assigned a new.

These changes along with changing to for loops are shown with associated comments beginning with CHANGED (which shows up as bright green in my editor).

We can hijack the testbench from the question linked in a comment to ThatComputerGuy's answer:

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

entity main_tb is
end entity;

architecture foo of main_tb is
    signal bin:     std_logic_vector (7 downto 0) := (others => '0');
    -- (initialized to prevent those annoying metavalue warnings)

    signal bcdu:    std_logic_vector(3 downto 0);
    signal bcdd:    std_logic_vector(3 downto 0);
    signal bcdc:    std_logic_vector(3 downto 0); 


begin

DUT:
    entity work.main (behavioral)
        port map (
            bin => bin,
            bcdu => bcdu,
            bcdd => bcdd,
            bcdc => bcdc
        );

STIMULUS:
    process

    begin
        for i in 0 to 255 loop
            bin <= std_logic_vector(to_unsigned(i,8));
            wait for 30 ns;
        end loop;
        wait for 30 ns;
        wait;
    end process;
end architecture;

And that shows us the 8 bit binary to 3 digit BCD conversion works in simulation:

main_tb.jpg

and should work in synthesis when using for loops instead. Here the maximum numerical value BIN can convey is 255 in decimal of x"FF" in hex.

This simulation was performed with ghdl and displayed with gtkwave.

Synthesis with successive subtraction represents more synthesis optimization effort to eventually end up with the same result as the method mentioned by ThatComputerGuy (add3, which is an optimization of Double Dabble).