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
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