Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[slang-netlist] Refactor to use ValueDriver::getBounds #839

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tools/netlist/include/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ namespace netlist {
/// A singleton to hold global configuration options.
class Config {
public:
bool debugEnabled{};
bool debugEnabled{false};
bool quietEnabled{false};

Config() = default;

Expand Down
32 changes: 26 additions & 6 deletions tools/netlist/include/Debug.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
//------------------------------------------------------------------------------
//! @file Debug.h
//! @brief Provide a debug printing macro.
//! @brief Provide debug printing macros.
//
// SPDX-FileCopyrightText: Michael Popoloski
// SPDX-License-Identifier: MIT
//------------------------------------------------------------------------------
#pragma once

#include "Config.h"
#include <iostream>
#include <fmt/core.h>

namespace netlist {

template<typename... T>
void DebugMessage(const char* filename, const int line, fmt::format_string<T...> fmt, T&&... args) {
fmt::print("{}:{}: ", filename, line);
fmt::print(fmt, std::forward<T>(args)...);
}

template<typename... T>
void InfoMessage(fmt::format_string<T...> fmt, T&&... args) {
fmt::print(fmt, std::forward<T>(args)...);
}

} // namespace netlist

#ifdef DEBUG
# define DEBUG_PRINT(x) \
if (netlist::Config::getInstance().debugEnabled) { \
std::cerr << x; \
# define DEBUG_PRINT(str, ...) \
if (netlist::Config::getInstance().debugEnabled) { \
DebugMessage(__FILE__, __LINE__, str __VA_OPT__(, ) __VA_ARGS__); \
}
#else
# define DEBUG_PRINT(x)
# define DEBUG_PRINT(str, ...)
#endif

#define INFO_PRINT(str, ...) \
if (!Config::getInstance().quietEnabled) { \
InfoMessage(str __VA_OPT__(, ) __VA_ARGS__); \
}
76 changes: 72 additions & 4 deletions tools/netlist/include/Netlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
#include "fmt/color.h"
#include "fmt/format.h"
#include <iostream>
#include <utility>

#include "slang/ast/ASTVisitor.h"
#include "slang/ast/Expression.h"
#include "slang/ast/Symbol.h"
#include "slang/ast/symbols/CompilationUnitSymbols.h"
#include "slang/ast/types/AllTypes.h"
#include "slang/ast/types/Type.h"
#include "slang/diagnostics/TextDiagnosticClient.h"
#include "slang/numeric/ConstantValue.h"
#include "slang/numeric/SVInt.h"
#include "slang/syntax/SyntaxTree.h"
#include "slang/syntax/SyntaxVisitor.h"
#include "slang/util/Util.h"
Expand Down Expand Up @@ -313,6 +319,8 @@ class NetlistVariableReference : public NetlistNode {
bool leftOperand;
/// Selectors applied to the variable reference.
SelectorsListType selectors;
// Access bounds.
ConstantRange bounds;
};

/// A class representing the design netlist.
Expand All @@ -328,7 +336,7 @@ class Netlist : public DirectedGraph<NetlistNode, NetlistEdge> {
SLANG_ASSERT(lookupPort(nodePtr->hierarchicalPath) == nullptr &&
"Port declaration already exists");
nodes.push_back(std::move(nodePtr));
DEBUG_PRINT("Add port decl " << node.hierarchicalPath << "\n");
DEBUG_PRINT("Add port decl {}\n", node.hierarchicalPath);
return node;
}

Expand All @@ -340,7 +348,7 @@ class Netlist : public DirectedGraph<NetlistNode, NetlistEdge> {
SLANG_ASSERT(lookupVariable(nodePtr->hierarchicalPath) == nullptr &&
"Variable declaration already exists");
nodes.push_back(std::move(nodePtr));
DEBUG_PRINT("Add var decl " << node.hierarchicalPath << "\n");
DEBUG_PRINT("Add var decl {}\n", node.hierarchicalPath);
return node;
}

Expand All @@ -350,7 +358,7 @@ class Netlist : public DirectedGraph<NetlistNode, NetlistEdge> {
auto& node = nodePtr->as<NetlistVariableAlias>();
symbol.getHierarchicalPath(node.hierarchicalPath);
nodes.push_back(std::move(nodePtr));
DEBUG_PRINT("Add var alias " << node.hierarchicalPath << "\n");
DEBUG_PRINT("Add var alias {}\n", node.hierarchicalPath);
return node;
}

Expand All @@ -360,7 +368,7 @@ class Netlist : public DirectedGraph<NetlistNode, NetlistEdge> {
auto nodePtr = std::make_unique<NetlistVariableReference>(symbol, expr, leftOperand);
auto& node = nodePtr->as<NetlistVariableReference>();
nodes.push_back(std::move(nodePtr));
DEBUG_PRINT("Add var ref " << symbol.name << "\n");
DEBUG_PRINT("Add var ref ", symbol.name);
return node;
}

Expand Down Expand Up @@ -396,6 +404,66 @@ class Netlist : public DirectedGraph<NetlistNode, NetlistEdge> {
auto it = std::ranges::find_if(*this, compareNode);
return it != end() ? &it->get()->as<NetlistVariableReference>() : nullptr;
}

/// Perform a transformation on the netlist graph to split variable /
/// declaration nodes into multiple parts corresponding to / the access ranges of
/// references to the variable incoming (l-values) and outgoing edges (r-values).
void split() {
std::vector<std::tuple<NetlistVariableDeclaration*, NetlistEdge*, NetlistEdge*>>
modifications;
// Find each variable declaration nodes in the graph that has multiple
// outgoing edges.
for (auto& node : nodes) {
if (node->kind == NodeKind::VariableDeclaration && node->outDegree() > 1) {
auto& varDeclNode = node->as<NetlistVariableDeclaration>();
auto& varType = varDeclNode.symbol.getDeclaredType()->getType();
DEBUG_PRINT("Variable {} has type {}\n", varDeclNode.hierarchicalPath,
varType.toString());
std::vector<NetlistEdge*> inEdges;
getInEdgesToNode(*node, inEdges);
// Find pairs of input and output edges that are attached to variable
// refertence nodes. Eg.
// var ref -> var decl -> var ref
// If the variable references select the same part of a structured
// variable, then transform them into:
// var ref -> var alias -> var ref
// And mark the original edges as disabled.
for (auto* inEdge : inEdges) {
for (auto& outEdge : *node) {
if (inEdge->getSourceNode().kind == NodeKind::VariableReference &&
outEdge->getTargetNode().kind == NodeKind::VariableReference) {
auto& sourceVarRef =
inEdge->getSourceNode().as<NetlistVariableReference>();
auto& targetVarRef =
outEdge->getTargetNode().as<NetlistVariableReference>();
// Match if the selection made by the target node intersects with the
// selection made by the source node.
auto match = sourceVarRef.bounds.overlaps(targetVarRef.bounds);
if (match) {
DEBUG_PRINT("New dependency through variable {} -> {}\n",
sourceVarRef.toString(), targetVarRef.toString());
modifications.emplace_back(&varDeclNode, inEdge, outEdge.get());
}
}
}
}
}
}
// Apply the operations to the graph.
for (auto& modification : modifications) {
auto* varDeclNode = std::get<0>(modification);
auto* inEdge = std::get<1>(modification);
auto* outEdge = std::get<2>(modification);
// Disable the existing edges.
inEdge->disable();
outEdge->disable();
// Create a new node that aliases the variable declaration.
auto& varAliasNode = addVariableAlias(varDeclNode->symbol);
// Create edges through the new node.
inEdge->getSourceNode().addEdge(varAliasNode);
varAliasNode.addEdge(outEdge->getTargetNode());
}
}
};

} // namespace netlist
65 changes: 57 additions & 8 deletions tools/netlist/include/NetlistVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "slang/ast/Symbol.h"
#include "slang/ast/symbols/BlockSymbols.h"
#include "slang/ast/symbols/CompilationUnitSymbols.h"
#include "slang/ast/symbols/ValueSymbol.h"
#include "slang/diagnostics/TextDiagnosticClient.h"
#include "slang/syntax/SyntaxTree.h"
#include "slang/syntax/SyntaxVisitor.h"
Expand All @@ -43,21 +44,20 @@ static void connectDeclToVar(Netlist& netlist, NetlistNode& declNode,
const std::string& hierarchicalPath) {
auto* varNode = netlist.lookupVariable(hierarchicalPath);
netlist.addEdge(*varNode, declNode);
DEBUG_PRINT(fmt::format("Edge decl {} to ref {}\n", varNode->getName(), declNode.getName()));
DEBUG_PRINT("Edge decl {} to ref {}\n", varNode->getName(), declNode.getName());
}

static void connectVarToDecl(Netlist& netlist, NetlistNode& varNode,
const std::string& hierarchicalPath) {
auto* declNode = netlist.lookupVariable(hierarchicalPath);
netlist.addEdge(varNode, *declNode);
DEBUG_PRINT(fmt::format("Edge ref {} to decl {}\n", varNode.getName(), declNode->getName()));
DEBUG_PRINT("Edge ref {} to decl {}\n", varNode.getName(), declNode->getName());
}

static void connectVarToVar(Netlist& netlist, NetlistNode& sourceVarNode,
NetlistNode& targetVarNode) {
netlist.addEdge(sourceVarNode, targetVarNode);
DEBUG_PRINT(
fmt::format("Edge ref {} to ref {}\n", sourceVarNode.getName(), targetVarNode.getName()));
DEBUG_PRINT("Edge ref {} to ref {}\n", sourceVarNode.getName(), targetVarNode.getName());
}

/// An AST visitor to identify variable references with selectors in
Expand All @@ -70,11 +70,14 @@ class VariableReferenceVisitor : public ast::ASTVisitor<VariableReferenceVisitor
evalCtx(evalCtx), leftOperand(leftOperand) {}

void handle(const ast::NamedValueExpression& expr) {

// If the symbol reference is to a constant (eg a parameter or enum
// value), then skip it.
if (!expr.eval(evalCtx).bad()) {
// If the symbol reference is to a constant (eg a parameter or enum
// value), then skip it.
return;
}

// Add the variable reference to the netlist.
auto& node = netlist.addVariableReference(expr.symbol, expr, leftOperand);
varList.push_back(&node);
for (auto* selector : selectors) {
Expand All @@ -93,7 +96,29 @@ class VariableReferenceVisitor : public ast::ASTVisitor<VariableReferenceVisitor
node.addMemberAccess(selector->as<ast::MemberAccessExpression>().member.name);
}
}

// Reverse the selectors.
std::reverse(node.selectors.begin(), node.selectors.end());

// Determine the access range to the variable.
if (!selectors.empty()) {
SmallVector<std::pair<const slang::ast::ValueSymbol*, const slang::ast::Expression*>>
prefixes;
selectors.front()->getLongestStaticPrefixes(prefixes, evalCtx);
SLANG_ASSERT(prefixes.size() == 1);
auto [prefixSymbol, prefixExpr] = prefixes.back();
auto bounds = slang::ast::ValueDriver::getBounds(*prefixExpr, evalCtx,
prefixSymbol->getType());
node.bounds = {static_cast<int32_t>(bounds->first),
static_cast<int32_t>(bounds->second)};
}
else {
node.bounds = {0, getTypeBitWidth(expr.symbol.getType()) - 1};
}
DEBUG_PRINT("Variable reference: {} bounds {}:{}\n", node.toString(), node.bounds.lower(),
node.bounds.upper());

// Clear the selectors for the next named value.
selectors.clear();
}

Expand Down Expand Up @@ -121,6 +146,30 @@ class VariableReferenceVisitor : public ast::ASTVisitor<VariableReferenceVisitor
bool leftOperand;
std::vector<NetlistNode*> varList;
std::vector<const ast::Expression*> selectors;

std::pair<size_t, size_t> getTypeBitWidthImpl(slang::ast::Type const& type) {
size_t fixedSize = type.getBitWidth();
if (fixedSize > 0) {
return {1, fixedSize};
}

size_t multiplier = 0;
const auto& ct = type.getCanonicalType();
if (ct.kind == slang::ast::SymbolKind::FixedSizeUnpackedArrayType) {
auto [multiplierElem, fixedSizeElem] = getTypeBitWidthImpl(*type.getArrayElementType());
auto rw = ct.as<slang::ast::FixedSizeUnpackedArrayType>().range.width();
return {multiplierElem * rw, fixedSizeElem};
}

SLANG_UNREACHABLE;
}

/// Return the bit width of a slang type, treating unpacked arrays as
/// as if they were packed.
int32_t getTypeBitWidth(slang::ast::Type const& type) {
auto [multiplierElem, fixedSizeElem] = getTypeBitWidthImpl(type);
return (int32_t)(multiplierElem * fixedSizeElem);
}
};

/// An AST visitor to create dependencies between occurrances of variables
Expand All @@ -145,7 +194,7 @@ class AssignmentVisitor : public ast::ASTVisitor<AssignmentVisitor, false, true>
for (auto* rightNode : visitorRHS.getVars()) {
// Add edge from variable declaration to RHS variable reference.
connectDeclToVar(netlist, *rightNode, getSymbolHierPath(rightNode->symbol));
// Add edge form RHS expression term to LHS expression terms.
// Add edge from RHS expression term to LHS expression terms.
connectVarToVar(netlist, *rightNode, *leftNode);
}
}
Expand Down Expand Up @@ -371,7 +420,7 @@ class NetlistVisitor : public ast::ASTVisitor<NetlistVisitor, true, false> {

/// Instance.
void handle(const ast::InstanceSymbol& symbol) {
DEBUG_PRINT(fmt::format("Instance {}\n", symbol.name));
DEBUG_PRINT("Instance {}\n", symbol.name);
if (symbol.name.empty()) {
// An instance without a name has been excluded from the design.
// This can happen when the --top option is used and there is an
Expand Down
Loading