Search code examples
vhdl

Can't finish compiling a process with while loop


I have the following process, which is supposed to right-shift its input (input_1) until it reaches 0 and then end the loop:

DFF_PROCESS: process (input_0, input_1, input_2, CLK)
  variable eX : integer;
  ...
begin
  eX := input_1;
  ...
  while (eX > 0) loop
    if ( CLK'event and CLK = '1' ) then
      ...
      eX := to_integer(shift_right(to_unsigned(eX,32), 1));
    end if;
  end loop;
  result <= rE;
end process;

But when I try to compile it, it does not generate any errors, but gets stuck at ~10% of the Analysis&Synthesis part. The compiler seems to be working (checked via ProcessExplorer), however even after more than an hour, there was no progress. Can anyone help?

I need this component to be synthesisable, so if my approach has a logical flaw, please point me in the right direction.


Solution

  • Synthesising a while loop is usually a bad idea, and a little thought will show why...

    While loops are not guaranteed to terminate; and the number of iterations cannot generally be known at compile (synthesis) time - therefore you are trying to generate a hardware structure whose size is unknown until runtime!

    That won't work...

    Typically you need to transform the While loop into a closely related For loop with locally static bounds (roughly : can be determined from this source file) - which is synthesisable.

    In your case you are converting the variable to a 32-bit unsigned and shifting it right 1 bit at a time : a suitable For loop would loop over the bits in a 32-bit unsigned.

    That doesn't cope with differing input values : however a conditional ( if ex = 0 then ) inside the for loop will do that (and obviously is synthesisable)

    Something like...

    DFF_PROCESS: process (CLK, input_1)
    variable eX : unsigned(31 downto 0);
        ...
    begin
        eX := to_unsigned(input_1);
        ...
        for i in ex'range loop  -- better way of saying 31 downto 0)
            if rising_edge(clk) then
                if ex > 0 then
                   ...   
                   eX := shift_right(eX, 1);
                end if;
            end if;
        end loop;
    end process;
    

    This has other problems : it's not a proper clocked process, and ex is continually overwritten by the input value so it won't do what you expect.

    There are two ways forward : one fast (one result per clock cycle) but generating lots of hardware, the other small (but it'll take 32 clock cycles worst case).

    The first unrolls the loop, generating 32 copies of the loop body, to generate a result in a single clock cycle :

    DFF_PROCESS: process (CLK)
    variable eX : unsigned(31 downto 0);
        ...
    begin
        if rising_edge(clk) then
            eX := to_unsigned(input_1, ex'length);
            ...
            for i in ex'range loop  
                if ex > 0 then  -- done
                   ...
                   eX := shift_right(eX, 1);
                end if;
            end loop;
            result <= ...
        end if;
    end process;
    

    The other is a state machine which can either accept a new input value, or process a single bit, but not both, in each clock cycle.

    DFF_PROCESS: process (CLK)
       variable eX : unsigned(31 downto 0);
       type State_type is (Read, Process_Data);
       variable State : State_Type;
    
    begin
       if rising_edge(clk) then
          if State = Read then
             eX    := to_unsigned(input_1, ex'length);
             State := Process_Data;
          else
             if ex = 0 then
                result <= ...
                State := Read;
             else
                ...
                eX := shift_right(eX, 1);
             end if;
          end if;
       end if;
    end process;
    

    (You can add states to make it wait before Reading a new value if you wish...)