This is a quick hello-world about slang. slang is a really interesting library (it has a CLI too) to parse, process, and check SystemVerilog. This is a snippet from doc:

slang is a software library that provides various components for lexing, parsing, type checking, and elaborating SystemVerilog code. It comes with an executable tool that can compile and lint any SystemVerilog project, but it is also intended to be usable as a front end for synthesis tools, simulators, linters, code editors, and refactoring tools.

slang is the fastest and most compliant SystemVerilog frontend (according to the open source chipsalliance test suite).

Build Link to heading

The steps to build slang are at docs, but I will use bare bones commands (avoiding CMake integration) to work through the errors and understand the dependencies.

cd slang
cmake -B build
cmake --build build -j4

Now we should have the following bin/ and lib/.

bin/
├── netlist_unittests
├── rewriter
├── slang
├── slang-hier
├── slang-netlist
├── slang-reflect
├── slang-tidy
├── tidy_unittests
└── unittests
lib/
├── libCatch2.a
├── libCatch2Main.a
├── libfmt.a
├── libmimalloc.a
└── libsvlang.a

C++ APIs Link to heading

The first thing to do is to pipe clean the compilation commands for C++ applications using slang APIs.

#include "slang/ast/Compilation.h"
#include "slang/driver/Driver.h"
#include "slang/util/VersionInfo.h"

using namespace slang;
using namespace slang::driver;

int main(int argc, char** argv) {
    Driver driver;
    driver.addStandardArgs();

    std::optional<bool> showHelp;
    std::optional<bool> showVersion;

    printf("slang version %d.%d.%d+%s\n", VersionInfo::getMajor(),
        VersionInfo::getMinor(), VersionInfo::getPatch(),
        std::string(VersionInfo::getHash()).c_str());
    return 0;
}

There are two caveats though:

  • slang needs libboost.
  • it also needs C++20 features. So, it needs a new version of C++.
sudo apt install libboost1.83-dev
g++ -g -std=c++20   main.cpp -I slang/build/source/ -I slang/include/ -Islang/external -Islang/build/_deps/fmt-src/include  -c
g++ -o main.bin main.o slang/build/lib/libsvlang.a ./slang/build/lib/libfmt.a ./slang/build/lib/libmimalloc.a

That’s it.

$ ./main.bin 
slang version 8.0.108+27c84c78

slang Link to heading

The main CLI is ./build/bin/slang which takes many of the options simulators take. One of the interesting options is json dumper

./slang/build/bin/slang test.sv --ast-json t.json
{
  "design": {
    "name": "$root",
    "kind": "Root",
    "addr": 4572765965120,
    "members": [
      {
        "name": "",
        "kind": "CompilationUnit",
        "addr": 4572766721248
      },
      {
        "name": "top",
        "kind": "Instance",
        "addr": 4572766723544,
        "body": {
          "name": "top",
          "kind": "InstanceBody",
          "addr": 4572766721544,
          "members": [
            {
              "name": "address",
              "kind": "Variable",
              "addr": 4572766721728,
              "type": "reg[7:0]",
              "lifetime": "Static"
            },
            {
              "name": "data_in",
              "kind": "Variable",
              "addr": 4572766722072,
              "type": "reg[7:0]",
              "lifetime": "Static"
            },

Another nice example is macro expansion with -E. I am using this next time I write unnecessarily complicated macros.

`define REG reg 

module top;

`REG x;
endmodule

This generates the following output

./slang/build/bin/slang test1.sv -E

 
module top;
reg x;
endmodule

slang-tidy Link to heading

Other than slang CLI, there are other useful utilities. I wasn’t sure what it does, so I diff’ed the options (I should have looked at the documentation first!)

<   -q,--quiet                                          Suppress non-essential output
<   -E,--preprocess                                     Only run the preprocessor (and print preprocessed files to stdout)
<   --macros-only                                       Print a list of found macros and exit
<   --parse-only                                        Stop after parsing input files, don't perform elaboration or type checking
<   --disable-analysis                                  Disables post-elaboration analysis passes,which prevents some diagnostics from being issued
<   --comments                                          Include comments in preprocessed output (with -E)
<   --directives                                        Include compiler directives in preprocessed output (with -E)
<   --obfuscate-ids                                     Randomize all identifiers in preprocessed output (with -E)
<   --ast-json <file>                                   Dump the compiled AST in JSON format to the specified file, or '-' for stdout
<   --ast-json-scope <path>                             When dumping AST to JSON, include only the scopes specified by the given hierarchical paths
<   --ast-json-source-info                              When dumping AST to JSON, include source line and file information
<   --ast-json-detailed-types                           When dumping AST to JSON, expand out all type information
<   --time-trace <path>                                 Do performance profiling of the slang compiler and output the results to the given file in Chrome Event Tracing JSON format
---
>   --print-descriptions                                Displays the description of each check and exits
>   --print-short-descriptions                          Displays the short description of each check and exits
>   --config-file                                       Path to where the tidy config file is located
>   --skip-file                                         Files to be skipped by slang-tidy
>   --skip-path                                         Paths to be skipped by slang-tidy
>   -q,--quiet                                          slang-tidy will only print errors. Options that make slang-tidy print information will not be affected by this.
>   --super-quiet                                       slang-tidy will not print anything. Options that make slang-tidy print information will not be affected by this.
>   --code                                              print information about the error or warning.

Running slang-tidy generates nice lint messages. Note to self, look at linter rules.

./slang/build/bin/slang-tidy test.sv
NoOldAlwaysSyntax] WARN
test.sv:15:3: warning: [STYLE-4] use of old always verilog syntax
  always @ (address or data_in or read_write or chip_en)
  ^
test.sv:20:3: warning: [STYLE-4] use of old always verilog syntax
  always @ (read_write or chip_en or address)
  ^
test.sv:20:3: warning: [STYLE-4] use of old always verilog syntax
  always @ (read_write or chip_en or address)
  ^

[EnforcePortSuffix] WARN
test.sv:9:20: warning: [STYLE-2] port 'address' is not correctly suffixed with suffix: "_i"
  input wire [7:0] address, data_in;
                   ^
test.sv:9:29: warning: [STYLE-2] port 'data_in' is not correctly suffixed with suffix: "_i"
  input wire [7:0] address, data_in;
                            ^
test.sv:10:20: warning: [STYLE-2] port 'data_out' is not correctly suffixed with suffix: "_o"
  output reg [7:0] data_out;
                   ^
test.sv:11:14: warning: [STYLE-2] port 'read_write' is not correctly suffixed with suffix: "_i"
  input wire read_write, chip_en;
             ^
test.sv:11:26: warning: [STYLE-2] port 'chip_en' is not correctly suffixed with suffix: "_i"
  input wire read_write, chip_en;
                         ^

[AlwaysCombNonBlocking] PASS
[GenerateNamed] PASS
[AlwaysFFBlocking] PASS
[XilinxDoNotCareValues] PASS
[CastSignedIndex] PASS
[AlwaysFFAssignmentOutsideConditional] PASS
[NoDotStarInPortConnection] WARN
test.sv:33:18: warning: [STYLE-11] use of .* in port connection list
memory m_memory(.*);

slang-netlist Link to heading

From slang docs:

slang-netlist is a library and tool for analyzing the source-level static connectivity of a design.

<   -E,--preprocess                                     Only run the preprocessor (and print preprocessed files to stdout)
<   --macros-only                                       Print a list of found macros and exit
<   --parse-only                                        Stop after parsing input files, don't perform elaboration or type checking
<   --disable-analysis                                  Disables post-elaboration analysis passes,which prevents some diagnostics from being issued
<   --comments                                          Include comments in preprocessed output (with -E)
<   --directives                                        Include compiler directives in preprocessed output (with -E)
<   --obfuscate-ids                                     Randomize all identifiers in preprocessed output (with -E)
---
>   -d,--debug                                          Output debugging information
>   -c,--comb-loops                                     Detect combinatorial loops
>   --unroll-for-loops                                  Unroll procedural for loops
87,89c83,86
<   --ast-json-source-info                              When dumping AST to JSON, include source line and file information
<   --ast-json-detailed-types                           When dumping AST to JSON, expand out all type information
<   --time-trace <path>                                 Do performance profiling of the slang compiler and output the results to the given file in Chrome Event Tracing JSON format
---
>   --netlist-dot <file>                                Dump the netlist in DOT format to the specified file, or '-' for stdout
>   --from <name>                                       Specify a start point from which to trace a path
>   --to <name>                                         Specify a finish point to trace a path to
> 
./slang/build/bin/slang-netlist test.sv -d --netlist-dot out.dot
dot -Tsvg out.dot -o out.svg

Example image

slang-hier Link to heading

From slang docs:

A tool that can display information about a Verilog hierarchy.

<   -q,--quiet                                          Suppress non-essential output
<   -E,--preprocess                                     Only run the preprocessor (and print preprocessed files to stdout)
<   --macros-only                                       Print a list of found macros and exit
<   --parse-only                                        Stop after parsing input files, don't perform elaboration or type checking
<   --disable-analysis                                  Disables post-elaboration analysis passes,which prevents some diagnostics from being issued
<   --comments                                          Include comments in preprocessed output (with -E)
<   --directives                                        Include compiler directives in preprocessed output (with -E)
<   --obfuscate-ids                                     Randomize all identifiers in preprocessed output (with -E)
<   --ast-json <file>                                   Dump the compiled AST in JSON format to the specified file, or '-' for stdout
<   --ast-json-scope <path>                             When dumping AST to JSON, include only the scopes specified by the given hierarchical paths
<   --ast-json-source-info                              When dumping AST to JSON, include source line and file information
<   --ast-json-detailed-types                           When dumping AST to JSON, expand out all type information
<   --time-trace <path>                                 Do performance profiling of the slang compiler and output the results to the given file in Chrome Event Tracing JSON format
---
>   --params                                            Display instance parameter values
>   --max-depth <depth>                                 Maximum instance depth to be printed
>   --inst-prefix <inst-prefix>                         Skip all instance subtrees not under this prefix (inst.sub_inst...)
>   --inst-regex <inst-regex>                           Show only instances matched by regex (scans whole tree)
>   --custom-format <fmt::format string>                Use libfmt-style strings to format output with {inst}, {module}, {file} as argument names
$ ./slang/build/bin/slang-hier test.sv
Module="top" Instance="top" File="test.sv" 
Module="memory" Instance="top.m_memory" File="test.sv" 
Top level design units:
    top

Build succeeded: 0 errors, 0 warnings

Python Bindings Link to heading

Another super useful way to use slang is through the Python bindings

pip install pyslang

A small example from the documentation is memory module

module memory(
    address,
    data_in,
    data_out,
    read_write,
    chip_en
  );

  input wire [7:0] address, data_in;
  output reg [7:0] data_out;
  input wire read_write, chip_en;

  reg [7:0] mem [0:255];

  always @ (address or data_in or read_write or chip_en)
    if (read_write == 1 && chip_en == 1) begin
      mem[address] = data_in;
  end

  always @ (read_write or chip_en or address)
    if (read_write == 0 && chip_en)
      data_out = mem[address];
    else
      data_out = 0;

endmodule

module top;
  reg [7:0] address, data_in;
  wire [7:0] data_out;
  reg read_write, chip_en;
  memory m_memory(.*);
endmodule

It can iterate the module memory and/or the ports. Interestingly, it can simulate SV expressions. I wonder how far I can push it.

import pyslang

tree = pyslang.SyntaxTree.fromFile('test.sv')
mod = tree.root.members[0]
print(mod.header.name.value)
print(mod.members[0].kind)
print(mod.members[1].header.dataType)

session = pyslang.ScriptSession()
session.eval("logic bit_arr [16] = '{0:1, 1:1, 2:1, default:0};")
result = session.eval("bit_arr.sum with ( int'(item) );")
print(result)
memory
SyntaxKind.PortDeclaration
 reg [7:0]
3