-----Original Message----- From: owner-sv-ec@eda.org [mailto:owner-sv-ec@eda.org] On Behalf Of Mark Hartoog Sent: Friday, August 03, 2007 10:02 AM To: sv-ec@eda.org Subject: [sv-ec] Name resolution issues Problem statement: First I am going to describe the name resolution problems I am attempting to address. The name resolution issues arise because of several features: 1) Wild card package imports. 2) Segmented $unit 3) Late binding caused by type parameters. 4) Late binding of identifiers in bind statements. 5) Forward references to fields in classes. In Verilog-2005 all non-dotted identifiers, except for task and function calls, can be resolved as they are parsed. For simplicity I am going to leave task and function identifiers out of the discussion for the moment. The basic question is should we continue to require all of these non-dotted identifiers to be resolved at parse time, or will some of the identifiers be resolved later. There are several later points where they could be resolved. These include end of scope, end of module, end of compilation unit, and at elaboration. In the case of bind statements, we have little choice. Non-dotted identifiers in bind statements have to be resolved at elaboration time. Section 22.10 says that the effect of a bind to is to create an instance at the end of the target scope. Identifiers in the bind statement can clearly bind to any symbol in that scope. Although the LRM does not say, they presumably can also bind to symbols that have been imported into that scope. What is less clear is whether they can bind to symbols in $unit defined after the target module (same for nested modules) or whether the bind statement can cause additional symbols to be wild card imported into the scope. In addition to bind the language features or potential features that cause problems are: 1) Randomize with blocks: module m #(type TP = CB) (); int a; TP x; initial x.randomize with { a < 1 }; The identifier'a' is suppose to be resolved first in the scope of the variable 'x,' but the type of 'x' is a type parameters, so the scope cannot be examined until elaboration. The only way to fix this so it can be resolved at parse time is to make a backwards incompatible change to the syntax to force users to indicate which identifiers must resolved in the scope of the variable. 2) Forward references to fields in classes: int a; class C; function int f(); return a; endfunction int a; endclass Both C++ and Java require declaration before use for variables in procedural contexts like Verilog, but they allow use before declaration of class fields and methods. I think users expect this behavior in classes. This can be implemented by delaying the resolution of identifiers in classes until end of class. 3) Forward typedefs as base class: typedef class CB; int a; class C extends CB; function int f(); return a; endfunction endclass class CB; int a; endclass This feature is in P1800-2005, so removing it would be a backwards compatibility issue. On the other hand it really doesn't add anything useful to the language, so it probably could be removed without causing any serious problems. To implement this you have to delay resolution of identifiers in classes to end of compilation unit, since worse case the base class could be a forward typedef in $unit. 4) Type parameters as base class. module m #(type TP = CB) (); int a; class C extends CB; function int f(); return a; endfunction endclass It is unclear if this is allowed in P1800-2005. I know of several implementation that have taken a liberal interpretation of class_identifier to include type parameters that are assigned class types in other contexts where the LRM indicates a class_identifer is required. I have also gotten customer inquires as to why they cannot use a type parameter for a base class. I do not know of any implementations that actually allows this. In C++ and Java template classes this is illegal. If this is allowed in the language, then identifiers in class task and functions cannot be resolved until elaboration. (Note: I am not proposing that we add this now. I included this here, because customers have asked why they cannot do this, and this would be completely ruled out by requiring early name resolution.) Proposals to address these Problems Making the language so that all simple identifiers can be resolved at parse time will simplify tools. On the other hand, if some identifiers are not going to be resolved till elaboration, then I don't see why we should be making incompatible changes in the language or making the language less powerful to reduce the number of cases that identifiers are resolved after parse time. The important question is can we resolve the identifiers later, say at end of compilation unit or elaboration, and obtain results that are consistent with the wild card package import rules and $unit scoping rules. I believe this is possible. Here is an example I will use to illustrate the algorithm: package P; int a; endpackage class SomeClass; endclass module m #(type TP = SomeClass); import P::*; TP x; initial x.randomize with { a < b }; int a; // should be an error if 'a' was wild card imported by randomize. endmodule int b; // 'b' in randomize should never bind to this, because it is after module class Ok; int a; int b; endclass class Bad; int c, d; endclass module top; m #(Ok) m1(); m #(Bad) m2(); endmodule In instance top.m1, everything is fine. The identifiers 'a' and 'b' resolve to fields of the class Ok. In instance top.m2, neither 'a' nor 'b' can be resolved in the class Bad. Here 'a' should cause the wild card package import of P::a. This would then make the declaration of 'a' later in the module an error. The identifier 'b' should have no resolution and be an error. It should not resolve to $unit::b because that declaration is after the module. In this case these identifiers can only be processed at elaboration time, because only then is the scope of the variable 'x' known. The technique described below can resolve these correctly and produce the correct error messages. This may not be the only way of doing this. It is just one way of doing it. To do this we need to identify a candidate resolution for each identifier in the randomize with block at parse time. We record this resolution as a candidate, but do not actually make the resolution. In the example above, the candidate for 'a' would be the wild card package import of P::a. We identify P::a as the candidate, but we do not actually make the package import. When we parse the declaration of 'a' later, this declaration is legal because P::a has not been imported. For 'b' there is no candidate resolution at parse time. We record a null candidate resolution. At elaboration time we try to resolve 'a' and 'b' in the scope of the variable 'x'. In top.m1, they both resolve, and everything is fine. In top.m2, neither 'a' nor 'b' resolves in the scope of 'x'. The only other resolution that can be considered for 'a' or 'b' at elaboration time is the candidate that was identified at parse time. In top.m2, there is a candidate for 'a'. To resolve 'a' to this candidate we would have to wild card import P::a, but this wild card import is no longer legal. There is now a symbol 'a' in the scope of module 'm', so instead of resolving 'a', we give an error message that the declaration of 'a' is illegal because 'a' has been wild card imported. For 'b' there was no parse time candidate, so we simply give an unresolved identifier error message. A similar thing can be done in classes, even in classes where the base class is a type parameter. This does not address the issues with bind. Bind statements are not in the target context at parse time, so you cannot have parse time candidates for them. The basic issue is illustrated by this example: package P; int a; endpackage module m; import P::*; endmodule module b (input int x, y); endmodule int b; module top; m m1(); bind m: m1 b b1(a, b); endmodule Here 'a' and 'b' need to be resolved at elaboration time in module 'm'. If this instantiation were present at the end of the module 'm' at parse time, 'a' would resolve to P::a and cause 'a' to be wild card imported into 'm'. I think it should still do this at elaboration time. The identifier 'b' is a little trickier. We cannot use parse time candidates here. The choices seem to be 1) Let 'b' resolve to $unit::b. 2) Do not allow any bind identifiers to resolve in $unit. If you want it to resolve to $unit, the user can put $unit::b in the bind statement. (Note: this is a little strange. It is the $unit of the target, not the $unit that contains the bind statement) 3) Require tools to have a segmented view of $unit so that identifiers can be correctly resolved. I think either 1 or 2 are good enough, but I can live with 3 too. Summary It is possible to resolve all simple identifiers at parse time as others have proposed. To do this we need to: 1) Make backwards incompatible changes in the language. 2) Forever rule out useful features like forward references to class members, a feature that most other languages with classes allow. I see no reason to do this. The Verilog language has always required name resolution at elaboration time for task, function and hierarchical names. The bind construct clearly requires name resolution at elaboration for simple identifiers. I see no compelling reason for making backwards incompatible changes to avoid a few more cases of delayed resolution of simple identifiers. -- This message has been scanned for viruses and dangerous content by MailScanner, and is believed to be clean. -- This message has been scanned for viruses and dangerous content by MailScanner, and is believed to be clean.Received on Fri Aug 3 10:13:36 2007
This archive was generated by hypermail 2.1.8 : Fri Aug 03 2007 - 10:13:46 PDT