Thoughts on IC development, EDA, and hardware design languages.

SystemVerilog Insights Series: Packages and Macros Together? Watch Out!

Posted by: Trent McClements | Posted on: December 9th, 2014 | 4 Comments

Share on LinkedIn22Share on Facebook0Tweet about this on Twitter0Share on Google+0Email this to someone

In Verilog, marcos were (arguably) a necessary evil for defining global constants. Luckily SystemVerilog has a better, cleaner way: Packages. But, unfortunately, to ensure backward compatibility, macros didn’t go away. Now, in SystemVerilog you can use both, but if you do, watch out..!

In our last post in this series we started to look at compilation units, and we promised that in this post we would push deeper into the intricacies of how packages, macros and compilation units come together.

First, let’s take a look at how packages and macros are related to compilation units. We’ll then probe into a use case that will show the subtlety surrounding these structures.

As per the SystemVerilog LRM packages provide a declaration space and can contain things like types, nets, variables, tasks, etc. Packages’ scope is global and spans across compilation units. That is, a package defined as a file that was read into one compilation unit will be available in all other compilation units. A really useful feature that has been in VHDL for a long time.

Now, macros are text substitution compiler directives. These structures are also global in scope and can be redefined. As such, their value is dependent on the order that files are analyzed in. Macros also have a global scope, but this is contained to with the compilation unit of the file that defined them. So you cannot define a macro in one compilation unit and use it in another. This actually makes a lot of sense — you really don’t want the macros defined in re-use sub-modules to pollute, and potentially change the behaviour, of your other modules (think macro-name collisions).

So, packages have a scope that spanning compilation units and macros have a scope contained within one. Simple right? Seems so, but nothing in the SystemVerilog LRM restricts you from defining a macro within a package.

package pkg;
integer wait_cnt = 0;
`define READ_DELAY_MAX 12
integer read_delay_cnt = 0;

What happens now? Well, you may believe that you have worked within a corner of the LRM and created a macro that has thrown off the compilation unit shackle, thus able to reference READ_DELAY_MAX anywhere that the “pkg” package is imported. Not true! The macro, as a pre-compiler directive, is not actually part of the package. The macro, though defined within the package text, exists outside of the package definition and thus is still constrained to within the compilation unit that the package was first analyzed into. So any file analyzed into that compilation unit will understand the meaning of READ_DELAY_MAX but all other files outside that compilation unit will not. The only way, in the SystemVerilog world of compilation units, to have a global macro definition is to use a multi-file compilation unit and compile all of your files into that unit.

Once you work through the above example the situation doesn’t seem overly complex. However, in the midst of managing 15,000 Verilog and SystemVerilog files from 100+ designers across the world, all using different coding styles, getting code that uses macros, packages and compilation units to compile you really need to keep their interdependencies and scope in mind. I’ve run into more than a few situations where a better understanding of construct scoping and how your SystemVerilog parser will analyze my RTL would have saved me a bunch of debug time!

Have you ever had tricky problem in this area? If so let us know what it was….


Share on LinkedIn22Share on Facebook0Tweet about this on Twitter0Share on Google+0Email this to someone

Comments (4)

  1. Dave Rich - Reply
    December 15, 2014

    Hi Trent, You have described everything correctly according to my understanding. I just wished your only example was not something that we strongly recommend against writing that way. Text macros are not only in the global compilation unit space, they are processed before the compiler understands anything else. So although the text macro appears to be inside the package, the compiler may not even know it is inside a package. Thiat is why macros are sometimes called pre-processor directives.

    Our recommendation is all text macros defined in a separate file that is `include’s where needed.

    • Trent McClements - Reply
      December 15, 2014

      Hi Dave,

      Thanks for your comment. And you are right, my example is not showing good coding practice, though I believe such usage is actually quite common. My intention was to use an example that highlights the issue but I certainly should have mentioned in the blog that we don’t recommend coding in this fashion. Macros are actually quite tricky and can have some hard-to-trace consequences in a design. Every company should have, and enforce, their own standard on how to use them!

  2. Shalom Bresticker - Reply
    January 7, 2015

    “The only way, in the SystemVerilog world of compilation units, to have a global macro definition is to use a multi-file compilation unit and compile all of your files into that unit.”

    A common approach is to put global macros in a special file and `include or compile that file in each compilation unit.

    • Trent McClements - Reply
      January 7, 2015

      That is true, and a common work-around. This approach certainly gives access to a common macro in all files where the include file is used or each compilation unit that file is compiled into. Not a true “global”, in the pedantic sense of the word, but certainly an approach to achieve a similar effect!

Leave a Comment