Skip to content

Commit a24e4f4

Browse files
committed
InferDomains: add infer-public flag, check interfaces of modules for completeness
1 parent 4ceeecc commit a24e4f4

File tree

4 files changed

+138
-18
lines changed

4 files changed

+138
-18
lines changed

include/circt/Dialect/FIRRTL/Passes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,10 @@ def InferDomains : Pass<"firrtl-infer-domains", "firrtl::CircuitOp"> {
920920
E.g., this pass can be used to check for illegal clock-domain-crossings if
921921
clock domains are specified for signals in the design.
922922
}];
923+
let options = [
924+
Option<"inferPublic", "infer-public", "bool", "false",
925+
"Infer domains on public modules.">
926+
];
923927
}
924928

925929
def LowerDomains : Pass<"firrtl-lower-domains", "firrtl::CircuitOp"> {

lib/Dialect/FIRRTL/Transforms/InferDomains.cpp

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,67 @@ void solve(Term *lhs, Term *rhs) {
352352

353353
} // namespace
354354

355+
//====--------------------------------------------------------------------------
356+
// CheckModuleDomains
357+
//====--------------------------------------------------------------------------
358+
359+
/// Check that a module has complete domain information.
360+
static LogicalResult checkModuleDomains(GlobalState &globals,
361+
FModuleLike module) {
362+
auto numDomains = globals.circuitInfo.getNumDomains();
363+
auto domainInfo = module.getDomainInfoAttr();
364+
DenseMap<unsigned, unsigned> typeIDTable;
365+
for (size_t i = 0, e = module.getNumPorts(); i < e; ++i) {
366+
auto type = module.getPortType(i);
367+
368+
if (isa<DomainType>(type)) {
369+
auto typeID = globals.circuitInfo.getDomainTypeID(domainInfo, i);
370+
typeIDTable[i] = typeID;
371+
continue;
372+
}
373+
374+
if (auto baseType = type_dyn_cast<FIRRTLBaseType>(type)) {
375+
SmallVector<IntegerAttr> associations(numDomains);
376+
auto domains = getPortDomainAssociation(domainInfo, i);
377+
for (auto index : domains) {
378+
auto typeID = typeIDTable[index.getUInt()];
379+
auto &entry = associations[typeID];
380+
if (entry && entry != index) {
381+
auto domainName = globals.circuitInfo.getDomain(typeID).getNameAttr();
382+
auto portName = module.getPortNameAttr(i);
383+
auto diag = emitError(module.getPortLocation(i))
384+
<< "ambiguous " << domainName << " association for port "
385+
<< portName;
386+
387+
auto d1Loc = module.getPortLocation(entry.getUInt());
388+
auto d1Name = module.getPortNameAttr(entry.getUInt());
389+
diag.attachNote(d1Loc)
390+
<< "associated with " << domainName << " port " << d1Name;
391+
392+
auto d2Loc = module.getPortLocation(index.getUInt());
393+
auto d2Name = module.getPortNameAttr(index.getUInt());
394+
diag.attachNote(d2Loc)
395+
<< "associated with " << domainName << " port " << d2Name;
396+
}
397+
entry = index;
398+
}
399+
400+
for (size_t typeID = 0; typeID < numDomains; ++typeID) {
401+
auto association = associations[typeID];
402+
if (!association) {
403+
auto domainName = globals.circuitInfo.getDomain(typeID).getNameAttr();
404+
auto portName = module.getPortNameAttr(i);
405+
return emitError(module.getPortLocation(i))
406+
<< "missing " << domainName << " association for port "
407+
<< portName;
408+
}
409+
}
410+
}
411+
}
412+
413+
return success();
414+
}
415+
355416
//====--------------------------------------------------------------------------
356417
// InferModuleDomains: Primary workhorse for inferring domains on modules.
357418
//====--------------------------------------------------------------------------
@@ -525,7 +586,7 @@ LogicalResult InferModuleDomains::operator()(FModuleOp module) {
525586
}
526587

527588
LogicalResult InferModuleDomains::processPorts(FModuleOp module) {
528-
auto portDomainInfo = module.getDomainInfoAttr();
589+
auto domainInfo = module.getDomainInfoAttr();
529590
auto numPorts = module.getNumPorts();
530591

531592
// Process module ports - domain ports define explicit domains.
@@ -535,7 +596,7 @@ LogicalResult InferModuleDomains::processPorts(FModuleOp module) {
535596

536597
// This is a domain port.
537598
if (isa<DomainType>(port.getType())) {
538-
auto typeID = globals.circuitInfo.getDomainTypeID(portDomainInfo, i);
599+
auto typeID = globals.circuitInfo.getDomainTypeID(domainInfo, i);
539600
domainTypeIDTable[i] = typeID;
540601
if (module.getPortDirection(i) == Direction::In) {
541602
setTermForDomain(port, allocate<ValueTerm>(port));
@@ -544,7 +605,7 @@ LogicalResult InferModuleDomains::processPorts(FModuleOp module) {
544605
}
545606

546607
// This is a port, which may have explicit domain information.
547-
auto portDomains = getPortDomainAssociation(portDomainInfo, i);
608+
auto portDomains = getPortDomainAssociation(domainInfo, i);
548609
if (portDomains.empty())
549610
continue;
550611

@@ -1305,13 +1366,34 @@ void InferModuleDomains::emitDomainPortInferenceError(T op, size_t i) const {
13051366
}
13061367
}
13071368

1369+
static LogicalResult inferModuleDomains(GlobalState &globals,
1370+
FModuleOp module) {
1371+
return InferModuleDomains::run(globals, module);
1372+
}
1373+
13081374
//===---------------------------------------------------------------------------
13091375
// InferDomainsPass: Top-level pass implementation.
13101376
//===---------------------------------------------------------------------------
13111377

1378+
static LogicalResult runOnModuleLike(bool inferPublic, GlobalState &globals,
1379+
Operation *op) {
1380+
if (auto module = dyn_cast<FModuleOp>(op)) {
1381+
if (module.isPublic() && !inferPublic)
1382+
return checkModuleDomains(globals, module);
1383+
return inferModuleDomains(globals, module);
1384+
}
1385+
1386+
if (auto extModule = dyn_cast<FExtModuleOp>(op)) {
1387+
return checkModuleDomains(globals, extModule);
1388+
}
1389+
1390+
return success();
1391+
}
1392+
13121393
namespace {
13131394
struct InferDomainsPass
13141395
: public circt::firrtl::impl::InferDomainsBase<InferDomainsPass> {
1396+
using InferDomainsBase::InferDomainsBase;
13151397
void runOnOperation() override;
13161398
};
13171399
} // namespace
@@ -1320,16 +1402,11 @@ void InferDomainsPass::runOnOperation() {
13201402
LLVM_DEBUG(debugPassHeader(this) << "\n");
13211403
auto circuit = getOperation();
13221404
auto &instanceGraph = getAnalysis<InstanceGraph>();
1323-
13241405
GlobalState globals(circuit);
13251406
DenseSet<InstanceGraphNode *> visited;
13261407
for (auto *root : instanceGraph) {
13271408
for (auto *node : llvm::post_order_ext(root, visited)) {
1328-
auto module = dyn_cast<FModuleOp>(node->getOperation());
1329-
if (!module)
1330-
continue;
1331-
1332-
if (failed(InferModuleDomains::run(globals, module))) {
1409+
if (failed(runOnModuleLike(inferPublic, globals, node->getModule()))) {
13331410
signalPassFailure();
13341411
return;
13351412
}

test/Dialect/FIRRTL/infer-domains-errors.mlir

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-domains))' %s --verify-diagnostics --split-input-file
1+
// RUN: circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-domains{infer-public=true}))' %s --verify-diagnostics --split-input-file
22

33
// Port annotated with same domain type twice.
44
firrtl.circuit "DomainCrossOnPort" {
@@ -86,16 +86,28 @@ firrtl.circuit "UnableToInferDomainOfPortDrivenByConstantExpr" {
8686

8787
// Incomplete extmodule domain information.
8888

89-
firrtl.circuit "IncompleteDomainInfoForExtModule" {
89+
firrtl.circuit "Top" {
9090
firrtl.domain @ClockDomain
9191

92-
firrtl.extmodule @Foo(in i: !firrtl.uint<1>)
92+
// expected-error @below {{missing "ClockDomain" association for port "i"}}
93+
firrtl.extmodule @Top(in i: !firrtl.uint<1>)
94+
}
9395

94-
firrtl.module @IncompleteDomainInfoForExtModule(in %i: !firrtl.uint<1>) {
95-
// expected-error @below {{'firrtl.instance' op missing "ClockDomain" association for port "i"}}
96-
%foo_i = firrtl.instance foo @Foo(in i: !firrtl.uint<1>)
97-
firrtl.matchingconnect %foo_i, %i : !firrtl.uint<1>
98-
}
96+
// -----
97+
98+
// Conflicting extmodule domain information.
99+
100+
firrtl.circuit "Top" {
101+
firrtl.domain @ClockDomain
102+
103+
firrtl.extmodule @Top(
104+
// expected-note @below {{associated with "ClockDomain" port "D1"}}
105+
in D1 : !firrtl.domain of @ClockDomain,
106+
// expected-note @below {{associated with "ClockDomain" port "D2"}}
107+
in D2 : !firrtl.domain of @ClockDomain,
108+
// expected-error @below {{ambiguous "ClockDomain" association for port "i"}}
109+
in i: !firrtl.uint<1> domains [D1, D2]
110+
)
99111
}
100112

101113
// -----

test/Dialect/FIRRTL/infer-domains.mlir

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-domains))' %s | FileCheck %s
1+
// RUN: circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-domains{infer-public=true}))' %s | FileCheck %s
22

33
// Legal domain usage - no crossing.
44
firrtl.circuit "LegalDomains" {
@@ -248,3 +248,30 @@ firrtl.circuit "ConstantInMultipleDomains" {
248248
firrtl.matchingconnect %y_i, %c0_ui1 : !firrtl.uint<1>
249249
}
250250
}
251+
252+
firrtl.circuit "Top" {
253+
firrtl.domain @ClockDomain
254+
firrtl.extmodule @Foo(
255+
in ClockDomain : !firrtl.domain of @ClockDomain,
256+
in i: !firrtl.uint<1> domains [ClockDomain],
257+
out o : !firrtl.uint<1> domains [ClockDomain]
258+
)
259+
260+
firrtl.module @Top(in %ClockDomain : !firrtl.domain of @ClockDomain ) {
261+
%foo1_ClockDomain, %foo1_i, %foo1_o = firrtl.instance foo1 @Foo(
262+
in ClockDomain : !firrtl.domain of @ClockDomain,
263+
in i: !firrtl.uint<1> domains [ClockDomain],
264+
out o : !firrtl.uint<1> domains [ClockDomain]
265+
)
266+
267+
%foo2_ClockDomain, %foo2_i, %foo2_o = firrtl.instance foo2 @Foo(
268+
in ClockDomain : !firrtl.domain of @ClockDomain,
269+
in i: !firrtl.uint<1> domains [ClockDomain],
270+
out o : !firrtl.uint<1> domains [ClockDomain]
271+
)
272+
273+
firrtl.domain.define %foo1_ClockDomain, %ClockDomain
274+
firrtl.matchingconnect %foo2_i, %foo1_o : !firrtl.uint<1>
275+
firrtl.matchingconnect %foo1_i, %foo2_o : !firrtl.uint<1>
276+
}
277+
}

0 commit comments

Comments
 (0)