• Top
    • Documentation
    • Books
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
    • ACL2
    • Interfacing-tools
    • Hardware-verification
      • Gl
      • Esim
      • Vl2014
        • Warnings
          • Vl-warninglist->types
          • Vl-warning
          • Propagating-errors
          • Vl-reportcard
          • Vl-warning-sort
          • Lint-warning-suppression
          • Clean-warnings
          • Warn
          • Vl-print-warnings-with-header
          • Vl-some-warning-fatalp
          • Vl-print-warnings-with-named-header
          • Flat-warnings
          • Vl-remove-warnings
          • Vl-keep-warnings
          • Vl-print-warnings
          • Vl-some-warning-of-type-p
          • Vl-clean-warnings
          • Vl-warnings-to-string
          • Vl-warninglist
          • Vl-print-warning
          • Ok
          • Vl-trace-warnings
          • Fatal
        • Primitives
        • Use-set
        • Syntax
        • Getting-started
        • Utilities
        • Loader
        • Transforms
        • Lint
        • Mlib
        • Server
        • Kit
        • Printer
        • Esim-vl
        • Well-formedness
      • Sv
      • Fgl
      • Vwsim
      • Vl
      • X86isa
      • Svl
      • Rtl
    • Software-verification
    • Math
    • Testing-utilities
  • Vl2014

Warnings

Support for handling warnings and errors.

Introduction

Many parts of VL can run into situations where we want to issue a warning or cause an error. For instance:

  • Our loader could encounter syntax that is simply malformed, or it could run into a construct that is legal but that we don't support yet. It could also notice valid but strange cases, e.g., 4'd16 is well-defined but weird because 16 doesn't fit in 4 bits.
  • Our transforms might run into semantically ill-formed constructs, e.g., perhaps a module declares wire [3:0] foo and then later declares integer foo, or perhaps we are trying to instantiate a module that is not defined.
  • Our lint checks might notice "code smells" where even though the input Verilog is semantically well-formed, it is somehow strange and looks like it could be an error. For instance, perhaps there are multiple assignments to the same wire, or expressions like a & b where a and b have different sizes.

Handling these many kinds of cases is tricky. In the early days of VL, our approach to warnings and errors was quite ad-hoc. We sometimes printed warning messages to standard output using the cw function. For more serious conditions, we sometimes caused errors using er. This approach had a number of problems. In particular,

  • It led us to see many of the same warnings repeatedly because our various well-formedness checks were run many times on the same modules in different stages of the translation.
  • For some warnings, we did not particularly care about the individual instances of the warning. For instance, unless we're interested in fixing the "if" statements to be ?: instead, we don't want to be told about each occurrence of an if statement. We just want a higher-level note that hey, there are 30 if-statements to clean up.
  • The warnings were not "attached" in any way to the modules that they were actually about. Practically speaking, this might mean that users might not even see the warnings that had been generated for the modules they are working on.
  • There is no way to recover form an error created with er, so if we ran into some bad problem with a particular module, it could actually prevent us from translating any of the modules. This was particularly troublesome because Verilog is such a large language that, especially in the beginning, we often ran into constructs that we did not yet support.

These sorts of problems quickly led us to want a more coherent, global approach to dealing with warnings and errors.

Warning Objects

Our new approach to warning and error handling centers around explicit vl-warning-p objects.

These objects are in many ways similar to the Exception objects found in other programming languages. Each warning has a type and a message that describes the error. These messages can conveniently make use of VL's printer, so you can directly pretty-print arbitrary Verilog constructs when writing warning messages.

We use vl-warning-p objects universally, for all kinds of warnings and errors. That is, everything from the most minor of code smells (wire foo is never used for anything), to the most severe problems (the module you're instantiating isn't defined) results in a warning. Since it is useful to distinguish minor commentary from severe problems, our warning objects include a fatalp field.

As a general philosophy or strategy for using these warning objects:

  • Warnings messages should never be printed to standard output. Instead, we should create a vl-warning-p object that gives the context, and explains the problem as clearly and concisely as possible.
  • Errors should not cause sudden, unrecoverable exits. That is, er should never be used for warnings that could plausibly be triggered by malformed Verilog. (However, it is reasonable to use er in an assert-like fashion, to rule out programming problems that we believe are impossible.)
  • Non-fatal warnings should be used for any issues that are purely stylistic, "code smells," etc., such as linter-like checks.
  • Fatal warnings should be used for any issues that are truly errors. For instance: malformed syntax, conflicting declarations of some name, references to undefined modules, etc.
  • Fatal warnings may also be used when a transform encounters constructs that are valid but not supported, e.g., because we have simply not yet spent the time to implement them.

Accumulating Warnings

Warning objects are simple enough to understand, but what do we actually do with them? We adopt another general principle:

  • Every warning object should be associated with the top-level design elements (e.g., module, package, interface, etc.) where it was caused.

This approach allows us to easily do many practically useful things with the warnings. For instance, it lets us easily filter out any modules that have fatal warnings—an important operation for vl-simplify. As another example, we can create reports, e.g., about all of the warnings for some particular module, or all the warnings of some particular type throughout all of the modules, etc. These capability is used in tools like vl-lint.

Practically implementing this philosophy is slightly tricky.

Deep within some particular transform, we might encounter something that is wrong and decide to issue a warning. In a typical object-oriented programming language, this would be trivial: our module class might have an add-warning that (destructively) adds a new warning to the module.

But our programming language is truly functional, so we cannot modify existing modules. Instead, whenever some subsidiary function wants to produce a warning, its caller must take measures to ensure that the warning is eventually added to the appropriate module.

Our usual approach is to add a warnings accumulator as an argument to our functions. Typically this argument should be named warnings. Functions that take a warnings accumulator return the (possibly extended) accumulator as one of their return values. Macros like ok, warn and fatal can assist with implementing this convention.

At a high level, then, a function that transforms a top-level design element, e.g., a module, begins by obtaining the current warnings for the module. Using these warnings as the initial accumulator, it calls its subsidiary helpers to carry out its work. This work transforms various parts of the module, and meanwhile the warnings are perhaps extended. Finally, the function returns a new vl-module-p which is updated with the extended list of warnings.

Subtopics

Vl-warninglist->types
(vl-warninglist->types x) maps vl-warning->type across a list.
Vl-warning
Fundamental warning object used throughout vl2014.
Propagating-errors
The error handling strategy used by vl-simplify.
Vl-reportcard
A (typically fast) alist associating names to warnings.
Vl-warning-sort
Mergesort warnings using vl-warning-<
Lint-warning-suppression
An attribute- mechanism for suppressing particular warnings when using lint.
Clean-warnings
A transform to clean up all the warnings in a design.
Warn
Extend a warnings accumulator with a non-fatal warning.
Vl-print-warnings-with-header
Pretty-print a vl-warninglist-p with a header saying how many warnings there are.
Vl-some-warning-fatalp
Check if any warning is marked as fatal.
Vl-print-warnings-with-named-header
Flat-warnings
Extract flat lists of warnings from various design elements.
Vl-remove-warnings
Remove warnings of certain types.
Vl-keep-warnings
Keep only warnings of certain types.
Vl-print-warnings
Pretty-print a vl-warninglist-p.
Vl-some-warning-of-type-p
Check if there are any warnings of certain types.
Vl-clean-warnings
Sort and remove duplicates from a list of warnings.
Vl-warnings-to-string
Pretty-print a vl-warninglist-p into a string.
Vl-warninglist
A list of vl-warning-p objects.
Vl-print-warning
Pretty-print a vl-warning-p.
Ok
A convenient shorthand for calling vl-warninglist-fix.
Vl-trace-warnings
Pretty-print warnings as they are created.
Fatal
Extend a warnings accumulator with a fatal warning.