CIRCT 23.0.0git
Loading...
Searching...
No Matches
ExportVerilog.cpp
Go to the documentation of this file.
1//===- ExportVerilog.cpp - Verilog Emitter --------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This is the main Verilog emitter implementation.
10//
11// CAREFUL: This file covers the emission phase of `ExportVerilog` which mainly
12// walks the IR and produces output. Do NOT modify the IR during this walk, as
13// emission occurs in a highly parallel fashion. If you need to modify the IR,
14// do so during the preparation phase which lives in `PrepareForEmission.cpp`.
15//
16//===----------------------------------------------------------------------===//
17
35#include "circt/Support/LLVM.h"
37#include "circt/Support/Path.h"
42#include "mlir/IR/BuiltinOps.h"
43#include "mlir/IR/ImplicitLocOpBuilder.h"
44#include "mlir/IR/Location.h"
45#include "mlir/IR/Threading.h"
46#include "mlir/Interfaces/FunctionImplementation.h"
47#include "mlir/Pass/PassManager.h"
48#include "mlir/Support/FileUtilities.h"
49#include "llvm/ADT/MapVector.h"
50#include "llvm/ADT/STLExtras.h"
51#include "llvm/ADT/StringSet.h"
52#include "llvm/ADT/TypeSwitch.h"
53#include "llvm/Support/FileSystem.h"
54#include "llvm/Support/FormattedStream.h"
55#include "llvm/Support/Path.h"
56#include "llvm/Support/SaveAndRestore.h"
57#include "llvm/Support/ToolOutputFile.h"
58#include "llvm/Support/raw_ostream.h"
59
60namespace circt {
61#define GEN_PASS_DEF_EXPORTSPLITVERILOG
62#define GEN_PASS_DEF_EXPORTVERILOG
63#include "circt/Conversion/Passes.h.inc"
64} // namespace circt
65
66using namespace circt;
67using namespace comb;
68using namespace hw;
69using namespace sv;
70using namespace ExportVerilog;
71
72using namespace pretty;
73
74#define DEBUG_TYPE "export-verilog"
75
76StringRef circtHeader = "circt_header.svh";
77StringRef circtHeaderInclude = "`include \"circt_header.svh\"\n";
78
79namespace {
80/// This enum keeps track of the precedence level of various binary operators,
81/// where a lower number binds tighter.
82enum VerilogPrecedence {
83 // Normal precedence levels.
84 Symbol, // Atomic symbol like "foo" and {a,b}
85 Selection, // () , [] , :: , ., $signed()
86 Unary, // Unary operators like ~foo
87 Multiply, // * , / , %
88 Addition, // + , -
89 Shift, // << , >>, <<<, >>>
90 Comparison, // > , >= , < , <=
91 Equality, // == , !=
92 And, // &
93 Xor, // ^ , ^~
94 Or, // |
95 AndShortCircuit, // &&
96 Conditional, // ? :
97
98 LowestPrecedence, // Sentinel which is always the lowest precedence.
99};
100
101/// This enum keeps track of whether the emitted subexpression is signed or
102/// unsigned as seen from the Verilog language perspective.
103enum SubExprSignResult { IsSigned, IsUnsigned };
104
105/// This is information precomputed about each subexpression in the tree we
106/// are emitting as a unit.
107struct SubExprInfo {
108 /// The precedence of this expression.
109 VerilogPrecedence precedence;
110
111 /// The signedness of the expression.
112 SubExprSignResult signedness;
113
114 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
115 : precedence(precedence), signedness(signedness) {}
116};
117
118} // end anonymous namespace
119
120//===----------------------------------------------------------------------===//
121// Helper routines
122//===----------------------------------------------------------------------===//
123
124static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value) {
125 return Builder(ctx).getI32IntegerAttr(value);
126}
127
128static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value) {
129 return Builder(ctx).getIntegerAttr(t, value);
130}
131
132/// Return true for nullary operations that are better emitted multiple
133/// times as inline expression (when they have multiple uses) rather than having
134/// a temporary wire.
135///
136/// This can only handle nullary expressions, because we don't want to replicate
137/// subtrees arbitrarily.
138static bool isDuplicatableNullaryExpression(Operation *op) {
139 // We don't want wires that are just constants aesthetically.
140 if (isConstantExpression(op))
141 return true;
142
143 // If this is a small verbatim expression with no side effects, duplicate it
144 // inline.
145 if (isa<VerbatimExprOp>(op)) {
146 if (op->getNumOperands() == 0 &&
147 op->getAttrOfType<StringAttr>("format_string").getValue().size() <= 32)
148 return true;
149 }
150
151 // Always duplicate XMRs into their use site.
152 if (isa<XMRRefOp>(op))
153 return true;
154
155 // If this is a macro reference without side effects, allow duplication.
156 if (isa<MacroRefExprOp>(op))
157 return true;
158
159 return false;
160}
161
162// Return true if the expression can be inlined even when the op has multiple
163// uses. Be careful to add operations here since it might cause exponential
164// emission without proper restrictions.
165static bool isDuplicatableExpression(Operation *op) {
166 if (op->getNumOperands() == 0)
168
169 // It is cheap to inline extract op.
170 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
171 return true;
172
173 // We only inline array_get with a constant, port or wire index.
174 if (auto array = dyn_cast<hw::ArrayGetOp>(op)) {
175 auto *indexOp = array.getIndex().getDefiningOp();
176 if (!indexOp || isa<ConstantOp>(indexOp))
177 return true;
178 if (auto read = dyn_cast<ReadInOutOp>(indexOp)) {
179 auto *readSrc = read.getInput().getDefiningOp();
180 // A port or wire is ok to duplicate reads.
181 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
182 }
183
184 return false;
185 }
186
187 return false;
188}
189
190/// Return the verilog name of the operations that can define a symbol.
191/// Legalized names are added to "hw.verilogName" so look up it when the
192/// attribute already exists.
193StringRef ExportVerilog::getSymOpName(Operation *symOp) {
194 // Typeswitch of operation types which can define a symbol.
195 // If legalizeNames has renamed it, then the attribute must be set.
196 if (auto attr = symOp->getAttrOfType<StringAttr>("hw.verilogName"))
197 return attr.getValue();
198 return TypeSwitch<Operation *, StringRef>(symOp)
199 .Case<HWModuleOp, HWModuleExternOp, HWModuleGeneratedOp,
200 sv::SVVerbatimModuleOp, FuncOp>(
201 [](Operation *op) { return getVerilogModuleName(op); })
202 .Case<SVVerbatimSourceOp>([](SVVerbatimSourceOp op) {
203 return op.getVerilogNameAttr().getValue();
204 })
205 .Case<InterfaceOp>([&](InterfaceOp op) {
206 return getVerilogModuleNameAttr(op).getValue();
207 })
208 .Case<InterfaceSignalOp>(
209 [&](InterfaceSignalOp op) { return op.getSymName(); })
210 .Case<InterfaceModportOp>(
211 [&](InterfaceModportOp op) { return op.getSymName(); })
212 .Default([&](Operation *op) {
213 if (auto attr = op->getAttrOfType<StringAttr>("name"))
214 return attr.getValue();
215 if (auto attr = op->getAttrOfType<StringAttr>("instanceName"))
216 return attr.getValue();
217 if (auto attr = op->getAttrOfType<StringAttr>("sv.namehint"))
218 return attr.getValue();
219 if (auto attr =
220 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
221 return attr.getValue();
222 return StringRef("");
223 });
224}
225
226/// Emits a known-safe token that is legal when indexing into singleton arrays.
227template <typename PPS>
228static void emitZeroWidthIndexingValue(PPS &os) {
229 os << "/*Zero width*/ 1\'b0";
230}
231
232/// Return the verilog name of the port for the module.
233static StringRef getPortVerilogName(Operation *module, size_t portArgNum) {
234 auto hml = cast<HWModuleLike>(module);
235 return hml.getPort(portArgNum).getVerilogName();
236}
237
238/// Return the verilog name of the port for the module.
239static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum) {
240 auto hml = cast<HWModuleLike>(module);
241 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
242 if (auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
243 if (auto updatedName = attrs.getAs<StringAttr>("hw.verilogName"))
244 return updatedName.getValue();
245 return hml.getHWModuleType().getPortName(pId);
246}
247
248/// This predicate returns true if the specified operation is considered a
249/// potentially inlinable Verilog expression. These nodes always have a single
250/// result, but may have side effects (e.g. `sv.verbatim.expr.se`).
251/// MemoryEffects should be checked if a client cares.
253 // These are SV dialect expressions.
254 if (isa<ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
255 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
256 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
257 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
258 UnpackedOpenArrayCastOp, ConcatStrOp>(op))
259 return true;
260
261 // These are Verif dialect expressions.
262 if (isa<verif::ContractOp>(op))
263 return true;
264
265 // All HW combinational logic ops and SV expression ops are Verilog
266 // expressions.
267 return isCombinational(op) || isExpression(op);
268}
269
270// NOLINTBEGIN(misc-no-recursion)
271/// Push this type's dimension into a vector.
272static void getTypeDims(
273 SmallVectorImpl<Attribute> &dims, Type type, Location loc,
274 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
275 if (auto integer = hw::type_dyn_cast<IntegerType>(type)) {
276 if (integer.getWidth() != 1)
277 dims.push_back(getInt32Attr(type.getContext(), integer.getWidth()));
278 return;
279 }
280 if (auto array = hw::type_dyn_cast<ArrayType>(type)) {
281 dims.push_back(getInt32Attr(type.getContext(), array.getNumElements()));
282 getTypeDims(dims, array.getElementType(), loc, errorHandler);
283
284 return;
285 }
286 if (auto intType = hw::type_dyn_cast<IntType>(type)) {
287 dims.push_back(intType.getWidth());
288 return;
289 }
290
291 if (auto inout = hw::type_dyn_cast<InOutType>(type))
292 return getTypeDims(dims, inout.getElementType(), loc, errorHandler);
293 if (auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
294 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
295 if (auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
296 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
297 if (hw::type_isa<InterfaceType, StructType, EnumType, UnionType>(type))
298 return;
299
300 errorHandler(loc) << "value has an unsupported verilog type " << type;
301}
302// NOLINTEND(misc-no-recursion)
303
304/// True iff 'a' and 'b' have the same wire dims.
306 Type a, Type b, Location loc,
307 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
308 SmallVector<Attribute, 4> aDims;
309 getTypeDims(aDims, a, loc, errorHandler);
310
311 SmallVector<Attribute, 4> bDims;
312 getTypeDims(bDims, b, loc, errorHandler);
313
314 return aDims == bDims;
315}
316
317// NOLINTBEGIN(misc-no-recursion)
319 type = getCanonicalType(type);
320 if (auto intType = dyn_cast<IntegerType>(type))
321 return intType.getWidth() == 0;
322 if (auto inout = dyn_cast<hw::InOutType>(type))
323 return isZeroBitType(inout.getElementType());
324 if (auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
325 return uarray.getNumElements() == 0 ||
326 isZeroBitType(uarray.getElementType());
327 if (auto array = dyn_cast<hw::ArrayType>(type))
328 return array.getNumElements() == 0 || isZeroBitType(array.getElementType());
329 if (auto structType = dyn_cast<hw::StructType>(type))
330 return llvm::all_of(structType.getElements(),
331 [](auto elem) { return isZeroBitType(elem.type); });
332 if (auto enumType = dyn_cast<hw::EnumType>(type))
333 return enumType.getFields().empty();
334 if (auto unionType = dyn_cast<hw::UnionType>(type))
335 return hw::getBitWidth(unionType) == 0;
336
337 // We have an open type system, so assume it is ok.
338 return false;
339}
340// NOLINTEND(misc-no-recursion)
341
342/// Given a set of known nested types (those supported by this pass), strip off
343/// leading unpacked types. This strips off portions of the type that are
344/// printed to the right of the name in verilog.
345// NOLINTBEGIN(misc-no-recursion)
346static Type stripUnpackedTypes(Type type) {
347 return TypeSwitch<Type, Type>(type)
348 .Case<InOutType>([](InOutType inoutType) {
349 return stripUnpackedTypes(inoutType.getElementType());
350 })
351 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](auto arrayType) {
352 return stripUnpackedTypes(arrayType.getElementType());
353 })
354 .Default([](Type type) { return type; });
355}
356
357/// Return true if the type has a leading unpacked type.
358static bool hasLeadingUnpackedType(Type type) {
359 assert(isa<hw::InOutType>(type) && "inout type is expected");
360 auto elementType = cast<hw::InOutType>(type).getElementType();
362}
363
364/// Return true if type has a struct type as a subtype.
365static bool hasStructType(Type type) {
366 return TypeSwitch<Type, bool>(type)
367 .Case<InOutType, UnpackedArrayType, ArrayType>([](auto parentType) {
368 return hasStructType(parentType.getElementType());
369 })
370 .Case<StructType>([](auto) { return true; })
371 .Default([](auto) { return false; });
372}
373// NOLINTEND(misc-no-recursion)
374
375//===----------------------------------------------------------------------===//
376// Location comparison
377//===----------------------------------------------------------------------===//
378
379// NOLINTBEGIN(misc-no-recursion)
380
381static int compareLocs(Location lhs, Location rhs);
382
383// NameLoc comparator - compare names, then child locations.
384static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs) {
385 if (auto name = lhs.getName().compare(rhs.getName()))
386 return name;
387 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
388}
389
390// FileLineColLoc comparator.
391static int compareLocsImpl(mlir::FileLineColLoc lhs, mlir::FileLineColLoc rhs) {
392 if (auto fn = lhs.getFilename().compare(rhs.getFilename()))
393 return fn;
394 if (lhs.getLine() != rhs.getLine())
395 return lhs.getLine() < rhs.getLine() ? -1 : 1;
396 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
397}
398
399// CallSiteLoc comparator. Compare first on the callee, then on the caller.
400static int compareLocsImpl(mlir::CallSiteLoc lhs, mlir::CallSiteLoc rhs) {
401 Location lhsCallee = lhs.getCallee();
402 Location rhsCallee = rhs.getCallee();
403 if (auto res = compareLocs(lhsCallee, rhsCallee))
404 return res;
405
406 Location lhsCaller = lhs.getCaller();
407 Location rhsCaller = rhs.getCaller();
408 return compareLocs(lhsCaller, rhsCaller);
409}
410
411template <typename TTargetLoc>
412FailureOr<int> dispatchCompareLocations(Location lhs, Location rhs) {
413 auto lhsT = dyn_cast<TTargetLoc>(lhs);
414 auto rhsT = dyn_cast<TTargetLoc>(rhs);
415 if (lhsT && rhsT) {
416 // Both are of the target location type, compare them directly.
417 return compareLocsImpl(lhsT, rhsT);
418 }
419 if (lhsT) {
420 // lhs is TTargetLoc => it comes before rhs.
421 return -1;
422 }
423 if (rhsT) {
424 // rhs is TTargetLoc => it comes before lhs.
425 return 1;
426 }
427
428 return failure();
429}
430
431// Top-level comparator for two arbitrarily typed locations.
432// First order comparison by location type:
433// 1. FileLineColLoc
434// 2. NameLoc
435// 3. CallSiteLoc
436// 4. Anything else...
437// Intra-location type comparison is delegated to the corresponding
438// compareLocsImpl() function.
439static int compareLocs(Location lhs, Location rhs) {
440 // FileLineColLoc
441 if (auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
442 succeeded(res))
443 return *res;
444
445 // NameLoc
446 if (auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
447 succeeded(res))
448 return *res;
449
450 // CallSiteLoc
451 if (auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
452 succeeded(res))
453 return *res;
454
455 // Anything else...
456 return 0;
457}
458
459// NOLINTEND(misc-no-recursion)
460
461//===----------------------------------------------------------------------===//
462// Location printing
463//===----------------------------------------------------------------------===//
464
465/// Pull apart any fused locations into the location set, such that they are
466/// uniqued. Any other location type will be added as-is.
467static void collectAndUniqueLocations(Location loc,
468 SmallPtrSetImpl<Attribute> &locationSet) {
469 llvm::TypeSwitch<Location, void>(loc)
470 .Case<FusedLoc>([&](auto fusedLoc) {
471 for (auto subLoc : fusedLoc.getLocations())
472 collectAndUniqueLocations(subLoc, locationSet);
473 })
474 .Default([&](auto loc) { locationSet.insert(loc); });
475}
476
477// Sorts a vector of locations in-place.
478template <typename TVector>
479static void sortLocationVector(TVector &vec) {
480 llvm::array_pod_sort(
481 vec.begin(), vec.end(), [](const auto *lhs, const auto *rhs) -> int {
482 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
483 });
484}
485
487public:
488 // Generates location info for a single location in the specified style.
490 SmallPtrSet<Attribute, 8> locationSet;
491 locationSet.insert(loc);
492 llvm::raw_string_ostream os(output);
493 emitLocationSetInfo(os, style, locationSet);
494 }
495
496 // Generates location info for a set of operations in the specified style.
498 const SmallPtrSetImpl<Operation *> &ops) {
499 // Multiple operations may come from the same location or may not have
500 // useful
501 // location info. Unique it now.
502 SmallPtrSet<Attribute, 8> locationSet;
503 for (auto *op : ops)
504 collectAndUniqueLocations(op->getLoc(), locationSet);
505 llvm::raw_string_ostream os(output);
506 emitLocationSetInfo(os, style, locationSet);
507 }
508
509 StringRef strref() { return output; }
510
511private:
512 void emitLocationSetInfo(llvm::raw_string_ostream &os,
514 const SmallPtrSetImpl<Attribute> &locationSet) {
515 if (style == LoweringOptions::LocationInfoStyle::None)
516 return;
517 std::string resstr;
518 llvm::raw_string_ostream sstr(resstr);
519 LocationEmitter::Impl(sstr, style, locationSet);
520 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
521 os << resstr;
522 return;
523 }
524 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
525 "other styles must be already handled");
526 os << "@[" << resstr << "]";
527 }
528
529 std::string output;
530
531 struct Impl {
532
533 // NOLINTBEGIN(misc-no-recursion)
535 const SmallPtrSetImpl<Attribute> &locationSet)
536 : os(os), style(style) {
537 emitLocationSetInfoImpl(locationSet);
538 }
539
540 // Emit CallSiteLocs.
541 void emitLocationInfo(mlir::CallSiteLoc loc) {
542 os << "{";
543 emitLocationInfo(loc.getCallee());
544 os << " <- ";
545 emitLocationInfo(loc.getCaller());
546 os << "}";
547 }
548
549 // Emit NameLocs.
550 void emitLocationInfo(mlir::NameLoc loc) {
551 bool withName = !loc.getName().empty();
552 if (withName)
553 os << "'" << loc.getName().strref() << "'(";
554 emitLocationInfo(loc.getChildLoc());
555
556 if (withName)
557 os << ")";
558 }
559
560 // Emit FileLineColLocs.
561 void emitLocationInfo(FileLineColLoc loc) {
562 os << loc.getFilename().getValue();
563 if (auto line = loc.getLine()) {
564 os << ':' << line;
565 if (auto col = loc.getColumn())
566 os << ':' << col;
567 }
568 }
569
570 // Generates a string representation of a set of FileLineColLocs.
571 // The entries are sorted by filename, line, col. Try to merge together
572 // entries to reduce verbosity on the column info.
573 void
574 printFileLineColSetInfo(llvm::SmallVector<FileLineColLoc, 8> locVector) {
575 // The entries are sorted by filename, line, col. Try to merge together
576 // entries to reduce verbosity on the column info.
577 StringRef lastFileName;
578 for (size_t i = 0, e = locVector.size(); i != e;) {
579 if (i != 0)
580 os << ", ";
581
582 // Print the filename if it changed.
583 auto first = locVector[i];
584 if (first.getFilename() != lastFileName) {
585 lastFileName = first.getFilename();
586 os << lastFileName;
587 }
588
589 // Scan for entries with the same file/line.
590 size_t end = i + 1;
591 while (end != e &&
592 first.getFilename() == locVector[end].getFilename() &&
593 first.getLine() == locVector[end].getLine())
594 ++end;
595
596 // If we have one entry, print it normally.
597 if (end == i + 1) {
598 if (auto line = first.getLine()) {
599 os << ':' << line;
600 if (auto col = first.getColumn())
601 os << ':' << col;
602 }
603 ++i;
604 continue;
605 }
606
607 // Otherwise print a brace enclosed list.
608 os << ':' << first.getLine() << ":{";
609 while (i != end) {
610 os << locVector[i++].getColumn();
611
612 if (i != end)
613 os << ',';
614 }
615 os << '}';
616 }
617 }
618
619 /// Return the location information in the specified style. This is the main
620 /// dispatch function for calling the location-specific routines.
621 void emitLocationInfo(Location loc) {
622 llvm::TypeSwitch<Location, void>(loc)
623 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
624 [&](auto loc) { emitLocationInfo(loc); })
625 .Case<mlir::FusedLoc>([&](auto loc) {
626 SmallPtrSet<Attribute, 8> locationSet;
627 collectAndUniqueLocations(loc, locationSet);
628 emitLocationSetInfoImpl(locationSet);
629 })
630 .Default([&](auto loc) {
631 // Don't print anything for unhandled locations.
632 });
633 }
634
635 /// Emit the location information of `locationSet` to `sstr`. The emitted
636 /// string
637 /// may potentially be an empty string given the contents of the
638 /// `locationSet`.
639 void
640 emitLocationSetInfoImpl(const SmallPtrSetImpl<Attribute> &locationSet) {
641 // Fast pass some common cases.
642 switch (locationSet.size()) {
643 case 1:
644 emitLocationInfo(cast<LocationAttr>(*locationSet.begin()));
645 [[fallthrough]];
646 case 0:
647 return;
648 default:
649 break;
650 }
651
652 // Sort the entries into distinct location printing kinds.
653 SmallVector<FileLineColLoc, 8> flcLocs;
654 SmallVector<Attribute, 8> otherLocs;
655 flcLocs.reserve(locationSet.size());
656 otherLocs.reserve(locationSet.size());
657 for (Attribute loc : locationSet) {
658 if (auto flcLoc = dyn_cast<FileLineColLoc>(loc))
659 flcLocs.push_back(flcLoc);
660 else
661 otherLocs.push_back(loc);
662 }
663
664 // SmallPtrSet iteration is non-deterministic, so sort the location
665 // vectors to ensure deterministic output.
666 sortLocationVector(otherLocs);
667 sortLocationVector(flcLocs);
668
669 // To detect whether something actually got emitted, we inspect the stream
670 // for size changes. This is due to the possiblity of locations which are
671 // not supposed to be emitted (e.g. `loc("")`).
672 size_t sstrSize = os.tell();
673 bool emittedAnything = false;
674 auto recheckEmittedSomething = [&]() {
675 size_t currSize = os.tell();
676 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
677 emittedAnything |= emittedSomethingSinceLastCheck;
678 sstrSize = currSize;
679 return emittedSomethingSinceLastCheck;
680 };
681
682 // First, emit the other locations through the generic location dispatch
683 // function.
684 llvm::interleave(
685 otherLocs,
686 [&](Attribute loc) { emitLocationInfo(cast<LocationAttr>(loc)); },
687 [&] {
688 if (recheckEmittedSomething()) {
689 os << ", ";
690 recheckEmittedSomething(); // reset detector to reflect the comma.
691 }
692 });
693
694 // If we emitted anything, and we have FileLineColLocs, then emit a
695 // location-separating comma.
696 if (emittedAnything && !flcLocs.empty())
697 os << ", ";
698 // Then, emit the FileLineColLocs.
700 }
701 llvm::raw_string_ostream &os;
703
704 // NOLINTEND(misc-no-recursion)
705 };
706};
707
708/// Most expressions are invalid to bit-select from in Verilog, but some
709/// things are ok. Return true if it is ok to inline bitselect from the
710/// result of this expression. It is conservatively correct to return false.
711static bool isOkToBitSelectFrom(Value v) {
712 // Module ports are always ok to bit select from.
713 if (isa<BlockArgument>(v))
714 return true;
715
716 // Read_inout is valid to inline for bit-select. See `select` syntax on
717 // SV spec A.8.4 (P1174).
718 if (auto read = v.getDefiningOp<ReadInOutOp>())
719 return true;
720
721 // Aggregate access can be inlined.
722 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
723 v.getDefiningOp()))
724 return true;
725
726 // Interface signal can be inlined.
727 if (v.getDefiningOp<ReadInterfaceSignalOp>())
728 return true;
729
730 // TODO: We could handle concat and other operators here.
731 return false;
732}
733
734/// Return true if we are unable to ever inline the specified operation. This
735/// happens because not all Verilog expressions are composable, notably you
736/// can only use bit selects like x[4:6] on simple expressions, you cannot use
737/// expressions in the sensitivity list of always blocks, etc.
738static bool isExpressionUnableToInline(Operation *op,
739 const LoweringOptions &options) {
740 if (auto cast = dyn_cast<BitcastOp>(op))
741 if (!haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
742 op->getLoc(),
743 [&](Location loc) { return emitError(loc); })) {
744 // Even if dimentions don't match, we can inline when its user doesn't
745 // rely on the type.
746 if (op->hasOneUse() &&
747 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
748 return false;
749 // Bitcasts rely on the type being assigned to, so we cannot inline.
750 return true;
751 }
752
753 // StructCreateOp needs to be assigning to a named temporary so that types
754 // are inferred properly by verilog
755 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
756 op))
757 return true;
758
759 // Aggregate literal syntax only works in an assignment expression, where
760 // the Verilog expression's type is determined by the LHS.
761 if (auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
762 return true;
763
764 // Verbatim with a long string should be emitted as an out-of-line declration.
765 if (auto verbatim = dyn_cast<VerbatimExprOp>(op))
766 if (verbatim.getFormatString().size() > 32)
767 return true;
768
769 // Scan the users of the operation to see if any of them need this to be
770 // emitted out-of-line.
771 for (auto &use : op->getUses()) {
772 auto *user = use.getOwner();
773
774 // Verilog bit selection is required by the standard to be:
775 // "a vector, packed array, packed structure, parameter or concatenation".
776 //
777 // It cannot be an arbitrary expression, e.g. this is invalid:
778 // assign bar = {{a}, {b}, {c}, {d}}[idx];
779 //
780 // To handle these, we push the subexpression into a temporary.
781 if (isa<ExtractOp, ArraySliceOp, ArrayGetOp, ArrayInjectOp, StructExtractOp,
782 StructInjectOp, StructExplodeOp, UnionExtractOp,
783 IndexedPartSelectOp>(user))
784 if (use.getOperandNumber() == 0 && // ignore index operands.
785 !isOkToBitSelectFrom(use.get()))
786 return true;
787
788 // Handle option disallowing expressions in event control.
789 if (!options.allowExprInEventControl) {
790 // Check operations used for event control, anything other than
791 // a read of a wire must be out of line.
792
793 // Helper to determine if the use will be part of "event control",
794 // based on what the operation using it is and as which operand.
795 auto usedInExprControl = [user, &use]() {
796 return TypeSwitch<Operation *, bool>(user)
797 .Case<ltl::ClockOp>([&](auto clockOp) {
798 // LTL Clock op's clock operand must be a name.
799 return clockOp.getClock() == use.get();
800 })
801 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
802 sv::CoverConcurrentOp>(
803 [&](auto op) { return op.getClock() == use.get(); })
804 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
805 sv::CoverPropertyOp>([&](auto op) {
806 return op.getDisable() == use.get() || op.getClock() == use.get();
807 })
808 .Case<AlwaysOp, AlwaysFFOp>([](auto) {
809 // Always blocks must have a name in their sensitivity list.
810 // (all operands)
811 return true;
812 })
813 .Default([](auto) { return false; });
814 };
815
816 if (!usedInExprControl())
817 continue;
818
819 // Otherwise, this can only be inlined if is (already) a read of a wire.
820 auto read = dyn_cast<ReadInOutOp>(op);
821 if (!read)
822 return true;
823 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
824 return true;
825 }
826 }
827 return false;
828}
829
831
832/// Compute how many statements are within this block, for begin/end markers.
834 unsigned numStatements = 0;
835 block.walk([&](Operation *op) {
836 if (isVerilogExpression(op) ||
837 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
838 return WalkResult::advance();
839 numStatements +=
840 TypeSwitch<Operation *, unsigned>(op)
841 .Case<VerbatimOp>([&](auto) {
842 // We don't know how many statements we emitted, so assume
843 // conservatively that a lot got put out. This will make sure we
844 // get a begin/end block around this.
845 return 3;
846 })
847 .Case<IfOp>([&](auto) {
848 // We count if as multiple statements to make sure it is always
849 // surrounded by a begin/end so we don't get if/else confusion in
850 // cases like this:
851 // if (cond)
852 // if (otherCond) // This should force a begin!
853 // stmt
854 // else // Goes with the outer if!
855 // thing;
856 return 2;
857 })
858 .Case<IfDefOp, IfDefProceduralOp>([&](auto) { return 3; })
859 .Case<OutputOp>([&](OutputOp oop) {
860 // Skip single-use instance outputs, they don't get statements.
861 // Keep this synchronized with visitStmt(InstanceOp,OutputOp).
862 return llvm::count_if(oop->getOperands(), [&](auto operand) {
863 Operation *op = operand.getDefiningOp();
864 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
865 });
866 })
867 .Default([](auto) { return 1; });
868 if (numStatements > 1)
869 return WalkResult::interrupt();
870 return WalkResult::advance();
871 });
872 if (numStatements == 0)
874 if (numStatements == 1)
877}
878
879/// Return true if this expression should be emitted inline into any statement
880/// that uses it.
882 const LoweringOptions &options) {
883 // Never create a temporary for a dead expression.
884 if (op->getResult(0).use_empty())
885 return true;
886
887 // Never create a temporary which is only going to be assigned to an output
888 // port, wire, or reg.
889 if (op->hasOneUse() &&
890 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
891 *op->getUsers().begin()))
892 return true;
893
894 // If mux inlining is dissallowed, we cannot inline muxes.
895 if (options.disallowMuxInlining && isa<MuxOp>(op))
896 return false;
897
898 // If this operation has multiple uses, we can't generally inline it unless
899 // the op is duplicatable.
900 if (!op->getResult(0).hasOneUse() && !isDuplicatableExpression(op))
901 return false;
902
903 // If it isn't structurally possible to inline this expression, emit it out
904 // of line.
905 return !isExpressionUnableToInline(op, options);
906}
907
908/// Find a nested IfOp in an else block that can be printed as `else if`
909/// instead of nesting it into a new `begin` - `end` block. The block must
910/// contain a single IfOp and optionally expressions which can be hoisted out.
911static IfOp findNestedElseIf(Block *elseBlock) {
912 IfOp ifOp;
913 for (auto &op : *elseBlock) {
914 if (auto opIf = dyn_cast<IfOp>(op)) {
915 if (ifOp)
916 return {};
917 ifOp = opIf;
918 continue;
919 }
920 if (!isVerilogExpression(&op))
921 return {};
922 }
923 // SV attributes cannot be attached to `else if` so reject when ifOp has SV
924 // attributes.
925 if (ifOp && hasSVAttributes(ifOp))
926 return {};
927 return ifOp;
928}
929
930/// Emit SystemVerilog attributes.
931template <typename PPS>
932static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak) {
933 enum Container { NoContainer, InComment, InAttr };
934 Container currentContainer = NoContainer;
935
936 auto closeContainer = [&] {
937 if (currentContainer == NoContainer)
938 return;
939 if (currentContainer == InComment)
940 ps << " */";
941 else if (currentContainer == InAttr)
942 ps << " *)";
943 ps << PP::end << PP::end;
944
945 currentContainer = NoContainer;
946 };
947
948 bool isFirstContainer = true;
949 auto openContainer = [&](Container newContainer) {
950 assert(newContainer != NoContainer);
951 if (currentContainer == newContainer)
952 return false;
953 closeContainer();
954 // If not first container, insert break point but no space.
955 if (!isFirstContainer)
956 ps << (mayBreak ? PP::space : PP::nbsp);
957 isFirstContainer = false;
958 // fit container on one line if possible, break if needed.
959 ps << PP::ibox0;
960 if (newContainer == InComment)
961 ps << "/* ";
962 else if (newContainer == InAttr)
963 ps << "(* ";
964 currentContainer = newContainer;
965 // Pack attributes within to fit, align to current column when breaking.
966 ps << PP::ibox0;
967 return true;
968 };
969
970 // Break containers to starting column (0), put all on same line OR
971 // put each on their own line (cbox).
972 ps.scopedBox(PP::cbox0, [&]() {
973 for (auto attr : attrs.getAsRange<SVAttributeAttr>()) {
974 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
975 : InAttr))
976 ps << "," << (mayBreak ? PP::space : PP::nbsp);
977 ps << PPExtString(attr.getName().getValue());
978 if (attr.getExpression())
979 ps << " = " << PPExtString(attr.getExpression().getValue());
980 }
981 closeContainer();
982 });
983}
984
985/// Retrieve value's verilog name from IR. The name must already have been
986/// added in pre-pass and passed through "hw.verilogName" attr.
987StringRef getVerilogValueName(Value val) {
988 if (auto *op = val.getDefiningOp())
989 return getSymOpName(op);
990
991 if (auto port = dyn_cast<BlockArgument>(val)) {
992 // If the value is defined by for op, use its associated verilog name.
993 auto parent = port.getParentBlock()->getParentOp();
994 if (isa<ForOp, GenerateForOp>(parent))
995 return parent->getAttrOfType<StringAttr>("hw.verilogName");
996 return getInputPortVerilogName(port.getParentBlock()->getParentOp(),
997 port.getArgNumber());
998 }
999 assert(false && "unhandled value");
1000 return {};
1001}
1002
1003//===----------------------------------------------------------------------===//
1004// VerilogEmitterState
1005//===----------------------------------------------------------------------===//
1006
1007namespace {
1008
1009/// This class maintains the mutable state that cross-cuts and is shared by the
1010/// various emitters.
1011class VerilogEmitterState {
1012public:
1013 explicit VerilogEmitterState(ModuleOp designOp,
1014 const SharedEmitterState &shared,
1015 const LoweringOptions &options,
1016 const HWSymbolCache &symbolCache,
1017 const GlobalNameTable &globalNames,
1018 const FileMapping &fileMapping,
1019 llvm::formatted_raw_ostream &os,
1020 StringAttr fileName, OpLocMap &verilogLocMap)
1021 : designOp(designOp), shared(shared), options(options),
1022 symbolCache(symbolCache), globalNames(globalNames),
1023 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1024 pp(os, options.emittedLineLength), fileName(fileName) {
1025 pp.setListener(&saver);
1026 }
1027 /// This is the root mlir::ModuleOp that holds the whole design being emitted.
1028 ModuleOp designOp;
1029
1030 const SharedEmitterState &shared;
1031
1032 /// The emitter options which control verilog emission.
1033 const LoweringOptions &options;
1034
1035 /// This is a cache of various information about the IR, in frozen state.
1036 const HWSymbolCache &symbolCache;
1037
1038 /// This tracks global names where the Verilog name needs to be different than
1039 /// the IR name.
1040 const GlobalNameTable &globalNames;
1041
1042 /// Tracks the referenceable files through their symbol.
1043 const FileMapping &fileMapping;
1044
1045 /// The stream to emit to. Use a formatted_raw_ostream, to easily get the
1046 /// current location(line,column) on the stream. This is required to record
1047 /// the verilog output location information corresponding to any op.
1048 llvm::formatted_raw_ostream &os;
1049
1050 bool encounteredError = false;
1051
1052 /// Pretty printing:
1053
1054 /// Whether a newline is expected, emitted late to provide opportunity to
1055 /// open/close boxes we don't know we need at level of individual statement.
1056 /// Every statement should set this instead of directly emitting (last)
1057 /// newline. Most statements end with emitLocationInfoAndNewLine which handles
1058 /// this.
1059 bool pendingNewline = false;
1060
1061 /// Used to record the verilog output file location of an op.
1062 OpLocMap &verilogLocMap;
1063 /// String storage backing Tokens built from temporary strings.
1064 /// PrettyPrinter will clear this as appropriate.
1067 verilogLocMap);
1068
1069 /// Pretty printer.
1070 PrettyPrinter pp;
1071
1072 /// Name of the output file, used for debug information.
1073 StringAttr fileName;
1074
1075 /// Update the location attribute of the ops with the verilog locations
1076 /// recorded in `verilogLocMap` and clear the map. `lineOffset` is added to
1077 /// all the line numbers, this is required when the modules are exported in
1078 /// parallel.
1079 void addVerilogLocToOps(unsigned int lineOffset, StringAttr fileName) {
1080 verilogLocMap.updateIRWithLoc(lineOffset, fileName,
1081 shared.designOp->getContext());
1082 verilogLocMap.clear();
1083 }
1084
1085private:
1086 VerilogEmitterState(const VerilogEmitterState &) = delete;
1087 void operator=(const VerilogEmitterState &) = delete;
1088};
1089} // namespace
1090
1091//===----------------------------------------------------------------------===//
1092// EmitterBase
1093//===----------------------------------------------------------------------===//
1094
1095namespace {
1096
1097/// The data that is unique to each callback. The operation and a flag to
1098/// indicate if the callback is for begin or end of the operation print
1099/// location.
1100using CallbackDataTy = std::pair<Operation *, bool>;
1101class EmitterBase {
1102public:
1103 // All of the mutable state we are maintaining.
1104 VerilogEmitterState &state;
1105
1106 /// Stream helper (pp, saver).
1108
1109 explicit EmitterBase(VerilogEmitterState &state)
1110 : state(state),
1111 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1112
1113 InFlightDiagnostic emitError(Operation *op, const Twine &message) {
1114 state.encounteredError = true;
1115 return op->emitError(message);
1116 }
1117
1118 InFlightDiagnostic emitOpError(Operation *op, const Twine &message) {
1119 state.encounteredError = true;
1120 return op->emitOpError(message);
1121 }
1122
1123 InFlightDiagnostic emitError(Location loc, const Twine &message = "") {
1124 state.encounteredError = true;
1125 return mlir::emitError(loc, message);
1126 }
1127
1128 void emitLocationImpl(llvm::StringRef location) {
1129 // Break so previous content is not impacted by following,
1130 // but use a 'neverbreak' so it always fits.
1131 ps << PP::neverbreak;
1132 if (!location.empty())
1133 ps << "\t// " << location; // (don't use tabs in normal pretty-printing)
1134 }
1135
1136 void emitLocationInfo(Location loc) {
1137 emitLocationImpl(
1138 LocationEmitter(state.options.locationInfoStyle, loc).strref());
1139 }
1140
1141 /// If we have location information for any of the specified operations,
1142 /// aggregate it together and print a pretty comment specifying where the
1143 /// operations came from. In any case, print a newline.
1144 void emitLocationInfoAndNewLine(const SmallPtrSetImpl<Operation *> &ops) {
1145 emitLocationImpl(
1146 LocationEmitter(state.options.locationInfoStyle, ops).strref());
1147 setPendingNewline();
1148 }
1149
1150 template <typename PPS>
1151 void emitTextWithSubstitutions(PPS &ps, StringRef string, Operation *op,
1152 llvm::function_ref<void(Value)> operandEmitter,
1153 ArrayAttr symAttrs);
1154
1155 /// Emit the value of a StringAttr as one or more Verilog "one-line" comments
1156 /// ("//"). Break the comment to respect the emittedLineLength and trim
1157 /// whitespace after a line break. Do nothing if the StringAttr is null or
1158 /// the value is empty.
1159 void emitComment(StringAttr comment);
1160
1161 /// If previous emission requires a newline, emit it now.
1162 /// This gives us opportunity to open/close boxes before linebreak.
1163 void emitPendingNewlineIfNeeded() {
1164 if (state.pendingNewline) {
1165 state.pendingNewline = false;
1166 ps << PP::newline;
1167 }
1168 }
1169 void setPendingNewline() {
1170 assert(!state.pendingNewline);
1171 state.pendingNewline = true;
1172 }
1173
1174 void startStatement() { emitPendingNewlineIfNeeded(); }
1175
1176private:
1177 void operator=(const EmitterBase &) = delete;
1178 EmitterBase(const EmitterBase &) = delete;
1179};
1180} // end anonymous namespace
1181
1182template <typename PPS>
1183void EmitterBase::emitTextWithSubstitutions(
1184 PPS &ps, StringRef string, Operation *op,
1185 llvm::function_ref<void(Value)> operandEmitter, ArrayAttr symAttrs) {
1186
1187 // Perform operand substitions as we emit the line string. We turn {{42}}
1188 // into the value of operand 42.
1189 auto namify = [&](Attribute sym, HWSymbolCache::Item item) {
1190 // CAVEAT: These accesses can reach into other modules through inner name
1191 // references, which are currently being processed. Do not add those remote
1192 // operations to this module's `names`, which is reserved for things named
1193 // *within* this module. Instead, you have to rely on those remote
1194 // operations to have been named inside the global names table. If they
1195 // haven't, take a look at name legalization first.
1196 if (auto *itemOp = item.getOp()) {
1197 if (item.hasPort()) {
1198 return getPortVerilogName(itemOp, item.getPort());
1199 }
1200 StringRef symOpName = getSymOpName(itemOp);
1201 if (!symOpName.empty())
1202 return symOpName;
1203 emitError(itemOp, "cannot get name for symbol ") << sym;
1204 } else {
1205 emitError(op, "cannot get name for symbol ") << sym;
1206 }
1207 return StringRef("<INVALID>");
1208 };
1209
1210 // Scan 'line' for a substitution, emitting any non-substitution prefix,
1211 // then the mentioned operand, chopping the relevant text off 'line' and
1212 // returning true. This returns false if no substitution is found.
1213 unsigned numSymOps = symAttrs.size();
1214 auto emitUntilSubstitution = [&](size_t next = 0) -> bool {
1215 size_t start = 0;
1216 while (true) {
1217 next = string.find("{{", next);
1218 if (next == StringRef::npos)
1219 return false;
1220
1221 // Check to make sure we have a number followed by }}. If not, we
1222 // ignore the {{ sequence as something that could happen in Verilog.
1223 next += 2;
1224 start = next;
1225 while (next < string.size() && isdigit(string[next]))
1226 ++next;
1227 // We need at least one digit.
1228 if (start == next) {
1229 next--;
1230 continue;
1231 }
1232 size_t operandNoLength = next - start;
1233
1234 // Format string options follow a ':'.
1235 StringRef fmtOptsStr;
1236 if (string[next] == ':') {
1237 size_t startFmtOpts = next + 1;
1238 while (next < string.size() && string[next] != '}')
1239 ++next;
1240 fmtOptsStr = string.substr(startFmtOpts, next - startFmtOpts);
1241 }
1242
1243 // We must have a }} right after the digits.
1244 if (!string.substr(next).starts_with("}}"))
1245 continue;
1246
1247 // We must be able to decode the integer into an unsigned.
1248 unsigned operandNo = 0;
1249 if (string.drop_front(start)
1250 .take_front(operandNoLength)
1251 .getAsInteger(10, operandNo)) {
1252 emitError(op, "operand substitution too large");
1253 continue;
1254 }
1255 next += 2;
1256
1257 // Emit any text before the substitution.
1258 auto before = string.take_front(start - 2);
1259 if (!before.empty())
1260 ps << PPExtString(before);
1261
1262 // operandNo can either refer to Operands or symOps. symOps are
1263 // numbered after the operands.
1264 if (operandNo < op->getNumOperands())
1265 // Emit the operand.
1266 operandEmitter(op->getOperand(operandNo));
1267 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1268 unsigned symOpNum = operandNo - op->getNumOperands();
1269 auto sym = symAttrs[symOpNum];
1270 StringRef symVerilogName;
1271 if (auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1272 if (auto *symOp = state.symbolCache.getDefinition(fsym)) {
1273 if (auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1274 auto namepath = globalRef.getNamepathAttr().getValue();
1275 for (auto [index, sym] : llvm::enumerate(namepath)) {
1276 // Emit the seperator string.
1277 if (index > 0)
1278 ps << (fmtOptsStr.empty() ? "." : fmtOptsStr);
1279
1280 auto innerRef = cast<InnerRefAttr>(sym);
1281 auto ref = state.symbolCache.getInnerDefinition(
1282 innerRef.getModule(), innerRef.getName());
1283 ps << namify(innerRef, ref);
1284 }
1285 } else {
1286 symVerilogName = namify(sym, symOp);
1287 }
1288 }
1289 } else if (auto isym = dyn_cast<InnerRefAttr>(sym)) {
1290 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1291 isym.getName());
1292 symVerilogName = namify(sym, symOp);
1293 }
1294 if (!symVerilogName.empty())
1295 ps << PPExtString(symVerilogName);
1296 } else {
1297 emitError(op, "operand " + llvm::utostr(operandNo) + " isn't valid");
1298 continue;
1299 }
1300 // Forget about the part we emitted.
1301 string = string.drop_front(next);
1302 return true;
1303 }
1304 };
1305
1306 // Emit all the substitutions.
1307 while (emitUntilSubstitution())
1308 ;
1309
1310 // Emit any text after the last substitution.
1311 if (!string.empty())
1312 ps << PPExtString(string);
1313}
1314
1315void EmitterBase::emitComment(StringAttr comment) {
1316 if (!comment)
1317 return;
1318
1319 // Set a line length for the comment. Subtract off the leading comment and
1320 // space ("// ") as well as the current indent level to simplify later
1321 // arithmetic. Ensure that this line length doesn't go below zero.
1322 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1323
1324 // Process the comment in line chunks extracted from manually specified line
1325 // breaks. This is done to preserve user-specified line breaking if used.
1326 auto ref = comment.getValue();
1327 StringRef line;
1328 while (!ref.empty()) {
1329 std::tie(line, ref) = ref.split("\n");
1330 // Emit each comment line breaking it if it exceeds the emittedLineLength.
1331 for (;;) {
1332 startStatement();
1333 ps << "// ";
1334
1335 // Base case 1: the entire comment fits on one line.
1336 if (line.size() <= lineLength) {
1337 ps << PPExtString(line);
1338 setPendingNewline();
1339 break;
1340 }
1341
1342 // The comment does NOT fit on one line. Use a simple algorithm to find
1343 // a position to break the line:
1344 // 1) Search backwards for whitespace and break there if you find it.
1345 // 2) If no whitespace exists in (1), search forward for whitespace
1346 // and break there.
1347 // This algorithm violates the emittedLineLength if (2) ever occurrs,
1348 // but it's dead simple.
1349 auto breakPos = line.rfind(' ', lineLength);
1350 // No whitespace exists looking backwards.
1351 if (breakPos == StringRef::npos) {
1352 breakPos = line.find(' ', lineLength);
1353 // No whitespace exists looking forward (you hit the end of the
1354 // string).
1355 if (breakPos == StringRef::npos)
1356 breakPos = line.size();
1357 }
1358
1359 // Emit up to the break position. Trim any whitespace after the break
1360 // position. Exit if nothing is left to emit. Otherwise, update the
1361 // comment ref and continue;
1362 ps << PPExtString(line.take_front(breakPos));
1363 setPendingNewline();
1364 breakPos = line.find_first_not_of(' ', breakPos);
1365 // Base Case 2: nothing left except whitespace.
1366 if (breakPos == StringRef::npos)
1367 break;
1368
1369 line = line.drop_front(breakPos);
1370 }
1371 }
1372}
1373
1374/// Given an expression that is spilled into a temporary wire, try to synthesize
1375/// a better name than "_T_42" based on the structure of the expression.
1376// NOLINTBEGIN(misc-no-recursion)
1378 StringAttr result;
1379 bool addPrefixUnderScore = true;
1380
1381 // Look through read_inout.
1382 if (auto read = expr.getDefiningOp<ReadInOutOp>())
1383 return inferStructuralNameForTemporary(read.getInput());
1384
1385 // Module ports carry names!
1386 if (auto blockArg = dyn_cast<BlockArgument>(expr)) {
1387 auto moduleOp =
1388 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1389 StringRef name = getPortVerilogName(moduleOp, blockArg.getArgNumber());
1390 result = StringAttr::get(expr.getContext(), name);
1391
1392 } else if (auto *op = expr.getDefiningOp()) {
1393 // Uses of a wire, register or logic can be done inline.
1394 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1395 StringRef name = getSymOpName(op);
1396 result = StringAttr::get(expr.getContext(), name);
1397
1398 } else if (auto nameHint = op->getAttrOfType<StringAttr>("sv.namehint")) {
1399 // Use a dialect (sv) attribute to get a hint for the name if the op
1400 // doesn't explicitly specify it. Do this last
1401 result = nameHint;
1402
1403 // If there is a namehint, don't add underscores to the name.
1404 addPrefixUnderScore = false;
1405 } else {
1406 TypeSwitch<Operation *>(op)
1407 // Generate a pretty name for VerbatimExpr's that look macro-like
1408 // using the same logic that generates the MLIR syntax name.
1409 .Case([&result](VerbatimExprOp verbatim) {
1410 verbatim.getAsmResultNames([&](Value, StringRef name) {
1411 result = StringAttr::get(verbatim.getContext(), name);
1412 });
1413 })
1414 .Case([&result](VerbatimExprSEOp verbatim) {
1415 verbatim.getAsmResultNames([&](Value, StringRef name) {
1416 result = StringAttr::get(verbatim.getContext(), name);
1417 });
1418 })
1419
1420 // If this is an extract from a namable object, derive a name from it.
1421 .Case([&result](ExtractOp extract) {
1422 if (auto operandName =
1423 inferStructuralNameForTemporary(extract.getInput())) {
1424 unsigned numBits =
1425 cast<IntegerType>(extract.getType()).getWidth();
1426 if (numBits == 1)
1427 result = StringAttr::get(extract.getContext(),
1428 operandName.strref() + "_" +
1429 Twine(extract.getLowBit()));
1430 else
1431 result = StringAttr::get(
1432 extract.getContext(),
1433 operandName.strref() + "_" +
1434 Twine(extract.getLowBit() + numBits - 1) + "to" +
1435 Twine(extract.getLowBit()));
1436 }
1437 });
1438 // TODO: handle other common patterns.
1439 }
1440 }
1441
1442 // Make sure any synthesized name starts with an _.
1443 if (!result || result.strref().empty())
1444 return {};
1445
1446 // Make sure that all temporary names start with an underscore.
1447 if (addPrefixUnderScore && result.strref().front() != '_')
1448 result = StringAttr::get(expr.getContext(), "_" + result.strref());
1449
1450 return result;
1451}
1452// NOLINTEND(misc-no-recursion)
1453
1454//===----------------------------------------------------------------------===//
1455// ModuleEmitter
1456//===----------------------------------------------------------------------===//
1457
1458namespace {
1459
1460class ModuleEmitter : public EmitterBase {
1461public:
1462 explicit ModuleEmitter(VerilogEmitterState &state)
1463 : EmitterBase(state), currentModuleOp(nullptr),
1464 fieldNameResolver(FieldNameResolver(state.globalNames, state.options)) {
1465 }
1466 ~ModuleEmitter() {
1467 emitPendingNewlineIfNeeded();
1468 ps.eof();
1469 };
1470
1471 void emitParameters(Operation *module, ArrayAttr params);
1472 void emitPortList(Operation *module, const ModulePortInfo &portInfo,
1473 bool emitAsTwoStateType = false);
1474
1475 void emitHWModule(HWModuleOp module);
1476 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1477 void emitFunc(FuncOp);
1478
1479 // Statements.
1480 void emitStatement(Operation *op);
1481 void emitBind(BindOp op);
1482 void emitBindInterface(BindInterfaceOp op);
1483
1484 void emitSVAttributes(Operation *op);
1485
1486 /// Legalize the given field name if it is an invalid verilog name.
1487 StringRef getVerilogStructFieldName(StringAttr field) {
1488 return fieldNameResolver.getRenamedFieldName(field).getValue();
1489 }
1490
1491 //===--------------------------------------------------------------------===//
1492 // Methods for formatting types.
1493
1494 /// Emit a type's packed dimensions.
1495 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1496
1497 /// Print the specified packed portion of the type to the specified stream,
1498 ///
1499 /// * 'optionalAliasType' can be provided to perform any alias-aware printing
1500 /// of the inner type.
1501 /// * When `implicitIntType` is false, a "logic" is printed. This is used in
1502 /// struct fields and typedefs.
1503 /// * When `singleBitDefaultType` is false, single bit values are printed as
1504 /// `[0:0]`. This is used in parameter lists.
1505 ///
1506 /// This returns true if anything was printed.
1507 bool printPackedType(Type type, raw_ostream &os, Location loc,
1508 Type optionalAliasType = {}, bool implicitIntType = true,
1509 bool singleBitDefaultType = true,
1510 bool emitAsTwoStateType = false);
1511
1512 /// Output the unpacked array dimensions. This is the part of the type that
1513 /// is to the right of the name.
1514 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1515
1516 //===--------------------------------------------------------------------===//
1517 // Methods for formatting parameters.
1518
1519 /// Prints a parameter attribute expression in a Verilog compatible way to the
1520 /// specified stream. This returns the precedence of the generated string.
1521 SubExprInfo printParamValue(Attribute value, raw_ostream &os,
1522 function_ref<InFlightDiagnostic()> emitError);
1523
1524 SubExprInfo printParamValue(Attribute value, raw_ostream &os,
1525 VerilogPrecedence parenthesizeIfLooserThan,
1526 function_ref<InFlightDiagnostic()> emitError);
1527
1528 //===--------------------------------------------------------------------===//
1529 // Mutable state while emitting a module body.
1530
1531 /// This is the current module being emitted for a HWModuleOp.
1532 Operation *currentModuleOp;
1533
1534 /// This set keeps track of expressions that were emitted into their
1535 /// 'automatic logic' or 'localparam' declaration. This is only used for
1536 /// expressions in a procedural region, because we otherwise just emit wires
1537 /// on demand.
1538 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1539
1540 /// This class keeps track of field name renamings in the module scope.
1541 FieldNameResolver fieldNameResolver;
1542
1543 /// This keeps track of assignments folded into wire emissions
1544 SmallPtrSet<Operation *, 16> assignsInlined;
1545};
1546
1547} // end anonymous namespace
1548
1549/// Return the word (e.g. "reg") in Verilog to declare the specified thing.
1550/// If `stripAutomatic` is true, "automatic" is not used even for a declaration
1551/// in a non-procedural region.
1552static StringRef getVerilogDeclWord(Operation *op,
1553 const ModuleEmitter &emitter) {
1554 if (isa<RegOp>(op)) {
1555 // Check if the type stored in this register is a struct or array of
1556 // structs. In this case, according to spec section 6.8, the "reg" prefix
1557 // should be left off.
1558 auto elementType =
1559 cast<InOutType>(op->getResult(0).getType()).getElementType();
1560 // Unwrap arrays. Since packed arrays cannot contain unpacked arrays, we can
1561 // unpack unpacked arrays first.
1562 while (auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(elementType))
1563 elementType = arrayType.getElementType();
1564 while (auto arrayType = hw::type_dyn_cast<ArrayType>(elementType))
1565 elementType = arrayType.getElementType();
1566
1567 if (isa<StructType, UnionType, EnumType, TypeAliasType>(elementType))
1568 return "";
1569
1570 return "reg";
1571 }
1572 if (isa<sv::WireOp>(op))
1573 return "wire";
1574 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1575 return "localparam";
1576
1577 // Interfaces instances use the name of the declared interface.
1578 if (auto interface = dyn_cast<InterfaceInstanceOp>(op))
1579 return interface.getInterfaceType().getInterface().getValue();
1580
1581 // If 'op' is in a module, output 'wire'. If 'op' is in a procedural block,
1582 // fall through to default.
1583 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1584
1585 // If this decl is within a function, "automatic" is not needed because
1586 // "automatic" is added to its definition.
1587 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1588
1589 if (isa<LogicOp>(op)) {
1590 // If the logic op is defined in a procedural region, add 'automatic'
1591 // keyword. If the op has a struct type, 'logic' keyword is already emitted
1592 // within a struct type definition (e.g. struct packed {logic foo;}). So we
1593 // should not emit extra 'logic'.
1594 bool hasStruct = hasStructType(op->getResult(0).getType());
1595 if (isProcedural && !stripAutomatic)
1596 return hasStruct ? "automatic" : "automatic logic";
1597 return hasStruct ? "" : "logic";
1598 }
1599
1600 if (!isProcedural)
1601 return "wire";
1602
1603 if (stripAutomatic)
1604 return hasStructType(op->getResult(0).getType()) ? "" : "logic";
1605
1606 // "automatic" values aren't allowed in disallowLocalVariables mode.
1607 assert(!emitter.state.options.disallowLocalVariables &&
1608 "automatic variables not allowed");
1609
1610 // If the type contains a struct type, we have to use only "automatic" because
1611 // "automatic struct" is syntactically correct.
1612 return hasStructType(op->getResult(0).getType()) ? "automatic"
1613 : "automatic logic";
1614}
1615
1616//===----------------------------------------------------------------------===//
1617// Methods for formatting types.
1618
1619/// Emit a single dimension.
1620static void emitDim(Attribute width, raw_ostream &os, Location loc,
1621 ModuleEmitter &emitter, bool downTo) {
1622 if (!width) {
1623 os << "<<invalid type>>";
1624 return;
1625 }
1626 if (auto intAttr = dyn_cast<IntegerAttr>(width)) {
1627 if (intAttr.getValue().isZero()) {
1628 os << "/*Zero Width*/";
1629 } else {
1630 os << '[';
1631 if (!downTo)
1632 os << "0:";
1633 os << (intAttr.getValue().getZExtValue() - 1);
1634 if (downTo)
1635 os << ":0";
1636 os << ']';
1637 }
1638 return;
1639 }
1640
1641 // Otherwise it must be a parameterized dimension. Shove the "-1" into the
1642 // attribute so it gets printed in canonical form.
1643 auto typedAttr = dyn_cast<TypedAttr>(width);
1644 if (!typedAttr) {
1645 emitter.emitError(loc, "untyped dimension attribute ") << width;
1646 return;
1647 }
1648 auto negOne =
1649 getIntAttr(loc.getContext(), typedAttr.getType(),
1650 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L, true));
1651 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1652 os << '[';
1653 if (!downTo)
1654 os << "0:";
1655 emitter.printParamValue(width, os, [loc, &emitter]() {
1656 return emitter.emitError(loc, "invalid parameter in type");
1657 });
1658 if (downTo)
1659 os << ":0";
1660 os << ']';
1661}
1662
1663/// Emit a list of packed dimensions.
1664static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1665 ModuleEmitter &emitter) {
1666 for (Attribute width : dims) {
1667 emitDim(width, os, loc, emitter, /*downTo=*/true);
1668 }
1669}
1670
1671/// Emit a type's packed dimensions.
1672void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1673 SmallVector<Attribute, 4> dims;
1674 getTypeDims(dims, type, loc,
1675 [&](Location loc) { return this->emitError(loc); });
1676 emitDims(dims, os, loc, *this);
1677}
1678
1679/// Return a 2-state integer atom type name if the width matches. See Spec 6.8
1680/// Variable declarations.
1681static StringRef getTwoStateIntegerAtomType(size_t width) {
1682 switch (width) {
1683 case 8:
1684 return "byte";
1685 case 16:
1686 return "shortint";
1687 case 32:
1688 return "int";
1689 case 64:
1690 return "longint";
1691 default:
1692 return "";
1693 }
1694}
1695
1696/// Output the basic type that consists of packed and primitive types. This is
1697/// those to the left of the name in verilog. implicitIntType controls whether
1698/// to print a base type for (logic) for inteters or whether the caller will
1699/// have handled this (with logic, wire, reg, etc).
1700/// optionalAliasType can be provided to perform any necessary alias-aware
1701/// printing of 'type'.
1702///
1703/// Returns true when anything was printed out.
1704// NOLINTBEGIN(misc-no-recursion)
1705static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc,
1706 SmallVectorImpl<Attribute> &dims,
1707 bool implicitIntType, bool singleBitDefaultType,
1708 ModuleEmitter &emitter,
1709 Type optionalAliasType = {},
1710 bool emitAsTwoStateType = false) {
1711 return TypeSwitch<Type, bool>(type)
1712 .Case<IntegerType>([&](IntegerType integerType) -> bool {
1713 if (emitAsTwoStateType && dims.empty()) {
1714 auto typeName = getTwoStateIntegerAtomType(integerType.getWidth());
1715 if (!typeName.empty()) {
1716 os << typeName;
1717 return true;
1718 }
1719 }
1720 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1721 dims.push_back(
1722 getInt32Attr(type.getContext(), integerType.getWidth()));
1723
1724 StringRef typeName =
1725 (emitAsTwoStateType ? "bit" : (implicitIntType ? "" : "logic"));
1726 if (!typeName.empty()) {
1727 os << typeName;
1728 if (!dims.empty())
1729 os << ' ';
1730 }
1731
1732 emitDims(dims, os, loc, emitter);
1733 return !dims.empty() || !implicitIntType;
1734 })
1735 .Case<IntType>([&](IntType intType) {
1736 if (!implicitIntType)
1737 os << "logic ";
1738 dims.push_back(intType.getWidth());
1739 emitDims(dims, os, loc, emitter);
1740 return true;
1741 })
1742 .Case<ArrayType>([&](ArrayType arrayType) {
1743 dims.push_back(arrayType.getSizeAttr());
1744 return printPackedTypeImpl(arrayType.getElementType(), os, loc, dims,
1745 implicitIntType, singleBitDefaultType,
1746 emitter, /*optionalAliasType=*/{},
1747 emitAsTwoStateType);
1748 })
1749 .Case<InOutType>([&](InOutType inoutType) {
1750 return printPackedTypeImpl(inoutType.getElementType(), os, loc, dims,
1751 implicitIntType, singleBitDefaultType,
1752 emitter, /*optionalAliasType=*/{},
1753 emitAsTwoStateType);
1754 })
1755 .Case<EnumType>([&](EnumType enumType) {
1756 assert(enumType.getBitWidth().has_value() &&
1757 "enum type must have bitwidth");
1758 os << "enum ";
1759 if (enumType.getBitWidth() != 32)
1760 os << "bit [" << *enumType.getBitWidth() - 1 << ":0] ";
1761 os << "{";
1762 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1763 llvm::interleaveComma(
1764 enumType.getFields().getAsRange<StringAttr>(), os,
1765 [&](auto enumerator) {
1766 os << emitter.fieldNameResolver.getEnumFieldName(
1767 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1768 });
1769 os << "}";
1770 return true;
1771 })
1772 .Case<StructType>([&](StructType structType) {
1773 if (structType.getElements().empty() || isZeroBitType(structType)) {
1774 os << "/*Zero Width*/";
1775 return true;
1776 }
1777 os << "struct packed {";
1778 for (auto &element : structType.getElements()) {
1779 if (isZeroBitType(element.type)) {
1780 os << "/*" << emitter.getVerilogStructFieldName(element.name)
1781 << ": Zero Width;*/ ";
1782 continue;
1783 }
1784 SmallVector<Attribute, 8> structDims;
1785 printPackedTypeImpl(stripUnpackedTypes(element.type), os, loc,
1786 structDims,
1787 /*implicitIntType=*/false,
1788 /*singleBitDefaultType=*/true, emitter,
1789 /*optionalAliasType=*/{}, emitAsTwoStateType);
1790 os << ' ' << emitter.getVerilogStructFieldName(element.name);
1791 emitter.printUnpackedTypePostfix(element.type, os);
1792 os << "; ";
1793 }
1794 os << '}';
1795 emitDims(dims, os, loc, emitter);
1796 return true;
1797 })
1798 .Case<UnionType>([&](UnionType unionType) {
1799 if (unionType.getElements().empty() || isZeroBitType(unionType)) {
1800 os << "/*Zero Width*/";
1801 return true;
1802 }
1803
1804 int64_t unionWidth = hw::getBitWidth(unionType);
1805 os << "union packed {";
1806 for (auto &element : unionType.getElements()) {
1807 if (isZeroBitType(element.type)) {
1808 os << "/*" << emitter.getVerilogStructFieldName(element.name)
1809 << ": Zero Width;*/ ";
1810 continue;
1811 }
1812 int64_t elementWidth = hw::getBitWidth(element.type);
1813 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1814 if (needsPadding) {
1815 os << " struct packed {";
1816 if (element.offset) {
1817 os << (emitAsTwoStateType ? "bit" : "logic") << " ["
1818 << element.offset - 1 << ":0] "
1819 << "__pre_padding_" << element.name.getValue() << "; ";
1820 }
1821 }
1822
1823 SmallVector<Attribute, 8> structDims;
1825 stripUnpackedTypes(element.type), os, loc, structDims,
1826 /*implicitIntType=*/false,
1827 /*singleBitDefaultType=*/true, emitter, {}, emitAsTwoStateType);
1828 os << ' ' << emitter.getVerilogStructFieldName(element.name);
1829 emitter.printUnpackedTypePostfix(element.type, os);
1830 os << ";";
1831
1832 if (needsPadding) {
1833 if (elementWidth + (int64_t)element.offset < unionWidth) {
1834 os << " " << (emitAsTwoStateType ? "bit" : "logic") << " ["
1835 << unionWidth - (elementWidth + element.offset) - 1 << ":0] "
1836 << "__post_padding_" << element.name.getValue() << ";";
1837 }
1838 os << "} " << emitter.getVerilogStructFieldName(element.name)
1839 << ";";
1840 }
1841 }
1842 os << '}';
1843 emitDims(dims, os, loc, emitter);
1844 return true;
1845 })
1846
1847 .Case<InterfaceType>([](InterfaceType ifaceType) { return false; })
1848 .Case<ModportType>([&](ModportType modportType) {
1849 auto modportAttr = modportType.getModport();
1850 os << modportAttr.getRootReference().getValue() << "."
1851 << modportAttr.getNestedReferences().front().getValue();
1852 return true;
1853 })
1854 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1855 os << "<<unexpected unpacked array>>";
1856 emitter.emitError(loc, "Unexpected unpacked array in packed type ")
1857 << arrayType;
1858 return true;
1859 })
1860 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1861 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1862 if (!typedecl) {
1863 emitter.emitError(loc, "unresolvable type reference");
1864 return false;
1865 }
1866 if (typedecl.getType() != typeRef.getInnerType()) {
1867 emitter.emitError(loc, "declared type did not match aliased type");
1868 return false;
1869 }
1870
1871 os << typedecl.getPreferredName();
1872 emitDims(dims, os, typedecl->getLoc(), emitter);
1873 return true;
1874 })
1875 .Default([&](Type type) {
1876 os << "<<invalid type '" << type << "'>>";
1877 emitter.emitError(loc, "value has an unsupported verilog type ")
1878 << type;
1879 return true;
1880 });
1881}
1882// NOLINTEND(misc-no-recursion)
1883
1884/// Print the specified packed portion of the type to the specified stream,
1885///
1886/// * When `implicitIntType` is false, a "logic" is printed. This is used in
1887/// struct fields and typedefs.
1888/// * When `singleBitDefaultType` is false, single bit values are printed as
1889/// `[0:0]`. This is used in parameter lists.
1890/// * When `emitAsTwoStateType` is true, a "bit" is printed. This is used in
1891/// DPI function import statement.
1892///
1893/// This returns true if anything was printed.
1894bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1895 Type optionalAliasType,
1896 bool implicitIntType,
1897 bool singleBitDefaultType,
1898 bool emitAsTwoStateType) {
1899 SmallVector<Attribute, 8> packedDimensions;
1900 return printPackedTypeImpl(type, os, loc, packedDimensions, implicitIntType,
1901 singleBitDefaultType, *this, optionalAliasType,
1902 emitAsTwoStateType);
1903}
1904
1905/// Output the unpacked array dimensions. This is the part of the type that is
1906/// to the right of the name.
1907// NOLINTBEGIN(misc-no-recursion)
1908void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1909 TypeSwitch<Type, void>(type)
1910 .Case<InOutType>([&](InOutType inoutType) {
1911 printUnpackedTypePostfix(inoutType.getElementType(), os);
1912 })
1913 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1914 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1915 : state.designOp->getLoc();
1916 emitDim(arrayType.getSizeAttr(), os, loc, *this,
1917 /*downTo=*/false);
1918 printUnpackedTypePostfix(arrayType.getElementType(), os);
1919 })
1920 .Case<sv::UnpackedOpenArrayType>([&](auto arrayType) {
1921 os << "[]";
1922 printUnpackedTypePostfix(arrayType.getElementType(), os);
1923 })
1924 .Case<InterfaceType>([&](auto) {
1925 // Interface instantiations have parentheses like a module with no
1926 // ports.
1927 os << "()";
1928 });
1929}
1930// NOLINTEND(misc-no-recursion)
1931
1932//===----------------------------------------------------------------------===//
1933// Methods for formatting parameters.
1934
1935/// Prints a parameter attribute expression in a Verilog compatible way to the
1936/// specified stream. This returns the precedence of the generated string.
1937SubExprInfo
1938ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1939 function_ref<InFlightDiagnostic()> emitError) {
1940 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1941 emitError);
1942}
1943
1944/// Helper that prints a parameter constant value in a Verilog compatible way.
1945/// This returns the precedence of the generated string.
1946// NOLINTBEGIN(misc-no-recursion)
1947SubExprInfo
1948ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1949 VerilogPrecedence parenthesizeIfLooserThan,
1950 function_ref<InFlightDiagnostic()> emitError) {
1951 if (auto intAttr = dyn_cast<IntegerAttr>(value)) {
1952 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1953 APInt value = intAttr.getValue();
1954
1955 // We omit the width specifier if the value is <= 32-bits in size, which
1956 // makes this more compatible with unknown width extmodules.
1957 if (intTy.getWidth() > 32) {
1958 // Sign comes out before any width specifier.
1959 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1960 os << '-';
1961 value = -value;
1962 }
1963 if (intTy.isSigned())
1964 os << intTy.getWidth() << "'sd";
1965 else
1966 os << intTy.getWidth() << "'d";
1967 }
1968 value.print(os, intTy.isSigned());
1969 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1970 }
1971 if (auto strAttr = dyn_cast<StringAttr>(value)) {
1972 os << '"';
1973 os.write_escaped(strAttr.getValue());
1974 os << '"';
1975 return {Symbol, IsUnsigned};
1976 }
1977 if (auto fpAttr = dyn_cast<FloatAttr>(value)) {
1978 // TODO: relying on float printing to be precise is not a good idea.
1979 os << fpAttr.getValueAsDouble();
1980 return {Symbol, IsUnsigned};
1981 }
1982 if (auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1983 os << verbatimParam.getValue().getValue();
1984 return {Symbol, IsUnsigned};
1985 }
1986 if (auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1987 // Get the name of this parameter (in case it got renamed).
1988 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1989 parameterRef.getName());
1990
1991 // TODO: Should we support signed parameters?
1992 return {Symbol, IsUnsigned};
1993 }
1994
1995 // Handle nested expressions.
1996 auto expr = dyn_cast<ParamExprAttr>(value);
1997 if (!expr) {
1998 os << "<<UNKNOWN MLIRATTR: " << value << ">>";
1999 emitError() << " = " << value;
2000 return {LowestPrecedence, IsUnsigned};
2001 }
2002
2003 StringRef operatorStr;
2004 StringRef openStr, closeStr;
2005 VerilogPrecedence subprecedence = LowestPrecedence;
2006 VerilogPrecedence prec; // precedence of the emitted expression.
2007 std::optional<SubExprSignResult> operandSign;
2008 bool isUnary = false;
2009 bool hasOpenClose = false;
2010
2011 switch (expr.getOpcode()) {
2012 case PEO::Add:
2013 operatorStr = " + ";
2014 subprecedence = Addition;
2015 break;
2016 case PEO::Mul:
2017 operatorStr = " * ";
2018 subprecedence = Multiply;
2019 break;
2020 case PEO::And:
2021 operatorStr = " & ";
2022 subprecedence = And;
2023 break;
2024 case PEO::Or:
2025 operatorStr = " | ";
2026 subprecedence = Or;
2027 break;
2028 case PEO::Xor:
2029 operatorStr = " ^ ";
2030 subprecedence = Xor;
2031 break;
2032 case PEO::Shl:
2033 operatorStr = " << ";
2034 subprecedence = Shift;
2035 break;
2036 case PEO::ShrU:
2037 // >> in verilog is always a logical shift even if operands are signed.
2038 operatorStr = " >> ";
2039 subprecedence = Shift;
2040 break;
2041 case PEO::ShrS:
2042 // >>> in verilog is an arithmetic shift if both operands are signed.
2043 operatorStr = " >>> ";
2044 subprecedence = Shift;
2045 operandSign = IsSigned;
2046 break;
2047 case PEO::DivU:
2048 operatorStr = " / ";
2049 subprecedence = Multiply;
2050 operandSign = IsUnsigned;
2051 break;
2052 case PEO::DivS:
2053 operatorStr = " / ";
2054 subprecedence = Multiply;
2055 operandSign = IsSigned;
2056 break;
2057 case PEO::ModU:
2058 operatorStr = " % ";
2059 subprecedence = Multiply;
2060 operandSign = IsUnsigned;
2061 break;
2062 case PEO::ModS:
2063 operatorStr = " % ";
2064 subprecedence = Multiply;
2065 operandSign = IsSigned;
2066 break;
2067 case PEO::CLog2:
2068 openStr = "$clog2(";
2069 closeStr = ")";
2070 operandSign = IsUnsigned;
2071 hasOpenClose = true;
2072 prec = Symbol;
2073 break;
2074 case PEO::StrConcat:
2075 openStr = "{";
2076 closeStr = "}";
2077 hasOpenClose = true;
2078 operatorStr = ", ";
2079 // We don't have Concat precedence, but it's lowest anyway. (SV Table 11-2).
2080 subprecedence = LowestPrecedence;
2081 prec = Symbol;
2082 break;
2083 }
2084 if (!hasOpenClose)
2085 prec = subprecedence;
2086
2087 // unary -> one element.
2088 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2089 // one element -> {unary || open/close}.
2090 assert(isUnary || hasOpenClose ||
2091 !llvm::hasSingleElement(expr.getOperands()));
2092
2093 // Emit the specified operand with a $signed() or $unsigned() wrapper around
2094 // it if context requires a specific signedness to compute the right value.
2095 // This returns true if the operand is signed.
2096 // TODO: This could try harder to omit redundant casts like the mainline
2097 // expression emitter.
2098 auto emitOperand = [&](Attribute operand) -> bool {
2099 // If surrounding with signed/unsigned, inner expr doesn't need parens.
2100 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2101 if (operandSign.has_value())
2102 os << (*operandSign == IsSigned ? "$signed(" : "$unsigned(");
2103 auto signedness =
2104 printParamValue(operand, os, subprec, emitError).signedness;
2105 if (operandSign.has_value()) {
2106 os << ')';
2107 signedness = *operandSign;
2108 }
2109 return signedness == IsSigned;
2110 };
2111
2112 // Check outer precedence, wrap in parentheses if needed.
2113 if (prec > parenthesizeIfLooserThan)
2114 os << '(';
2115
2116 // Emit opening portion of the operation.
2117 if (hasOpenClose)
2118 os << openStr;
2119 else if (isUnary)
2120 os << operatorStr;
2121
2122 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2123 for (auto op : expr.getOperands().drop_front()) {
2124 // Handle the special case of (a + b + -42) as (a + b - 42).
2125 // TODO: Also handle (a + b + x*-1).
2126 if (expr.getOpcode() == PEO::Add) {
2127 if (auto integer = dyn_cast<IntegerAttr>(op)) {
2128 const APInt &value = integer.getValue();
2129 if (value.isNegative() && !value.isMinSignedValue()) {
2130 os << " - ";
2131 allOperandsSigned &=
2132 emitOperand(IntegerAttr::get(op.getType(), -value));
2133 continue;
2134 }
2135 }
2136 }
2137
2138 os << operatorStr;
2139 allOperandsSigned &= emitOperand(op);
2140 }
2141 if (hasOpenClose)
2142 os << closeStr;
2143 if (prec > parenthesizeIfLooserThan) {
2144 os << ')';
2145 prec = Selection;
2146 }
2147 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2148}
2149// NOLINTEND(misc-no-recursion)
2150
2151//===----------------------------------------------------------------------===//
2152// Expression Emission
2153//===----------------------------------------------------------------------===//
2154
2155namespace {
2156/// This builds a recursively nested expression from an SSA use-def graph. This
2157/// uses a post-order walk, but it needs to obey precedence and signedness
2158/// constraints that depend on the behavior of the child nodes.
2159/// To handle this, we must buffer all output so we can insert parentheses
2160/// and other things if we find out that it was needed later.
2161// NOLINTBEGIN(misc-no-recursion)
2162class ExprEmitter : public EmitterBase,
2163 public TypeOpVisitor<ExprEmitter, SubExprInfo>,
2164 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2165 public sv::Visitor<ExprEmitter, SubExprInfo> {
2166public:
2167 /// Create an ExprEmitter for the specified module emitter, and keeping track
2168 /// of any emitted expressions in the specified set.
2169 ExprEmitter(ModuleEmitter &emitter,
2170 SmallPtrSetImpl<Operation *> &emittedExprs)
2171 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2172
2173 ExprEmitter(ModuleEmitter &emitter,
2174 SmallPtrSetImpl<Operation *> &emittedExprs,
2175 BufferingPP::BufferVec &tokens)
2176 : EmitterBase(emitter.state), emitter(emitter),
2177 emittedExprs(emittedExprs), buffer(tokens),
2178 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2179 assert(state.pp.getListener() == &state.saver);
2180 }
2181
2182 /// Emit the specified value as an expression. If this is an inline-emitted
2183 /// expression, we emit that expression, otherwise we emit a reference to the
2184 /// already computed name.
2185 ///
2186 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2187 bool isAssignmentLikeContext) {
2188 assert(localTokens.empty());
2189 // Wrap to this column.
2190 ps.scopedBox(PP::ibox0, [&]() {
2191 // Require unsigned in an assignment context since every wire is
2192 // declared as unsigned.
2193 emitSubExpr(exp, parenthesizeIfLooserThan,
2194 /*signRequirement*/
2195 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2196 /*isSelfDeterminedUnsignedValue*/ false,
2197 isAssignmentLikeContext);
2198 });
2199 // If we are not using an external token buffer provided through the
2200 // constructor, but we're using the default `ExprEmitter`-scoped buffer,
2201 // flush it.
2202 if (&buffer.tokens == &localTokens)
2203 buffer.flush(state.pp);
2204 }
2205
2206private:
2207 friend class TypeOpVisitor<ExprEmitter, SubExprInfo>;
2208 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2209 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2210
2211 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2212
2213 /// Emit the specified value `exp` as a subexpression to the stream. The
2214 /// `parenthesizeIfLooserThan` parameter indicates when parentheses should be
2215 /// added aroun the subexpression. The `signReq` flag can cause emitSubExpr
2216 /// to emit a subexpression that is guaranteed to be signed or unsigned, and
2217 /// the `isSelfDeterminedUnsignedValue` flag indicates whether the value is
2218 /// known to be have "self determined" width, allowing us to omit extensions.
2219 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2220 SubExprSignRequirement signReq = NoRequirement,
2221 bool isSelfDeterminedUnsignedValue = false,
2222 bool isAssignmentLikeContext = false);
2223
2224 /// Emit SystemVerilog attributes attached to the expression op as dialect
2225 /// attributes.
2226 void emitSVAttributes(Operation *op);
2227
2228 SubExprInfo visitUnhandledExpr(Operation *op);
2229 SubExprInfo visitInvalidComb(Operation *op) {
2230 return dispatchTypeOpVisitor(op);
2231 }
2232 SubExprInfo visitUnhandledComb(Operation *op) {
2233 return visitUnhandledExpr(op);
2234 }
2235 SubExprInfo visitInvalidTypeOp(Operation *op) {
2236 return dispatchSVVisitor(op);
2237 }
2238 SubExprInfo visitUnhandledTypeOp(Operation *op) {
2239 return visitUnhandledExpr(op);
2240 }
2241 SubExprInfo visitUnhandledSV(Operation *op) { return visitUnhandledExpr(op); }
2242
2243 /// These are flags that control `emitBinary`.
2244 enum EmitBinaryFlags {
2245 EB_RequireSignedOperands = RequireSigned, /* 0x1*/
2246 EB_RequireUnsignedOperands = RequireUnsigned, /* 0x2*/
2247 EB_OperandSignRequirementMask = 0x3,
2248
2249 /// This flag indicates that the RHS operand is an unsigned value that has
2250 /// "self determined" width. This means that we can omit explicit zero
2251 /// extensions from it, and don't impose a sign on it.
2252 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2253
2254 /// This flag indicates that the result should be wrapped in a $signed(x)
2255 /// expression to force the result to signed.
2256 EB_ForceResultSigned = 0x8,
2257 };
2258
2259 /// Emit a binary expression. The "emitBinaryFlags" are a bitset from
2260 /// EmitBinaryFlags.
2261 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2262 const char *syntax, unsigned emitBinaryFlags = 0);
2263
2264 SubExprInfo emitUnary(Operation *op, const char *syntax,
2265 bool resultAlwaysUnsigned = false);
2266
2267 /// Emit the specified value as a subexpression, wrapping in an ibox2.
2268 void emitSubExprIBox2(
2269 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2270 ps.scopedBox(PP::ibox2,
2271 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2272 }
2273
2274 /// Emit a range of values separated by commas and a breakable space.
2275 /// Each value is emitted by invoking `eachFn`.
2276 template <typename Container, typename EachFn>
2277 void interleaveComma(const Container &c, EachFn eachFn) {
2278 llvm::interleave(c, eachFn, [&]() { ps << "," << PP::space; });
2279 }
2280
2281 /// Emit a range of values separated by commas and a breakable space.
2282 /// Each value is emitted in an ibox2.
2283 void interleaveComma(ValueRange ops) {
2284 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2285 }
2286
2287 /// Emit an array-literal-like structure, separated by commas.
2288 /// Use callbacks to emit open tokens, closing tokens, and handle each value.
2289 /// If it fits, will be emitted on a single line with no space between
2290 /// list and surrounding open and close.
2291 /// Otherwise, each item is placed on its own line.
2292 /// This has property that if any element requires breaking, all elements
2293 /// are emitted on separate lines (with open/close attached to first/last).
2294 /// `{a + b, x + y, c}`
2295 /// OR
2296 /// ```
2297 /// {a + b,
2298 /// x + y,
2299 /// c}
2300 /// ```
2301 template <typename Container, typename OpenFunc, typename CloseFunc,
2302 typename EachFunc>
2303 void emitBracedList(const Container &c, OpenFunc openFn, EachFunc eachFn,
2304 CloseFunc closeFn) {
2305 openFn();
2306 ps.scopedBox(PP::cbox0, [&]() {
2307 interleaveComma(c, eachFn);
2308 closeFn();
2309 });
2310 }
2311
2312 /// Emit braced list of values surrounded by specified open/close.
2313 template <typename OpenFunc, typename CloseFunc>
2314 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2315 return emitBracedList(
2316 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2317 }
2318
2319 /// Emit braced list of values surrounded by `{` and `}`.
2320 void emitBracedList(ValueRange ops) {
2321 return emitBracedList(
2322 ops, [&]() { ps << "{"; }, [&]() { ps << "}"; });
2323 }
2324
2325 /// Print an APInt constant.
2326 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2327
2328 /// Print a constant array.
2329 void printConstantArray(ArrayAttr elementValues, Type elementType,
2330 bool printAsPattern, Operation *op);
2331 /// Print a constant struct.
2332 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2333 ArrayAttr fieldValues, bool printAsPattern,
2334 Operation *op);
2335 /// Print an aggregate array or struct constant as the given type.
2336 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2337
2338 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2339 SubExprInfo visitSV(GetModportOp op);
2340 SubExprInfo visitSV(SystemFunctionOp op);
2341 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2342 SubExprInfo visitSV(XMROp op);
2343 SubExprInfo visitSV(SFormatFOp op);
2344 SubExprInfo visitSV(XMRRefOp op);
2345 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2346 SubExprInfo visitSV(VerbatimExprOp op) {
2347 return visitVerbatimExprOp(op, op.getSymbols());
2348 }
2349 SubExprInfo visitSV(VerbatimExprSEOp op) {
2350 return visitVerbatimExprOp(op, op.getSymbols());
2351 }
2352 SubExprInfo visitSV(MacroRefExprOp op);
2353 SubExprInfo visitSV(MacroRefExprSEOp op);
2354 template <typename MacroTy>
2355 SubExprInfo emitMacroCall(MacroTy op);
2356
2357 SubExprInfo visitSV(ConstantXOp op);
2358 SubExprInfo visitSV(ConstantZOp op);
2359 SubExprInfo visitSV(ConstantStrOp op);
2360 SubExprInfo visitSV(ConcatStrOp op);
2361
2362 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2363 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2364 // Cast op is noop.
2365 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2366 }
2367
2368 // Noop cast operators.
2369 SubExprInfo visitSV(ReadInOutOp op) {
2370 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2371 emitSVAttributes(op);
2372 return result;
2373 }
2374 SubExprInfo visitSV(ArrayIndexInOutOp op);
2375 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2376 SubExprInfo visitSV(IndexedPartSelectOp op);
2377 SubExprInfo visitSV(StructFieldInOutOp op);
2378
2379 // Sampled value functions
2380 SubExprInfo visitSV(SampledOp op);
2381
2382 // Time system functions
2383 SubExprInfo visitSV(TimeOp op);
2384 SubExprInfo visitSV(STimeOp op);
2385
2386 // Other
2387 using TypeOpVisitor::visitTypeOp;
2388 SubExprInfo visitTypeOp(ConstantOp op);
2389 SubExprInfo visitTypeOp(AggregateConstantOp op);
2390 SubExprInfo visitTypeOp(BitcastOp op);
2391 SubExprInfo visitTypeOp(ParamValueOp op);
2392 SubExprInfo visitTypeOp(ArraySliceOp op);
2393 SubExprInfo visitTypeOp(ArrayGetOp op);
2394 SubExprInfo visitTypeOp(ArrayCreateOp op);
2395 SubExprInfo visitTypeOp(ArrayConcatOp op);
2396 SubExprInfo visitTypeOp(StructCreateOp op);
2397 SubExprInfo visitTypeOp(StructExtractOp op);
2398 SubExprInfo visitTypeOp(StructInjectOp op);
2399 SubExprInfo visitTypeOp(UnionCreateOp op);
2400 SubExprInfo visitTypeOp(UnionExtractOp op);
2401 SubExprInfo visitTypeOp(EnumCmpOp op);
2402 SubExprInfo visitTypeOp(EnumConstantOp op);
2403
2404 // Comb Dialect Operations
2405 using CombinationalVisitor::visitComb;
2406 SubExprInfo visitComb(MuxOp op);
2407 SubExprInfo visitComb(ReverseOp op);
2408 SubExprInfo visitComb(AddOp op) {
2409 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2410 return emitBinary(op, Addition, "+");
2411 }
2412 SubExprInfo visitComb(SubOp op) { return emitBinary(op, Addition, "-"); }
2413 SubExprInfo visitComb(MulOp op) {
2414 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2415 return emitBinary(op, Multiply, "*");
2416 }
2417 SubExprInfo visitComb(DivUOp op) {
2418 return emitBinary(op, Multiply, "/", EB_RequireUnsignedOperands);
2419 }
2420 SubExprInfo visitComb(DivSOp op) {
2421 return emitBinary(op, Multiply, "/",
2422 EB_RequireSignedOperands | EB_ForceResultSigned);
2423 }
2424 SubExprInfo visitComb(ModUOp op) {
2425 return emitBinary(op, Multiply, "%", EB_RequireUnsignedOperands);
2426 }
2427 SubExprInfo visitComb(ModSOp op) {
2428 return emitBinary(op, Multiply, "%",
2429 EB_RequireSignedOperands | EB_ForceResultSigned);
2430 }
2431 SubExprInfo visitComb(ShlOp op) {
2432 return emitBinary(op, Shift, "<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2433 }
2434 SubExprInfo visitComb(ShrUOp op) {
2435 // >> in Verilog is always an unsigned right shift.
2436 return emitBinary(op, Shift, ">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2437 }
2438 SubExprInfo visitComb(ShrSOp op) {
2439 // >>> is only an arithmetic shift right when both operands are signed.
2440 // Otherwise it does a logical shift.
2441 return emitBinary(op, Shift, ">>>",
2442 EB_RequireSignedOperands | EB_ForceResultSigned |
2443 EB_RHS_UnsignedWithSelfDeterminedWidth);
2444 }
2445 SubExprInfo visitComb(AndOp op) {
2446 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2447 return emitBinary(op, And, "&");
2448 }
2449 SubExprInfo visitComb(OrOp op) {
2450 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2451 return emitBinary(op, Or, "|");
2452 }
2453 SubExprInfo visitComb(XorOp op) {
2454 if (op.isBinaryNot())
2455 return emitUnary(op, "~");
2456 assert(op.getNumOperands() == 2 && "prelowering should handle variadics");
2457 return emitBinary(op, Xor, "^");
2458 }
2459
2460 // SystemVerilog spec 11.8.1: "Reduction operator results are unsigned,
2461 // regardless of the operands."
2462 SubExprInfo visitComb(ParityOp op) { return emitUnary(op, "^", true); }
2463
2464 SubExprInfo visitComb(ReplicateOp op);
2465 SubExprInfo visitComb(ConcatOp op);
2466 SubExprInfo visitComb(ExtractOp op);
2467 SubExprInfo visitComb(ICmpOp op);
2468
2469 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2470 auto d = emitOpError(op, "must be printed as assignment pattern, but is "
2471 "not printed within an assignment-like context");
2472 d.attachNote() << "this is likely a bug in PrepareForEmission, which is "
2473 "supposed to spill such expressions";
2474 return d;
2475 }
2476
2477 SubExprInfo printStructCreate(
2478 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2479 llvm::function_ref<void(const hw::detail::FieldInfo &, unsigned)> fieldFn,
2480 bool printAsPattern, Operation *op);
2481
2482public:
2483 ModuleEmitter &emitter;
2484
2485private:
2486 /// This is set (before a visit method is called) if emitSubExpr would
2487 /// prefer to get an output of a specific sign. This is a hint to cause the
2488 /// visitor to change its emission strategy, but the visit method can ignore
2489 /// it without a correctness problem.
2490 SubExprSignRequirement signPreference = NoRequirement;
2491
2492 /// Keep track of all operations emitted within this subexpression for
2493 /// location information tracking.
2494 SmallPtrSetImpl<Operation *> &emittedExprs;
2495
2496 /// Tokens buffered for inserting casts/parens after emitting children.
2497 SmallVector<Token> localTokens;
2498
2499 /// Stores tokens until told to flush. Uses provided buffer (tokens).
2500 BufferingPP buffer;
2501
2502 /// Stream to emit expressions into, will add to buffer.
2504
2505 /// Tracks whether the expression being emitted is currently within an
2506 /// assignment-like context. Certain constructs such as `'{...}` assignment
2507 /// patterns are restricted to only appear in assignment-like contexts.
2508 /// Others, like packed struct and array constants, can be printed as either
2509 /// `{...}` concatenation or `'{...}` assignment pattern, depending on whether
2510 /// they appear within an assignment-like context or not.
2511 bool isAssignmentLikeContext = false;
2512};
2513} // end anonymous namespace
2514
2515SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2516 const char *syntax,
2517 unsigned emitBinaryFlags) {
2518 if (hasSVAttributes(op))
2519 emitError(op, "SV attributes emission is unimplemented for the op");
2520
2521 // It's tempting to wrap expressions in groups as we emit them,
2522 // but that can cause bad wrapping as-is:
2523 // add(a, add(b, add(c, add(d, e))))
2524 // ->
2525 // group(a + (group(b + group(c + group(d + e)))))
2526 // Which will break after 'a +' first.
2527 // TODO: Build tree capturing precedence/fixity at same level, group those!
2528 // Maybe like: https://www.tweag.io/blog/2022-02-10-ormolu-and-operators/ .
2529 // For now, only group within punctuation, such as parens + braces.
2530 if (emitBinaryFlags & EB_ForceResultSigned)
2531 ps << "$signed(" << PP::ibox0;
2532 auto operandSignReq =
2533 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2534 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2535 // Bit of a kludge: if this is a comparison, don't break on either side.
2536 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2537 // Use non-breaking space between op and RHS so breaking is consistent.
2538 ps << lhsSpace << syntax << PP::nbsp; // PP::space;
2539
2540 // Right associative operators are already generally variadic, we need to
2541 // handle things like: (a<4> == b<4>) == (c<3> == d<3>). When processing the
2542 // top operation of the tree, the rhs needs parens. When processing
2543 // known-reassociative operators like +, ^, etc we don't need parens.
2544 // TODO: MLIR should have general "Associative" trait.
2545 auto rhsPrec = prec;
2546 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2547 rhsPrec = VerilogPrecedence(prec - 1);
2548
2549 // If the RHS operand has self-determined width and always treated as
2550 // unsigned, inform emitSubExpr of this. This is true for the shift amount in
2551 // a shift operation.
2552 bool rhsIsUnsignedValueWithSelfDeterminedWidth = false;
2553 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2554 rhsIsUnsignedValueWithSelfDeterminedWidth = true;
2555 operandSignReq = NoRequirement;
2556 }
2557
2558 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2559 rhsIsUnsignedValueWithSelfDeterminedWidth);
2560
2561 // SystemVerilog 11.8.1 says that the result of a binary expression is signed
2562 // only if both operands are signed.
2563 SubExprSignResult signedness = IsUnsigned;
2564 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2565 signedness = IsSigned;
2566
2567 if (emitBinaryFlags & EB_ForceResultSigned) {
2568 ps << PP::end << ")";
2569 signedness = IsSigned;
2570 prec = Selection;
2571 }
2572
2573 return {prec, signedness};
2574}
2575
2576SubExprInfo ExprEmitter::emitUnary(Operation *op, const char *syntax,
2577 bool resultAlwaysUnsigned) {
2578 if (hasSVAttributes(op))
2579 emitError(op, "SV attributes emission is unimplemented for the op");
2580
2581 ps << syntax;
2582 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2583 // For reduction operators "&" and "|", make precedence lowest to avoid
2584 // emitting an expression like `a & &b`, which is syntactically valid but some
2585 // tools produce LINT warnings.
2586 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2587 resultAlwaysUnsigned ? IsUnsigned : signedness};
2588}
2589
2590/// Emit SystemVerilog attributes attached to the expression op as dialect
2591/// attributes.
2592void ExprEmitter::emitSVAttributes(Operation *op) {
2593 // SystemVerilog 2017 Section 5.12.
2594 auto svAttrs = getSVAttributes(op);
2595 if (!svAttrs)
2596 return;
2597
2598 // For now, no breaks for attributes.
2599 ps << PP::nbsp;
2600 emitSVAttributesImpl(ps, svAttrs, /*mayBreak=*/false);
2601}
2602
2603/// If the specified extension is a zero extended version of another value,
2604/// return the shorter value, otherwise return null.
2605static Value isZeroExtension(Value value) {
2606 auto concat = value.getDefiningOp<ConcatOp>();
2607 if (!concat || concat.getNumOperands() != 2)
2608 return {};
2609
2610 auto constant = concat.getOperand(0).getDefiningOp<ConstantOp>();
2611 if (constant && constant.getValue().isZero())
2612 return concat.getOperand(1);
2613 return {};
2614}
2615
2616/// Emit the specified value `exp` as a subexpression to the stream. The
2617/// `parenthesizeIfLooserThan` parameter indicates when parentheses should be
2618/// added aroun the subexpression. The `signReq` flag can cause emitSubExpr
2619/// to emit a subexpression that is guaranteed to be signed or unsigned, and
2620/// the `isSelfDeterminedUnsignedValue` flag indicates whether the value is
2621/// known to be have "self determined" width, allowing us to omit extensions.
2622SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2623 VerilogPrecedence parenthesizeIfLooserThan,
2624 SubExprSignRequirement signRequirement,
2625 bool isSelfDeterminedUnsignedValue,
2626 bool isAssignmentLikeContext) {
2627 // `verif.contract` ops act as no-ops.
2628 if (auto result = dyn_cast<OpResult>(exp))
2629 if (auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2630 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2631 parenthesizeIfLooserThan, signRequirement,
2632 isSelfDeterminedUnsignedValue,
2633 isAssignmentLikeContext);
2634
2635 // If this is a self-determined unsigned value, look through any inline zero
2636 // extensions. This occurs on the RHS of a shift operation for example.
2637 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2638 if (auto smaller = isZeroExtension(exp))
2639 exp = smaller;
2640 }
2641
2642 auto *op = exp.getDefiningOp();
2643 bool shouldEmitInlineExpr = op && isVerilogExpression(op);
2644
2645 // If this is a non-expr or shouldn't be done inline, just refer to its name.
2646 if (!shouldEmitInlineExpr) {
2647 // All wires are declared as unsigned, so if the client needed it signed,
2648 // emit a conversion.
2649 if (signRequirement == RequireSigned) {
2650 ps << "$signed(" << PPExtString(getVerilogValueName(exp)) << ")";
2651 return {Symbol, IsSigned};
2652 }
2653
2654 ps << PPExtString(getVerilogValueName(exp));
2655 return {Symbol, IsUnsigned};
2656 }
2657
2658 unsigned subExprStartIndex = buffer.tokens.size();
2659 if (op)
2660 ps.addCallback({op, true});
2661 llvm::scope_exit done([&]() {
2662 if (op)
2663 ps.addCallback({op, false});
2664 });
2665
2666 // Inform the visit method about the preferred sign we want from the result.
2667 // It may choose to ignore this, but some emitters can change behavior based
2668 // on contextual desired sign.
2669 signPreference = signRequirement;
2670
2671 bool bitCastAdded = false;
2672 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2673 if (auto inType =
2674 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2675 ps.addAsString(inType.getWidth());
2676 ps << "'(" << PP::ibox0;
2677 bitCastAdded = true;
2678 }
2679 // Okay, this is an expression we should emit inline. Do this through our
2680 // visitor.
2681 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2682 isAssignmentLikeContext);
2683 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2684
2685 // Check cases where we have to insert things before the expression now that
2686 // we know things about it.
2687 auto addPrefix = [&](StringToken &&t) {
2688 // insert {Prefix, ibox0}.
2689 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2690 BeginToken(0));
2691 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2692 };
2693 auto closeBoxAndParen = [&]() { ps << PP::end << ")"; };
2694 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2695 addPrefix(StringToken("$signed("));
2696 closeBoxAndParen();
2697 expInfo.signedness = IsSigned;
2698 expInfo.precedence = Selection;
2699 } else if (signRequirement == RequireUnsigned &&
2700 expInfo.signedness == IsSigned) {
2701 addPrefix(StringToken("$unsigned("));
2702 closeBoxAndParen();
2703 expInfo.signedness = IsUnsigned;
2704 expInfo.precedence = Selection;
2705 } else if (expInfo.precedence > parenthesizeIfLooserThan) {
2706 // If this subexpression would bind looser than the expression it is bound
2707 // into, then we need to parenthesize it. Insert the parentheses
2708 // retroactively.
2709 addPrefix(StringToken("("));
2710 closeBoxAndParen();
2711 // Reset the precedence to the () level.
2712 expInfo.precedence = Selection;
2713 }
2714 if (bitCastAdded) {
2715 closeBoxAndParen();
2716 }
2717
2718 // Remember that we emitted this.
2719 emittedExprs.insert(exp.getDefiningOp());
2720 return expInfo;
2721}
2722
2723SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2724 auto openFn = [&]() {
2725 ps << "{";
2726 ps.addAsString(op.getMultiple());
2727 ps << "{";
2728 };
2729 auto closeFn = [&]() { ps << "}}"; };
2730
2731 // If the subexpression is an inline concat, we can emit it as part of the
2732 // replicate.
2733 if (auto concatOp = op.getOperand().getDefiningOp<ConcatOp>()) {
2734 if (op.getOperand().hasOneUse()) {
2735 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2736 return {Symbol, IsUnsigned};
2737 }
2738 }
2739 emitBracedList(op.getOperand(), openFn, closeFn);
2740 return {Symbol, IsUnsigned};
2741}
2742
2743SubExprInfo ExprEmitter::visitComb(ConcatOp op) {
2744 emitBracedList(op.getOperands());
2745 return {Symbol, IsUnsigned};
2746}
2747
2748SubExprInfo ExprEmitter::visitTypeOp(BitcastOp op) {
2749 // NOTE: Bitcasts are emitted out-of-line with their own wire declaration when
2750 // their dimensions don't match. SystemVerilog uses the wire declaration to
2751 // know what type this value is being casted to.
2752 Type toType = op.getType();
2753 if (!haveMatchingDims(
2754 toType, op.getInput().getType(), op.getLoc(),
2755 [&](Location loc) { return emitter.emitError(loc, ""); })) {
2756 ps << "/*cast(bit";
2757 ps.invokeWithStringOS(
2758 [&](auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2759 ps << ")*/";
2760 }
2761 return emitSubExpr(op.getInput(), LowestPrecedence);
2762}
2763
2764SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2765 const char *symop[] = {"==", "!=", "<", "<=", ">", ">=", "<",
2766 "<=", ">", ">=", "===", "!==", "==?", "!=?"};
2767 SubExprSignRequirement signop[] = {
2768 // Equality
2769 NoRequirement, NoRequirement,
2770 // Signed Comparisons
2771 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2772 // Unsigned Comparisons
2773 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2774 // Weird Comparisons
2775 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2776
2777 auto pred = static_cast<uint64_t>(op.getPredicate());
2778 assert(pred < sizeof(symop) / sizeof(symop[0]));
2779
2780 // Lower "== -1" to Reduction And.
2781 if (op.isEqualAllOnes())
2782 return emitUnary(op, "&", true);
2783
2784 // Lower "!= 0" to Reduction Or.
2785 if (op.isNotEqualZero())
2786 return emitUnary(op, "|", true);
2787
2788 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2789
2790 // SystemVerilog 11.8.1: "Comparison... operator results are unsigned,
2791 // regardless of the operands".
2792 result.signedness = IsUnsigned;
2793 return result;
2794}
2795
2796SubExprInfo ExprEmitter::visitComb(ExtractOp op) {
2797 if (hasSVAttributes(op))
2798 emitError(op, "SV attributes emission is unimplemented for the op");
2799
2800 unsigned loBit = op.getLowBit();
2801 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2802
2803 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2804 assert((x.precedence == Symbol ||
2805 (x.precedence == Selection && isOkToBitSelectFrom(op.getInput()))) &&
2806 "should be handled by isExpressionUnableToInline");
2807
2808 // If we're extracting the whole input, just return it. This is valid but
2809 // non-canonical IR, and we don't want to generate invalid Verilog.
2810 if (loBit == 0 &&
2811 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2812 return x;
2813
2814 ps << "[";
2815 ps.addAsString(hiBit);
2816 if (hiBit != loBit) { // Emit x[4] instead of x[4:4].
2817 ps << ":";
2818 ps.addAsString(loBit);
2819 }
2820 ps << "]";
2821 return {Unary, IsUnsigned};
2822}
2823
2824SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2825 if (hasSVAttributes(op))
2826 emitError(op, "SV attributes emission is unimplemented for the op");
2827
2828 auto decl = op.getReferencedDecl(state.symbolCache);
2829 ps << PPExtString(getVerilogValueName(op.getIface())) << "."
2830 << PPExtString(getSymOpName(decl));
2831 return {Selection, IsUnsigned};
2832}
2833
2834SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2835 if (hasSVAttributes(op))
2836 emitError(op, "SV attributes emission is unimplemented for the op");
2837
2838 ps << "$" << PPExtString(op.getFnName()) << "(";
2839 ps.scopedBox(PP::ibox0, [&]() {
2840 llvm::interleave(
2841 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2842 [&]() { ps << "," << PP::space; });
2843 ps << ")";
2844 });
2845 return {Symbol, IsUnsigned};
2846}
2847
2848SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2849 if (hasSVAttributes(op))
2850 emitError(op, "SV attributes emission is unimplemented for the op");
2851
2852 auto decl = op.getReferencedDecl(state.symbolCache);
2853
2854 ps << PPExtString(getVerilogValueName(op.getIface())) << "."
2855 << PPExtString(getSymOpName(decl));
2856 return {Selection, IsUnsigned};
2857}
2858
2859SubExprInfo ExprEmitter::visitSV(XMROp op) {
2860 if (hasSVAttributes(op))
2861 emitError(op, "SV attributes emission is unimplemented for the op");
2862
2863 if (op.getIsRooted())
2864 ps << "$root.";
2865 for (auto s : op.getPath())
2866 ps << PPExtString(cast<StringAttr>(s).getValue()) << ".";
2867 ps << PPExtString(op.getTerminal());
2868 return {Selection, IsUnsigned};
2869}
2870
2871// TODO: This shares a lot of code with the getNameRemotely mtehod. Combine
2872// these to share logic.
2873SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2874 if (hasSVAttributes(op))
2875 emitError(op, "SV attributes emission is unimplemented for the op");
2876
2877 // The XMR is pointing at a GlobalRef.
2878 auto globalRef = op.getReferencedPath(&state.symbolCache);
2879 auto namepath = globalRef.getNamepathAttr().getValue();
2880 auto *module = state.symbolCache.getDefinition(
2881 cast<InnerRefAttr>(namepath.front()).getModule());
2882 ps << PPExtString(getSymOpName(module));
2883 for (auto sym : namepath) {
2884 ps << ".";
2885 auto innerRef = cast<InnerRefAttr>(sym);
2886 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2887 innerRef.getName());
2888 if (ref.hasPort()) {
2889 ps << PPExtString(getPortVerilogName(ref.getOp(), ref.getPort()));
2890 continue;
2891 }
2892 ps << PPExtString(getSymOpName(ref.getOp()));
2893 }
2894 auto leaf = op.getVerbatimSuffixAttr();
2895 if (leaf && leaf.size())
2896 ps << PPExtString(leaf);
2897 return {Selection, IsUnsigned};
2898}
2899
2900SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2901 if (hasSVAttributes(op))
2902 emitError(op, "SV attributes emission is unimplemented for the op");
2903
2904 emitTextWithSubstitutions(
2905 ps, op->getAttrOfType<StringAttr>("format_string").getValue(), op,
2906 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2907
2908 return {Unary, IsUnsigned};
2909}
2910
2911template <typename MacroTy>
2912SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2913 if (hasSVAttributes(op))
2914 emitError(op, "SV attributes emission is unimplemented for the op");
2915
2916 // Use the specified name or the symbol name as appropriate.
2917 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2918 assert(macroOp && "Invalid IR");
2919 StringRef name =
2920 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2921 ps << "`" << PPExtString(name);
2922 if (!op.getInputs().empty()) {
2923 ps << "(";
2924 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2925 emitExpression(val, LowestPrecedence, /*isAssignmentLikeContext=*/false);
2926 });
2927 ps << ")";
2928 }
2929 return {LowestPrecedence, IsUnsigned};
2930}
2931
2932SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2933 return emitMacroCall(op);
2934}
2935
2936SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2937 return emitMacroCall(op);
2938}
2939
2940SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2941 if (hasSVAttributes(op))
2942 emitError(op, "SV attributes emission is unimplemented for the op");
2943
2944 ps.addAsString(op.getWidth());
2945 ps << "'bx";
2946 return {Unary, IsUnsigned};
2947}
2948
2949SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2950 if (hasSVAttributes(op))
2951 emitError(op, "SV attributes emission is unimplemented for the op");
2952
2953 ps.writeQuotedEscaped(op.getStr());
2954 return {Symbol, IsUnsigned}; // is a string unsigned? Yes! SV 5.9
2955}
2956
2957SubExprInfo ExprEmitter::visitSV(ConcatStrOp op) {
2958 if (hasSVAttributes(op))
2959 emitError(op, "SV attributes emission is unimplemented for the op");
2960
2961 // Emits the SystemVerilog concatenation `{a, b, ...}`. Strings are unsigned
2962 // (SV 5.9) and braces bind at the primary/Symbol level.
2963 emitBracedList(op.getInputs());
2964 return {Symbol, IsUnsigned};
2965}
2966
2967SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2968 if (hasSVAttributes(op))
2969 emitError(op, "SV attributes emission is unimplemented for the op");
2970
2971 ps.addAsString(op.getWidth());
2972 ps << "'bz";
2973 return {Unary, IsUnsigned};
2974}
2975
2976SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2977 bool isNegated = false;
2978 // If this is a negative signed number and not MININT (e.g. -128), then print
2979 // it as a negated positive number.
2980 if (signPreference == RequireSigned && value.isNegative() &&
2981 !value.isMinSignedValue()) {
2982 ps << "-";
2983 isNegated = true;
2984 }
2985
2986 ps.addAsString(type.getWidth());
2987 ps << "'";
2988
2989 // Emit this as a signed constant if the caller would prefer that.
2990 if (signPreference == RequireSigned)
2991 ps << "sh";
2992 else
2993 ps << "h";
2994
2995 // Print negated if required.
2996 SmallString<32> valueStr;
2997 if (isNegated) {
2998 (-value).toStringUnsigned(valueStr, 16);
2999 } else {
3000 value.toStringUnsigned(valueStr, 16);
3001 }
3002 ps << valueStr;
3003 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
3004}
3005
3006SubExprInfo ExprEmitter::visitTypeOp(ConstantOp op) {
3007 if (hasSVAttributes(op))
3008 emitError(op, "SV attributes emission is unimplemented for the op");
3009
3010 auto value = op.getValue();
3011 // We currently only allow zero width values to be handled as special cases in
3012 // the various operations that may come across them. If we reached this point
3013 // in the emitter, the value should be considered illegal to emit.
3014 if (value.getBitWidth() == 0) {
3015 emitOpError(op, "will not emit zero width constants in the general case");
3016 ps << "<<unsupported zero width constant: "
3017 << PPExtString(op->getName().getStringRef()) << ">>";
3018 return {Unary, IsUnsigned};
3019 }
3020
3021 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3022}
3023
3024void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type elementType,
3025 bool printAsPattern, Operation *op) {
3026 if (printAsPattern && !isAssignmentLikeContext)
3027 emitAssignmentPatternContextError(op);
3028 StringRef openDelim = printAsPattern ? "'{" : "{";
3029
3030 emitBracedList(
3031 elementValues, [&]() { ps << openDelim; },
3032 [&](Attribute elementValue) {
3033 printConstantAggregate(elementValue, elementType, op);
3034 },
3035 [&]() { ps << "}"; });
3036}
3037
3038void ExprEmitter::printConstantStruct(
3039 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3040 bool printAsPattern, Operation *op) {
3041 if (printAsPattern && !isAssignmentLikeContext)
3042 emitAssignmentPatternContextError(op);
3043
3044 // Only emit elements with non-zero bit width.
3045 // TODO: Ideally we should emit zero bit values as comments, e.g. `{/*a:
3046 // ZeroBit,*/ b: foo, /* c: ZeroBit*/ d: bar}`. However it's tedious to
3047 // nicely emit all edge cases hence currently we just elide zero bit
3048 // values.
3049 auto fieldRange = llvm::make_filter_range(
3050 llvm::zip(fieldInfos, fieldValues), [](const auto &fieldAndValue) {
3051 // Elide zero bit elements.
3052 return !isZeroBitType(std::get<0>(fieldAndValue).type);
3053 });
3054
3055 if (printAsPattern) {
3056 emitBracedList(
3057 fieldRange, [&]() { ps << "'{"; },
3058 [&](const auto &fieldAndValue) {
3059 ps.scopedBox(PP::ibox2, [&]() {
3060 const auto &[field, value] = fieldAndValue;
3061 ps << PPExtString(emitter.getVerilogStructFieldName(field.name))
3062 << ":" << PP::space;
3063 printConstantAggregate(value, field.type, op);
3064 });
3065 },
3066 [&]() { ps << "}"; });
3067 } else {
3068 emitBracedList(
3069 fieldRange, [&]() { ps << "{"; },
3070 [&](const auto &fieldAndValue) {
3071 ps.scopedBox(PP::ibox2, [&]() {
3072 const auto &[field, value] = fieldAndValue;
3073 printConstantAggregate(value, field.type, op);
3074 });
3075 },
3076 [&]() { ps << "}"; });
3077 }
3078}
3079
3080void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3081 Operation *op) {
3082 // Packed arrays can be printed as concatenation or pattern.
3083 if (auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3084 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3085 isAssignmentLikeContext, op);
3086
3087 // Unpacked arrays must be printed as pattern.
3088 if (auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3089 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3090 true, op);
3091
3092 // Packed structs can be printed as concatenation or pattern.
3093 if (auto structType = hw::type_dyn_cast<StructType>(type))
3094 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3095 isAssignmentLikeContext, op);
3096
3097 if (auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3098 auto value = cast<IntegerAttr>(attr).getValue();
3099 printConstantScalar(value, intType);
3100 return;
3101 }
3102
3103 emitOpError(op, "contains constant of type ")
3104 << type << " which cannot be emitted as Verilog";
3105}
3106
3107SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3108 if (hasSVAttributes(op))
3109 emitError(op, "SV attributes emission is unimplemented for the op");
3110
3111 // If the constant op as a whole is zero-width, it is an error.
3112 assert(!isZeroBitType(op.getType()) &&
3113 "zero-bit types not allowed at this point");
3114
3115 printConstantAggregate(op.getFields(), op.getType(), op);
3116 return {Symbol, IsUnsigned};
3117}
3118
3119SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3120 if (hasSVAttributes(op))
3121 emitError(op, "SV attributes emission is unimplemented for the op");
3122
3123 return ps.invokeWithStringOS([&](auto &os) {
3124 return emitter.printParamValue(op.getValue(), os, [&]() {
3125 return op->emitOpError("invalid parameter use");
3126 });
3127 });
3128}
3129
3130// 11.5.1 "Vector bit-select and part-select addressing" allows a '+:' syntax
3131// for slicing operations.
3132SubExprInfo ExprEmitter::visitTypeOp(ArraySliceOp op) {
3133 if (hasSVAttributes(op))
3134 emitError(op, "SV attributes emission is unimplemented for the op");
3135
3136 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3137
3138 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3139 ps << "[";
3140 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3141 ps << " +: ";
3142 ps.addAsString(dstWidth);
3143 ps << "]";
3144 return {Selection, arrayPrec.signedness};
3145}
3146
3147SubExprInfo ExprEmitter::visitTypeOp(ArrayGetOp op) {
3148 emitSubExpr(op.getInput(), Selection);
3149 ps << "[";
3150 if (isZeroBitType(op.getIndex().getType()))
3152 else
3153 emitSubExpr(op.getIndex(), LowestPrecedence);
3154 ps << "]";
3155 emitSVAttributes(op);
3156 return {Selection, IsUnsigned};
3157}
3158
3159// Syntax from: section 5.11 "Array literals".
3160SubExprInfo ExprEmitter::visitTypeOp(ArrayCreateOp op) {
3161 if (hasSVAttributes(op))
3162 emitError(op, "SV attributes emission is unimplemented for the op");
3163
3164 if (op.isUniform()) {
3165 ps << "{";
3166 ps.addAsString(op.getInputs().size());
3167 ps << "{";
3168 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3169 ps << "}}";
3170 } else {
3171 emitBracedList(
3172 op.getInputs(), [&]() { ps << "{"; },
3173 [&](Value v) {
3174 ps << "{";
3175 emitSubExprIBox2(v);
3176 ps << "}";
3177 },
3178 [&]() { ps << "}"; });
3179 }
3180 return {Unary, IsUnsigned};
3181}
3182
3183SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3184 if (hasSVAttributes(op))
3185 emitError(op, "SV attributes emission is unimplemented for the op");
3186
3187 emitBracedList(
3188 llvm::reverse(op.getInputs()), [&]() { ps << "'{"; },
3189 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps << "}"; });
3190 return {Unary, IsUnsigned};
3191}
3192
3193SubExprInfo ExprEmitter::visitTypeOp(ArrayConcatOp op) {
3194 if (hasSVAttributes(op))
3195 emitError(op, "SV attributes emission is unimplemented for the op");
3196
3197 emitBracedList(op.getOperands());
3198 return {Unary, IsUnsigned};
3199}
3200
3201SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3202 if (hasSVAttributes(op))
3203 emitError(op, "SV attributes emission is unimplemented for the op");
3204
3205 auto index = op.getIndex();
3206 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3207 ps << "[";
3208 if (isZeroBitType(index.getType()))
3210 else
3211 emitSubExpr(index, LowestPrecedence);
3212 ps << "]";
3213 return {Selection, arrayPrec.signedness};
3214}
3215
3216SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3217 if (hasSVAttributes(op))
3218 emitError(op, "SV attributes emission is unimplemented for the op");
3219
3220 auto prec = emitSubExpr(op.getInput(), Selection);
3221 ps << "[";
3222 emitSubExpr(op.getBase(), LowestPrecedence);
3223 if (op.getDecrement())
3224 ps << " -: ";
3225 else
3226 ps << " +: ";
3227 ps.addAsString(op.getWidth());
3228 ps << "]";
3229 return {Selection, prec.signedness};
3230}
3231
3232SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3233 if (hasSVAttributes(op))
3234 emitError(op, "SV attributes emission is unimplemented for the op");
3235
3236 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3237 ps << "[";
3238 emitSubExpr(op.getBase(), LowestPrecedence);
3239 if (op.getDecrement())
3240 ps << " -: ";
3241 else
3242 ps << " +: ";
3243 ps.addAsString(op.getWidth());
3244 ps << "]";
3245 return info;
3246}
3247
3248SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3249 if (hasSVAttributes(op))
3250 emitError(op, "SV attributes emission is unimplemented for the op");
3251
3252 auto prec = emitSubExpr(op.getInput(), Selection);
3253 ps << "."
3254 << PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3255 return {Selection, prec.signedness};
3256}
3257
3258SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3259 if (hasSVAttributes(op))
3260 emitError(op, "SV attributes emission is unimplemented for the op");
3261
3262 ps << "$sampled(";
3263 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3264 ps << ")";
3265 return info;
3266}
3267
3268SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3269 if (hasSVAttributes(op))
3270 emitError(op, "SV attributes emission is unimplemented for the op");
3271
3272 ps << "$sformatf(";
3273 ps.scopedBox(PP::ibox0, [&]() {
3274 ps.writeQuotedEscaped(op.getFormatString());
3275 // TODO: if any of these breaks, it'd be "nice" to break
3276 // after the comma, instead of:
3277 // $sformatf("...", a + b,
3278 // longexpr_goes
3279 // + here, c);
3280 // (without forcing breaking between all elements, like braced list)
3281 for (auto operand : op.getSubstitutions()) {
3282 ps << "," << PP::space;
3283 emitSubExpr(operand, LowestPrecedence);
3284 }
3285 });
3286 ps << ")";
3287 return {Symbol, IsUnsigned};
3288}
3289
3290SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3291 if (hasSVAttributes(op))
3292 emitError(op, "SV attributes emission is unimplemented for the op");
3293
3294 ps << "$time";
3295 return {Symbol, IsUnsigned};
3296}
3297
3298SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3299 if (hasSVAttributes(op))
3300 emitError(op, "SV attributes emission is unimplemented for the op");
3301
3302 ps << "$stime";
3303 return {Symbol, IsUnsigned};
3304}
3305
3306SubExprInfo ExprEmitter::visitComb(MuxOp op) {
3307 // The ?: operator is right associative.
3308
3309 // Layout:
3310 // cond ? a : b
3311 // (long
3312 // + cond) ? a : b
3313 // long
3314 // + cond
3315 // ? a : b
3316 // long
3317 // + cond
3318 // ? a
3319 // : b
3320 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3321 ps.scopedBox(PP::ibox0, [&]() {
3322 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3323 });
3324 ps << BreakToken(1, 2);
3325 ps << "?";
3326 emitSVAttributes(op);
3327 ps << " ";
3328 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3329 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3330 });
3331 ps << BreakToken(1, 2) << ": ";
3332
3333 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3334 return emitSubExpr(op.getFalseValue(), Conditional);
3335 });
3336
3337 SubExprSignResult signedness = IsUnsigned;
3338 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3339 signedness = IsSigned;
3340
3341 return {Conditional, signedness};
3342 });
3343}
3344
3345SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3346 if (hasSVAttributes(op))
3347 emitError(op, "SV attributes emission is unimplemented for the op");
3348
3349 ps << "{<<{";
3350 emitSubExpr(op.getInput(), LowestPrecedence);
3351 ps << "}}";
3352
3353 return {Symbol, IsUnsigned};
3354}
3355
3356SubExprInfo ExprEmitter::printStructCreate(
3357 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3358 llvm::function_ref<void(const hw::detail::FieldInfo &, unsigned)> fieldFn,
3359 bool printAsPattern, Operation *op) {
3360 if (printAsPattern && !isAssignmentLikeContext)
3361 emitAssignmentPatternContextError(op);
3362
3363 // Elide zero bit elements.
3364 auto filteredFields = llvm::make_filter_range(
3365 llvm::enumerate(fieldInfos),
3366 [](const auto &field) { return !isZeroBitType(field.value().type); });
3367
3368 if (printAsPattern) {
3369 emitBracedList(
3370 filteredFields, [&]() { ps << "'{"; },
3371 [&](const auto &field) {
3372 ps.scopedBox(PP::ibox2, [&]() {
3373 ps << PPExtString(
3374 emitter.getVerilogStructFieldName(field.value().name))
3375 << ":" << PP::space;
3376 fieldFn(field.value(), field.index());
3377 });
3378 },
3379 [&]() { ps << "}"; });
3380 } else {
3381 emitBracedList(
3382 filteredFields, [&]() { ps << "{"; },
3383 [&](const auto &field) {
3384 ps.scopedBox(PP::ibox2,
3385 [&]() { fieldFn(field.value(), field.index()); });
3386 },
3387 [&]() { ps << "}"; });
3388 }
3389
3390 return {Selection, IsUnsigned};
3391}
3392
3393SubExprInfo ExprEmitter::visitTypeOp(StructCreateOp op) {
3394 if (hasSVAttributes(op))
3395 emitError(op, "SV attributes emission is unimplemented for the op");
3396
3397 // TODO: For unpacked structs, once we have support for them, `printAsPattern`
3398 // should be set to true.
3399 bool printAsPattern = isAssignmentLikeContext;
3400 StructType structType = op.getType();
3401 return printStructCreate(
3402 structType.getElements(),
3403 [&](const auto &field, auto index) {
3404 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3405 /*isSelfDeterminedUnsignedValue=*/false,
3406 /*isAssignmentLikeContext=*/isAssignmentLikeContext);
3407 },
3408 printAsPattern, op);
3409}
3410
3411SubExprInfo ExprEmitter::visitTypeOp(StructExtractOp op) {
3412 if (hasSVAttributes(op))
3413 emitError(op, "SV attributes emission is unimplemented for the op");
3414
3415 emitSubExpr(op.getInput(), Selection);
3416 ps << "."
3417 << PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3418 return {Selection, IsUnsigned};
3419}
3420
3421SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3422 if (hasSVAttributes(op))
3423 emitError(op, "SV attributes emission is unimplemented for the op");
3424
3425 // TODO: For unpacked structs, once we have support for them, `printAsPattern`
3426 // should be set to true.
3427 bool printAsPattern = isAssignmentLikeContext;
3428 StructType structType = op.getType();
3429 return printStructCreate(
3430 structType.getElements(),
3431 [&](const auto &field, auto index) {
3432 if (field.name == op.getFieldNameAttr()) {
3433 emitSubExpr(op.getNewValue(), Selection);
3434 } else {
3435 emitSubExpr(op.getInput(), Selection);
3436 ps << "."
3437 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3438 }
3439 },
3440 printAsPattern, op);
3441}
3442
3443SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3444 ps << PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3445 return {Selection, IsUnsigned};
3446}
3447
3448SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3449 if (hasSVAttributes(op))
3450 emitError(op, "SV attributes emission is unimplemented for the op");
3451 auto result = emitBinary(op, Comparison, "==", NoRequirement);
3452 // SystemVerilog 11.8.1: "Comparison... operator results are unsigned,
3453 // regardless of the operands".
3454 result.signedness = IsUnsigned;
3455 return result;
3456}
3457
3458SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3459 if (hasSVAttributes(op))
3460 emitError(op, "SV attributes emission is unimplemented for the op");
3461
3462 // Check if this union type has been padded.
3463 auto unionType = cast<UnionType>(getCanonicalType(op.getType()));
3464 auto unionWidth = hw::getBitWidth(unionType);
3465 auto &element = unionType.getElements()[op.getFieldIndex()];
3466 auto elementWidth = hw::getBitWidth(element.type);
3467
3468 // If the element is 0 width, just fill the union with 0s.
3469 if (!elementWidth) {
3470 ps.addAsString(unionWidth);
3471 ps << "'h0";
3472 return {Unary, IsUnsigned};
3473 }
3474
3475 // If the element has no padding, emit it directly.
3476 if (elementWidth == unionWidth) {
3477 emitSubExpr(op.getInput(), LowestPrecedence);
3478 return {Unary, IsUnsigned};
3479 }
3480
3481 // Emit the value as a bitconcat, supplying 0 for the padding bits.
3482 ps << "{";
3483 ps.scopedBox(PP::ibox0, [&]() {
3484 if (auto prePadding = element.offset) {
3485 ps.addAsString(prePadding);
3486 ps << "'h0," << PP::space;
3487 }
3488 emitSubExpr(op.getInput(), Selection);
3489 if (auto postPadding = unionWidth - elementWidth - element.offset) {
3490 ps << "," << PP::space;
3491 ps.addAsString(postPadding);
3492 ps << "'h0";
3493 }
3494 ps << "}";
3495 });
3496
3497 return {Unary, IsUnsigned};
3498}
3499
3500SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3501 if (hasSVAttributes(op))
3502 emitError(op, "SV attributes emission is unimplemented for the op");
3503 emitSubExpr(op.getInput(), Selection);
3504
3505 // Check if this union type has been padded.
3506 auto unionType = cast<UnionType>(getCanonicalType(op.getInput().getType()));
3507 auto unionWidth = hw::getBitWidth(unionType);
3508 auto &element = unionType.getElements()[op.getFieldIndex()];
3509 auto elementWidth = hw::getBitWidth(element.type);
3510 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3511 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3512
3513 // If the element needs padding then we need to get the actual element out
3514 // of an anonymous structure.
3515 if (needsPadding)
3516 ps << "." << PPExtString(verilogFieldName);
3517
3518 // Get the correct member from the union.
3519 ps << "." << PPExtString(verilogFieldName);
3520 return {Selection, IsUnsigned};
3521}
3522
3523SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3524 emitOpError(op, "cannot emit this expression to Verilog");
3525 ps << "<<unsupported expr: " << PPExtString(op->getName().getStringRef())
3526 << ">>";
3527 return {Symbol, IsUnsigned};
3528}
3529// NOLINTEND(misc-no-recursion)
3530
3531//===----------------------------------------------------------------------===//
3532// Property Emission
3533//===----------------------------------------------------------------------===//
3534
3535// NOLINTBEGIN(misc-no-recursion)
3536
3537namespace {
3538/// Precedence level of various property and sequence expressions. Lower numbers
3539/// bind tighter.
3540///
3541/// See IEEE 1800-2017 section 16.12 "Declaring properties", specifically table
3542/// 16-3 on "Sequence and property operator precedence and associativity".
3543enum class PropertyPrecedence {
3544 Symbol, // Atomic symbol like `foo` and regular boolean expressions
3545 Repeat, // Sequence `[*]`, `[=]`, `[->]`
3546 Concat, // Sequence `##`
3547 Throughout, // Sequence `throughout`
3548 Within, // Sequence `within`
3549 Intersect, // Sequence `intersect`
3550 Unary, // Property `not`, `nexttime`-like
3551 And, // Sequence and property `and`
3552 Or, // Sequence and property `or`
3553 Iff, // Property `iff`
3554 Until, // Property `until`-like, `implies`
3555 Implication, // Property `|->`, `|=>`, `#-#`, `#=#`
3556 Qualifier, // Property `always`-like, `eventually`-like, `if`, `case`,
3557 // `accept`-like, `reject`-like
3558 Clocking, // `@(...)`, `disable iff` (not specified in the standard)
3559 Lowest, // Sentinel which is always the lowest precedence.
3560};
3561
3562/// Additional information on emitted property and sequence expressions.
3563struct EmittedProperty {
3564 /// The precedence of this expression.
3565 PropertyPrecedence precedence;
3566};
3567
3568/// A helper to emit recursively nested property and sequence expressions for
3569/// SystemVerilog assertions.
3570class PropertyEmitter : public EmitterBase,
3571 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3572public:
3573 /// Create a PropertyEmitter for the specified module emitter, and keeping
3574 /// track of any emitted expressions in the specified set.
3575 PropertyEmitter(ModuleEmitter &emitter,
3576 SmallPtrSetImpl<Operation *> &emittedOps)
3577 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3578 PropertyEmitter(ModuleEmitter &emitter,
3579 SmallPtrSetImpl<Operation *> &emittedOps,
3580 BufferingPP::BufferVec &tokens)
3581 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3582 buffer(tokens),
3583 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3584 assert(state.pp.getListener() == &state.saver);
3585 }
3586
3587 void emitAssertPropertyDisable(
3588 Value property, Value disable,
3589 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3590
3591 void emitAssertPropertyBody(
3592 Value property, Value disable,
3593 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3594
3595 void emitAssertPropertyBody(
3596 Value property, sv::EventControl event, Value clock, Value disable,
3597 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3598
3599private:
3600 /// Emit the specified value as an SVA property or sequence.
3601 EmittedProperty
3602 emitNestedProperty(Value property,
3603 PropertyPrecedence parenthesizeIfLooserThan);
3604 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3605 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3606
3607 EmittedProperty visitUnhandledLTL(Operation *op);
3608 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3609 EmittedProperty visitLTL(ltl::AndOp op);
3610 EmittedProperty visitLTL(ltl::OrOp op);
3611 EmittedProperty visitLTL(ltl::IntersectOp op);
3612 EmittedProperty visitLTL(ltl::DelayOp op);
3613 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3614 EmittedProperty visitLTL(ltl::ConcatOp op);
3615 EmittedProperty visitLTL(ltl::RepeatOp op);
3616 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3617 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3618 EmittedProperty visitLTL(ltl::NotOp op);
3619 EmittedProperty visitLTL(ltl::ImplicationOp op);
3620 EmittedProperty visitLTL(ltl::UntilOp op);
3621 EmittedProperty visitLTL(ltl::EventuallyOp op);
3622 EmittedProperty visitLTL(ltl::ClockOp op);
3623
3624 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3625 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3626 void emitLTLConcat(ValueRange inputs);
3627
3628public:
3629 ModuleEmitter &emitter;
3630
3631private:
3632 /// Keep track of all operations emitted within this subexpression for
3633 /// location information tracking.
3634 SmallPtrSetImpl<Operation *> &emittedOps;
3635
3636 /// Tokens buffered for inserting casts/parens after emitting children.
3637 SmallVector<Token> localTokens;
3638
3639 /// Stores tokens until told to flush. Uses provided buffer (tokens).
3640 BufferingPP buffer;
3641
3642 /// Stream to emit expressions into, will add to buffer.
3644};
3645} // end anonymous namespace
3646
3647// Emits a disable signal and its containing property.
3648// This function can be called from withing another emission process in which
3649// case we don't need to check that the local tokens are empty.
3650void PropertyEmitter::emitAssertPropertyDisable(
3651 Value property, Value disable,
3652 PropertyPrecedence parenthesizeIfLooserThan) {
3653 // If the property is tied to a disable, emit that.
3654 if (disable) {
3655 ps << "disable iff" << PP::nbsp << "(";
3656 ps.scopedBox(PP::ibox2, [&] {
3657 emitNestedProperty(disable, PropertyPrecedence::Unary);
3658 ps << ")";
3659 });
3660 ps << PP::space;
3661 }
3662
3663 ps.scopedBox(PP::ibox0,
3664 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3665}
3666
3667// Emits a disable signal and its containing property.
3668// This function can be called from withing another emission process in which
3669// case we don't need to check that the local tokens are empty.
3670void PropertyEmitter::emitAssertPropertyBody(
3671 Value property, Value disable,
3672 PropertyPrecedence parenthesizeIfLooserThan) {
3673 assert(localTokens.empty());
3674
3675 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3676
3677 // If we are not using an external token buffer provided through the
3678 // constructor, but we're using the default `PropertyEmitter`-scoped buffer,
3679 // flush it.
3680 if (&buffer.tokens == &localTokens)
3681 buffer.flush(state.pp);
3682}
3683
3684void PropertyEmitter::emitAssertPropertyBody(
3685 Value property, sv::EventControl event, Value clock, Value disable,
3686 PropertyPrecedence parenthesizeIfLooserThan) {
3687 assert(localTokens.empty());
3688 // Wrap to this column.
3689 ps << "@(";
3690 ps.scopedBox(PP::ibox2, [&] {
3691 ps << PPExtString(stringifyEventControl(event)) << PP::space;
3692 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3693 ps << ")";
3694 });
3695 ps << PP::space;
3696
3697 // Emit the rest of the body
3698 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3699
3700 // If we are not using an external token buffer provided through the
3701 // constructor, but we're using the default `PropertyEmitter`-scoped buffer,
3702 // flush it.
3703 if (&buffer.tokens == &localTokens)
3704 buffer.flush(state.pp);
3705}
3706
3707EmittedProperty PropertyEmitter::emitNestedProperty(
3708 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3709 // Emit the property as a plain expression if it doesn't have a property or
3710 // sequence type, in which case it is just a boolean expression.
3711 //
3712 // We use the `LowestPrecedence` for the boolean expression such that it never
3713 // gets parenthesized. According to IEEE 1800-2017, "the operators described
3714 // in Table 11-2 have higher precedence than the sequence and property
3715 // operators". Therefore any boolean expression behaves just like a
3716 // `PropertyPrecedence::Symbol` and needs no parantheses, which is equivalent
3717 // to `VerilogPrecedence::LowestPrecedence`.
3718 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3719 ExprEmitter(emitter, emittedOps, buffer.tokens)
3720 .emitExpression(property, LowestPrecedence,
3721 /*isAssignmentLikeContext=*/false);
3722 return {PropertyPrecedence::Symbol};
3723 }
3724
3725 unsigned startIndex = buffer.tokens.size();
3726 auto info = dispatchLTLVisitor(property.getDefiningOp());
3727
3728 // If this subexpression would bind looser than the expression it is bound
3729 // into, then we need to parenthesize it. Insert the parentheses
3730 // retroactively.
3731 if (info.precedence > parenthesizeIfLooserThan) {
3732 // Insert {"(", ibox0} before the subexpression.
3733 buffer.tokens.insert(buffer.tokens.begin() + startIndex, BeginToken(0));
3734 buffer.tokens.insert(buffer.tokens.begin() + startIndex, StringToken("("));
3735 // Insert {end, ")" } after the subexpression.
3736 ps << PP::end << ")";
3737 // Reset the precedence level.
3738 info.precedence = PropertyPrecedence::Symbol;
3739 }
3740
3741 // Remember that we emitted this.
3742 emittedOps.insert(property.getDefiningOp());
3743 return info;
3744}
3745
3746EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3747 emitOpError(op, "emission as Verilog property or sequence not supported");
3748 ps << "<<unsupported: " << PPExtString(op->getName().getStringRef()) << ">>";
3749 return {PropertyPrecedence::Symbol};
3750}
3751
3752EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3753 // Emit the boolean constant value as a literal.
3754 ps << (op.getValueAttr().getValue() ? "1'h1" : "1'h0");
3755 return {PropertyPrecedence::Symbol};
3756}
3757
3758EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3759 llvm::interleave(
3760 op.getInputs(),
3761 [&](auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3762 [&]() { ps << PP::space << "and" << PP::nbsp; });
3763 return {PropertyPrecedence::And};
3764}
3765
3766EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3767 llvm::interleave(
3768 op.getInputs(),
3769 [&](auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3770 [&]() { ps << PP::space << "or" << PP::nbsp; });
3771 return {PropertyPrecedence::Or};
3772}
3773
3774EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3775 llvm::interleave(
3776 op.getInputs(),
3777 [&](auto input) {
3778 emitNestedProperty(input, PropertyPrecedence::Intersect);
3779 },
3780 [&]() { ps << PP::space << "intersect" << PP::nbsp; });
3781 return {PropertyPrecedence::Intersect};
3782}
3783
3784void PropertyEmitter::emitLTLDelay(int64_t delay,
3785 std::optional<int64_t> length) {
3786 ps << "##";
3787 if (length) {
3788 if (*length == 0) {
3789 ps.addAsString(delay);
3790 } else {
3791 ps << "[";
3792 ps.addAsString(delay);
3793 ps << ":";
3794 ps.addAsString(delay + *length);
3795 ps << "]";
3796 }
3797 } else {
3798 if (delay == 0) {
3799 ps << "[*]";
3800 } else if (delay == 1) {
3801 ps << "[+]";
3802 } else {
3803 ps << "[";
3804 ps.addAsString(delay);
3805 ps << ":$]";
3806 }
3807 }
3808}
3809
3810void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3811 ps << "@(";
3812 ps.scopedBox(PP::ibox2, [&] {
3813 ps << PPExtString(stringifyClockEdge(edge)) << PP::space;
3814 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3815 ps << ")";
3816 });
3817}
3818
3819EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3820 emitLTLDelay(op.getDelay(), op.getLength());
3821 ps << PP::space;
3822 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3823 return {PropertyPrecedence::Concat};
3824}
3825
3826EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3827 emitLTLClockingEvent(op.getEdge(), op.getClock());
3828 ps << PP::space;
3829 emitLTLDelay(op.getDelay(), op.getLength());
3830 ps << PP::space;
3831 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3832 return {PropertyPrecedence::Clocking};
3833}
3834
3835void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3836 bool addSeparator = false;
3837 for (auto input : inputs) {
3838 if (addSeparator) {
3839 ps << PP::space;
3840 if (!input.getDefiningOp<ltl::DelayOp>())
3841 ps << "##0" << PP::space;
3842 }
3843 addSeparator = true;
3844 emitNestedProperty(input, PropertyPrecedence::Concat);
3845 }
3846}
3847
3848EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3849 emitLTLConcat(op.getInputs());
3850 return {PropertyPrecedence::Concat};
3851}
3852
3853EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3854 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3855 if (auto more = op.getMore()) {
3856 ps << "[*";
3857 ps.addAsString(op.getBase());
3858 if (*more != 0) {
3859 ps << ":";
3860 ps.addAsString(op.getBase() + *more);
3861 }
3862 ps << "]";
3863 } else {
3864 if (op.getBase() == 0) {
3865 ps << "[*]";
3866 } else if (op.getBase() == 1) {
3867 ps << "[+]";
3868 } else {
3869 ps << "[*";
3870 ps.addAsString(op.getBase());
3871 ps << ":$]";
3872 }
3873 }
3874 return {PropertyPrecedence::Repeat};
3875}
3876
3877EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3878 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3879 // More always exists
3880 auto more = op.getMore();
3881 ps << "[->";
3882 ps.addAsString(op.getBase());
3883 if (more != 0) {
3884 ps << ":";
3885 ps.addAsString(op.getBase() + more);
3886 }
3887 ps << "]";
3888
3889 return {PropertyPrecedence::Repeat};
3890}
3891
3892EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3893 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3894 // More always exists
3895 auto more = op.getMore();
3896 ps << "[=";
3897 ps.addAsString(op.getBase());
3898 if (more != 0) {
3899 ps << ":";
3900 ps.addAsString(op.getBase() + more);
3901 }
3902 ps << "]";
3903
3904 return {PropertyPrecedence::Repeat};
3905}
3906
3907EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3908 ps << "not" << PP::space;
3909 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3910 return {PropertyPrecedence::Unary};
3911}
3912
3913/// For a value `concat(..., delay(const(true), 1, 0))`, return `...`. This is
3914/// useful for emitting `(seq ##1 true) |-> prop` as `seq |=> prop`.
3915static ValueRange getNonOverlappingConcatSubrange(Value value) {
3916 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3917 if (!concatOp || concatOp.getInputs().size() < 2)
3918 return {};
3919 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3920 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3921 return {};
3922 auto constOp = delayOp.getInput().getDefiningOp<ConstantOp>();
3923 if (!constOp || !constOp.getValue().isOne())
3924 return {};
3925 return concatOp.getInputs().drop_back();
3926}
3927
3928EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3929 // Emit `(seq ##1 true) |-> prop` as `seq |=> prop`.
3930 if (auto range = getNonOverlappingConcatSubrange(op.getAntecedent());
3931 !range.empty()) {
3932 emitLTLConcat(range);
3933 ps << PP::space << "|=>" << PP::nbsp;
3934 } else {
3935 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3936 ps << PP::space << "|->" << PP::nbsp;
3937 }
3938 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3939 return {PropertyPrecedence::Implication};
3940}
3941
3942EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3943 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3944 ps << PP::space << "until" << PP::space;
3945 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3946 return {PropertyPrecedence::Until};
3947}
3948
3949EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3950 ps << "s_eventually" << PP::space;
3951 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3952 return {PropertyPrecedence::Qualifier};
3953}
3954
3955EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3956 emitLTLClockingEvent(op.getEdge(), op.getClock());
3957 ps << PP::space;
3958 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3959 return {PropertyPrecedence::Clocking};
3960}
3961
3962// NOLINTEND(misc-no-recursion)
3963
3964//===----------------------------------------------------------------------===//
3965// NameCollector
3966//===----------------------------------------------------------------------===//
3967
3968namespace {
3969class NameCollector {
3970public:
3971 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3972
3973 // Scan operations in the specified block, collecting information about
3974 // those that need to be emitted as declarations.
3975 void collectNames(Block &block);
3976
3977 size_t getMaxDeclNameWidth() const { return maxDeclNameWidth; }
3978 size_t getMaxTypeWidth() const { return maxTypeWidth; }
3979
3980private:
3981 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3982 ModuleEmitter &moduleEmitter;
3983
3984 /// Types that are longer than `maxTypeWidthBound` are not added to the
3985 /// `maxTypeWidth` to prevent one single huge type from messing up the
3986 /// alignment of all other declarations.
3987 static constexpr size_t maxTypeWidthBound = 32;
3988};
3989} // namespace
3990
3991// NOLINTNEXTLINE(misc-no-recursion)
3992void NameCollector::collectNames(Block &block) {
3993 // Loop over all of the results of all of the ops. Anything that defines a
3994 // value needs to be noticed.
3995 for (auto &op : block) {
3996 // Instances have an instance name to recognize but we don't need to look
3997 // at the result values since wires used by instances should be traversed
3998 // anyway.
3999 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
4000 op))
4001 continue;
4002 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
4003 continue;
4004
4005 if (!isVerilogExpression(&op)) {
4006 for (auto result : op.getResults()) {
4007 StringRef declName = getVerilogDeclWord(&op, moduleEmitter);
4008 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
4009 SmallString<16> typeString;
4010
4011 // Convert the port's type to a string and measure it.
4012 {
4013 llvm::raw_svector_ostream stringStream(typeString);
4014 moduleEmitter.printPackedType(stripUnpackedTypes(result.getType()),
4015 stringStream, op.getLoc());
4016 }
4017 if (typeString.size() <= maxTypeWidthBound)
4018 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4019 }
4020 }
4021
4022 // Recursively process any regions under the op iff this is a procedural
4023 // #ifdef region: we need to emit automatic logic values at the top of the
4024 // enclosing region.
4025 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4026 for (auto &region : op.getRegions()) {
4027 if (!region.empty())
4028 collectNames(region.front());
4029 }
4030 continue;
4031 }
4032 }
4033}
4034
4035//===----------------------------------------------------------------------===//
4036// StmtEmitter
4037//===----------------------------------------------------------------------===//
4038
4039namespace {
4040/// This emits statement-related operations.
4041// NOLINTBEGIN(misc-no-recursion)
4042class StmtEmitter : public EmitterBase,
4043 public hw::StmtVisitor<StmtEmitter, LogicalResult>,
4044 public sv::Visitor<StmtEmitter, LogicalResult>,
4045 public verif::Visitor<StmtEmitter, LogicalResult> {
4046public:
4047 /// Create an ExprEmitter for the specified module emitter, and keeping track
4048 /// of any emitted expressions in the specified set.
4049 StmtEmitter(ModuleEmitter &emitter, const LoweringOptions &options)
4050 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4051
4052 void emitStatement(Operation *op);
4053 void emitStatementBlock(Block &body);
4054
4055 /// Emit a declaration.
4056 LogicalResult emitDeclaration(Operation *op);
4057
4058private:
4059 void collectNamesAndCalculateDeclarationWidths(Block &block);
4060
4061 void
4062 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4063 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4064 bool isAssignmentLikeContext = false);
4065 void emitSVAttributes(Operation *op);
4066
4067 using hw::StmtVisitor<StmtEmitter, LogicalResult>::visitStmt;
4068 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4069 using verif::Visitor<StmtEmitter, LogicalResult>::visitVerif;
4070 friend class hw::StmtVisitor<StmtEmitter, LogicalResult>;
4071 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4072 friend class verif::Visitor<StmtEmitter, LogicalResult>;
4073
4074 // Visitor methods.
4075 LogicalResult visitUnhandledStmt(Operation *op) { return failure(); }
4076 LogicalResult visitInvalidStmt(Operation *op) { return failure(); }
4077 LogicalResult visitUnhandledSV(Operation *op) { return failure(); }
4078 LogicalResult visitInvalidSV(Operation *op) { return failure(); }
4079 LogicalResult visitUnhandledVerif(Operation *op) { return failure(); }
4080 LogicalResult visitInvalidVerif(Operation *op) { return failure(); }
4081
4082 LogicalResult visitSV(sv::WireOp op) { return emitDeclaration(op); }
4083 LogicalResult visitSV(RegOp op) { return emitDeclaration(op); }
4084 LogicalResult visitSV(LogicOp op) { return emitDeclaration(op); }
4085 LogicalResult visitSV(LocalParamOp op) { return emitDeclaration(op); }
4086 template <typename Op>
4087 LogicalResult
4088 emitAssignLike(Op op, PPExtString syntax,
4089 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4090 void emitAssignLike(llvm::function_ref<void()> emitLHS,
4091 llvm::function_ref<void()> emitRHS, PPExtString syntax,
4092 PPExtString postSyntax = PPExtString(";"),
4093 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4094 LogicalResult visitSV(AssignOp op);
4095 LogicalResult visitSV(BPAssignOp op);
4096 LogicalResult visitSV(PAssignOp op);
4097 LogicalResult visitSV(ForceOp op);
4098 LogicalResult visitSV(ReleaseOp op);
4099 LogicalResult visitSV(AliasOp op);
4100 LogicalResult visitSV(InterfaceInstanceOp op);
4101 LogicalResult emitOutputLikeOp(Operation *op, const ModulePortInfo &ports);
4102 LogicalResult visitStmt(OutputOp op);
4103
4104 LogicalResult visitStmt(InstanceOp op);
4105 void emitInstancePortList(Operation *op, ModulePortInfo &modPortInfo,
4106 ArrayRef<Value> instPortValues);
4107
4108 LogicalResult visitStmt(TypeScopeOp op);
4109 LogicalResult visitStmt(TypedeclOp op);
4110
4111 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4112 LogicalResult visitSV(OrderedOutputOp op);
4113 LogicalResult visitSV(IfDefOp op) { return emitIfDef(op, op.getCond()); }
4114 LogicalResult visitSV(IfDefProceduralOp op) {
4115 return emitIfDef(op, op.getCond());
4116 }
4117 LogicalResult visitSV(IfOp op);
4118 LogicalResult visitSV(AlwaysOp op);
4119 LogicalResult visitSV(AlwaysCombOp op);
4120 LogicalResult visitSV(AlwaysFFOp op);
4121 LogicalResult visitSV(InitialOp op);
4122 LogicalResult visitSV(CaseOp op);
4123 template <typename OpTy, typename EmitPrefixFn>
4124 LogicalResult
4125 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4126 ValueRange substitutions, EmitPrefixFn emitPrefix);
4127 LogicalResult visitSV(WriteOp op);
4128 LogicalResult visitSV(FWriteOp op);
4129 LogicalResult visitSV(FFlushOp op);
4130 LogicalResult visitSV(FCloseOp op);
4131 LogicalResult visitSV(VerbatimOp op);
4132 LogicalResult visitSV(MacroRefOp op);
4133
4134 LogicalResult emitSimulationControlTask(Operation *op, PPExtString taskName,
4135 std::optional<unsigned> verbosity);
4136 LogicalResult visitSV(StopOp op);
4137 LogicalResult visitSV(FinishOp op);
4138 LogicalResult visitSV(ExitOp op);
4139
4140 LogicalResult emitSeverityMessageTask(Operation *op, PPExtString taskName,
4141 std::optional<unsigned> verbosity,
4142 StringAttr message,
4143 ValueRange operands);
4144
4145 // Helper template for nonfatal message operations
4146 template <typename OpTy>
4147 LogicalResult emitNonfatalMessageOp(OpTy op, const char *taskName) {
4148 return emitSeverityMessageTask(op, PPExtString(taskName), {},
4149 op.getMessageAttr(), op.getSubstitutions());
4150 }
4151
4152 // Helper template for fatal message operations
4153 template <typename OpTy>
4154 LogicalResult emitFatalMessageOp(OpTy op) {
4155 return emitSeverityMessageTask(op, PPExtString("$fatal"), op.getVerbosity(),
4156 op.getMessageAttr(), op.getSubstitutions());
4157 }
4158
4159 LogicalResult visitSV(FatalProceduralOp op);
4160 LogicalResult visitSV(FatalOp op);
4161 LogicalResult visitSV(ErrorProceduralOp op);
4162 LogicalResult visitSV(WarningProceduralOp op);
4163 LogicalResult visitSV(InfoProceduralOp op);
4164 LogicalResult visitSV(ErrorOp op);
4165 LogicalResult visitSV(WarningOp op);
4166 LogicalResult visitSV(InfoOp op);
4167
4168 LogicalResult visitSV(ReadMemOp op);
4169
4170 LogicalResult visitSV(GenerateOp op);
4171 LogicalResult visitSV(GenerateCaseOp op);
4172 LogicalResult visitSV(GenerateForOp op);
4173
4174 LogicalResult visitSV(ForOp op);
4175
4176 void emitAssertionLabel(Operation *op);
4177 void emitAssertionMessage(StringAttr message, ValueRange args,
4178 SmallPtrSetImpl<Operation *> &ops,
4179 bool isConcurrent);
4180 template <typename Op>
4181 LogicalResult emitImmediateAssertion(Op op, PPExtString opName);
4182 LogicalResult visitSV(AssertOp op);
4183 LogicalResult visitSV(AssumeOp op);
4184 LogicalResult visitSV(CoverOp op);
4185 template <typename Op>
4186 LogicalResult emitConcurrentAssertion(Op op, PPExtString opName);
4187 LogicalResult visitSV(AssertConcurrentOp op);
4188 LogicalResult visitSV(AssumeConcurrentOp op);
4189 LogicalResult visitSV(CoverConcurrentOp op);
4190 template <typename Op>
4191 LogicalResult emitPropertyAssertion(Op op, PPExtString opName);
4192 LogicalResult visitSV(AssertPropertyOp op);
4193 LogicalResult visitSV(AssumePropertyOp op);
4194 LogicalResult visitSV(CoverPropertyOp op);
4195
4196 LogicalResult visitSV(BindOp op);
4197 LogicalResult visitSV(InterfaceOp op);
4198 LogicalResult visitSV(sv::SVVerbatimSourceOp op);
4199 LogicalResult visitSV(InterfaceSignalOp op);
4200 LogicalResult visitSV(InterfaceModportOp op);
4201 LogicalResult visitSV(AssignInterfaceSignalOp op);
4202 LogicalResult visitSV(MacroErrorOp op);
4203 LogicalResult visitSV(MacroDefOp op);
4204
4205 void emitBlockAsStatement(Block *block,
4206 const SmallPtrSetImpl<Operation *> &locationOps,
4207 StringRef multiLineComment = StringRef());
4208
4209 LogicalResult visitSV(FuncDPIImportOp op);
4210 template <typename CallOp>
4211 LogicalResult emitFunctionCall(CallOp callOp);
4212 LogicalResult visitSV(FuncCallProceduralOp op);
4213 LogicalResult visitSV(FuncCallOp op);
4214 LogicalResult visitSV(ReturnOp op);
4215 LogicalResult visitSV(IncludeOp op);
4216
4217public:
4218 ModuleEmitter &emitter;
4219
4220private:
4221 /// These keep track of the maximum length of name width and type width in the
4222 /// current statement scope.
4223 size_t maxDeclNameWidth = 0;
4224 size_t maxTypeWidth = 0;
4225
4226 const LoweringOptions &options;
4227};
4228
4229} // end anonymous namespace
4230
4231/// Emit the specified value as an expression. If this is an inline-emitted
4232/// expression, we emit that expression, otherwise we emit a reference to the
4233/// already computed name.
4234///
4235void StmtEmitter::emitExpression(Value exp,
4236 SmallPtrSetImpl<Operation *> &emittedExprs,
4237 VerilogPrecedence parenthesizeIfLooserThan,
4238 bool isAssignmentLikeContext) {
4239 ExprEmitter(emitter, emittedExprs)
4240 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4241}
4242
4243/// Emit SystemVerilog attributes attached to the statement op as dialect
4244/// attributes.
4245void StmtEmitter::emitSVAttributes(Operation *op) {
4246 // SystemVerilog 2017 Section 5.12.
4247 auto svAttrs = getSVAttributes(op);
4248 if (!svAttrs)
4249 return;
4250
4251 startStatement(); // For attributes.
4252 emitSVAttributesImpl(ps, svAttrs, /*mayBreak=*/true);
4253 setPendingNewline();
4254}
4255
4256void StmtEmitter::emitAssignLike(llvm::function_ref<void()> emitLHS,
4257 llvm::function_ref<void()> emitRHS,
4258 PPExtString syntax, PPExtString postSyntax,
4259 std::optional<PPExtString> wordBeforeLHS) {
4260 // If wraps, indent.
4261 ps.scopedBox(PP::ibox2, [&]() {
4262 if (wordBeforeLHS) {
4263 ps << *wordBeforeLHS << PP::space;
4264 }
4265 emitLHS();
4266 // Allow breaking before 'syntax' (e.g., '=') if long assignment.
4267 ps << PP::space << syntax << PP::space;
4268 // RHS is boxed to right of the syntax.
4269 ps.scopedBox(PP::ibox0, [&]() {
4270 emitRHS();
4271 ps << postSyntax;
4272 });
4273 });
4274}
4275
4276template <typename Op>
4277LogicalResult
4278StmtEmitter::emitAssignLike(Op op, PPExtString syntax,
4279 std::optional<PPExtString> wordBeforeLHS) {
4280 SmallPtrSet<Operation *, 8> ops;
4281 ops.insert(op);
4282
4283 startStatement();
4284 ps.addCallback({op, true});
4285 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4286 [&]() {
4287 emitExpression(op.getSrc(), ops, LowestPrecedence,
4288 /*isAssignmentLikeContext=*/true);
4289 },
4290 syntax, PPExtString(";"), wordBeforeLHS);
4291
4292 ps.addCallback({op, false});
4293 emitLocationInfoAndNewLine(ops);
4294 return success();
4295}
4296
4297LogicalResult StmtEmitter::visitSV(AssignOp op) {
4298 // prepare assigns wires to instance outputs and function results, but these
4299 // are logically handled in the port binding list when outputing an instance.
4300 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4301 return success();
4302
4303 if (emitter.assignsInlined.count(op))
4304 return success();
4305
4306 // Emit SV attributes. See Spec 12.3.
4307 emitSVAttributes(op);
4308
4309 return emitAssignLike(op, PPExtString("="), PPExtString("assign"));
4310}
4311
4312LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4313 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4314 return success();
4315
4316 // If the assign is emitted into logic declaration, we must not emit again.
4317 if (emitter.assignsInlined.count(op))
4318 return success();
4319
4320 // Emit SV attributes. See Spec 12.3.
4321 emitSVAttributes(op);
4322
4323 return emitAssignLike(op, PPExtString("="));
4324}
4325
4326LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4327 // Emit SV attributes. See Spec 12.3.
4328 emitSVAttributes(op);
4329
4330 return emitAssignLike(op, PPExtString("<="));
4331}
4332
4333LogicalResult StmtEmitter::visitSV(ForceOp op) {
4334 if (hasSVAttributes(op))
4335 emitError(op, "SV attributes emission is unimplemented for the op");
4336
4337 return emitAssignLike(op, PPExtString("="), PPExtString("force"));
4338}
4339
4340LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4341 if (hasSVAttributes(op))
4342 emitError(op, "SV attributes emission is unimplemented for the op");
4343
4344 startStatement();
4345 SmallPtrSet<Operation *, 8> ops;
4346 ops.insert(op);
4347 ps.addCallback({op, true});
4348 ps.scopedBox(PP::ibox2, [&]() {
4349 ps << "release" << PP::space;
4350 emitExpression(op.getDest(), ops);
4351 ps << ";";
4352 });
4353 ps.addCallback({op, false});
4354 emitLocationInfoAndNewLine(ops);
4355 return success();
4356}
4357
4358LogicalResult StmtEmitter::visitSV(AliasOp op) {
4359 if (hasSVAttributes(op))
4360 emitError(op, "SV attributes emission is unimplemented for the op");
4361
4362 startStatement();
4363 SmallPtrSet<Operation *, 8> ops;
4364 ops.insert(op);
4365 ps.addCallback({op, true});
4366 ps.scopedBox(PP::ibox2, [&]() {
4367 ps << "alias" << PP::space;
4368 ps.scopedBox(PP::cbox0, [&]() { // If any breaks, all break.
4369 llvm::interleave(
4370 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4371 [&]() { ps << PP::nbsp << "=" << PP::space; });
4372 ps << ";";
4373 });
4374 });
4375 ps.addCallback({op, false});
4376 emitLocationInfoAndNewLine(ops);
4377 return success();
4378}
4379
4380LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4381 auto doNotPrint = op.getDoNotPrint();
4382 if (doNotPrint && !state.options.emitBindComments)
4383 return success();
4384
4385 if (hasSVAttributes(op))
4386 emitError(op, "SV attributes emission is unimplemented for the op");
4387
4388 startStatement();
4389 StringRef prefix = "";
4390 ps.addCallback({op, true});
4391 if (doNotPrint) {
4392 prefix = "// ";
4393 ps << "// This interface is elsewhere emitted as a bind statement."
4394 << PP::newline;
4395 }
4396
4397 SmallPtrSet<Operation *, 8> ops;
4398 ops.insert(op);
4399
4400 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4401 assert(interfaceOp && "InterfaceInstanceOp has invalid symbol that does not "
4402 "point to an interface");
4403
4404 auto verilogName = getSymOpName(interfaceOp);
4405 if (!prefix.empty())
4406 ps << PPExtString(prefix);
4407 ps << PPExtString(verilogName)
4408 << PP::nbsp /* don't break, may be comment line */
4409 << PPExtString(op.getName()) << "();";
4410
4411 ps.addCallback({op, false});
4412 emitLocationInfoAndNewLine(ops);
4413
4414 return success();
4415}
4416
4417/// For OutputOp and ReturnOp we put "assign" statements at the end of the
4418/// Verilog module or function respectively to assign outputs to intermediate
4419/// wires.
4420LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4421 const ModulePortInfo &ports) {
4422 SmallPtrSet<Operation *, 8> ops;
4423 size_t operandIndex = 0;
4424 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4425 for (PortInfo port : ports.getOutputs()) {
4426 auto operand = op->getOperand(operandIndex);
4427 // Outputs that are set by the output port of an instance are handled
4428 // directly when the instance is emitted.
4429 // Keep synced with countStatements() and visitStmt(InstanceOp).
4430 if (operand.hasOneUse() && operand.getDefiningOp() &&
4431 isa<InstanceOp>(operand.getDefiningOp())) {
4432 ++operandIndex;
4433 continue;
4434 }
4435
4436 ops.clear();
4437 ops.insert(op);
4438
4439 startStatement();
4440 ps.addCallback({op, true});
4441 bool isZeroBit = isZeroBitType(port.type);
4442 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
4443 if (isZeroBit)
4444 ps << "// Zero width: ";
4445 // Emit "assign" only in a non-procedural region.
4446 if (!isProcedural)
4447 ps << "assign" << PP::space;
4448 ps << PPExtString(port.getVerilogName());
4449 ps << PP::space << "=" << PP::space;
4450 ps.scopedBox(PP::ibox0, [&]() {
4451 // If this is a zero-width constant then don't emit it (illegal). Else,
4452 // emit the expression - even for zero width - for traceability.
4453 if (isZeroBit &&
4454 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4455 ps << "/*Zero width*/";
4456 else
4457 emitExpression(operand, ops, LowestPrecedence,
4458 /*isAssignmentLikeContext=*/true);
4459 ps << ";";
4460 });
4461 });
4462 ps.addCallback({op, false});
4463 emitLocationInfoAndNewLine(ops);
4464
4465 ++operandIndex;
4466 }
4467 return success();
4468}
4469
4470LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4471 auto parent = op->getParentOfType<PortList>();
4472 ModulePortInfo ports(parent.getPortList());
4473 return emitOutputLikeOp(op, ports);
4474}
4475
4476LogicalResult StmtEmitter::visitStmt(TypeScopeOp op) {
4477 startStatement();
4478 auto typescopeDef = ("_TYPESCOPE_" + op.getSymName()).str();
4479 ps << "`ifndef " << typescopeDef << PP::newline;
4480 ps << "`define " << typescopeDef;
4481 setPendingNewline();
4482 emitStatementBlock(*op.getBodyBlock());
4483 startStatement();
4484 ps << "`endif // " << typescopeDef;
4485 setPendingNewline();
4486 return success();
4487}
4488
4489LogicalResult StmtEmitter::visitStmt(TypedeclOp op) {
4490 if (hasSVAttributes(op))
4491 emitError(op, "SV attributes emission is unimplemented for the op");
4492
4493 startStatement();
4494 auto zeroBitType = isZeroBitType(op.getType());
4495 if (zeroBitType)
4496 ps << PP::neverbox << "// ";
4497
4498 SmallPtrSet<Operation *, 8> ops;
4499 ops.insert(op);
4500 ps.scopedBox(PP::ibox2, [&]() {
4501 ps << "typedef" << PP::space;
4502 ps.invokeWithStringOS([&](auto &os) {
4503 emitter.printPackedType(stripUnpackedTypes(op.getType()), os, op.getLoc(),
4504 op.getAliasType(), false);
4505 });
4506 ps << PP::space << PPExtString(op.getPreferredName());
4507 ps.invokeWithStringOS(
4508 [&](auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4509 ps << ";";
4510 });
4511 if (zeroBitType)
4512 ps << PP::end;
4513 emitLocationInfoAndNewLine(ops);
4514 return success();
4515}
4516
4517template <typename CallOpTy>
4518LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4519 startStatement();
4520
4521 auto callee =
4522 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4523
4524 SmallPtrSet<Operation *, 8> ops;
4525 ops.insert(op);
4526 assert(callee);
4527
4528 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4529 if (explicitReturn) {
4530 assert(explicitReturn.hasOneUse());
4531 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4532 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4533 emitExpression(bpassignOp.getDest(), ops);
4534 } else {
4535 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4536 ps << "assign" << PP::nbsp;
4537 emitExpression(assignOp.getDest(), ops);
4538 }
4539 ps << PP::nbsp << "=" << PP::nbsp;
4540 }
4541
4542 auto arguments = callee.getPortList(true);
4543
4544 ps << PPExtString(getSymOpName(callee)) << "(";
4545
4546 bool needsComma = false;
4547 auto printArg = [&](Value value) {
4548 if (needsComma)
4549 ps << "," << PP::space;
4550 emitExpression(value, ops);
4551 needsComma = true;
4552 };
4553
4554 ps.scopedBox(PP::ibox0, [&] {
4555 unsigned inputIndex = 0, outputIndex = 0;
4556 for (auto arg : arguments) {
4557 if (arg.dir == hw::ModulePort::Output)
4558 printArg(
4559 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4560 else
4561 printArg(op.getInputs()[inputIndex++]);
4562 }
4563 });
4564
4565 ps << ");";
4566 emitLocationInfoAndNewLine(ops);
4567 return success();
4568}
4569
4570LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4571 return emitFunctionCall(op);
4572}
4573
4574LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4575 return emitFunctionCall(op);
4576}
4577
4578template <typename PPS>
4579void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op,
4580 bool isAutomatic = false,
4581 bool emitAsTwoStateType = false) {
4582 ps << "function" << PP::nbsp;
4583 if (isAutomatic)
4584 ps << "automatic" << PP::nbsp;
4585 auto retType = op.getExplicitlyReturnedType();
4586 if (retType) {
4587 ps.invokeWithStringOS([&](auto &os) {
4588 emitter.printPackedType(retType, os, op->getLoc(), {}, false, true,
4589 emitAsTwoStateType);
4590 });
4591 } else
4592 ps << "void";
4593 ps << PP::nbsp << PPExtString(getSymOpName(op));
4594
4595 emitter.emitPortList(
4596 op, ModulePortInfo(op.getPortList(/*excludeExplicitReturn=*/true)), true);
4597}
4598
4599LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4600 auto parent = op->getParentOfType<sv::FuncOp>();
4601 ModulePortInfo ports(parent.getPortList(false));
4602 return emitOutputLikeOp(op, ports);
4603}
4604
4605LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4606 startStatement();
4607 ps << "`include" << PP::nbsp;
4608
4609 if (op.getStyle() == IncludeStyle::System)
4610 ps << "<" << op.getTarget() << ">";
4611 else
4612 ps << "\"" << op.getTarget() << "\"";
4613
4614 emitLocationInfo(op.getLoc());
4615 setPendingNewline();
4616 return success();
4617}
4618
4619LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4620 startStatement();
4621
4622 ps << "import" << PP::nbsp << "\"DPI-C\"" << PP::nbsp << "context"
4623 << PP::nbsp;
4624
4625 // Emit a linkage name if provided.
4626 if (auto linkageName = importOp.getLinkageName())
4627 ps << *linkageName << PP::nbsp << "=" << PP::nbsp;
4628 auto op =
4629 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4630 assert(op.isDeclaration() && "function must be a declaration");
4631 emitFunctionSignature(emitter, ps, op, /*isAutomatic=*/false,
4632 /*emitAsTwoStateType=*/true);
4633 assert(state.pendingNewline);
4634 ps << PP::newline;
4635
4636 return success();
4637}
4638
4639LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4640 if (hasSVAttributes(op))
4641 emitError(op, "SV attributes emission is unimplemented for the op");
4642
4643 startStatement();
4644 SmallPtrSet<Operation *, 8> ops;
4645 ops.insert(op);
4646
4647 ps.addCallback({op, true});
4648 ps << "$fflush(";
4649 if (auto fd = op.getFd())
4650 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4651
4652 ps << ");";
4653 ps.addCallback({op, false});
4654 emitLocationInfoAndNewLine(ops);
4655 return success();
4656}
4657
4658LogicalResult StmtEmitter::visitSV(FCloseOp op) {
4659 if (hasSVAttributes(op))
4660 emitError(op, "SV attributes emission is unimplemented for the op");
4661
4662 startStatement();
4663 SmallPtrSet<Operation *, 8> ops;
4664 ops.insert(op);
4665
4666 ps.addCallback({op, true});
4667 ps << "$fclose(";
4668 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4669 ps << ");";
4670 ps.addCallback({op, false});
4671 emitLocationInfoAndNewLine(ops);
4672 return success();
4673}
4674
4675template <typename OpTy, typename EmitPrefixFn>
4676LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4677 StringRef formatString,
4678 ValueRange substitutions,
4679 EmitPrefixFn emitPrefix) {
4680 if (hasSVAttributes(op))
4681 emitError(op, "SV attributes emission is unimplemented for the op");
4682
4683 startStatement();
4684 SmallPtrSet<Operation *, 8> ops;
4685 ops.insert(op);
4686
4687 ps.addCallback({op, true});
4688 ps << callee;
4689 ps.scopedBox(PP::ibox0, [&]() {
4690 emitPrefix(ops);
4691 ps.writeQuotedEscaped(formatString);
4692 // TODO: if any of these breaks, it'd be "nice" to break
4693 // after the comma, instead of:
4694 // $fwrite(5, "...", a + b,
4695 // longexpr_goes
4696 // + here, c);
4697 // (without forcing breaking between all elements, like braced list)
4698 for (auto operand : substitutions) {
4699 ps << "," << PP::space;
4700 emitExpression(operand, ops);
4701 }
4702 ps << ");";
4703 });
4704 ps.addCallback({op, false});
4705 emitLocationInfoAndNewLine(ops);
4706 return success();
4707}
4708
4709LogicalResult StmtEmitter::visitSV(WriteOp op) {
4710 return emitFormattedWriteLikeOp(op, "$write(", op.getFormatString(),
4711 op.getSubstitutions(),
4712 [&](SmallPtrSetImpl<Operation *> &) {});
4713}
4714
4715LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4716 return emitFormattedWriteLikeOp(op, "$fwrite(", op.getFormatString(),
4717 op.getSubstitutions(),
4718 [&](SmallPtrSetImpl<Operation *> &ops) {
4719 emitExpression(op.getFd(), ops);
4720 ps << "," << PP::space;
4721 });
4722}
4723
4724LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4725 if (hasSVAttributes(op))
4726 emitError(op, "SV attributes emission is unimplemented for the op");
4727
4728 startStatement();
4729 SmallPtrSet<Operation *, 8> ops;
4730 ops.insert(op);
4731 ps << PP::neverbox;
4732
4733 // Drop an extraneous \n off the end of the string if present.
4734 StringRef string = op.getFormatString();
4735 if (string.ends_with("\n"))
4736 string = string.drop_back();
4737
4738 // Emit each \n separated piece of the string with each piece properly
4739 // indented. The convention is to not emit the \n so
4740 // emitLocationInfoAndNewLine can do that for the last line.
4741 bool isFirst = true;
4742
4743 // Emit each line of the string at a time.
4744 while (!string.empty()) {
4745 auto lhsRhs = string.split('\n');
4746 if (isFirst)
4747 isFirst = false;
4748 else {
4749 ps << PP::end << PP::newline << PP::neverbox;
4750 }
4751
4752 // Emit each chunk of the line.
4753 emitTextWithSubstitutions(
4754 ps, lhsRhs.first, op,
4755 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4756 string = lhsRhs.second;
4757 }
4758
4759 ps << PP::end;
4760
4761 emitLocationInfoAndNewLine(ops);
4762 return success();
4763}
4764
4765// Emit macro as a statement.
4766LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4767 if (hasSVAttributes(op)) {
4768 emitError(op, "SV attributes emission is unimplemented for the op");
4769 return failure();
4770 }
4771 startStatement();
4772 SmallPtrSet<Operation *, 8> ops;
4773 ops.insert(op);
4774 ps << PP::neverbox;
4775
4776 // Use the specified name or the symbol name as appropriate.
4777 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4778 assert(macroOp && "Invalid IR");
4779 StringRef name =
4780 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4781 ps << "`" << PPExtString(name);
4782 if (!op.getInputs().empty()) {
4783 ps << "(";
4784 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4785 emitExpression(val, ops, LowestPrecedence,
4786 /*isAssignmentLikeContext=*/false);
4787 });
4788 ps << ")";
4789 }
4790 ps << PP::end;
4791 emitLocationInfoAndNewLine(ops);
4792 return success();
4793}
4794
4795/// Emit one of the simulation control tasks `$stop`, `$finish`, or `$exit`.
4796LogicalResult
4797StmtEmitter::emitSimulationControlTask(Operation *op, PPExtString taskName,
4798 std::optional<unsigned> verbosity) {
4799 if (hasSVAttributes(op))
4800 emitError(op, "SV attributes emission is unimplemented for the op");
4801
4802 startStatement();
4803 SmallPtrSet<Operation *, 8> ops;
4804 ops.insert(op);
4805 ps.addCallback({op, true});
4806 ps << taskName;
4807 if (verbosity && *verbosity != 1) {
4808 ps << "(";
4809 ps.addAsString(*verbosity);
4810 ps << ")";
4811 }
4812 ps << ";";
4813 ps.addCallback({op, false});
4814 emitLocationInfoAndNewLine(ops);
4815 return success();
4816}
4817
4818LogicalResult StmtEmitter::visitSV(StopOp op) {
4819 return emitSimulationControlTask(op, PPExtString("$stop"), op.getVerbosity());
4820}
4821
4822LogicalResult StmtEmitter::visitSV(FinishOp op) {
4823 return emitSimulationControlTask(op, PPExtString("$finish"),
4824 op.getVerbosity());
4825}
4826
4827LogicalResult StmtEmitter::visitSV(ExitOp op) {
4828 return emitSimulationControlTask(op, PPExtString("$exit"), {});
4829}
4830
4831/// Emit one of the severity message tasks `$fatal`, `$error`, `$warning`, or
4832/// `$info`.
4833LogicalResult
4834StmtEmitter::emitSeverityMessageTask(Operation *op, PPExtString taskName,
4835 std::optional<unsigned> verbosity,
4836 StringAttr message, ValueRange operands) {
4837 if (hasSVAttributes(op))
4838 emitError(op, "SV attributes emission is unimplemented for the op");
4839
4840 startStatement();
4841 SmallPtrSet<Operation *, 8> ops;
4842 ops.insert(op);
4843 ps.addCallback({op, true});
4844 ps << taskName;
4845
4846 // In case we have a message to print, or the operation has an optional
4847 // verbosity and that verbosity is present, print the parenthesized parameter
4848 // list.
4849 if ((verbosity && *verbosity != 1) || message) {
4850 ps << "(";
4851 ps.scopedBox(PP::ibox0, [&]() {
4852 // If the operation takes a verbosity, print it if it is set, or print the
4853 // default "1".
4854 if (verbosity)
4855 ps.addAsString(*verbosity);
4856
4857 // Print the message and interpolation operands if present.
4858 if (message) {
4859 if (verbosity)
4860 ps << "," << PP::space;
4861 ps.writeQuotedEscaped(message.getValue());
4862 // TODO: good comma/wrapping behavior as elsewhere.
4863 for (auto operand : operands) {
4864 ps << "," << PP::space;
4865 emitExpression(operand, ops);
4866 }
4867 }
4868
4869 ps << ")";
4870 });
4871 }
4872
4873 ps << ";";
4874 ps.addCallback({op, false});
4875 emitLocationInfoAndNewLine(ops);
4876 return success();
4877}
4878
4879LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4880 return emitFatalMessageOp(op);
4881}
4882
4883LogicalResult StmtEmitter::visitSV(FatalOp op) {
4884 return emitFatalMessageOp(op);
4885}
4886
4887LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4888 return emitNonfatalMessageOp(op, "$error");
4889}
4890
4891LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4892 return emitNonfatalMessageOp(op, "$warning");
4893}
4894
4895LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4896 return emitNonfatalMessageOp(op, "$info");
4897}
4898
4899LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4900 return emitNonfatalMessageOp(op, "$error");
4901}
4902
4903LogicalResult StmtEmitter::visitSV(WarningOp op) {
4904 return emitNonfatalMessageOp(op, "$warning");
4905}
4906
4907LogicalResult StmtEmitter::visitSV(InfoOp op) {
4908 return emitNonfatalMessageOp(op, "$info");
4909}
4910
4911LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4912 SmallPtrSet<Operation *, 8> ops({op});
4913
4914 startStatement();
4915 ps.addCallback({op, true});
4916 ps << "$readmem";
4917 switch (op.getBaseAttr().getValue()) {
4918 case MemBaseTypeAttr::MemBaseBin:
4919 ps << "b";
4920 break;
4921 case MemBaseTypeAttr::MemBaseHex:
4922 ps << "h";
4923 break;
4924 }
4925 ps << "(";
4926 ps.scopedBox(PP::ibox0, [&]() {
4927 ps.writeQuotedEscaped(op.getFilename());
4928 ps << "," << PP::space;
4929 emitExpression(op.getDest(), ops);
4930 });
4931
4932 ps << ");";
4933 ps.addCallback({op, false});
4934 emitLocationInfoAndNewLine(ops);
4935 return success();
4936}
4937
4938LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4939 emitSVAttributes(op);
4940 // TODO: location info?
4941 startStatement();
4942 ps.addCallback({op, true});
4943 ps << "generate" << PP::newline;
4944 ps << "begin: " << PPExtString(getSymOpName(op));
4945 setPendingNewline();
4946 emitStatementBlock(op.getBody().getBlocks().front());
4947 startStatement();
4948 ps << "end: " << PPExtString(getSymOpName(op)) << PP::newline;
4949 ps << "endgenerate";
4950 ps.addCallback({op, false});
4951 setPendingNewline();
4952 return success();
4953}
4954
4955LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4956 emitSVAttributes(op);
4957 // TODO: location info?
4958 startStatement();
4959 ps.addCallback({op, true});
4960 ps << "case (";
4961 ps.invokeWithStringOS([&](auto &os) {
4962 emitter.printParamValue(
4963 op.getCond(), os, VerilogPrecedence::Selection,
4964 [&]() { return op->emitOpError("invalid case parameter"); });
4965 });
4966 ps << ")";
4967 setPendingNewline();
4968
4969 // Ensure that all of the per-case arrays are the same length.
4970 ArrayAttr patterns = op.getCasePatterns();
4971 ArrayAttr caseNames = op.getCaseNames();
4972 MutableArrayRef<Region> regions = op.getCaseRegions();
4973 assert(patterns.size() == regions.size());
4974 assert(patterns.size() == caseNames.size());
4975
4976 // TODO: We'll probably need to store the legalized names somewhere for
4977 // `verbose` formatting. Set up the infra for storing names recursively. Just
4978 // store this locally for now.
4979 llvm::StringMap<size_t> nextGenIds;
4980 ps.scopedBox(PP::bbox2, [&]() {
4981 // Emit each case.
4982 for (size_t i = 0, e = patterns.size(); i < e; ++i) {
4983 auto &region = regions[i];
4984 assert(region.hasOneBlock());
4985 Attribute patternAttr = patterns[i];
4986
4987 startStatement();
4988 if (!isa<mlir::TypedAttr>(patternAttr))
4989 ps << "default";
4990 else
4991 ps.invokeWithStringOS([&](auto &os) {
4992 emitter.printParamValue(
4993 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4994 [&]() { return op->emitOpError("invalid case value"); });
4995 });
4996
4997 StringRef legalName =
4998 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4999 options.caseInsensitiveKeywords);
5000 ps << ": begin: " << PPExtString(legalName);
5001 setPendingNewline();
5002 emitStatementBlock(region.getBlocks().front());
5003 startStatement();
5004 ps << "end: " << PPExtString(legalName);
5005 setPendingNewline();
5006 }
5007 });
5008
5009 startStatement();
5010 ps << "endcase";
5011 ps.addCallback({op, false});
5012 setPendingNewline();
5013 return success();
5014}
5015
5016LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
5017 emitSVAttributes(op);
5018 llvm::SmallPtrSet<Operation *, 8> ops;
5019 ps.addCallback({op, true});
5020 startStatement();
5021
5022 StringRef inductionVarName = op->getAttrOfType<StringAttr>("hw.verilogName");
5023
5024 ps << "for (";
5025 ps.scopedBox(PP::cbox0, [&]() {
5026 emitAssignLike(
5027 [&]() { ps << "genvar" << PP::nbsp << PPExtString(inductionVarName); },
5028 [&]() {
5029 ps.invokeWithStringOS([&](auto &os) {
5030 emitter.printParamValue(
5031 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5032 [&]() { return op->emitOpError("invalid lower bound"); });
5033 });
5034 },
5035 PPExtString("="));
5036 ps << PP::space;
5037
5038 emitAssignLike(
5039 [&]() { ps << PPExtString(inductionVarName); },
5040 [&]() {
5041 ps.invokeWithStringOS([&](auto &os) {
5042 emitter.printParamValue(
5043 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5044 [&]() { return op->emitOpError("invalid upper bound"); });
5045 });
5046 },
5047 PPExtString("<"));
5048 ps << PP::space;
5049
5050 ps << PPExtString(inductionVarName) << PP::nbsp << "+=" << PP::nbsp;
5051 ps.invokeWithStringOS([&](auto &os) {
5052 emitter.printParamValue(
5053 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5054 [&]() { return op->emitOpError("invalid step"); });
5055 });
5056 ps << ") begin";
5057 StringRef blockName = op.getGenBlockName();
5058 if (!blockName.empty())
5059 ps << " : " << PPExtString(blockName);
5060 });
5061
5062 ps << PP::neverbreak;
5063 setPendingNewline();
5064 emitStatementBlock(op.getBody().getBlocks().front());
5065 startStatement();
5066 ps << "end";
5067 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5068 ps << " // " << PPExtString(blockName);
5069 ps.addCallback({op, false});
5070 setPendingNewline();
5071 return success();
5072}
5073
5074LogicalResult StmtEmitter::visitSV(ForOp op) {
5075 emitSVAttributes(op);
5076 llvm::SmallPtrSet<Operation *, 8> ops;
5077 ps.addCallback({op, true});
5078 startStatement();
5079 auto inductionVarName = op->getAttrOfType<StringAttr>("hw.verilogName");
5080 ps << "for (";
5081 // Emit statements on same line if possible, or put each on own line.
5082 ps.scopedBox(PP::cbox0, [&]() {
5083 // Emit initialization assignment.
5084 emitAssignLike(
5085 [&]() {
5086 ps << "logic" << PP::nbsp;
5087 ps.invokeWithStringOS([&](auto &os) {
5088 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5089 os);
5090 });
5091 ps << PP::nbsp << PPExtString(inductionVarName);
5092 },
5093 [&]() { emitExpression(op.getLowerBound(), ops); }, PPExtString("="));
5094 // Break between statements.
5095 ps << PP::space;
5096
5097 // Emit bounds-check statement.
5098 emitAssignLike([&]() { ps << PPExtString(inductionVarName); },
5099 [&]() { emitExpression(op.getUpperBound(), ops); },
5100 PPExtString("<"));
5101 // Break between statements.
5102 ps << PP::space;
5103
5104 // Emit update statement and trailing syntax.
5105 emitAssignLike([&]() { ps << PPExtString(inductionVarName); },
5106 [&]() { emitExpression(op.getStep(), ops); },
5107 PPExtString("+="), PPExtString(") begin"));
5108 });
5109 // Don't break for because of newline.
5110 ps << PP::neverbreak;
5111 setPendingNewline();
5112 emitStatementBlock(op.getBody().getBlocks().front());
5113 startStatement();
5114 ps << "end";
5115 ps.addCallback({op, false});
5116 emitLocationInfoAndNewLine(ops);
5117 return success();
5118}
5119
5120/// Emit the `<label>:` portion of a verification operation.
5121void StmtEmitter::emitAssertionLabel(Operation *op) {
5122 if (auto label = op->getAttrOfType<StringAttr>("hw.verilogName"))
5123 ps << PPExtString(label) << ":" << PP::space;
5124}
5125
5126/// Emit the optional ` else $error(...)` portion of an immediate or concurrent
5127/// verification operation.
5128void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5129 SmallPtrSetImpl<Operation *> &ops,
5130 bool isConcurrent = false) {
5131 if (!message)
5132 return;
5133 ps << PP::space << "else" << PP::nbsp << "$error(";
5134 ps.scopedBox(PP::ibox0, [&]() {
5135 ps.writeQuotedEscaped(message.getValue());
5136 // TODO: box, break/wrap behavior!
5137 for (auto arg : args) {
5138 ps << "," << PP::space;
5139 emitExpression(arg, ops);
5140 }
5141 ps << ")";
5142 });
5143}
5144
5145template <typename Op>
5146LogicalResult StmtEmitter::emitImmediateAssertion(Op op, PPExtString opName) {
5147 if (hasSVAttributes(op))
5148 emitError(op, "SV attributes emission is unimplemented for the op");
5149
5150 startStatement();
5151 SmallPtrSet<Operation *, 8> ops;
5152 ops.insert(op);
5153 ps.addCallback({op, true});
5154 ps.scopedBox(PP::ibox2, [&]() {
5155 emitAssertionLabel(op);
5156 ps.scopedBox(PP::cbox0, [&]() {
5157 ps << opName;
5158 switch (op.getDefer()) {
5159 case DeferAssert::Immediate:
5160 break;
5161 case DeferAssert::Observed:
5162 ps << " #0 ";
5163 break;
5164 case DeferAssert::Final:
5165 ps << " final ";
5166 break;
5167 }
5168 ps << "(";
5169 ps.scopedBox(PP::ibox0, [&]() {
5170 emitExpression(op.getExpression(), ops);
5171 ps << ")";
5172 });
5173 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5174 ps << ";";
5175 });
5176 });
5177 ps.addCallback({op, false});
5178 emitLocationInfoAndNewLine(ops);
5179 return success();
5180}
5181
5182LogicalResult StmtEmitter::visitSV(AssertOp op) {
5183 return emitImmediateAssertion(op, PPExtString("assert"));
5184}
5185
5186LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5187 return emitImmediateAssertion(op, PPExtString("assume"));
5188}
5189
5190LogicalResult StmtEmitter::visitSV(CoverOp op) {
5191 return emitImmediateAssertion(op, PPExtString("cover"));
5192}
5193
5194template <typename Op>
5195LogicalResult StmtEmitter::emitConcurrentAssertion(Op op, PPExtString opName) {
5196 if (hasSVAttributes(op))
5197 emitError(op, "SV attributes emission is unimplemented for the op");
5198
5199 startStatement();
5200 SmallPtrSet<Operation *, 8> ops;
5201 ops.insert(op);
5202 ps.addCallback({op, true});
5203 ps.scopedBox(PP::ibox2, [&]() {
5204 emitAssertionLabel(op);
5205 ps.scopedBox(PP::cbox0, [&]() {
5206 ps << opName << PP::nbsp << "property (";
5207 ps.scopedBox(PP::ibox0, [&]() {
5208 ps << "@(" << PPExtString(stringifyEventControl(op.getEvent()))
5209 << PP::nbsp;
5210 emitExpression(op.getClock(), ops);
5211 ps << ")" << PP::space;
5212 emitExpression(op.getProperty(), ops);
5213 ps << ")";
5214 });
5215 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5216 true);
5217 ps << ";";
5218 });
5219 });
5220 ps.addCallback({op, false});
5221 emitLocationInfoAndNewLine(ops);
5222 return success();
5223}
5224
5225LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5226 return emitConcurrentAssertion(op, PPExtString("assert"));
5227}
5228
5229LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5230 return emitConcurrentAssertion(op, PPExtString("assume"));
5231}
5232
5233LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5234 return emitConcurrentAssertion(op, PPExtString("cover"));
5235}
5236
5237// Property assertions are what gets emitted if the user want to combine
5238// concurrent assertions with a disable signal, a clock and an ltl property.
5239template <typename Op>
5240LogicalResult StmtEmitter::emitPropertyAssertion(Op op, PPExtString opName) {
5241 if (hasSVAttributes(op))
5242 emitError(op, "SV attributes emission is unimplemented for the op");
5243
5244 // If we are inside a procedural region we have the option of emitting either
5245 // an `assert` or `assert property`. If we are in a non-procedural region,
5246 // e.g., the body of a module, we have to use the concurrent form `assert
5247 // property` (which also supports plain booleans).
5248 //
5249 // See IEEE 1800-2017 section 16.14.5 "Using concurrent assertion statements
5250 // outside procedural code" and 16.14.6 "Embedding concurrent assertions in
5251 // procedural code".
5252 Operation *parent = op->getParentOp();
5253 Value property = op.getProperty();
5254 bool isTemporal = !property.getType().isSignlessInteger(1);
5255 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5256 bool emitAsImmediate = !isTemporal && isProcedural;
5257
5258 startStatement();
5259 SmallPtrSet<Operation *, 8> ops;
5260 ops.insert(op);
5261 ps.addCallback({op, true});
5262 ps.scopedBox(PP::ibox2, [&]() {
5263 // Check for a label and emit it if necessary
5264 emitAssertionLabel(op);
5265 // Emit the assertion
5266 ps.scopedBox(PP::cbox0, [&]() {
5267 if (emitAsImmediate)
5268 ps << opName << "(";
5269 else
5270 ps << opName << PP::nbsp << "property" << PP::nbsp << "(";
5271 // Event only exists if the clock exists
5272 Value clock = op.getClock();
5273 auto event = op.getEvent();
5274 if (clock)
5275 ps.scopedBox(PP::ibox2, [&]() {
5276 PropertyEmitter(emitter, ops)
5277 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5278 });
5279 else
5280 ps.scopedBox(PP::ibox2, [&]() {
5281 PropertyEmitter(emitter, ops)
5282 .emitAssertPropertyBody(property, op.getDisable());
5283 });
5284 ps << ");";
5285 });
5286 });
5287 ps.addCallback({op, false});
5288 emitLocationInfoAndNewLine(ops);
5289 return success();
5290}
5291
5292LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5293 return emitPropertyAssertion(op, PPExtString("assert"));
5294}
5295
5296LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5297 return emitPropertyAssertion(op, PPExtString("assume"));
5298}
5299
5300LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5301 return emitPropertyAssertion(op, PPExtString("cover"));
5302}
5303
5304LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5305 if (hasSVAttributes(op))
5306 emitError(op, "SV attributes emission is unimplemented for the op");
5307
5308 auto ident = PPExtString(
5309 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5310 .getMacroIdentifier());
5311
5312 startStatement();
5313 bool hasEmptyThen = op->getRegion(0).front().empty();
5314 if (hasEmptyThen)
5315 ps << "`ifndef " << ident;
5316 else
5317 ps << "`ifdef " << ident;
5318
5319 SmallPtrSet<Operation *, 8> ops;
5320 ops.insert(op);
5321 emitLocationInfoAndNewLine(ops);
5322
5323 if (!hasEmptyThen)
5324 emitStatementBlock(op->getRegion(0).front());
5325
5326 if (!op->getRegion(1).empty()) {
5327 if (!hasEmptyThen) {
5328 startStatement();
5329 ps << "`else // " << ident;
5330 setPendingNewline();
5331 }
5332 emitStatementBlock(op->getRegion(1).front());
5333 }
5334 startStatement();
5335 ps << "`endif // ";
5336 if (hasEmptyThen)
5337 ps << "not def ";
5338 ps << ident;
5339 setPendingNewline();
5340 return success();
5341}
5342
5343/// Emit the body of a control flow statement that is surrounded by begin/end
5344/// markers if non-singular. If the control flow construct is multi-line and
5345/// if multiLineComment is non-null, the string is included in a comment after
5346/// the 'end' to make it easier to associate.
5347void StmtEmitter::emitBlockAsStatement(
5348 Block *block, const SmallPtrSetImpl<Operation *> &locationOps,
5349 StringRef multiLineComment) {
5350
5351 // Determine if we need begin/end by scanning the block.
5352 auto count = countStatements(*block);
5353 auto needsBeginEnd =
5354 count != BlockStatementCount::One || state.options.alwaysEmitBeginEnd;
5355 if (needsBeginEnd)
5356 ps << " begin";
5357 emitLocationInfoAndNewLine(locationOps);
5358
5359 if (count != BlockStatementCount::Zero)
5360 emitStatementBlock(*block);
5361
5362 if (needsBeginEnd) {
5363 startStatement();
5364 ps << "end";
5365 // Emit comment if there's an 'end', regardless of line count.
5366 if (!multiLineComment.empty())
5367 ps << " // " << multiLineComment;
5368 setPendingNewline();
5369 }
5370}
5371
5372LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5373 // Emit the body.
5374 for (auto &op : ooop.getBody().front())
5375 emitStatement(&op);
5376 return success();
5377}
5378
5379LogicalResult StmtEmitter::visitSV(IfOp op) {
5380 SmallPtrSet<Operation *, 8> ops;
5381
5382 auto ifcondBox = PP::ibox2;
5383
5384 emitSVAttributes(op);
5385 startStatement();
5386 ps.addCallback({op, true});
5387 ps << "if (" << ifcondBox;
5388
5389 // In the loop, emit an if statement assuming the keyword introducing
5390 // it (either "if (" or "else if (") was printed already.
5391 IfOp ifOp = op;
5392 for (;;) {
5393 ops.clear();
5394 ops.insert(ifOp);
5395
5396 // Emit the condition and the then block.
5397 emitExpression(ifOp.getCond(), ops);
5398 ps << PP::end << ")";
5399 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5400
5401 if (!ifOp.hasElse())
5402 break;
5403
5404 startStatement();
5405 Block *elseBlock = ifOp.getElseBlock();
5406 auto nestedElseIfOp = findNestedElseIf(elseBlock);
5407 if (!nestedElseIfOp) {
5408 // The else block does not contain an if-else that can be flattened.
5409 ops.clear();
5410 ops.insert(ifOp);
5411 ps << "else";
5412 emitBlockAsStatement(elseBlock, ops);
5413 break;
5414 }
5415
5416 // Introduce the 'else if', and iteratively continue unfolding any if-else
5417 // statements inside of it.
5418 ifOp = nestedElseIfOp;
5419 ps << "else if (" << ifcondBox;
5420 }
5421 ps.addCallback({op, false});
5422
5423 return success();
5424}
5425
5426LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5427 emitSVAttributes(op);
5428 SmallPtrSet<Operation *, 8> ops;
5429 ops.insert(op);
5430 startStatement();
5431
5432 auto printEvent = [&](AlwaysOp::Condition cond) {
5433 ps << PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5434 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5435 };
5436 ps.addCallback({op, true});
5437
5438 switch (op.getNumConditions()) {
5439 case 0:
5440 ps << "always @*";
5441 break;
5442 case 1:
5443 ps << "always @(";
5444 printEvent(op.getCondition(0));
5445 ps << ")";
5446 break;
5447 default:
5448 ps << "always @(";
5449 ps.scopedBox(PP::cbox0, [&]() {
5450 printEvent(op.getCondition(0));
5451 for (size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5452 ps << PP::space << "or" << PP::space;
5453 printEvent(op.getCondition(i));
5454 }
5455 ps << ")";
5456 });
5457 break;
5458 }
5459
5460 // Build the comment string, leave out the signal expressions (since they
5461 // can be large).
5462 std::string comment;
5463 if (op.getNumConditions() == 0) {
5464 comment = "always @*";
5465 } else {
5466 comment = "always @(";
5467 llvm::interleave(
5468 op.getEvents(),
5469 [&](Attribute eventAttr) {
5470 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5471 comment += stringifyEventControl(event);
5472 },
5473 [&]() { comment += ", "; });
5474 comment += ')';
5475 }
5476
5477 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5478 ps.addCallback({op, false});
5479 return success();
5480}
5481
5482LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5483 emitSVAttributes(op);
5484 SmallPtrSet<Operation *, 8> ops;
5485 ops.insert(op);
5486 startStatement();
5487
5488 ps.addCallback({op, true});
5489 StringRef opString = "always_comb";
5490 if (state.options.noAlwaysComb)
5491 opString = "always @(*)";
5492
5493 ps << PPExtString(opString);
5494 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5495 ps.addCallback({op, false});
5496 return success();
5497}
5498
5499LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5500 emitSVAttributes(op);
5501
5502 SmallPtrSet<Operation *, 8> ops;
5503 ops.insert(op);
5504 startStatement();
5505
5506 ps.addCallback({op, true});
5507 ps << "always_ff @(";
5508 ps.scopedBox(PP::cbox0, [&]() {
5509 ps << PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5510 emitExpression(op.getClock(), ops);
5511 if (op.getResetStyle() == ResetType::AsyncReset) {
5512 ps << PP::nbsp << "or" << PP::space
5513 << PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5514 emitExpression(op.getReset(), ops);
5515 }
5516 ps << ")";
5517 });
5518
5519 // Build the comment string, leave out the signal expressions (since they
5520 // can be large).
5521 std::string comment;
5522 comment += "always_ff @(";
5523 comment += stringifyEventControl(op.getClockEdge());
5524 if (op.getResetStyle() == ResetType::AsyncReset) {
5525 comment += " or ";
5526 comment += stringifyEventControl(*op.getResetEdge());
5527 }
5528 comment += ')';
5529
5530 if (op.getResetStyle() == ResetType::NoReset)
5531 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5532 else {
5533 ps << " begin";
5534 emitLocationInfoAndNewLine(ops);
5535 ps.scopedBox(PP::bbox2, [&]() {
5536 startStatement();
5537 ps << "if (";
5538 // TODO: group, like normal 'if'.
5539 // Negative edge async resets need to invert the reset condition. This
5540 // is noted in the op description.
5541 if (op.getResetStyle() == ResetType::AsyncReset &&
5542 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5543 ps << "!";
5544 emitExpression(op.getReset(), ops);
5545 ps << ")";
5546 emitBlockAsStatement(op.getResetBlock(), ops);
5547 startStatement();
5548 ps << "else";
5549 emitBlockAsStatement(op.getBodyBlock(), ops);
5550 });
5551
5552 startStatement();
5553 ps << "end";
5554 ps << " // " << comment;
5555 setPendingNewline();
5556 }
5557 ps.addCallback({op, false});
5558 return success();
5559}
5560
5561LogicalResult StmtEmitter::visitSV(InitialOp op) {
5562 emitSVAttributes(op);
5563 SmallPtrSet<Operation *, 8> ops;
5564 ops.insert(op);
5565 startStatement();
5566 ps.addCallback({op, true});
5567 ps << "initial";
5568 emitBlockAsStatement(op.getBodyBlock(), ops, "initial");
5569 ps.addCallback({op, false});
5570 return success();
5571}
5572
5573LogicalResult StmtEmitter::visitSV(CaseOp op) {
5574 emitSVAttributes(op);
5575 SmallPtrSet<Operation *, 8> ops, emptyOps;
5576 ops.insert(op);
5577 startStatement();
5578 ps.addCallback({op, true});
5579 if (op.getValidationQualifier() !=
5580 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5581 ps << PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5582 op.getValidationQualifier()))
5583 << PP::nbsp;
5584 const char *opname = nullptr;
5585 switch (op.getCaseStyle()) {
5586 case CaseStmtType::CaseStmt:
5587 opname = "case";
5588 break;
5589 case CaseStmtType::CaseXStmt:
5590 opname = "casex";
5591 break;
5592 case CaseStmtType::CaseZStmt:
5593 opname = "casez";
5594 break;
5595 }
5596 ps << opname << " (";
5597 ps.scopedBox(PP::ibox0, [&]() {
5598 emitExpression(op.getCond(), ops);
5599 ps << ")";
5600 });
5601 emitLocationInfoAndNewLine(ops);
5602
5603 size_t caseValueIndex = 0;
5604 ps.scopedBox(PP::bbox2, [&]() {
5605 for (auto &caseInfo : op.getCases()) {
5606 startStatement();
5607 auto &pattern = caseInfo.pattern;
5608
5609 llvm::TypeSwitch<CasePattern *>(pattern.get())
5610 .Case<CaseBitPattern>([&](auto bitPattern) {
5611 // TODO: We could emit in hex if/when the size is a multiple of
5612 // 4 and there are no x's crossing nibble boundaries.
5613 ps.invokeWithStringOS([&](auto &os) {
5614 os << bitPattern->getWidth() << "'b";
5615 for (size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5616 os << getLetter(bitPattern->getBit(e - bit - 1));
5617 });
5618 })
5619 .Case<CaseEnumPattern>([&](auto enumPattern) {
5620 ps << PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5621 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5622 })
5623 .Case<CaseExprPattern>([&](auto) {
5624 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5625 })
5626 .Case<CaseDefaultPattern>([&](auto) { ps << "default"; })
5627 .Default([&](auto) { assert(false && "unhandled case pattern"); });
5628
5629 ps << ":";
5630 emitBlockAsStatement(caseInfo.block, emptyOps);
5631 }
5632 });
5633
5634 startStatement();
5635 ps << "endcase";
5636 ps.addCallback({op, false});
5637 emitLocationInfoAndNewLine(ops);
5638 return success();
5639}
5640
5641LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5642 bool doNotPrint = op.getDoNotPrint();
5643 if (doNotPrint && !state.options.emitBindComments)
5644 return success();
5645
5646 // Emit SV attributes if the op is not emitted as a bind statement.
5647 if (!doNotPrint)
5648 emitSVAttributes(op);
5649 startStatement();
5650 ps.addCallback({op, true});
5651 if (doNotPrint) {
5652 ps << PP::ibox2
5653 << "/* This instance is elsewhere emitted as a bind statement."
5654 << PP::newline;
5655 if (hasSVAttributes(op))
5656 op->emitWarning() << "is emitted as a bind statement but has SV "
5657 "attributes. The attributes will not be emitted.";
5658 }
5659
5660 SmallPtrSet<Operation *, 8> ops;
5661 ops.insert(op);
5662
5663 // Use the specified name or the symbol name as appropriate.
5664 auto *moduleOp =
5665 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5666 assert(moduleOp && "Invalid IR");
5667 ps << PPExtString(getVerilogModuleName(moduleOp));
5668
5669 // If this is a parameterized module, then emit the parameters.
5670 if (!op.getParameters().empty()) {
5671 // All the parameters may be defaulted -- don't print out an empty list if
5672 // so.
5673 bool printed = false;
5674 for (auto params :
5675 llvm::zip(op.getParameters(),
5676 moduleOp->getAttrOfType<ArrayAttr>("parameters"))) {
5677 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5678 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5679 // Ignore values that line up with their default.
5680 if (param.getValue() == modParam.getValue())
5681 continue;
5682
5683 // Handle # if this is the first parameter we're printing.
5684 if (!printed) {
5685 ps << " #(" << PP::bbox2 << PP::newline;
5686 printed = true;
5687 } else {
5688 ps << "," << PP::newline;
5689 }
5690 ps << ".";
5691 ps << PPExtString(
5692 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5693 ps << "(";
5694 ps.invokeWithStringOS([&](auto &os) {
5695 emitter.printParamValue(param.getValue(), os, [&]() {
5696 return op->emitOpError("invalid instance parameter '")
5697 << param.getName().getValue() << "' value";
5698 });
5699 });
5700 ps << ")";
5701 }
5702 if (printed) {
5703 ps << PP::end << PP::newline << ")";
5704 }
5705 }
5706
5707 ps << PP::nbsp << PPExtString(getSymOpName(op));
5708
5709 ModulePortInfo modPortInfo(cast<PortList>(moduleOp).getPortList());
5710 SmallVector<Value> instPortValues(modPortInfo.size());
5711 op.getValues(instPortValues, modPortInfo);
5712 emitInstancePortList(op, modPortInfo, instPortValues);
5713
5714 ps.addCallback({op, false});
5715 emitLocationInfoAndNewLine(ops);
5716 if (doNotPrint) {
5717 ps << PP::end;
5718 startStatement();
5719 ps << "*/";
5720 setPendingNewline();
5721 }
5722 return success();
5723}
5724
5725void StmtEmitter::emitInstancePortList(Operation *op,
5726 ModulePortInfo &modPortInfo,
5727 ArrayRef<Value> instPortValues) {
5728 SmallPtrSet<Operation *, 8> ops;
5729 ops.insert(op);
5730
5731 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5732 ModulePortInfo containingPortList(containingModule.getPortList());
5733
5734 ps << " (";
5735
5736 // Get the max port name length so we can align the '('.
5737 size_t maxNameLength = 0;
5738 for (auto &elt : modPortInfo) {
5739 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5740 }
5741
5742 auto getWireForValue = [&](Value result) {
5743 return result.getUsers().begin()->getOperand(0);
5744 };
5745
5746 // Emit the argument and result ports.
5747 bool isFirst = true; // True until we print a port.
5748 bool isZeroWidth = false;
5749
5750 for (size_t portNum = 0, portEnd = modPortInfo.size(); portNum < portEnd;
5751 ++portNum) {
5752 auto &modPort = modPortInfo.at(portNum);
5753 isZeroWidth = isZeroBitType(modPort.type);
5754 Value portVal = instPortValues[portNum];
5755
5756 // Decide if we should print a comma. We can't do this if we're the first
5757 // port or if all the subsequent ports are zero width.
5758 if (!isFirst) {
5759 bool shouldPrintComma = true;
5760 if (isZeroWidth) {
5761 shouldPrintComma = false;
5762 for (size_t i = portNum + 1, e = modPortInfo.size(); i != e; ++i)
5763 if (!isZeroBitType(modPortInfo.at(i).type)) {
5764 shouldPrintComma = true;
5765 break;
5766 }
5767 }
5768
5769 if (shouldPrintComma)
5770 ps << ",";
5771 }
5772 emitLocationInfoAndNewLine(ops);
5773
5774 // Emit the port's name.
5775 startStatement();
5776 if (!isZeroWidth) {
5777 // If this is a real port we're printing, then it isn't the first one. Any
5778 // subsequent ones will need a comma.
5779 isFirst = false;
5780 ps << " ";
5781 } else {
5782 // We comment out zero width ports, so their presence and initializer
5783 // expressions are still emitted textually.
5784 ps << "//";
5785 }
5786
5787 ps.scopedBox(isZeroWidth ? PP::neverbox : PP::ibox2, [&]() {
5788 auto modPortName = modPort.getVerilogName();
5789 ps << "." << PPExtString(modPortName);
5790 ps.spaces(maxNameLength - modPortName.size() + 1);
5791 ps << "(";
5792 ps.scopedBox(PP::ibox0, [&]() {
5793 // Emit the value as an expression.
5794 ops.clear();
5795
5796 // Output ports that are not connected to single use output ports were
5797 // lowered to wire.
5798 OutputOp output;
5799 if (!modPort.isOutput()) {
5800 if (isZeroWidth &&
5801 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5802 ps << "/* Zero width */";
5803 else
5804 emitExpression(portVal, ops, LowestPrecedence);
5805 } else if (portVal.use_empty()) {
5806 ps << "/* unused */";
5807 } else if (portVal.hasOneUse() &&
5808 (output = dyn_cast_or_null<OutputOp>(
5809 portVal.getUses().begin()->getOwner()))) {
5810 // If this is directly using the output port of the containing module,
5811 // just specify that directly so we avoid a temporary wire.
5812 // Keep this synchronized with countStatements() and
5813 // visitStmt(OutputOp).
5814 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5815 ps << PPExtString(
5816 containingPortList.atOutput(outputPortNo).getVerilogName());
5817 } else {
5818 portVal = getWireForValue(portVal);
5819 emitExpression(portVal, ops);
5820 }
5821 ps << ")";
5822 });
5823 });
5824 }
5825 if (!isFirst || isZeroWidth) {
5826 emitLocationInfoAndNewLine(ops);
5827 ops.clear();
5828 startStatement();
5829 }
5830 ps << ");";
5831}
5832
5833// This may be called in the top-level, not just in an hw.module. Thus we can't
5834// use the name map to find expression names for arguments to the instance, nor
5835// do we need to emit subexpressions. Prepare pass, which has run for all
5836// modules prior to this, has ensured that all arguments are bound to wires,
5837// regs, or ports, with legalized names, so we can lookup up the names through
5838// the IR.
5839LogicalResult StmtEmitter::visitSV(BindOp op) {
5840 emitter.emitBind(op);
5841 assert(state.pendingNewline);
5842 return success();
5843}
5844
5845LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5846 emitComment(op.getCommentAttr());
5847 // Emit SV attributes.
5848 emitSVAttributes(op);
5849 // TODO: source info!
5850 startStatement();
5851 ps.addCallback({op, true});
5852 ps << "interface " << PPExtString(getSymOpName(op)) << ";";
5853 setPendingNewline();
5854 // FIXME: Don't emit the body of this as general statements, they aren't!
5855 emitStatementBlock(*op.getBodyBlock());
5856 startStatement();
5857 ps << "endinterface" << PP::newline;
5858 ps.addCallback({op, false});
5859 setPendingNewline();
5860 return success();
5861}
5862
5863LogicalResult StmtEmitter::visitSV(sv::SVVerbatimSourceOp op) {
5864 emitSVAttributes(op);
5865 startStatement();
5866 ps.addCallback({op, true});
5867
5868 ps << op.getContent();
5869
5870 ps.addCallback({op, false});
5871 setPendingNewline();
5872 return success();
5873}
5874
5875LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5876 // Emit SV attributes.
5877 emitSVAttributes(op);
5878 startStatement();
5879 ps.addCallback({op, true});
5880 if (isZeroBitType(op.getType()))
5881 ps << PP::neverbox << "// ";
5882 ps.invokeWithStringOS([&](auto &os) {
5883 emitter.printPackedType(stripUnpackedTypes(op.getType()), os, op->getLoc(),
5884 Type(), false);
5885 });
5886 ps << PP::nbsp << PPExtString(getSymOpName(op));
5887 ps.invokeWithStringOS(
5888 [&](auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5889 ps << ";";
5890 if (isZeroBitType(op.getType()))
5891 ps << PP::end; // Close never-break group.
5892 ps.addCallback({op, false});
5893 setPendingNewline();
5894 return success();
5895}
5896
5897LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5898 startStatement();
5899 ps.addCallback({op, true});
5900 ps << "modport " << PPExtString(getSymOpName(op)) << "(";
5901
5902 // TODO: revisit, better breaks/grouping.
5903 llvm::interleaveComma(op.getPorts(), ps, [&](const Attribute &portAttr) {
5904 auto port = cast<ModportStructAttr>(portAttr);
5905 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) << " ";
5906 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5907 ps << PPExtString(getSymOpName(signalDecl));
5908 });
5909
5910 ps << ");";
5911 ps.addCallback({op, false});
5912 setPendingNewline();
5913 return success();
5914}
5915
5916LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5917 startStatement();
5918 ps.addCallback({op, true});
5919 SmallPtrSet<Operation *, 8> emitted;
5920 // TODO: emit like emitAssignLike does, maybe refactor.
5921 ps << "assign ";
5922 emitExpression(op.getIface(), emitted);
5923 ps << "." << PPExtString(op.getSignalName()) << " = ";
5924 emitExpression(op.getRhs(), emitted);
5925 ps << ";";
5926 ps.addCallback({op, false});
5927 setPendingNewline();
5928 return success();
5929}
5930
5931LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5932 startStatement();
5933 ps << "`" << op.getMacroIdentifier();
5934 setPendingNewline();
5935 return success();
5936}
5937
5938LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5939 auto decl = op.getReferencedMacro(&state.symbolCache);
5940 // TODO: source info!
5941 startStatement();
5942 ps.addCallback({op, true});
5943 ps << "`define " << PPExtString(getSymOpName(decl));
5944 if (decl.getArgs()) {
5945 ps << "(";
5946 llvm::interleaveComma(*decl.getArgs(), ps, [&](const Attribute &name) {
5947 ps << cast<StringAttr>(name);
5948 });
5949 ps << ")";
5950 }
5951 if (!op.getFormatString().empty()) {
5952 ps << " ";
5953 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5954 op.getSymbols());
5955 }
5956 ps.addCallback({op, false});
5957 setPendingNewline();
5958 return success();
5959}
5960
5961void StmtEmitter::emitStatement(Operation *op) {
5962 // Expressions may either be ignored or emitted as an expression statements.
5963 if (isVerilogExpression(op))
5964 return;
5965
5966 // Ignore LTL expressions as they are emitted as part of verification
5967 // statements. Ignore debug ops as they are emitted as part of debug info.
5968 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5969 return;
5970
5971 // Handle HW statements, SV statements.
5972 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5973 succeeded(dispatchVerifVisitor(op)))
5974 return;
5975
5976 emitOpError(op, "emission to Verilog not supported");
5977 emitPendingNewlineIfNeeded();
5978 ps << "unknown MLIR operation " << PPExtString(op->getName().getStringRef());
5979 setPendingNewline();
5980}
5981
5982/// Given an operation corresponding to a VerilogExpression, determine whether
5983/// it is safe to emit inline into a 'localparam' or 'automatic logic' varaible
5984/// initializer in a procedural region.
5985///
5986/// We can't emit exprs inline when they refer to something else that can't be
5987/// emitted inline, when they're in a general #ifdef region,
5988static bool
5990 StmtEmitter &stmtEmitter) {
5991 if (!isVerilogExpression(op))
5992 return false;
5993
5994 // If the expression exists in an #ifdef region, then bail. Emitting it
5995 // inline would cause it to be executed unconditionally, because the
5996 // declarations are outside the #ifdef.
5997 if (isa<IfDefProceduralOp>(op->getParentOp()))
5998 return false;
5999
6000 // This expression tree can be emitted into the initializer if all leaf
6001 // references are safe to refer to from here. They are only safe if they are
6002 // defined in an enclosing scope (guaranteed to already be live by now) or if
6003 // they are defined in this block and already emitted to an inline automatic
6004 // logic variable.
6005 SmallVector<Value, 8> exprsToScan(op->getOperands());
6006
6007 // This loop is guaranteed to terminate because we're only scanning up
6008 // single-use expressions and other things that 'isExpressionEmittedInline'
6009 // returns success for. Cycles won't get in here.
6010 while (!exprsToScan.empty()) {
6011 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
6012 if (!expr)
6013 continue; // Ports are always safe to reference.
6014
6015 // If this is an inout op, check that its inout op has no blocking
6016 // assignment. A register or logic might be mutated by a blocking assignment
6017 // so it is not always safe to inline.
6018 if (auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
6019 auto *defOp = readInout.getOperand().getDefiningOp();
6020
6021 // If it is a read from an inout port, it's unsafe to inline in general.
6022 if (!defOp)
6023 return false;
6024
6025 // If the operand is a wire, it's OK to inline the read.
6026 if (isa<sv::WireOp>(defOp))
6027 continue;
6028
6029 // Reject struct_field_inout/array_index_inout for now because it's
6030 // necessary to consider aliasing inout operations.
6031 if (!isa<RegOp, LogicOp>(defOp))
6032 return false;
6033
6034 // It's safe to inline if all users are read op, passign or assign.
6035 // If the op is a logic op whose single assignment is inlined into
6036 // declaration, we can inline the read.
6037 if (isa<LogicOp>(defOp) &&
6038 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6039 continue;
6040
6041 // Check that it's safe for all users to be inlined.
6042 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6043 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6044 }))
6045 continue;
6046 return false;
6047 }
6048
6049 // If this is an internal node in the expression tree, process its operands.
6050 if (isExpressionEmittedInline(expr, stmtEmitter.state.options)) {
6051 exprsToScan.append(expr->getOperands().begin(),
6052 expr->getOperands().end());
6053 continue;
6054 }
6055
6056 // Otherwise, this isn't an inlinable expression. If it is defined outside
6057 // this block, then it is live-in.
6058 if (expr->getBlock() != op->getBlock())
6059 continue;
6060
6061 // Otherwise, if it is defined in this block then it is only ok to reference
6062 // if it has already been emitted into an automatic logic.
6063 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6064 return false;
6065 }
6066
6067 return true;
6068}
6069
6070template <class AssignTy>
6071static AssignTy getSingleAssignAndCheckUsers(Operation *op) {
6072 AssignTy singleAssign;
6073 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6074 if (hasSVAttributes(user))
6075 return false;
6076
6077 if (auto assign = dyn_cast<AssignTy>(user)) {
6078 if (singleAssign)
6079 return false;
6080 singleAssign = assign;
6081 return true;
6082 }
6083
6084 return isa<ReadInOutOp>(user);
6085 }))
6086 return singleAssign;
6087 return {};
6088}
6089
6090/// Return true if `op1` dominates users of `op2`.
6091static bool checkDominanceOfUsers(Operation *op1, Operation *op2) {
6092 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6093 /// TODO: Use MLIR DominanceInfo.
6094
6095 // If the op1 and op2 are in different blocks, conservatively return false.
6096 if (op1->getBlock() != user->getBlock())
6097 return false;
6098
6099 if (op1 == user)
6100 return true;
6101
6102 return op1->isBeforeInBlock(user);
6103 });
6104}
6105
6106LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6107 emitSVAttributes(op);
6108 auto value = op->getResult(0);
6109 SmallPtrSet<Operation *, 8> opsForLocation;
6110 opsForLocation.insert(op);
6111 startStatement();
6112 ps.addCallback({op, true});
6113
6114 // Emit the leading word, like 'wire', 'reg' or 'logic'.
6115 auto type = value.getType();
6116 auto word = getVerilogDeclWord(op, emitter);
6117 auto isZeroBit = isZeroBitType(type);
6118
6119 // LocalParams always need the bitwidth, otherwise they are considered to have
6120 // an unknown size.
6121 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6122
6123 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
6124 unsigned targetColumn = 0;
6125 unsigned column = 0;
6126
6127 // Emit the declaration keyword.
6128 if (maxDeclNameWidth > 0)
6129 targetColumn += maxDeclNameWidth + 1;
6130
6131 if (isZeroBit) {
6132 ps << "// Zero width: " << PPExtString(word) << PP::space;
6133 } else if (!word.empty()) {
6134 ps << PPExtString(word);
6135 column += word.size();
6136 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6137 ps.spaces(numSpaces);
6138 column += numSpaces;
6139 }
6140
6141 SmallString<8> typeString;
6142 // Convert the port's type to a string and measure it.
6143 {
6144 llvm::raw_svector_ostream stringStream(typeString);
6145 emitter.printPackedType(stripUnpackedTypes(type), stringStream,
6146 op->getLoc(), /*optionalAliasType=*/{},
6147 /*implicitIntType=*/true, singleBitDefaultType);
6148 }
6149 // Emit the type.
6150 if (maxTypeWidth > 0)
6151 targetColumn += maxTypeWidth + 1;
6152 unsigned numSpaces = 0;
6153 if (!typeString.empty()) {
6154 ps << typeString;
6155 column += typeString.size();
6156 ++numSpaces;
6157 }
6158 if (targetColumn > column)
6159 numSpaces = targetColumn - column;
6160 ps.spaces(numSpaces);
6161 column += numSpaces;
6162
6163 // Emit the name.
6164 ps << PPExtString(getSymOpName(op));
6165
6166 // Print out any array subscripts or other post-name stuff.
6167 ps.invokeWithStringOS(
6168 [&](auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6169
6170 // Print debug info.
6171 if (state.options.printDebugInfo) {
6172 if (auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6173 auto innerSym = innerSymOp.getInnerSymAttr();
6174 if (innerSym && !innerSym.empty()) {
6175 ps << " /* ";
6176 ps.invokeWithStringOS([&](auto &os) { os << innerSym; });
6177 ps << " */";
6178 }
6179 }
6180 }
6181
6182 if (auto localparam = dyn_cast<LocalParamOp>(op)) {
6183 ps << PP::space << "=" << PP::space;
6184 ps.invokeWithStringOS([&](auto &os) {
6185 emitter.printParamValue(localparam.getValue(), os, [&]() {
6186 return op->emitOpError("invalid localparam value");
6187 });
6188 });
6189 }
6190
6191 if (auto regOp = dyn_cast<RegOp>(op)) {
6192 if (auto initValue = regOp.getInit()) {
6193 ps << PP::space << "=" << PP::space;
6194 ps.scopedBox(PP::ibox0, [&]() {
6195 emitExpression(initValue, opsForLocation, LowestPrecedence,
6196 /*isAssignmentLikeContext=*/true);
6197 });
6198 }
6199 }
6200
6201 // Try inlining an assignment into declarations.
6202 // FIXME: Unpacked array is not inlined since several tools doesn't support
6203 // that syntax. See Issue 6363.
6204 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6205 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6206 !hasLeadingUnpackedType(op->getResult(0).getType())) {
6207 // Get a single assignments if any.
6208 if (auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6209 auto *source = singleAssign.getSrc().getDefiningOp();
6210 // Check that the source value is OK to inline in the current emission
6211 // point. A port or constant is fine, otherwise check that the assign is
6212 // next to the operation.
6213 if (!source || isa<ConstantOp>(source) ||
6214 op->getNextNode() == singleAssign) {
6215 ps << PP::space << "=" << PP::space;
6216 ps.scopedBox(PP::ibox0, [&]() {
6217 emitExpression(singleAssign.getSrc(), opsForLocation,
6218 LowestPrecedence,
6219 /*isAssignmentLikeContext=*/true);
6220 });
6221 emitter.assignsInlined.insert(singleAssign);
6222 }
6223 }
6224 }
6225
6226 // Try inlining a blocking assignment to logic op declaration.
6227 // FIXME: Unpacked array is not inlined since several tools doesn't support
6228 // that syntax. See Issue 6363.
6229 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6230 op->getParentOp()->hasTrait<ProceduralRegion>() &&
6231 !hasLeadingUnpackedType(op->getResult(0).getType())) {
6232 // Get a single assignment which might be possible to inline.
6233 if (auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6234 // It is necessary for the assignment to dominate users of the op.
6235 if (checkDominanceOfUsers(singleAssign, op)) {
6236 auto *source = singleAssign.getSrc().getDefiningOp();
6237 // A port or constant can be inlined at everywhere. Otherwise, check
6238 // the validity by
6239 // `isExpressionEmittedInlineIntoProceduralDeclaration`.
6240 if (!source || isa<ConstantOp>(source) ||
6242 *this)) {
6243 ps << PP::space << "=" << PP::space;
6244 ps.scopedBox(PP::ibox0, [&]() {
6245 emitExpression(singleAssign.getSrc(), opsForLocation,
6246 LowestPrecedence,
6247 /*isAssignmentLikeContext=*/true);
6248 });
6249 // Remember that the assignment and logic op are emitted into decl.
6250 emitter.assignsInlined.insert(singleAssign);
6251 emitter.expressionsEmittedIntoDecl.insert(op);
6252 }
6253 }
6254 }
6255 }
6256 ps << ";";
6257 });
6258 ps.addCallback({op, false});
6259 emitLocationInfoAndNewLine(opsForLocation);
6260 return success();
6261}
6262
6263void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6264 // In the first pass, we fill in the symbol table, calculate the max width
6265 // of the declaration words and the max type width.
6266 NameCollector collector(emitter);
6267 collector.collectNames(block);
6268
6269 // Record maxDeclNameWidth and maxTypeWidth in the current scope.
6270 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6271 maxTypeWidth = collector.getMaxTypeWidth();
6272}
6273
6274void StmtEmitter::emitStatementBlock(Block &body) {
6275 ps.scopedBox(PP::bbox2, [&]() {
6276 // Ensure decl alignment values are preserved after the block is emitted.
6277 // These values were computed for and from all declarations in the current
6278 // block (before/after this nested block), so be sure they're restored
6279 // and not overwritten by the declaration alignment within the block.
6280 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6281 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6282
6283 // Build up the symbol table for all of the values that need names in the
6284 // module. #ifdef's in procedural regions are special because local
6285 // variables are all emitted at the top of their enclosing blocks.
6286 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6287 collectNamesAndCalculateDeclarationWidths(body);
6288
6289 // Emit the body.
6290 for (auto &op : body) {
6291 emitStatement(&op);
6292 }
6293 });
6294}
6295// NOLINTEND(misc-no-recursion)
6296
6297void ModuleEmitter::emitStatement(Operation *op) {
6298 StmtEmitter(*this, state.options).emitStatement(op);
6299}
6300
6301/// Emit SystemVerilog attributes attached to the expression op as dialect
6302/// attributes.
6303void ModuleEmitter::emitSVAttributes(Operation *op) {
6304 // SystemVerilog 2017 Section 5.12.
6305 auto svAttrs = getSVAttributes(op);
6306 if (!svAttrs)
6307 return;
6308
6309 startStatement(); // For attributes.
6310 emitSVAttributesImpl(ps, svAttrs, /*mayBreak=*/true);
6311 setPendingNewline();
6312}
6313
6314//===----------------------------------------------------------------------===//
6315// Module Driver
6316//===----------------------------------------------------------------------===//
6317
6318void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6319 auto verilogName = module.getVerilogModuleNameAttr();
6320 startStatement();
6321 ps << "// external generated module " << PPExtString(verilogName.getValue())
6322 << PP::newline;
6323 setPendingNewline();
6324}
6325
6326// This may be called in the top-level, not just in an hw.module. Thus we can't
6327// use the name map to find expression names for arguments to the instance, nor
6328// do we need to emit subexpressions. Prepare pass, which has run for all
6329// modules prior to this, has ensured that all arguments are bound to wires,
6330// regs, or ports, with legalized names, so we can lookup up the names through
6331// the IR.
6332void ModuleEmitter::emitBind(BindOp op) {
6333 if (hasSVAttributes(op))
6334 emitError(op, "SV attributes emission is unimplemented for the op");
6335 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6336
6337 HWModuleOp parentMod = inst->getParentOfType<hw::HWModuleOp>();
6338 ModulePortInfo parentPortList(parentMod.getPortList());
6339 auto parentVerilogName = getVerilogModuleNameAttr(parentMod);
6340
6341 Operation *childMod =
6342 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6343 auto childVerilogName = getVerilogModuleNameAttr(childMod);
6344
6345 startStatement();
6346 ps.addCallback({op, true});
6347 ps << "bind " << PPExtString(parentVerilogName.getValue()) << PP::nbsp
6348 << PPExtString(childVerilogName.getValue()) << PP::nbsp
6349 << PPExtString(getSymOpName(inst)) << " (";
6350 bool isFirst = true; // True until we print a port.
6351 ps.scopedBox(PP::bbox2, [&]() {
6352 auto parentPortInfo = parentMod.getPortList();
6353 ModulePortInfo childPortInfo(cast<PortList>(childMod).getPortList());
6354
6355 // Get the max port name length so we can align the '('.
6356 size_t maxNameLength = 0;
6357 for (auto &elt : childPortInfo) {
6358 auto portName = elt.getVerilogName();
6359 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6360 maxNameLength = std::max(maxNameLength, elt.getName().size());
6361 }
6362
6363 SmallVector<Value> instPortValues(childPortInfo.size());
6364 inst.getValues(instPortValues, childPortInfo);
6365 // Emit the argument and result ports.
6366 for (auto [idx, elt] : llvm::enumerate(childPortInfo)) {
6367 // Figure out which value we are emitting.
6368 Value portVal = instPortValues[idx];
6369 bool isZeroWidth = isZeroBitType(elt.type);
6370
6371 // Decide if we should print a comma. We can't do this if we're the
6372 // first port or if all the subsequent ports are zero width.
6373 if (!isFirst) {
6374 bool shouldPrintComma = true;
6375 if (isZeroWidth) {
6376 shouldPrintComma = false;
6377 for (size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6378 if (!isZeroBitType(childPortInfo.at(i).type)) {
6379 shouldPrintComma = true;
6380 break;
6381 }
6382 }
6383
6384 if (shouldPrintComma)
6385 ps << ",";
6386 }
6387 ps << PP::newline;
6388
6389 // Emit the port's name.
6390 if (!isZeroWidth) {
6391 // If this is a real port we're printing, then it isn't the first
6392 // one. Any subsequent ones will need a comma.
6393 isFirst = false;
6394 } else {
6395 // We comment out zero width ports, so their presence and
6396 // initializer expressions are still emitted textually.
6397 ps << PP::neverbox << "//";
6398 }
6399
6400 ps << "." << PPExtString(elt.getName());
6401 ps.nbsp(maxNameLength - elt.getName().size());
6402 ps << " (";
6403 llvm::SmallPtrSet<Operation *, 4> ops;
6404 if (elt.isOutput()) {
6405 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6406 "output port must have either single or no use");
6407 if (portVal.use_empty()) {
6408 ps << "/* unused */";
6409 } else if (auto output = dyn_cast_or_null<OutputOp>(
6410 portVal.getUses().begin()->getOwner())) {
6411 // If this is directly using the output port of the containing
6412 // module, just specify that directly.
6413 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6414 ps << PPExtString(
6415 parentPortList.atOutput(outputPortNo).getVerilogName());
6416 } else {
6417 portVal = portVal.getUsers().begin()->getOperand(0);
6418 ExprEmitter(*this, ops)
6419 .emitExpression(portVal, LowestPrecedence,
6420 /*isAssignmentLikeContext=*/false);
6421 }
6422 } else {
6423 ExprEmitter(*this, ops)
6424 .emitExpression(portVal, LowestPrecedence,
6425 /*isAssignmentLikeContext=*/false);
6426 }
6427
6428 ps << ")";
6429
6430 if (isZeroWidth)
6431 ps << PP::end; // Close never-break group.
6432 }
6433 });
6434 if (!isFirst)
6435 ps << PP::newline;
6436 ps << ");";
6437 ps.addCallback({op, false});
6438 setPendingNewline();
6439}
6440
6441void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6442 if (hasSVAttributes(op))
6443 emitError(op, "SV attributes emission is unimplemented for the op");
6444
6445 auto instance = op.getReferencedInstance(&state.symbolCache);
6446 auto instantiator = instance->getParentOfType<HWModuleOp>().getName();
6447 auto *interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6448 instance.getInterfaceType().getInterface());
6449 startStatement();
6450 ps.addCallback({op, true});
6451 ps << "bind " << PPExtString(instantiator) << PP::nbsp
6452 << PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6453 << PPExtString(getSymOpName(instance)) << " (.*);" << PP::newline;
6454 ps.addCallback({op, false});
6455 setPendingNewline();
6456}
6457
6458void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6459 if (params.empty())
6460 return;
6461
6462 auto printParamType = [&](Type type, Attribute defaultValue,
6463 SmallString<8> &result) {
6464 result.clear();
6465 llvm::raw_svector_ostream sstream(result);
6466
6467 // If there is a default value like "32" then just print without type at
6468 // all.
6469 if (defaultValue) {
6470 if (auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6471 if (intAttr.getValue().getBitWidth() == 32)
6472 return;
6473 if (auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6474 if (fpAttr.getType().isF64())
6475 return;
6476 }
6477 if (isa<NoneType>(type))
6478 return;
6479
6480 // Classic Verilog parser don't allow a type in the parameter declaration.
6481 // For compatibility with them, we omit the type when it is implicit based
6482 // on its initializer value, and print the type commented out when it is
6483 // a 32-bit "integer" parameter.
6484 if (auto intType = type_dyn_cast<IntegerType>(type))
6485 if (intType.getWidth() == 32) {
6486 sstream << "/*integer*/";
6487 return;
6488 }
6489
6490 printPackedType(type, sstream, module->getLoc(),
6491 /*optionalAliasType=*/Type(),
6492 /*implicitIntType=*/true,
6493 // Print single-bit values as explicit `[0:0]` type.
6494 /*singleBitDefaultType=*/false);
6495 };
6496
6497 // Determine the max width of the parameter types so things are lined up.
6498 size_t maxTypeWidth = 0;
6499 SmallString<8> scratch;
6500 for (auto param : params) {
6501 auto paramAttr = cast<ParamDeclAttr>(param);
6502 // Measure the type length by printing it to a temporary string.
6503 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6504 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6505 }
6506
6507 if (maxTypeWidth > 0) // add a space if any type exists.
6508 maxTypeWidth += 1;
6509
6510 ps.scopedBox(PP::bbox2, [&]() {
6511 ps << PP::newline << "#(";
6512 ps.scopedBox(PP::cbox0, [&]() {
6513 llvm::interleave(
6514 params,
6515 [&](Attribute param) {
6516 auto paramAttr = cast<ParamDeclAttr>(param);
6517 auto defaultValue = paramAttr.getValue(); // may be null if absent.
6518 ps << "parameter ";
6519 printParamType(paramAttr.getType(), defaultValue, scratch);
6520 if (!scratch.empty())
6521 ps << scratch;
6522 if (scratch.size() < maxTypeWidth)
6523 ps.nbsp(maxTypeWidth - scratch.size());
6524
6525 ps << PPExtString(state.globalNames.getParameterVerilogName(
6526 module, paramAttr.getName()));
6527
6528 if (defaultValue) {
6529 ps << " = ";
6530 ps.invokeWithStringOS([&](auto &os) {
6531 printParamValue(defaultValue, os, [&]() {
6532 return module->emitError("parameter '")
6533 << paramAttr.getName().getValue()
6534 << "' has invalid value";
6535 });
6536 });
6537 }
6538 },
6539 [&]() { ps << "," << PP::newline; });
6540 ps << ") ";
6541 });
6542 });
6543}
6544
6545void ModuleEmitter::emitPortList(Operation *module,
6546 const ModulePortInfo &portInfo,
6547 bool emitAsTwoStateType) {
6548 ps << "(";
6549 if (portInfo.size())
6550 emitLocationInfo(module->getLoc());
6551
6552 // Determine the width of the widest type we have to print so everything
6553 // lines up nicely.
6554 bool hasOutputs = false, hasZeroWidth = false;
6555 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6556 SmallVector<SmallString<8>, 16> portTypeStrings;
6557
6558 for (size_t i = 0, e = portInfo.size(); i < e; ++i) {
6559 auto port = portInfo.at(i);
6560 hasOutputs |= port.isOutput();
6561 hasZeroWidth |= isZeroBitType(port.type);
6562 if (!isZeroBitType(port.type))
6563 lastNonZeroPort = i;
6564
6565 // Convert the port's type to a string and measure it.
6566 portTypeStrings.push_back({});
6567 {
6568 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6569 printPackedType(stripUnpackedTypes(port.type), stringStream,
6570 module->getLoc(), {}, true, true, emitAsTwoStateType);
6571 }
6572
6573 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6574 }
6575
6576 if (maxTypeWidth > 0) // add a space if any type exists
6577 maxTypeWidth += 1;
6578
6579 // Emit the port list.
6580 ps.scopedBox(PP::bbox2, [&]() {
6581 for (size_t portIdx = 0, e = portInfo.size(); portIdx != e;) {
6582 auto lastPort = e - 1;
6583
6584 ps << PP::newline;
6585 auto portType = portInfo.at(portIdx).type;
6586
6587 // If this is a zero width type, emit the port as a comment and create a
6588 // neverbox to ensure we don't insert a line break.
6589 bool isZeroWidth = false;
6590 if (hasZeroWidth) {
6591 isZeroWidth = isZeroBitType(portType);
6592 if (isZeroWidth)
6593 ps << PP::neverbox;
6594 ps << (isZeroWidth ? "// " : " ");
6595 }
6596
6597 // Emit the port direction and optional wire keyword.
6598 auto thisPortDirection = portInfo.at(portIdx).dir;
6599 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6600 (state.options.emitWireInPorts ? 5 : 0) +
6601 maxTypeWidth;
6602 // Modport-typed ports (e.g., MyBundle.sink) already encode their
6603 // direction in the interface modport definition, so we suppress the
6604 // direction and wire keywords for them.
6605 if (!isa<ModportType>(portType)) {
6606 switch (thisPortDirection) {
6607 case ModulePort::Direction::Output:
6608 ps << "output ";
6609 break;
6610 case ModulePort::Direction::Input:
6611 ps << (hasOutputs ? "input " : "input ");
6612 break;
6613 case ModulePort::Direction::InOut:
6614 ps << (hasOutputs ? "inout " : "inout ");
6615 break;
6616 }
6617 if (state.options.emitWireInPorts)
6618 ps << "wire ";
6619 if (!portTypeStrings[portIdx].empty())
6620 ps << portTypeStrings[portIdx];
6621 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6622 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6623 } else {
6624 ps << portTypeStrings[portIdx];
6625 if (portTypeStrings[portIdx].size() < startOfNamePos)
6626 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6627 }
6628
6629 // Emit the name.
6630 ps << PPExtString(portInfo.at(portIdx).getVerilogName());
6631
6632 // Emit array dimensions.
6633 ps.invokeWithStringOS(
6634 [&](auto &os) { printUnpackedTypePostfix(portType, os); });
6635
6636 // Emit the symbol.
6637 auto innerSym = portInfo.at(portIdx).getSym();
6638 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6639 ps << " /* ";
6640 ps.invokeWithStringOS([&](auto &os) { os << innerSym; });
6641 ps << " */";
6642 }
6643
6644 // Emit the comma if this is not the last real port.
6645 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6646 ps << ",";
6647
6648 // Emit the location.
6649 if (auto loc = portInfo.at(portIdx).loc)
6650 emitLocationInfo(loc);
6651
6652 if (isZeroWidth)
6653 ps << PP::end; // Close never-break group.
6654
6655 ++portIdx;
6656
6657 // If we have any more ports with the same types and the same
6658 // direction, emit them in a list one per line. Optionally skip this
6659 // behavior when requested by user.
6660 if (!state.options.disallowPortDeclSharing) {
6661 while (portIdx != e && portInfo.at(portIdx).dir == thisPortDirection &&
6662 stripUnpackedTypes(portType) ==
6663 stripUnpackedTypes(portInfo.at(portIdx).type)) {
6664 auto port = portInfo.at(portIdx);
6665 // Append this to the running port decl.
6666 ps << PP::newline;
6667
6668 bool isZeroWidth = false;
6669 if (hasZeroWidth) {
6670 isZeroWidth = isZeroBitType(portType);
6671 if (isZeroWidth)
6672 ps << PP::neverbox;
6673 ps << (isZeroWidth ? "// " : " ");
6674 }
6675
6676 ps.nbsp(startOfNamePos);
6677
6678 // Emit the name.
6679 StringRef name = port.getVerilogName();
6680 ps << PPExtString(name);
6681
6682 // Emit array dimensions.
6683 ps.invokeWithStringOS(
6684 [&](auto &os) { printUnpackedTypePostfix(port.type, os); });
6685
6686 // Emit the symbol.
6687 auto sym = port.getSym();
6688 if (state.options.printDebugInfo && sym && !sym.empty())
6689 ps << " /* inner_sym: " << PPExtString(sym.getSymName().getValue())
6690 << " */";
6691
6692 // Emit the comma if this is not the last real port.
6693 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6694 ps << ",";
6695
6696 // Emit the location.
6697 if (auto loc = port.loc)
6698 emitLocationInfo(loc);
6699
6700 if (isZeroWidth)
6701 ps << PP::end; // Close never-break group.
6702
6703 ++portIdx;
6704 }
6705 }
6706 }
6707 });
6708
6709 if (!portInfo.size()) {
6710 ps << ");";
6711 SmallPtrSet<Operation *, 8> moduleOpSet;
6712 moduleOpSet.insert(module);
6713 emitLocationInfoAndNewLine(moduleOpSet);
6714 } else {
6715 ps << PP::newline;
6716 ps << ");" << PP::newline;
6717 setPendingNewline();
6718 }
6719}
6720
6721void ModuleEmitter::emitHWModule(HWModuleOp module) {
6722 currentModuleOp = module;
6723
6724 emitComment(module.getCommentAttr());
6725 emitSVAttributes(module);
6726 startStatement();
6727 ps.addCallback({module, true});
6728 ps << "module " << PPExtString(getVerilogModuleName(module));
6729
6730 // If we have any parameters, print them on their own line.
6731 emitParameters(module, module.getParameters());
6732
6733 emitPortList(module, ModulePortInfo(module.getPortList()));
6734
6735 assert(state.pendingNewline);
6736
6737 // Emit the body of the module.
6738 StmtEmitter(*this, state.options).emitStatementBlock(*module.getBodyBlock());
6739 startStatement();
6740 ps << "endmodule";
6741 ps.addCallback({module, false});
6742 ps << PP::newline;
6743 setPendingNewline();
6744
6745 currentModuleOp = nullptr;
6746}
6747
6748void ModuleEmitter::emitFunc(FuncOp func) {
6749 // Nothing to emit for a declaration.
6750 if (func.isDeclaration())
6751 return;
6752
6753 currentModuleOp = func;
6754 startStatement();
6755 ps.addCallback({func, true});
6756 // A function is moduled as an automatic function.
6757 emitFunctionSignature(*this, ps, func, /*isAutomatic=*/true);
6758 // Emit the body of the module.
6759 StmtEmitter(*this, state.options).emitStatementBlock(*func.getBodyBlock());
6760 startStatement();
6761 ps << "endfunction";
6762 ps << PP::newline;
6763 currentModuleOp = nullptr;
6764}
6765
6766//===----------------------------------------------------------------------===//
6767// Emitter for files & file lists.
6768//===----------------------------------------------------------------------===//
6769
6770class FileEmitter : public EmitterBase {
6771public:
6772 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6773
6774 void emit(emit::FileOp op) {
6775 emit(op.getBody());
6776 ps.eof();
6777 }
6778 void emit(emit::FragmentOp op) { emit(op.getBody()); }
6779 void emit(emit::FileListOp op);
6780
6781private:
6782 void emit(Block *block);
6783
6784 void emitOp(emit::RefOp op);
6785 void emitOp(emit::VerbatimOp op);
6786};
6787
6788void FileEmitter::emit(Block *block) {
6789 for (Operation &op : *block) {
6790 TypeSwitch<Operation *>(&op)
6791 .Case<emit::VerbatimOp, emit::RefOp>([&](auto op) { emitOp(op); })
6792 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6793 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
6794 .Case<BindOp>([&](auto op) { ModuleEmitter(state).emitBind(op); })
6795 .Case<BindInterfaceOp>(
6796 [&](auto op) { ModuleEmitter(state).emitBindInterface(op); })
6797 .Case<TypeScopeOp>([&](auto typedecls) {
6798 ModuleEmitter(state).emitStatement(typedecls);
6799 })
6800 .Default(
6801 [&](auto op) { emitOpError(op, "cannot be emitted to a file"); });
6802 }
6803}
6804
6805void FileEmitter::emit(emit::FileListOp op) {
6806 // Find the associated file ops and write the paths on individual lines.
6807 for (auto sym : op.getFiles()) {
6808 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6809
6810 auto it = state.fileMapping.find(fileName);
6811 if (it == state.fileMapping.end()) {
6812 emitOpError(op, " references an invalid file: ") << sym;
6813 continue;
6814 }
6815
6816 auto file = cast<emit::FileOp>(it->second);
6817 ps << PP::neverbox << PPExtString(file.getFileName()) << PP::end
6818 << PP::newline;
6819 }
6820 ps.eof();
6821}
6822
6823void FileEmitter::emitOp(emit::RefOp op) {
6824 StringAttr target = op.getTargetAttr().getAttr();
6825 auto *targetOp = state.symbolCache.getDefinition(target);
6826 assert(isa<emit::Emittable>(targetOp) && "target must be emittable");
6827
6828 TypeSwitch<Operation *>(targetOp)
6829 .Case<sv::FuncOp>([&](auto func) { ModuleEmitter(state).emitFunc(func); })
6830 .Case<hw::HWModuleOp>(
6831 [&](auto module) { ModuleEmitter(state).emitHWModule(module); })
6832 .Case<TypeScopeOp>([&](auto typedecls) {
6833 ModuleEmitter(state).emitStatement(typedecls);
6834 })
6835 .Default(
6836 [&](auto op) { emitOpError(op, "cannot be emitted to a file"); });
6837}
6838
6839void FileEmitter::emitOp(emit::VerbatimOp op) {
6840 startStatement();
6841
6842 SmallPtrSet<Operation *, 8> ops;
6843 ops.insert(op);
6844
6845 // Emit each line of the string at a time, emitting the
6846 // location comment after the last emitted line.
6847 StringRef text = op.getText();
6848
6849 ps << PP::neverbox;
6850 do {
6851 const auto &[lhs, rhs] = text.split('\n');
6852 if (!lhs.empty())
6853 ps << PPExtString(lhs);
6854 if (!rhs.empty())
6855 ps << PP::end << PP::newline << PP::neverbox;
6856 text = rhs;
6857 } while (!text.empty());
6858 ps << PP::end;
6859
6860 emitLocationInfoAndNewLine(ops);
6861}
6862
6863//===----------------------------------------------------------------------===//
6864// Top level "file" emitter logic
6865//===----------------------------------------------------------------------===//
6866
6867/// Organize the operations in the root MLIR module into output files to be
6868/// generated. If `separateModules` is true, a handful of top-level
6869/// declarations will be split into separate output files even in the absence
6870/// of an explicit output file attribute.
6871void SharedEmitterState::gatherFiles(bool separateModules) {
6872
6873 /// Collect all the inner names from the specified module and add them to the
6874 /// IRCache. Declarations (named things) only exist at the top level of the
6875 /// module. Also keep track of any modules that contain bind operations.
6876 /// These are non-hierarchical references which we need to be careful about
6877 /// during emission.
6878 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6879 moduleOp->walk([&](Operation *op) {
6880 // Populate the symbolCache with all operations that can define a symbol.
6881 if (auto name = op->getAttrOfType<InnerSymAttr>(
6883 symbolCache.addDefinition(moduleOp->getAttrOfType<StringAttr>(
6884 SymbolTable::getSymbolAttrName()),
6885 name.getSymName(), op);
6886 if (isa<BindOp>(op))
6887 modulesContainingBinds.insert(moduleOp);
6888 });
6889 };
6890
6891 /// Collect any port marked as being referenced via symbol.
6892 auto collectPorts = [&](auto moduleOp) {
6893 auto portInfo = moduleOp.getPortList();
6894 for (auto [i, p] : llvm::enumerate(portInfo)) {
6895 if (!p.attrs || p.attrs.empty())
6896 continue;
6897 for (NamedAttribute portAttr : p.attrs) {
6898 if (auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6899 symbolCache.addDefinition(moduleOp.getNameAttr(), sym.getSymName(),
6900 moduleOp, i);
6901 }
6902 }
6903 }
6904 };
6905
6906 // Create a mapping identifying the files each symbol is emitted to.
6907 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6908 for (auto file : designOp.getOps<emit::FileOp>())
6909 for (auto refs : file.getOps<emit::RefOp>())
6910 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6911
6912 SmallString<32> outputPath;
6913 for (auto &op : *designOp.getBody()) {
6914 auto info = OpFileInfo{&op, replicatedOps.size()};
6915
6916 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6917
6918 bool hasFileName = false;
6919 bool emitReplicatedOps = !isFileOp;
6920 bool addToFilelist = !isFileOp;
6921
6922 outputPath.clear();
6923
6924 // Check if the operation has an explicit `output_file` attribute set. If
6925 // it does, extract the information from the attribute.
6926 auto attr = op.getAttrOfType<hw::OutputFileAttr>("output_file");
6927 if (attr) {
6928 LLVM_DEBUG(llvm::dbgs() << "Found output_file attribute " << attr
6929 << " on " << op << "\n";);
6930 if (!attr.isDirectory())
6931 hasFileName = true;
6932 appendPossiblyAbsolutePath(outputPath, attr.getFilename().getValue());
6933 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6934 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6935 }
6936
6937 auto separateFile = [&](Operation *op, Twine defaultFileName = "") {
6938 // If we're emitting to a separate file and the output_file attribute
6939 // didn't specify a filename, take the default one if present or emit an
6940 // error if not.
6941 if (!hasFileName) {
6942 if (!defaultFileName.isTriviallyEmpty()) {
6943 llvm::sys::path::append(outputPath, defaultFileName);
6944 } else {
6945 op->emitError("file name unspecified");
6946 encounteredError = true;
6947 llvm::sys::path::append(outputPath, "error.out");
6948 }
6949 }
6950
6951 auto destFile = StringAttr::get(op->getContext(), outputPath);
6952 auto &file = files[destFile];
6953 file.ops.push_back(info);
6954 file.emitReplicatedOps = emitReplicatedOps;
6955 file.addToFilelist = addToFilelist;
6956 file.isVerilog = outputPath.ends_with(".sv");
6957
6958 // Back-annotate the op with an OutputFileAttr if there wasn't one. If it
6959 // was a directory, back-annotate the final file path. This is so output
6960 // files are explicit in the final MLIR after export.
6961 if (!attr || attr.isDirectory()) {
6962 auto excludeFromFileListAttr =
6963 BoolAttr::get(op->getContext(), !addToFilelist);
6964 auto includeReplicatedOpsAttr =
6965 BoolAttr::get(op->getContext(), emitReplicatedOps);
6966 auto outputFileAttr = hw::OutputFileAttr::get(
6967 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6968 op->setAttr("output_file", outputFileAttr);
6969 }
6970 };
6971
6972 // Separate the operation into dedicated output file, or emit into the
6973 // root file, or replicate in all output files.
6974 TypeSwitch<Operation *>(&op)
6975 .Case<emit::FileOp, emit::FileListOp>([&](auto file) {
6976 // Emit file ops to their respective files.
6977 fileMapping.try_emplace(file.getSymNameAttr(), file);
6978 separateFile(file, file.getFileName());
6979 })
6980 .Case<emit::FragmentOp>([&](auto fragment) {
6981 fragmentMapping.try_emplace(fragment.getSymNameAttr(), fragment);
6982 })
6983 .Case<HWModuleOp>([&](auto mod) {
6984 // Build the IR cache.
6985 auto sym = mod.getNameAttr();
6986 symbolCache.addDefinition(sym, mod);
6987 collectPorts(mod);
6988 collectInstanceSymbolsAndBinds(mod);
6989
6990 if (auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6991 if (it->second.size() != 1 || attr) {
6992 // This is a temporary check, present as long as both
6993 // output_file and file operations are used.
6994 op.emitError("modules can be emitted to a single file");
6995 encounteredError = true;
6996 } else {
6997 // The op is not separated into a file as it will be
6998 // pulled into the unique file operation it references.
6999 }
7000 } else {
7001 // Emit into a separate file named after the module.
7002 if (attr || separateModules)
7003 separateFile(mod, getVerilogModuleName(mod) + ".sv");
7004 else
7005 rootFile.ops.push_back(info);
7006 }
7007 })
7008 .Case<InterfaceOp>([&](InterfaceOp intf) {
7009 // Build the IR cache.
7010 symbolCache.addDefinition(intf.getNameAttr(), intf);
7011 // Populate the symbolCache with all operations that can define a
7012 // symbol.
7013 for (auto &op : *intf.getBodyBlock())
7014 if (auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
7015 if (auto name = symOp.getNameAttr())
7016 symbolCache.addDefinition(name, symOp);
7017
7018 // Emit into a separate file named after the interface.
7019 if (attr || separateModules)
7020 separateFile(intf, intf.getSymName() + ".sv");
7021 else
7022 rootFile.ops.push_back(info);
7023 })
7024 .Case<sv::SVVerbatimSourceOp>([&](sv::SVVerbatimSourceOp op) {
7025 symbolCache.addDefinition(op.getNameAttr(), op);
7026 separateFile(op, op.getOutputFile().getFilename().getValue());
7027 })
7028 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](auto op) {
7029 // Build the IR cache.
7030 symbolCache.addDefinition(op.getNameAttr(), op);
7031 collectPorts(op);
7032 // External modules are _not_ emitted.
7033 })
7034 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7035 [&](Operation *op) {
7036 // Emit into a separate file using the specified file name or
7037 // replicate the operation in each outputfile.
7038 if (!attr) {
7039 replicatedOps.push_back(op);
7040 } else
7041 separateFile(op, "");
7042 })
7043 .Case<FuncOp>([&](auto op) {
7044 // Emit into a separate file using the specified file name or
7045 // replicate the operation in each outputfile.
7046 if (!attr) {
7047 replicatedOps.push_back(op);
7048 } else
7049 separateFile(op, "");
7050
7051 symbolCache.addDefinition(op.getSymNameAttr(), op);
7052 })
7053 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7054 symbolCache.addDefinition(schemaOp.getNameAttr(), schemaOp);
7055 })
7056 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7057 symbolCache.addDefinition(hierPathOp.getSymNameAttr(), hierPathOp);
7058 })
7059 .Case<TypeScopeOp>([&](TypeScopeOp op) {
7060 symbolCache.addDefinition(op.getNameAttr(), op);
7061 // TODO: How do we want to handle typedefs in a split output?
7062 if (!attr) {
7063 replicatedOps.push_back(op);
7064 } else
7065 separateFile(op, "");
7066 })
7067 .Case<BindOp>([&](auto op) {
7068 if (!attr) {
7069 separateFile(op, "bindfile.sv");
7070 } else {
7071 separateFile(op);
7072 }
7073 })
7074 .Case<MacroErrorOp>([&](auto op) { replicatedOps.push_back(op); })
7075 .Case<MacroDeclOp>([&](auto op) {
7076 symbolCache.addDefinition(op.getSymNameAttr(), op);
7077 })
7078 .Case<sv::ReserveNamesOp>([](auto op) {
7079 // This op was already used in gathering used names.
7080 })
7081 .Case<om::ClassLike>([&](auto op) {
7082 symbolCache.addDefinition(op.getSymNameAttr(), op);
7083 })
7084 .Case<om::ConstantOp>([&](auto op) {
7085 // Constant ops might reference symbols, skip them.
7086 })
7087 .Default([&](auto *) {
7088 op.emitError("unknown operation (SharedEmitterState::gatherFiles)");
7089 encounteredError = true;
7090 });
7091 }
7092
7093 // We've built the whole symbol cache. Freeze it so things can start
7094 // querying it (potentially concurrently).
7096}
7097
7098/// Given a FileInfo, collect all the replicated and designated operations
7099/// that go into it and append them to "thingsToEmit".
7101 EmissionList &thingsToEmit,
7102 bool emitHeader) {
7103 // Include the version string comment when the file is verilog.
7105 thingsToEmit.emplace_back(circt::getCirctVersionComment());
7106
7107 // If we're emitting replicated ops, keep track of where we are in the list.
7108 size_t lastReplicatedOp = 0;
7109
7110 bool emitHeaderInclude =
7111 emitHeader && file.emitReplicatedOps && !file.isHeader;
7112
7113 if (emitHeaderInclude)
7114 thingsToEmit.emplace_back(circtHeaderInclude);
7115
7116 size_t numReplicatedOps =
7117 file.emitReplicatedOps && !emitHeaderInclude ? replicatedOps.size() : 0;
7118
7119 // Emit each operation in the file preceded by the replicated ops not yet
7120 // printed.
7121 DenseSet<emit::FragmentOp> includedFragments;
7122 for (const auto &opInfo : file.ops) {
7123 Operation *op = opInfo.op;
7124
7125 // Emit the replicated per-file operations before the main operation's
7126 // position (if enabled).
7127 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7128 ++lastReplicatedOp)
7129 thingsToEmit.emplace_back(replicatedOps[lastReplicatedOp]);
7130
7131 // Pull in the fragments that the op references. In one file, each
7132 // fragment is emitted only once.
7133 if (auto fragments =
7134 op->getAttrOfType<ArrayAttr>(emit::getFragmentsAttrName())) {
7135 for (auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7136 auto it = fragmentMapping.find(sym.getAttr());
7137 if (it == fragmentMapping.end()) {
7138 encounteredError = true;
7139 op->emitError("cannot find referenced fragment ") << sym;
7140 continue;
7141 }
7142 emit::FragmentOp fragment = it->second;
7143 if (includedFragments.insert(fragment).second) {
7144 thingsToEmit.emplace_back(it->second);
7145 }
7146 }
7147 }
7148
7149 // Emit the operation itself.
7150 thingsToEmit.emplace_back(op);
7151 }
7152
7153 // Emit the replicated per-file operations after the last operation (if
7154 // enabled).
7155 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7156 thingsToEmit.emplace_back(replicatedOps[lastReplicatedOp]);
7157}
7158
7159static void emitOperation(VerilogEmitterState &state, Operation *op) {
7160 TypeSwitch<Operation *>(op)
7161 .Case<HWModuleOp>([&](auto op) { ModuleEmitter(state).emitHWModule(op); })
7162 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](auto op) {
7163 // External modules are _not_ emitted.
7164 })
7165 .Case<HWModuleGeneratedOp>(
7166 [&](auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7167 .Case<HWGeneratorSchemaOp>([&](auto op) { /* Empty */ })
7168 .Case<BindOp>([&](auto op) { ModuleEmitter(state).emitBind(op); })
7169 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7170 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
7171 .Case<TypeScopeOp>([&](auto typedecls) {
7172 ModuleEmitter(state).emitStatement(typedecls);
7173 })
7174 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7175 [&](auto op) { FileEmitter(state).emit(op); })
7176 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7177 [&](auto op) { ModuleEmitter(state).emitStatement(op); })
7178 .Case<FuncOp>([&](auto op) { ModuleEmitter(state).emitFunc(op); })
7179 .Case<IncludeOp>([&](auto op) { ModuleEmitter(state).emitStatement(op); })
7180 .Default([&](auto *op) {
7181 state.encounteredError = true;
7182 op->emitError("unknown operation (ExportVerilog::emitOperation)");
7183 });
7184}
7185
7186/// Actually emit the collected list of operations and strings to the
7187/// specified file.
7189 llvm::formatted_raw_ostream &os,
7190 StringAttr fileName, bool parallelize) {
7191 MLIRContext *context = designOp->getContext();
7192
7193 // Disable parallelization overhead if MLIR threading is disabled.
7194 if (parallelize)
7195 parallelize &= context->isMultithreadingEnabled();
7196
7197 // If we aren't parallelizing output, directly output each operation to the
7198 // specified stream.
7199 if (!parallelize) {
7200 // All the modules share the same map to store the verilog output location
7201 // on the stream.
7202 OpLocMap verilogLocMap(os);
7203 VerilogEmitterState state(designOp, *this, options, symbolCache,
7204 globalNames, fileMapping, os, fileName,
7205 verilogLocMap);
7206 size_t lineOffset = 0;
7207 for (auto &entry : thingsToEmit) {
7208 entry.verilogLocs.setStream(os);
7209 if (auto *op = entry.getOperation()) {
7210 emitOperation(state, op);
7211 // Since the modules are exported sequentially, update all the ops with
7212 // the verilog location. This also clears the map, so that the map only
7213 // contains the current iteration's ops.
7214 state.addVerilogLocToOps(lineOffset, fileName);
7215 } else {
7216 os << entry.getStringData();
7217 ++lineOffset;
7218 }
7219 }
7220
7221 if (state.encounteredError)
7222 encounteredError = true;
7223 return;
7224 }
7225
7226 // If we are parallelizing emission, we emit each independent operation to a
7227 // string buffer in parallel, then concat at the end.
7228 parallelForEach(context, thingsToEmit, [&](StringOrOpToEmit &stringOrOp) {
7229 auto *op = stringOrOp.getOperation();
7230 if (!op)
7231 return; // Ignore things that are already strings.
7232
7233 // BindOp emission reaches into the hw.module of the instance, and that
7234 // body may be being transformed by its own emission. Defer their
7235 // emission to the serial phase. They are speedy to emit anyway.
7236 if (isa<BindOp>(op) || modulesContainingBinds.count(op))
7237 return;
7238
7239 SmallString<256> buffer;
7240 llvm::raw_svector_ostream tmpStream(buffer);
7241 llvm::formatted_raw_ostream rs(tmpStream);
7242 // Each `thingToEmit` (op) uses a unique map to store verilog locations.
7243 stringOrOp.verilogLocs.setStream(rs);
7244 VerilogEmitterState state(designOp, *this, options, symbolCache,
7245 globalNames, fileMapping, rs, fileName,
7246 stringOrOp.verilogLocs);
7247 emitOperation(state, op);
7248 stringOrOp.setString(buffer);
7249 if (state.encounteredError)
7250 encounteredError = true;
7251 });
7252
7253 // Finally emit each entry now that we know it is a string.
7254 for (auto &entry : thingsToEmit) {
7255 // Almost everything is lowered to a string, just concat the strings onto
7256 // the output stream.
7257 auto *op = entry.getOperation();
7258 if (!op) {
7259 auto lineOffset = os.getLine() + 1;
7260 os << entry.getStringData();
7261 // Ensure the line numbers are offset properly in the map. Each `entry`
7262 // was exported in parallel onto independent string streams, hence the
7263 // line numbers need to be updated with the offset in the current stream.
7264 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7265 continue;
7266 }
7267 entry.verilogLocs.setStream(os);
7268
7269 // If this wasn't emitted to a string (e.g. it is a bind) do so now.
7270 VerilogEmitterState state(designOp, *this, options, symbolCache,
7271 globalNames, fileMapping, os, fileName,
7272 entry.verilogLocs);
7273 emitOperation(state, op);
7274 state.addVerilogLocToOps(0, fileName);
7275 if (state.encounteredError) {
7276 encounteredError = true;
7277 return;
7278 }
7279 }
7280}
7281
7282//===----------------------------------------------------------------------===//
7283// Unified Emitter
7284//===----------------------------------------------------------------------===//
7285
7286static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os) {
7287 LoweringOptions options(module);
7288 GlobalNameTable globalNames = legalizeGlobalNames(module, options);
7289
7290 SharedEmitterState emitter(module, options, std::move(globalNames));
7291 emitter.gatherFiles(false);
7292
7294 module.emitWarning()
7295 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7296 "created only at SplitExportVerilog";
7297
7299
7300 // Collect the contents of the main file. This is a container for anything
7301 // not explicitly split out into a separate file.
7302 emitter.collectOpsForFile(emitter.rootFile, list);
7303
7304 // Emit the separate files.
7305 for (const auto &it : emitter.files) {
7306 list.emplace_back("\n// ----- 8< ----- FILE \"" + it.first.str() +
7307 "\" ----- 8< -----\n\n");
7308 emitter.collectOpsForFile(it.second, list);
7309 }
7310
7311 // Emit the filelists.
7312 for (auto &it : emitter.fileLists) {
7313 std::string contents("\n// ----- 8< ----- FILE \"" + it.first().str() +
7314 "\" ----- 8< -----\n\n");
7315 for (auto &name : it.second)
7316 contents += name.str() + "\n";
7317 list.emplace_back(contents);
7318 }
7319
7320 llvm::formatted_raw_ostream rs(os);
7321 // Finally, emit all the ops we collected.
7322 // output file name is not known, it can be specified as command line
7323 // argument.
7324 emitter.emitOps(list, rs, StringAttr::get(module.getContext(), ""),
7325 /*parallelize=*/true);
7326 return failure(emitter.encounteredError);
7327}
7328
7329LogicalResult circt::exportVerilog(ModuleOp module, llvm::raw_ostream &os) {
7330 LoweringOptions options(module);
7331 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7332 module.walk(
7333 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7334 if (failed(failableParallelForEach(
7335 module->getContext(), modulesToPrepare,
7336 [&](auto op) { return prepareHWModule(op, options); })))
7337 return failure();
7338 return exportVerilogImpl(module, os);
7339}
7340
7341namespace {
7342
7343struct ExportVerilogPass
7344 : public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7345 ExportVerilogPass(raw_ostream &os) : os(os) {}
7346 void runOnOperation() override {
7347 // Prepare the ops in the module for emission.
7348 mlir::OpPassManager preparePM("builtin.module");
7349 preparePM.addPass(createLegalizeAnonEnums());
7350 auto &modulePM = preparePM.nestAny();
7351 modulePM.addPass(createPrepareForEmission());
7352 if (failed(runPipeline(preparePM, getOperation())))
7353 return signalPassFailure();
7354
7355 if (failed(exportVerilogImpl(getOperation(), os)))
7356 return signalPassFailure();
7357 }
7358
7359private:
7360 raw_ostream &os;
7361};
7362
7363struct ExportVerilogStreamOwnedPass : public ExportVerilogPass {
7364 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7365 : ExportVerilogPass{*os} {
7366 owned = std::move(os);
7367 }
7368
7369private:
7370 std::unique_ptr<llvm::raw_ostream> owned;
7371};
7372} // end anonymous namespace
7373
7374std::unique_ptr<mlir::Pass>
7375circt::createExportVerilogPass(std::unique_ptr<llvm::raw_ostream> os) {
7376 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7377}
7378
7379std::unique_ptr<mlir::Pass>
7380circt::createExportVerilogPass(llvm::raw_ostream &os) {
7381 return std::make_unique<ExportVerilogPass>(os);
7382}
7383
7384std::unique_ptr<mlir::Pass> circt::createExportVerilogPass() {
7385 return createExportVerilogPass(llvm::outs());
7386}
7387
7388//===----------------------------------------------------------------------===//
7389// Split Emitter
7390//===----------------------------------------------------------------------===//
7391
7392static std::unique_ptr<llvm::ToolOutputFile>
7393createOutputFile(StringRef fileName, StringRef dirname,
7394 SharedEmitterState &emitter) {
7395 // Determine the output path from the output directory and filename.
7396 SmallString<128> outputFilename(dirname);
7397 appendPossiblyAbsolutePath(outputFilename, fileName);
7398 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7399
7400 // Create the output directory if needed.
7401 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7402 if (error) {
7403 emitter.designOp.emitError("cannot create output directory \"")
7404 << outputDir << "\": " << error.message();
7405 emitter.encounteredError = true;
7406 return {};
7407 }
7408
7409 // Open the output file.
7410 std::string errorMessage;
7411 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7412 if (!output) {
7413 emitter.designOp.emitError(errorMessage);
7414 emitter.encounteredError = true;
7415 }
7416 return output;
7417}
7418
7419static void createSplitOutputFile(StringAttr fileName, FileInfo &file,
7420 StringRef dirname,
7421 SharedEmitterState &emitter) {
7422 auto output = createOutputFile(fileName, dirname, emitter);
7423 if (!output)
7424 return;
7425
7427 emitter.collectOpsForFile(file, list,
7429
7430 llvm::formatted_raw_ostream rs(output->os());
7431 // Emit the file, copying the global options into the individual module
7432 // state. Don't parallelize emission of the ops within this file - we
7433 // already parallelize per-file emission and we pay a string copy overhead
7434 // for parallelization.
7435 emitter.emitOps(list, rs,
7436 StringAttr::get(fileName.getContext(), output->getFilename()),
7437 /*parallelize=*/false);
7438 output->keep();
7439}
7440
7441static LogicalResult exportSplitVerilogImpl(ModuleOp module,
7442 StringRef dirname) {
7443 // Prepare the ops in the module for emission and legalize the names that will
7444 // end up in the output.
7445 LoweringOptions options(module);
7446 GlobalNameTable globalNames = legalizeGlobalNames(module, options);
7447
7448 SharedEmitterState emitter(module, options, std::move(globalNames));
7449 emitter.gatherFiles(true);
7450
7451 if (emitter.options.emitReplicatedOpsToHeader) {
7452 // Add a header to the file list.
7453 bool insertSuccess =
7454 emitter.files
7455 .insert({StringAttr::get(module.getContext(), circtHeader),
7456 FileInfo{/*ops*/ {},
7457 /*emitReplicatedOps*/ true,
7458 /*addToFilelist*/ true,
7459 /*isHeader*/ true}})
7460 .second;
7461 if (!insertSuccess) {
7462 module.emitError() << "tried to emit a heder to " << circtHeader
7463 << ", but the file is used as an output too.";
7464 return failure();
7465 }
7466 }
7467
7468 // Emit each file in parallel if context enables it.
7469 parallelForEach(module->getContext(), emitter.files.begin(),
7470 emitter.files.end(), [&](auto &it) {
7471 createSplitOutputFile(it.first, it.second, dirname,
7472 emitter);
7473 });
7474
7475 // Write the file list.
7476 SmallString<128> filelistPath(dirname);
7477 llvm::sys::path::append(filelistPath, "filelist.f");
7478
7479 std::string errorMessage;
7480 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7481 if (!output) {
7482 module->emitError(errorMessage);
7483 return failure();
7484 }
7485
7486 for (const auto &it : emitter.files) {
7487 if (it.second.addToFilelist)
7488 output->os() << it.first.str() << "\n";
7489 }
7490 output->keep();
7491
7492 // Emit the filelists.
7493 for (auto &it : emitter.fileLists) {
7494 auto output = createOutputFile(it.first(), dirname, emitter);
7495 if (!output)
7496 continue;
7497 for (auto &name : it.second)
7498 output->os() << name.str() << "\n";
7499 output->keep();
7500 }
7501
7502 return failure(emitter.encounteredError);
7503}
7504
7505LogicalResult circt::exportSplitVerilog(ModuleOp module, StringRef dirname) {
7506 LoweringOptions options(module);
7507 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7508 module.walk(
7509 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7510 if (failed(failableParallelForEach(
7511 module->getContext(), modulesToPrepare,
7512 [&](auto op) { return prepareHWModule(op, options); })))
7513 return failure();
7514
7515 return exportSplitVerilogImpl(module, dirname);
7516}
7517
7518namespace {
7519
7520struct ExportSplitVerilogPass
7521 : public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7522 ExportSplitVerilogPass(StringRef directory) {
7523 directoryName = directory.str();
7524 }
7525 void runOnOperation() override {
7526 // Prepare the ops in the module for emission.
7527 mlir::OpPassManager preparePM("builtin.module");
7528
7529 auto &modulePM = preparePM.nest<hw::HWModuleOp>();
7530 modulePM.addPass(createPrepareForEmission());
7531 if (failed(runPipeline(preparePM, getOperation())))
7532 return signalPassFailure();
7533
7534 if (failed(exportSplitVerilogImpl(getOperation(), directoryName)))
7535 return signalPassFailure();
7536 }
7537};
7538} // end anonymous namespace
7539
7540std::unique_ptr<mlir::Pass>
7541circt::createExportSplitVerilogPass(StringRef directory) {
7542 return std::make_unique<ExportSplitVerilogPass>(directory);
7543}
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
static bool hasSVAttributes(Operation *op)
Definition CombFolds.cpp:67
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
StringRef circtHeader
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
BlockStatementCount
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool haveMatchingDims(Type a, Type b, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
True iff 'a' and 'b' have the same wire dims.
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
Push this type's dimension into a vector.
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static std::unique_ptr< Context > context
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
#define isdigit(x)
Definition FIRLexer.cpp:26
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
Definition HWOps.cpp:505
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition HWOps.cpp:1438
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
Signals that an operation's regions are procedural.
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
Definition HWSymCache.h:76
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=invalidPort)
Definition HWSymCache.h:44
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
Definition HWVisitors.h:89
This helps visit TypeOp nodes.
Definition HWVisitors.h:25
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
Definition HWVisitors.h:27
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
Definition HWVisitors.h:57
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Definition HWVisitors.h:50
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
Definition sv.py:70
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
Definition EmitOps.h:30
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
Definition HWOps.cpp:59
StringRef getVerilogModuleName(Operation *module)
Definition HWOps.h:56
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
Definition HWOps.cpp:551
mlir::Type getCanonicalType(mlir::Type type)
Definition HWTypes.cpp:49
void info(Twine message)
Definition LSPUtils.cpp:20
PP
Send one of these to TokenStream to add the corresponding token.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
Definition SVOps.cpp:875
circt::hw::InOutType InOutType
Definition SVTypes.h:25
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
Definition Path.cpp:37
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
Definition Path.cpp:26
Definition comb.py:1
Definition emit.py:1
Definition hw.py:1
Definition sv.py:1
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
This class keeps track of global names at the module/interface level.
Information to control the emission of a single operation into a file.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
mlir::Type type
Definition HWTypes.h:32
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Definition HWTypes.h:93
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.