Tools
Documentation for ancillary tools included with slang
slang includes several tools besides the main command line driver tool (which is documented in Command Line Reference ). Many of these tools are contributions that may not be as complete or well supported as the main slang library.
slang-hier
A tool that can display information about a Verilog hierarchy.
This tool accepts the standard set of slang driver command line options, which lets you configure your design. Then the tool will display information like module instance names and resolved parameter values.
Additional options to control output:
--params
Include instance parameter values in the output.
--max-depth <depth>
The maximum instance depth of the hierarchy to be printed. Everything deeper than that will be ignored.
--inst-prefix <prefix>
A hierarchical path indicating which hierarchy to display. All parts of the design not under this prefix will be ignored.
--inst-regex <regex>
Only instances that match the given regex (anywhere in the design tree) will be shown. All others will be skipped.
--custom-format <fmt>
A libfmt style format string that controls how the output is printed. Use {inst}
, {module}
, {file}
as argument names in the string.
slang-netlist
Warning
slang-netlist is a work in progress and may not work as expected. Check TODO.md for a list of some new features and fixes that are planned. If you encounter a problem, please submit a bug report via Issues.
slang-netlist is a library and tool for analysing the source-level static connectivity of a design. This capability can be useful, for example, to develop structural checks or to investigate timing paths, rather than having to use synthesis to obtain a gate-level netlist.
Using the example of a simple adder:
module adder #(parameter p_width = 32)( input logic [p_width-1:0] i_a, input logic [p_width-1:0] i_b, output logic [p_width-1:0] o_sum, output logic o_co ); logic [p_width-1:0] sum; logic co; assign {co, sum} = i_a + i_b; assign o_sum = sum; assign o_co = co; endmodule
The slang-netlist command-line tool can be used to trace paths through the design, such as:
➜ slang-netlist adder.sv --from adder.i_a --to adder.o_sum -q adder.sv:10:22: note: variable i_a read from assign {co, sum} = i_a + i_b; ^~~ adder.sv:10:15: note: variable sum assigned to assign {co, sum} = i_a + i_b; ^~~ adder.sv:11:18: note: variable sum read from assign o_sum = sum; ^~~ adder.sv:11:10: note: variable o_sum assigned to assign o_sum = sum; ^~~~~
slang-reflect
slang-reflect is a tool that aims to help developers to remove the burden of creating C/C++
representations of SystemVerilog structs, enums and parameters. Its behavior is inspired from the verilator public option provided by verilator
, and it is of a great use when building testbenches using C++/SystemC
.
To indicate which SystemVerilog types we want to make public we need to annotate its declaration with // public
or // verilator public
. For example:
typedef struct { logic k; } pkg_struct_comment /* public */; typedef enum {ONE = 2, TWO, THREE} my_enum /* public */; localparam my_local /* public */ = 8'h42;
For all types that have been declared public
inside the same package
or module
, the tool will create a header file with a namespace inside it, both of them with the name of the SystemVerilog container. Inside the namespace, there will be the declaration of the public
types. For example, a SystemVerilog file that looks like this:
package foo; localparam foo_local /* public */ = 12; endpackage package bar; localparam bar_local /* public */ = 12; endpackage module top; localparam top_local /* verilator public */ = 12; endmodule
The auto-generated files and code will look like this:
// bar.h #pragma once #include ... namespace bar { static constexpr uint32_t bar_local = 12; } // foo.h #pragma once #include ... namespace foo { static constexpr uint32_t foo_local = 12; } // top.h #pragma once #include ... namespace top { static constexpr uint32_t top_local = 12; }
CPP Reserved Keywords
Since not all CPP kewords are SystemVerilog keywords, if any name of a member, enum, struct or parameter collides with a reserved C++ keyword, it will be renamed with a _
in front. For example if a member of a struct is named operator
it will be emitted as _operator
.
SystemVerilog Namspace References
In order to handle references from other packages that are done using the import foo::*
or foo::my_local
, the foo header will be included into the headers where their types or parameters are being used.
package bar; typedef struct packed { logic k; } bar_struct /* public */; endpackage package foo; typedef struct { bar::bar_struct foo_struct; } foo_struct /* public */; endpackage
will transpile into
// bar.h #pragma once #include ... namespace bar { struct bar_struct { bool k; ... }; } // foo.h #pragma once #include ... #include "bar.h" namespace foo { struct foo_struct { bar::bar_struct foo_struct; ... }; }
SystemVerilog Typedefs
When the type of a member of a struct comes from a typedef, the tool will resolve the typedef and emit the base type. For example:
package bar; typedef enum {ONE = 5, TWO, THREE} my_enum /* public */; typedef my_enum t_enum; typedef struct packed { t_enum e; } bar_struct /* public */; endpackage
// bar.h #pragma once #include ... namespace bar { struct my_enum { enum Type : uint32_t { ONE = 5, TWO = 6, THREE = 7 }; ... }; struct bar_struct { my_enum e; ... }; }
Emitted Headers
The includes that are emitted along with each file are:
#include <ostream> #include <cstddef> #include <cstdint> #include <string> #include <sstream> #include <systemc.h>
The #include <systemc.h>
can be removed if the --no-sc
argument is provided to the tool.
SystemVerilog Parameters
SystemVerilog parameters are transpiled as a static constexpr
with the type being uint32_t
or uint64_t
. For example
package foo; localparam bar /* public */ = 42; endpackage
namespace foo { static constexpr uint32_t bar = 42; }
SystemVerilog Enums
SystemVerilog enums are transpiled as a struct
in order to provide convenient methods to serialize and deserialize the enum into the underlying type. For example:
package bar; typedef enum {ONE = 5, TWO, THREE} my_enum /* public */; endpackage
// bar.h #pragma once #include ... namespace bar { struct my_enum { enum Type : uint32_t { ONE = 5, TWO = 6, THREE = 7 }; static constexpr size_t _size = 32; Type type; my_enum() = default; my_enum (uint32_t data) { switch (data) { case 5: type = Type::ONE; break; case 6: type = Type::TWO; break; case 7: type = Type::THREE; break; default: throw std::runtime_error("Can not create my_enum from provided value"); } } my_enum (Type& type) { this->type = type; } friend std::ostream& operator<<(std::ostream& os, const my_enum& data) { switch (data.type) { case Type::ONE: os << "ONE"; break; case Type::TWO: os << "TWO"; break; case Type::THREE: os << "THREE"; break; } return os; } my_enum& operator=(const Type t) { this->type = t; return *this; } operator uint64_t() const { return static_cast<uint64_t>(type); } Type operator() () const { return type; } }; }
SystemVerilog Structs
SystemVerilog structs are transpiled as a struct
that provide convenient methods to serialize, deserialize and print the struct.
Members of the struct that are not another struct or enum, will be transpiled as uint32_t
, uint64_t
or sc_bv<N>
. In the case the option --no-sc
has been provided to the tool and there is a member bigger than 64 bits, or the struct itself has a size bigger than 64 bits, the tool will report an error asking for enabling the SystemC support.
For example:
package bar; typedef struct packed { logic [7:0] b; logic [15:0] h; logic [31:0] w; } bar_struct /* public */; endpackage
// bar.h #pragma once #include ... namespace bar { struct bar_struct { uint32_t w; uint32_t h; uint32_t b; static constexpr size_t w_s = 0; static constexpr size_t w_w = 32; static constexpr size_t h_s = 32; static constexpr size_t h_w = 16; static constexpr size_t b_s = 48; static constexpr size_t b_w = 8; static constexpr size_t _size = 56; bar_struct() = default; bar_struct(const uint64_t& data) { w = (data >> w_s) & (~0ULL >> (64 - 32)); h = (data >> h_s) & (~0ULL >> (64 - 16)); b = (data >> b_s) & (~0ULL >> (64 - 8)); } bar_struct(const sc_bv<56>& data) { w = data.range(w_s + w_w - 1, w_s).to_uint64(); h = data.range(h_s + h_w - 1, h_s).to_uint64(); b = data.range(b_s + b_w - 1, b_s).to_uint64(); } operator uint64_t() const { uint64_t ret = 0; ret |= static_cast<uint64_t>(w) << w_s; ret |= static_cast<uint64_t>(h) << h_s; ret |= static_cast<uint64_t>(b) << b_s; return ret; } operator sc_bv<56>() const { auto ret = sc_bv<56>(); ret.range(w_s + w_w - 1, w_s) = w; ret.range(h_s + h_w - 1, h_s) = h; ret.range(b_s + b_w - 1, b_s) = b; return ret; } std::string to_string() const { std::stringstream ss; ss << "w" << " = " << w; ss << " h" << " = " << h; ss << " b" << " = " << b; return ss.str(); } friend std::ostream& operator<<(std::ostream& os, const bar_struct& data) { os << data.to_string(); return os; } static uint32_t get_w (const uint64_t& data) { return (data >> w_s) & (~0ULL >> (64 - 32)); } static uint32_t get_h (const uint64_t& data) { return (data >> h_s) & (~0ULL >> (64 - 16)); } static uint32_t get_b (const uint64_t& data) { return (data >> b_s) & (~0ULL >> (64 - 8)); } }; }
rewriter
A simple tool that shows using the syntax API to read in a source file, parse it, and write it back out again. This can be used for testing purposes, to make sure syntax trees round trip correctly, or as a basic example of working with the syntax API.
Usage:
rewriter <file-name>
slang-tidy
A SystemVerilog linter
Configuration File
Slang Tidy can be configured using a .slang-tidy
. This file can be provided using the --config-file
argument or the slang-tidy
tool will search for it from the path where it has been called until the root directory.
Configuration file grammar
The grammar of the config file is the following
config ::= section+ section ::= checks_config_section | checks_section checks_section ::= "Checks:" [EOL] rule+ rule ::= ["-"] ((check_or_all ",")* | check_or_all) END check_or_all ::= "*" | check_group "-" check_name_or_all check_group ::= identifier check_name_or_all ::= "*" | identifier checks_config_section ::= "CheckConfigs:" [EOL] config+ config ::= ((config_tuple ",")* | config_tuple) END config_tuple ::= config_name ":" config_value config_name ::= identifier config_value ::= expression | "[" ((expression ",")* expression)? "]" identifier ::= { A-Z | a-z }+ expression ::= { A-Z | a-z | 0-9 | "_" }+ EOL ::= '\n' | '\r' | '\r\n' END ::= EOL* | EOF
Configuration file
The syntax of the .slang-tidy
file is similar to the syntax of the .clang-tidy
. There are two main sections: Checks
and CheckConfigs
, that can appear as many times as needed in the config file.
The Checks
section is a comma separated list of globs with an optional -
prefix, that will enable or disable the specified check or group of checks.
Enable a check: synthesis-only-assigned-on-reset Disable a check: -synthesis-only-assigned-on-reset Enable a group: synthesis-* Disable a group: -synthesis-* Enable all: * Disable all: -*
The CheckConfigs
is a dictionary like, config: value
, comma separated list of options for the different checks. The available options are:
Config | Type | Default |
---|---|---|
clkName | string | clk_i |
clkNameRegexString | string | "clk\S*|clock\S*" |
resetName | string | rst_ni |
resetIsActiveHigh | bool | true |
inputPortSuffix | [string] | [_i] |
outputPortSuffix | [string] | [_o] |
inoutPortSuffix | [string] | [_io] |
moduleInstantiationPrefix | string | i_ |
An example of a possible configuration file:
Checks: -*, synthesis-only-assigned-on-reset, style-enforce-port-suffix CheckConfigs: clkName: clk, clkNameRegexString: "clk_signal\S*|clock_port\S*", resetIsActiveHigh: false, inputPortSuffix: _k, outputPortSuffix: _p
How to add a new check
- Create a new
cpp
file with the name of the check in CamelCase format inside the check kind folder. - Inside the new
cpp
file create a class that inherits fromTidyChecks
. Use thecheck
function to implement the code that will perform the check in the AST. - Use the
REGISTER
macro to register the new check in the factory. - Create the new tidy diagnostic in the
TidyDiags.h
file. - Add the new check to the corresponding map in the
TidyConfig
constructor. - Update the documentation accordingly
- Add the new
cpp
file to CMakeLists.txt