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
5658namespace {
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+
58194struct 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.
285451struct 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
0 commit comments