Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which region are continuous assignments and primitive instantiations with #0 scheduled

All #0 related code examples I have found are related to procedural code (IE code inside begin-end). What about continuous assignments and primitive instantiations? The IEEE 1364 & IEEE 1800 (Verilog & SystemVerilog respectively) only give a one line description that I can find (Quoting all version of IEEE 1364 under the section name "The stratified event queue"):

An explicit zero delay (#0) requires that the process be suspended and added as an inactive event for the current time so that the process is resumed in the next simulation cycle in the current time.

I read documents and talked with a few engineers that have been working with Verilog long before the IEEE Std 1364-1995. In summary, the inactive region was failed solution to synchronizing flip-flops with Verilog's indeterminate processing order. Later Verilog created non-blocking assignments (<=) and resolved the synchronizing with indeterminate order. The inactive region was left in the scheduler to not break legacy code and a few obscure corner cases. Modern guidelines say to avoid the using #0 because it creates race conditions and may hinder simulation performance. The performance impact is a don't care for small designs. I run huge designs that with mixed RTL to transistor level modules. So even small performance gains add up and not having to debug a rouge race conditions are time savers.

I've ran test case removing/adding #0 to Verilog primitives on large scale designs. Some simulators have notable changes others do not. It is difficult to tell who is doing a better job following the LRM or has a smarter optimizer.

Adding a per-compile script to remove hard coded forms of #0, is easy enough. The challenge is with parameterized delay. Do I really need to create generate blocks for to avoid the inactive region? Feels like it could introduce more problems than solve:

generate
  if ( RISE > 0 || FALL > 0)
    tranif1 #(RISE,FALL) ipassgate ( D, S, G );
  else
    tranif1              ipassgate ( D, S, G );
  if ( RISE > 0 || FALL > 0 || DECAY > 0)
    cmos #(RISE,FALL,DECAY) i1 ( out, in, NG, PG );
  else
    cmos                    i1 ( out, in, NG, PG );
  if (DELAY > 0)
    assign #(DELAY) io = drive ? data : 'z;
  else
    assign          io = drive ? data : 'z;
endgenerate

Verilog primitives and continuous assignments have been with Verilog since the beginning. I believe parameterized delay has been around longer then the inactive region. I haven't found any documentation on recommendation or explanation for these conditions. My local network of Verilog/SystemVerilog gurus are all unsure which region it should run in. Is there a detail we are all overlooking or is it a gray area in the language? If it is a gray area, how do I determine which way it is implanted?

An accepted answer should include a citation to any version of IEEE1364 or IEEE1800. Or at least a way to do proof of concept testing.

like image 951
Greg Avatar asked Oct 23 '25 17:10

Greg


1 Answers

This is an easy one. Section 28.16 Gate and net delays of the 1800-2012 LRM as well as section 7.14 Gate and net delays of the 1364-2005 LRM both say

For both gates and nets, the default delay shall be zero when no delay specification is given. So that means

gateName instanceName (pins);

is equivalent to writing

gateName #0 instanceName (pins);

I'm not sure where the text you quoted came from, but section 4.4.2.3 Inactive events region of the 1800-2012 LRM says

If events are being executed in the active region set, an explicit #0 delay control requires the process to be suspended and an event to be scheduled into the Inactive region of the current time slot so that the process can be resumed in the next Inactive to Active iteration.

The key text is delay control, which is a procedural construct. So #0 as an inactive event only applies to procedural statements.

The problem with procedural #0's is that they move race conditions, they don't eliminate them. Sometimes you have to add multiple serial #0's to move away from a races condition, but you don't always know how many because another piece of code is also adding #0's. Just look at the UVM code; it's littered with messy #0's because they did not take the time to code things properly.

like image 82
dave_59 Avatar answered Oct 26 '25 16:10

dave_59