Skip to content

Commit 25b6e03

Browse files
authored
[FIRRTL] Add {{HierarchicalModuleName}} support (#8380)
Add support for the FIRRTL 5.0.0 printf substitution `{{HierarchicalModuleName}}`. Support this in the FIRRTL Dialect with a new operation. Add FIRRTL parsing, FIRRTL emission, and FIRRTL-to-HW lowering. Signed-off-by: Schuyler Eldridge <[email protected]>
1 parent 296a738 commit 25b6e03

File tree

9 files changed

+98
-31
lines changed

9 files changed

+98
-31
lines changed

include/circt/Dialect/FIRRTL/FIRRTLExpressions.td

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,4 +1705,27 @@ def TimeOp : FIRRTLOp<"fstring.time", [HasCustomSSAName, Pure]> {
17051705

17061706
}
17071707

1708+
def HierarchicalModuleNameOp :
1709+
FIRRTLOp<"fstring.hierarchicalmodulename", [HasCustomSSAName, Pure]> {
1710+
1711+
let summary = "an operation that represents the module name with its full path";
1712+
let description = [{
1713+
1714+
This operation represents the name of the current module with a
1715+
fully-qualified path in front of it. It returns a format string type which
1716+
can only be used as a substitution inside a printf. This operation is not
1717+
represented in the FIRRTL spec.
1718+
1719+
This operation is the FIRRTL Dialect representation of the special
1720+
substitution `{{HierarchicalModuleName}}`.
1721+
}];
1722+
1723+
let arguments = (ins);
1724+
let results = (outs FStringType:$result);
1725+
let assemblyFormat = [{
1726+
attr-dict `:` type($result)
1727+
}];
1728+
1729+
}
1730+
17081731
#endif // CIRCT_DIALECT_FIRRTL_FIRRTLEXPRESSIONS_TD

include/circt/Dialect/FIRRTL/FIRRTLVisitors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class ExprVisitor {
6868
DoubleConstantOp, ListCreateOp, ListConcatOp, UnresolvedPathOp,
6969
PathOp, IntegerAddOp, IntegerMulOp, IntegerShrOp,
7070
// Format String expressions
71-
TimeOp>([&](auto expr) -> ResultType {
71+
TimeOp, HierarchicalModuleNameOp>([&](auto expr) -> ResultType {
7272
return thisCast->visitExpr(expr, args...);
7373
})
7474
.Default([&](auto expr) -> ResultType {
@@ -229,6 +229,7 @@ class ExprVisitor {
229229

230230
// Format string expressions
231231
HANDLE(TimeOp, Unhandled);
232+
HANDLE(HierarchicalModuleNameOp, Unhandled);
232233
#undef HANDLE
233234
};
234235

lib/Conversion/FIRRTLToHW/LowerToHW.cpp

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
15201520
Value getLoweredNonClockValue(Value value);
15211521
Value getLoweredAndExtendedValue(Value value, Type destType);
15221522
Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1523-
Value getLoweredFmtOperand(Value operand);
1523+
std::optional<Value> getLoweredFmtOperand(Value operand);
15241524
LogicalResult setLowering(Value orig, Value result);
15251525
LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
15261526
template <typename ResultOpType, typename... CtorArgTypes>
@@ -1771,6 +1771,7 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
17711771

17721772
// Format String Operations
17731773
LogicalResult visitExpr(TimeOp op);
1774+
LogicalResult visitExpr(HierarchicalModuleNameOp op);
17741775

17751776
// Statements
17761777
LogicalResult lowerVerificationStatement(
@@ -2442,20 +2443,29 @@ Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
24422443
}
24432444

24442445
/// Return a lowered version of 'operand' suitable for use with substitution /
2445-
/// format strings. Zero bit operands are rewritten as one bit zeros and signed
2446-
/// integers are wrapped in $signed().
2447-
Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2446+
/// format strings. There are three possible results:
2447+
///
2448+
/// 1. Does not contain a value if no lowering is set. This is an error.
2449+
/// 2. The lowering contains an empty value. This means that the operand
2450+
/// should be dropped.
2451+
/// 3. The lowering contains a value. This means the operand should be used.
2452+
///
2453+
/// Zero bit operands are rewritten as one bit zeros and signed integers are
2454+
/// wrapped in $signed().
2455+
std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
24482456
// Handle special substitutions.
24492457
if (type_isa<FStringType>(operand.getType())) {
24502458
if (isa<TimeOp>(operand.getDefiningOp()))
24512459
return builder.create<sv::TimeOp>();
2460+
if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2461+
return {nullptr};
24522462
}
24532463

24542464
auto loweredValue = getLoweredValue(operand);
24552465
if (!loweredValue) {
24562466
// If this is a zero bit operand, just pass a one bit zero.
24572467
if (!isZeroBitFIRRTLType(operand.getType()))
2458-
return nullptr;
2468+
return {};
24592469
loweredValue = getOrCreateIntConstant(1, 0);
24602470
}
24612471

@@ -4331,10 +4341,12 @@ LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
43314341
return setLoweringTo<seq::ToClockOp>(op, readXmr);
43324342
}
43334343

4334-
// Do nothing when lowering this operation. We need to handle this at its usage
4335-
// sites to guarantee creation of `sv::TimeOp` that is colocated with the
4336-
// `sv::FWriteOp`.
4344+
// Do nothing when lowering fstring operations. These need to be handled at
4345+
// their usage sites (at the PrintfOps).
43374346
LogicalResult FIRRTLLowering::visitExpr(TimeOp op) { return success(); }
4347+
LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4348+
return success();
4349+
}
43384350

43394351
//===----------------------------------------------------------------------===//
43404352
// Statements
@@ -4594,14 +4606,25 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
45944606
auto substitution = op.getSubstitutions()[subIdx++];
45954607
assert(type_isa<FStringType>(substitution.getType()) &&
45964608
"the operand for a '{{}}' substitution must be an 'fstring' type");
4597-
if (auto timeOp = dyn_cast<TimeOp>(substitution.getDefiningOp())) {
4598-
formatString.append("%0t");
4599-
} else {
4600-
op.emitError("has a substitution with an unimplemented lowering")
4601-
.attachNote(substitution.getLoc())
4602-
<< "op with an unimplemented lowering is here";
4609+
auto result =
4610+
TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
4611+
.Case<TimeOp>([&](auto) {
4612+
formatString.append("%0t");
4613+
return success();
4614+
})
4615+
.Case<HierarchicalModuleNameOp>([&](auto) {
4616+
formatString.append("%m");
4617+
return success();
4618+
})
4619+
.Default([&](auto) {
4620+
op.emitError("has a substitution with an unimplemented "
4621+
"lowering")
4622+
.attachNote(substitution.getLoc())
4623+
<< "op with an unimplemented lowering is here";
4624+
return failure();
4625+
});
4626+
if (failed(result))
46034627
return failure();
4604-
}
46054628
i += 3;
46064629
break;
46074630
}
@@ -4633,10 +4656,11 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
46334656
// Lower the operands handling any special substitutions that need to be
46344657
// lowered on a per-use basis.
46354658
for (auto operand : op.getSubstitutions()) {
4636-
Value loweredValue = getLoweredFmtOperand(operand);
4659+
std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
46374660
if (!loweredValue)
46384661
failed = true;
4639-
operands.push_back(loweredValue);
4662+
if (*loweredValue)
4663+
operands.push_back(*loweredValue);
46404664
}
46414665
if (failed)
46424666
return;
@@ -4780,13 +4804,15 @@ LogicalResult FIRRTLLowering::lowerVerificationStatement(
47804804
if (!loweredValue)
47814805
return failure();
47824806

4807+
if (!*loweredValue)
4808+
break;
47834809
// For SVA assert/assume statements, wrap any message ops in $sampled() to
47844810
// guarantee that these will print with the same value as when the
47854811
// assertion triggers. (See SystemVerilog 2017 spec section 16.9.3 for
47864812
// more information.)
47874813
if (flavor == VerificationFlavor::SVA)
4788-
loweredValue = builder.create<sv::SampledOp>(loweredValue);
4789-
messageOps.push_back(loweredValue);
4814+
loweredValue = builder.create<sv::SampledOp>(*loweredValue);
4815+
messageOps.push_back(*loweredValue);
47904816
}
47914817
}
47924818

lib/Dialect/FIRRTL/Export/FIREmitter.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -897,10 +897,15 @@ void Emitter::emitStatement(PrintFOp op) {
897897
case '{':
898898
if (origFormatString.slice(i, i + 4) == "{{}}") {
899899
formatString.append("{{");
900-
if (isa<TimeOp>(op.getSubstitutions()[opIdx++].getDefiningOp()))
901-
formatString.append("SimulationTime");
902-
else
903-
emitError(op, "unsupported fstring substitution type");
900+
TypeSwitch<Operation *>(
901+
op.getSubstitutions()[opIdx++].getDefiningOp())
902+
.Case<TimeOp>(
903+
[&](auto) { formatString.append("SimulationTime"); })
904+
.Case<HierarchicalModuleNameOp>(
905+
[&](auto) { formatString.append("HierarchicalModuleName"); })
906+
.Default([&](auto) {
907+
emitError(op, "unsupported fstring substitution type");
908+
});
904909
formatString.append("}}");
905910
}
906911
i += 3;

lib/Dialect/FIRRTL/FIRRTLOps.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6378,6 +6378,11 @@ void TimeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
63786378
setNameFn(getResult(), "time");
63796379
}
63806380

6381+
void HierarchicalModuleNameOp::getAsmResultNames(
6382+
OpAsmSetValueNameFn setNameFn) {
6383+
setNameFn(getResult(), "hierarchicalmodulename");
6384+
}
6385+
63816386
//===----------------------------------------------------------------------===//
63826387
// TblGen Generated Logic.
63836388
//===----------------------------------------------------------------------===//

lib/Dialect/FIRRTL/Import/FIRParser.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3038,6 +3038,8 @@ ParseResult FIRStmtParser::parsePrintf() {
30383038
auto specialString = formatString.slice(start, i);
30393039
if (specialString == "SimulationTime") {
30403040
operands.push_back(builder.create<TimeOp>());
3041+
} else if (specialString == "HierarchicalModuleName") {
3042+
operands.push_back(builder.create<HierarchicalModuleNameOp>());
30413043
} else {
30423044
emitError(formatStringLoc)
30433045
<< "unknown printf substitution '" << specialString

test/Conversion/FIRRTLToHW/lower-to-hw.mlir

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ firrtl.circuit "Simple" attributes {annotations = [{class =
385385
// CHECK-NEXT: sv.if [[AND]] {
386386
// CHECK-NEXT: %PRINTF_FD_ = sv.macro.ref.expr @PRINTF_FD_() : () -> i32
387387
// CHECK-NEXT: [[TIME:%.+]] = sv.system.time : i64
388-
// CHECK-NEXT: sv.fwrite %PRINTF_FD_, "[%0t]: %0d"([[TIME]], %a) : i64, i4
388+
// CHECK-NEXT: sv.fwrite %PRINTF_FD_, "[%0t]: %0d %m"([[TIME]], %a) : i64, i4
389389
// CHECK-NEXT: }
390390
// CHECK-NEXT: }
391391
// CHECK-NEXT: }
@@ -406,7 +406,8 @@ firrtl.circuit "Simple" attributes {annotations = [{class =
406406
firrtl.printf %clock, %reset, "Hi signed %d %d\0A"(%1, %d) : !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<5>, !firrtl.sint<4>
407407

408408
%time = firrtl.fstring.time : !firrtl.fstring
409-
firrtl.printf %clock, %reset, "[{{}}]: %d" (%time, %a) : !firrtl.clock, !firrtl.uint<1>, !firrtl.fstring, !firrtl.uint<4>
409+
%hierarchicalmodulename = firrtl.fstring.hierarchicalmodulename : !firrtl.fstring
410+
firrtl.printf %clock, %reset, "[{{}}]: %d {{}}" (%time, %a, %hierarchicalmodulename) : !firrtl.clock, !firrtl.uint<1>, !firrtl.fstring, !firrtl.uint<4>, !firrtl.fstring
410411

411412
firrtl.skip
412413

test/Dialect/FIRRTL/parse-basic.fir

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,8 +2061,8 @@ circuit Foo:
20612061
public module Foo:
20622062
input clock: Clock
20632063
; In FIRRTL versions < 4.2.0, the following is not a "special substitution".
2064-
; CHECK{{LITERAL}}: firrtl.printf %clock, %c1_ui1, "[{{SimulationTime}}]: hello world "
2065-
printf(clock, UInt<1>(1), "[{{SimulationTime}}]: hello world")
2064+
; CHECK{{LITERAL}}: firrtl.printf %clock, %c1_ui1, "[{{SimulationTime}}]: hello from {{HierarchicalModuleName}}"
2065+
printf(clock, UInt<1>(1), "[{{SimulationTime}}]: hello from {{HierarchicalModuleName}}")
20662066

20672067
;// -----
20682068
FIRRTL version 5.0.0
@@ -2071,5 +2071,6 @@ circuit Foo:
20712071
input clock: Clock
20722072
; In FIRRTL versions >= 4.2.0, the following is not a "special substitution".
20732073
; CHECK: %time = firrtl.fstring.time : !firrtl.fstring
2074-
; CHECK{{LITERAL}}: firrtl.printf %clock, %c1_ui1, "[{{}}]: hello world "(%time)
2075-
printf(clock, UInt<1>(1), "[{{SimulationTime}}]: hello world")
2074+
; CHECK: %hierarchicalmodulename = firrtl.fstring.hierarchicalmodulename : !firrtl.fstring
2075+
; CHECK{{LITERAL}}: firrtl.printf %clock, %c1_ui1, "[{{}}]: hello world {{}}"(%time, %hierarchicalmodulename)
2076+
printf(clock, UInt<1>(1), "[{{SimulationTime}}]: hello from {{HierarchicalModuleName}}")

test/firtool/print.fir

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
; RUN: firtool %s | FileCheck %s
22

3-
FIRRTL version 5.1.0
3+
FIRRTL version 5.0.0
44
circuit PrintTest:
55
; CHECK-LABEL: module PrintTest
66
public module PrintTest :
@@ -23,3 +23,6 @@ circuit PrintTest:
2323

2424
; CHECK-NEXT: $fwrite(`PRINTF_FD_, "literals: %%\n");
2525
printf(clock, cond, "literals: %%\n")
26+
27+
; CHECK-NEXT: $fwrite(`PRINTF_FD_, "[%0t]: %m\n", $time);
28+
printf(clock, cond, "[{{SimulationTime}}]: {{HierarchicalModuleName}}\n")

0 commit comments

Comments
 (0)