Search code examples
for-loopvhdlquartus

How to determine if all for loops have ended, VHDL, Quartus-II


I'm struggling with a VHDL conundrum. Here's some code which should explain what I'm trying to do:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use work.all;

entity forLoopTest is
-- Number of bits known only at compilation
generic(
    bits : integer  range 1 to 1024;                := 256;
);
port(
    clk:    in  std_logic                           := '0';
    -- Single bit inputs from foo
    foo:    in  std_logic_vector(bits-1 downto 0)   := (others => '0');
    -- Output should be high when all inputs have gone to '1' at some point
    bar:    out std_logic
);
end forLoopTest;

------------------------------------------------------------------------------------------------------------

architecture implementation of forLoopTest is
    -- Create states for finite state machine, where finish implies a '1' has been received
    type FSM_states_single is (waitForHigh, finish);
    -- Make an array of the states, one for each input bit
    type FSM_states_multi is array (bits-1 downto 0) of FSM_states_single;
    -- Create signal of states initialised to the waiting condition
    signal state : FSM_states_multi                 := (others => waitForHigh);

begin

    process(clk, foo)
        -- For each input bit:
        for bitNumber in 0 to bits-1 loop
            case state(bitNumber) is
                -- Whilst waiting, poll the input bit
                when waitForHigh =>
                    -- If it goes high, then switch states
                    if (foo(bitNumber) = '1') then
                        state(bitNumber) <= finish;
                    end if;
                -- If input bit has gone high:
                when finish =>
                    -- What is simplest method of setting "bar"?
                        -- "bar" should be high if and only if all bits have equalled '1' at some point
                        -- Otherwise it should be '0'
                        -- Though of dominant setting '0', and submissive setting 'H', but multiple things setting output fails
                            -- Either explicitly, or only one of them is used, others ignored
            end case;
        end loop;

    end process;        

end implementation;

Basically, I am trying to find an optimal method of deducing when all "threads" of the for loop have completed. The above is a hypothetical example to illustrate the point. One method using the above code would be to simply "AND" all of the states. However, I'm not sure how to and an unknown number of variables (pre-compilation). Also I am curious to know what other methods of solving this problem are.

Thanks in advance!


Solution

  • Added the clock and a reset to your process. The reset allows you to clear state.

    No bar flip flop, it'd be easy to do, move an if statement.

    The case statement was removed because of how bar is derived, evaluating both states isn't necessary:

    library ieee;
    use ieee.std_logic_1164.all;
    -- use ieee.std_logic_arith.all;  -- not used
    use work.all;
    
    entity forlooptest is
        generic (
            bits : integer  range 1 to 1024 := 256 -- removed ';' 2 places
        );
        port (
            clk:    in  std_logic;
            reset:  in  std_logic;  -- added
            foo:    in  std_logic_vector(bits-1 downto 0) := (others => '0');
            bar:    out std_logic
        );
    end entity forlooptest;
    
    architecture foo of forlooptest is
    
        type FSM_states_single is (waitForHigh, finish);
        type FSM_states_multi is array (bits-1 downto 0) of FSM_states_single;
        signal state : FSM_states_multi := (others => waitForHigh);
    
    begin
    
    FOO_BAR:
        process (clk, reset)
            variable state_v:   FSM_states_multi;  -- added
        begin -- original missing begin
    
            state_v := state;  -- variable can be evaluated after assignment
            if reset = '1' then
                state_v := (others => WaitForHigh);
            elsif rising_edge(clk) then 
                for bitNumber in 0 to bits-1 loop 
                    if state_v(bitNumber) = waitForHigh and 
                                    foo(BitNumber) = '1'     then
                        state_v(bitNumber) := finish;
                    end if;
                end loop;  
                state <= state_v;  
            end if;
            if state_v = (state_v'range => finish)  then 
                bar <= '1';  -- bar not a FlipFlop move if statement above
            else             -- preceding end if and add to reset condition for FF
                bar <= '0';  -- no latch
            end if;
        end process;    
    
    end architecture;
    

    Making bar a flip flop can be done by moving it's if statement above the preceding end if, removing the else and assignment to '0', and adding bar <= '0' to the reset.

    There's also a variable copy of state so any updated bits are available immediately for evaluation. (A signal assignment doesn't take effect immediately while a variable assignment does).

    Note the heart of the matter, how to evaluate state (state_v) using an aggregate value with every position set to finish. You can't use an others here. The number of elements and their type has to be discernable from the aggregate expression as an input to the equality operator.

    Adding a small testbench with a limited range of bits:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity for_loop_test_tb is
    end entity;
    
    architecture fum of for_loop_test_tb is
        constant bits:  integer range 1 to 1024 := 16; 
        signal clk:    std_logic := '0';
        signal reset:  std_logic;  -- added
        signal foo:    std_logic_vector(bits-1 downto 0) := (others => '0');
        signal bar:    std_logic;
    begin
    DUT:
        entity work.forlooptest
            generic map (bits => bits)
            port map (
                clk => clk,
                reset => reset,
                foo => foo,
                bar => bar
            );
    CLOCK:
        process
        begin
            wait for 5 ns;
            clk <= not clk;
            if now > 150 ns then
                wait;
            end if;
        end process;
    
    STIMULI:
        process
        begin
            wait for 10 ns;
            reset <= '1';
            wait for 10 ns;
            reset <= '0';
            wait for 10 ns;
            foo <= x"0FF0";
            wait for 10 ns;
            foo <= x"0001";
            wait for 10 ns;
            foo <= x"F002";
            wait for 10 ns;
            foo <= x"0F00";
            wait for 10 ns;
            foo <= x"FF00";
            wait for 10 ns;
            foo <= x"0001";
            wait for 10 ns;
            foo <= x"00F0";
            wait for 10 ns;
            foo <= x"F0F0";
            wait for 10 ns;
            foo <= x"0004";
            wait for 10 ns;
            foo <= x"CCCC";
            wait;
        end process;
    
    end architecture;
    

    And that gives:

    for_loop_test_tb.png

    (The value for bits and the number of different input values for foo were restricted to provide a waveform easily interpreted.)