The painfull verilog preprocessor pitfall

Just a little note about how includes and `defines work in verilog which is VERY different from how they behave in most programming languages. This may not really hurt in a small project, but can become a real PITA in a big project with a dozen of third-party blocks.

TL;DR: Macro defines are have a global scope in verilog and propagate from file to file during one tool invocation.

Oops? How did that happen? 1.v included 1.vh that had A defined. This define persisted when compiling 2.v. However if we change the order of compilation – everything will work the other way. What’s even worse – iverilog will never warn us about redefining a macro (At least Icarus Verilog version 0.9.7 that is shipped in debian repositories)

Verilator (v. 3.900 2017-01-15) does exactly the same, but at least it spits out a warning when redefining a macro.

Finally, let’s test Cadence tools. The tools from cadence are really pricey, but you can always give them a spin for free to test out things out on edaplayground.

I will not use irun for this test (which is nothing, but a bunch of awful hacks IMO), but revert to plain old three-step invocation. Cadence tool called ncvlog compiles stuff into a library before elaboration, so we can choose between invoking ncvlog for each file or feed all files to it at once. Surprisingly this will result in a totally different behavior. See for yourself.

What can be done about it?

Hint: There’s no silver bullet. Life is pain.

This behavior should never be a problem for a small project. But if you are developing a SoC and licensed/downloaded from opencores a bunch of third-party IPs – chances are they may use a lot of macros to enable/disable different functionality. Even worse, they may depend on defines to be propagated via a file-list.

The ability of some tools (e.g. cadence) to isolate the scope of macro visibility for certain groups of files is NOT the solution (and is likely an undocumented feature): When synthesizing you’ll need to pass the full filelist to the tool and you’ll end up with different RTL for synthesis and simulation which is even a bigger problem that can lead to infinite amount of pain.

Watch out for warnings on macro redefinitions, pick the tools that actually DO warn about them and treat them as errors! (This won’t save you from a hidden `ifdef in a third-party code, though)

Use scripts to catch and check defines / ifdef in third-party code and check the codebase for possible collisions regularly.