├── .clang-format ├── .clang-tidy ├── .editorconfig ├── .flake8 ├── .git_archival.txt ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── copilot-instructions.md └── workflows │ ├── build.yml │ ├── copilot-setup-steps.yml │ ├── coverage.yml │ ├── docs.yml │ ├── python-build.yml │ └── python-dist.yml ├── .gitignore ├── .lcovrc ├── .pre-commit-config.yaml ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── CMakeLists.txt ├── CMakePresets.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSES ├── BSL-1.0.txt └── MIT.txt ├── README.md ├── REUSE.toml ├── bindings ├── CMakeLists.txt └── python │ ├── ASTBindings.cpp │ ├── AnalysisBindings.cpp │ ├── CompBindings.cpp │ ├── ExpressionBindings.cpp │ ├── NumericBindings.cpp │ ├── PyVisitors.h │ ├── StatementBindings.cpp │ ├── SymbolBindings.cpp │ ├── SyntaxBindings.cpp │ ├── TypeBindings.cpp │ ├── UtilBindings.cpp │ ├── pyslang.cpp │ └── pyslang.h ├── cmake ├── coverage.cmake ├── gitversion.cmake ├── gitversion.cmake.in ├── install-rules.cmake └── slangConfig.cmake.in ├── codecov.yml ├── docs ├── CMakeLists.txt ├── Doxyfile.in ├── api-reference.dox ├── building.dox ├── command-line-ref.dox ├── common-components.dox ├── cppreference-doxygen-web.tag.xml ├── developer-guide.dox ├── diagnostics.dox ├── grammar.md ├── language-support.dox ├── mainpage.dox ├── namespaces.dox ├── parsing.dox ├── source-management.dox ├── tools.dox └── user-manual.dox ├── external ├── BS_thread_pool.hpp ├── CMakeLists.txt ├── boost_concurrent.hpp ├── boost_unordered.hpp ├── expected.hpp └── ieee1800 │ ├── sv_vpi_user.h │ ├── svdpi.h │ ├── vpi_compatibility.h │ └── vpi_user.h ├── include └── slang │ ├── analysis │ ├── AbstractFlowAnalysis.h │ ├── AnalysisManager.h │ ├── AnalysisOptions.h │ ├── AnalyzedAssertion.h │ ├── AnalyzedProcedure.h │ ├── CaseDecisionDag.h │ ├── ClockInference.h │ ├── DataFlowAnalysis.h │ ├── DriverTracker.h │ └── ValueDriver.h │ ├── ast │ ├── ASTContext.h │ ├── ASTDiagMap.h │ ├── ASTSerializer.h │ ├── ASTVisitor.h │ ├── Bitstream.h │ ├── Compilation.h │ ├── Constraints.h │ ├── EvalContext.h │ ├── Expression.h │ ├── HierarchicalReference.h │ ├── LSPUtilities.h │ ├── LValue.h │ ├── Lookup.h │ ├── OpaqueInstancePath.h │ ├── Patterns.h │ ├── SFormat.h │ ├── Scope.h │ ├── ScriptSession.h │ ├── SemanticFacts.h │ ├── Statement.h │ ├── Symbol.h │ ├── SystemSubroutine.h │ ├── TimingControl.h │ ├── expressions │ │ ├── AssertionExpr.h │ │ ├── AssignmentExpressions.h │ │ ├── CallExpression.h │ │ ├── ConversionExpression.h │ │ ├── LiteralExpressions.h │ │ ├── MiscExpressions.h │ │ ├── Operator.h │ │ ├── OperatorExpressions.h │ │ └── SelectExpressions.h │ ├── statements │ │ ├── ConditionalStatements.h │ │ ├── LoopStatements.h │ │ └── MiscStatements.h │ ├── symbols │ │ ├── AttributeSymbol.h │ │ ├── BlockSymbols.h │ │ ├── ClassSymbols.h │ │ ├── CompilationUnitSymbols.h │ │ ├── CoverSymbols.h │ │ ├── InstanceSymbols.h │ │ ├── MemberSymbols.h │ │ ├── ParameterSymbols.h │ │ ├── PortSymbols.h │ │ ├── SpecifySymbols.h │ │ ├── SubroutineSymbols.h │ │ ├── SymbolBuilders.h │ │ ├── ValueSymbol.h │ │ └── VariableSymbols.h │ └── types │ │ ├── AllTypes.h │ │ ├── DeclaredType.h │ │ ├── NetType.h │ │ ├── Type.h │ │ └── TypePrinter.h │ ├── diagnostics │ ├── DiagArgFormatter.h │ ├── DiagnosticClient.h │ ├── DiagnosticEngine.h │ ├── Diagnostics.h │ ├── JsonDiagnosticClient.h │ └── TextDiagnosticClient.h │ ├── driver │ ├── Driver.h │ └── SourceLoader.h │ ├── numeric │ ├── ConstantValue.h │ ├── MathUtils.h │ ├── SVInt.h │ └── Time.h │ ├── parsing │ ├── Lexer.h │ ├── LexerFacts.h │ ├── NumberParser.h │ ├── Parser.h │ ├── ParserBase.h │ ├── ParserMetadata.h │ ├── Preprocessor.h │ └── Token.h │ ├── syntax │ ├── SyntaxFacts.h │ ├── SyntaxNode.h │ ├── SyntaxPrinter.h │ ├── SyntaxTree.h │ └── SyntaxVisitor.h │ ├── text │ ├── CharInfo.h │ ├── FormatBuffer.h │ ├── Glob.h │ ├── Json.h │ ├── SourceLocation.h │ └── SourceManager.h │ └── util │ ├── Bag.h │ ├── BumpAllocator.h │ ├── CommandLine.h │ ├── ConcurrentMap.h │ ├── CopyPtr.h │ ├── Enum.h │ ├── FlatMap.h │ ├── Function.h │ ├── Hash.h │ ├── IntervalMap.h │ ├── Iterator.h │ ├── LanguageVersion.h │ ├── OS.h │ ├── PointerIntPair.h │ ├── PoolAllocator.h │ ├── Random.h │ ├── ScopeGuard.h │ ├── SmallMap.h │ ├── SmallVector.h │ ├── String.h │ ├── TimeTrace.h │ ├── TypeTraits.h │ ├── Util.h │ └── VersionInfo.h ├── pyproject.toml ├── pyslang ├── docs │ ├── conf.py │ └── pages │ │ └── index.rst ├── examples │ └── driver.py └── tests │ ├── some_file.svh │ ├── test_accessing_spans.py │ ├── test_analysis.py │ ├── test_basics.py │ ├── test_custom_systasks.py │ ├── test_extract_comments.py │ ├── test_syntax_rewriter.py │ ├── test_syntax_tree_equality.py │ └── test_visitors.py ├── scripts ├── diagnostic_gen.py ├── diagnostics.txt ├── grammar.txt ├── grammar_gen.py ├── sv-lang.pc.in ├── syntax.txt ├── syntax_gen.py ├── systemnames.txt ├── tokenkinds.txt ├── triviakinds.txt ├── warning_docs.txt └── win32.manifest ├── slang.natvis ├── source ├── CMakeLists.txt ├── analysis │ ├── AbstractFlowAnalysis.cpp │ ├── AnalysisManager.cpp │ ├── AnalysisScopeVisitor.h │ ├── AnalyzedAssertion.cpp │ ├── AnalyzedProcedure.cpp │ ├── CaseDecisionDag.cpp │ ├── ClockInference.cpp │ ├── DataFlowAnalysis.cpp │ ├── DriverTracker.cpp │ ├── NonProceduralExprVisitor.h │ └── ValueDriver.cpp ├── ast │ ├── ASTContext.cpp │ ├── ASTDiagMap.cpp │ ├── ASTSerializer.cpp │ ├── Bitstream.cpp │ ├── CMakeLists.txt │ ├── Compilation.cpp │ ├── Constraints.cpp │ ├── ElabVisitors.h │ ├── EvalContext.cpp │ ├── Expression.cpp │ ├── FmtHelpers.cpp │ ├── FmtHelpers.h │ ├── HierarchicalReference.cpp │ ├── LSPUtilities.cpp │ ├── LValue.cpp │ ├── Lookup.cpp │ ├── OpaqueInstancePath.cpp │ ├── Patterns.cpp │ ├── SFormat.cpp │ ├── Scope.cpp │ ├── ScriptSession.cpp │ ├── SemanticFacts.cpp │ ├── Statement.cpp │ ├── Symbol.cpp │ ├── SystemSubroutine.cpp │ ├── TimingControl.cpp │ ├── builtins │ │ ├── ArrayMethods.cpp │ │ ├── Builtins.h │ │ ├── ConversionFuncs.cpp │ │ ├── CoverageFuncs.cpp │ │ ├── EnumMethods.cpp │ │ ├── GateTypes.cpp │ │ ├── MathFuncs.cpp │ │ ├── MiscSystemFuncs.cpp │ │ ├── NonConstFuncs.cpp │ │ ├── QueryFuncs.cpp │ │ ├── StdPackage.cpp │ │ ├── StringMethods.cpp │ │ └── SystemTasks.cpp │ ├── expressions │ │ ├── AssertionExpr.cpp │ │ ├── AssignmentExpressions.cpp │ │ ├── CallExpression.cpp │ │ ├── ConversionExpression.cpp │ │ ├── LiteralExpressions.cpp │ │ ├── MiscExpressions.cpp │ │ ├── Operator.cpp │ │ ├── OperatorExpressions.cpp │ │ └── SelectExpressions.cpp │ ├── statements │ │ ├── ConditionalStatements.cpp │ │ ├── LoopStatements.cpp │ │ └── MiscStatements.cpp │ ├── symbols │ │ ├── AttributeSymbol.cpp │ │ ├── BlockSymbols.cpp │ │ ├── ClassSymbols.cpp │ │ ├── CompilationUnitSymbols.cpp │ │ ├── CoverSymbols.cpp │ │ ├── InstanceSymbols.cpp │ │ ├── MemberSymbols.cpp │ │ ├── ParameterBuilder.cpp │ │ ├── ParameterBuilder.h │ │ ├── ParameterSymbols.cpp │ │ ├── PortSymbols.cpp │ │ ├── SpecifySymbols.cpp │ │ ├── SubroutineSymbols.cpp │ │ ├── SymbolBuilders.cpp │ │ ├── ValueSymbol.cpp │ │ └── VariableSymbols.cpp │ └── types │ │ ├── AllTypes.cpp │ │ ├── DeclaredType.cpp │ │ ├── NetType.cpp │ │ ├── Type.cpp │ │ └── TypePrinter.cpp ├── diagnostics │ ├── DiagnosticClient.cpp │ ├── DiagnosticEngine.cpp │ ├── Diagnostics.cpp │ ├── JsonDiagnosticClient.cpp │ └── TextDiagnosticClient.cpp ├── driver │ ├── Driver.cpp │ └── SourceLoader.cpp ├── numeric │ ├── ConstantValue.cpp │ ├── SVInt.cpp │ ├── SVIntHelpers.h │ └── Time.cpp ├── parsing │ ├── Lexer.cpp │ ├── LexerFacts.cpp │ ├── NumberParser.cpp │ ├── Parser.cpp │ ├── ParserBase.cpp │ ├── ParserMetadata.cpp │ ├── Parser_expressions.cpp │ ├── Parser_members.cpp │ ├── Parser_statements.cpp │ ├── Preprocessor.cpp │ ├── Preprocessor_macros.cpp │ ├── Preprocessor_pragmas.cpp │ └── Token.cpp ├── syntax │ ├── SyntaxFacts.cpp │ ├── SyntaxNode.cpp │ ├── SyntaxPrinter.cpp │ ├── SyntaxTree.cpp │ └── SyntaxVisitor.cpp ├── text │ ├── CharInfo.cpp │ ├── Glob.cpp │ ├── Json.cpp │ ├── SourceLocation.cpp │ └── SourceManager.cpp └── util │ ├── BumpAllocator.cpp │ ├── CommandLine.cpp │ ├── IntervalMap.cpp │ ├── OS.cpp │ ├── SmallVector.cpp │ ├── String.cpp │ ├── TimeTrace.cpp │ ├── Util.cpp │ └── VersionInfo.cpp.in ├── tests ├── CMakeLists.txt ├── regression │ ├── CMakeLists.txt │ ├── all.sv │ ├── delayed_reg.v │ └── wire_module.v ├── snippets.md └── unittests │ ├── CMakeLists.txt │ ├── DriverTests.cpp │ ├── FileTests.cpp │ ├── Test.cpp │ ├── Test.h │ ├── analysis │ ├── AnalysisTests.h │ ├── AssertionAnalysisTests.cpp │ ├── CaseAnalysisTests.cpp │ ├── DFATests.cpp │ ├── MultiAssignTests.cpp │ └── UnusedTests.cpp │ ├── ast │ ├── AssertionTests.cpp │ ├── ClassTests.cpp │ ├── ConfigTests.cpp │ ├── CoverTests.cpp │ ├── EvalTests.cpp │ ├── ExpressionTests.cpp │ ├── HierarchyTests.cpp │ ├── InterfaceTests.cpp │ ├── LookupTests.cpp │ ├── MemberTests.cpp │ ├── ParameterTests.cpp │ ├── PortTests.cpp │ ├── PrimitiveTests.cpp │ ├── SerializerTests.cpp │ ├── StatementTests.cpp │ ├── SubroutineTests.cpp │ ├── SystemFuncTests.cpp │ ├── TypeTests.cpp │ └── WarningTests.cpp │ ├── data │ ├── cmd.f │ ├── cmd2.f │ ├── cmd3.f │ ├── cmd4.f │ ├── file_defn.svh │ ├── file_uses_define_in_file_with_no_eol.sv │ ├── file_uses_defn.svh │ ├── file_with_no_eol.sv │ ├── include.svh │ ├── include_once.svh │ ├── infinite.f │ ├── infinite.map │ ├── infinite_chain.svh │ ├── library │ │ ├── lib.map │ │ ├── libmod.qv │ │ ├── other.sv │ │ └── pkg.sv │ ├── libtest │ │ ├── mod1.sv │ │ ├── mod2.sv │ │ ├── testlib.map │ │ └── top.sv │ ├── local.svh │ ├── nested │ │ ├── file.svh │ │ ├── macro.svh │ │ └── nested_local.svh │ ├── system │ │ ├── system.map │ │ └── system.svh │ ├── test.sv │ ├── test2.sv │ ├── test3.sv │ ├── test4.sv │ ├── test5.sv │ ├── test6.sv │ └── unit.f │ ├── main.cpp │ ├── parsing │ ├── DiagnosticTests.cpp │ ├── ExpressionParsingTests.cpp │ ├── LexerTests.cpp │ ├── MemberParsingTests.cpp │ ├── PreprocessorTests.cpp │ ├── StatementParsingTests.cpp │ └── VisitorTests.cpp │ └── util │ ├── CommandLineTests.cpp │ ├── IntervalMapTests.cpp │ ├── NumericTests.cpp │ ├── SmallVectorTests.cpp │ └── UtilTests.cpp └── tools ├── CMakeLists.txt ├── driver ├── CMakeLists.txt └── slang_main.cpp ├── hier ├── CMakeLists.txt ├── README.md └── hier.cpp ├── netlist ├── CMakeLists.txt ├── README.md ├── TODO.md ├── include │ ├── CombLoops.h │ ├── Config.h │ ├── CycleDetector.h │ ├── Debug.h │ ├── DepthFirstSearch.h │ ├── DirectedGraph.h │ ├── Netlist.h │ ├── NetlistPath.h │ ├── NetlistVisitorOptions.hpp │ ├── PathFinder.h │ └── visitors │ │ ├── ContinuousAssignVisitor.hpp │ │ ├── GenerateBlockVisitor.hpp │ │ ├── InstanceDeclVisitor.hpp │ │ ├── InstanceVisitor.hpp │ │ ├── NetlistVisitor.h │ │ ├── ProceduralBlockVisitor.hpp │ │ └── VariableReferenceVisitor.hpp ├── netlist.cpp ├── source │ └── Netlist.cpp └── tests │ ├── CMakeLists.txt │ ├── CombLoopsTests.cpp │ ├── CycleDetectorTests.cpp │ ├── DepthFirstSearchTests.cpp │ ├── DirectedGraphTests.cpp │ ├── NameTests.cpp │ ├── NetlistTest.h │ ├── PathTests.cpp │ ├── SplitTests.cpp │ └── VariableSelectorsTests.cpp ├── reflect ├── CMakeLists.txt ├── README.md ├── include │ ├── ASTVisitors.h │ ├── CppEmitter.h │ ├── SvEnum.h │ ├── SvGeneric.h │ ├── SvLocalParam.h │ ├── SvStruct.h │ ├── SvType.h │ ├── SvTypeReflector.h │ └── SvUnion.h └── src │ ├── SvEnum.cpp │ ├── SvLocalParam.cpp │ ├── SvStruct.cpp │ ├── SvType.cpp │ ├── SvTypeReflector.cpp │ ├── SvUnion.cpp │ └── reflect.cpp ├── rewriter ├── CMakeLists.txt ├── README.md └── rewriter.cpp ├── threadtest ├── CMakeLists.txt ├── README.md └── threadtest.cpp └── tidy ├── CMakeLists.txt ├── README.md ├── include ├── ASTHelperVisitors.h ├── TidyConfig.h ├── TidyConfigParser.h ├── TidyConfigPrinter.h ├── TidyDiags.h ├── TidyFactory.h └── TidyKind.h ├── src ├── ASTHelperVisitors.cpp ├── TidyConfig.cpp ├── TidyConfigParser.cpp ├── style │ ├── AlwaysCombNamed.cpp │ ├── AlwaysCombNonBlocking.cpp │ ├── AlwaysFFBlocking.cpp │ ├── EnforceModuleInstantiationPrefix.cpp │ ├── EnforcePortPrefix.cpp │ ├── EnforcePortSuffix.cpp │ ├── GenerateNamed.cpp │ ├── NoDotStarInPortConnection.cpp │ ├── NoDotVarInPortConnection.cpp │ ├── NoImplicitPortNameInPortConnection.cpp │ ├── NoLegacyGenerate.cpp │ ├── NoOldAlwaysSyntax.cpp │ └── OnlyANSIPortDecl.cpp ├── synthesis │ ├── AlwaysFFAssignmentOutsideConditional.cpp │ ├── CastSignedIndex.cpp │ ├── NoLatchesOnDesign.cpp │ ├── OnlyAssignedOnReset.cpp │ ├── RegisterHasNoReset.cpp │ ├── UndrivenRange.cpp │ ├── UnusedSensitiveSignal.cpp │ └── XilinxDoNotCareValues.cpp └── tidy.cpp └── tests ├── AlwaysCombNamedTest.cpp ├── AlwaysCombNonBlockingTest.cpp ├── AlwaysFFAssignmentOutsideConditionalTest.cpp ├── AlwaysFFBlockingTest.cpp ├── CMakeLists.txt ├── CastSignedIndexTest.cpp ├── EnforceModuleInstantiationTest.cpp ├── EnforcePortPrefixTest.cpp ├── EnforcePortSuffixTest.cpp ├── GenerateNamedTest.cpp ├── NoDotStarInPortConnectionTest.cpp ├── NoDotVarInPortConnectionTest.cpp ├── NoImplicitPortNameInPortConnectionTest.cpp ├── NoLatchesOnDesignTest.cpp ├── NoLegacyGenerateTest.cpp ├── NoOldAlwaysSyntaxTest.cpp ├── OnlyANSIPortDecl.cpp ├── OnlyAssignedOnResetTest.cpp ├── PrintConfigTest.cpp ├── RegisterHasNoResetTest.cpp ├── TidyConfigParserTest.cpp ├── TidyTest.h ├── UndrivenRangeTest.cpp ├── UnusedSensitiveSignalTest.cpp └── XilinxDoNotCareValuesTest.cpp /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -*, 3 | clang-analyzer-*, 4 | bugprone-*, 5 | performance-*, 6 | modernize-*, 7 | portability-*, 8 | misc-*, 9 | -modernize-use-auto, 10 | -modernize-use-trailing-return-type, 11 | -modernize-raw-string-literal, 12 | -modernize-use-nodiscard, 13 | -modernize-avoid-c-arrays, 14 | -modernize-use-emplace, 15 | -modernize-return-braced-init-list, 16 | -modernize-loop-convert, 17 | -modernize-use-designated-initializers, 18 | -modernize-use-ranges, 19 | -modernize-use-integer-sign-comparison, 20 | -bugprone-suspicious-semicolon, 21 | -bugprone-branch-clone, 22 | -bugprone-sizeof-expression, 23 | -bugprone-easily-swappable-parameters, 24 | -bugprone-implicit-widening-of-multiplication-result, 25 | -bugprone-not-null-terminated-result, 26 | -bugprone-unchecked-optional-access, 27 | -bugprone-assignment-in-if-condition, 28 | -bugprone-switch-missing-default-case, 29 | -bugprone-suspicious-stringview-data-usage, 30 | -bugprone-return-const-ref-from-parameter, 31 | -clang-analyzer-cplusplus.NewDelete*, 32 | -clang-analyzer-cplusplus.InnerPointer, 33 | -clang-analyzer-core.NullDereference, 34 | -clang-analyzer-core.NonNullParamChecker, 35 | -clang-analyzer-core.UndefinedBinaryOperatorResult, 36 | -clang-analyzer-core.uninitialized.Assign, 37 | -clang-analyzer-optin.cplusplus.UninitializedObject, 38 | -misc-non-private-member-variables-in-classes, 39 | -misc-no-recursion, 40 | -misc-const-correctness, 41 | -misc-use-anonymous-namespace, 42 | -misc-include-cleaner, 43 | -misc-header-include-cycle, 44 | -misc-use-internal-linkage, 45 | -performance-no-int-to-ptr, 46 | -performance-enum-size, 47 | -portability-simd-intrinsics, 48 | -portability-template-virtual-member-function, 49 | -performance-unnecessary-value-param 50 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | extend-ignore = E203,E501,F401,F403,F405 4 | -------------------------------------------------------------------------------- /.git_archival.txt: -------------------------------------------------------------------------------- 1 | node: 647a72b7b7fd4f8c6b2dfa496a20bc30bbfaef8a 2 | node-date: 2025-06-04T07:30:16-04:00 3 | describe-name: v8.0-288-g647a72b7b 4 | ref-names: HEAD -> master 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | .git_archival.txt export-subst 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your bug report related to the C++ slang project or the pyslang Python bindings?** 11 | C++ slang / Pyslang Python bindings 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior. Typically a minimized snippet of input code that can be fed to the compiler to produce the bug you're seeing. If you provide a code snippet, please do so as actual text instead of a screenshot, as it's much easier to copy the text and reproduce the problem locally than it is to transcribe it from an image first. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. If you have a non-standard setup or anything unusual that may be contributing to the problem it's good to include that here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to the C++ slang project or the pyslang Python bindings?** 11 | C++ slang / Pyslang Python bindings 12 | 13 | **Is your feature request related to a problem? Please describe.** 14 | A clear and concise description of what the problem is. 15 | 16 | **Describe the solution you'd like** 17 | A clear and concise description of what you want to happen. 18 | 19 | **Describe alternatives you've considered** 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | This project is a SystemVerilog compiler and set of related tools, developed in C++. 2 | Code in the repository is expected to be extremely high quality, with a focus on performance and correctness. 3 | Please follow the instructions below to ensure that your contributions meet these standards. 4 | 5 | ## Development Flow 6 | cmake is used to build the project. The following commands are used to build and run the tests: 7 | ```bash 8 | cmake -B build 9 | cmake --build build -j8 10 | ctest --test-dir build --output-on-failure 11 | ``` 12 | 13 | ## Code Style 14 | Please follow the style of the existing code. Modern C++ features, up to C++ 20, are preferred, and the code should 15 | be written in a way that is easy to read and understand. 16 | 17 | `pre-commit` is used to enforce formatting prior to committing. It should be installed and configured to run 18 | automatically on commit. 19 | 20 | ## Guidelines 21 | 1. Follow C++ best practices and idiomatic patterns 22 | 2. Maintain existing code structure and organization 23 | 3. Write unit tests for new functionality. 24 | 4. Document public APIs and complex logic. 25 | - API reference documentation is added via Doxygen comments. 26 | - More in-depth documentation is added via Markdown files in the `docs` directory. 27 | 28 | ## Python Bindings 29 | This project includes Python bindings fsor the main library. These bindings are generated using pybind11. 30 | When adding to or changing the public API, consider updating the Python bindings as well. 31 | The bindings are located in the `bindings/python` directory. The tests and documentation for the Python bindings 32 | are in the 'pyslang' folder. The bindings are built using CMake, and the following commands 33 | are used to build and run the tests: 34 | ```bash 35 | pip install . --no-build-isolation --config-settings build-dir=build/python_build 36 | pytest 37 | ``` 38 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI Build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - preset: clang-sanitize 17 | os: ubuntu-latest 18 | - preset: gcc-release 19 | os: ubuntu-24.04-arm 20 | - preset: gcc-11-release 21 | os: ubuntu-latest 22 | - preset: gcc-debug-shared 23 | os: ubuntu-latest 24 | - preset: gcc-debug-noexcept 25 | os: ubuntu-latest 26 | - preset: macos-release 27 | os: macos-latest 28 | - preset: win64-release 29 | os: windows-latest 30 | - preset: win64-debug-shared 31 | os: windows-latest 32 | - preset: win64-debug-noexcept 33 | os: windows-latest 34 | 35 | runs-on: ${{ matrix.os }} 36 | steps: 37 | - uses: actions/checkout@v4 38 | with: 39 | fetch-depth: 0 40 | - name: Install dependencies 41 | if: matrix.os == 'ubuntu-latest' 42 | run: | 43 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 44 | sudo add-apt-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main" 45 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 46 | sudo apt-get update 47 | sudo apt-get install -y g++-14 g++-11 clang-20 clang-tidy-20 48 | - uses: ilammy/msvc-dev-cmd@v1 49 | if: matrix.os == 'windows-latest' 50 | - uses: maxim-lobanov/setup-xcode@v1 51 | if: matrix.os == 'macos-latest' 52 | with: 53 | xcode-version: 'latest' 54 | - name: Configure 55 | run: cmake --preset ${{ matrix.preset }} -DSLANG_CI_BUILD=ON -DSLANG_INCLUDE_UVM_TEST=ON 56 | - name: Build 57 | run: cmake --build build/${{ matrix.preset }} -j8 58 | - name: Run tests 59 | run: ctest --test-dir build/${{ matrix.preset }} --output-on-failure --no-tests=error -j8 60 | -------------------------------------------------------------------------------- /.github/workflows/copilot-setup-steps.yml: -------------------------------------------------------------------------------- 1 | name: "Copilot Setup Steps" 2 | 3 | # Allow testing of the setup steps from your repository's "Actions" tab. 4 | on: workflow_dispatch 5 | 6 | jobs: 7 | # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. 8 | copilot-setup-steps: 9 | runs-on: ubuntu-latest 10 | 11 | # Set the permissions to the lowest permissions possible needed for your steps. 12 | # Copilot will be given its own token for its operations. 13 | permissions: 14 | # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. 15 | contents: read 16 | 17 | # You can define any steps you want, and they will run before the agent starts. 18 | # If you do not check out your code, Copilot will do this for you. 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Install Dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install pytest pre-commit pybind11 scikit-build-core 29 | pre-commit install 30 | pre-commit run 31 | 32 | - name: Configure and Build 33 | run: | 34 | cmake -B build 35 | cmake --build build -j8 36 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | jobs: 11 | linux-coverage: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - name: Install dependencies 18 | run: | 19 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 20 | sudo apt-get update 21 | sudo apt-get install -y g++-14 22 | wget --quiet https://github.com/linux-test-project/lcov/releases/download/v2.3/lcov-2.3.tar.gz 23 | tar -xf lcov-2.3.tar.gz 24 | cd lcov-2.3 25 | sudo make install 26 | sudo cpan App::cpanminus 27 | sudo cpanm --notest Capture::Tiny DateTime 28 | - name: Configure 29 | run: cmake --preset=ci-coverage 30 | - name: Build 31 | run: cmake --build build/ci-coverage -j8 32 | - name: Run tests 33 | run: ctest --test-dir build/ci-coverage --output-on-failure --no-tests=error -j8 34 | - name: Process coverage info 35 | run: cmake --build build/ci-coverage -t coverage 36 | - name: Submit to codecov.io 37 | uses: codecov/codecov-action@v5 38 | with: 39 | files: build/ci-coverage/coverage.info 40 | plugins: noop 41 | disable_search: true 42 | token: ${{ secrets.CODECOV_TOKEN }} 43 | verbose: true 44 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | description: Build and deploy documentation (both C++/slang and Python/pyslang) 3 | 4 | on: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Install Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: '3.12' 22 | - name: Install dependencies 23 | run: | 24 | pip install jinja2 pygments docutils 25 | wget --quiet https://sourceforge.net/projects/doxygen/files/rel-1.13.2/doxygen-1.13.2.linux.bin.tar.gz 26 | tar -xf doxygen-1.13.2.linux.bin.tar.gz 27 | cp doxygen-1.13.2/bin/doxygen /usr/local/bin 28 | - name: Build slang and pyslang docs via CMake 29 | run: | 30 | cmake -B build -DSLANG_INCLUDE_DOCS=ON -DSLANG_INCLUDE_TESTS=OFF 31 | cmake --build build --target docs -j8 32 | - name: Compress docs artifacts 33 | run: | 34 | tar -czvf slang.tar.gz build/docs build/bin/slang 35 | - name: Upload artifact for preview 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: slang-docs 39 | path: slang.tar.gz 40 | retention-days: 4 41 | - name: Upload 42 | if: github.event_name != 'pull_request' && github.repository == 'MikePopoloski/slang' 43 | uses: appleboy/scp-action@master 44 | with: 45 | host: ${{ secrets.SSH_DEPLOY_HOST }} 46 | username: ${{ secrets.SSH_DEPLOY_USERNAME }} 47 | port: ${{ secrets.SSH_DEPLOY_PORT }} 48 | key: ${{ secrets.SSH_DEPLOY_KEY }} 49 | passphrase: ${{ secrets.SSH_DEPLOY_PASSPHRASE }} 50 | source: slang.tar.gz 51 | target: upload/ 52 | - name: Deploy 53 | if: github.event_name != 'pull_request' && github.repository == 'MikePopoloski/slang' 54 | uses: appleboy/ssh-action@master 55 | with: 56 | host: ${{ secrets.SSH_DEPLOY_HOST }} 57 | username: ${{ secrets.SSH_DEPLOY_USERNAME }} 58 | port: ${{ secrets.SSH_DEPLOY_PORT }} 59 | key: ${{ secrets.SSH_DEPLOY_KEY }} 60 | passphrase: ${{ secrets.SSH_DEPLOY_PASSPHRASE }} 61 | script: slang-website/deploy_docs.sh 62 | -------------------------------------------------------------------------------- /.github/workflows/python-build.yml: -------------------------------------------------------------------------------- 1 | name: "Python Build" 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | name: Build with Pip 13 | runs-on: ${{ matrix.platform }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | platform: [windows-latest, macos-latest, ubuntu-latest] 18 | python-version: ["3.11", "pypy-3.9"] 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | - uses: maxim-lobanov/setup-xcode@v1 24 | if: matrix.platform == 'macos-latest' 25 | with: 26 | xcode-version: 'latest' 27 | - uses: actions/setup-python@v5 28 | with: 29 | python-version: ${{ matrix.python-version }} 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install pytest 34 | - name: Build and install 35 | run: pip install --verbose . 36 | - name: Test 37 | run: pytest 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python ignores. 2 | .pytest_cache/ 3 | .venv/ 4 | venv/ 5 | .cache/ 6 | .DS_Store 7 | __pycache__/ 8 | *.pyc 9 | *.pyo 10 | *.pyd 11 | _version.py 12 | __init__.pyi 13 | 14 | # C and CMake ignores. 15 | build/ 16 | install/ 17 | compile_commands.json 18 | CMakeSettings.json 19 | *egg-info/ 20 | cmake-build-*/ 21 | prefix/ 22 | CMakeLists.txt.user 23 | CMakeUserPresets.json 24 | .ccls 25 | 26 | # Editor ignores. 27 | .vscode/ 28 | !.vscode/settings.json 29 | !.vscode/extensions.json 30 | 31 | .idea/ 32 | .vs/ 33 | *.code-workspace 34 | -------------------------------------------------------------------------------- /.lcovrc: -------------------------------------------------------------------------------- 1 | # 2 | # lcovrc - settings for LCOV 3 | # 4 | 5 | # Specify if geninfo should try to automatically determine the base-directory 6 | # when collecting coverage data. 7 | geninfo_auto_base = 1 8 | 9 | # Use gcov intermediate format? Valid values are 0, 1, auto 10 | geninfo_intermediate = auto 11 | 12 | # Specify if exception branches should be excluded from branch coverage. 13 | no_exception_branch = 1 14 | 15 | # Location of the insmod tool 16 | lcov_insmod_tool = /sbin/insmod 17 | 18 | # Location of the modprobe tool 19 | lcov_modprobe_tool = /sbin/modprobe 20 | 21 | # Location of the rmmod tool 22 | lcov_rmmod_tool = /sbin/rmmod 23 | 24 | # Location for temporary directories 25 | lcov_tmp_dir = /tmp 26 | 27 | # Show full paths during list operation if non-zero (same as --list-full-path 28 | # option of lcov) 29 | lcov_list_full_path = 0 30 | 31 | # Specify the maximum width for list output. This value is ignored when 32 | # lcov_list_full_path is non-zero. 33 | lcov_list_width = 80 34 | 35 | # Specify the maximum percentage of file names which may be truncated when 36 | # choosing a directory prefix in list output. This value is ignored when 37 | # lcov_list_full_path is non-zero. 38 | lcov_list_truncate_max = 20 39 | 40 | # Specify if function coverage data should be collected and processed. 41 | function_coverage = 1 42 | 43 | # Specify if branch coverage data should be collected and processed. 44 | branch_coverage = 0 45 | 46 | geninfo_unexecuted_blocks = 1 47 | 48 | # Add project-specific exclusions for the SLANG_UNREACHABLE macro 49 | # since that branch is by design never reachable in real code, 50 | # and for a 'default' case block that is empty since it's very 51 | # irritating to see missed coverage on that line -- either the remaining 52 | # block is also missed or it's an empty case and we don't care. 53 | omit_lines = SLANG_UNREACHABLE 54 | omit_lines = default:$ 55 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # To use: 2 | # 3 | # pre-commit run -a 4 | # 5 | # Or: 6 | # 7 | # pre-commit install # (runs every time you commit in git) 8 | # 9 | # To update this file: 10 | # 11 | # pre-commit autoupdate 12 | # 13 | # See https://github.com/pre-commit/pre-commit 14 | 15 | ci: 16 | autoupdate_commit_msg: "chore: update pre-commit hooks" 17 | autofix_commit_msg: "style: pre-commit fixes" 18 | 19 | exclude: grammar.md 20 | 21 | repos: 22 | # Standard hooks 23 | - repo: https://github.com/pre-commit/pre-commit-hooks 24 | rev: v5.0.0 25 | hooks: 26 | - id: check-case-conflict 27 | - id: check-merge-conflict 28 | - id: check-yaml 29 | args: [--allow-multiple-documents] 30 | - id: debug-statements 31 | - id: end-of-file-fixer 32 | - id: fix-byte-order-marker 33 | - id: mixed-line-ending 34 | args: [--fix,lf] 35 | - id: trailing-whitespace 36 | 37 | # Black, the python code formatter 38 | - repo: https://github.com/psf/black 39 | rev: 25.1.0 40 | hooks: 41 | - id: black 42 | exclude: ^(docs) 43 | 44 | # flake8 for python linting 45 | - repo: https://github.com/pycqa/flake8 46 | rev: 7.2.0 47 | hooks: 48 | - id: flake8 49 | additional_dependencies: [flake8-bugbear] 50 | 51 | # Sort your python imports in a standard form 52 | - repo: https://github.com/PyCQA/isort 53 | rev: 6.0.1 54 | hooks: 55 | - id: isort 56 | 57 | # CMake formatting 58 | - repo: https://github.com/cheshirekow/cmake-format-precommit 59 | rev: v0.6.13 60 | hooks: 61 | - id: cmake-format 62 | additional_dependencies: [pyyaml] 63 | types: [file] 64 | files: (\.cmake|CMakeLists.txt)(.in)?$ 65 | 66 | # clang-format 67 | - repo: https://github.com/pre-commit/mirrors-clang-format 68 | rev: v20.1.5 69 | hooks: 70 | - id: clang-format 71 | exclude: ^external/.* 72 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "matepek.vscode-catch2-test-adapter", 4 | ], 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.testing.pytestArgs": [], 3 | "python.testing.unittestEnabled": false, 4 | "python.testing.pytestEnabled": true, 5 | "testMate.cpp.test.workingDirectory": "${workspaceFolder}", 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2025 Michael Popoloski 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSES/BSL-1.0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: 4 | 5 | The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | SPDX-PackageName = "slang" 3 | SPDX-PackageSupplier = "Michael Popoloski " 4 | SPDX-PackageDownloadLocation = "https://sv-lang.com/" 5 | 6 | [[annotations]] 7 | path = "**" 8 | precedence = "aggregate" 9 | SPDX-FileCopyrightText = "Michael Popoloski" 10 | SPDX-License-Identifier = "MIT" 11 | 12 | [[annotations]] 13 | path = "external/boost_unordered.hpp" 14 | precedence = "aggregate" 15 | SPDX-FileCopyrightText = "Boost authors" 16 | SPDX-License-Identifier = "BSL-1.0" 17 | 18 | [[annotations]] 19 | path = "external/expected.hpp" 20 | precedence = "aggregate" 21 | SPDX-FileCopyrightText = "Martin Moene" 22 | SPDX-License-Identifier = "BSL-1.0" 23 | 24 | [[annotations]] 25 | path = "external/BS_thread_pool.hpp" 26 | precedence = "aggregate" 27 | SPDX-FileCopyrightText = "Barak Shoshany" 28 | SPDX-License-Identifier = "MIT" 29 | -------------------------------------------------------------------------------- /bindings/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | set(find_pkg_args "") 7 | if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.24.0") 8 | set(find_pkg_args "FIND_PACKAGE_ARGS" "2.13.1") 9 | endif() 10 | 11 | FetchContent_Declare( 12 | pybind11 13 | GIT_REPOSITORY https://github.com/pybind/pybind11.git 14 | GIT_TAG v2.13.6 15 | GIT_SHALLOW ON 16 | ${find_pkg_args}) 17 | FetchContent_MakeAvailable(pybind11) 18 | 19 | if(pybind11_FOUND) 20 | message(STATUS "Using system pybind11 library: ${pybind11_VERSION}") 21 | message(STATUS "Using system pybind11 include: ${pybind11_INCLUDE_DIRS}") 22 | else() 23 | message(STATUS "Using remote pybind11 library") 24 | endif() 25 | 26 | # Generate bindings for syntax node types 27 | add_custom_command( 28 | COMMAND 29 | ${Python_EXECUTABLE} ${SCRIPTS_DIR}/syntax_gen.py --dir 30 | ${CMAKE_CURRENT_BINARY_DIR} --python-bindings --syntax 31 | ${SCRIPTS_DIR}/syntax.txt 32 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings0.cpp 33 | ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings1.cpp 34 | ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings2.cpp 35 | ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings3.cpp 36 | DEPENDS ${SCRIPTS_DIR}/syntax_gen.py ${SCRIPTS_DIR}/syntax.txt 37 | COMMENT "Generating syntax bindings") 38 | 39 | # Add the pyslang module via pybind11 40 | pybind11_add_module( 41 | pyslang 42 | MODULE 43 | python/AnalysisBindings.cpp 44 | python/ASTBindings.cpp 45 | python/CompBindings.cpp 46 | python/ExpressionBindings.cpp 47 | python/NumericBindings.cpp 48 | python/pyslang.cpp 49 | python/StatementBindings.cpp 50 | python/SymbolBindings.cpp 51 | python/SyntaxBindings.cpp 52 | python/TypeBindings.cpp 53 | python/UtilBindings.cpp 54 | ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings0.cpp 55 | ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings1.cpp 56 | ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings2.cpp 57 | ${CMAKE_CURRENT_BINARY_DIR}/PySyntaxBindings3.cpp) 58 | set_source_files_properties( 59 | python/pyslang.cpp PROPERTIES COMPILE_DEFINITIONS 60 | VERSION_INFO=${PROJECT_VERSION}) 61 | target_link_libraries(pyslang PUBLIC slang::slang) 62 | target_include_directories(pyslang PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/python) 63 | 64 | install( 65 | TARGETS pyslang 66 | COMPONENT pylib 67 | DESTINATION .) 68 | -------------------------------------------------------------------------------- /bindings/python/PyVisitors.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // PyVisitors.h 3 | // SPDX-FileCopyrightText: Michael Popoloski 4 | // SPDX-License-Identifier: MIT 5 | //------------------------------------------------------------------------------ 6 | #pragma once 7 | 8 | #include "pyslang.h" 9 | 10 | #include "slang/ast/ASTVisitor.h" 11 | 12 | enum class VisitAction { 13 | Advance, 14 | Skip, 15 | Interrupt, 16 | }; 17 | 18 | template class BaseVisitor, auto... baseArgs> 19 | struct PyVisitorBase : public BaseVisitor { 20 | py::object f; 21 | bool interrupted = false; 22 | 23 | static inline constexpr auto doc = 24 | "Visit a pyslang object with a callback function `f`.\n\n" 25 | "The callback function `f` should take a single argument, which is the " 26 | "current node being visited.\n\n" 27 | "The return value of `f` determines the next node to visit. " 28 | "If `f` ever returns `pyslang.VisitAction.Interrupt`, the visit is aborted " 29 | "and no additional nodes are visited. If `f` returns `pyslang.VisitAction.Skip`, " 30 | "then no child nodes of the current node are visited. " 31 | "For any other return value, including `pyslang.VisitAction.Advance`, " 32 | "the return value is ignored, and the walk continues."; 33 | 34 | explicit PyVisitorBase(py::object f) : f{f} {} 35 | 36 | template 37 | void handle(const T& t) { 38 | if (this->interrupted) 39 | return; 40 | py::object result = this->f(&t); 41 | if (result.equal(py::cast(VisitAction::Interrupt))) { 42 | this->interrupted = true; 43 | return; 44 | } 45 | if (result.not_equal(py::cast(VisitAction::Skip))) 46 | this->visitDefault(t); 47 | } 48 | }; 49 | 50 | struct PyASTVisitor : PyVisitorBase { 51 | using PyVisitorBase::PyVisitorBase; 52 | }; 53 | 54 | template 55 | void pyASTVisit(const T& t, py::object f) { 56 | PyASTVisitor visitor{f}; 57 | t.visit(visitor); 58 | } 59 | -------------------------------------------------------------------------------- /bindings/python/pyslang.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // pyslang.cpp 3 | // SPDX-FileCopyrightText: Michael Popoloski 4 | // SPDX-License-Identifier: MIT 5 | //------------------------------------------------------------------------------ 6 | #include "pyslang.h" 7 | 8 | void registerAnalysis(py::module_& m); 9 | void registerAST(py::module_& m); 10 | void registerCompilation(py::module_& m); 11 | void registerExpressions(py::module_& m); 12 | void registerNumeric(py::module_& m); 13 | void registerUtil(py::module_& m); 14 | void registerStatements(py::module_& m); 15 | void registerSymbols(py::module_& m); 16 | void registerSyntax(py::module_& m); 17 | void registerSyntaxNodes0(py::module_& m); 18 | void registerSyntaxNodes1(py::module_& m); 19 | void registerSyntaxNodes2(py::module_& m); 20 | void registerSyntaxNodes3(py::module_& m); 21 | void registerTypes(py::module_& m); 22 | 23 | PYBIND11_MODULE(pyslang, m) { 24 | m.doc() = "Python bindings for slang, the SystemVerilog compiler library"; 25 | 26 | #ifdef VERSION_INFO 27 | m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); 28 | #else 29 | m.attr("__version__") = "dev"; 30 | #endif 31 | 32 | registerAnalysis(m); 33 | registerAST(m); 34 | registerCompilation(m); 35 | registerExpressions(m); 36 | registerNumeric(m); 37 | registerUtil(m); 38 | registerStatements(m); 39 | registerSymbols(m); 40 | registerSyntax(m); 41 | registerSyntaxNodes0(m); 42 | registerSyntaxNodes1(m); 43 | registerSyntaxNodes2(m); 44 | registerSyntaxNodes3(m); 45 | registerTypes(m); 46 | 47 | py::register_exception_translator([](std::exception_ptr p) { 48 | try { 49 | if (p) 50 | std::rethrow_exception(p); 51 | } 52 | catch (const std::filesystem::filesystem_error& e) { 53 | const auto code = e.code(); 54 | const auto p1 = e.path1().string(); 55 | const auto p2 = e.path2().string(); 56 | PyErr_SetObject(PyExc_IOError, 57 | py::make_tuple(code.value(), code.message(), 58 | p1.empty() ? py::none() : py::cast(p1), code.value(), 59 | p2.empty() ? py::none() : py::cast(p2)) 60 | .ptr()); 61 | } 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /cmake/coverage.cmake: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | set(COVERAGE_GCOV_TOOL 7 | "gcov" 8 | CACHE STRING "Name of the gcov binary to run") 9 | 10 | set(COVERAGE_TRACE_COMMAND 11 | lcov -c -q -o "${PROJECT_BINARY_DIR}/coverage.info" --gcov-tool 12 | ${COVERAGE_GCOV_TOOL} -d "${PROJECT_BINARY_DIR}" --include 13 | "${PROJECT_SOURCE_DIR}/source/*" --include 14 | "${PROJECT_SOURCE_DIR}/include/slang/*" --config-file 15 | "${PROJECT_SOURCE_DIR}/.lcovrc" 16 | CACHE STRING 17 | "; separated command to generate a trace for the 'coverage' target") 18 | 19 | set(COVERAGE_HTML_COMMAND 20 | genhtml "${PROJECT_BINARY_DIR}/coverage.info" -p "${PROJECT_SOURCE_DIR}" -o 21 | "${PROJECT_BINARY_DIR}/coverage_html" --ignore-errors inconsistent 22 | --ignore-errors corrupt 23 | CACHE 24 | STRING 25 | "; separated command to generate an HTML report for the 'coverage' target" 26 | ) 27 | 28 | add_custom_target( 29 | coverage 30 | COMMAND ${COVERAGE_TRACE_COMMAND} 31 | COMMAND ${COVERAGE_HTML_COMMAND} 32 | COMMENT "Generating coverage report" 33 | VERBATIM) 34 | -------------------------------------------------------------------------------- /cmake/gitversion.cmake.in: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | # Internal file used to force a reconfiguration if the git commit changes. 7 | set(HEAD_HASH) 8 | 9 | file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) 10 | 11 | string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) 12 | if(HEAD_CONTENTS MATCHES "ref") 13 | # named branch 14 | string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") 15 | if(EXISTS "@GIT_DIR@/${HEAD_REF}") 16 | configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) 17 | else() 18 | configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) 19 | file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) 20 | if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") 21 | set(HEAD_HASH "${CMAKE_MATCH_1}") 22 | endif() 23 | endif() 24 | else() 25 | # detached HEAD 26 | configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) 27 | endif() 28 | 29 | if(NOT HEAD_HASH) 30 | file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) 31 | string(STRIP "${HEAD_HASH}" HEAD_HASH) 32 | endif() 33 | -------------------------------------------------------------------------------- /cmake/install-rules.cmake: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | install( 7 | TARGETS slang_slang 8 | EXPORT slangTargets 9 | RUNTIME COMPONENT slang_Runtime 10 | LIBRARY COMPONENT slang_Runtime NAMELINK_COMPONENT slang_Development 11 | ARCHIVE COMPONENT slang_Development 12 | INCLUDES 13 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 14 | 15 | if(SLANG_INCLUDE_TOOLS) 16 | install(TARGETS slang_driver RUNTIME COMPONENT slang_Runtime) 17 | endif() 18 | 19 | include(CMakePackageConfigHelpers) 20 | 21 | # Package export / installation rules 22 | set(SLANG_CMAKECONFIG_INSTALL_DIR 23 | "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 24 | CACHE STRING "install path for slangConfig.cmake") 25 | 26 | install( 27 | EXPORT slangTargets 28 | NAMESPACE "slang::" 29 | DESTINATION ${SLANG_CMAKECONFIG_INSTALL_DIR} 30 | COMPONENT slang_Development) 31 | 32 | if(fmt_FOUND) 33 | set(FMT_FIND_DEP "find_dependency(fmt)") 34 | endif() 35 | if(Boost_FOUND) 36 | set(BOOST_FIND_DEP "find_dependency(Boost)") 37 | endif() 38 | if(mimalloc_FOUND) 39 | set(MIMALLOC_FIND_DEP "find_dependency(mimalloc)") 40 | endif() 41 | if(cpptrace_FOUND) 42 | set(CPPTRACE_FIND_DEP "find_dependency(cpptrace)") 43 | endif() 44 | 45 | configure_package_config_file( 46 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/slangConfig.cmake.in 47 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 48 | INSTALL_DESTINATION ${SLANG_CMAKECONFIG_INSTALL_DIR}) 49 | 50 | write_basic_package_version_file( 51 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 52 | VERSION ${PROJECT_VERSION} 53 | COMPATIBILITY SameMajorVersion) 54 | 55 | install( 56 | FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 57 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 58 | DESTINATION ${SLANG_CMAKECONFIG_INSTALL_DIR} 59 | COMPONENT slang_Development) 60 | 61 | if(UNIX) 62 | # Install pkg-config input file 63 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scripts/sv-lang.pc.in 64 | ${CMAKE_CURRENT_BINARY_DIR}/sv-lang.pc @ONLY) 65 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sv-lang.pc 66 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) 67 | endif() 68 | -------------------------------------------------------------------------------- /cmake/slangConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | @FMT_FIND_DEP@ 6 | @BOOST_FIND_DEP@ 7 | @MIMALLOC_FIND_DEP@ 8 | @CPPTRACE_FIND_DEP@ 9 | 10 | include("${CMAKE_CURRENT_LIST_DIR}/slangTargets.cmake") 11 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | 10 | status: 11 | project: 12 | default: 13 | target: 85% 14 | threshold: 1% 15 | patch: no 16 | changes: no 17 | 18 | ignore: 19 | - "external" 20 | - "tests" 21 | - "tools" 22 | - "build" 23 | 24 | parsers: 25 | gcov: 26 | branch_detection: 27 | conditional: yes 28 | loop: yes 29 | method: no 30 | macro: no 31 | 32 | comment: 33 | layout: "reach, diff, flags, files, footer" 34 | behavior: default 35 | require_changes: no 36 | -------------------------------------------------------------------------------- /docs/api-reference.dox: -------------------------------------------------------------------------------- 1 | /** @page api-reference API Reference 2 | @brief Documentation for the public slang C++ API 3 | 4 | The @ref slang root namespace contains most of the entry points for using the library. 5 | See the individual listing pages for a complete reference, but some useful starting 6 | points for exploration are: 7 | 8 | - The @ref slang::syntax::SyntaxTree class is a convenient way to parse a source file (or some source 9 | text in memory) into syntax nodes. 10 | - The @ref slang::syntax::SyntaxVisitor class can be used to walk a syntax tree. 11 | - The @ref slang::syntax::SyntaxRewriter class is a SyntaxVisitor with helper methods for rewriting 12 | syntax nodes into some new form (which would be useful for refactoring tools). 13 | - The @ref slang::ast::Compilation class is the main entry point for type checking and elaboration. 14 | - The @ref slang::ast::ScriptSession is a high level code interpreter; you can give it some source 15 | code and it will return a constant value that is the result of executing that code. 16 | 17 | */ 18 | -------------------------------------------------------------------------------- /docs/developer-guide.dox: -------------------------------------------------------------------------------- 1 | /** @page developer-guide Developer Guide 2 | @brief Information on the internals of slang and its use as a library 3 | 4 | slang is designed first and foremost as a collection of software libraries that 5 | can be used, in whole or in part, in other projects. Those might include things 6 | like linting tools, code editor completion, code generation as part of a build, 7 | automated refactoring tools, runtime JITing or interpretation, simulators, 8 | and synthesis tools. 9 | 10 | This section gives an overview of the slang libraries and codebase, 11 | and serves as a starting point for learning to use it in your own projects. 12 | 13 | - @ref building - Instructions for obtaining and building the library 14 | - @ref commoncomp - Overview of shared code and practices used throughout the library 15 | - @ref sourcemanagement - APIs for loading and managing source code for compilation 16 | - @ref diagnostics - APIs that support issuing diagnostics 17 | - @ref parsing - APIs for parsing SystemVerilog source code 18 | 19 | */ 20 | -------------------------------------------------------------------------------- /docs/grammar.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MikePopoloski/slang/647a72b7b7fd4f8c6b2dfa496a20bc30bbfaef8a/docs/grammar.md -------------------------------------------------------------------------------- /docs/namespaces.dox: -------------------------------------------------------------------------------- 1 | /** @dir include/slang 2 | @brief Root of the @ref slang library 3 | */ 4 | 5 | /** @dir include/slang/analysis 6 | @brief Post-elaboration analysis framework 7 | */ 8 | 9 | /** @dir include/slang/ast 10 | @brief The SystemVerilog AST 11 | */ 12 | 13 | /** @dir include/slang/ast/expressions 14 | @brief Expression definitions 15 | */ 16 | 17 | /** @dir include/slang/ast/symbols 18 | @brief Symbol definitions 19 | */ 20 | 21 | /** @dir include/slang/ast/types 22 | @brief Type definitions 23 | */ 24 | 25 | /** @dir include/slang/diagnostics 26 | @brief Diagnostic definitions 27 | */ 28 | 29 | /** @dir include/slang/driver 30 | @brief Frontend tool driver 31 | */ 32 | 33 | /** @dir include/slang/numeric 34 | @brief Numeric-related utility types 35 | */ 36 | 37 | /** @dir include/slang/parsing 38 | @brief Lexing, preprocessing, and parsing functionality 39 | */ 40 | 41 | /** @dir include/slang/syntax 42 | @brief Syntax tree manipulation 43 | */ 44 | 45 | /** @dir include/slang/text 46 | @brief Source file and text manipulation 47 | */ 48 | 49 | /** @dir include/slang/util 50 | @brief Various generic utility types 51 | */ 52 | 53 | /** @namespace slang 54 | @brief Root namespace 55 | */ 56 | 57 | /** @namespace slang::analysis 58 | @brief Post-elaboration analysis framework 59 | */ 60 | 61 | /** @namespace slang::assert 62 | @brief Assertion-related utilities 63 | */ 64 | 65 | /** @namespace slang::ast 66 | @brief The SystemVerilog AST 67 | */ 68 | 69 | /** @namespace slang::::SFormat 70 | @brief Utility methods for formatting strings using SystemVerilog formatting styles 71 | */ 72 | 73 | /** @namespace slang::driver 74 | @brief Frontend tool driver 75 | */ 76 | 77 | /** @namespace slang::parsing 78 | @brief Lexing, preprocessing, and parsing functionality 79 | */ 80 | 81 | /** @namespace slang::syntax 82 | @brief Syntax tree manipulation 83 | */ 84 | -------------------------------------------------------------------------------- /docs/source-management.dox: -------------------------------------------------------------------------------- 1 | /** @page sourcemanagement Source Management 2 | @brief APIs for loading and managing source code for compilation 3 | 4 | @section sourceman Source Manager 5 | 6 | The @ref slang::SourceManager class is responsible for loading and storing all 7 | source code that will be parsed or compiled. All of the source is stored in 8 | memory until the SourceManager is destroyed; because the rest of the library 9 | only uses string_views that point into this memory, you must keep the 10 | SourceManager alive until you're fully done with all parsing and compilation. 11 | 12 | The SourceManager operates on "buffers", which are groups of source code. 13 | Source files are one kind of buffer, which can be added to the manager via 14 | the @ref slang::SourceManager::readSource method. It returns a @ref slang::SourceBuffer 15 | which contains a unique @ref slang::BufferID for that buffer plus a pointer to the 16 | actual source text. 17 | 18 | @code{.cpp} 19 | SourceManager sm; 20 | SourceBuffer buffer = sm.readSource("path/to/mySource.sv"); 21 | @endcode 22 | 23 | In addition to reading source from disk, you can also pass source in directly 24 | via the @ref slang::SourceManager::assignText method. 25 | 26 | @code{.cpp} 27 | SourceManager sm; 28 | SourceBuffer buffer = sm.assignText("module m; endmodule"); 29 | @endcode 30 | 31 | Besides files, macro expansions are the other kind of buffer that can be 32 | tracked by the manager. Each macro expansion constitutes a new buffer, and 33 | tracks both where the original macro text came from as well as where the 34 | expansion is occurring. 35 | 36 | @section sourceloc Source Locations 37 | 38 | It's important to be able to concisely represent locations in the user's 39 | original source code so that accurate diagnostics can be issued. 40 | The @ref slang::SourceLocation class is used for this purpose. It combines a 41 | BufferID with a byte offset representing the location within that buffer. 42 | 43 | SourceLocation is designed to be compact; in release mode it consumes only 44 | 8 bytes. In order to learn useful things about that location you need the 45 | SourceManager that originally created it. SourceManager has many methods 46 | for querying information about source locations; see the API reference for 47 | more details. 48 | 49 | In debug builds each SourceLocation includes the textual name of the buffer 50 | with which it is associated, to ease inspection when viewing locations in 51 | a debugger. 52 | 53 | There is also a @ref slang::SourceRange type that combines two SourceLocations 54 | to denote a range of source code. 55 | 56 | */ 57 | -------------------------------------------------------------------------------- /docs/tools.dox: -------------------------------------------------------------------------------- 1 | /** @page tools Tools 2 | @brief Documentation for ancillary tools included with slang 3 | 4 | slang includes several tools besides the main command line driver tool (which is documented in 5 | @ref command-line-ref ). Many of these tools are contributions that may not be as complete or 6 | well supported as the main slang library. 7 | 8 | @tableofcontents 9 | 10 | \include{doc} tools/hier/README.md 11 | \include{doc} tools/netlist/README.md 12 | \include{doc} tools/reflect/README.md 13 | \include{doc} tools/rewriter/README.md 14 | \include{doc} tools/threadtest/README.md 15 | \include{doc} tools/tidy/README.md 16 | 17 | */ 18 | -------------------------------------------------------------------------------- /include/slang/analysis/AnalysisOptions.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file AnalysisOptions.h 3 | //! @brief Various options that control analysis 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/util/Enum.h" 11 | 12 | namespace slang::analysis { 13 | 14 | /// Defines flags that control analysis behavior. 15 | enum class SLANG_EXPORT AnalysisFlags { 16 | /// No flags specified. 17 | None = 0, 18 | 19 | /// Analysis should check for and report on unused symbols. 20 | CheckUnused = 1 << 0, 21 | 22 | /// 'unique' and 'priority' keywords are used to assume full case coverage. 23 | FullCaseUniquePriority = 1 << 1, 24 | 25 | /// Require X and Z bits be covered for full case coverage. 26 | /// If not set, only 0 and 1 bits are required. 27 | FullCaseFourState = 1 << 2, 28 | 29 | /// Allow multi-driven subroutine local variables. 30 | AllowMultiDrivenLocals = 1 << 3, 31 | 32 | /// Signals driven by an always_comb are normally not allowed to be driven 33 | /// by any other process. This flag allows initial blocks to 34 | /// also drive such signals. 35 | AllowDupInitialDrivers = 1 << 4, 36 | }; 37 | SLANG_BITMASK(AnalysisFlags, AllowDupInitialDrivers) 38 | 39 | /// Contains various options that can control analysis behavior. 40 | struct SLANG_EXPORT AnalysisOptions { 41 | /// Flags that control analysis behavior. 42 | bitmask flags; 43 | 44 | /// The number of threads to use for analysis. 45 | uint32_t numThreads = 0; 46 | 47 | /// The maximum number of case analysis steps to perform before giving up. 48 | uint32_t maxCaseAnalysisSteps = 65535; 49 | 50 | /// The maximum number of loop analysis steps to perform before giving up. 51 | uint32_t maxLoopAnalysisSteps = 65535; 52 | }; 53 | 54 | } // namespace slang::analysis 55 | -------------------------------------------------------------------------------- /include/slang/analysis/AnalyzedAssertion.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file AnalyzedAssertion.h 3 | //! @brief Analysis support for concurrent assertions 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/util/Util.h" 11 | 12 | namespace slang::ast { 13 | 14 | class Expression; 15 | class Statement; 16 | class Symbol; 17 | class TimingControl; 18 | 19 | } // namespace slang::ast 20 | 21 | namespace slang::analysis { 22 | 23 | class AnalysisContext; 24 | class AnalyzedProcedure; 25 | class AnalyzedScope; 26 | 27 | /// Represents an analyzed assertion (or procedural checker). 28 | class SLANG_EXPORT AnalyzedAssertion { 29 | public: 30 | /// If this assertion is a procedural checker, this is the expanded 31 | /// analyzed checker body. 32 | const AnalyzedScope* checkerScope = nullptr; 33 | 34 | /// Constructs a new AnalyzedAssertion object. 35 | AnalyzedAssertion(AnalysisContext& context, const ast::TimingControl* contextualClock, 36 | const AnalyzedProcedure& procedure, const ast::Statement& stmt, 37 | const ast::Symbol* checkerInstance); 38 | 39 | /// Constructs a new AnalyzedAssertion object. 40 | AnalyzedAssertion(AnalysisContext& context, const ast::TimingControl* contextualClock, 41 | const AnalyzedProcedure* procedure, const ast::Symbol& parentSymbol, 42 | const ast::Expression& expr); 43 | }; 44 | 45 | } // namespace slang::analysis 46 | -------------------------------------------------------------------------------- /include/slang/ast/ASTDiagMap.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file ASTDiagMap.h 3 | //! @brief AST Diagnostic Map 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | #include "slang/diagnostics/Diagnostics.h" 14 | #include "slang/util/FlatMap.h" 15 | 16 | namespace slang::ast { 17 | 18 | /// A helper class that maintains a map of issued diagnostics keyed by 19 | /// their code and location to aid in deduplicating diagnostics across 20 | /// an AST hierarchy. 21 | class SLANG_EXPORT ASTDiagMap { 22 | public: 23 | /// Adds a diagnostic to the map, deduplicating if needed. 24 | Diagnostic& add(Diagnostic diag, bool& isNew); 25 | 26 | /// Coalesces all issued diagnostics into a set that is ready for presenting. 27 | /// If the @a sourceManager is provided it will be used to sort the diagnostics. 28 | Diagnostics coalesce(const SourceManager* sourceManager); 29 | 30 | private: 31 | flat_hash_map, std::vector> map; 32 | }; 33 | 34 | } // namespace slang::ast 35 | -------------------------------------------------------------------------------- /include/slang/ast/ScriptSession.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file ScriptSession.h 3 | //! @brief High-level interface to the compiler tools to evaluate snippets of code 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/ast/Compilation.h" 11 | #include "slang/ast/EvalContext.h" 12 | #include "slang/syntax/SyntaxFwd.h" 13 | 14 | namespace slang::syntax { 15 | class SyntaxTree; 16 | } 17 | 18 | namespace slang::ast { 19 | 20 | /// A helper class that allows evaluating arbitrary snippets of SystemVerilog 21 | /// source code and maintaining state across multiple eval calls. 22 | class SLANG_EXPORT ScriptSession { 23 | public: 24 | /// A bag of options to apply to the various evaluated snippets. 25 | Bag options; 26 | 27 | /// A compilation object that holds state across evaluation calls. 28 | Compilation compilation; 29 | 30 | /// A compilation unit that acts as a scope for evaluation. 31 | CompilationUnitSymbol& scope; 32 | 33 | /// Constructs a new ScriptSession. 34 | explicit ScriptSession(Bag options = {}); 35 | 36 | /// Tries to evaluate the given snippet of SystemVerilog code 37 | /// and returns the result as a constant value. 38 | ConstantValue eval(std::string_view text); 39 | 40 | /// Tries to evaluate the given expression tree and returns the 41 | /// result as a constant value. 42 | ConstantValue evalExpression(const syntax::ExpressionSyntax& expr); 43 | 44 | /// Tries to evaluate the given statement tree and returns the 45 | /// result as a constant value. 46 | void evalStatement(const syntax::StatementSyntax& expr); 47 | 48 | /// Copies the packages from the given compilation into this session. 49 | void copyPackagesFrom(const Compilation& other); 50 | 51 | /// Gets any diagnostics that have been issued during evaluation calls. 52 | Diagnostics getDiagnostics(); 53 | 54 | private: 55 | std::vector> syntaxTrees; 56 | ASTContext astCtx; 57 | EvalContext evalContext; 58 | }; 59 | 60 | } // namespace slang::ast 61 | -------------------------------------------------------------------------------- /include/slang/ast/symbols/AttributeSymbol.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file AttributeSymbol.h 3 | //! @brief Symbol definition for source attributes 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/ast/Symbol.h" 11 | #include "slang/syntax/SyntaxFwd.h" 12 | #include "slang/text/SourceLocation.h" 13 | 14 | namespace slang::ast { 15 | 16 | class ASTSerializer; 17 | class Scope; 18 | 19 | class SLANG_EXPORT AttributeSymbol : public Symbol { 20 | public: 21 | AttributeSymbol(std::string_view name, SourceLocation loc, const Symbol& symbol, 22 | const syntax::ExpressionSyntax& expr); 23 | 24 | AttributeSymbol(std::string_view name, SourceLocation loc, const Scope& scope, 25 | LookupLocation lookupLocation, const syntax::ExpressionSyntax& expr); 26 | 27 | AttributeSymbol(std::string_view name, SourceLocation loc, const ConstantValue& value); 28 | 29 | const ConstantValue& getValue() const; 30 | 31 | void serializeTo(ASTSerializer& serializer) const; 32 | 33 | static std::span fromSyntax( 34 | std::span syntax, const Scope& scope, 35 | const Symbol& symbol); 36 | 37 | static std::span fromSyntax( 38 | std::span syntax, const Scope& scope, 39 | LookupLocation lookupLocation); 40 | 41 | static bool isKind(SymbolKind kind) { return kind == SymbolKind::Attribute; } 42 | 43 | private: 44 | const Symbol* symbol = nullptr; 45 | const Scope* scope = nullptr; 46 | const syntax::ExpressionSyntax* expr = nullptr; 47 | mutable const ConstantValue* value = nullptr; 48 | LookupLocation lookupLocation; 49 | }; 50 | 51 | } // namespace slang::ast 52 | -------------------------------------------------------------------------------- /include/slang/diagnostics/DiagArgFormatter.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file DiagArgFormatter.h 3 | //! @brief Interface for formatting custom diagnostic argument types 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | #include "slang/slang_export.h" 14 | 15 | namespace slang { 16 | 17 | class Diagnostic; 18 | 19 | class SLANG_EXPORT DiagArgFormatter { 20 | public: 21 | virtual ~DiagArgFormatter() {} 22 | 23 | virtual void startMessage(const Diagnostic&) {} 24 | virtual std::string format(const std::any& arg) = 0; 25 | }; 26 | 27 | } // namespace slang 28 | -------------------------------------------------------------------------------- /include/slang/diagnostics/DiagnosticClient.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file DiagnosticClient.h 3 | //! @brief Client interface for diagnostic rendering 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/diagnostics/DiagnosticEngine.h" 11 | #include "slang/util/Util.h" 12 | 13 | namespace slang { 14 | 15 | /// A base class for diagnostic clients, which receive issued diagnostics 16 | /// and present them to the user in some form. 17 | class SLANG_EXPORT DiagnosticClient { 18 | public: 19 | virtual ~DiagnosticClient() = default; 20 | 21 | /// Called when a diagnostic is issued by the engine. 22 | virtual void report(const ReportedDiagnostic& diagnostic) = 0; 23 | 24 | /// Sets the engine that this client is associated with. 25 | /// This is called by the engine when the client is added to it. 26 | void setEngine(const DiagnosticEngine& engine); 27 | 28 | /// Sets whether displayed filenames for diagnostics should be 29 | /// made absolute, or whether to use the relative path. 30 | void showAbsPaths(bool set) { absPaths = set; } 31 | 32 | protected: 33 | const DiagnosticEngine* engine = nullptr; 34 | const SourceManager* sourceManager = nullptr; 35 | bool absPaths = false; 36 | 37 | std::string getFileName(SourceLocation location) const; 38 | void getIncludeStack(BufferID buffer, SmallVectorBase& stack) const; 39 | std::string_view getSourceLine(SourceLocation location, size_t col) const; 40 | static std::string_view getSeverityString(DiagnosticSeverity severity); 41 | }; 42 | 43 | } // namespace slang 44 | -------------------------------------------------------------------------------- /include/slang/diagnostics/JsonDiagnosticClient.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file JsonDiagnosticClient.h 3 | //! @brief Diagnostic client that formats to JSON 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | 12 | #include "slang/diagnostics/DiagnosticClient.h" 13 | 14 | namespace slang { 15 | 16 | class JsonWriter; 17 | 18 | class SLANG_EXPORT JsonDiagnosticClient : public DiagnosticClient { 19 | public: 20 | JsonDiagnosticClient(JsonWriter& writer) : writer(writer) {} 21 | 22 | void report(const ReportedDiagnostic& diagnostic) override; 23 | 24 | private: 25 | JsonWriter& writer; 26 | 27 | void formatDiag(SourceLocation loc, std::span ranges, 28 | DiagnosticSeverity severity, std::string_view message, 29 | std::string_view optionName); 30 | }; 31 | 32 | } // namespace slang 33 | -------------------------------------------------------------------------------- /include/slang/diagnostics/TextDiagnosticClient.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file TextDiagnosticClient.h 3 | //! @brief Diagnostic client that formats to a text string 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | #include "slang/diagnostics/DiagnosticClient.h" 14 | 15 | namespace slang { 16 | 17 | class FormatBuffer; 18 | 19 | enum class ShowHierarchyPathOption { Auto, Always, Never }; 20 | 21 | class SLANG_EXPORT TextDiagnosticClient : public DiagnosticClient { 22 | public: 23 | fmt::terminal_color noteColor; 24 | fmt::terminal_color warningColor; 25 | fmt::terminal_color errorColor; 26 | fmt::terminal_color fatalColor; 27 | fmt::terminal_color highlightColor; 28 | fmt::terminal_color filenameColor; 29 | fmt::terminal_color locationColor; 30 | 31 | TextDiagnosticClient(); 32 | ~TextDiagnosticClient(); 33 | 34 | void showColors(bool show); 35 | void showColumn(bool show) { includeColumn = show; } 36 | void showLocation(bool show) { includeLocation = show; } 37 | void showSourceLine(bool show) { includeSource = show; } 38 | void showOptionName(bool show) { includeOptionName = show; } 39 | void showIncludeStack(bool show) { includeFileStack = show; } 40 | void showMacroExpansion(bool show) { includeExpansion = show; } 41 | void showHierarchyInstance(ShowHierarchyPathOption option) { includeHierarchy = option; } 42 | 43 | fmt::terminal_color getSeverityColor(DiagnosticSeverity severity) const; 44 | 45 | void report(const ReportedDiagnostic& diagnostic) override; 46 | 47 | void clear(); 48 | std::string getString() const; 49 | 50 | private: 51 | std::unique_ptr buffer; 52 | bool includeColumn = true; 53 | bool includeLocation = true; 54 | bool includeSource = true; 55 | bool includeOptionName = true; 56 | bool includeFileStack = true; 57 | bool includeExpansion = true; 58 | ShowHierarchyPathOption includeHierarchy = ShowHierarchyPathOption::Auto; 59 | 60 | void formatDiag(SourceLocation loc, std::span ranges, 61 | DiagnosticSeverity severity, std::string_view message, 62 | std::string_view optionName); 63 | }; 64 | 65 | } // namespace slang 66 | -------------------------------------------------------------------------------- /include/slang/parsing/LexerFacts.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file LexerFacts.h 3 | //! @brief Random lexer-related utility functions 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | 12 | #include "slang/util/FlatMap.h" 13 | #include "slang/util/LanguageVersion.h" 14 | 15 | namespace slang::syntax { 16 | enum class SyntaxKind; 17 | } 18 | 19 | namespace slang::parsing { 20 | 21 | enum class TokenKind : uint16_t; 22 | 23 | /// Different restricted sets of keywords that can be set using the 24 | /// `begin_keywords directive. 25 | enum class SLANG_EXPORT KeywordVersion : uint8_t { 26 | // Note: The values of the enum correspond to indexes to 27 | // allKeywords[] in LexerFacts.cpp 28 | v1364_1995 = 0, 29 | v1364_2001_noconfig = 1, 30 | v1364_2001 = 2, 31 | v1364_2005 = 3, 32 | v1800_2005 = 4, 33 | v1800_2009 = 5, 34 | v1800_2012 = 6, 35 | v1800_2017 = 7, 36 | v1800_2023 = 8 37 | }; 38 | 39 | class SLANG_EXPORT LexerFacts { 40 | public: 41 | static TokenKind getSystemKeywordKind(std::string_view text); 42 | static std::string_view getTokenKindText(TokenKind kind); 43 | static KeywordVersion getDefaultKeywordVersion(LanguageVersion languageVersion); 44 | static std::optional getKeywordVersion(std::string_view text); 45 | static const flat_hash_map* getKeywordTable( 46 | KeywordVersion version); 47 | 48 | static syntax::SyntaxKind getDirectiveKind(std::string_view directive, 49 | bool enableLegacyProtect); 50 | static std::string_view getDirectiveText(syntax::SyntaxKind kind); 51 | 52 | /// This checks all keywords, regardless of the current keyword table. Should 53 | /// only be used when it is ok to get a false positive for a keyword that may 54 | /// not currently be in the keyword table (such as handling macro arguments). 55 | static bool isKeyword(TokenKind kind); 56 | 57 | private: 58 | LexerFacts() = default; 59 | }; 60 | 61 | } // namespace slang::parsing 62 | -------------------------------------------------------------------------------- /include/slang/text/FormatBuffer.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // FormatBuffer.h 3 | // Internal string formatting helper class 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | namespace slang { 14 | 15 | class FormatBuffer { 16 | public: 17 | void append(std::string_view str) { buf.append(str.data(), str.data() + str.size()); } 18 | 19 | void append(const fmt::text_style& style, std::string_view str) { format(style, "{}", str); } 20 | 21 | template 22 | void format(fmt::format_string fmt, Args&&... args) { 23 | fmt::detail::vformat_to(buf, fmt::string_view(fmt), fmt::make_format_args(args...)); 24 | } 25 | 26 | template 27 | void format(const fmt::text_style& style, fmt::format_string fmt, Args&&... args) { 28 | if (!showColors) { 29 | fmt::detail::vformat_to(buf, fmt::string_view(fmt), fmt::make_format_args(args...)); 30 | } 31 | else { 32 | fmt::detail::vformat_to(buf, style, fmt::string_view(fmt), 33 | fmt::make_format_args(args...)); 34 | } 35 | } 36 | 37 | size_t size() const { return buf.size(); } 38 | const char* data() const { return buf.data(); } 39 | char back() const { return buf.data()[buf.size() - 1]; } 40 | bool empty() const { return buf.size() == 0; } 41 | 42 | void pop_back() { buf.resize(buf.size() - 1); } 43 | void clear() { buf.clear(); } 44 | void resize(size_t newSize) { buf.resize(newSize); } 45 | 46 | void setColorsEnabled(bool enabled) { showColors = enabled; } 47 | 48 | std::string str() const { return fmt::to_string(buf); } 49 | 50 | private: 51 | fmt::memory_buffer buf; 52 | bool showColors = false; 53 | }; 54 | 55 | } // namespace slang 56 | -------------------------------------------------------------------------------- /include/slang/util/ConcurrentMap.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file ConcurrentMap.h 3 | //! @brief Concurrent unordered hash map and set 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/util/Hash.h" 11 | 12 | #define BOOST_UNORDERED_DISABLE_PARALLEL_ALGORITHMS 13 | #ifdef SLANG_BOOST_SINGLE_HEADER 14 | # include 15 | #else 16 | # include 17 | # include 18 | #endif 19 | 20 | namespace slang { 21 | 22 | template, typename E = std::equal_to, 23 | typename A = std::allocator>> 24 | using concurrent_map = boost::concurrent_flat_map; 25 | 26 | template, typename E = std::equal_to, 27 | typename A = std::allocator> 28 | using concurrent_set = boost::concurrent_flat_set; 29 | 30 | } // namespace slang 31 | -------------------------------------------------------------------------------- /include/slang/util/FlatMap.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file FlatMap.h 3 | //! @brief Flat unordered hash map and set 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/util/Hash.h" 11 | 12 | #ifdef SLANG_BOOST_SINGLE_HEADER 13 | # include 14 | #else 15 | # include 16 | # include 17 | #endif 18 | 19 | namespace slang { 20 | 21 | template, typename E = std::equal_to, 22 | typename A = std::allocator>> 23 | using flat_hash_map = boost::unordered_flat_map; 24 | 25 | template, typename E = std::equal_to, 26 | typename A = std::allocator> 27 | using flat_hash_set = boost::unordered_flat_set; 28 | 29 | } // namespace slang 30 | -------------------------------------------------------------------------------- /include/slang/util/Function.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file Function.h 3 | //! @brief Function-related utilities 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace slang { 15 | 16 | /// An efficient, type-erasing, non-owning reference to a callable. This is 17 | /// intended for use as the type of a function parameter that is not used 18 | /// after the function in question returns. 19 | /// 20 | /// This class does not own the callable, so it is not in general safe to store 21 | /// a function_ref. 22 | /// 23 | /// NOTE: This is based on the type of the same name from the LLVM project. 24 | template 25 | class function_ref; 26 | 27 | template 28 | class function_ref { 29 | Ret (*callback)(intptr_t callable, Params... params) = nullptr; 30 | intptr_t callable; 31 | 32 | template 33 | static Ret callback_fn(intptr_t callable, Params... params) { 34 | return (*reinterpret_cast(callable))(std::forward(params)...); 35 | } 36 | 37 | public: 38 | function_ref() = default; 39 | function_ref(std::nullptr_t) {} 40 | 41 | template 42 | requires(!std::is_same_v, function_ref> && 43 | std::is_invocable_r_v) 44 | function_ref(Callable&& callable) : 45 | callback(callback_fn>), 46 | callable(reinterpret_cast(&callable)) {} 47 | 48 | /// Invokes the function with the given parameters. 49 | Ret operator()(Params... params) const { 50 | return callback(callable, std::forward(params)...); 51 | } 52 | 53 | /// @return true if the function_ref points to a valid function; otherwise, false 54 | /// if the function is null / empty. 55 | explicit operator bool() const { return callback; } 56 | }; 57 | 58 | } // namespace slang 59 | -------------------------------------------------------------------------------- /include/slang/util/LanguageVersion.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file LanguageVersion.h 3 | //! @brief Enum specify SystemVerilog language versions 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | 12 | namespace slang { 13 | 14 | /// Specifies SystemVerilog language versions. 15 | enum class LanguageVersion { v1800_2017, v1800_2023, Default = v1800_2017 }; 16 | 17 | inline std::string_view toString(LanguageVersion lv) { 18 | switch (lv) { 19 | case LanguageVersion::v1800_2017: 20 | return "1800-2017"; 21 | case LanguageVersion::v1800_2023: 22 | return "1800-2023"; 23 | } 24 | return ""; 25 | } 26 | 27 | } // namespace slang 28 | -------------------------------------------------------------------------------- /include/slang/util/PointerIntPair.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file PointerIntPair.h 3 | //! @brief Space optimized pointer + int pair type 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/util/Util.h" 11 | 12 | namespace slang { 13 | 14 | /// A data structure that operates as a pair of a pointer and an integer, 15 | /// which fits in the space of one full sized pointer by exploiting unused 16 | /// bits in the pointer representation. 17 | template 18 | class PointerIntPair { 19 | enum : uintptr_t { 20 | PointerMask = ~(uintptr_t)(((intptr_t)1 << LowBitsAvailable) - 1), 21 | IntShift = (uintptr_t)LowBitsAvailable - IntBits, 22 | IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1), 23 | ShiftedIntMask = (uintptr_t)(IntMask << IntShift) 24 | }; 25 | 26 | public: 27 | PointerIntPair() = default; 28 | PointerIntPair(TPointer ptr, TInt intVal) { setBoth(ptr, intVal); } 29 | 30 | TPointer getPointer() const { 31 | return static_cast(reinterpret_cast(value & PointerMask)); 32 | } 33 | 34 | TInt getInt() const { return TInt((value >> IntShift) & IntMask); } 35 | 36 | void setPointer(TPointer ptr) { 37 | uintptr_t ptrWord = reinterpret_cast(ptr); 38 | SLANG_ASSERT((ptrWord & ~PointerMask) == 0); 39 | value = ptrWord | (value & ~PointerMask); 40 | } 41 | 42 | void setInt(TInt intVal) { 43 | uintptr_t intWord = static_cast(intVal); 44 | SLANG_ASSERT((intWord & ~IntMask) == 0); 45 | value = (value & ~ShiftedIntMask) | (intWord << IntShift); 46 | } 47 | 48 | void setBoth(TPointer ptr, TInt intVal) { 49 | value = 0; 50 | setPointer(ptr); 51 | setInt(intVal); 52 | } 53 | 54 | uintptr_t getOpaqueValue() const { return value; } 55 | 56 | auto operator<=>(const PointerIntPair& rhs) const = default; 57 | 58 | private: 59 | uintptr_t value = 0; 60 | }; 61 | 62 | } // namespace slang 63 | -------------------------------------------------------------------------------- /include/slang/util/Random.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file Random.h 3 | //! @brief Utility functions related to randomization 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "slang/util/Util.h" 16 | 17 | namespace slang { 18 | 19 | /// Gets a (mostly) uniformly distributed value within the provided [min,max] range. 20 | /// This is a poor man's implementation of std::uniform_int_distribution because 21 | /// the standard type isn't required to produce identical results across implementations. 22 | /// 23 | /// This implementation is potentially slightly biased if you use a large range but 24 | /// slang doesn't need super precisely distributed random numbers for anything so it's fine. 25 | template 26 | static TValue getUniformIntDist(TGenerator& gen, TValue min, TValue max) { 27 | TValue range = max - min + 1; 28 | return (gen() % range) + min; 29 | } 30 | 31 | /// Create a randomly seeded random number generator. 32 | template 33 | static T createRandomGenerator() { 34 | auto constexpr seedBytes = sizeof(typename T::result_type) * T::state_size; 35 | auto constexpr seedLen = seedBytes / sizeof(std::seed_seq::result_type); 36 | auto seed = std::array(); 37 | auto dev = std::random_device(); 38 | std::ranges::generate_n(begin(seed), seedLen, std::ref(dev)); 39 | auto seedSeq = std::seed_seq(begin(seed), end(seed)); 40 | return T{seedSeq}; 41 | } 42 | 43 | } // namespace slang 44 | -------------------------------------------------------------------------------- /include/slang/util/ScopeGuard.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file ScopeGuard.h 3 | //! @brief Contains the ScopeGuard utility class 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | 12 | namespace slang { 13 | 14 | /// A simple guard class that ensures a given function is invoked when the 15 | /// guard object is destroyed. 16 | template 17 | class ScopeGuard { 18 | public: 19 | explicit ScopeGuard(F&& f) noexcept : func(std::move(f)) {} 20 | 21 | ScopeGuard(ScopeGuard&& other) noexcept : 22 | func(std::move(other.func)), valid(std::exchange(other.valid, false)) {} 23 | 24 | ScopeGuard(const ScopeGuard&) = delete; 25 | ScopeGuard& operator=(const ScopeGuard&) = delete; 26 | ScopeGuard& operator=(ScopeGuard&&) = delete; 27 | 28 | ~ScopeGuard() noexcept { 29 | if (valid) 30 | func(); 31 | } 32 | 33 | void release() noexcept { valid = false; } 34 | 35 | private: 36 | F func; 37 | bool valid = true; 38 | }; 39 | 40 | template 41 | explicit ScopeGuard(F&& f) -> ScopeGuard; 42 | 43 | } // namespace slang 44 | -------------------------------------------------------------------------------- /include/slang/util/VersionInfo.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file VersionInfo.h 3 | //! @brief Library build-time version information 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | 12 | #include "slang/slang_export.h" 13 | 14 | namespace slang { 15 | 16 | /// Provides access to compile-time generated version info about the library. 17 | class SLANG_EXPORT VersionInfo { 18 | public: 19 | /// Gets the major version number of the library. 20 | static int getMajor(); 21 | 22 | /// Gets the minor version number of the library. 23 | static int getMinor(); 24 | 25 | /// Gets the patch version number of the library. 26 | static int getPatch(); 27 | 28 | /// Gets a string describing the git hash of the library. 29 | static std::string_view getHash(); 30 | }; 31 | 32 | } // namespace slang 33 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "pyslang" 3 | version = "8.1.0" 4 | description = "Python bindings for slang, a library for compiling SystemVerilog" 5 | readme = { file = "README.md", content-type = "text/markdown" } 6 | authors = [{ name = "Mike Popoloski" }] 7 | keywords = ["slang", "verilog", "systemverilog", "parsing", "compiler", "eda"] 8 | license = { file = "LICENSE" } 9 | classifiers = [ 10 | "Development Status :: 5 - Production/Stable", 11 | "Intended Audience :: Developers", 12 | "License :: OSI Approved :: MIT License", 13 | "Operating System :: MacOS :: MacOS X", 14 | "Operating System :: Microsoft :: Windows", 15 | "Operating System :: POSIX :: Linux", 16 | "Operating System :: Unix", 17 | "Programming Language :: C++", 18 | "Programming Language :: Python :: 3", 19 | "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", 20 | "Topic :: Software Development :: Compilers", 21 | "Topic :: Software Development :: Libraries :: Python Modules", 22 | ] 23 | 24 | [project.urls] 25 | Homepage = "https://sv-lang.com/" 26 | Documentation = "https://sv-lang.com/pyslang/" 27 | Repository = "https://github.com/MikePopoloski/slang" 28 | Issues = "https://github.com/MikePopoloski/slang/issues" 29 | Changelog = "https://github.com/MikePopoloski/slang/blob/master/CHANGELOG.md" 30 | 31 | [project.optional-dependencies] 32 | test = ["pytest"] 33 | 34 | [build-system] 35 | requires = ["pybind11", "scikit-build-core"] 36 | build-backend = "scikit_build_core.build" 37 | 38 | [tool.scikit-build] 39 | cmake.source-dir = "." 40 | install.components = ["pylib"] 41 | sdist.exclude = [".github", "./build", "./install", "./tests", "./tools"] 42 | wheel.packages = ["pyslang/pyslang"] 43 | 44 | [tool.scikit-build.cmake.define] 45 | SLANG_INCLUDE_TESTS = "OFF" 46 | SLANG_INCLUDE_TOOLS = "OFF" 47 | SLANG_INCLUDE_PYLIB = "ON" 48 | 49 | [tool.cibuildwheel] 50 | archs = ["auto64"] 51 | manylinux-x86_64-image = "manylinux_2_28" 52 | manylinux-aarch64-image = "manylinux_2_28" 53 | skip = ["pp*", "cp36-*", "cp37-*", "cp38-*", "*musllinux*"] 54 | build-verbosity = 1 55 | test-command = "pytest {project}/pyslang/tests" 56 | test-extras = ["test"] 57 | test-skip = ["*universal2:arm64"] 58 | 59 | [tool.pytest.ini_options] 60 | testpaths = ["pyslang/tests"] 61 | -------------------------------------------------------------------------------- /pyslang/docs/conf.py: -------------------------------------------------------------------------------- 1 | # Docs: https://mcss.mosra.cz/documentation/python/ 2 | # Run this file with `/documentation/python.py ` 3 | 4 | import os 5 | 6 | root_dir = os.environ["DOC_OUTPUT_DIR"] 7 | 8 | # Set the m.css configuration variables. 9 | PROJECT_TITLE = "pyslang" 10 | INPUT_MODULES = ["pyslang"] 11 | PYBIND11_COMPATIBILITY = True 12 | OUTPUT = root_dir 13 | 14 | INPUT_PAGES = [ 15 | "pages/index.rst", # Custom home page. `index.rst` gets used as the homepage by default. 16 | ] 17 | 18 | LINKS_NAVBAR1 = [ 19 | ("Slang Documentation", "https://sv-lang.com/", []), 20 | # Default links: 21 | # ("Pages", "pages", []), # -> Currently empty. 22 | ("Modules", "modules", []), 23 | ("Classes", "classes", []), 24 | # End default links. 25 | ("GitHub", "https://github.com/MikePopoloski/slang", []), 26 | ("PyPI", "https://pypi.org/project/pyslang/", []), 27 | ] 28 | 29 | # Output the stubs for comparison/review, but not actually used. 30 | OUTPUT_STUBS = os.path.join(OUTPUT, "stubs") 31 | -------------------------------------------------------------------------------- /pyslang/docs/pages/index.rst: -------------------------------------------------------------------------------- 1 | Pyslang 2 | =============== 3 | 4 | The Slang project contains Python bindings, packaged as a library called ``pyslang``. 5 | 6 | This library provides a Pythonic interface to the Slang parser and evaluator, allowing you to load SystemVerilog source files, inspect the syntax tree, and evaluate expressions. 7 | 8 | .. code-block:: bash 9 | 10 | pip install pyslang 11 | 12 | or, to update your installed version to the latest release: 13 | 14 | .. code-block:: bash 15 | 16 | pip install -U pyslang 17 | 18 | or, to checkout and install a local build: 19 | 20 | .. code-block:: bash 21 | 22 | git clone https://github.com/MikePopoloski/slang.git 23 | cd slang 24 | pip install . 25 | 26 | Example Python Usage 27 | ==================== 28 | 29 | Given a ``test.sv`` source file: 30 | 31 | .. code-block:: systemverilog 32 | 33 | module memory( 34 | address, 35 | data_in, 36 | data_out, 37 | read_write, 38 | chip_en 39 | ); 40 | 41 | input wire [7:0] address, data_in; 42 | output reg [7:0] data_out; 43 | input wire read_write, chip_en; 44 | 45 | reg [7:0] mem [0:255]; 46 | 47 | always @ (address or data_in or read_write or chip_en) 48 | if (read_write == 1 && chip_en == 1) begin 49 | mem[address] = data_in; 50 | end 51 | 52 | always @ (read_write or chip_en or address) 53 | if (read_write == 0 && chip_en) 54 | data_out = mem[address]; 55 | else 56 | data_out = 0; 57 | 58 | endmodule 59 | 60 | We can use slang to load the syntax tree and inspect it: 61 | 62 | .. code-block:: python 63 | 64 | import pyslang 65 | 66 | tree = pyslang.SyntaxTree.fromFile('test.sv') 67 | mod = tree.root.members[0] 68 | print(mod.header.name.value) 69 | print(mod.members[0].kind) 70 | print(mod.members[1].header.dataType) 71 | 72 | Expected output: 73 | 74 | .. code-block:: text 75 | 76 | memory 77 | SyntaxKind.PortDeclaration 78 | reg [7:0] 79 | 80 | We can also evaluate arbitrary SystemVerilog expressions: 81 | 82 | .. code-block:: python 83 | 84 | session = pyslang.ScriptSession() 85 | session.eval("logic bit_arr [16] = '{0:1, 1:1, 2:1, default:0};") 86 | result = session.eval("bit_arr.sum with ( int'(item) );") 87 | print(result) 88 | 89 | Expected output: 90 | 91 | .. code-block:: text 92 | 93 | 3 94 | -------------------------------------------------------------------------------- /pyslang/examples/driver.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Michael Popoloski 2 | # SPDX-License-Identifier: MIT 3 | 4 | import sys 5 | 6 | from pyslang import CommandLineOptions, Driver 7 | 8 | 9 | def main(): 10 | """Reads a list of files as command line arguments and parses them using the slang driver. 11 | 12 | After compilation/elaboration, any diagnostics (e.g., syntax errors) are reported to the console. 13 | Writes to both stderr and stdout. 14 | """ 15 | # Create a slang driver with default command line arguments 16 | driver = Driver() 17 | driver.addStandardArgs() 18 | 19 | # Parse command line arguments 20 | args = " ".join(sys.argv) 21 | if not driver.parseCommandLine(args, CommandLineOptions()): 22 | return 23 | 24 | # Process options and parse all provided sources 25 | if not driver.processOptions() or not driver.parseAllSources(): 26 | return 27 | 28 | # Perform elaboration and report all diagnostics 29 | driver.runFullCompilation(quiet=False) 30 | 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /pyslang/tests/some_file.svh: -------------------------------------------------------------------------------- 1 | parameter p = 1; 2 | -------------------------------------------------------------------------------- /pyslang/tests/test_accessing_spans.py: -------------------------------------------------------------------------------- 1 | """Test accessing `std::span` elements.""" 2 | 3 | import pyslang 4 | 5 | CASE_STATEMENT_VERILOG_1 = """ 6 | module simple_alu ( 7 | input [1:0] opcode, // Operation code 8 | input [3:0] a, // First operand 9 | input [3:0] b, // Second operand 10 | output reg [3:0] result // Result of operation 11 | ); 12 | 13 | always @(*) begin 14 | case (opcode) 15 | 2'b00: result = a + b; // Add 16 | 2'b01: result = a - b; // Subtract 17 | 2'b10: result = a & b; // Bitwise AND 18 | 2'b11: result = a | b; // Bitwise OR 19 | default: result = 4'b0000; // Default case 20 | endcase 21 | end 22 | 23 | endmodule 24 | """ 25 | 26 | 27 | def test_continuous_assign_expression_access_span() -> None: 28 | tree = pyslang.SyntaxTree.fromText(CASE_STATEMENT_VERILOG_1, "test.sv") 29 | 30 | compilation = pyslang.Compilation() 31 | compilation.addSyntaxTree(tree) 32 | 33 | # `compilation.getCompilationUnits()`, in C++, returns a `std::span` object. Check that it is 34 | # accessible and converted to a list with the Python bindings. 35 | std_span_as_list = compilation.getCompilationUnits() 36 | assert std_span_as_list is not None 37 | assert isinstance(std_span_as_list, list) 38 | assert len(std_span_as_list) == 1 39 | assert isinstance(std_span_as_list[0], pyslang.Symbol) 40 | assert isinstance(std_span_as_list[0], pyslang.CompilationUnitSymbol) 41 | 42 | 43 | def test_token_construction() -> None: 44 | t1 = pyslang.Token() 45 | assert isinstance(t1, pyslang.Token) 46 | 47 | t2 = pyslang.Token( 48 | pyslang.BumpAllocator(), 49 | pyslang.TokenKind(12), 50 | [pyslang.Trivia()], # This argument, in C++, is a `std::span` object. 51 | "'{", 52 | pyslang.SourceLocation(), 53 | ) 54 | assert isinstance(t2, pyslang.Token) 55 | assert str(t2) == "'{" 56 | -------------------------------------------------------------------------------- /pyslang/tests/test_analysis.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Michael Popoloski 2 | # SPDX-License-Identifier: MIT 3 | 4 | import pyslang 5 | 6 | 7 | def test_driver_analysis(): 8 | """Test analysis of variable drivers""" 9 | 10 | tree = pyslang.SyntaxTree.fromText( 11 | """ 12 | module m; 13 | int i; 14 | always @* i = 1; 15 | endmodule 16 | 17 | module top; 18 | m m1(); 19 | 20 | always @* m1.i = 2; 21 | endmodule 22 | """ 23 | ) 24 | compilation = pyslang.Compilation() 25 | compilation.addSyntaxTree(tree) 26 | compilation.getAllDiagnostics() 27 | 28 | i = compilation.getRoot().lookupName("top.m1.i") 29 | 30 | analysisManager = pyslang.AnalysisManager() 31 | analysisManager.analyze(compilation) 32 | 33 | assert i is not None 34 | drivers = analysisManager.getDrivers(i) 35 | assert len(drivers) == 2 36 | -------------------------------------------------------------------------------- /pyslang/tests/test_custom_systasks.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Michael Popoloski 2 | # SPDX-License-Identifier: MIT 3 | 4 | import pyslang 5 | 6 | testfile = """ 7 | module m; 8 | real r; 9 | initial begin 10 | r = $foo("asdf"); 11 | end 12 | 13 | $info("bar:%0d", $bar(42)); 14 | endmodule 15 | """ 16 | 17 | 18 | def test_custom_systasks(): 19 | c = pyslang.Compilation() 20 | c.addSyntaxTree(pyslang.SyntaxTree.fromText(testfile)) 21 | 22 | foo = pyslang.NonConstantFunction("$foo", c.realType, 1, [c.stringType]) 23 | c.addSystemSubroutine(foo) 24 | 25 | class BarFunc(pyslang.SimpleSystemSubroutine): 26 | def __init__(self): 27 | pyslang.SimpleSystemSubroutine.__init__( 28 | self, 29 | "$bar", 30 | pyslang.SubroutineKind.Function, 31 | 1, 32 | [c.intType], 33 | c.intType, 34 | False, 35 | False, 36 | ) 37 | 38 | def eval(self, context, args, sourceRange, callInfo): 39 | cv = args[0].eval(context) 40 | if not cv: 41 | return cv 42 | 43 | return cv.value + 10 44 | 45 | c.addSystemSubroutine(BarFunc()) 46 | 47 | diags = c.getAllDiagnostics() 48 | report = pyslang.DiagnosticEngine.reportAll(c.sourceManager, diags) 49 | assert ( 50 | ("\n" + report) 51 | == """ 52 | source:8:5: note: $info encountered: bar:52 53 | $info("bar:%0d", $bar(42)); 54 | ^ 55 | """ 56 | ) 57 | -------------------------------------------------------------------------------- /pyslang/tests/test_extract_comments.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Michael Popoloski 2 | # SPDX-License-Identifier: MIT 3 | 4 | import pyslang 5 | 6 | testfile = """ 7 | //! Module description 8 | //! ***this is code*** sample 9 | //! | Tables | Are | Cool | 10 | //! |----------|:------------:|------:| 11 | //! | col 1 is | left-aligned | $1600 | 12 | 13 | module gray_counter ( 14 | out , // counter out 15 | clk , //! clock 16 | clk1 , //! clock sample 17 | rst //! **active high reset** 18 | ); 19 | 20 | input clk, clk1, rst; 21 | output [7:0] out; 22 | wire [7:0] out; 23 | reg [7:0] count; 24 | 25 | endmodule 26 | """ 27 | 28 | 29 | def test_extract_comments(): 30 | tree = pyslang.SyntaxTree.fromText(testfile) 31 | assert tree.root.kind == pyslang.SyntaxKind.ModuleDeclaration 32 | 33 | moduleComments = [] 34 | for t in tree.root.getFirstToken().trivia: 35 | if t.kind == pyslang.TriviaKind.LineComment: 36 | comment = t.getRawText() 37 | if comment.startswith("//!"): 38 | moduleComments.append(comment[3:].strip()) 39 | 40 | portComments = {} 41 | portList = tree.root.header.ports 42 | lastPort = None 43 | 44 | def getLeadingComments(tok): 45 | if lastPort is not None: 46 | for t in tok.trivia: 47 | if t.kind == pyslang.TriviaKind.LineComment: 48 | comment = t.getRawText() 49 | if comment.startswith("//!"): 50 | portComments[lastPort].append(comment[3:].strip()) 51 | elif t.kind == pyslang.TriviaKind.EndOfLine: 52 | break 53 | 54 | if portList is not None: 55 | for port in portList.ports: 56 | if isinstance(port, pyslang.ImplicitNonAnsiPortSyntax): 57 | tok = port.getFirstToken() 58 | getLeadingComments(tok) 59 | 60 | portName = tok.value 61 | portComments[portName] = [] 62 | lastPort = portName 63 | 64 | getLeadingComments(portList.closeParen) 65 | 66 | assert len(moduleComments) == 5 67 | assert moduleComments[4] == "| col 1 is | left-aligned | $1600 |" 68 | 69 | for k, _ in portComments.copy().items(): 70 | if len(portComments[k]) == 0: 71 | del portComments[k] 72 | 73 | assert len(portComments) == 3 74 | assert portComments["rst"][0] == "**active high reset**" 75 | -------------------------------------------------------------------------------- /scripts/grammar_gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Takes an input text file containing the SystemVerilog language grammar 3 | # and converts it to nice linked markdown. 4 | # 5 | # SPDX-FileCopyrightText: Michael Popoloski 6 | # SPDX-License-Identifier: MIT 7 | 8 | import os 9 | import re 10 | 11 | ourdir = os.path.dirname(os.path.realpath(__file__)) 12 | 13 | inf = open(os.path.join(ourdir, "grammar.txt")) 14 | outf = open(os.path.join(ourdir, "../docs/grammar.md"), "w") 15 | 16 | outf.write("# SystemVerilog\n") 17 | 18 | 19 | def entry(line): 20 | match = re.match(r"(\w+) ::=", line) 21 | if match: 22 | s = match.group(1) 23 | outf.write('{} ::='.format(s, s.replace("_", "\\_"))) 24 | line = line[len(match.group(0)) :] 25 | else: 26 | outf.write("    ") 27 | 28 | saved_match = None 29 | match = re.search(r"(\w+) ::=", line) 30 | if match: 31 | saved_match = line[match.start() :] 32 | line = line[: match.start()] 33 | 34 | def replacer(m): 35 | s = m.group(1) 36 | t = s 37 | for c in ["*", "-", "|", "[", "{", "_"]: 38 | t = t.replace(c, "\\" + c) 39 | if s not in ["|", "[", "]", "{", "}"]: 40 | return "`{}`".format(s) 41 | return t 42 | 43 | line = line.replace("{,", "{ ,") 44 | line = re.sub(r"([^\w\s]+)", replacer, line) 45 | line = re.sub(r"(\w+)", r"[\1](#\1)", line) 46 | outf.write(line + " \n") 47 | 48 | if saved_match: 49 | entry(saved_match) 50 | 51 | 52 | for line in inf: 53 | line = line.strip() 54 | if not line: 55 | continue 56 | 57 | if line.startswith("A."): 58 | count = line.split(" ")[0].count(".") 59 | if count == 1: 60 | outf.write("## ") 61 | elif count == 2: 62 | outf.write("### ") 63 | elif count == 3: 64 | outf.write("#### ") 65 | else: 66 | raise Exception("wut") 67 | outf.write(line + "\n") 68 | else: 69 | entry(line) 70 | -------------------------------------------------------------------------------- /scripts/sv-lang.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | includedir="${prefix}/@CMAKE_INSTALL_INCLUDEDIR@" 3 | libdir="${prefix}/@CMAKE_INSTALL_LIBDIR@" 4 | 5 | Name: @PROJECT_NAME@ 6 | Description: @PROJECT_DESCRIPTION@ 7 | URL: @PROJECT_HOMEPAGE_URL@ 8 | Version: @PROJECT_VERSION@ 9 | Cflags: -I"${includedir}" 10 | Libs: -L"${libdir}" -lsvlang 11 | -------------------------------------------------------------------------------- /scripts/triviakinds.txt: -------------------------------------------------------------------------------- 1 | Unknown 2 | Whitespace 3 | EndOfLine 4 | LineComment 5 | BlockComment 6 | DisabledText 7 | SkippedTokens 8 | SkippedSyntax 9 | Directive 10 | -------------------------------------------------------------------------------- /scripts/win32.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | UTF-8 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /slang.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | empty 6 | {{ size={sz()} }} 7 | 8 | sz() 9 | 10 | 11 | 12 | 13 | sz() 14 | 15 | 16 | 17 | n=0 18 | pg++ 19 | 20 | 21 | 22 | *p 23 | 24 | n++ 25 | p++ 26 | 27 | 28 | 29 | 30 | 31 | 32 | {v} 33 | 34 | 35 | {v} 36 | 37 | 38 | {value} 39 | 40 | 41 | null 42 | 43 | 44 | $ 45 | 46 | 47 | {bitWidth}'sd{val} 48 | {bitWidth}'d{val} 49 | <unknown bits> 50 | {{bits={bitWidth} signed={signFlag}}} 51 | 52 | 53 | -------------------------------------------------------------------------------- /source/analysis/ValueDriver.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // ValueDriver.cpp 3 | // Tracking of assigned / driven symbols 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #include "slang/analysis/ValueDriver.h" 9 | 10 | #include "slang/ast/symbols/BlockSymbols.h" 11 | 12 | namespace slang::analysis { 13 | 14 | using namespace ast; 15 | 16 | ValueDriver::ValueDriver(DriverKind kind, const Expression& longestStaticPrefix, 17 | const Symbol& containingSymbol, bitmask flags) : 18 | prefixExpression(&longestStaticPrefix), containingSymbol(&containingSymbol), flags(flags), 19 | kind(kind) { 20 | 21 | if (containingSymbol.kind == SymbolKind::ProceduralBlock) { 22 | source = static_cast( 23 | containingSymbol.as().procedureKind); 24 | } 25 | else if (containingSymbol.kind == SymbolKind::Subroutine) { 26 | source = DriverSource::Subroutine; 27 | } 28 | else { 29 | source = DriverSource::Other; 30 | } 31 | } 32 | 33 | SourceRange ValueDriver::getSourceRange() const { 34 | if (procCallExpression) 35 | return procCallExpression->sourceRange; 36 | return prefixExpression->sourceRange; 37 | } 38 | 39 | } // namespace slang::analysis 40 | -------------------------------------------------------------------------------- /source/ast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | target_sources( 7 | slang_slang 8 | PRIVATE builtins/ArrayMethods.cpp 9 | builtins/ConversionFuncs.cpp 10 | builtins/CoverageFuncs.cpp 11 | builtins/EnumMethods.cpp 12 | builtins/GateTypes.cpp 13 | builtins/MathFuncs.cpp 14 | builtins/MiscSystemFuncs.cpp 15 | builtins/NonConstFuncs.cpp 16 | builtins/QueryFuncs.cpp 17 | builtins/StdPackage.cpp 18 | builtins/StringMethods.cpp 19 | builtins/SystemTasks.cpp 20 | expressions/AssertionExpr.cpp 21 | expressions/AssignmentExpressions.cpp 22 | expressions/CallExpression.cpp 23 | expressions/ConversionExpression.cpp 24 | expressions/LiteralExpressions.cpp 25 | expressions/MiscExpressions.cpp 26 | expressions/Operator.cpp 27 | expressions/OperatorExpressions.cpp 28 | expressions/SelectExpressions.cpp 29 | statements/ConditionalStatements.cpp 30 | statements/LoopStatements.cpp 31 | statements/MiscStatements.cpp 32 | symbols/AttributeSymbol.cpp 33 | symbols/BlockSymbols.cpp 34 | symbols/ClassSymbols.cpp 35 | symbols/CompilationUnitSymbols.cpp 36 | symbols/CoverSymbols.cpp 37 | symbols/InstanceSymbols.cpp 38 | symbols/MemberSymbols.cpp 39 | symbols/ParameterBuilder.cpp 40 | symbols/ParameterSymbols.cpp 41 | symbols/PortSymbols.cpp 42 | symbols/SpecifySymbols.cpp 43 | symbols/SubroutineSymbols.cpp 44 | symbols/SymbolBuilders.cpp 45 | symbols/ValueSymbol.cpp 46 | symbols/VariableSymbols.cpp 47 | types/AllTypes.cpp 48 | types/DeclaredType.cpp 49 | types/NetType.cpp 50 | types/TypePrinter.cpp 51 | types/Type.cpp 52 | ASTContext.cpp 53 | ASTDiagMap.cpp 54 | ASTSerializer.cpp 55 | Bitstream.cpp 56 | Compilation.cpp 57 | Constraints.cpp 58 | EvalContext.cpp 59 | Expression.cpp 60 | FmtHelpers.cpp 61 | HierarchicalReference.cpp 62 | Lookup.cpp 63 | LSPUtilities.cpp 64 | LValue.cpp 65 | OpaqueInstancePath.cpp 66 | Patterns.cpp 67 | Scope.cpp 68 | ScriptSession.cpp 69 | SemanticFacts.cpp 70 | SFormat.cpp 71 | Statement.cpp 72 | Symbol.cpp 73 | SystemSubroutine.cpp 74 | TimingControl.cpp) 75 | -------------------------------------------------------------------------------- /source/ast/FmtHelpers.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // FmtHelpers.h 3 | // Helpers for implementing the string formatting system functions 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "slang/text/SourceLocation.h" 15 | 16 | namespace slang::ast { 17 | 18 | class ASTContext; 19 | class Expression; 20 | class EvalContext; 21 | class Scope; 22 | 23 | class FmtHelpers { 24 | public: 25 | static bool checkDisplayArgs(const ASTContext& context, 26 | const std::span& args); 27 | 28 | static bool checkSFormatArgs(const ASTContext& context, 29 | const std::span& args); 30 | 31 | static std::optional formatArgs(std::string_view formatString, SourceLocation loc, 32 | const Scope& scope, EvalContext& context, 33 | const std::span& args, 34 | bool isStringLiteral); 35 | 36 | static std::optional formatDisplay(const Scope& scope, EvalContext& context, 37 | const std::span& args); 38 | 39 | static bool checkFinishNum(const ASTContext& context, const Expression& arg); 40 | 41 | private: 42 | FmtHelpers() = default; 43 | }; 44 | 45 | } // namespace slang::ast 46 | -------------------------------------------------------------------------------- /source/ast/symbols/ValueSymbol.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // ValueSymbol.cpp 3 | // Base class for all value symbols 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #include "slang/ast/symbols/ValueSymbol.h" 9 | 10 | #include "slang/ast/Compilation.h" 11 | #include "slang/ast/Scope.h" 12 | #include "slang/ast/symbols/VariableSymbols.h" 13 | #include "slang/syntax/AllSyntax.h" 14 | 15 | namespace slang::ast { 16 | 17 | using namespace syntax; 18 | 19 | ValueSymbol::ValueSymbol(SymbolKind kind, std::string_view name, SourceLocation location, 20 | bitmask flags) : 21 | Symbol(kind, name, location), declaredType(*this, flags) { 22 | } 23 | 24 | void ValueSymbol::setFromDeclarator(const DeclaratorSyntax& decl) { 25 | declaredType.setFromDeclarator(decl); 26 | setSyntax(decl); 27 | } 28 | 29 | bool ValueSymbol::isKind(SymbolKind kind) { 30 | switch (kind) { 31 | case SymbolKind::Net: 32 | case SymbolKind::EnumValue: 33 | case SymbolKind::Parameter: 34 | case SymbolKind::PrimitivePort: 35 | case SymbolKind::ModportPort: 36 | case SymbolKind::Specparam: 37 | return true; 38 | default: 39 | return VariableSymbol::isKind(kind); 40 | } 41 | } 42 | 43 | void ValueSymbol::addPortBackref(const PortSymbol& port) const { 44 | auto scope = getParentScope(); 45 | SLANG_ASSERT(scope); 46 | 47 | auto& comp = scope->getCompilation(); 48 | firstPortBackref = comp.emplace(port, firstPortBackref); 49 | } 50 | 51 | } // namespace slang::ast 52 | -------------------------------------------------------------------------------- /source/text/SourceLocation.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SourceLocation.cpp 3 | // Source element location tracking 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #include "slang/text/SourceLocation.h" 9 | 10 | namespace slang { 11 | 12 | const SourceLocation SourceLocation::NoLocation{BufferID((1u << 28) - 1, ""), (1ull << 36) - 1}; 13 | const SourceRange SourceRange::NoLocation{SourceLocation::NoLocation, SourceLocation::NoLocation}; 14 | 15 | } // namespace slang 16 | -------------------------------------------------------------------------------- /source/util/BumpAllocator.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // BumpAllocator.cpp 3 | // Fast allocator based on pointer bumping 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #include "slang/util/BumpAllocator.h" 9 | 10 | #include 11 | 12 | namespace slang { 13 | 14 | BumpAllocator::BumpAllocator() { 15 | head = allocSegment(nullptr, INITIAL_SIZE); 16 | endPtr = (byte*)head + INITIAL_SIZE; 17 | } 18 | 19 | BumpAllocator::~BumpAllocator() { 20 | Segment* seg = head; 21 | while (seg) { 22 | Segment* prev = seg->prev; 23 | ::operator delete(seg); 24 | seg = prev; 25 | } 26 | } 27 | 28 | BumpAllocator::BumpAllocator(BumpAllocator&& other) noexcept : 29 | head(std::exchange(other.head, nullptr)), endPtr(other.endPtr) { 30 | } 31 | 32 | BumpAllocator& BumpAllocator::operator=(BumpAllocator&& other) noexcept { 33 | if (this != &other) { 34 | this->~BumpAllocator(); 35 | new (this) BumpAllocator(std::move(other)); 36 | } 37 | return *this; 38 | } 39 | 40 | void BumpAllocator::steal(BumpAllocator&& other) { 41 | SLANG_ASSERT(!isFrozen()); 42 | Segment* seg = other.head; 43 | if (!seg) 44 | return; 45 | 46 | while (seg->prev) 47 | seg = seg->prev; 48 | 49 | seg->prev = head->prev; 50 | head->prev = std::exchange(other.head, nullptr); 51 | } 52 | 53 | byte* BumpAllocator::allocateSlow(size_t size, size_t alignment) { 54 | // for really large allocations, give them their own segment 55 | if (size > (SEGMENT_SIZE >> 1)) { 56 | size = (size + alignment - 1) & ~(alignment - 1); 57 | head->prev = allocSegment(head->prev, size + sizeof(Segment)); 58 | return alignPtr(head->prev->current, alignment); 59 | } 60 | 61 | // otherwise, start a new block 62 | head = allocSegment(head, SEGMENT_SIZE); 63 | endPtr = (byte*)head + SEGMENT_SIZE; 64 | return allocate(size, alignment); 65 | } 66 | 67 | BumpAllocator::Segment* BumpAllocator::allocSegment(Segment* prev, size_t size) { 68 | auto seg = (Segment*)::operator new(size); 69 | seg->prev = prev; 70 | seg->current = (byte*)seg + sizeof(Segment); 71 | return seg; 72 | } 73 | 74 | } // namespace slang 75 | -------------------------------------------------------------------------------- /source/util/SmallVector.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // SmallVector.cpp 3 | // 4 | // SPDX-FileCopyrightText: Michael Popoloski 5 | // SPDX-License-Identifier: MIT 6 | //------------------------------------------------------------------------------ 7 | #include "slang/util/SmallVector.h" 8 | 9 | namespace slang::detail { 10 | 11 | void throwOutOfRange() { 12 | SLANG_THROW(std::out_of_range("vector index out of range")); 13 | } 14 | 15 | void throwLengthError() { 16 | SLANG_THROW(std::length_error("vector is at maximum size")); 17 | } 18 | 19 | } // namespace slang::detail 20 | -------------------------------------------------------------------------------- /source/util/Util.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Util.cpp 3 | // Various utility functions and basic types used throughout the library 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #include "slang/util/Util.h" 9 | 10 | #include 11 | 12 | #if defined(SLANG_USE_MIMALLOC) 13 | # include 14 | #endif 15 | 16 | namespace slang::assert { 17 | 18 | [[noreturn]] void assertFailed(const char* expr, const std::source_location& location) { 19 | auto msg = fmt::format("Assertion '{}' failed\n in file {}, line {}\n" 20 | " function: {}\n", 21 | expr, location.file_name(), location.line(), location.function_name()); 22 | 23 | #if __cpp_exceptions 24 | throw AssertionException(msg); 25 | #else 26 | fprintf(stderr, "%s", msg.c_str()); 27 | std::abort(); 28 | #endif 29 | } 30 | 31 | #if !__cpp_exceptions 32 | [[noreturn]] void handleThrow(const char* msg, const std::source_location& location) { 33 | fprintf(stderr, 34 | "internal compiler error: '%s'\n in file %s, line %d\n" 35 | " function: %s\n", 36 | msg, location.file_name(), location.line(), location.function_name()); 37 | std::abort(); 38 | } 39 | #endif 40 | 41 | [[noreturn]] void handleUnreachable(const std::source_location& location) { 42 | auto msg = fmt::format("Supposedly unreachable code was executed\n in file {}, line {}\n" 43 | " function: {}\n", 44 | location.file_name(), location.line(), location.function_name()); 45 | 46 | #if __cpp_exceptions 47 | throw std::logic_error(msg); 48 | #else 49 | fprintf(stderr, "%s", msg.c_str()); 50 | std::abort(); 51 | #endif 52 | } 53 | 54 | } // namespace slang::assert 55 | -------------------------------------------------------------------------------- /source/util/VersionInfo.cpp.in: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // VersionInfo.cpp 3 | // Input file for build-time version source generation 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #include "slang/util/VersionInfo.h" 9 | 10 | using std::string_view; 11 | using namespace std::literals; 12 | 13 | namespace slang { 14 | 15 | int VersionInfo::getMajor() { 16 | return @SLANG_VERSION_MAJOR@; 17 | } 18 | 19 | int VersionInfo::getMinor() { 20 | return @SLANG_VERSION_MINOR@; 21 | } 22 | 23 | int VersionInfo::getPatch() { 24 | return @SLANG_VERSION_PATCH@; 25 | } 26 | 27 | string_view VersionInfo::getHash() { 28 | return "@SLANG_VERSION_HASH@"sv; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_subdirectory(unittests) 7 | add_subdirectory(regression) 8 | -------------------------------------------------------------------------------- /tests/regression/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_test(NAME regression_delayed_reg 7 | COMMAND slang::driver "${CMAKE_CURRENT_LIST_DIR}/delayed_reg.v") 8 | add_test(NAME regression_wire_module 9 | COMMAND slang::driver "${CMAKE_CURRENT_LIST_DIR}/wire_module.v") 10 | add_test(NAME regression_all_file 11 | COMMAND slang::driver "${CMAKE_CURRENT_LIST_DIR}/all.sv" 12 | "--ast-json=-" "--ast-json-detailed-types") 13 | 14 | if(SLANG_INCLUDE_UVM_TEST) 15 | FetchContent_Declare( 16 | uvm 17 | URL https://github.com/accellera-official/uvm-core/archive/refs/tags/2020.3.1.zip 18 | URL_HASH MD5=38c2191e13242da4823e00051e406781 19 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/uvm" 20 | UPDATE_DISCONNECTED YES) 21 | FetchContent_MakeAvailable(uvm) 22 | 23 | add_test(NAME regression_uvm 24 | COMMAND slang::driver "${CMAKE_CURRENT_BINARY_DIR}/uvm/src/uvm.sv" 25 | "-I" "${CMAKE_CURRENT_BINARY_DIR}/uvm/src") 26 | endif() 27 | -------------------------------------------------------------------------------- /tests/regression/delayed_reg.v: -------------------------------------------------------------------------------- 1 | module delayed_reg (input logic in, output logic out); 2 | 3 | always @(posedge in) begin 4 | #10 out = !out; 5 | end 6 | 7 | endmodule 8 | -------------------------------------------------------------------------------- /tests/regression/wire_module.v: -------------------------------------------------------------------------------- 1 | module wire_module (input in, output out); 2 | 3 | assign out = in; 4 | 5 | endmodule 6 | -------------------------------------------------------------------------------- /tests/snippets.md: -------------------------------------------------------------------------------- 1 | These are snippets of code that I've run into that cause problems for other 2 | tools and should probably get coverage in slang at some point. It's not always 3 | clear from the spec how they ought to behave. 4 | 5 | 6 | ``` 7 | module m(a); 8 | output signed a; 9 | 10 | struct packed { logic[2:0] f; } a; 11 | 12 | initial begin 13 | a = 3'b100; 14 | $display(a > 0); 15 | end 16 | 17 | endmodule 18 | ``` 19 | 20 | ``` 21 | module m; 22 | logic [1:4] foo = 4'b0101; 23 | logic [0:1] a = 1; 24 | initial $display("%d", $left(foo[a+:2])); 25 | endmodule 26 | ``` 27 | 28 | ``` 29 | module n({a, b}); 30 | ref logic a; 31 | input wire b; 32 | endmodule 33 | 34 | module m; 35 | logic [1:0] a; 36 | 37 | n n1(a); 38 | endmodule 39 | ``` 40 | 41 | ``` 42 | localparam int k = 1; 43 | 44 | class C; 45 | extern function int foo(int i = k); 46 | localparam int k = 2; 47 | endclass 48 | 49 | function int C::foo(int i); 50 | return i; 51 | endfunction 52 | 53 | module m; 54 | C c = new; 55 | initial $display(c.foo()); 56 | endmodule 57 | ``` 58 | -------------------------------------------------------------------------------- /tests/unittests/analysis/AnalysisTests.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | #pragma once 4 | 5 | #include "Test.h" 6 | #include 7 | 8 | #include "slang/analysis/AbstractFlowAnalysis.h" 9 | #include "slang/analysis/AnalysisManager.h" 10 | 11 | #define CHECK_DIAGS_EMPTY \ 12 | do { \ 13 | if (!diags.empty()) { \ 14 | FAIL_CHECK(report(diags)); \ 15 | } \ 16 | } while (0) 17 | 18 | using namespace slang::analysis; 19 | 20 | inline std::pair analyze(const std::string& text, 21 | Compilation& compilation, 22 | AnalysisManager& analysisManager) { 23 | auto tree = SyntaxTree::fromText(text); 24 | compilation.addSyntaxTree(tree); 25 | auto diags = compilation.getAllDiagnostics(); 26 | if (!std::ranges::all_of(diags, [](auto& diag) { return !diag.isError(); })) { 27 | FAIL_CHECK(report(diags)); 28 | return {}; 29 | } 30 | 31 | compilation.freeze(); 32 | 33 | auto design = analysisManager.analyze(compilation); 34 | return {analysisManager.getDiagnostics(compilation.getSourceManager()), design}; 35 | } 36 | -------------------------------------------------------------------------------- /tests/unittests/data/cmd.f: -------------------------------------------------------------------------------- 1 | --compat vcs 2 | -v test.sv 3 | -------------------------------------------------------------------------------- /tests/unittests/data/cmd2.f: -------------------------------------------------------------------------------- 1 | --foo-bar=baz 2 | -------------------------------------------------------------------------------- /tests/unittests/data/cmd3.f: -------------------------------------------------------------------------------- 1 | test2.sv 2 | -F cmd4.f 3 | test4.sv 4 | -------------------------------------------------------------------------------- /tests/unittests/data/cmd4.f: -------------------------------------------------------------------------------- 1 | test3.sv 2 | -------------------------------------------------------------------------------- /tests/unittests/data/file_defn.svh: -------------------------------------------------------------------------------- 1 | `define BAR `__FILE__ 2 | `define FOO `BAR 3 | -------------------------------------------------------------------------------- /tests/unittests/data/file_uses_define_in_file_with_no_eol.sv: -------------------------------------------------------------------------------- 1 | module main; 2 | initial begin 3 | $display("Something: %d", `SOMETHING); 4 | end 5 | endmodule 6 | -------------------------------------------------------------------------------- /tests/unittests/data/file_uses_defn.svh: -------------------------------------------------------------------------------- 1 | `include "file_defn.svh" 2 | `FOO 3 | -------------------------------------------------------------------------------- /tests/unittests/data/file_with_no_eol.sv: -------------------------------------------------------------------------------- 1 | `define SOMETHING 1337 2 | -------------------------------------------------------------------------------- /tests/unittests/data/include.svh: -------------------------------------------------------------------------------- 1 | /* hello */ 2 | `include "local.svh" 3 | `include "nested/file.svh" 4 | `include "../data/nested/file.svh" 5 | -------------------------------------------------------------------------------- /tests/unittests/data/include_once.svh: -------------------------------------------------------------------------------- 1 | `pragma once 2 | `include "local.svh" 3 | -------------------------------------------------------------------------------- /tests/unittests/data/infinite.f: -------------------------------------------------------------------------------- 1 | -f infinite.f 2 | -------------------------------------------------------------------------------- /tests/unittests/data/infinite.map: -------------------------------------------------------------------------------- 1 | include infinite.map; 2 | -------------------------------------------------------------------------------- /tests/unittests/data/infinite_chain.svh: -------------------------------------------------------------------------------- 1 | `include "infinite_chain.svh" 2 | -------------------------------------------------------------------------------- /tests/unittests/data/library/lib.map: -------------------------------------------------------------------------------- 1 | include ../system/system.map; 2 | 3 | library libfoo libmod.??, *.sv -incdir ../nested; 4 | -------------------------------------------------------------------------------- /tests/unittests/data/library/libmod.qv: -------------------------------------------------------------------------------- 1 | `pragma diagnostic warn="-Wfoobaz" 2 | 3 | module libmod; 4 | import pkg::*; 5 | int k = pkg::bar; 6 | endmodule 7 | 8 | `ifdef SOME_DEF 9 | module qq; 10 | endmodule 11 | `endif 12 | 13 | interface I; 14 | int i; 15 | modport m(input i); 16 | endinterface 17 | -------------------------------------------------------------------------------- /tests/unittests/data/library/other.sv: -------------------------------------------------------------------------------- 1 | `include "macro.svh" 2 | 3 | module other; 4 | `FOO(1); 5 | endmodule 6 | -------------------------------------------------------------------------------- /tests/unittests/data/library/pkg.sv: -------------------------------------------------------------------------------- 1 | package pkg; 2 | localparam int bar = 42; 3 | 4 | class C; endclass 5 | endpackage 6 | -------------------------------------------------------------------------------- /tests/unittests/data/libtest/mod1.sv: -------------------------------------------------------------------------------- 1 | module mod1; 2 | endmodule 3 | -------------------------------------------------------------------------------- /tests/unittests/data/libtest/mod2.sv: -------------------------------------------------------------------------------- 1 | module mod1; 2 | endmodule 3 | -------------------------------------------------------------------------------- /tests/unittests/data/libtest/testlib.map: -------------------------------------------------------------------------------- 1 | library lib1 mod1.sv; 2 | library lib2 mod2.sv; 3 | -------------------------------------------------------------------------------- /tests/unittests/data/libtest/top.sv: -------------------------------------------------------------------------------- 1 | module top; 2 | mod1 m(); 3 | endmodule 4 | -------------------------------------------------------------------------------- /tests/unittests/data/local.svh: -------------------------------------------------------------------------------- 1 | // Just a test string 2 | "test string" 3 | -------------------------------------------------------------------------------- /tests/unittests/data/nested/file.svh: -------------------------------------------------------------------------------- 1 | `include "nested_local.svh" 2 | `include "nonexistent.svh" 3 | -------------------------------------------------------------------------------- /tests/unittests/data/nested/macro.svh: -------------------------------------------------------------------------------- 1 | `define FOO(a) logic [2:0] b = a 2 | -------------------------------------------------------------------------------- /tests/unittests/data/nested/nested_local.svh: -------------------------------------------------------------------------------- 1 | "test string" 2 | -------------------------------------------------------------------------------- /tests/unittests/data/system/system.map: -------------------------------------------------------------------------------- 1 | library libsys ../test6.sv; 2 | -------------------------------------------------------------------------------- /tests/unittests/data/system/system.svh: -------------------------------------------------------------------------------- 1 | "system stuff!" 2 | -------------------------------------------------------------------------------- /tests/unittests/data/test.sv: -------------------------------------------------------------------------------- 1 | `include "file_defn.svh" 2 | `define ID(x) x 3 | 4 | module m; 5 | // hello 6 | int i = 32'haa_bb??e; 7 | string s = `FOO; 8 | 9 | begin end 10 | endmodule 11 | 12 | `ifdef FOOBAR 13 | `include "mod1.sv" 14 | `endif 15 | -------------------------------------------------------------------------------- /tests/unittests/data/test2.sv: -------------------------------------------------------------------------------- 1 | module n; 2 | m m1(); 3 | int `ID(foo) = 1; 4 | endmodule 5 | -------------------------------------------------------------------------------- /tests/unittests/data/test3.sv: -------------------------------------------------------------------------------- 1 | module m; 2 | libmod lm(); 3 | endmodule 4 | 5 | module n(I.m im); 6 | endmodule 7 | 8 | `define SOME_DEF 9 | -------------------------------------------------------------------------------- /tests/unittests/data/test4.sv: -------------------------------------------------------------------------------- 1 | module baz; 2 | int k = j; 3 | int j = 1; 4 | 5 | `ifndef FOOBAR 6 | int blah = unknown; 7 | `endif 8 | endmodule 9 | 10 | module unused; 11 | `pragma diagnostic warn="-Wfoobaz" 12 | endmodule 13 | 14 | `ifdef FOOBAR 15 | module frob #(parameter int bar); 16 | endmodule 17 | `endif 18 | -------------------------------------------------------------------------------- /tests/unittests/data/test5.sv: -------------------------------------------------------------------------------- 1 | module k; 2 | logic [3:0] a; 3 | logic [2:0] b = a; 4 | endmodule 5 | -------------------------------------------------------------------------------- /tests/unittests/data/test6.sv: -------------------------------------------------------------------------------- 1 | `include "nested/macro.svh" 2 | 3 | module m; 4 | logic [3:0] a; 5 | `FOO(a); 6 | endmodule 7 | -------------------------------------------------------------------------------- /tests/unittests/data/unit.f: -------------------------------------------------------------------------------- 1 | --library mylib 2 | test.sv 3 | test2.sv 4 | -DFOOBAR 5 | -Ilibtest 6 | -------------------------------------------------------------------------------- /tests/unittests/main.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | 6 | #include "slang/diagnostics/Diagnostics.h" 7 | #include "slang/syntax/SyntaxTree.h" 8 | #include "slang/text/SourceManager.h" 9 | #include "slang/util/BumpAllocator.h" 10 | #include "slang/util/OS.h" 11 | 12 | namespace slang { 13 | 14 | BumpAllocator alloc; 15 | Diagnostics diagnostics; 16 | 17 | } // namespace slang 18 | 19 | int main(int argc, char* argv[]) { 20 | slang::OS::setupConsole(); 21 | 22 | slang::syntax::SyntaxTree::getDefaultSourceManager().setDisableProximatePaths(true); 23 | 24 | Catch::Session session; 25 | session.configData().defaultColourMode = Catch::ColourMode::ANSI; 26 | return session.run(argc, argv); 27 | } 28 | -------------------------------------------------------------------------------- /tests/unittests/util/UtilTests.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include 6 | #include 7 | #include 8 | 9 | #include "slang/util/Random.h" 10 | #include "slang/util/TimeTrace.h" 11 | 12 | using namespace Catch::Matchers; 13 | 14 | #if __cpp_exceptions && defined(CI_BUILD) && SLANG_ASSERT_ENABLED 15 | TEST_CASE("Assertions") { 16 | int i = 4; 17 | SLANG_ASSERT(i == 4); 18 | 19 | CHECK_THROWS_AS([&] { SLANG_ASSERT(i == 5); }(), slang::assert::AssertionException); 20 | 21 | CHECK_THROWS_AS([&] { SLANG_UNREACHABLE; }(), std::logic_error); 22 | } 23 | #endif 24 | 25 | TEST_CASE("TypeName test") { 26 | CHECK(typeName() == "void"); 27 | 28 | auto name = typeName(); 29 | CHECK_THAT(std::string(name), ContainsSubstring("string")); 30 | 31 | name = typeName(); 32 | CHECK_THAT(std::string(name), ContainsSubstring("slang::ast::AssertionKind")); 33 | } 34 | 35 | TEST_CASE("createRandomGenerator construction") { 36 | // Basic construction test, not much else we can do with it. 37 | auto rng = createRandomGenerator(); 38 | } 39 | 40 | TEST_CASE("TimeTrace tests") { 41 | TimeTrace::initialize(); 42 | 43 | auto frob = [] { 44 | TimeTraceScope timeScope("Nested\nbaz"sv, ""sv); 45 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 46 | }; 47 | 48 | BS::thread_pool pool; 49 | for (int i = 0; i < 20; i++) { 50 | pool.detach_task([i, &frob] { 51 | if (i % 2 == 0) { 52 | TimeTraceScope timeScope("Foo\"thing"sv, std::to_string(i)); 53 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 54 | } 55 | else { 56 | TimeTraceScope timeScope("Foo\"thing"sv, [i] { return std::to_string(i); }); 57 | frob(); 58 | } 59 | }); 60 | } 61 | 62 | pool.wait(); 63 | 64 | std::ostringstream sstr; 65 | TimeTrace::write(sstr); 66 | } 67 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_subdirectory(driver) 7 | add_subdirectory(hier) 8 | add_subdirectory(netlist) 9 | add_subdirectory(reflect) 10 | add_subdirectory(rewriter) 11 | add_subdirectory(tidy) 12 | 13 | if(SLANG_INCLUDE_THREADTEST) 14 | add_subdirectory(threadtest) 15 | endif() 16 | -------------------------------------------------------------------------------- /tools/driver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_executable(slang_driver slang_main.cpp) 7 | add_executable(slang::driver ALIAS slang_driver) 8 | 9 | target_link_libraries(slang_driver PRIVATE slang::slang) 10 | set_target_properties(slang_driver PROPERTIES OUTPUT_NAME "slang") 11 | 12 | if(SLANG_FUZZ_TARGET) 13 | message("Tweaking driver for fuzz testing") 14 | target_compile_definitions(slang_driver PRIVATE FUZZ_TARGET) 15 | 16 | target_compile_options(slang_driver PRIVATE "-fsanitize=fuzzer") 17 | target_link_libraries(slang_driver PRIVATE "-fsanitize=fuzzer") 18 | endif() 19 | 20 | if(SLANG_INCLUDE_INSTALL) 21 | install(TARGETS slang_driver RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 22 | endif() 23 | 24 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 25 | target_sources(slang_driver 26 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 27 | endif() 28 | -------------------------------------------------------------------------------- /tools/hier/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_executable(slang_hier hier.cpp) 7 | add_executable(slang::hier ALIAS slang_hier) 8 | 9 | target_link_libraries(slang_hier PRIVATE slang::slang) 10 | set_target_properties(slang_hier PROPERTIES OUTPUT_NAME "slang-hier") 11 | 12 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 13 | target_sources(slang_hier 14 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 15 | endif() 16 | 17 | if(SLANG_INCLUDE_INSTALL) 18 | install(TARGETS slang_hier RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 19 | endif() 20 | -------------------------------------------------------------------------------- /tools/hier/README.md: -------------------------------------------------------------------------------- 1 | slang-hier 2 | ========== 3 | A tool that can display information about a Verilog hierarchy. 4 | 5 | This tool accepts the standard set of slang driver command line options, 6 | which lets you configure your design. Then the tool will display 7 | information like module instance names and resolved parameter values. 8 | 9 | Additional options to control output: 10 | 11 | `--params` 12 | 13 | Include instance parameter values in the output. 14 | 15 | `--max-depth ` 16 | 17 | The maximum instance depth of the hierarchy to be printed. 18 | Everything deeper than that will be ignored. 19 | 20 | `--inst-prefix ` 21 | 22 | A hierarchical path indicating which hierarchy to display. 23 | All parts of the design not under this prefix will be ignored. 24 | 25 | `--inst-regex ` 26 | 27 | Only instances that match the given regex (anywhere in the design tree) 28 | will be shown. All others will be skipped. 29 | 30 | `--custom-format ` 31 | 32 | A libfmt style format string that controls how the output is printed. 33 | Use `{inst}`, `{module}`, `{file}` as argument names in the string. 34 | -------------------------------------------------------------------------------- /tools/netlist/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_executable(slang_netlist netlist.cpp source/Netlist.cpp) 7 | add_executable(slang::netlist ALIAS slang_netlist) 8 | 9 | target_link_libraries(slang_netlist PRIVATE slang::slang) 10 | set_target_properties(slang_netlist PROPERTIES OUTPUT_NAME "slang-netlist") 11 | target_include_directories(slang_netlist PRIVATE include) 12 | 13 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 14 | target_sources(slang_netlist 15 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 16 | endif() 17 | 18 | if(SLANG_INCLUDE_INSTALL) 19 | install(TARGETS slang_netlist RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 20 | endif() 21 | 22 | if(SLANG_INCLUDE_TESTS) 23 | add_subdirectory(tests) 24 | endif() 25 | -------------------------------------------------------------------------------- /tools/netlist/README.md: -------------------------------------------------------------------------------- 1 | slang-netlist 2 | ============= 3 | 4 | > **Warning** 5 | > 6 | > slang-netlist is a work in progress and may not work as expected. Check 7 | > TODO.md for a list of some new features and fixes that are planned. If you 8 | > encounter a problem, please submit a bug report via Issues. 9 | 10 | slang-netlist is a library and tool for analysing the source-level static 11 | connectivity of a design. This capability can be useful, for example, to 12 | develop structural checks or to investigate timing paths, rather than having to 13 | use synthesis to obtain a gate-level netlist. 14 | 15 | Using the example of a simple adder: 16 | 17 | ``` 18 | module adder 19 | #(parameter p_width = 32)( 20 | input logic [p_width-1:0] i_a, 21 | input logic [p_width-1:0] i_b, 22 | output logic [p_width-1:0] o_sum, 23 | output logic o_co 24 | ); 25 | logic [p_width-1:0] sum; 26 | logic co; 27 | assign {co, sum} = i_a + i_b; 28 | assign o_sum = sum; 29 | assign o_co = co; 30 | endmodule 31 | ``` 32 | 33 | The slang-netlist command-line tool can be used to trace paths through the 34 | design, such as: 35 | 36 | ``` 37 | ➜ slang-netlist adder.sv --from adder.i_a --to adder.o_sum -q 38 | adder.sv:10:22: note: variable i_a read from 39 | assign {co, sum} = i_a + i_b; 40 | ^~~ 41 | 42 | adder.sv:10:15: note: variable sum assigned to 43 | assign {co, sum} = i_a + i_b; 44 | ^~~ 45 | 46 | adder.sv:11:18: note: variable sum read from 47 | assign o_sum = sum; 48 | ^~~ 49 | 50 | adder.sv:11:10: note: variable o_sum assigned to 51 | assign o_sum = sum; 52 | ^~~~~ 53 | ``` 54 | -------------------------------------------------------------------------------- /tools/netlist/TODO.md: -------------------------------------------------------------------------------- 1 | To dos 2 | ====== 3 | 4 | - Support descending ranges in split variable type handling, eg [0:3]. 5 | - Dumping of a dot file outputs random characters at the end. 6 | - Support for more procedural statements, the full list is: 7 | 8 | InvalidStatement 9 | EmptyStatement 10 | StatementList 11 | BlockStatement 12 | ReturnStatement 13 | BreakStatement 14 | ContinueStatement 15 | DisableStatement 16 | VariableDeclStatement 17 | ConditionalStatement 18 | CaseStatement 19 | PatternCaseStatement 20 | ForLoopStatement 21 | RepeatLoopStatement 22 | ForeachLoopStatement 23 | WhileLoopStatement 24 | DoWhileLoopStatement 25 | ForeverLoopStatement 26 | ExpressionStatement 27 | TimedStatement 28 | ImmediateAssertionStatement 29 | ConcurrentAssertionStatement 30 | DisableForkStatement 31 | WaitStatement 32 | WaitForkStatement 33 | WaitOrderStatement 34 | EventTriggerStatement 35 | ProceduralAssignStatement 36 | ProceduralDeassignStatement 37 | RandCaseStatement 38 | RandSequenceStatement 39 | ProceduralCheckerStatement 40 | 41 | - Optimise lookups of nodes in the netlist by adding tables for variable 42 | declarations, variable references, ports etc. 43 | - Reporting of variables in the netlist (by type, matching patterns). 44 | - Infer sequential elements in the netlist (ie non-blocking assignment and 45 | sensitive to a clock edge). 46 | - Constrain paths to start on particular node types (port, register, net etc). 47 | - Support restricting paths to stop at sequential elements. 48 | - Support paths passing through particular nodes. 49 | - Support paths avoiding particular nodes. 50 | - Support reporting of paths fanning into or out of a particular node. 51 | - Provide Python bindings. 52 | -------------------------------------------------------------------------------- /tools/netlist/include/CombLoops.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file CombLoops.h 3 | //! @brief Algorithm for finding combinatorial loops 4 | // 5 | // SPDX-FileCopyrightText: Udi Finkelstein 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "CycleDetector.h" 11 | #include "Netlist.h" 12 | #include 13 | 14 | namespace netlist { 15 | 16 | struct CombEdgePredicate { 17 | CombEdgePredicate() = default; 18 | bool operator()(const NetlistEdge& edge) { 19 | return !edge.disabled && edge.edgeKind == ast::EdgeKind::None; 20 | } 21 | }; 22 | 23 | class CombLoops { 24 | Netlist const& netlist; 25 | 26 | public: 27 | CombLoops(Netlist const& netlist) : netlist(netlist) {} 28 | 29 | auto getAllLoops() { 30 | using CycleDetectorType = CycleDetector; 31 | CycleDetectorType detector(netlist); 32 | return detector.detectCycles(); 33 | } 34 | }; 35 | 36 | } // namespace netlist 37 | -------------------------------------------------------------------------------- /tools/netlist/include/Config.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file Config.h 3 | //! @brief Provide singleton configuration class debug printing macro. 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | namespace netlist { 11 | 12 | /// A singleton to hold global configuration options. 13 | class Config { 14 | public: 15 | bool debugEnabled{false}; 16 | bool quietEnabled{false}; 17 | 18 | Config() = default; 19 | 20 | static Config& getInstance() { 21 | static Config instance; 22 | return instance; 23 | } 24 | 25 | // Prevent copies from being made. 26 | Config(Config const&) = delete; 27 | void operator=(Config const&) = delete; 28 | }; 29 | 30 | } // namespace netlist 31 | -------------------------------------------------------------------------------- /tools/netlist/include/Debug.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file Debug.h 3 | //! @brief Provide debug printing macros. 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "Config.h" 11 | #include 12 | #include 13 | #include 14 | 15 | namespace netlist { 16 | 17 | inline const char* file_name(const char* file) { 18 | return strrchr(file, '/') ? strrchr(file, '/') + 1 : file; 19 | } 20 | 21 | template 22 | void DebugMessage(const std::source_location& location, fmt::format_string fmt, T&&... args) { 23 | fmt::print("{}:{}: ", file_name(location.file_name()), location.line()); 24 | fmt::print(fmt, std::forward(args)...); 25 | } 26 | 27 | template 28 | void InfoMessage(fmt::format_string fmt, T&&... args) { 29 | fmt::print(fmt, std::forward(args)...); 30 | } 31 | 32 | } // namespace netlist 33 | 34 | #ifdef SLANG_DEBUG 35 | # define DEBUG_PRINT(str, ...) \ 36 | if (netlist::Config::getInstance().debugEnabled) { \ 37 | DebugMessage(std::source_location::current(), str __VA_OPT__(, ) __VA_ARGS__); \ 38 | } 39 | #else 40 | # define DEBUG_PRINT(str, ...) 41 | #endif 42 | 43 | #define INFO_PRINT(str, ...) \ 44 | if (!Config::getInstance().quietEnabled) { \ 45 | InfoMessage(str __VA_OPT__(, ) __VA_ARGS__); \ 46 | } 47 | -------------------------------------------------------------------------------- /tools/netlist/include/NetlistVisitorOptions.hpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file NetlistVisitorOptions.h 3 | //! @brief Options controlling the way the netlist is created. 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | 12 | /// Hold various options controlling the way the netlist is created. 13 | struct NetlistVisitorOptions { 14 | 15 | /// If enabled, unroll for loops in procedural blocks. 16 | bool unrollForLoops{false}; 17 | }; 18 | -------------------------------------------------------------------------------- /tools/netlist/include/visitors/GenerateBlockVisitor.hpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file GenerateBlockVisitor.h 3 | //! @brief Visit generate blocks as part of the construction of a netlist graph. 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "visitors/ContinuousAssignVisitor.hpp" 11 | #include "visitors/ProceduralBlockVisitor.hpp" 12 | 13 | using namespace slang; 14 | 15 | namespace netlist { 16 | 17 | /// Visit generate blocks. When slang elaborates the design, generate loops are unrolled 18 | /// and conditionals evaluated. Branches in a condition that are not taken are 19 | /// marked as uninstantiated, and are therefore not visited. 20 | class GenerateBlockVisitor : public ast::ASTVisitor { 21 | public: 22 | explicit GenerateBlockVisitor(ast::Compilation& compilation, Netlist& netlist, 23 | NetlistVisitorOptions const& options) : 24 | compilation(compilation), netlist(netlist), options(options) {} 25 | 26 | /// Variable declaration. 27 | void handle(const ast::VariableSymbol& symbol) { netlist.addVariableDeclaration(symbol); } 28 | 29 | /// Net declaration. 30 | void handle(const ast::NetSymbol& symbol) { netlist.addVariableDeclaration(symbol); } 31 | 32 | /// Procedural block. 33 | void handle(const ast::ProceduralBlockSymbol& symbol) { 34 | ProceduralBlockVisitor visitor(compilation, netlist, options, ast::EdgeKind::None); 35 | symbol.visit(visitor); 36 | } 37 | 38 | /// Continuous assignment statement. 39 | void handle(const ast::ContinuousAssignSymbol& symbol) { 40 | ast::EvalContext evalCtx(ast::ASTContext(compilation.getRoot(), ast::LookupLocation::max)); 41 | SmallVector condVars; 42 | ContinuousAssignVisitor visitor(netlist, evalCtx, condVars); 43 | symbol.visit(visitor); 44 | } 45 | 46 | private: 47 | Netlist& netlist; 48 | NetlistVisitorOptions const& options; 49 | ast::Compilation& compilation; 50 | }; 51 | 52 | } // namespace netlist 53 | -------------------------------------------------------------------------------- /tools/netlist/include/visitors/NetlistVisitor.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file NetlistVisitor.h 3 | //! @brief An AST visitor to build a netlist graph. 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "Netlist.h" 11 | #include "NetlistVisitorOptions.hpp" 12 | #include "visitors/InstanceDeclVisitor.hpp" 13 | #include "visitors/InstanceVisitor.hpp" 14 | 15 | #include "slang/ast/ASTVisitor.h" 16 | #include "slang/ast/Compilation.h" 17 | #include "slang/ast/symbols/CompilationUnitSymbols.h" 18 | 19 | using namespace slang; 20 | 21 | namespace netlist { 22 | 23 | /// The top-level visitor that traverses the AST and builds a netlist connectivity graph. 24 | class NetlistVisitor : public ast::ASTVisitor { 25 | public: 26 | explicit NetlistVisitor(ast::Compilation& compilation, Netlist& netlist, 27 | NetlistVisitorOptions const& options) : 28 | compilation(compilation), netlist(netlist), options(options) {} 29 | 30 | void handle(const ast::InstanceSymbol& symbol) { 31 | 32 | { 33 | InstanceDeclVisitor visitor(compilation, netlist); 34 | symbol.visit(visitor); 35 | } 36 | 37 | { 38 | InstanceVisitor visitor(compilation, netlist, options); 39 | symbol.visit(visitor); 40 | } 41 | } 42 | 43 | private: 44 | ast::Compilation& compilation; 45 | Netlist& netlist; 46 | NetlistVisitorOptions const& options; 47 | }; 48 | 49 | } // namespace netlist 50 | -------------------------------------------------------------------------------- /tools/netlist/source/Netlist.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Netlist.h" 5 | 6 | size_t netlist::NetlistNode::nextID = 0; 7 | -------------------------------------------------------------------------------- /tools/netlist/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_executable( 7 | netlist_unittests 8 | ../../../tests/unittests/main.cpp 9 | ../../../tests/unittests/Test.cpp 10 | ../source/Netlist.cpp 11 | CombLoopsTests.cpp 12 | CycleDetectorTests.cpp 13 | DepthFirstSearchTests.cpp 14 | DirectedGraphTests.cpp 15 | NameTests.cpp 16 | PathTests.cpp 17 | SplitTests.cpp 18 | VariableSelectorsTests.cpp) 19 | 20 | target_link_libraries(netlist_unittests PRIVATE slang::slang Catch2::Catch2) 21 | 22 | target_compile_definitions(netlist_unittests PRIVATE UNITTESTS) 23 | 24 | target_include_directories(netlist_unittests PRIVATE ../include 25 | ../../../tests/unittests) 26 | 27 | add_test(NAME netlist_unittests COMMAND netlist_unittests) 28 | 29 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 30 | target_sources(netlist_unittests 31 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 32 | endif() 33 | -------------------------------------------------------------------------------- /tools/netlist/tests/NetlistTest.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Netlist.h" 5 | #include "PathFinder.h" 6 | #include "visitors/NetlistVisitor.h" 7 | #include 8 | 9 | #include "slang/ast/Compilation.h" 10 | 11 | using namespace netlist; 12 | 13 | inline Netlist createNetlist(ast::Compilation& compilation) { 14 | Netlist netlist; 15 | NetlistVisitorOptions options; 16 | NetlistVisitor visitor(compilation, netlist, options); 17 | compilation.getRoot().visit(visitor); 18 | netlist.split(); 19 | return netlist; 20 | } 21 | 22 | inline Netlist createNetlist(ast::Compilation& compilation, NetlistVisitorOptions const& options) { 23 | Netlist netlist; 24 | NetlistVisitor visitor(compilation, netlist, options); 25 | compilation.getRoot().visit(visitor); 26 | netlist.split(); 27 | return netlist; 28 | } 29 | 30 | inline bool pathExists(Netlist& netlist, const std::string& startName, const std::string& endName) { 31 | PathFinder pathFinder(netlist); 32 | auto* startNode = netlist.lookupPort(startName); 33 | auto* endNode = netlist.lookupPort(endName); 34 | return !pathFinder.find(*startNode, *endNode).empty(); 35 | } 36 | -------------------------------------------------------------------------------- /tools/netlist/tests/SplitTests.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file NetlistTest.cpp 3 | //! @brief Variable splitting unit tests. 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #include "NetlistTest.h" 10 | #include "Test.h" 11 | 12 | // Test the logic that splits variable declaration nodes into sets of ALIAS 13 | // nodes with connections based on their selection bounds. 14 | 15 | TEST_CASE("One in, two out") { 16 | auto tree = SyntaxTree::fromText(R"( 17 | module foo( 18 | input logic [15:0] a, 19 | output logic [7:0] b, 20 | output logic [7:0] c 21 | ); 22 | 23 | logic [15:0] x; 24 | 25 | assign x = a; 26 | assign b = x[7:0]; 27 | assign c = x[15:8]; 28 | 29 | endmodule 30 | )"); 31 | Compilation compilation; 32 | compilation.addSyntaxTree(tree); 33 | NO_COMPILATION_ERRORS; 34 | auto netlist = createNetlist(compilation); 35 | // foo.x should have one ALIAS. 36 | auto v = netlist.lookupVariable("foo.x"); 37 | CHECK(v != nullptr); 38 | CHECK(v->aliases.size() == 1); 39 | } 40 | 41 | TEST_CASE("Two in, one out") { 42 | auto tree = SyntaxTree::fromText(R"( 43 | module foo( 44 | input logic [7:0] a, 45 | input logic [7:0] b, 46 | output logic [15:0] c 47 | ); 48 | 49 | logic [15:0] x; 50 | 51 | assign x[7:0] = a; 52 | assign x[15:8] = b; 53 | assign c = x; 54 | 55 | endmodule 56 | )"); 57 | Compilation compilation; 58 | compilation.addSyntaxTree(tree); 59 | NO_COMPILATION_ERRORS; 60 | auto netlist = createNetlist(compilation); 61 | // foo.x should have two ALIAS components. 62 | auto v = netlist.lookupVariable("foo.x"); 63 | CHECK(v != nullptr); 64 | CHECK(v->aliases.size() == 0); 65 | } 66 | 67 | TEST_CASE("Two in, two out") { 68 | auto tree = SyntaxTree::fromText(R"( 69 | module foo( 70 | input logic [7:0] a, 71 | input logic [7:0] b, 72 | output logic [7:0] c, 73 | output logic [7:0] d 74 | ); 75 | 76 | logic [15:0] x; 77 | 78 | assign x[7:0] = a; 79 | assign x[15:8] = b; 80 | assign c = x[7:0]; 81 | assign d = x[15:8]; 82 | 83 | endmodule 84 | )"); 85 | Compilation compilation; 86 | compilation.addSyntaxTree(tree); 87 | NO_COMPILATION_ERRORS; 88 | auto netlist = createNetlist(compilation); 89 | // foo.x should have two ALIAS components. 90 | auto v = netlist.lookupVariable("foo.x"); 91 | CHECK(v != nullptr); 92 | CHECK(v->aliases.size() == 2); 93 | } 94 | -------------------------------------------------------------------------------- /tools/reflect/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_library( 7 | slang_reflect_obj_lib OBJECT 8 | src/SvStruct.cpp src/SvType.cpp src/SvEnum.cpp src/SvTypeReflector.cpp 9 | src/SvLocalParam.cpp src/SvUnion.cpp) 10 | 11 | target_include_directories(slang_reflect_obj_lib PUBLIC include) 12 | target_link_libraries(slang_reflect_obj_lib PUBLIC slang::slang) 13 | 14 | add_executable(slang_reflect src/reflect.cpp) 15 | add_executable(slang::reflect ALIAS slang_reflect) 16 | 17 | target_link_libraries(slang_reflect PRIVATE slang_reflect_obj_lib) 18 | set_target_properties(slang_reflect PROPERTIES OUTPUT_NAME "slang-reflect") 19 | 20 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 21 | target_sources(slang_reflect 22 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 23 | endif() 24 | 25 | if(SLANG_INCLUDE_INSTALL) 26 | install(TARGETS slang_reflect RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 27 | endif() 28 | -------------------------------------------------------------------------------- /tools/reflect/include/ASTVisitors.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file ASTVisitors.h 3 | //! @brief Reusable AST visitors 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include "slang/ast/ASTVisitor.h" 14 | #include "slang/syntax/SyntaxVisitor.h" 15 | 16 | class PublicDirectiveVisitor : public slang::syntax::SyntaxVisitor { 17 | public: 18 | explicit PublicDirectiveVisitor(const slang::parsing::TokenKind tokenKind) : 19 | tokenKind(tokenKind) {} 20 | 21 | void visitToken(const slang::parsing::Token token) { 22 | if (token.kind == tokenKind) { 23 | auto blockComments = token.trivia() | std::views::filter([](auto& v) { 24 | return v.kind == slang::parsing::TriviaKind::BlockComment; 25 | }); 26 | 27 | for (auto& blockComment : blockComments) { 28 | isPublic = std::ranges::find(publicDirectives, blockComment.getRawText()) != 29 | publicDirectives.end(); 30 | } 31 | } 32 | } 33 | 34 | bool operator()() { return std::exchange(isPublic, false); } 35 | 36 | private: 37 | bool isPublic{false}; 38 | slang::parsing::TokenKind tokenKind; 39 | 40 | constexpr static std::array publicDirectives = {"/* public */", 41 | "/*verilator public*/", 42 | "/* verilator public */"}; 43 | }; 44 | -------------------------------------------------------------------------------- /tools/reflect/include/SvEnum.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file SvEnum.h 3 | //! @brief Handles with SystemVerilog Enums 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #pragma once 10 | 11 | #include "SvGeneric.h" 12 | 13 | #include "slang/ast/types/AllTypes.h" 14 | 15 | class SvEnum final : public SvGeneric { 16 | public: 17 | explicit SvEnum(const slang::ast::TypeAliasType& type) : SvGeneric(Kind::Enum), type(type) {} 18 | 19 | void toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) const override; 20 | 21 | private: 22 | const slang::ast::TypeAliasType& type; 23 | }; 24 | -------------------------------------------------------------------------------- /tools/reflect/include/SvLocalParam.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file SvLocalParam.h 3 | //! @brief Handles with SystemVerilog local parameters 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #pragma once 10 | 11 | #include "SvGeneric.h" 12 | #include 13 | 14 | #include "slang/ast/symbols/ParameterSymbols.h" 15 | 16 | class SvLocalParam final : public SvGeneric { 17 | public: 18 | explicit SvLocalParam(const slang::ast::ParameterSymbol& parameter) : 19 | SvGeneric(Kind::LocalParam), parameter(parameter) {} 20 | 21 | void toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) const override; 22 | 23 | private: 24 | const slang::ast::ParameterSymbol& parameter; 25 | }; 26 | -------------------------------------------------------------------------------- /tools/reflect/include/SvStruct.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file SvStruct.h 3 | //! @brief Handles with SystemVerilog structs 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #pragma once 10 | 11 | #include "CppEmitter.h" 12 | #include "SvGeneric.h" 13 | #include "fmt/format.h" 14 | #include 15 | 16 | class SvStruct final : public SvGeneric { 17 | public: 18 | explicit SvStruct(const slang::ast::TypeAliasType& type) : 19 | SvGeneric(Kind::Struct), type(type) {} 20 | 21 | void toCpp(HppFile& hppFile, std::string_view _namespace, const SvAliases& aliases, 22 | bool noSystemC) const override; 23 | 24 | private: 25 | const slang::ast::TypeAliasType& type; 26 | }; 27 | -------------------------------------------------------------------------------- /tools/reflect/include/SvType.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file SvType.h 3 | //! @brief C++ Type representation of a SystemVerilog type 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #pragma once 10 | 11 | #include "slang/ast/types/Type.h" 12 | 13 | namespace CppType { 14 | enum Type { BOOL, U32, U64, SC_BV, STRUCT, ENUM, UNION }; 15 | 16 | std::string toString(const Type& cppType); 17 | Type fromSize(size_t size); 18 | } // namespace CppType 19 | 20 | class SvType { 21 | public: 22 | explicit SvType(const slang::ast::Type& type); 23 | explicit SvType(const slang::ast::Type& type, const std::string_view name) : SvType(type) { 24 | this->name = name; 25 | } 26 | 27 | [[nodiscard]] bool isStruct() const { return cppType == CppType::STRUCT; } 28 | [[nodiscard]] bool isEnum() const { return cppType == CppType::ENUM; } 29 | [[nodiscard]] bool isUnion() const { return cppType == CppType::UNION; } 30 | [[nodiscard]] bool isStructEnumOrUnion() const { 31 | return this->isStruct() || this->isEnum() || this->isUnion(); 32 | } 33 | 34 | [[nodiscard]] std::string toString() const; 35 | friend std::ostream& operator<<(std::ostream& os, const SvType& type); 36 | 37 | CppType::Type cppType; 38 | size_t size; 39 | // It will only contain useful data if the cppType is either a struct or an enum 40 | std::string_view name; 41 | std::string_view _namespace; 42 | }; 43 | -------------------------------------------------------------------------------- /tools/reflect/include/SvTypeReflector.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file SvTypeReflector.h 3 | //! @brief Top level of the type reflector library 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #pragma once 10 | 11 | #include "CppEmitter.h" 12 | 13 | #include "slang/ast/Compilation.h" 14 | 15 | class SvTypeReflector { 16 | public: 17 | explicit SvTypeReflector(std::unique_ptr compilation, 18 | const bool verbose, const bool noSystemC) : 19 | verbose(verbose), noSystemC(noSystemC), cppEmitter(noSystemC), 20 | compilation(std::move(compilation)) {} 21 | 22 | void reflect(); 23 | 24 | std::string emit() const { return cppEmitter.emit(); } 25 | void emitToFile(const fs::path& path) const { cppEmitter.emitToFile(path); } 26 | 27 | private: 28 | bool verbose; 29 | bool noSystemC; 30 | CppEmitter cppEmitter; 31 | std::unique_ptr compilation; 32 | }; 33 | -------------------------------------------------------------------------------- /tools/reflect/include/SvUnion.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file SvEnum.h 3 | //! @brief Handles with SystemVerilog Enums 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | 9 | #pragma once 10 | 11 | #include "SvGeneric.h" 12 | 13 | #include "slang/ast/types/AllTypes.h" 14 | 15 | class SvUnion final : public SvGeneric { 16 | public: 17 | explicit SvUnion(const slang::ast::TypeAliasType& type) : SvGeneric(Kind::Union), type(type) {} 18 | 19 | void toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool noSystemC) const override; 20 | 21 | private: 22 | const slang::ast::TypeAliasType& type; 23 | }; 24 | -------------------------------------------------------------------------------- /tools/reflect/src/SvType.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "SvType.h" 5 | 6 | #include 7 | #include 8 | 9 | #include "slang/ast/Scope.h" 10 | 11 | using namespace slang::ast; 12 | 13 | SvType::SvType(const Type& type) { 14 | size = type.getBitstreamWidth(); 15 | name = type.name; 16 | if (type.isScalar() || type.isArray()) 17 | cppType = CppType::fromSize(size); 18 | else if (type.isEnum()) 19 | cppType = CppType::ENUM; 20 | else if (type.isStruct()) 21 | cppType = CppType::STRUCT; 22 | else if (type.isUnion()) 23 | cppType = CppType::UNION; 24 | else 25 | SLANG_UNREACHABLE; 26 | 27 | if (this->isStructEnumOrUnion()) 28 | _namespace = type.getParentScope()->asSymbol().name; 29 | } 30 | 31 | std::ostream& operator<<(std::ostream& os, const SvType& type) { 32 | return os << type.toString(); 33 | } 34 | 35 | std::string SvType::toString() const { 36 | std::stringstream ss; 37 | if (cppType == CppType::SC_BV) 38 | ss << format(fmt::runtime(CppType::toString(cppType)), size); 39 | else if (this->isStructEnumOrUnion()) 40 | ss << format(fmt::runtime(CppType::toString(cppType)), name); 41 | else 42 | ss << CppType::toString(cppType); 43 | 44 | return ss.str(); 45 | } 46 | 47 | namespace CppType { 48 | std::string toString(const Type& cppType) { 49 | // clang-format off 50 | switch (cppType) { 51 | case BOOL: return "bool"; 52 | case U32: return "uint32_t"; 53 | case U64: return "uint64_t"; 54 | case SC_BV: return "sc_bv<{}>"; 55 | case STRUCT: return "{}"; 56 | case ENUM: return "{}"; 57 | case UNION: return "{}"; 58 | } 59 | // clang-format on 60 | SLANG_UNREACHABLE; 61 | } 62 | 63 | Type fromSize(const size_t size) { 64 | // clang-format off 65 | if (size == 1) return BOOL; 66 | if (size <= 32) return U32; 67 | if (size <= 64) return U64; 68 | return SC_BV; 69 | // clang-format on 70 | } 71 | } // namespace CppType 72 | -------------------------------------------------------------------------------- /tools/rewriter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_executable(rewriter rewriter.cpp) 7 | target_link_libraries(rewriter PRIVATE slang::slang) 8 | 9 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 10 | target_sources(rewriter PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 11 | endif() 12 | -------------------------------------------------------------------------------- /tools/rewriter/README.md: -------------------------------------------------------------------------------- 1 | rewriter 2 | ======== 3 | A simple tool that shows using the syntax API to read in a source file, 4 | parse it, and write it back out again. This can be used for testing purposes, 5 | to make sure syntax trees round trip correctly, or as a basic example of 6 | working with the syntax API. 7 | 8 | Usage: 9 | 10 | ``` 11 | rewriter 12 | ``` 13 | -------------------------------------------------------------------------------- /tools/rewriter/rewriter.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // rewriter.cpp 3 | // Simple tool that parses an input file and writes it back out; used 4 | // for verifying the round-trip nature of the parse tree. 5 | // 6 | // SPDX-FileCopyrightText: Michael Popoloski 7 | // SPDX-License-Identifier: MIT 8 | //------------------------------------------------------------------------------ 9 | 10 | #include 11 | #if defined(_WIN32) 12 | # include 13 | # include 14 | #endif 15 | 16 | #include 17 | 18 | #include "slang/syntax/SyntaxPrinter.h" 19 | #include "slang/syntax/SyntaxTree.h" 20 | #include "slang/util/OS.h" 21 | 22 | using namespace slang; 23 | using namespace slang::syntax; 24 | 25 | int main(int argc, char** argv) { 26 | OS::setupConsole(); 27 | 28 | SLANG_TRY { 29 | if (argc != 2) { 30 | fmt::print(stderr, "usage: rewriter file\n"); 31 | return 1; 32 | } 33 | 34 | auto tree = SyntaxTree::fromFile(argv[1]); 35 | if (!tree) { 36 | fmt::print(stderr, "error: '{}': {}\n", argv[1], tree.error().first.message()); 37 | return 1; 38 | } 39 | 40 | // Make sure we reproduce newlines correctly on Windows: 41 | #if defined(_WIN32) 42 | _setmode(_fileno(stdout), _O_BINARY); 43 | #endif 44 | 45 | printf("%s", SyntaxPrinter::printFile(*tree.value()).c_str()); 46 | return 0; 47 | } 48 | SLANG_CATCH(const std::exception& e) { 49 | SLANG_REPORT_EXCEPTION(e, "internal compiler error (exception): {}\n"); 50 | return 2; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tools/threadtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_executable(slang_threadtest threadtest.cpp) 7 | add_executable(slang::threadtest ALIAS slang_threadtest) 8 | 9 | target_link_libraries(slang_threadtest PRIVATE slang::slang) 10 | 11 | set_target_properties(slang_threadtest PROPERTIES OUTPUT_NAME 12 | "slang-threadtest") 13 | 14 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 15 | target_sources(slang_threadtest 16 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 17 | endif() 18 | 19 | if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL 20 | "GNU") 21 | string(FIND "${CMAKE_CXX_FLAGS}" "fsanitize" found) 22 | if(found EQUAL -1) 23 | target_compile_options(slang_threadtest PRIVATE "-fsanitize=thread") 24 | target_link_libraries(slang_threadtest PRIVATE "-fsanitize=thread") 25 | endif() 26 | endif() 27 | -------------------------------------------------------------------------------- /tools/threadtest/README.md: -------------------------------------------------------------------------------- 1 | slang-threadtest 2 | ================ 3 | A simple tool that allows testing the thread safety invariants of the AST. 4 | The AST, once an initial visitation has occurred and all diagnostics are collected, 5 | should then be able to be visited from multiple threads without issue. This tool 6 | runs many such visitations across a design to see if any asserts fire. 7 | 8 | Usage: 9 | 10 | ``` 11 | slang-threadtest [-n num_iterations] 12 | ``` 13 | -------------------------------------------------------------------------------- /tools/threadtest/threadtest.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // slang_threadtest.cpp 3 | // Testing tool for multithreaded AST visitation 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #include 9 | 10 | #include "slang/ast/ASTSerializer.h" 11 | #include "slang/ast/Compilation.h" 12 | #include "slang/ast/symbols/CompilationUnitSymbols.h" 13 | #include "slang/driver/Driver.h" 14 | #include "slang/text/Json.h" 15 | 16 | using namespace slang; 17 | using namespace slang::ast; 18 | using namespace slang::driver; 19 | 20 | int main(int argc, char** argv) { 21 | SLANG_TRY { 22 | OS::setupConsole(); 23 | OS::tryEnableColors(); 24 | 25 | Driver driver; 26 | driver.addStandardArgs(); 27 | 28 | std::optional count; 29 | driver.cmdLine.add("-n,--count", count, "Number of iterations to perform"); 30 | 31 | if (!driver.parseCommandLine(argc, argv) || !driver.processOptions() || 32 | !driver.parseAllSources()) { 33 | return 1; 34 | } 35 | 36 | auto compilation = driver.createCompilation(); 37 | driver.reportCompilation(*compilation, true); 38 | if (!driver.reportDiagnostics(true)) 39 | return 2; 40 | 41 | compilation->freeze(); 42 | 43 | BS::thread_pool threadPool; 44 | threadPool.detach_blocks(0, count.value_or(1000), [&](int from, int to) { 45 | SLANG_TRY { 46 | JsonWriter writer; 47 | ASTSerializer serializer(*compilation, writer); 48 | serializer.setTryConstantFold(false); 49 | 50 | serializer.startArray(); 51 | for (int i = from; i < to; i++) 52 | serializer.serialize(compilation->getRoot()); 53 | serializer.endArray(); 54 | } 55 | SLANG_CATCH(const std::exception& e) { 56 | SLANG_REPORT_EXCEPTION(e, "{}\n"); 57 | } 58 | }); 59 | 60 | return 0; 61 | } 62 | SLANG_CATCH(const std::exception& e) { 63 | SLANG_REPORT_EXCEPTION(e, "{}\n"); 64 | } 65 | return 3; 66 | } 67 | -------------------------------------------------------------------------------- /tools/tidy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_library( 7 | slang_tidy_obj_lib OBJECT 8 | src/TidyConfig.cpp 9 | src/TidyConfigParser.cpp 10 | src/ASTHelperVisitors.cpp 11 | src/synthesis/OnlyAssignedOnReset.cpp 12 | src/synthesis/RegisterHasNoReset.cpp 13 | src/style/EnforcePortPrefix.cpp 14 | src/style/EnforcePortSuffix.cpp 15 | src/synthesis/NoLatchesOnDesign.cpp 16 | src/style/NoOldAlwaysSyntax.cpp 17 | src/style/AlwaysCombNonBlocking.cpp 18 | src/style/AlwaysFFBlocking.cpp 19 | src/style/EnforceModuleInstantiationPrefix.cpp 20 | src/style/OnlyANSIPortDecl.cpp 21 | src/synthesis/XilinxDoNotCareValues.cpp 22 | src/synthesis/CastSignedIndex.cpp 23 | src/style/NoDotStarInPortConnection.cpp 24 | src/style/NoImplicitPortNameInPortConnection.cpp 25 | src/style/AlwaysCombNamed.cpp 26 | src/style/GenerateNamed.cpp 27 | src/style/NoDotVarInPortConnection.cpp 28 | src/style/NoLegacyGenerate.cpp 29 | src/synthesis/AlwaysFFAssignmentOutsideConditional.cpp 30 | src/synthesis/UnusedSensitiveSignal.cpp 31 | src/synthesis/UndrivenRange.cpp) 32 | 33 | target_include_directories(slang_tidy_obj_lib PUBLIC include) 34 | target_link_libraries(slang_tidy_obj_lib PUBLIC slang::slang) 35 | 36 | add_executable(slang_tidy src/tidy.cpp) 37 | add_executable(slang::tidy ALIAS slang_tidy) 38 | 39 | target_link_libraries(slang_tidy PRIVATE slang_tidy_obj_lib) 40 | set_target_properties(slang_tidy PROPERTIES OUTPUT_NAME "slang-tidy") 41 | 42 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 43 | target_sources(slang_tidy 44 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 45 | endif() 46 | 47 | if(SLANG_INCLUDE_INSTALL) 48 | install(TARGETS slang_tidy RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 49 | endif() 50 | 51 | if(SLANG_INCLUDE_TESTS) 52 | add_subdirectory(tests) 53 | endif() 54 | -------------------------------------------------------------------------------- /tools/tidy/include/TidyConfigPrinter.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file TidyConfigPrinter.h 3 | //! @brief Configuration file printing 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "TidyConfigParser.h" 11 | #include "TidyFactory.h" 12 | 13 | #include "slang/text/FormatBuffer.h" 14 | 15 | struct TidyConfigPrinter { 16 | 17 | static std::string toLower(const std::string_view input) { 18 | std::string result(input); 19 | std::transform(result.begin(), result.end(), result.begin(), 20 | [](unsigned char c) { return std::tolower(c); }); 21 | return result; 22 | } 23 | 24 | static slang::FormatBuffer dumpConfig(TidyConfig const& tidyConfig) { 25 | slang::FormatBuffer result; 26 | result.append("Checks:\n"); 27 | const auto& enabledChecks = Registry::getEnabledChecks(); 28 | for (auto it = enabledChecks.begin(); it != enabledChecks.end(); ++it) { 29 | const auto check = Registry::create(*it); 30 | result.append(fmt::format(" {}-{}", toLower(toString(check->getKind())), 31 | TidyConfigParser::unformatCheckName(check->name()))); 32 | if (std::next(it) != enabledChecks.end()) { 33 | result.append(",\n"); 34 | } 35 | else { 36 | result.append("\n"); 37 | } 38 | } 39 | result.append("\n"); 40 | 41 | result.append("CheckConfigs:\n"); 42 | const auto& configValues = tidyConfig.serialise(); 43 | std::vector> populatedValues; 44 | for (auto& [name, value] : configValues) { 45 | if (value.empty()) { 46 | // Skip empty entries; 47 | continue; 48 | } 49 | populatedValues.push_back({name, value}); 50 | } 51 | for (auto it = populatedValues.begin(); it != populatedValues.end(); ++it) { 52 | result.append(fmt::format(" {}: \"{}\"", it->first, it->second)); 53 | if (std::next(it) != populatedValues.end()) { 54 | result.append(",\n"); 55 | } 56 | else { 57 | result.append("\n"); 58 | } 59 | } 60 | 61 | return result; 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /tools/tidy/include/TidyDiags.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file TidyDiags.h 3 | //! @brief Generated diagnostic enums for the Tidy subsystem 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include "slang/diagnostics/Diagnostics.h" 11 | 12 | namespace slang::diag { 13 | 14 | inline constexpr DiagCode OnlyAssignedOnReset(DiagSubsystem::Tidy, 0); 15 | inline constexpr DiagCode RegisterNotAssignedOnReset(DiagSubsystem::Tidy, 1); 16 | inline constexpr DiagCode EnforcePortSuffix(DiagSubsystem::Tidy, 2); 17 | inline constexpr DiagCode NoLatchesOnDesign(DiagSubsystem::Tidy, 3); 18 | inline constexpr DiagCode NoOldAlwaysSyntax(DiagSubsystem::Tidy, 4); 19 | inline constexpr DiagCode AlwaysCombNonBlocking(DiagSubsystem::Tidy, 5); 20 | inline constexpr DiagCode AlwaysFFBlocking(DiagSubsystem::Tidy, 6); 21 | inline constexpr DiagCode EnforceModuleInstantiationPrefix(DiagSubsystem::Tidy, 7); 22 | inline constexpr DiagCode OnlyANSIPortDecl(DiagSubsystem::Tidy, 8); 23 | inline constexpr DiagCode XilinxDoNotCareValues(DiagSubsystem::Tidy, 9); 24 | inline constexpr DiagCode CastSignedIndex(DiagSubsystem::Tidy, 10); 25 | inline constexpr DiagCode NoDotStarInPortConnection(DiagSubsystem::Tidy, 11); 26 | inline constexpr DiagCode NoImplicitPortNameInPortConnection(DiagSubsystem::Tidy, 12); 27 | inline constexpr DiagCode AlwaysCombBlockNamed(DiagSubsystem::Tidy, 13); 28 | inline constexpr DiagCode GenerateNamed(DiagSubsystem::Tidy, 14); 29 | inline constexpr DiagCode NoDotVarInPortConnection(DiagSubsystem::Tidy, 15); 30 | inline constexpr DiagCode NoLegacyGenerate(DiagSubsystem::Tidy, 16); 31 | inline constexpr DiagCode AlwaysFFAssignmentOutsideConditional(DiagSubsystem::Tidy, 17); 32 | inline constexpr DiagCode UnusedSensitiveSignal(DiagSubsystem::Tidy, 18); 33 | inline constexpr DiagCode EnforcePortPrefix(DiagSubsystem::Tidy, 19); 34 | inline constexpr DiagCode UndrivenRange(DiagSubsystem::Tidy, 20); 35 | 36 | } // namespace slang::diag 37 | -------------------------------------------------------------------------------- /tools/tidy/include/TidyKind.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | //! @file TidyKind.h 3 | //! @brief Enum describing the different kinds of checks 4 | // 5 | // SPDX-FileCopyrightText: Michael Popoloski 6 | // SPDX-License-Identifier: MIT 7 | //------------------------------------------------------------------------------ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "slang/util/Util.h" 16 | 17 | namespace slang { 18 | // clang-format off 19 | #define KIND(x) \ 20 | x(Synthesis) \ 21 | x(Style) 22 | SLANG_ENUM(TidyKind, KIND) 23 | #undef KIND 24 | // clang-format on 25 | 26 | inline std::optional tidyKindFromStr(std::string str) { 27 | std::transform(str.begin(), str.end(), str.begin(), ::tolower); 28 | if (str == "synthesis" || str == "synth") 29 | return slang::TidyKind::Synthesis; 30 | if (str == "style") 31 | return slang::TidyKind::Style; 32 | return {}; 33 | } 34 | 35 | } // namespace slang 36 | -------------------------------------------------------------------------------- /tools/tidy/src/ASTHelperVisitors.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | 6 | #include "slang/syntax/AllSyntax.h" 7 | 8 | std::optional getIdentifier(const slang::ast::Expression& expr) { 9 | const slang::ast::Symbol* symbol = nullptr; 10 | if (slang::ast::MemberAccessExpression::isKind(expr.kind)) { 11 | auto& memberAccess = expr.as(); 12 | // Recursively get the base identifier from the value part of the member access 13 | return getIdentifier(memberAccess.value()); 14 | } 15 | else { 16 | symbol = expr.getSymbolReference(); 17 | } 18 | 19 | if (symbol) { 20 | return symbol->name; 21 | } 22 | return {}; 23 | } 24 | 25 | std::optional getExpressionSourceLocation( 26 | const slang::ast::Expression& expr) { 27 | if (!expr.syntax) { 28 | return std::nullopt; 29 | } 30 | return expr.syntax->getFirstToken().location(); 31 | } 32 | -------------------------------------------------------------------------------- /tools/tidy/src/style/AlwaysCombNamed.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | #include "fmt/color.h" 7 | 8 | #include "slang/syntax/AllSyntax.h" 9 | 10 | using namespace slang; 11 | using namespace slang::ast; 12 | 13 | namespace always_comb_named { 14 | struct MainVisitor : public TidyVisitor, ASTVisitor { 15 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 16 | 17 | void handle(const ProceduralBlockSymbol& symbol) { 18 | 19 | NEEDS_SKIP_SYMBOL(symbol) 20 | 21 | if (symbol.procedureKind != ProceduralBlockKind::AlwaysComb || 22 | symbol.getBody().kind != StatementKind::Block) 23 | return; 24 | 25 | bool isUnnamed = (symbol.getBody().as().blockSymbol == nullptr || 26 | symbol.getBody().as().blockSymbol->name.empty()); 27 | 28 | if (isUnnamed) { 29 | diags.add(diag::AlwaysCombBlockNamed, symbol.location); 30 | } 31 | } 32 | }; 33 | } // namespace always_comb_named 34 | 35 | using namespace always_comb_named; 36 | class AlwaysCombBlockNamed : public TidyCheck { 37 | public: 38 | [[maybe_unused]] explicit AlwaysCombBlockNamed(TidyKind kind) : TidyCheck(kind) {} 39 | 40 | bool check(const ast::RootSymbol& root, const slang::analysis::AnalysisManager&) override { 41 | MainVisitor visitor(diagnostics); 42 | root.visit(visitor); 43 | return diagnostics.empty(); 44 | } 45 | 46 | DiagCode diagCode() const override { return diag::AlwaysCombBlockNamed; } 47 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 48 | std::string diagString() const override { return "definition of an unnamed always_comb block"; } 49 | std::string name() const override { return "AlwaysCombBlockNamed"; } 50 | std::string description() const override { return shortDescription(); } 51 | std::string shortDescription() const override { 52 | return "Enforces that each always_comb block is named"; 53 | } 54 | }; 55 | 56 | REGISTER(AlwaysCombBlockNamed, AlwaysCombBlockNamed, TidyKind::Style) 57 | -------------------------------------------------------------------------------- /tools/tidy/src/style/AlwaysCombNonBlocking.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | #include "fmt/color.h" 7 | 8 | #include "slang/syntax/AllSyntax.h" 9 | 10 | using namespace slang; 11 | using namespace slang::ast; 12 | 13 | namespace always_comb_non_blocking { 14 | 15 | struct MainVisitor : public TidyVisitor, ASTVisitor { 16 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 17 | 18 | void handle(const ProceduralBlockSymbol& symbol) { 19 | NEEDS_SKIP_SYMBOL(symbol) 20 | if (symbol.procedureKind == ProceduralBlockKind::AlwaysComb) { 21 | bool hasNonBlockingAssignment = false; 22 | symbol.visitStmts(makeVisitor([&](auto&, const AssignmentExpression& expr) { 23 | if (expr.isNonBlocking()) { 24 | hasNonBlockingAssignment = true; 25 | } 26 | })); 27 | if (hasNonBlockingAssignment) { 28 | diags.add(diag::AlwaysCombNonBlocking, symbol.location); 29 | } 30 | } 31 | } 32 | }; 33 | } // namespace always_comb_non_blocking 34 | 35 | using namespace always_comb_non_blocking; 36 | class AlwaysCombNonBlocking : public TidyCheck { 37 | public: 38 | [[maybe_unused]] explicit AlwaysCombNonBlocking(TidyKind kind) : TidyCheck(kind) {} 39 | 40 | bool check(const ast::RootSymbol& root, const slang::analysis::AnalysisManager&) override { 41 | MainVisitor visitor(diagnostics); 42 | root.visit(visitor); 43 | return diagnostics.empty(); 44 | } 45 | 46 | DiagCode diagCode() const override { return diag::AlwaysCombNonBlocking; } 47 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 48 | std::string diagString() const override { 49 | return "use of a non blocking assignment inside always_comb"; 50 | } 51 | std::string name() const override { return "AlwaysCombNonBlocking"; } 52 | std::string description() const override { return shortDescription(); } 53 | std::string shortDescription() const override { 54 | return "Enforces that non blocking assignments are not being used inside always_comb " 55 | "blocks"; 56 | } 57 | }; 58 | 59 | REGISTER(AlwaysCombNonBlocking, AlwaysCombNonBlocking, TidyKind::Style) 60 | -------------------------------------------------------------------------------- /tools/tidy/src/style/GenerateNamed.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | #include "fmt/color.h" 7 | 8 | #include "slang/syntax/AllSyntax.h" 9 | 10 | using namespace slang; 11 | using namespace slang::ast; 12 | 13 | namespace generate_named { 14 | struct MainVisitor : public TidyVisitor, ASTVisitor { 15 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 16 | 17 | void handle(const GenerateBlockSymbol& symbol) { 18 | 19 | NEEDS_SKIP_SYMBOL(symbol) 20 | 21 | if (!symbol.getParentScope()) 22 | return; 23 | 24 | auto& parSymbol = symbol.getParentScope()->asSymbol(); 25 | 26 | bool isUnnamed = symbol.name.empty(); 27 | if (parSymbol.kind == SymbolKind::GenerateBlockArray) { 28 | if (symbol.constructIndex != 0) 29 | return; 30 | isUnnamed = parSymbol.name.empty(); 31 | } 32 | 33 | if (isUnnamed) { 34 | diags.add(diag::GenerateNamed, symbol.location); 35 | } 36 | } 37 | }; 38 | } // namespace generate_named 39 | 40 | using namespace generate_named; 41 | class GenerateNamed : public TidyCheck { 42 | public: 43 | [[maybe_unused]] explicit GenerateNamed(TidyKind kind) : TidyCheck(kind) {} 44 | 45 | bool check(const ast::RootSymbol& root, const slang::analysis::AnalysisManager&) override { 46 | MainVisitor visitor(diagnostics); 47 | root.visit(visitor); 48 | return diagnostics.empty(); 49 | } 50 | 51 | DiagCode diagCode() const override { return diag::GenerateNamed; } 52 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 53 | std::string diagString() const override { return "definition of an unnamed generate block"; } 54 | std::string name() const override { return "GenerateNamed"; } 55 | std::string description() const override { return shortDescription(); } 56 | std::string shortDescription() const override { 57 | return "Enforces that each generate block is named"; 58 | } 59 | }; 60 | 61 | REGISTER(GenerateNamed, GenerateNamed, TidyKind::Style) 62 | -------------------------------------------------------------------------------- /tools/tidy/src/style/NoDotStarInPortConnection.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | 7 | #include "slang/syntax/AllSyntax.h" 8 | #include "slang/syntax/SyntaxVisitor.h" 9 | 10 | using namespace slang; 11 | using namespace slang::ast; 12 | using namespace slang::syntax; 13 | 14 | namespace no_dot_start_in_port_connection { 15 | 16 | struct PortConnectionVisitor : public SyntaxVisitor { 17 | void handle(const WildcardPortConnectionSyntax& port) { foundPorts.push_back(&port); } 18 | 19 | public: 20 | std::vector foundPorts; 21 | }; 22 | 23 | struct MainVisitor : public TidyVisitor, ASTVisitor { 24 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 25 | 26 | void handle(const InstanceBodySymbol& symbol) { 27 | NEEDS_SKIP_SYMBOL(symbol) 28 | PortConnectionVisitor visitor; 29 | symbol.getSyntax()->visit(visitor); 30 | for (const auto port : visitor.foundPorts) 31 | diags.add(diag::NoDotStarInPortConnection, port->star.location()); 32 | } 33 | }; 34 | } // namespace no_dot_start_in_port_connection 35 | 36 | using namespace no_dot_start_in_port_connection; 37 | 38 | class NoDotStarInPortConnection : public TidyCheck { 39 | public: 40 | [[maybe_unused]] explicit NoDotStarInPortConnection(TidyKind kind) : TidyCheck(kind) {} 41 | 42 | bool check(const RootSymbol& root, const slang::analysis::AnalysisManager&) override { 43 | MainVisitor visitor(diagnostics); 44 | root.visit(visitor); 45 | return diagnostics.empty(); 46 | } 47 | 48 | DiagCode diagCode() const override { return diag::NoDotStarInPortConnection; } 49 | 50 | std::string diagString() const override { return "use of .* in port connection list"; } 51 | 52 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 53 | 54 | std::string name() const override { return "NoDotStarInPortConnection"; } 55 | 56 | std::string description() const override { return shortDescription(); } 57 | 58 | std::string shortDescription() const override { 59 | return "Checks if in a module instantiation any port is connected using .* syntax."; 60 | } 61 | }; 62 | 63 | REGISTER(NoDotStarInPortConnection, NoDotStarInPortConnection, TidyKind::Style) 64 | -------------------------------------------------------------------------------- /tools/tidy/src/style/NoImplicitPortNameInPortConnection.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | #include 7 | 8 | #include "slang/syntax/AllSyntax.h" 9 | #include "slang/syntax/SyntaxVisitor.h" 10 | 11 | using namespace slang; 12 | using namespace slang::ast; 13 | using namespace slang::syntax; 14 | 15 | namespace no_implicit_port_name_in_port_connection { 16 | 17 | struct PortConnectionVisitor : public SyntaxVisitor { 18 | void handle(const PortConnectionSyntax& syntax) { 19 | if (syntax.toString().find('(') == std::string::npos) 20 | found = true; 21 | } 22 | 23 | public: 24 | bool found{false}; 25 | }; 26 | 27 | struct MainVisitor : public TidyVisitor, ASTVisitor { 28 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 29 | 30 | void handle(const InstanceBodySymbol& symbol) { 31 | NEEDS_SKIP_SYMBOL(symbol) 32 | PortConnectionVisitor visitor; 33 | symbol.getSyntax()->visit(visitor); 34 | if (visitor.found) 35 | diags.add(diag::NoImplicitPortNameInPortConnection, symbol.location); 36 | } 37 | }; 38 | } // namespace no_implicit_port_name_in_port_connection 39 | 40 | using namespace no_implicit_port_name_in_port_connection; 41 | 42 | class NoImplicitPortNameInPortConnection : public TidyCheck { 43 | public: 44 | [[maybe_unused]] explicit NoImplicitPortNameInPortConnection(TidyKind kind) : TidyCheck(kind) {} 45 | 46 | bool check(const RootSymbol& root, const slang::analysis::AnalysisManager&) override { 47 | MainVisitor visitor(diagnostics); 48 | root.visit(visitor); 49 | return diagnostics.empty(); 50 | } 51 | 52 | DiagCode diagCode() const override { return diag::NoImplicitPortNameInPortConnection; } 53 | 54 | std::string diagString() const override { 55 | return "port name not specified. Please use .port_name(net) syntax."; 56 | } 57 | 58 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 59 | 60 | std::string name() const override { return "NoImplicitPortNameInPortConnection"; } 61 | 62 | std::string description() const override { return shortDescription(); } 63 | 64 | std::string shortDescription() const override { 65 | return "Checks if in a module instantiation any port is connected using .port_name syntax."; 66 | } 67 | }; 68 | 69 | REGISTER(NoImplicitPortNameInPortConnection, NoImplicitPortNameInPortConnection, TidyKind::Style) 70 | -------------------------------------------------------------------------------- /tools/tidy/src/style/NoLegacyGenerate.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | #include "fmt/color.h" 7 | 8 | #include "slang/syntax/AllSyntax.h" 9 | #include "slang/syntax/SyntaxVisitor.h" 10 | 11 | using namespace slang; 12 | using namespace slang::ast; 13 | using namespace slang::syntax; 14 | 15 | namespace no_legacy_generate { 16 | 17 | struct GenerateVisitor : public SyntaxVisitor { 18 | void handle(const GenerateRegionSyntax& syntax) { foundGenerate.push_back(&syntax); } 19 | 20 | public: 21 | std::vector foundGenerate; 22 | }; 23 | 24 | struct MainVisitor : public TidyVisitor, ASTVisitor { 25 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 26 | 27 | void handle(const InstanceBodySymbol& symbol) { 28 | NEEDS_SKIP_SYMBOL(symbol) 29 | if (!symbol.getSyntax()) 30 | return; 31 | 32 | GenerateVisitor visitor; 33 | symbol.getSyntax()->visit(visitor); 34 | 35 | for (const auto& syntax : visitor.foundGenerate) { 36 | diags.add(diag::NoLegacyGenerate, syntax->keyword.location()); 37 | } 38 | } 39 | }; 40 | } // namespace no_legacy_generate 41 | 42 | using namespace no_legacy_generate; 43 | class NoLegacyGenerate : public TidyCheck { 44 | public: 45 | [[maybe_unused]] explicit NoLegacyGenerate(TidyKind kind) : TidyCheck(kind) {} 46 | 47 | bool check(const ast::RootSymbol& root, const slang::analysis::AnalysisManager&) override { 48 | MainVisitor visitor(diagnostics); 49 | root.visit(visitor); 50 | return diagnostics.empty(); 51 | } 52 | 53 | DiagCode diagCode() const override { return diag::NoLegacyGenerate; } 54 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 55 | std::string diagString() const override { return "usage of generate block is deprecated"; } 56 | std::string name() const override { return "NoLegacyGenerate"; } 57 | std::string description() const override { return shortDescription(); } 58 | std::string shortDescription() const override { 59 | return "Enforces that no generate block is declared since it is deprecated"; 60 | } 61 | }; 62 | 63 | REGISTER(NoLegacyGenerate, NoLegacyGenerate, TidyKind::Style) 64 | -------------------------------------------------------------------------------- /tools/tidy/src/style/NoOldAlwaysSyntax.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | 7 | #include "slang/syntax/AllSyntax.h" 8 | 9 | using namespace slang; 10 | using namespace slang::ast; 11 | 12 | namespace no_old_always_syntax { 13 | struct MainVisitor : public TidyVisitor, ASTVisitor { 14 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 15 | 16 | void handle(const ast::ProceduralBlockSymbol& symbol) { 17 | NEEDS_SKIP_SYMBOL(symbol) 18 | 19 | if (symbol.isFromAssertion) 20 | return; 21 | 22 | if (symbol.procedureKind == ProceduralBlockKind::Always) { 23 | // Parent scopes of the always block 24 | std::set scopes; 25 | for (auto scope = symbol.getHierarchicalParent(); scope; 26 | scope = scope->asSymbol().getHierarchicalParent()) { 27 | scopes.insert(scope); 28 | } 29 | 30 | // If there are assignments of variables that are declared outside the always block, 31 | // issue the diagnostic 32 | CollectLHSSymbols visitor; 33 | symbol.visit(visitor); 34 | for (auto& s : visitor.symbols) 35 | if (scopes.contains(s->getHierarchicalParent())) 36 | diags.add(diag::NoOldAlwaysSyntax, symbol.location); 37 | } 38 | } 39 | }; 40 | } // namespace no_old_always_syntax 41 | 42 | using namespace no_old_always_syntax; 43 | 44 | class NoOldAlwaysSyntax : public TidyCheck { 45 | public: 46 | [[maybe_unused]] explicit NoOldAlwaysSyntax(TidyKind kind) : TidyCheck(kind) {} 47 | 48 | bool check(const RootSymbol& root, const slang::analysis::AnalysisManager&) override { 49 | MainVisitor visitor(diagnostics); 50 | root.visit(visitor); 51 | return diagnostics.empty(); 52 | } 53 | 54 | DiagCode diagCode() const override { return diag::NoOldAlwaysSyntax; } 55 | 56 | std::string diagString() const override { return "use of old always verilog syntax"; } 57 | 58 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 59 | 60 | std::string name() const override { return "NoOldAlwaysSyntax"; } 61 | 62 | std::string description() const override { return shortDescription(); } 63 | 64 | std::string shortDescription() const override { 65 | return "Checks if old always verilog syntax is being use in the design."; 66 | } 67 | }; 68 | 69 | REGISTER(NoOldAlwaysSyntax, NoOldAlwaysSyntax, TidyKind::Style) 70 | -------------------------------------------------------------------------------- /tools/tidy/src/style/OnlyANSIPortDecl.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | 7 | #include "slang/syntax/AllSyntax.h" 8 | 9 | using namespace slang; 10 | using namespace slang::ast; 11 | 12 | namespace no_ansi_port_decl { 13 | struct MainVisitor : public TidyVisitor, ASTVisitor { 14 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 15 | 16 | void handle(const PortSymbol& port) { 17 | NEEDS_SKIP_SYMBOL(port) 18 | 19 | if (!port.isAnsiPort) { 20 | diags.add(diag::OnlyANSIPortDecl, port.location) << port.internalSymbol->name; 21 | } 22 | } 23 | }; 24 | } // namespace no_ansi_port_decl 25 | 26 | using namespace no_ansi_port_decl; 27 | class OnlyANSIPortDecl : public TidyCheck { 28 | public: 29 | [[maybe_unused]] explicit OnlyANSIPortDecl(TidyKind kind) : TidyCheck(kind) {} 30 | 31 | bool check(const ast::RootSymbol& root, const slang::analysis::AnalysisManager&) override { 32 | MainVisitor visitor(diagnostics); 33 | root.visit(visitor); 34 | return diagnostics.empty(); 35 | } 36 | 37 | DiagCode diagCode() const override { return diag::OnlyANSIPortDecl; } 38 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 39 | std::string diagString() const override { 40 | return "port '{}' is declared using non-ANSI port declaration style"; 41 | } 42 | std::string name() const override { return "OnlyANSIPortDecl"; } 43 | std::string description() const override { 44 | return "Enforces that all ports in the design are declared using the ANSI port " 45 | "declaration style"; 46 | } 47 | std::string shortDescription() const override { return "Enforces ANSI port declaration style"; } 48 | }; 49 | 50 | REGISTER(OnlyANSIPortDecl, OnlyANSIPortDecl, TidyKind::Style) 51 | -------------------------------------------------------------------------------- /tools/tidy/src/synthesis/CastSignedIndex.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | #include "fmt/color.h" 7 | 8 | #include "slang/syntax/AllSyntax.h" 9 | 10 | using namespace slang; 11 | using namespace slang::ast; 12 | 13 | namespace cast_signed_index { 14 | struct MainVisitor : public TidyVisitor, ASTVisitor { 15 | explicit MainVisitor(Diagnostics& diagnostics) : TidyVisitor(diagnostics) {} 16 | 17 | void handle(const ElementSelectExpression& expr) { 18 | if (auto& selector = expr.selector(); 19 | selector.kind == slang::ast::ExpressionKind::Conversion && selector.type->isSigned()) 20 | diags.add(diag::CastSignedIndex, selector.sourceRange); 21 | } 22 | }; 23 | } // namespace cast_signed_index 24 | 25 | using namespace cast_signed_index; 26 | 27 | class CastSignedIndex : public TidyCheck { 28 | public: 29 | [[maybe_unused]] explicit CastSignedIndex(TidyKind kind) : TidyCheck(kind) {} 30 | 31 | bool check(const RootSymbol& root, const slang::analysis::AnalysisManager&) override { 32 | MainVisitor visitor(diagnostics); 33 | root.visit(visitor); 34 | return diagnostics.empty(); 35 | } 36 | 37 | DiagCode diagCode() const override { return diag::CastSignedIndex; } 38 | 39 | std::string diagString() const override { 40 | return "casting signed indexes can potentially produce negative indexes. Please remove the " 41 | "cast or make the index unsigned"; 42 | } 43 | 44 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Warning; } 45 | 46 | std::string name() const override { return "CastSignedIndex"; } 47 | 48 | std::string description() const override { return shortDescription(); } 49 | 50 | std::string shortDescription() const override { 51 | return "The cast of a signed index can potentially produce negative indexes making some " 52 | "positions unreachable."; 53 | } 54 | }; 55 | 56 | REGISTER(CastSignedIndex, CastSignedIndex, TidyKind::Synthesis) 57 | -------------------------------------------------------------------------------- /tools/tidy/src/synthesis/NoLatchesOnDesign.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "ASTHelperVisitors.h" 5 | #include "TidyDiags.h" 6 | #include "fmt/color.h" 7 | 8 | #include "slang/analysis/AnalysisManager.h" 9 | #include "slang/syntax/AllSyntax.h" 10 | 11 | using namespace slang; 12 | using namespace slang::ast; 13 | using namespace slang::analysis; 14 | 15 | namespace no_latches_on_design { 16 | struct MainVisitor : public TidyVisitor, ASTVisitor { 17 | const AnalysisManager& analysisManager; 18 | 19 | MainVisitor(Diagnostics& diagnostics, const AnalysisManager& analysisManager) : 20 | TidyVisitor(diagnostics), analysisManager(analysisManager) {} 21 | 22 | void handle(const VariableSymbol& symbol) { 23 | NEEDS_SKIP_SYMBOL(symbol) 24 | 25 | auto drivers = analysisManager.getDrivers(symbol); 26 | if (drivers.empty()) 27 | return; 28 | 29 | auto firstDriver = drivers[0].first; 30 | if (firstDriver && firstDriver->source == DriverSource::AlwaysLatch) { 31 | diags.add(diag::NoLatchesOnDesign, symbol.location); 32 | } 33 | } 34 | }; 35 | } // namespace no_latches_on_design 36 | 37 | using namespace no_latches_on_design; 38 | 39 | class NoLatchesOnDesign : public TidyCheck { 40 | public: 41 | [[maybe_unused]] explicit NoLatchesOnDesign(TidyKind kind) : TidyCheck(kind) {} 42 | 43 | bool check(const RootSymbol& root, const AnalysisManager& analysisManager) override { 44 | MainVisitor visitor(diagnostics, analysisManager); 45 | root.visit(visitor); 46 | return diagnostics.empty(); 47 | } 48 | 49 | DiagCode diagCode() const override { return diag::NoLatchesOnDesign; } 50 | 51 | std::string diagString() const override { return "latches are not allowed in this design"; } 52 | 53 | DiagnosticSeverity diagSeverity() const override { return DiagnosticSeverity::Error; } 54 | 55 | std::string name() const override { return "NoLatchesOnDesign"; } 56 | 57 | std::string description() const override { return shortDescription(); } 58 | 59 | std::string shortDescription() const override { 60 | return "Checks for latches in the design. It will fail if any are found."; 61 | } 62 | }; 63 | 64 | REGISTER(NoLatchesOnDesign, NoLatchesOnDesign, TidyKind::Synthesis) 65 | -------------------------------------------------------------------------------- /tools/tidy/tests/AlwaysCombNamedTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("AlwaysCombBlockNamed: Unnamed always_comb block") { 9 | auto result = runCheckTest("AlwaysCombBlockNamed", R"( 10 | module top (); 11 | logic a, b; 12 | always_comb begin 13 | a = b; 14 | end 15 | endmodule 16 | )"); 17 | CHECK_FALSE(result); 18 | } 19 | 20 | TEST_CASE("AlwaysCombBlockNamed: Named always_comb block") { 21 | auto result = runCheckTest("AlwaysCombBlockNamed", R"( 22 | module top (); 23 | logic a, b; 24 | always_comb begin : named_comb2 25 | a = b; 26 | end 27 | endmodule 28 | )"); 29 | CHECK(result); 30 | } 31 | 32 | TEST_CASE("AlwaysCombBlockNamed: Unnamed simple always_comb block") { 33 | auto result = runCheckTest("AlwaysCombBlockNamed", R"( 34 | module add_or_sub 35 | #(parameter N = 4) 36 | ( 37 | input logic [N-1:0] x_i, y_i, 38 | input logic add, 39 | output logic [N-1:0] z_o 40 | ); 41 | 42 | always_comb 43 | if (add) 44 | z = x + y; 45 | else 46 | z = x - y; 47 | endmodule 48 | )"); 49 | CHECK(result); 50 | } 51 | -------------------------------------------------------------------------------- /tools/tidy/tests/AlwaysCombNonBlockingTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("AlwaysCombNonBlocking: Non blocking assignment inside always_comb") { 9 | auto result = runCheckTest("AlwaysCombNonBlocking", R"( 10 | module top (); 11 | logic a, b; 12 | always_comb begin 13 | a <= b; 14 | end 15 | endmodule 16 | )"); 17 | CHECK_FALSE(result); 18 | } 19 | 20 | TEST_CASE("AlwaysCombNonBlocking: Blocking assignment inside always_comb") { 21 | auto result = runCheckTest("AlwaysCombNonBlocking", R"( 22 | module top (); 23 | logic a, b; 24 | always_comb begin 25 | a = b; 26 | end 27 | endmodule 28 | )"); 29 | CHECK(result); 30 | } 31 | -------------------------------------------------------------------------------- /tools/tidy/tests/AlwaysFFAssignmentOutsideConditionalTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("AlwaysFFAssignmentOutsideConditional: Assignment inside always_ff and outside if " 9 | "statement with reset") { 10 | auto result = runCheckTest("AlwaysFFAssignmentOutsideConditional", R"( 11 | module top; 12 | logic clk_i; 13 | logic rst_ni; 14 | logic foo, bar; 15 | int a; 16 | 17 | always_ff @(posedge clk_i or negedge rst_ni) begin 18 | foo <= bar; 19 | if(rst_ni) a <= '0; 20 | else a <= a +1; 21 | end 22 | endmodule 23 | )"); 24 | CHECK_FALSE(result); 25 | } 26 | 27 | TEST_CASE("AlwaysFFAssignmentOutsideConditional: All assignments inside either if or else " 28 | "statements inside the always_ff block") { 29 | auto result = runCheckTest("AlwaysFFAssignmentOutsideConditional", R"( 30 | module top; 31 | logic clk_i; 32 | logic rst_ni; 33 | logic a, b; 34 | 35 | always_ff @(posedge clk_i or negedge rst_ni) begin 36 | if (~rst_ni) begin 37 | a <= '0; 38 | end else begin 39 | a <= 1'b1; 40 | b <= 1'b1; 41 | end 42 | end 43 | endmodule 44 | )"); 45 | CHECK(result); 46 | } 47 | -------------------------------------------------------------------------------- /tools/tidy/tests/AlwaysFFBlockingTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("AlwaysFFBlocking: Blocking assignment inside always_ff") { 9 | auto result = runCheckTest("AlwaysFFBlocking", R"( 10 | module top (); 11 | logic a, b, c; 12 | always_ff @(posedge c) begin 13 | a = b; 14 | end 15 | endmodule 16 | )"); 17 | CHECK_FALSE(result); 18 | } 19 | 20 | TEST_CASE("AlwaysFFBlocking: Correct blocking assignment inside always_ff") { 21 | auto result = runCheckTest("AlwaysFFBlocking", R"( 22 | module top (); 23 | logic a, b, c; 24 | always_ff @(posedge c) begin 25 | int k = 1; 26 | k = 42; 27 | a <= b; 28 | end 29 | endmodule 30 | )"); 31 | CHECK(result); 32 | } 33 | 34 | TEST_CASE("AlwaysFFBlocking: Incorrect blocking assignment inside always_ff") { 35 | auto result = runCheckTest("AlwaysFFBlocking", R"( 36 | module top (); 37 | logic a, b, c; 38 | always_ff @(posedge c) begin 39 | int k = 1; 40 | k = 42; 41 | a = b; 42 | end 43 | endmodule 44 | )"); 45 | CHECK_FALSE(result); 46 | } 47 | -------------------------------------------------------------------------------- /tools/tidy/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ~~~ 2 | # SPDX-FileCopyrightText: Michael Popoloski 3 | # SPDX-License-Identifier: MIT 4 | # ~~~ 5 | 6 | add_executable( 7 | tidy_unittests 8 | ../../../tests/unittests/main.cpp 9 | ../../../tests/unittests/Test.cpp 10 | TidyConfigParserTest.cpp 11 | OnlyAssignedOnResetTest.cpp 12 | RegisterHasNoResetTest.cpp 13 | NoLatchesOnDesignTest.cpp 14 | EnforcePortPrefixTest.cpp 15 | EnforcePortSuffixTest.cpp 16 | NoOldAlwaysSyntaxTest.cpp 17 | AlwaysCombNonBlockingTest.cpp 18 | AlwaysFFBlockingTest.cpp 19 | EnforceModuleInstantiationTest.cpp 20 | OnlyANSIPortDecl.cpp 21 | XilinxDoNotCareValuesTest.cpp 22 | CastSignedIndexTest.cpp 23 | NoDotStarInPortConnectionTest.cpp 24 | NoImplicitPortNameInPortConnectionTest.cpp 25 | AlwaysCombNamedTest.cpp 26 | GenerateNamedTest.cpp 27 | NoDotVarInPortConnectionTest.cpp 28 | NoLegacyGenerateTest.cpp 29 | AlwaysFFAssignmentOutsideConditionalTest.cpp 30 | UnusedSensitiveSignalTest.cpp 31 | PrintConfigTest.cpp 32 | UndrivenRangeTest.cpp) 33 | 34 | target_link_libraries(tidy_unittests PRIVATE Catch2::Catch2 slang_tidy_obj_lib) 35 | target_compile_definitions(tidy_unittests PRIVATE UNITTESTS) 36 | target_include_directories(tidy_unittests PRIVATE ../../../tests/unittests) 37 | 38 | add_test(NAME tidy_unittests COMMAND tidy_unittests) 39 | 40 | if(CMAKE_SYSTEM_NAME MATCHES "Windows") 41 | target_sources(tidy_unittests 42 | PRIVATE ${PROJECT_SOURCE_DIR}/scripts/win32.manifest) 43 | endif() 44 | -------------------------------------------------------------------------------- /tools/tidy/tests/CastSignedIndexTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("CastSignedIndex: Negative index") { 9 | auto result = runCheckTest("CastSignedIndex", R"( 10 | module top; 11 | logic a [4]; 12 | 13 | always_comb begin 14 | for (int i = 0; i < 4; i++) begin 15 | a[2'(i)] = i; 16 | end 17 | end 18 | endmodule 19 | )"); 20 | CHECK_FALSE(result); 21 | } 22 | 23 | TEST_CASE("CastSignedIndex: Signed index") { 24 | auto result = runCheckTest("CastSignedIndex", R"( 25 | module top; 26 | logic a [4]; 27 | 28 | always_comb begin 29 | for (int i = 0; i < 4; i++) begin 30 | a[i] = i; 31 | end 32 | end 33 | endmodule 34 | )"); 35 | CHECK(result); 36 | } 37 | -------------------------------------------------------------------------------- /tools/tidy/tests/EnforceModuleInstantiationTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("EnforceModuleInstantiationPrefix: Incorrect module instantiation prefix") { 9 | auto result = runCheckTest("EnforceModuleInstantiationPrefix", R"( 10 | module test (); 11 | endmodule 12 | 13 | module top(); 14 | test test(); 15 | endmodule 16 | )"); 17 | CHECK_FALSE(result); 18 | } 19 | 20 | TEST_CASE("EnforceModuleInstantiationPrefix: Correct module instantiation prefix") { 21 | auto result = runCheckTest("EnforceModuleInstantiationPrefix", R"( 22 | module test (); 23 | endmodule 24 | 25 | module top(); 26 | test i_test(); 27 | endmodule 28 | )"); 29 | CHECK(result); 30 | } 31 | -------------------------------------------------------------------------------- /tools/tidy/tests/NoDotStarInPortConnectionTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("NoDotStarInPortConnection: Use of dot star in module port connection") { 9 | auto result = runCheckTest("NoDotStarInPortConnection", R"( 10 | module test (input clk, input rst); 11 | endmodule 12 | 13 | module top (); 14 | logic clk, rst; 15 | test t (.clk(clk), .*); 16 | endmodule 17 | )"); 18 | CHECK_FALSE(result); 19 | } 20 | 21 | TEST_CASE("NoDotStarInPortConnection: Module port connection port by port") { 22 | auto result = runCheckTest("NoDotStarInPortConnection", R"( 23 | module test (input clk, input rst); 24 | endmodule 25 | 26 | module top (); 27 | logic clk, rst; 28 | test t (.clk, .rst(rst)); 29 | endmodule 30 | )"); 31 | CHECK(result); 32 | } 33 | -------------------------------------------------------------------------------- /tools/tidy/tests/NoDotVarInPortConnectionTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("NoDotVarInPortConnection: Use of dot var in module port connection") { 9 | auto result = runCheckTest("NoDotVarInPortConnection", R"( 10 | module test (input clk, input rst); 11 | endmodule 12 | 13 | module top (); 14 | logic clk, rst; 15 | test t (.clk(clk), .rst); 16 | endmodule 17 | )"); 18 | CHECK_FALSE(result); 19 | } 20 | 21 | TEST_CASE("NoDotVarInPortConnection: Module port connection port by port") { 22 | auto result = runCheckTest("NoDotVarInPortConnection", R"( 23 | module test (input clk, input rst); 24 | endmodule 25 | 26 | module top (); 27 | logic clk, rst; 28 | test t (.clk(clk), .rst(rst)); 29 | endmodule 30 | )"); 31 | CHECK(result); 32 | } 33 | 34 | TEST_CASE("NoDotVarInPortConnection: Use of dot star in module port connection") { 35 | auto result = runCheckTest("NoDotVarInPortConnection", R"( 36 | module test (input clk, input rst); 37 | endmodule 38 | 39 | module top (); 40 | logic clk, rst; 41 | test t (.*); 42 | endmodule 43 | )"); 44 | CHECK(result); 45 | } 46 | -------------------------------------------------------------------------------- /tools/tidy/tests/NoImplicitPortNameInPortConnectionTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE( 9 | "NoImplicitPortNameInPortConnection: Only port name specified in module port connection") { 10 | auto result = runCheckTest("NoImplicitPortNameInPortConnection", R"( 11 | module test (input clk, input rst); 12 | endmodule 13 | 14 | module top (); 15 | logic clk, rst; 16 | test t (.clk, .rst(rst)); 17 | endmodule 18 | )"); 19 | CHECK_FALSE(result); 20 | } 21 | 22 | TEST_CASE("NoImplicitPortNameInPortConnection: Module port connection port by port") { 23 | auto result = runCheckTest("NoImplicitPortNameInPortConnection", R"( 24 | module test (input clk, input rst); 25 | endmodule 26 | 27 | module top (); 28 | logic clk, rst; 29 | test t (.clk(clk), .rst(rst)); 30 | endmodule 31 | )"); 32 | CHECK(result); 33 | } 34 | -------------------------------------------------------------------------------- /tools/tidy/tests/NoLatchesOnDesignTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("NoLatchesOnDesign: Design with latch") { 9 | auto result = runCheckTest("NoLatchesOnDesign", R"( 10 | module top ( 11 | input logic a, 12 | output logic b 13 | ); 14 | always_latch begin 15 | a <= b; 16 | end 17 | endmodule 18 | )"); 19 | CHECK_FALSE(result); 20 | } 21 | 22 | TEST_CASE("NoLatchesOnDesign: Design without latch") { 23 | auto result = runCheckTest("NoLatchesOnDesign", R"( 24 | module top ( 25 | input logic a, 26 | output logic b 27 | ); 28 | always_comb begin 29 | a = b; 30 | end 31 | endmodule 32 | )"); 33 | CHECK(result); 34 | } 35 | -------------------------------------------------------------------------------- /tools/tidy/tests/NoLegacyGenerateTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-FileCopyrightText: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("NoLegacyGenerate: For loop inside generate block") { 9 | auto result = runCheckTest("NoLegacyGenerate", R"( 10 | module eq_n 11 | #( parameter N =4) 12 | ( 13 | input logic [N -1:0] a , b , 14 | output logic eq 15 | ) ; 16 | logic [N -1:0] tmp ; 17 | generate begin : named_generate 18 | genvar i ; 19 | for ( i = 0; i < N ; i = i + 1) 20 | xnor gen_u ( tmp [ i ] , a [ i ] , b [ i ]) ; 21 | end 22 | endgenerate 23 | endmodule 24 | )"); 25 | CHECK_FALSE(result); 26 | } 27 | 28 | TEST_CASE("NoLegacyGenerate: For loop inside unnamed generate block") { 29 | auto result = runCheckTest("NoLegacyGenerate", R"( 30 | module eq_n 31 | #( parameter N =4) 32 | ( 33 | input logic [N -1:0] a , b , 34 | output logic eq 35 | ) ; 36 | logic [N -1:0] tmp ; 37 | generate 38 | genvar i ; 39 | for ( i = 0; i < N ; i = i + 1) 40 | xnor gen_u ( tmp [ i ] , a [ i ] , b [ i ]) ; 41 | endgenerate 42 | endmodule 43 | )"); 44 | CHECK_FALSE(result); 45 | } 46 | 47 | TEST_CASE("NoLegacyGenerate: For loop, no generate block") { 48 | auto result = runCheckTest("NoLegacyGenerate", R"( 49 | module eq_n 50 | #( parameter N =4) 51 | ( 52 | input logic [N -1:0] a , b , 53 | output logic eq 54 | ) ; 55 | logic [N -1:0] tmp ; 56 | for (genvar i = 0; i < N ; i = i + 1) 57 | xnor gen_u ( tmp [ i ] , a [ i ] , b [ i ]) ; 58 | endmodule 59 | )"); 60 | CHECK(result); 61 | } 62 | 63 | TEST_CASE("NoLegacyGenerate: For loop inside conditional block") { 64 | auto result = runCheckTest("NoLegacyGenerate", R"( 65 | module eq_n 66 | #( 67 | parameter N = 4, 68 | parameter cond = 0 69 | ) 70 | ( 71 | input logic [N -1:0] a , b , 72 | output logic eq 73 | ) ; 74 | logic [N -1:0] tmp ; 75 | if (cond) begin 76 | for (genvar i = 0; i < N ; i = i + 1) begin : foo_name 77 | xnor gen_u ( tmp [ i ] , a [ i ] , b [ i ]) ; 78 | end 79 | end 80 | 81 | endmodule 82 | 83 | )"); 84 | CHECK(result); 85 | } 86 | -------------------------------------------------------------------------------- /tools/tidy/tests/OnlyANSIPortDecl.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | TEST_CASE("OnlyANSIPortDecl: Use of non-ANSI port declaration style") { 9 | auto result = runCheckTest("OnlyANSIPortDecl", R"( 10 | module top(a,b,c); 11 | input logic a; 12 | inout logic b; 13 | output logic c; 14 | 15 | endmodule 16 | )"); 17 | CHECK_FALSE(result); 18 | } 19 | 20 | TEST_CASE("OnlyANSIPortDecl: Use of ANSI port declaration style") { 21 | auto result = runCheckTest("OnlyANSIPortDecl", R"( 22 | module top( 23 | input logic a, 24 | inout logic b, 25 | output logic c 26 | ); 27 | 28 | endmodule 29 | )"); 30 | CHECK(result); 31 | } 32 | 33 | TEST_CASE("OnlyANSIPortDecl: No port declaration") { 34 | auto result = runCheckTest("OnlyANSIPortDecl", R"( 35 | module top; 36 | endmodule 37 | )"); 38 | CHECK(result); 39 | } 40 | -------------------------------------------------------------------------------- /tools/tidy/tests/PrintConfigTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyConfigPrinter.h" 6 | #include "TidyFactory.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "slang/util/OS.h" 14 | 15 | bool filesEqual(const std::string& file1, const std::string& file2) { 16 | std::ifstream f1(file1, std::ios::binary); 17 | std::ifstream f2(file2, std::ios::binary); 18 | 19 | if (!f1.is_open() || !f2.is_open()) { 20 | std::cerr << "Error opening one of the files.\n"; 21 | return false; 22 | } 23 | 24 | std::istreambuf_iterator begin1(f1); 25 | std::istreambuf_iterator begin2(f2); 26 | std::istreambuf_iterator end; 27 | 28 | return std::equal(begin1, end, begin2); 29 | } 30 | 31 | TEST_CASE("Round trip config file") { 32 | 33 | std::error_code ec; 34 | auto p = fs::temp_directory_path(ec); 35 | fs::current_path(p, ec); 36 | 37 | TidyConfig config; 38 | OS::writeFile("tidy-config1", TidyConfigPrinter::dumpConfig(config).str()); 39 | 40 | auto newConfig = TidyConfigParser(std::filesystem::path("tidy-config1")).getConfig(); 41 | OS::writeFile("tidy-config2", TidyConfigPrinter::dumpConfig(newConfig).str()); 42 | 43 | CHECK(filesEqual("tidy-config1", "tidy-config2")); 44 | } 45 | -------------------------------------------------------------------------------- /tools/tidy/tests/TidyTest.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | #pragma once 4 | 5 | #include "Test.h" 6 | #include "TidyFactory.h" 7 | 8 | #include "slang/analysis/AnalysisManager.h" 9 | #include "slang/diagnostics/DiagnosticEngine.h" 10 | #include "slang/diagnostics/TextDiagnosticClient.h" 11 | 12 | inline bool runCheckTest(const std::string& checkName, std::string_view code, 13 | std::optional inputConfig = {}, 14 | std::string* output = nullptr) { 15 | auto tree = SyntaxTree::fromText(code); 16 | 17 | Compilation compilation; 18 | compilation.addSyntaxTree(tree); 19 | compilation.getAllDiagnostics(); 20 | auto& root = compilation.getRoot(); 21 | 22 | compilation.freeze(); 23 | 24 | analysis::AnalysisManager analysisManager; 25 | analysisManager.analyze(compilation); 26 | 27 | TidyConfig config; 28 | Registry::setConfig(inputConfig ? *inputConfig : config); 29 | Registry::setSourceManager(compilation.getSourceManager()); 30 | auto check = Registry::create(checkName); 31 | auto result = check->check(root, analysisManager); 32 | 33 | if (output) { 34 | DiagnosticEngine diagEngine(*compilation.getSourceManager()); 35 | diagEngine.setMessage(check->diagCode(), check->diagMessage()); 36 | diagEngine.setSeverity(check->diagCode(), check->diagSeverity()); 37 | 38 | auto& diags = check->getDiagnostics(); 39 | 40 | auto client = std::make_shared(); 41 | diagEngine.addClient(client); 42 | 43 | for (auto& diag : diags) { 44 | diagEngine.issue(diag); 45 | } 46 | 47 | *output = client->getString(); 48 | } 49 | 50 | return result; 51 | } 52 | -------------------------------------------------------------------------------- /tools/tidy/tests/UndrivenRangeTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "Test.h" 5 | #include "TidyFactory.h" 6 | #include "TidyTest.h" 7 | 8 | #include "slang/diagnostics/AnalysisDiags.h" 9 | #include "slang/diagnostics/DiagnosticEngine.h" 10 | #include "slang/diagnostics/TextDiagnosticClient.h" 11 | 12 | TEST_CASE("Undriven range: simple case with a two bit bus") { 13 | std::string output; 14 | auto result = runCheckTest("UndrivenRange", R"( 15 | module top; 16 | logic [1:0] a; 17 | always_comb 18 | a[0] = 1; 19 | endmodule 20 | )", 21 | {}, &output); 22 | 23 | CHECK_FALSE(result); 24 | 25 | CHECK("\n" + output == R"( 26 | source:3:15: warning: [SYNTHESIS-20] variable a has undriven bits: 1 27 | logic [1:0] a; 28 | ^ 29 | )"); 30 | } 31 | 32 | TEST_CASE("Undriven range: a 32b bus with missing drivers") { 33 | std::string output; 34 | auto result = runCheckTest("UndrivenRange", R"( 35 | module top; 36 | logic [31:0] a; 37 | always_comb begin 38 | a[7:0] = 8'hFF; 39 | a[11] = 1; 40 | a[30] = 0; 41 | end 42 | endmodule 43 | )", 44 | {}, &output); 45 | 46 | CHECK_FALSE(result); 47 | 48 | CHECK("\n" + output == R"( 49 | source:3:16: warning: [SYNTHESIS-20] variable a has undriven bits: 8:10, 12:29, 31 50 | logic [31:0] a; 51 | ^ 52 | )"); 53 | } 54 | -------------------------------------------------------------------------------- /tools/tidy/tests/XilinxDoNotCareValuesTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: Michael Popoloski 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "TidyTest.h" 5 | 6 | TEST_CASE("XilinxDoNotCareValues: Use of 'd") { 7 | auto result = runCheckTest("XilinxDoNotCareValues", R"( 8 | module top; 9 | logic [3:0] a = 4'd?; 10 | endmodule 11 | )"); 12 | CHECK_FALSE(result); 13 | } 14 | 15 | TEST_CASE("XilinxDoNotCareValues: Use of 'b") { 16 | auto result = runCheckTest("XilinxDoNotCareValues", R"( 17 | module top; 18 | logic [3:0] a = 4'b?; 19 | endmodule 20 | )"); 21 | CHECK(result); 22 | } 23 | 24 | TEST_CASE("XilinxDoNotCareValues: No do-not-care values") { 25 | auto result = runCheckTest("XilinxDoNotCareValues", R"( 26 | module top; 27 | logic [3:0] a = 4'd10; 28 | endmodule 29 | )"); 30 | CHECK(result); 31 | } 32 | 33 | TEST_CASE("XilinxDoNotCareValues: No do-not-care values but with comment") { 34 | auto result = runCheckTest("XilinxDoNotCareValues", R"( 35 | module top; 36 | logic [3:0] a = 37 | /*'d?*/4'd10; 38 | endmodule 39 | )"); 40 | CHECK(result); 41 | } 42 | --------------------------------------------------------------------------------