VHDL cominational loop in FSM, zero delay oscillation

Viewed 37

I have a two process FSM in vhdl. The error the simulator return is

FATAL_ERROR: Iteration limit 10000 is reached. Possible zero delay oscillation detected where simulation time can not advance. Please check your source code. Note that the iteration limit can be changed using switch -maxdeltaid.

Form what I understand there is a combinational loop in the FIRE_PROCESS_COMBINATIONAL process but I am unable to spot it. I also know this combinational loop is in the FIRE_ROLL state but can't figure it out. any help would be appreciated. Thanks.

FIRE_PROCESS_COMBINATIONAL : process(fire_pr_state, wfg_pps_i, do_fire_s, rolling_s, sel_9914_s)
    variable count_9914 : integer range 0 to NUM_9914-1 := NUM_9914-1;
  begin
    timer_fire_s <= 1;
    sel_9914_s <= (others=>'1');
    rolling_s <= '0';
    case fire_pr_state is
      when FIRE_RST =>
        timer_fire_s <= fire_rst_delay;
        fire_nx_state <= FIRE_WAIT;
      when FIRE_WAIT =>
        --timer_fire_s <= fire_roll_delay;
        fire_nx_state <= FIRE_WAIT; 
        if rising_edge(wfg_pps_i) then
          if (do_fire_s = '1') then
            if (rolling_s = '0') then
              fire_nx_state <= FIRE_ROLL;
            end if;
          end if;
        end if;
      when FIRE_ROLL =>
        if (sel_9914_s(sel_9914_s'high)='0') then
          fire_nx_state <= FIRE_WAIT;
          sel_9914_s <= (others=>'1');
        else
          --timer_fire_s <= fire_roll_delay;
          fire_nx_state <= FIRE_ROLL;
          rolling_s <= '1';
          if ((and sel_9914_s = '1')) then
            sel_9914_s <= (sel_9914_s'low=>'0', others=>'1');
          else
            sel_9914_s <= sel_9914_s rol 1;
          end if;
        end if;
      when others =>
        fire_nx_state <= FIRE_WAIT;
    end case;
    end process FIRE_PROCESS_COMBINATIONAL;

  FIRE_PROCESS_SEQUENTIAL : process(sys_clk_i, sys_rst_i)
    variable count_fire1_v : delay_type := 0;
  begin
    if (sys_rst_i = '1') then
      fire_pr_state <= FIRE_RST;
    elsif rising_edge(sys_clk_i) then
      count_fire1_v := count_fire1_v + 1;
      if (count_fire1_v >= timer_fire_s) then
        fire_pr_state <= fire_nx_state;
        count_fire1_v := 0;
      end if;
    end if;
  end process FIRE_PROCESS_SEQUENTIAL;
1 Answers

I tried to draw the combinational logic inside the FIRE_ROLL state. I focused on what happens on the MSB of the sel_9914_s signal, since it is the one driving the muxes (if-elses). The code should synthetize to something like this:

enter image description here

The first if/else uses the MSB to assign to the entire vector (hence the MSB as well) a 1 or..

The second if/else uses the entire sel_9914_s, AND-reduced, to decide to assign to the signal itself a value which can be either a 1 or a (1 or 0) depending on the previous bit in the rotation.

I have then drawn a tree of the possible values that B could have. If there are cycles in the tree that let B oscillates, then we have discovered the issue you are seeing in simulation.

enter image description here

Let's assume that B, which is sel_9914_s, starts as any number, which contains 1s and 0s, but the MSB is 0. Then we follow the orange path, we set it to all 1s (others=>'1') , hence we now enter the else, and we need to evaluate the AND. A evaluates as 1, hence we set the LSB as 0 and all the rest at 1 (sel_9914_s'low=>'0', others=>'1').

This brings us back to the beginning, following the blue path. This time A is not all 1s, so A evaluates as false, now we are rolling a vector of the type 111..0, meaning we keep getting ones and repeating the blue path, until we get a 0.... which let us repeating the orange path. This loop indefinitely.

enter image description here

Related