Skip to content

Commit

Permalink
[slang-netlist] Refactor to use ValueDriver::getBounds (#839)
Browse files Browse the repository at this point in the history
  • Loading branch information
jameshanlon authored Oct 28, 2023
1 parent 7493bac commit 6d4445e
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 554 deletions.
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

0 comments on commit 6d4445e

Please sign in to comment.