Simulation stuck – no progress in simulation time?!

To understand why simulation might get stuck, let’s first understand what the simulator is doing at every time step. Whenever some signal changes value, the simulation time is stopped. While time still being stopped, simulator proceeds with “delta cycle” and changes the values of all other signals that are affected with this change. If any signal is affected, simulation proceeds with another “delta cycle” checking for all other signals that are affected and the process continues in this manner, until all signal values get static. At this point, the simulation time is advanced.

So, based on the above description, we can say that the root-cause for a simulation to get stuck is that there are infinitely many “delta cycles” being inserted. There are two reasons (at least that I know) why it might be happening: i) Combinational Loops, or ii) HDL Coding style. Lets go over both in detail.

Combinational Loops

A combinational loop in the design may cause continuous updates of signal values (think ring oscillator), and hence the simulator keeps inserting delta cycles.

Note that running simulations is not the way to detect design combinational loops, because not all combinational loops ocillate! (think about the back to back inverter configuration e.g. an SRAM cell). So, it is possible to simulate designs containing combinational loops if all nodes eventually reach stable values on presentation of new inputs.

HDL coding style

This one is more annoying as it is not caused by a design issue, but rather by how the code is written and what simulator is being used. Here is an example:

// Design
module stuck (
  input  logic     in,
  output logic     out
);
  
  logic net_a, net_b;
  
  always_comb begin
    net_a   = 1'b0; // default assignment
    net_a   = in;
    out = net_b;
  end
  
  always_comb begin
    net_b = 1'b0; // default assignment
    net_b = net_a; 
  end
  
endmodule

There is no combinational loop here, and of course it is just a simple buffer which we could have done with a direct assignment of out = in. However, that’s not the point. What is shown above is a very common coding style: in an always block we start out with a default assignment and then follow that with subsequent assignments to same variables. Think of the next-state logic for example, where a frequently used coding style is to start out by assigning current state at the top of always block and then make subsequent assignments depending on inputs.

With certain simulators (at least I tried with Cadence Xcelium 19), the simulation will hang up. Lets say, the current state of variables is in=net_a=net_b=out=1’b0. When in transitions to 1’b1, f0llowing sequence of events will happen:

  1. in is within the sensitivity list of top always_comb and so this block will get triggered.
  2. net_a is assigned the default value 1’b0
  3. net_a is assigned new value of in i.e. 1’b1
  4. out is assigned the current value of net_b i.e. 1’b0
  5. Because of events 2-3, there was an update to net_a, and as net_a is in the sensitivity list of bottom always_comb, this block gets triggered
  6. net_b is assigned the default value 1’b0
  7. net_b is assigned the new value of net_a i.e. 1’b1
  8. Because of events 6-7, there was an update to net_a, and as net_a is in the sensitivity list of bottom always_comb, this block gets triggered
  9. net_a is assigned the default value 1’b0
  10. net_a is assigned value of in i.e. 1’b1
  11. out is assigned the new value of net_b i.e. 1’b1
  12. Because net_a glitched from 1’b1->1b’0->1’b1 during events 9-10, this glitch will trigger the bottom always_comb block
  13. net_b is assigned the default value 1’b0
  14. net_b is assigned the new value of net_a i.e. 1’b1
  15. Because net_b glitched from 1’b1->1b’0->1’b1 during events 13-14, this glitch will trigger the top always_comb block
  16. The back-and-forth triggering of the two always_comb blocks continues infinitely, and so we get a hang up in simulation at the time in had transtioned to 1’b1.

Best take-away recommendation from here is to avoid combinational blocks to feed back to each other, even if the signals involved are unrelated. Although there is nothing wrong with the design, it would be necessary to adjust the HDL code (maintaining logical-equivalence ) so that your simulator does not get stuck with infinite delta cycles. If the code cannot be reformatted, check if the simulator provides switches (e.g. -delay_trigger in case of Xcelium) which makes the blocks not sensitive to “zero width glitches”.

The -delay_trigger tells the simulator to wait until it’s evaluated the entire always block before deciding on whether or not there has been an event on any particular variable, and thus whether or not it should be added to the event queue. Setting this flag prevents the simulator from hanging in this case because variables which don’t change after a complete pass of the always block are not added to the event queue.

You can try out what we discussed at EDA Playground on this link: https://edaplayground.com/x/43ce. If you have the access, run Xcelium simulator and experiment using the -delay_trigger switch.

I want to leave you with a question: the sequence of events in the above analysis was initiated by in transitioning to 1’b1. What do you think if in was assigned 1’b0 at the start of simulation (check out the testbench in the playground linked), would the simulation still hang up? The default assignment is equal to value of in, surely there are no glitches?