User Manual

Instructions on how to use the slang tool as an end-user.

Quick Start

The slang binary runs a full compilation of given SystemVerilog source files. Eventually it will produce a full simulation binary as output, but for now it serves as a checker of source code syntax, types, and various language rules.

Running it on one or more valid SystemVerilog files will note the top level modules found, and will return zero:

// test1.sv
module m;
    struct { logic a; } s;
    int i = s.a + 1;
    initial $display("%d", i);
endmodule
> slang test1.sv
Top level design units:
    m

Build succeeded: 0 errors, 0 warnings

> echo $?
0

Running it on a file with errors will produce nicely formatted diagnostic output:

// test2.sv
module m;
    struct { logic a; } s;
    int i = s + 1;
    initial $display("%d", i);
endmodule
Top level design units:
    m

../test2.sv:4:12: error: invalid operands to binary expression ('<unnamed unpacked struct>' and 'int')
        int i = s + 1;
                ~ ^ ~

Build failed: 1 error, 0 warnings

Compilation Units

By default slang treats all input files as separate SystemVerilog compilation units. This is the preferred method both because it keeps the code logically separate (it doesn't require a specific ordering of files on the command line in order to compile correctly) and because internally the tool can parse files in parallel if it knows they don't have to be parsed in a specific order.

If you have an older project where file ordering does matter, you can pass the --single-unit option to have all input files treated as a single compilation unit.

Built-in Macros

There are a handful of slang-specific predefined macros that you can reference in your code:

NameValue
`__slang__1
`__slang_major__slang major version number
`__slang_minor__slang minor version number

Command Files

Command files allow specifying tool options in standalone composable files instead of needing to pass all of them on the command line. One or more command files can be used by passing them via the -F or -f command line flags – both flags function identically except in the treatment of file paths in the command file. -F files treat their paths as relative to the command file itself, where as -f files treat paths as relative to the current working directory.

The format of a command file is straightforward; all text is treated as whitespace-delimited arguments, similarly to how they might be passed on the command line. Newlines are treated like any other form of whitespace. Some important features are as follows:

  • Command files can contain comments. '#' starts a line comment that continues to the end of the line, regardless of where the '#' occurs. '//' similarly starts a line comment, but only if preceeded by whitespace. '/∗' and '∗/' form a C-style multiline block comment.
  • Single quotes and double quotes group text in a similar fashion to how they work in Bash and other shells.
  • The backslash character can be used to escape special characters and treat them like normal text.
  • Environment variables in the command file are expanded. Variables can be specified in one of three styles: $VAR, $(VAR), and ${VAR}.
  • Command files can further include other command files by using -f or -F.
  • Arguments set in a command file are overridden by anything set on the command line if they would otherwise conflict.

Nonstandard Built-in Functions

Besides the system tasks and functions specified in the SystemVerilog LRM, slang also provides the following nonstandard built-in functions:

$static_assert(condition[, message])

Performs an elaboration-time check of the provided condition (which must be a constant expression) and fails with an error if the condition is false. This works very similarly to the $error elaboration system task, with the optional message being a string that accepts formatting specifiers in the same style as the $sformatf function. If no message is provided it will print a default message about the failure.

$static_assert is usable in all contexts where $error is allowed, and additionally is supported in other contexts such as packages. It can also be used in task, function, and other statement blocks.

If the assertion fails and the provided condition contains a comparison operator, slang will print additional helpful information about both sides of the comparison to help diagnose the failure.

Example:

// test.sv
module m;
    localparam int foo = 12;
    struct packed { logic [4:1] a, b; } bar;
    $static_assert(foo < $bits(bar));
endmodule
test.sv:5:5: error: static assertion failed
    $static_assert(foo < $bits(bar));
    ^
test.sv:5:24: note: comparison reduces to (12 < 8)
    $static_assert(foo < $bits(bar));
                   ~~~~^~~~~~~~~~~~

Driver loop unrolling

SystemVerilog specifies various rules about how nets and variables can be driven. These rules all operate on the principal of "longest static prefixes" which are described in the LRM in section 11.5.3. Unintuitive behavior can result when operating on nested subsets of arrays or structures due to these rules, for example:

module m;
    int foo[10];
    initial
        for (int i = 1; i < 10; i += 2) begin : baz
            foo[i] = 2;
        end

    for (genvar i = 0; i < 10; i += 2) begin
        always_comb foo[i] = 1;
    end
endmodule

Under LRM rules, this results in a compilation error because inside a procedural block the for loop variable i is not constant and therefore the longest static prefix of the assignment is just foo, and so all elements are assumed assigned by the initial block. Most tools, slang included, implement an extension to attempt to unroll all procedural for loops in order to extend the longest static prefix of assignments within those loops, making the above example compile successfully. Such unrolling can be disabled with the --strict-driver-checking command line flag.

The limitations of this feature are as follows:

  • Only for loops are supported – other kinds of loops are not.
  • The for loop must declare its iteration variables and must provide stop and iteration expressions.
  • The number of iterations of all for loops in a block must be less than the limit given by --max-constexpr-steps
  • Only assignment statements are supported – output args from subroutines are not taken into account.
  • Conditional statements inside of for loops that depend on the iteration variable will work as expected.
  • All other forms of flow control inside each for loop (including break / continue) are ignored.

Pragmas

slang supports the following pragma directives as extensions to the base set specified by the LRM:

`pragma once

Files that contain this directive will not be included more than once into any given compilation unit, regardless of how many times they are specified via a `include directive. This is analagous to the #pragma once directive supported widely by C++ compilers, and can replace the more traditional usage of preprocessor header guards via `ifdef.

`pragma diagnostic options...

A set of pragmas that can control diagnostic warning output for various regions of source text. These options mirror those provided on the command line but have the advantage of being scoped to specific parts of your code.

The possible values for options are:

  • push and pop – create a scope via push for further diagnostic options that can later be reverted back to their previous state via a pop option.
  • ignore – One or more warning flags to ignore.
  • warn – One or more warning flags to enable.
  • error – One or more warning flags to turn into errors.
  • fatal – One or more warning flags to turn into hard fatal errors.

Example:

module m;
    ; // warn
`pragma diagnostic ignore="-Wempty-member"
    ; // hidden
`pragma diagnostic push
    ; // also hidden
`pragma diagnostic error="-Wempty-member"
    ; // error
`pragma diagnostic warn="-Wempty-member"
    ; // warn
`pragma diagnostic pop
`pragma diagnostic pop  // does nothing
    ; // hidden again

`pragma diagnostic fatal="empty-member" // ok to not use -W
`pragma diagnostic ignore=("default", "empty-member")
    ; // ignored
endmodule