Skip to content

Commit

Permalink
Support config instance path overrides for user-defined primitive ins…
Browse files Browse the repository at this point in the history
…tantiations
  • Loading branch information
MikePopoloski committed Mar 5, 2024
1 parent 9ccd114 commit 8ced18b
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 24 deletions.
4 changes: 3 additions & 1 deletion include/slang/ast/symbols/InstanceSymbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,10 @@ class SLANG_EXPORT UninstantiatedDefSymbol : public Symbol {

static void fromSyntax(Compilation& compilation,
const syntax::PrimitiveInstantiationSyntax& syntax,
const syntax::HierarchicalInstanceSyntax* specificInstance,
const ASTContext& context, SmallVectorBase<const Symbol*>& results,
SmallVectorBase<const Symbol*>& implicitNets);
SmallVectorBase<const Symbol*>& implicitNets,
SmallSet<std::string_view, 8>& implicitNetNames);

static void fromSyntax(Compilation& compilation,
const syntax::CheckerInstantiationSyntax& syntax,
Expand Down
102 changes: 79 additions & 23 deletions source/ast/symbols/InstanceSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,9 @@ void checkForInvalidNestedConfigNodes(const ASTContext& context,
using ResolvedInstanceRules =
SmallVector<std::pair<Compilation::DefinitionLookupResult, const HierarchicalInstanceSyntax*>>;

template<typename TSyntax>
void resolveInstanceOverrides(const ResolvedConfig& resolvedConfig, const ASTContext& context,
const HierarchyInstantiationSyntax& syntax, std::string_view defName,
const TSyntax& syntax, std::string_view defName, DiagCode missingCode,
ResolvedInstanceRules& results) {
auto& instOverrides = resolvedConfig.useConfig.getInstanceOverrides();
if (instOverrides.empty())
Expand Down Expand Up @@ -359,15 +360,15 @@ void resolveInstanceOverrides(const ResolvedConfig& resolvedConfig, const ASTCon
if (ruleIt != overrideMap.end() && ruleIt->second.rule) {
// We have an override rule, so use it to lookup the def.
auto defResult = comp.getDefinition(defName, *context.scope, *ruleIt->second.rule,
instSyntax->sourceRange(), diag::UnknownModule);
instSyntax->sourceRange(), missingCode);
results.push_back({defResult, instSyntax});
nestedConfig = defResult.configRoot;
}
else {
// No specific config rule, so use the default lookup behavior.
if (!explicitDef) {
explicitDef = comp.getDefinition(defName, *context.scope, syntax.type.range(),
diag::UnknownModule);
missingCode);
}
results.push_back({*explicitDef, instSyntax});
nestedConfig = explicitDef->configRoot;
Expand Down Expand Up @@ -654,7 +655,8 @@ void InstanceSymbol::fromSyntax(Compilation& comp, const HierarchyInstantiationS

if (resolvedConfig) {
ResolvedInstanceRules instanceRules;
resolveInstanceOverrides(*resolvedConfig, context, syntax, defName, instanceRules);
resolveInstanceOverrides(*resolvedConfig, context, syntax, defName, diag::UnknownModule,
instanceRules);
if (!instanceRules.empty()) {
for (auto& [defResult, instSyntax] : instanceRules)
createInstances(defResult, instSyntax);
Expand Down Expand Up @@ -1112,12 +1114,24 @@ void UninstantiatedDefSymbol::fromSyntax(

void UninstantiatedDefSymbol::fromSyntax(Compilation& compilation,
const PrimitiveInstantiationSyntax& syntax,
const syntax::HierarchicalInstanceSyntax* specificInstance,
const ASTContext& parentContext,
SmallVectorBase<const Symbol*>& results,
SmallVectorBase<const Symbol*>& implicitNets) {
SmallVectorBase<const Symbol*>& implicitNets,
SmallSet<std::string_view, 8>& implicitNetNames) {
ASTContext context = parentContext.resetFlags(ASTFlags::NonProcedural);
createUninstantiatedDefs(compilation, syntax, syntax.type.valueText(), context, {}, results,
implicitNets);
auto& netType = context.scope->getDefaultNetType();

if (specificInstance) {
createUninstantiatedDef(compilation, syntax, specificInstance, syntax.type.valueText(),
context, {}, results, implicitNets, implicitNetNames, netType);
}
else {
for (auto instanceSyntax : syntax.instances) {
createUninstantiatedDef(compilation, syntax, instanceSyntax, syntax.type.valueText(),
context, {}, results, implicitNets, implicitNetNames, netType);
}
}
}

void UninstantiatedDefSymbol::fromSyntax(Compilation& compilation,
Expand Down Expand Up @@ -1387,20 +1401,34 @@ void PrimitiveInstanceSymbol::fromSyntax(const PrimitiveInstantiationSyntax& syn
auto& comp = context.getCompilation();
auto name = syntax.type.valueText();

if (syntax.type.kind == TokenKind::Identifier) {
auto def = comp.getDefinition(name, *context.scope, syntax.type.range(),
diag::UnknownPrimitive)
.definition;
// If the type is not an identifier then it should be a gate keyword.
if (syntax.type.kind != TokenKind::Identifier) {
auto prim = comp.getGateType(name);
SLANG_ASSERT(prim);
createPrimitives(*prim, syntax, nullptr, context, results, implicitNets, implicitNetNames);
return;
}

auto createPrims = [&](const Compilation::DefinitionLookupResult& defResult,
const HierarchicalInstanceSyntax* specificInstance) {
if (defResult.configRule)
defResult.configRule->isUsed = true;

auto def = defResult.definition;
if (def) {
if (def->kind == SymbolKind::Primitive) {
createPrimitives(def->as<PrimitiveSymbol>(), syntax, nullptr, context, results,
implicitNets, implicitNetNames);
createPrimitives(def->as<PrimitiveSymbol>(), syntax, specificInstance, context,
results, implicitNets, implicitNetNames);
return;
}

SLANG_ASSERT(syntax.strength || syntax.delay);
if (syntax.strength) {
context.addDiag(diag::InstanceWithStrength, syntax.strength->sourceRange()) << name;
auto& diag = context.addDiag(diag::InstanceWithStrength,
syntax.strength->sourceRange());
diag << def->name;
if (specificInstance)
diag << specificInstance->sourceRange();
}
else if (comp.hasFlag(CompilationFlags::AllowBareValParamAssignment) &&
syntax.delay->kind == SyntaxKind::DelayControl) {
Expand All @@ -1422,24 +1450,52 @@ void PrimitiveInstanceSymbol::fromSyntax(const PrimitiveInstantiationSyntax& syn
parameters.copy(comp),
missing(TokenKind::CloseParenthesis, delayVal.getLastToken().location()));

// Rebuild the instance list. The const_casts are fine because
// we're going to immediately treat them as const again below.
SmallVector<TokenOrSyntax> instanceBuf;
if (specificInstance) {
instanceBuf.push_back(
const_cast<HierarchicalInstanceSyntax*>(specificInstance));
}
else {
for (auto inst : syntax.instances)
instanceBuf.push_back(const_cast<HierarchicalInstanceSyntax*>(inst));
}

auto instantiation = comp.emplace<HierarchyInstantiationSyntax>(
syntax.attributes, syntax.type, pvas, syntax.instances, syntax.semi);
syntax.attributes, syntax.type, pvas, instanceBuf.copy(comp), syntax.semi);
InstanceSymbol::fromSyntax(comp, *instantiation, context, results, implicitNets,
/* isFromBind */ false);
return;
}
else {
context.addDiag(diag::InstanceWithDelay,
syntax.delay->getFirstToken().location() + 1);
auto& diag = context.addDiag(diag::InstanceWithDelay,
syntax.delay->getFirstToken().location() + 1);
if (specificInstance)
diag << specificInstance->sourceRange();
}
}
UninstantiatedDefSymbol::fromSyntax(comp, syntax, context, results, implicitNets);
}
else {
auto prim = comp.getGateType(name);
SLANG_ASSERT(prim);
createPrimitives(*prim, syntax, nullptr, context, results, implicitNets, implicitNetNames);
UninstantiatedDefSymbol::fromSyntax(comp, syntax, specificInstance, context, results,
implicitNets, implicitNetNames);
};

// Check if there are configuration instance override paths to check.
auto parentInst = context.scope->getContainingInstance();
if (parentInst && parentInst->parentInstance && parentInst->parentInstance->resolvedConfig) {
ResolvedInstanceRules instanceRules;
resolveInstanceOverrides(*parentInst->parentInstance->resolvedConfig, context, syntax, name,
diag::UnknownPrimitive, instanceRules);
if (!instanceRules.empty()) {
for (auto& [defResult, instSyntax] : instanceRules)
createPrims(defResult, instSyntax);
return;
}
}

// Simple case; just create the primitive instances.
auto defResult = comp.getDefinition(name, *context.scope, syntax.type.range(),
diag::UnknownPrimitive);
createPrims(defResult, nullptr);
}

std::span<const Expression* const> PrimitiveInstanceSymbol::getPortConnections() const {
Expand Down
53 changes: 53 additions & 0 deletions tests/unittests/ast/ConfigTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1000,3 +1000,56 @@ endmodule
compilation.addSyntaxTree(tree);
NO_COMPILATION_ERRORS;
}

TEST_CASE("Config prim instance with module override") {
auto tree = SyntaxTree::fromText(R"(
config cfg1;
design top;
cell foo use m;
instance top.b2 use m;
endconfig
module m #(parameter int A);
endmodule
module top;
foo (supply0, pull1) b1();
baz #3 b2(), b3();
endmodule
)");
CompilationOptions options;
options.topModules.emplace("cfg1");
options.flags |= CompilationFlags::AllowBareValParamAssignment;

Compilation compilation(options);
compilation.addSyntaxTree(tree);

auto& diags = compilation.getAllDiagnostics();
REQUIRE(diags.size() == 2);
CHECK(diags[0].code == diag::InstanceWithStrength);
CHECK(diags[1].code == diag::UnknownPrimitive);
}

TEST_CASE("Config target multiple primitives with explicit primitive syntax") {
auto tree = SyntaxTree::fromText(R"(
config cfg1;
design top;
instance top.b1 use p1;
instance top.b2 use p1;
endconfig
primitive p1 (output c, input a, b);
table 00:0; endtable
endprimitive
module top;
bar (supply0, pull1) b1(c, a, b), b2(c, a, b);
endmodule
)");
CompilationOptions options;
options.topModules.emplace("cfg1");

Compilation compilation(options);
compilation.addSyntaxTree(tree);
NO_COMPILATION_ERRORS;
}

0 comments on commit 8ced18b

Please sign in to comment.