Skip to content

Commit d2a978d

Browse files
committed
[SimToSV] Implement sim.proc.print lowering logic
AI-assisted-by: OpenAI ChatGPT
1 parent f5d48db commit d2a978d

File tree

3 files changed

+263
-0
lines changed

3 files changed

+263
-0
lines changed

lib/Conversion/SimToSV/SimToSV.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "mlir/IR/Threading.h"
2626
#include "mlir/Pass/Pass.h"
2727
#include "mlir/Transforms/DialectConversion.h"
28+
#include "mlir/Transforms/RegionUtils.h"
29+
#include "llvm/ADT/SmallPtrSet.h"
2830

2931
#define DEBUG_TYPE "lower-sim-to-sv"
3032

@@ -55,6 +57,140 @@ static std::pair<Value, Value> needsClockAndConditionWrapper(Operation *op) {
5557

5658
namespace {
5759

60+
static void appendLiteralToFWriteFormat(SmallString<128> &formatString,
61+
StringRef literal) {
62+
for (char ch : literal) {
63+
if (ch == '%')
64+
formatString += "%%";
65+
else
66+
formatString.push_back(ch);
67+
}
68+
}
69+
70+
static LogicalResult appendIntegerSpecifier(SmallString<128> &formatString,
71+
bool isLeftAligned,
72+
uint8_t paddingChar,
73+
std::optional<int32_t> width,
74+
char spec) {
75+
formatString.push_back('%');
76+
if (isLeftAligned)
77+
formatString.push_back('-');
78+
79+
// SystemVerilog formatting only has built-in support for '0' and ' '. Keep
80+
// this lowering strict to avoid silently changing formatting semantics.
81+
if (paddingChar == '0') {
82+
formatString.push_back('0');
83+
} else if (paddingChar != ' ') {
84+
return failure();
85+
}
86+
87+
if (width.has_value())
88+
formatString += std::to_string(width.value());
89+
90+
formatString.push_back(spec);
91+
return success();
92+
}
93+
94+
static void appendFloatSpecifier(SmallString<128> &formatString,
95+
bool isLeftAligned,
96+
std::optional<int32_t> fieldWidth,
97+
int32_t fracDigits, char spec) {
98+
formatString.push_back('%');
99+
if (isLeftAligned)
100+
formatString.push_back('-');
101+
if (fieldWidth.has_value())
102+
formatString += std::to_string(fieldWidth.value());
103+
formatString.push_back('.');
104+
formatString += std::to_string(fracDigits);
105+
formatString.push_back(spec);
106+
}
107+
108+
static LogicalResult
109+
foldFormatStringToFWrite(Value input, SmallString<128> &formatString,
110+
SmallVectorImpl<Value> &args,
111+
llvm::SmallPtrSetImpl<Operation *> &visited) {
112+
Operation *op = input.getDefiningOp();
113+
if (!op)
114+
return failure();
115+
116+
if (auto concat = dyn_cast<FormatStringConcatOp>(op)) {
117+
if (!visited.insert(op).second)
118+
return failure();
119+
for (auto operand : concat.getInputs())
120+
if (failed(
121+
foldFormatStringToFWrite(operand, formatString, args, visited)))
122+
return failure();
123+
visited.erase(op);
124+
return success();
125+
}
126+
127+
return TypeSwitch<Operation *, LogicalResult>(op)
128+
.Case<FormatLiteralOp>([&](auto literal) -> LogicalResult {
129+
appendLiteralToFWriteFormat(formatString, literal.getLiteral());
130+
return success();
131+
})
132+
.Case<FormatHierPathOp>([&](auto hierPath) -> LogicalResult {
133+
formatString += hierPath.getUseEscapes() ? "%M" : "%m";
134+
return success();
135+
})
136+
.Case<FormatCharOp>([&](auto fmt) -> LogicalResult {
137+
formatString += "%c";
138+
args.push_back(fmt.getValue());
139+
return success();
140+
})
141+
.Case<FormatDecOp>([&](auto fmt) -> LogicalResult {
142+
if (failed(appendIntegerSpecifier(formatString, fmt.getIsLeftAligned(),
143+
fmt.getPaddingChar(),
144+
fmt.getSpecifierWidth(), 'd')))
145+
return failure();
146+
args.push_back(fmt.getValue());
147+
return success();
148+
})
149+
.Case<FormatHexOp>([&](auto fmt) -> LogicalResult {
150+
if (failed(appendIntegerSpecifier(
151+
formatString, fmt.getIsLeftAligned(), fmt.getPaddingChar(),
152+
fmt.getSpecifierWidth(), fmt.getIsHexUppercase() ? 'X' : 'x')))
153+
return failure();
154+
args.push_back(fmt.getValue());
155+
return success();
156+
})
157+
.Case<FormatOctOp>([&](auto fmt) -> LogicalResult {
158+
if (failed(appendIntegerSpecifier(formatString, fmt.getIsLeftAligned(),
159+
fmt.getPaddingChar(),
160+
fmt.getSpecifierWidth(), 'o')))
161+
return failure();
162+
args.push_back(fmt.getValue());
163+
return success();
164+
})
165+
.Case<FormatBinOp>([&](auto fmt) -> LogicalResult {
166+
if (failed(appendIntegerSpecifier(formatString, fmt.getIsLeftAligned(),
167+
fmt.getPaddingChar(),
168+
fmt.getSpecifierWidth(), 'b')))
169+
return failure();
170+
args.push_back(fmt.getValue());
171+
return success();
172+
})
173+
.Case<FormatScientificOp>([&](auto fmt) -> LogicalResult {
174+
appendFloatSpecifier(formatString, fmt.getIsLeftAligned(),
175+
fmt.getFieldWidth(), fmt.getFracDigits(), 'e');
176+
args.push_back(fmt.getValue());
177+
return success();
178+
})
179+
.Case<FormatFloatOp>([&](auto fmt) -> LogicalResult {
180+
appendFloatSpecifier(formatString, fmt.getIsLeftAligned(),
181+
fmt.getFieldWidth(), fmt.getFracDigits(), 'f');
182+
args.push_back(fmt.getValue());
183+
return success();
184+
})
185+
.Case<FormatGeneralOp>([&](auto fmt) -> LogicalResult {
186+
appendFloatSpecifier(formatString, fmt.getIsLeftAligned(),
187+
fmt.getFieldWidth(), fmt.getFracDigits(), 'g');
188+
args.push_back(fmt.getValue());
189+
return success();
190+
})
191+
.Default([](auto) { return failure(); });
192+
}
193+
58194
struct SimConversionState {
59195
hw::HWModuleOp module;
60196
bool usedSynthesisMacro = false;
@@ -281,6 +417,36 @@ class DPICallLowering : public SimConversionPattern<DPICallOp> {
281417
}
282418
};
283419

420+
namespace {
421+
class PrintFormattedProcLowering
422+
: public SimConversionPattern<PrintFormattedProcOp> {
423+
public:
424+
using SimConversionPattern<PrintFormattedProcOp>::SimConversionPattern;
425+
426+
LogicalResult
427+
matchAndRewrite(PrintFormattedProcOp op, OpAdaptor adaptor,
428+
ConversionPatternRewriter &rewriter) const final {
429+
SmallString<128> formatString;
430+
SmallVector<Value> args;
431+
llvm::SmallPtrSet<Operation *, 8> visited;
432+
if (failed(foldFormatStringToFWrite(op.getInput(), formatString, args,
433+
visited)))
434+
return rewriter.notifyMatchFailure(
435+
op, "unsupported format fragment or integer padding char; only "
436+
"space and '0' padding are supported");
437+
438+
// Align with FIRRTLToHW: default to writing to stderr.
439+
// Specifying an output stream is not currently supported.
440+
auto fd =
441+
hw::ConstantOp::create(rewriter, op.getLoc(), APInt(32, 0x80000002));
442+
sv::FWriteOp::create(rewriter, op.getLoc(), fd, formatString, args);
443+
444+
rewriter.eraseOp(op);
445+
return success();
446+
}
447+
};
448+
}; // namespace
449+
284450
// A helper struct to lower DPI function/call.
285451
struct LowerDPIFunc {
286452
llvm::DenseMap<StringAttr, StringAttr> symbolToFragment;
@@ -483,6 +649,10 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase<SimToSVPass> {
483649
SimConversionState state;
484650
ConversionTarget target(*context);
485651
target.addIllegalDialect<SimDialect>();
652+
target.addLegalOp<FormatLiteralOp, FormatHierPathOp, FormatCharOp,
653+
FormatDecOp, FormatHexOp, FormatOctOp, FormatBinOp,
654+
FormatScientificOp, FormatFloatOp, FormatGeneralOp,
655+
FormatStringConcatOp>();
486656
target.addLegalDialect<sv::SVDialect>();
487657
target.addLegalDialect<hw::HWDialect>();
488658
target.addLegalDialect<seq::SeqDialect>();
@@ -496,11 +666,15 @@ struct SimToSVPass : public circt::impl::LowerSimToSVBase<SimToSVPass> {
496666
patterns.add<TerminateOp>(convert);
497667
patterns.add<PauseOp>(convert);
498668
patterns.add<DPICallLowering>(context, state);
669+
patterns.add<PrintFormattedProcLowering>(context, state);
499670
auto result = applyPartialConversion(module, target, std::move(patterns));
500671

501672
if (failed(result))
502673
return result;
503674

675+
mlir::IRRewriter rewriter(module);
676+
(void)mlir::runRegionDCE(rewriter, module->getRegions());
677+
504678
// Set the emit fragment.
505679
lowerDPIFunc.addFragments(module, state.dpiCallees.takeVector());
506680

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: circt-opt --lower-sim-to-sv --split-input-file --verify-diagnostics %s
2+
3+
hw.module @unsupported_padding_char(in %arg : i8) {
4+
sv.initial {
5+
%lit = sim.fmt.literal "bad="
6+
%bad = sim.fmt.dec %arg paddingChar 42 specifierWidth 2 : i8
7+
%msg = sim.fmt.concat (%lit, %bad)
8+
// expected-error @below {{failed to legalize operation 'sim.proc.print'}}
9+
sim.proc.print %msg
10+
}
11+
}
12+
13+
// -----
14+
15+
hw.module @unsupported_input_block_argument(in %arg : !sim.fstring) {
16+
sv.initial {
17+
// expected-error @below {{failed to legalize operation 'sim.proc.print'}}
18+
sim.proc.print %arg
19+
}
20+
}

test/Conversion/SimToSV/print.mlir

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// RUN: circt-opt --lower-sim-to-sv %s | FileCheck %s
2+
3+
// CHECK-LABEL: hw.module @proc_print
4+
hw.module @proc_print(in %arg: i8) {
5+
// CHECK: sv.initial {
6+
sv.initial {
7+
%l0 = sim.fmt.literal "err: "
8+
%f0 = sim.fmt.hex %arg, isUpper true specifierWidth 2 : i8
9+
%l1 = sim.fmt.literal " 100%"
10+
%msg = sim.fmt.concat (%l0, %f0, %l1)
11+
12+
// CHECK-NEXT: %[[FD:.+]] = hw.constant -2147483646 : i32
13+
// CHECK-NEXT: sv.fwrite %[[FD]], "err: %02X 100%%"(%arg) : i8
14+
// CHECK-NOT: sim.fmt.
15+
sim.proc.print %msg
16+
}
17+
}
18+
19+
// CHECK-LABEL: hw.module @all_format_fragments
20+
hw.module @all_format_fragments(in %ival : i16, in %ch : i8, in %fval : f64) {
21+
sv.initial {
22+
%i0 = sim.fmt.literal "dec="
23+
%f0 = sim.fmt.dec %ival specifierWidth 6 signed : i16
24+
%i1 = sim.fmt.literal " hex="
25+
%f1 = sim.fmt.hex %ival, isUpper true paddingChar 48 specifierWidth 4 : i16
26+
%i2 = sim.fmt.literal " oct="
27+
%f2 = sim.fmt.oct %ival isLeftAligned true specifierWidth 6 : i16
28+
%i3 = sim.fmt.literal " bin="
29+
%f3 = sim.fmt.bin %ival paddingChar 32 specifierWidth 8 : i16
30+
%i4 = sim.fmt.literal " char="
31+
%f4 = sim.fmt.char %ch : i8
32+
%i5 = sim.fmt.literal " exp="
33+
%f5 = sim.fmt.exp %fval fieldWidth 10 fracDigits 3 : f64
34+
%i6 = sim.fmt.literal " flt="
35+
%f6 = sim.fmt.flt %fval isLeftAligned true fieldWidth 8 fracDigits 2 : f64
36+
%i7 = sim.fmt.literal " gen="
37+
%f7 = sim.fmt.gen %fval fracDigits 4 : f64
38+
%i8 = sim.fmt.literal " path="
39+
%f8 = sim.fmt.hier_path
40+
%i9 = sim.fmt.literal " esc="
41+
%f9 = sim.fmt.hier_path escaped
42+
%i10 = sim.fmt.literal " pct=%"
43+
%msg = sim.fmt.concat (%i0, %f0, %i1, %f1, %i2, %f2, %i3, %f3, %i4, %f4, %i5, %f5, %i6, %f6, %i7, %f7, %i8, %f8, %i9, %f9, %i10)
44+
45+
// CHECK: %[[FD:.+]] = hw.constant -2147483646 : i32
46+
// CHECK-NEXT: sv.fwrite %[[FD]], "dec=%6d hex=%04X oct=%-06o bin=%8b char=%c exp=%10.3e flt=%-8.2f gen=%.4g path=%m esc=%M pct=%%"(%ival, %ival, %ival, %ival, %ch, %fval, %fval, %fval) : i16, i16, i16, i16, i8, f64, f64, f64
47+
// CHECK-NOT: sim.fmt.
48+
sim.proc.print %msg
49+
}
50+
}
51+
52+
// CHECK-LABEL: hw.module @nested_concat_order
53+
hw.module @nested_concat_order(in %lhs : i8, in %rhs : i8) {
54+
sv.initial {
55+
%l0 = sim.fmt.literal "L="
56+
%l1 = sim.fmt.literal ", R="
57+
%d0 = sim.fmt.dec %lhs specifierWidth 3 : i8
58+
%h0 = sim.fmt.hex %rhs, isUpper false specifierWidth 2 : i8
59+
60+
%c0 = sim.fmt.concat (%l0, %d0)
61+
%c1 = sim.fmt.concat (%c0, %l1)
62+
%c2 = sim.fmt.concat (%c1, %h0)
63+
64+
// CHECK: %[[FD:.+]] = hw.constant -2147483646 : i32
65+
// CHECK-NEXT: sv.fwrite %[[FD]], "L=%3d, R=%02x"(%lhs, %rhs) : i8, i8
66+
// CHECK-NOT: sim.fmt.
67+
sim.proc.print %c2
68+
}
69+
}

0 commit comments

Comments
 (0)