• Top
    • Documentation
    • Books
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
    • ACL2
    • Interfacing-tools
    • Hardware-verification
      • Gl
      • Esim
      • Vl2014
        • Warnings
        • Primitives
        • Use-set
        • Syntax
        • Getting-started
        • Utilities
        • Loader
        • Transforms
          • Expression-sizing
          • Occform
          • Oprewrite
          • Expand-functions
          • Delayredux
          • Unparameterization
          • Caseelim
          • Split
          • Selresolve
          • Weirdint-elim
          • Vl-delta
          • Replicate-insts
          • Rangeresolve
          • Propagate
          • Clean-selects
          • Clean-params
          • Blankargs
          • Inline-mods
          • Expr-simp
          • Trunc
          • Always-top
          • Gatesplit
          • Gate-elim
          • Expression-optimization
          • Elim-supplies
          • Wildelim
          • Drop-blankports
          • Clean-warnings
          • Addinstnames
          • Custom-transform-hooks
          • Annotate
            • Make-implicit-wires
              • Vl-modulelist-make-implicit-wires
              • Vl-make-implicit-wires-aux
              • Shadowcheck
              • Vl-stmt-check-undeclared
              • Vl-make-implicit-wires-main
              • Vl-fundecl-check-undeclared
              • Vl-warn-about-undeclared-wires
              • Vl-implicitst
              • Vl-blockitem-check-undeclared
              • Vl-taskdecl-check-undeclared
              • Vl-modinst-exprs-for-implicit-wires
              • Vl-blockitemlist-check-undeclared
              • Vl-import-check-undeclared
              • Vl-make-ordinary-implicit-wires
              • Vl-gateinst-exprs-for-implicit-wires
              • Vl-remove-declared-wires
              • Vl-make-port-implicit-wires
              • Vl-module-make-implicit-wires
              • Vl-vardecl-exprs-for-implicit-wires
              • Vl-design-make-implicit-wires
            • Resolve-indexing
            • Origexprs
            • Argresolve
            • Portdecl-sign
            • Designwires
            • Udp-elim
            • Vl-annotate-design
          • Latchcode
          • Elim-unused-vars
          • Problem-modules
        • Lint
        • Mlib
        • Server
        • Kit
        • Printer
        • Esim-vl
        • Well-formedness
      • Sv
      • Fgl
      • Vwsim
      • Vl
      • X86isa
      • Svl
      • Rtl
    • Software-verification
    • Math
    • Testing-utilities
  • Annotate

Make-implicit-wires

Add declarations for implicit wires.

Verilog allows wires to be implicitly declared in certain cases.

Historically, VL added explicit declarations for these wires as part of the loader, even before a proper vl-module-p structures were generated. This allowed all transforms to expect that every wire should have a declaration. It also allowed us to consider the order of module elements, without having to rely on some technique such as the vl-location-ps associated with module elements, which could be unreliable if a module spans multiple files, e.g., because of includes.

When we added support for SystemVerilog import statements, we found this approach to be tricky. In particular, to decide whether we need to add a declaration for some particular wire foo, we now need to consider whether foo is defined by any imported package. For this, we really want to be able to inspect the whole vl-design while making implicit wires.

We therefore decided to split making implicit wires out of the parser and into a proper transform. However, we generally expect to run this transform very early, perhaps even as the first step after parsing. Normally this should be done as part of the annotate transform.

Note that this transform is somewhat unique. It is the only transform that considers the module elements in the order that they occur in the file. It reads this information from the loaditems field of the vl-module. As part of this transform, the loaditems field is cleared. Other transforms should never use loaditems.

Implicit Wires in the Verilog-2005 Standard

When a wire is implicitly declared, its type is controlled by the `default_nettype compiler directive (Section 19.2). But VL's preprocessor does not yet support this directive and will cause an error if it is used, so for now we can safely assume any implicit wires should just have type wire. (We can probably add support default_nettype without too much work, since our new approach of going through the module elements sequentially means that we have easy access to location information.)

We think wires need to be declared, explicitly or implicitly, before being used. The Verilog-2005 standard seems not to explicitly say whether or not this is the case, and the language at the start of Section 4.5 is somewhat vague: we are supposed to assume implicit nets in the absence of an explicit declaration in certain situations. But later in 4.5 we find some language that pretty strongly suggests we are only to consider whether or not there is an explicit declaration before the use of the net: "and that identifier has not been declared previously in the scope...."

Section 4.5 outlines the conditions under which an implicit wire declaration is supposed to be made. In each case, the implicit declaration is to be added to the nearest scope, but it seems like this only matters in the case of generate blocks, which we don't currently support. (You might think that it would matter for functions, tasks, and named blocks as well, but it doesn't seem possible to use implicit wires in these contexts, see bullet #4 below for details).

Note: I think that throughout Section 4.5, the words port expression declaration are a typo that should be port declaration; nowhere else in the Verilog-2005 standard do the words port expression declaration occur, at least according to Acrobat's find function.)

Case 1

If there is a port declaration like input [3:0] i; that has no corresponding wire declaration, then we are supposed to infer an implicit net with the vector width of the "port expression declaration," which I think means the [3:0] part. This seems basically reasonable when you just read it, but there are a lot of subtle issues that arise; see #7 below for some experiments.

Note that per 12.3.3, a port declaration like input wire [3:0] i; is treated as both an input declaration and an explicit wire declaration. We don't have to do anything special to handle this, because the parser automatically builds both a vl-portdecl-p and a vl-vardecl-p for such declarations; see vl-parse-port-declaration-noatts.

Case 2

If there is an undeclared identifier in the terminal list of a primitive or module instance, or in the left-hand side of a continuous assignment statement, then we are supposed to infer an implicit, scalar net.

I think a "primitive instance" is supposed to mean either a gate instance or a user-defined primitive instance. See for instance Section 7.1.6, which talks about "primitive instance connection lists" in reference to gates, and 11.6.6 where primitive terminals are distinguished from module ports. So, this means we should infer implicit wires in the ports/terminals of any instance, regardless of whether it's a gate/udp/module.

Implicit Wires in Verilog Implementations

We developed some tests (see test/test-implicit.v) to see how Verilog-XL and NCVerilog handle implicit wires. Here are our findings.

#1. As expected, both allow implicit wires to be inferred from the arguments of gate and module instances. This seems to be the intent of Case 2.

#2. As expected, both complain about undeclared wires on the right-hand side of an assign statement. This seems to agree with Case 2.

#3. As expected, NCVerilog allows us to infer implicit nets from the left-hand sides of assign elements. Unfortunately, Verilog-XL complains about such wires, which seems to contradict the Verilog-2005 standard. As a compromise, our approach is to mimick NCVerilog and infer the implicit net, but warn that some other tools like Verilog-XL may not allow the construct.

A subtle case is, what if #2 and #3 overlap, i.e., an undeclared wire occurs on both the left-hand and right-hand sides? NCVerilog seems to process the left-hand side first, and hence it allows us to infer an implicit wire for w when we give it an assignment like assign w = w. This isn't entirely contrived; someone might occasionally write things like assign {a, abar} = {foo, ~a}.

#4. Mostly as expected, neither tool allows undeclared wires on either side of an assignment in an always block. The tools even reject implicit wires in procedural continuous assignments, e.g., always @(b) assign w = a;. But this seems arguably incorrect: is not a procedural continuous assignment also a continuous assignment, whose LHS should therefore be able to contain implicit wires?

We mimick these tools and disallow the implicit net, even thought the spec might perhaps be interpreted as allowing it, because it allows us to avoid certain scoping issues. In particular, suppose we see a procedural continuous assignment statement, assign w = ..., where w is not declared. If this statement occurs in a task or a named block within an initial/always statement, should the declaration for w be added to the module or to this more local scope? I'm not sure. So, the good thing about not inferring implicit nets for these assignments is that I don't have to be able to answer the question, because I'm not going to infer a net anyway.

#5. As expected, neither implementation tolerates undeclared wires on either side of assignments in function bodies. This seems perfectly reasonable since functions aren't allowed to have procedural continuous assignments (10.4.4 E).

#6 As expected, both Verilog-XL and NCVerilog require that all wires be declared (either explicitly or implicitly) before they are used. For instance, they if a is declared but w is not, then they reject code fragments such as:

assign a = w;   // error here, saying w is undeclared
wire w;

And also for code like this:

not(a, w);
wire w;         // error here, saying w is implicitly declared above

#7 The whole declare-before-use thing is pretty strange for ports. Suppose c is a port of the module. Then, both NCVerilog and Verilog-XL will complain if you try to write:

wire c2 = c;    // error here, saying c is not declared.
input c;

Verilog-XL seems to require the port declaration to come before the wire declaration, if any. That is, it considers the following an error, whereas NCVerilog allows it:

wire c;
input c;

This seems to hold for implicit declarations, too. For instance, Verilog-XL rejects the following, but NCVerilog allows it:

buf(c, c2);     // implicit declaration of wire c here
input c;

But both Verilog-XL and NCVerilog allow the following, even though you might think the buf gate would introduce an implicit declaration of c that would conflict with the explicit declaration.

input c;
buf(c, c2);
wire c;

We try to be permissive and mimick NCVerilog, but add a (non-fatal) warning if a wire's net declaration comes before its port declaration to explain that some other tools do not permit this.

Other Notes

In previous versions of VL, we allowed declarations to come in any order, and inferred an implicit wire whenever a wire was used in a context that permitted it. We now try to be more faithful to other Verilog systems and require that wires be declared before their first uses, since this seems like the right way to interpret the Verilog-2005 standard.

With regard to Case 1, we add a net declaration for each port declaration that is missing a corresponding wire declaration. In the process, we make sure that at least some declaration (be it a wire or port declaration) of each port occurs before every use of the port. If only a wire declaration occurs before some use of the port wire, we issue a non-fatal warning saying that Verilog-XL does not tolerate this ordering.

When we add implicit wires for ports, we use the range of the port declaration, which seems to be correct with respect to the "vector width of the port declaration expression," described above. We also keep the signedness of the port, which isn't discussed above, but appears to be the correct thing to do; see portdecl-sign for details. We place the implicit wire declaration at the same location as the corresponding port declaration, which seems like the easiest place to put it. We also mark each implicit wire declaration with the VL_PORT_IMPLICIT attribute. This attribute is used by the printer to avoid printing any net declarations that were implicit.

With regard to Case 2, we add one-bit wire declarations for any undeclared wires that occur within the port arguments of gate and module instances, and which occur in the left-hand sides of continuous assignments. For assignments, we always issue a non-fatal warning that says Verilog-XL doesn't add implicit nets here, and we always process the left-hand side first (like NCVerilog). We add the wire declarations at the locations in which they are implicitly declared, and mark them with the VL_IMPLICIT attribute, which is useful in typo-detection.

Subtopics

Vl-modulelist-make-implicit-wires
(vl-modulelist-make-implicit-wires x ss) maps vl-module-make-implicit-wires across a list.
Vl-make-implicit-wires-aux
Main function for adding implicit wires.
Shadowcheck
Sanity check to prevent tricky kinds of global name shadowing.
Vl-stmt-check-undeclared
Check an arbitrary vl-stmt-p for uses of undeclared names.
Vl-make-implicit-wires-main
Augment a list of module elements with declarations for any implicit nets, and make sure that every identifier being used has a declaration.
Vl-fundecl-check-undeclared
Check an arbitrary vl-fundecl-p for uses of undeclared names.
Vl-warn-about-undeclared-wires
Add fatal warnings about names that are used but not declared.
Vl-implicitst
State for the make-implicit-wires transform.
Vl-blockitem-check-undeclared
Check for undeclared wires in an arbitrary vl-blockitem-p, and extends decls with the newly declared name.
Vl-taskdecl-check-undeclared
Check an arbitrary vl-taskdecl-p for uses of undeclared names.
Vl-modinst-exprs-for-implicit-wires
Get the expressions from a module instance, for making implicit wires.
Vl-blockitemlist-check-undeclared
Extends vl-blockitem-check-undeclared across a vl-blockitemlist-p.
Vl-import-check-undeclared
Vl-make-ordinary-implicit-wires
Generate net declarations for one-bit implicit wires.
Vl-gateinst-exprs-for-implicit-wires
Gets the expressions from a gate instance, for making implicit wires.
Vl-remove-declared-wires
Filter names to remove wires that are already declared. We remove the names of any port declarations, ordinary declarations, global names, and imported names.
Vl-make-port-implicit-wires
(vl-make-port-implicit-wires portdecls decls) generates variable declarations for ports that don't have corresponding variable declarations.
Vl-module-make-implicit-wires
Vl-vardecl-exprs-for-implicit-wires
Gets the expressions from a variable declaration, for making implicit wires. We omit the expressions inside the datatype.
Vl-design-make-implicit-wires