19 #include "../PassDetail.h"
41 #include "mlir/IR/BuiltinOps.h"
42 #include "mlir/IR/ImplicitLocOpBuilder.h"
43 #include "mlir/IR/Location.h"
44 #include "mlir/IR/Threading.h"
45 #include "mlir/Interfaces/FunctionImplementation.h"
46 #include "mlir/Pass/PassManager.h"
47 #include "mlir/Support/FileUtilities.h"
48 #include "llvm/ADT/MapVector.h"
49 #include "llvm/ADT/STLExtras.h"
50 #include "llvm/ADT/StringSet.h"
51 #include "llvm/ADT/TypeSwitch.h"
52 #include "llvm/Support/FileSystem.h"
53 #include "llvm/Support/FormattedStream.h"
54 #include "llvm/Support/Path.h"
55 #include "llvm/Support/SaveAndRestore.h"
56 #include "llvm/Support/ToolOutputFile.h"
57 #include "llvm/Support/raw_ostream.h"
59 using namespace circt;
64 using namespace ExportVerilog;
66 using namespace pretty;
68 #define DEBUG_TYPE "export-verilog"
76 enum VerilogPrecedence {
97 enum SubExprSignResult { IsSigned, IsUnsigned };
103 VerilogPrecedence precedence;
106 SubExprSignResult signedness;
108 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
109 : precedence(precedence), signedness(signedness) {}
119 return Builder(ctx).getI32IntegerAttr(value);
122 static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
123 return Builder(ctx).getIntegerAttr(t, value);
139 if (isa<VerbatimExprOp>(op)) {
140 if (op->getNumOperands() == 0 &&
141 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
146 if (isa<XMRRefOp>(op))
150 if (isa<MacroRefExprOp>(op))
160 if (op->getNumOperands() == 0)
164 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
168 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
169 auto *indexOp = array.getIndex().getDefiningOp();
170 if (!indexOp || isa<ConstantOp>(indexOp))
172 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
173 auto *readSrc = read.getInput().getDefiningOp();
175 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
190 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
191 return attr.getValue();
192 return TypeSwitch<Operation *, StringRef>(symOp)
195 .Case<InterfaceOp>([&](InterfaceOp op) {
198 .Case<InterfaceSignalOp>(
199 [&](InterfaceSignalOp op) {
return op.getSymName(); })
200 .Case<InterfaceModportOp>(
201 [&](InterfaceModportOp op) {
return op.getSymName(); })
202 .Default([&](Operation *op) {
203 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
204 return attr.getValue();
205 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
206 return attr.getValue();
207 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
208 return attr.getValue();
210 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
211 return attr.getValue();
212 return StringRef(
"");
217 template <
typename PPS>
219 os <<
"/*Zero width*/ 1\'b0";
224 auto hml = cast<HWModuleLike>(module);
225 return hml.getPort(portArgNum).getVerilogName();
230 auto hml = cast<HWModuleLike>(module);
231 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
232 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
233 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
234 return updatedName.getValue();
235 return hml.getHWModuleType().getPortName(pId);
244 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
245 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
246 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp,
247 SystemFunctionOp>(op))
257 static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
259 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
260 if (integer.getWidth() != 1)
261 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
264 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
265 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
270 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
271 dims.push_back(intType.getWidth());
275 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
276 return getTypeDims(dims, inout.getElementType(), loc);
277 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
278 return getTypeDims(dims, uarray.getElementType(), loc);
279 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
282 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
288 SmallVector<Attribute, 4> aDims;
291 SmallVector<Attribute, 4> bDims;
294 return aDims == bDims;
300 if (
auto intType = type.dyn_cast<IntegerType>())
301 return intType.getWidth() == 0;
304 if (
auto uarray = type.dyn_cast<hw::UnpackedArrayType>())
305 return uarray.getNumElements() == 0 ||
307 if (
auto array = type.dyn_cast<hw::ArrayType>())
308 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
309 if (
auto structType = type.dyn_cast<hw::StructType>())
310 return llvm::all_of(structType.getElements(),
311 [](
auto elem) { return isZeroBitType(elem.type); });
312 if (
auto enumType = type.dyn_cast<hw::EnumType>())
313 return enumType.getFields().empty();
314 if (
auto unionType = type.dyn_cast<hw::UnionType>())
327 return TypeSwitch<Type, Type>(type)
331 .Case<UnpackedArrayType>([](UnpackedArrayType arrayType) {
334 .Default([](Type type) {
return type; });
339 return TypeSwitch<Type, bool>(type)
340 .Case<
InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
343 .Case<StructType>([](
auto) {
return true; })
344 .Default([](
auto) {
return false; });
351 if (isa<RegOp>(op)) {
356 op->getResult(0).getType().cast<
InOutType>().getElementType();
364 while (
innerType.getElementType().isa<ArrayType>())
366 if (
innerType.getElementType().isa<StructType>() ||
367 innerType.getElementType().isa<TypeAliasType>())
375 if (isa<sv::WireOp>(op))
377 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
381 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
382 return interface.getInterfaceType().getInterface().getValue();
386 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
388 if (isa<LogicOp>(op)) {
395 return hasStruct ?
"automatic" :
"automatic logic";
396 return hasStruct ?
"" :
"logic";
407 return hasStructType(op->getResult(0).getType()) ?
"automatic"
417 static int compareLocs(Location lhs, Location rhs);
421 if (
auto name = lhs.getName().compare(rhs.getName()))
423 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
428 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
430 if (lhs.getLine() != rhs.getLine())
431 return lhs.getLine() < rhs.getLine() ? -1 : 1;
432 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
437 Location lhsCallee = lhs.getCallee();
438 Location rhsCallee = rhs.getCallee();
442 Location lhsCaller = lhs.getCaller();
443 Location rhsCaller = rhs.getCaller();
447 template <
typename TTargetLoc>
449 auto lhsT = dyn_cast<TTargetLoc>(lhs);
450 auto rhsT = dyn_cast<TTargetLoc>(rhs);
477 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
482 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
487 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
504 SmallPtrSetImpl<Attribute> &locationSet) {
505 llvm::TypeSwitch<Location, void>(loc)
506 .Case<FusedLoc>([&](
auto fusedLoc) {
507 for (
auto subLoc : fusedLoc.getLocations())
510 .Default([&](
auto loc) { locationSet.insert(loc); });
514 template <
typename TVector>
516 llvm::array_pod_sort(
517 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
518 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
526 SmallPtrSet<Attribute, 8> locationSet;
527 locationSet.insert(loc);
528 llvm::raw_string_ostream os(output);
529 emitLocationSetInfo(os, style, locationSet);
534 const SmallPtrSetImpl<Operation *> &ops) {
538 SmallPtrSet<Attribute, 8> locationSet;
541 llvm::raw_string_ostream os(output);
542 emitLocationSetInfo(os, style, locationSet);
550 const SmallPtrSetImpl<Attribute> &locationSet) {
551 if (style == LoweringOptions::LocationInfoStyle::None)
554 llvm::raw_string_ostream sstr(resstr);
556 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
560 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
561 "other styles must be already handled");
562 os <<
"@[" << resstr <<
"]";
571 const SmallPtrSetImpl<Attribute> &locationSet)
572 : os(os), style(style) {
573 emitLocationSetInfoImpl(locationSet);
579 emitLocationInfo(loc.getCallee());
581 emitLocationInfo(loc.getCaller());
587 bool withName = !loc.getName().empty();
589 os <<
"'" << loc.getName().strref() <<
"'(";
590 emitLocationInfo(loc.getChildLoc());
598 os << loc.getFilename().getValue();
599 if (
auto line = loc.getLine()) {
601 if (
auto col = loc.getColumn())
613 StringRef lastFileName;
614 for (
size_t i = 0, e = locVector.size(); i != e;) {
619 auto first = locVector[i];
620 if (first.getFilename() != lastFileName) {
621 lastFileName = first.getFilename();
628 first.getFilename() == locVector[
end].getFilename() &&
629 first.getLine() == locVector[
end].getLine())
634 if (
auto line = first.getLine()) {
636 if (
auto col = first.getColumn())
644 os <<
':' << first.getLine() <<
":{";
646 os << locVector[i++].getColumn();
658 llvm::TypeSwitch<Location, void>(loc)
659 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
660 [&](
auto loc) { emitLocationInfo(loc); })
661 .Case<mlir::FusedLoc>([&](
auto loc) {
662 SmallPtrSet<Attribute, 8> locationSet;
664 emitLocationSetInfoImpl(locationSet);
666 .Default([&](
auto loc) {
678 switch (locationSet.size()) {
680 emitLocationInfo(cast<LocationAttr>(*locationSet.begin()));
689 SmallVector<FileLineColLoc, 8> flcLocs;
690 SmallVector<Attribute, 8> otherLocs;
691 flcLocs.reserve(locationSet.size());
692 otherLocs.reserve(locationSet.size());
693 for (Attribute loc : locationSet) {
694 if (
auto flcLoc = loc.dyn_cast<FileLineColLoc>())
695 flcLocs.push_back(flcLoc);
697 otherLocs.push_back(loc);
708 size_t sstrSize = os.tell();
709 bool emittedAnything =
false;
710 auto recheckEmittedSomething = [&]() {
711 size_t currSize = os.tell();
712 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
713 emittedAnything |= emittedSomethingSinceLastCheck;
715 return emittedSomethingSinceLastCheck;
722 [&](Attribute loc) { emitLocationInfo(cast<LocationAttr>(loc)); },
724 if (recheckEmittedSomething()) {
726 recheckEmittedSomething();
732 if (emittedAnything && !flcLocs.empty())
735 printFileLineColSetInfo(flcLocs);
737 llvm::raw_string_ostream &
os;
749 if (v.isa<BlockArgument>())
758 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
763 if (v.getDefiningOp<ReadInterfaceSignalOp>())
776 if (
auto cast = dyn_cast<BitcastOp>(op))
777 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
781 if (op->hasOneUse() &&
782 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
790 if (isa<StructCreateOp, UnionCreateOp>(op))
795 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
799 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
800 if (verbatim.getFormatString().size() > 32)
805 for (
auto &use : op->getUses()) {
806 auto *user = use.getOwner();
816 UnionExtractOp, IndexedPartSelectOp>(user))
817 if (use.getOperandNumber() == 0 &&
828 auto usedInExprControl = [user, &use]() {
830 if (
auto disableOp = dyn_cast<ltl::DisableOp>(user))
831 return disableOp.getCondition() == use.get();
833 if (
auto clockOp = dyn_cast<ltl::ClockOp>(user))
834 return clockOp.getClock() == use.get();
837 return isa<AlwaysOp, AlwaysFFOp>(user);
840 if (!usedInExprControl())
844 auto read = dyn_cast<ReadInOutOp>(op);
847 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
858 unsigned numStatements = 0;
859 block.walk([&](Operation *op) {
861 return WalkResult::advance();
863 TypeSwitch<Operation *, unsigned>(op)
864 .Case<VerbatimOp>([&](
auto) {
870 .Case<IfOp>([&](
auto) {
881 .Case<
IfDefOp, IfDefProceduralOp>([&](
auto) { return 3; })
882 .Case<OutputOp>([&](OutputOp oop) {
885 return llvm::count_if(oop->getOperands(), [&](auto operand) {
886 Operation *op = operand.getDefiningOp();
887 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
890 .Default([](
auto) {
return 1; });
891 if (numStatements > 1)
892 return WalkResult::interrupt();
893 return WalkResult::advance();
895 if (numStatements == 0)
897 if (numStatements == 1)
907 if (op->getResult(0).use_empty())
912 if (op->hasOneUse() &&
913 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
914 *op->getUsers().begin()))
936 for (
auto &op : *elseBlock) {
937 if (
auto opIf = dyn_cast<IfOp>(op)) {
954 template <
typename PPS>
956 enum Container { NoContainer, InComment, InAttr };
957 Container currentContainer = NoContainer;
959 auto closeContainer = [&] {
960 if (currentContainer == NoContainer)
962 if (currentContainer == InComment)
964 else if (currentContainer == InAttr)
966 ps << PP::end << PP::end;
968 currentContainer = NoContainer;
971 bool isFirstContainer =
true;
972 auto openContainer = [&](Container newContainer) {
973 assert(newContainer != NoContainer);
974 if (currentContainer == newContainer)
978 if (!isFirstContainer)
979 ps << (mayBreak ? PP::space : PP::nbsp);
980 isFirstContainer =
false;
983 if (newContainer == InComment)
985 else if (newContainer == InAttr)
987 currentContainer = newContainer;
995 ps.scopedBox(PP::cbox0, [&]() {
996 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
997 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
999 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
1001 if (attr.getExpression())
1002 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
1011 if (
auto *op = val.getDefiningOp())
1014 if (
auto port = val.dyn_cast<BlockArgument>()) {
1016 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
1017 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
1019 port.getArgNumber());
1021 assert(
false &&
"unhandled value");
1033 class VerilogEmitterState {
1035 explicit VerilogEmitterState(ModuleOp designOp,
1041 llvm::formatted_raw_ostream &os,
1042 StringAttr fileName,
OpLocMap &verilogLocMap)
1043 : designOp(designOp), shared(shared), options(options),
1044 symbolCache(symbolCache), globalNames(globalNames),
1045 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1046 pp(os, options.emittedLineLength), fileName(fileName) {
1047 pp.setListener(&saver);
1070 llvm::formatted_raw_ostream &os;
1072 bool encounteredError =
false;
1073 unsigned currentIndent = 0;
1082 bool pendingNewline =
false;
1096 StringAttr fileName;
1102 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1105 verilogLocMap.
clear();
1109 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1110 void operator=(
const VerilogEmitterState &) =
delete;
1123 using CallbackDataTy = std::pair<Operation *, bool>;
1127 VerilogEmitterState &state;
1132 explicit EmitterBase(VerilogEmitterState &state)
1134 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1136 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1137 state.encounteredError =
true;
1138 return op->emitError(message);
1141 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1142 state.encounteredError =
true;
1143 return op->emitOpError(message);
1146 void emitLocationImpl(llvm::StringRef location) {
1149 ps << PP::neverbreak;
1150 if (!location.empty())
1151 ps <<
"\t// " << location;
1154 void emitLocationInfo(Location loc) {
1162 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1165 setPendingNewline();
1168 template <
typename PPS>
1169 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1170 llvm::function_ref<
void(Value)> operandEmitter,
1171 ArrayAttr symAttrs);
1177 void emitComment(StringAttr comment);
1181 void emitPendingNewlineIfNeeded() {
1182 if (state.pendingNewline) {
1183 state.pendingNewline =
false;
1187 void setPendingNewline() {
1188 assert(!state.pendingNewline);
1189 state.pendingNewline =
true;
1192 void startStatement() { emitPendingNewlineIfNeeded(); }
1195 void operator=(
const EmitterBase &) =
delete;
1196 EmitterBase(
const EmitterBase &) =
delete;
1200 template <
typename PPS>
1201 void EmitterBase::emitTextWithSubstitutions(
1202 PPS &ps, StringRef
string, Operation *op,
1203 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1214 if (
auto *itemOp = item.getOp()) {
1215 if (item.hasPort()) {
1219 if (!symOpName.empty())
1221 emitError(itemOp,
"cannot get name for symbol ") << sym;
1223 emitError(op,
"cannot get name for symbol ") << sym;
1225 return StringRef(
"<INVALID>");
1231 unsigned numSymOps = symAttrs.size();
1232 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1235 next =
string.find(
"{{", next);
1236 if (next == StringRef::npos)
1243 while (next <
string.size() &&
isdigit(
string[next]))
1246 if (start == next) {
1250 size_t operandNoLength = next - start;
1253 StringRef fmtOptsStr;
1254 if (
string[next] ==
':') {
1255 size_t startFmtOpts = next + 1;
1256 while (next <
string.size() &&
string[next] !=
'}')
1258 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1262 if (!
string.substr(next).starts_with(
"}}"))
1266 unsigned operandNo = 0;
1267 if (
string.drop_front(start)
1268 .take_front(operandNoLength)
1269 .getAsInteger(10, operandNo)) {
1270 emitError(op,
"operand substitution too large");
1276 auto before =
string.take_front(start - 2);
1277 if (!before.empty())
1282 if (operandNo < op->getNumOperands())
1284 operandEmitter(op->getOperand(operandNo));
1285 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1286 unsigned symOpNum = operandNo - op->getNumOperands();
1287 auto sym = symAttrs[symOpNum];
1288 StringRef symVerilogName;
1289 if (
auto fsym = sym.dyn_cast<FlatSymbolRefAttr>()) {
1290 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1291 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1292 auto namepath = globalRef.getNamepathAttr().getValue();
1293 for (
auto [index, sym] : llvm::enumerate(namepath)) {
1296 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1298 auto innerRef = cast<InnerRefAttr>(sym);
1299 auto ref = state.symbolCache.getInnerDefinition(
1300 innerRef.getModule(), innerRef.getName());
1301 ps << namify(innerRef, ref);
1304 symVerilogName = namify(sym, symOp);
1307 }
else if (
auto isym = sym.dyn_cast<InnerRefAttr>()) {
1308 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1310 symVerilogName = namify(sym, symOp);
1312 if (!symVerilogName.empty())
1315 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1319 string =
string.drop_front(next);
1325 while (emitUntilSubstitution())
1329 if (!
string.
empty())
1333 void EmitterBase::emitComment(StringAttr comment) {
1340 auto lineLength = state.options.emittedLineLength - state.currentIndent - 3;
1341 if (lineLength > state.options.emittedLineLength)
1346 auto ref = comment.getValue();
1348 while (!ref.empty()) {
1349 std::tie(line, ref) = ref.split(
"\n");
1356 if (line.size() <= lineLength) {
1358 setPendingNewline();
1369 auto breakPos = line.rfind(
' ', lineLength);
1371 if (breakPos == StringRef::npos) {
1372 breakPos = line.find(
' ', lineLength);
1375 if (breakPos == StringRef::npos)
1376 breakPos = line.size();
1383 setPendingNewline();
1384 breakPos = line.find_first_not_of(
' ', breakPos);
1386 if (breakPos == StringRef::npos)
1389 line = line.drop_front(breakPos);
1399 bool addPrefixUnderScore =
true;
1402 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1406 if (
auto blockArg = expr.dyn_cast<BlockArgument>()) {
1407 auto moduleOp = cast<HWModuleOp>(blockArg.getOwner()->getParentOp());
1411 }
else if (
auto *op = expr.getDefiningOp()) {
1413 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1417 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1423 addPrefixUnderScore =
false;
1425 TypeSwitch<Operation *>(op)
1428 .Case([&result](VerbatimExprOp verbatim) {
1429 verbatim.getAsmResultNames([&](Value, StringRef name) {
1433 .Case([&result](VerbatimExprSEOp verbatim) {
1434 verbatim.getAsmResultNames([&](Value, StringRef name) {
1441 if (
auto operandName =
1444 extract.getType().cast<IntegerType>().
getWidth();
1447 operandName.strref() +
"_" +
1448 Twine(extract.getLowBit()));
1451 extract.getContext(),
1452 operandName.strref() +
"_" +
1453 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1454 Twine(extract.getLowBit()));
1462 if (!result || result.strref().empty())
1466 if (addPrefixUnderScore && result.strref().front() !=
'_')
1479 class ModuleEmitter :
public EmitterBase {
1481 explicit ModuleEmitter(VerilogEmitterState &state)
1482 : EmitterBase(state), currentModuleOp(nullptr),
1486 emitPendingNewlineIfNeeded();
1490 void emitParameters(Operation *module, ArrayAttr params);
1491 void emitPortList(Operation *module,
const ModulePortInfo &portInfo);
1495 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1498 void emitStatement(Operation *op);
1499 void emitBind(BindOp op);
1500 void emitBindInterface(BindInterfaceOp op);
1502 void emitSVAttributes(Operation *op);
1505 StringRef getVerilogStructFieldName(StringAttr field) {
1506 return fieldNameResolver.getRenamedFieldName(field).getValue();
1513 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1525 bool printPackedType(Type type, raw_ostream &os, Location loc,
1526 Type optionalAliasType = {},
bool implicitIntType =
true,
1527 bool singleBitDefaultType =
true);
1531 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1539 function_ref<InFlightDiagnostic()> emitError);
1542 VerilogPrecedence parenthesizeIfLooserThan,
1543 function_ref<InFlightDiagnostic()> emitError);
1549 Operation *currentModuleOp;
1555 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1561 SmallPtrSet<Operation *, 16> assignsInlined;
1571 ModuleEmitter &emitter,
bool downTo) {
1573 os <<
"<<invalid type>>";
1576 if (
auto intAttr =
width.dyn_cast<IntegerAttr>()) {
1577 if (intAttr.getValue().isZero()) {
1578 os <<
"/*Zero Width*/";
1583 os << (intAttr.getValue().getZExtValue() - 1);
1593 auto typedAttr =
width.dyn_cast<TypedAttr>();
1595 mlir::emitError(loc,
"untyped dimension attribute ") <<
width;
1599 getIntAttr(loc.getContext(), typedAttr.getType(),
1600 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1605 emitter.printParamValue(
width, os, [loc]() {
1606 return mlir::emitError(loc,
"invalid parameter in type");
1614 static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1615 ModuleEmitter &emitter) {
1616 for (Attribute
width : dims) {
1622 void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1623 SmallVector<Attribute, 4> dims;
1638 SmallVectorImpl<Attribute> &dims,
1639 bool implicitIntType,
bool singleBitDefaultType,
1640 ModuleEmitter &emitter,
1641 Type optionalAliasType = {}) {
1642 return TypeSwitch<Type, bool>(type)
1643 .Case<IntegerType>([&](IntegerType integerType) {
1644 if (!implicitIntType)
1646 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1648 getInt32Attr(type.getContext(), integerType.getWidth()));
1649 if (!dims.empty() && !implicitIntType)
1653 return !dims.empty() || !implicitIntType;
1655 .Case<IntType>([&](IntType intType) {
1656 if (!implicitIntType)
1658 dims.push_back(intType.getWidth());
1662 .Case<ArrayType>([&](ArrayType arrayType) {
1663 dims.push_back(arrayType.getSizeAttr());
1665 implicitIntType, singleBitDefaultType,
1668 .Case<InOutType>([&](
InOutType inoutType) {
1670 implicitIntType, singleBitDefaultType,
1673 .Case<EnumType>([&](EnumType enumType) {
1675 if (enumType.getBitWidth() != 32)
1676 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1678 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1679 llvm::interleaveComma(
1680 enumType.getFields().getAsRange<StringAttr>(), os,
1681 [&](
auto enumerator) {
1682 os << emitter.fieldNameResolver.getEnumFieldName(
1683 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1688 .Case<StructType>([&](StructType structType) {
1689 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1690 os <<
"/*Zero Width*/";
1693 os <<
"struct packed {";
1694 for (
auto &element : structType.getElements()) {
1696 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1697 <<
": Zero Width;*/ ";
1700 SmallVector<Attribute, 8> structDims;
1705 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1706 emitter.printUnpackedTypePostfix(element.type, os);
1713 .Case<UnionType>([&](UnionType unionType) {
1714 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1715 os <<
"/*Zero Width*/";
1720 os <<
"union packed {";
1721 for (
auto &element : unionType.getElements()) {
1723 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1724 <<
": Zero Width;*/ ";
1728 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1730 os <<
" struct packed {";
1731 if (element.offset) {
1732 os <<
"logic [" << element.offset - 1 <<
":0] "
1733 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1737 SmallVector<Attribute, 8> structDims;
1742 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1743 emitter.printUnpackedTypePostfix(element.type, os);
1747 if (elementWidth + (int64_t)element.offset < unionWidth) {
1749 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1750 <<
"__post_padding_" << element.name.getValue() <<
";";
1752 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1761 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1762 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1763 os <<
"<<unexpected unpacked array>>";
1764 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1768 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1769 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1771 mlir::emitError(loc,
"unresolvable type reference");
1774 if (typedecl.getType() != typeRef.getInnerType()) {
1775 mlir::emitError(loc,
"declared type did not match aliased type");
1779 os << typedecl.getPreferredName();
1780 emitDims(dims, os, typedecl->getLoc(), emitter);
1783 .Default([&](Type type) {
1784 os <<
"<<invalid type '" << type <<
"'>>";
1785 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1799 bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1800 Type optionalAliasType,
1801 bool implicitIntType,
1802 bool singleBitDefaultType) {
1803 SmallVector<Attribute, 8> packedDimensions;
1805 singleBitDefaultType, *
this, optionalAliasType);
1811 void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1812 TypeSwitch<Type, void>(type)
1814 printUnpackedTypePostfix(inoutType.getElementType(), os);
1816 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1817 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1818 : state.designOp->getLoc();
1819 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1821 printUnpackedTypePostfix(arrayType.getElementType(), os);
1823 .Case<InterfaceType>([&](
auto) {
1838 function_ref<InFlightDiagnostic()> emitError) {
1839 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1848 VerilogPrecedence parenthesizeIfLooserThan,
1849 function_ref<InFlightDiagnostic()> emitError) {
1850 if (
auto intAttr = value.dyn_cast<IntegerAttr>()) {
1851 IntegerType intTy = intAttr.getType().cast<IntegerType>();
1852 APInt value = intAttr.getValue();
1856 if (intTy.getWidth() > 32) {
1858 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1862 if (intTy.isSigned())
1863 os << intTy.getWidth() <<
"'sd";
1865 os << intTy.getWidth() <<
"'d";
1867 value.print(os, intTy.isSigned());
1868 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1870 if (
auto strAttr = value.dyn_cast<StringAttr>()) {
1872 os.write_escaped(strAttr.getValue());
1874 return {Symbol, IsUnsigned};
1876 if (
auto fpAttr = value.dyn_cast<FloatAttr>()) {
1878 os << fpAttr.getValueAsDouble();
1879 return {Symbol, IsUnsigned};
1881 if (
auto verbatimParam = value.dyn_cast<ParamVerbatimAttr>()) {
1882 os << verbatimParam.getValue().getValue();
1883 return {Symbol, IsUnsigned};
1885 if (
auto parameterRef = value.dyn_cast<ParamDeclRefAttr>()) {
1887 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1888 parameterRef.getName());
1891 return {Symbol, IsUnsigned};
1895 auto expr = value.dyn_cast<ParamExprAttr>();
1897 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1898 emitError() <<
" = " << value;
1899 return {LowestPrecedence, IsUnsigned};
1902 StringRef operatorStr;
1903 StringRef openStr, closeStr;
1904 VerilogPrecedence subprecedence = LowestPrecedence;
1905 VerilogPrecedence prec;
1906 std::optional<SubExprSignResult> operandSign;
1907 bool isUnary =
false;
1908 bool hasOpenClose =
false;
1910 switch (expr.getOpcode()) {
1912 operatorStr =
" + ";
1913 subprecedence = Addition;
1916 operatorStr =
" * ";
1917 subprecedence = Multiply;
1920 operatorStr =
" & ";
1921 subprecedence = And;
1924 operatorStr =
" | ";
1928 operatorStr =
" ^ ";
1929 subprecedence = Xor;
1932 operatorStr =
" << ";
1933 subprecedence = Shift;
1937 operatorStr =
" >> ";
1938 subprecedence = Shift;
1942 operatorStr =
" >>> ";
1943 subprecedence = Shift;
1944 operandSign = IsSigned;
1947 operatorStr =
" / ";
1948 subprecedence = Multiply;
1949 operandSign = IsUnsigned;
1952 operatorStr =
" / ";
1953 subprecedence = Multiply;
1954 operandSign = IsSigned;
1957 operatorStr =
" % ";
1958 subprecedence = Multiply;
1959 operandSign = IsUnsigned;
1962 operatorStr =
" % ";
1963 subprecedence = Multiply;
1964 operandSign = IsSigned;
1967 openStr =
"$clog2(";
1969 operandSign = IsUnsigned;
1970 hasOpenClose =
true;
1973 case PEO::StrConcat:
1976 hasOpenClose =
true;
1979 subprecedence = LowestPrecedence;
1984 prec = subprecedence;
1987 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
1989 assert(isUnary || hasOpenClose ||
1990 !llvm::hasSingleElement(expr.getOperands()));
1997 auto emitOperand = [&](Attribute operand) ->
bool {
1999 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2000 if (operandSign.has_value())
2001 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2004 if (operandSign.has_value()) {
2006 signedness = *operandSign;
2008 return signedness == IsSigned;
2012 if (prec > parenthesizeIfLooserThan)
2021 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2022 for (
auto op : expr.getOperands().drop_front()) {
2025 if (expr.getOpcode() == PEO::Add) {
2026 if (
auto integer = op.dyn_cast<IntegerAttr>()) {
2027 const APInt &value = integer.getValue();
2028 if (value.isNegative() && !value.isMinSignedValue()) {
2030 allOperandsSigned &=
2038 allOperandsSigned &= emitOperand(op);
2042 if (prec > parenthesizeIfLooserThan) {
2046 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2061 class ExprEmitter :
public EmitterBase,
2063 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2064 public Visitor<ExprEmitter, SubExprInfo> {
2068 ExprEmitter(ModuleEmitter &emitter,
2069 SmallPtrSetImpl<Operation *> &emittedExprs)
2070 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2072 ExprEmitter(ModuleEmitter &emitter,
2073 SmallPtrSetImpl<Operation *> &emittedExprs,
2075 : EmitterBase(emitter.state), emitter(emitter),
2076 emittedExprs(emittedExprs), buffer(tokens),
2077 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2078 assert(state.pp.getListener() == &state.saver);
2085 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2086 bool isAssignmentLikeContext) {
2087 assert(localTokens.empty());
2089 ps.scopedBox(PP::ibox0, [&]() {
2090 emitSubExpr(exp, parenthesizeIfLooserThan,
2093 isAssignmentLikeContext);
2098 if (&buffer.tokens == &localTokens)
2099 buffer.flush(state.pp);
2104 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2105 friend class Visitor<ExprEmitter, SubExprInfo>;
2107 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2115 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2116 SubExprSignRequirement signReq = NoRequirement,
2117 bool isSelfDeterminedUnsignedValue =
false,
2118 bool isAssignmentLikeContext =
false);
2122 void emitSVAttributes(Operation *op);
2124 SubExprInfo visitUnhandledExpr(Operation *op);
2125 SubExprInfo visitInvalidComb(Operation *op) {
2126 return dispatchTypeOpVisitor(op);
2128 SubExprInfo visitUnhandledComb(Operation *op) {
2129 return visitUnhandledExpr(op);
2131 SubExprInfo visitInvalidTypeOp(Operation *op) {
2132 return dispatchSVVisitor(op);
2134 SubExprInfo visitUnhandledTypeOp(Operation *op) {
2135 return visitUnhandledExpr(op);
2137 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2139 using Visitor::visitSV;
2142 enum EmitBinaryFlags {
2143 EB_RequireSignedOperands = RequireSigned,
2144 EB_RequireUnsignedOperands = RequireUnsigned,
2145 EB_OperandSignRequirementMask = 0x3,
2150 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2154 EB_ForceResultSigned = 0x8,
2159 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2160 const char *syntax,
unsigned emitBinaryFlags = 0);
2162 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2163 bool resultAlwaysUnsigned =
false);
2166 void emitSubExprIBox2(
2167 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2168 ps.scopedBox(PP::ibox2,
2169 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2174 template <
typename Container,
typename EachFn>
2175 void interleaveComma(
const Container &c, EachFn eachFn) {
2176 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2181 void interleaveComma(ValueRange ops) {
2182 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2199 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2201 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2202 CloseFunc closeFn) {
2204 ps.scopedBox(PP::cbox0, [&]() {
2205 interleaveComma(c, eachFn);
2211 template <
typename OpenFunc,
typename CloseFunc>
2212 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2213 return emitBracedList(
2214 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2218 void emitBracedList(ValueRange ops) {
2219 return emitBracedList(
2220 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2224 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2227 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2228 bool printAsPattern, Operation *op);
2230 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2231 ArrayAttr fieldValues,
bool printAsPattern,
2234 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2236 SubExprInfo visitSV(GetModportOp op);
2237 SubExprInfo visitSV(SystemFunctionOp op);
2238 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2239 SubExprInfo visitSV(XMROp op);
2240 SubExprInfo visitSV(XMRRefOp op);
2241 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2242 SubExprInfo visitSV(VerbatimExprOp op) {
2243 return visitVerbatimExprOp(op, op.getSymbols());
2245 SubExprInfo visitSV(VerbatimExprSEOp op) {
2246 return visitVerbatimExprOp(op, op.getSymbols());
2248 SubExprInfo visitSV(MacroRefExprOp op);
2249 SubExprInfo visitSV(MacroRefExprSEOp op);
2250 template <
typename MacroTy>
2251 SubExprInfo emitMacroCall(MacroTy op);
2253 SubExprInfo visitSV(ConstantXOp op);
2254 SubExprInfo visitSV(ConstantZOp op);
2255 SubExprInfo visitSV(ConstantStrOp op);
2259 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2260 emitSVAttributes(op);
2263 SubExprInfo visitSV(ArrayIndexInOutOp op);
2264 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2265 SubExprInfo visitSV(IndexedPartSelectOp op);
2266 SubExprInfo visitSV(StructFieldInOutOp op);
2269 SubExprInfo visitSV(SampledOp op);
2272 using TypeOpVisitor::visitTypeOp;
2274 SubExprInfo visitTypeOp(AggregateConstantOp op);
2276 SubExprInfo visitTypeOp(ParamValueOp op);
2283 SubExprInfo visitTypeOp(StructInjectOp op);
2284 SubExprInfo visitTypeOp(UnionCreateOp op);
2285 SubExprInfo visitTypeOp(UnionExtractOp op);
2286 SubExprInfo visitTypeOp(EnumCmpOp op);
2287 SubExprInfo visitTypeOp(EnumConstantOp op);
2290 using CombinationalVisitor::visitComb;
2291 SubExprInfo visitComb(
MuxOp op);
2292 SubExprInfo visitComb(
AddOp op) {
2293 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2294 return emitBinary(op, Addition,
"+");
2296 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2297 SubExprInfo visitComb(
MulOp op) {
2298 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2299 return emitBinary(op, Multiply,
"*");
2301 SubExprInfo visitComb(
DivUOp op) {
2302 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2304 SubExprInfo visitComb(
DivSOp op) {
2305 return emitBinary(op, Multiply,
"/", EB_RequireSignedOperands);
2307 SubExprInfo visitComb(
ModUOp op) {
2308 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2310 SubExprInfo visitComb(
ModSOp op) {
2311 return emitBinary(op, Multiply,
"%", EB_RequireSignedOperands);
2313 SubExprInfo visitComb(
ShlOp op) {
2314 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2316 SubExprInfo visitComb(
ShrUOp op) {
2318 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2320 SubExprInfo visitComb(
ShrSOp op) {
2323 return emitBinary(op, Shift,
">>>",
2324 EB_RequireSignedOperands | EB_ForceResultSigned |
2325 EB_RHS_UnsignedWithSelfDeterminedWidth);
2327 SubExprInfo visitComb(
AndOp op) {
2328 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2329 return emitBinary(op, And,
"&");
2331 SubExprInfo visitComb(
OrOp op) {
2332 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2333 return emitBinary(op, Or,
"|");
2335 SubExprInfo visitComb(
XorOp op) {
2336 if (op.isBinaryNot())
2337 return emitUnary(op,
"~");
2338 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2339 return emitBinary(op, Xor,
"^");
2344 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2346 SubExprInfo visitComb(ReplicateOp op);
2347 SubExprInfo visitComb(
ConcatOp op);
2349 SubExprInfo visitComb(ICmpOp op);
2351 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2352 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2353 "not printed within an assignment-like context");
2354 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2355 "supposed to spill such expressions";
2359 SubExprInfo printStructCreate(
2360 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2362 bool printAsPattern, Operation *op);
2365 ModuleEmitter &emitter;
2372 SubExprSignRequirement signPreference = NoRequirement;
2376 SmallPtrSetImpl<Operation *> &emittedExprs;
2379 SmallVector<Token> localTokens;
2393 bool isAssignmentLikeContext =
false;
2397 SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2399 unsigned emitBinaryFlags) {
2401 emitError(op,
"SV attributes emission is unimplemented for the op");
2412 if (emitBinaryFlags & EB_ForceResultSigned)
2413 ps <<
"$signed(" << PP::ibox0;
2414 auto operandSignReq =
2415 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2416 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2418 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2420 ps << lhsSpace << syntax << PP::nbsp;
2427 auto rhsPrec = prec;
2428 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2429 rhsPrec = VerilogPrecedence(prec - 1);
2434 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2435 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2436 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2437 operandSignReq = NoRequirement;
2440 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2441 rhsIsUnsignedValueWithSelfDeterminedWidth);
2445 SubExprSignResult signedness = IsUnsigned;
2446 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2447 signedness = IsSigned;
2449 if (emitBinaryFlags & EB_ForceResultSigned) {
2450 ps << PP::end <<
")";
2451 signedness = IsSigned;
2455 return {prec, signedness};
2458 SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2459 bool resultAlwaysUnsigned) {
2461 emitError(op,
"SV attributes emission is unimplemented for the op");
2464 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2468 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2469 resultAlwaysUnsigned ? IsUnsigned : signedness};
2474 void ExprEmitter::emitSVAttributes(Operation *op) {
2493 if (constant && constant.getValue().isZero())
2494 return concat.getOperand(1);
2504 SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2505 VerilogPrecedence parenthesizeIfLooserThan,
2506 SubExprSignRequirement signRequirement,
2507 bool isSelfDeterminedUnsignedValue,
2508 bool isAssignmentLikeContext) {
2511 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2516 auto *op = exp.getDefiningOp();
2520 if (!shouldEmitInlineExpr) {
2523 if (signRequirement == RequireSigned) {
2525 return {Symbol, IsSigned};
2529 return {Symbol, IsUnsigned};
2532 unsigned subExprStartIndex = buffer.tokens.size();
2534 ps.addCallback({op,
true});
2535 auto done = llvm::make_scope_exit([&]() {
2537 ps.addCallback({op, false});
2543 signPreference = signRequirement;
2545 bool bitCastAdded =
false;
2546 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2548 (op->getResult(0).getType().dyn_cast_or_null<IntegerType>())) {
2549 ps.addAsString(inType.getWidth());
2550 ps <<
"'(" << PP::ibox0;
2551 bitCastAdded =
true;
2555 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2556 isAssignmentLikeContext);
2557 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2563 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2565 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2567 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2568 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2571 expInfo.signedness = IsSigned;
2572 expInfo.precedence = Selection;
2573 }
else if (signRequirement == RequireUnsigned &&
2574 expInfo.signedness == IsSigned) {
2577 expInfo.signedness = IsUnsigned;
2578 expInfo.precedence = Selection;
2579 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2586 expInfo.precedence = Selection;
2593 emittedExprs.insert(exp.getDefiningOp());
2597 SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2598 auto openFn = [&]() {
2600 ps.addAsString(op.getMultiple());
2603 auto closeFn = [&]() { ps <<
"}}"; };
2607 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2608 if (op.getOperand().hasOneUse()) {
2609 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2610 return {Symbol, IsUnsigned};
2613 emitBracedList(op.getOperand(), openFn, closeFn);
2614 return {Symbol, IsUnsigned};
2617 SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2618 emitBracedList(op.getOperands());
2619 return {Symbol, IsUnsigned};
2622 SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2626 Type toType = op.getType();
2629 ps.invokeWithStringOS(
2630 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2633 return emitSubExpr(op.getInput(), LowestPrecedence);
2636 SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2637 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2638 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2639 SubExprSignRequirement signop[] = {
2641 NoRequirement, NoRequirement,
2643 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2645 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2647 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2649 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2650 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2653 if (op.isEqualAllOnes())
2654 return emitUnary(op,
"&",
true);
2657 if (op.isNotEqualZero())
2658 return emitUnary(op,
"|",
true);
2660 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2664 result.signedness = IsUnsigned;
2668 SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2670 emitError(op,
"SV attributes emission is unimplemented for the op");
2672 unsigned loBit = op.getLowBit();
2673 unsigned hiBit = loBit + op.getType().cast<IntegerType>().
getWidth() - 1;
2675 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2676 assert((x.precedence == Symbol ||
2678 "should be handled by isExpressionUnableToInline");
2683 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2687 ps.addAsString(hiBit);
2688 if (hiBit != loBit) {
2690 ps.addAsString(loBit);
2693 return {Unary, IsUnsigned};
2696 SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2698 emitError(op,
"SV attributes emission is unimplemented for the op");
2700 auto decl = op.getReferencedDecl(state.symbolCache);
2703 return {Selection, IsUnsigned};
2706 SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2708 emitError(op,
"SV attributes emission is unimplemented for the op");
2711 ps.scopedBox(PP::ibox0, [&]() {
2713 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2714 [&]() { ps <<
"," << PP::space; });
2717 return {Symbol, IsUnsigned};
2720 SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2722 emitError(op,
"SV attributes emission is unimplemented for the op");
2724 auto decl = op.getReferencedDecl(state.symbolCache);
2728 return {Selection, IsUnsigned};
2731 SubExprInfo ExprEmitter::visitSV(XMROp op) {
2733 emitError(op,
"SV attributes emission is unimplemented for the op");
2735 if (op.getIsRooted())
2737 for (
auto s : op.getPath())
2738 ps <<
PPExtString(s.cast<StringAttr>().getValue()) <<
".";
2740 return {Selection, IsUnsigned};
2745 SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2747 emitError(op,
"SV attributes emission is unimplemented for the op");
2750 auto globalRef = op.getReferencedPath(&state.symbolCache);
2751 auto namepath = globalRef.getNamepathAttr().getValue();
2752 auto *module = state.symbolCache.getDefinition(
2753 cast<InnerRefAttr>(namepath.front()).getModule());
2755 for (
auto sym : namepath) {
2757 auto innerRef = cast<InnerRefAttr>(sym);
2758 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2759 innerRef.getName());
2760 if (ref.hasPort()) {
2766 auto leaf = op.getVerbatimSuffixAttr();
2767 if (leaf && leaf.size())
2769 return {Selection, IsUnsigned};
2772 SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2774 emitError(op,
"SV attributes emission is unimplemented for the op");
2776 emitTextWithSubstitutions(
2777 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2778 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2780 return {Unary, IsUnsigned};
2783 template <
typename MacroTy>
2784 SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2786 emitError(op,
"SV attributes emission is unimplemented for the op");
2789 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2790 assert(macroOp &&
"Invalid IR");
2792 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2794 if (!op.getInputs().empty()) {
2796 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2797 emitExpression(val, LowestPrecedence, false);
2801 return {LowestPrecedence, IsUnsigned};
2804 SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2805 return emitMacroCall(op);
2808 SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2809 return emitMacroCall(op);
2812 SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2814 emitError(op,
"SV attributes emission is unimplemented for the op");
2816 ps.addAsString(op.getWidth());
2818 return {Unary, IsUnsigned};
2821 SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2823 emitError(op,
"SV attributes emission is unimplemented for the op");
2825 ps.writeQuotedEscaped(op.getStr());
2826 return {Symbol, IsUnsigned};
2829 SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2831 emitError(op,
"SV attributes emission is unimplemented for the op");
2833 ps.addAsString(op.getWidth());
2835 return {Unary, IsUnsigned};
2838 SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2839 bool isNegated =
false;
2842 if (signPreference == RequireSigned && value.isNegative() &&
2843 !value.isMinSignedValue()) {
2848 ps.addAsString(type.getWidth());
2852 if (signPreference == RequireSigned)
2858 SmallString<32> valueStr;
2860 (-value).toStringUnsigned(valueStr, 16);
2862 value.toStringUnsigned(valueStr, 16);
2865 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2868 SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2870 emitError(op,
"SV attributes emission is unimplemented for the op");
2872 auto value = op.getValue();
2876 if (value.getBitWidth() == 0) {
2877 emitOpError(op,
"will not emit zero width constants in the general case");
2878 ps <<
"<<unsupported zero width constant: "
2879 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2880 return {Unary, IsUnsigned};
2883 return printConstantScalar(value, op.getType().cast<IntegerType>());
2886 void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2887 bool printAsPattern, Operation *op) {
2888 if (printAsPattern && !isAssignmentLikeContext)
2889 emitAssignmentPatternContextError(op);
2890 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2893 elementValues, [&]() { ps << openDelim; },
2894 [&](Attribute elementValue) {
2895 printConstantAggregate(elementValue,
elementType, op);
2897 [&]() { ps <<
"}"; });
2900 void ExprEmitter::printConstantStruct(
2901 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
2902 bool printAsPattern, Operation *op) {
2903 if (printAsPattern && !isAssignmentLikeContext)
2904 emitAssignmentPatternContextError(op);
2911 auto fieldRange = llvm::make_filter_range(
2912 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
2917 if (printAsPattern) {
2919 fieldRange, [&]() { ps <<
"'{"; },
2920 [&](
const auto &fieldAndValue) {
2921 ps.scopedBox(PP::ibox2, [&]() {
2922 const auto &[field, value] = fieldAndValue;
2923 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
2924 <<
":" << PP::space;
2925 printConstantAggregate(value, field.type, op);
2928 [&]() { ps <<
"}"; });
2931 fieldRange, [&]() { ps <<
"{"; },
2932 [&](
const auto &fieldAndValue) {
2933 ps.scopedBox(PP::ibox2, [&]() {
2934 const auto &[field, value] = fieldAndValue;
2935 printConstantAggregate(value, field.type, op);
2938 [&]() { ps <<
"}"; });
2942 void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
2945 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
2946 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
2947 isAssignmentLikeContext, op);
2950 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
2951 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
2955 if (
auto structType = hw::type_dyn_cast<StructType>(type))
2956 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
2957 isAssignmentLikeContext, op);
2959 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
2960 auto value = attr.cast<IntegerAttr>().getValue();
2961 printConstantScalar(value, intType);
2965 emitOpError(op,
"contains constant of type ")
2966 << type <<
" which cannot be emitted as Verilog";
2969 SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
2971 emitError(op,
"SV attributes emission is unimplemented for the op");
2975 "zero-bit types not allowed at this point");
2977 printConstantAggregate(op.getFields(), op.getType(), op);
2978 return {Symbol, IsUnsigned};
2981 SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
2983 emitError(op,
"SV attributes emission is unimplemented for the op");
2985 return ps.invokeWithStringOS([&](
auto &os) {
2986 return emitter.printParamValue(op.getValue(), os, [&]() {
2987 return op->emitOpError(
"invalid parameter use");
2994 SubExprInfo ExprEmitter::visitTypeOp(
ArraySliceOp op) {
2996 emitError(op,
"SV attributes emission is unimplemented for the op");
2998 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3000 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3002 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3004 ps.addAsString(dstWidth);
3006 return {Selection, arrayPrec.signedness};
3009 SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3010 emitSubExpr(op.getInput(), Selection);
3015 emitSubExpr(op.getIndex(), LowestPrecedence);
3017 emitSVAttributes(op);
3018 return {Selection, IsUnsigned};
3024 emitError(op,
"SV attributes emission is unimplemented for the op");
3026 if (op.isUniform()) {
3028 ps.addAsString(op.getInputs().size());
3030 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3034 op.getInputs(), [&]() { ps <<
"{"; },
3037 emitSubExprIBox2(v);
3040 [&]() { ps <<
"}"; });
3042 return {Unary, IsUnsigned};
3047 emitError(op,
"SV attributes emission is unimplemented for the op");
3049 emitBracedList(op.getOperands());
3050 return {Unary, IsUnsigned};
3053 SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3055 emitError(op,
"SV attributes emission is unimplemented for the op");
3057 auto index = op.getIndex();
3058 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3063 emitSubExpr(index, LowestPrecedence);
3065 return {Selection, arrayPrec.signedness};
3068 SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3070 emitError(op,
"SV attributes emission is unimplemented for the op");
3072 auto prec = emitSubExpr(op.getInput(), Selection);
3074 emitSubExpr(op.getBase(), LowestPrecedence);
3075 if (op.getDecrement())
3079 ps.addAsString(op.getWidth());
3081 return {Selection, prec.signedness};
3084 SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3086 emitError(op,
"SV attributes emission is unimplemented for the op");
3088 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3090 emitSubExpr(op.getBase(), LowestPrecedence);
3091 if (op.getDecrement())
3095 ps.addAsString(op.getWidth());
3100 SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3102 emitError(op,
"SV attributes emission is unimplemented for the op");
3104 auto prec = emitSubExpr(op.getInput(), Selection);
3106 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3107 return {Selection, prec.signedness};
3110 SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3112 emitError(op,
"SV attributes emission is unimplemented for the op");
3115 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3120 SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3134 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3135 ps.scopedBox(PP::ibox0, [&]() {
3136 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3140 emitSVAttributes(op);
3142 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3143 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3147 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3148 return emitSubExpr(op.getFalseValue(), Conditional);
3151 SubExprSignResult signedness = IsUnsigned;
3152 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3153 signedness = IsSigned;
3155 return {Conditional, signedness};
3159 SubExprInfo ExprEmitter::printStructCreate(
3160 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3162 bool printAsPattern, Operation *op) {
3163 if (printAsPattern && !isAssignmentLikeContext)
3164 emitAssignmentPatternContextError(op);
3167 auto filteredFields = llvm::make_filter_range(
3168 llvm::enumerate(fieldInfos),
3169 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3171 if (printAsPattern) {
3173 filteredFields, [&]() { ps <<
"'{"; },
3174 [&](
const auto &field) {
3175 ps.scopedBox(PP::ibox2, [&]() {
3177 emitter.getVerilogStructFieldName(field.value().name))
3178 <<
":" << PP::space;
3179 fieldFn(field.value(), field.index());
3182 [&]() { ps <<
"}"; });
3185 filteredFields, [&]() { ps <<
"{"; },
3186 [&](
const auto &field) {
3187 ps.scopedBox(PP::ibox2,
3188 [&]() { fieldFn(field.value(), field.index()); });
3190 [&]() { ps <<
"}"; });
3193 return {Selection, IsUnsigned};
3198 emitError(op,
"SV attributes emission is unimplemented for the op");
3202 bool printAsPattern = isAssignmentLikeContext;
3203 StructType structType = op.getType();
3204 return printStructCreate(
3205 structType.getElements(),
3206 [&](
const auto &field,
auto index) {
3207 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3209 isAssignmentLikeContext);
3211 printAsPattern, op);
3216 emitError(op,
"SV attributes emission is unimplemented for the op");
3218 emitSubExpr(op.getInput(), Selection);
3220 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3221 return {Selection, IsUnsigned};
3224 SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3226 emitError(op,
"SV attributes emission is unimplemented for the op");
3230 bool printAsPattern = isAssignmentLikeContext;
3231 StructType structType = op.getType();
3232 return printStructCreate(
3233 structType.getElements(),
3234 [&](
const auto &field,
auto index) {
3235 if (field.name == op.getFieldNameAttr()) {
3236 emitSubExpr(op.getNewValue(), Selection);
3238 emitSubExpr(op.getInput(), Selection);
3240 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3243 printAsPattern, op);
3246 SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3247 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3248 return {Selection, IsUnsigned};
3251 SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3253 emitError(op,
"SV attributes emission is unimplemented for the op");
3254 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3257 result.signedness = IsUnsigned;
3261 SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3263 emitError(op,
"SV attributes emission is unimplemented for the op");
3268 auto &element = unionType.getElements()[op.getFieldIndex()];
3272 if (!elementWidth) {
3273 ps.addAsString(unionWidth);
3275 return {Unary, IsUnsigned};
3279 if (elementWidth == unionWidth) {
3280 emitSubExpr(op.getInput(), LowestPrecedence);
3281 return {Unary, IsUnsigned};
3286 ps.scopedBox(PP::ibox0, [&]() {
3287 if (
auto prePadding = element.offset) {
3288 ps.addAsString(prePadding);
3289 ps <<
"'h0," << PP::space;
3291 emitSubExpr(op.getInput(), Selection);
3292 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3293 ps <<
"," << PP::space;
3294 ps.addAsString(postPadding);
3300 return {Unary, IsUnsigned};
3303 SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3305 emitError(op,
"SV attributes emission is unimplemented for the op");
3306 emitSubExpr(op.getInput(), Selection);
3309 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3311 auto &element = unionType.getElements()[op.getFieldIndex()];
3313 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3314 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3323 return {Selection, IsUnsigned};
3326 SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3327 emitOpError(op,
"cannot emit this expression to Verilog");
3328 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3330 return {Symbol, IsUnsigned};
3346 enum class PropertyPrecedence {
3366 struct EmittedProperty {
3368 PropertyPrecedence precedence;
3373 class PropertyEmitter :
public EmitterBase,
3374 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3378 PropertyEmitter(ModuleEmitter &emitter,
3379 SmallPtrSetImpl<Operation *> &emittedOps)
3380 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3381 PropertyEmitter(ModuleEmitter &emitter,
3382 SmallPtrSetImpl<Operation *> &emittedOps,
3384 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3386 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3387 assert(state.pp.getListener() == &state.saver);
3395 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3398 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3399 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3403 emitNestedProperty(Value property,
3404 PropertyPrecedence parenthesizeIfLooserThan);
3406 EmittedProperty visitUnhandledLTL(Operation *op);
3407 EmittedProperty visitLTL(ltl::AndOp op);
3408 EmittedProperty visitLTL(ltl::OrOp op);
3409 EmittedProperty visitLTL(ltl::DelayOp op);
3410 EmittedProperty visitLTL(ltl::ConcatOp op);
3411 EmittedProperty visitLTL(ltl::NotOp op);
3412 EmittedProperty visitLTL(ltl::ImplicationOp op);
3413 EmittedProperty visitLTL(ltl::EventuallyOp op);
3414 EmittedProperty visitLTL(ltl::ClockOp op);
3415 EmittedProperty visitLTL(ltl::DisableOp op);
3417 void emitLTLConcat(ValueRange
inputs);
3420 ModuleEmitter &emitter;
3425 SmallPtrSetImpl<Operation *> &emittedOps;
3428 SmallVector<Token> localTokens;
3438 void PropertyEmitter::emitProperty(
3439 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3440 assert(localTokens.empty());
3442 ps.scopedBox(PP::ibox0,
3443 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3447 if (&buffer.tokens == &localTokens)
3448 buffer.flush(state.pp);
3451 EmittedProperty PropertyEmitter::emitNestedProperty(
3452 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3462 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3463 ExprEmitter(emitter, emittedOps, buffer.tokens)
3464 .emitExpression(property, LowestPrecedence,
3466 return {PropertyPrecedence::Symbol};
3469 unsigned startIndex = buffer.tokens.size();
3470 auto info = dispatchLTLVisitor(property.getDefiningOp());
3475 if (info.precedence > parenthesizeIfLooserThan) {
3477 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3478 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3480 ps << PP::end <<
")";
3482 info.precedence = PropertyPrecedence::Symbol;
3486 emittedOps.insert(property.getDefiningOp());
3490 EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3491 emitOpError(op,
"emission as Verilog property or sequence not supported");
3492 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3493 return {PropertyPrecedence::Symbol};
3496 EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3499 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3500 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3501 return {PropertyPrecedence::And};
3504 EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3507 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3508 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3509 return {PropertyPrecedence::Or};
3512 EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3514 if (
auto length = op.getLength()) {
3516 ps.addAsString(op.getDelay());
3519 ps.addAsString(op.getDelay());
3521 ps.addAsString(op.getDelay() + *length);
3525 if (op.getDelay() == 0) {
3527 }
else if (op.getDelay() == 1) {
3531 ps.addAsString(op.getDelay());
3536 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3537 return {PropertyPrecedence::Concat};
3540 void PropertyEmitter::emitLTLConcat(ValueRange
inputs) {
3541 bool addSeparator =
false;
3542 for (
auto input :
inputs) {
3545 if (!input.getDefiningOp<ltl::DelayOp>())
3546 ps <<
"##0" << PP::space;
3548 addSeparator =
true;
3549 emitNestedProperty(input, PropertyPrecedence::Concat);
3553 EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3554 emitLTLConcat(op.getInputs());
3555 return {PropertyPrecedence::Concat};
3558 EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3559 ps <<
"not" << PP::space;
3560 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3561 return {PropertyPrecedence::Unary};
3567 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3568 if (!concatOp || concatOp.getInputs().size() < 2)
3570 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3571 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3573 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3574 if (!constOp || !constOp.getValue().isOne())
3576 return concatOp.getInputs().drop_back();
3579 EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3583 emitLTLConcat(range);
3584 ps << PP::space <<
"|=>" << PP::nbsp;
3586 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3587 ps << PP::space <<
"|->" << PP::nbsp;
3589 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3590 return {PropertyPrecedence::Implication};
3593 EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3594 ps <<
"s_eventually" << PP::space;
3595 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3596 return {PropertyPrecedence::Qualifier};
3599 EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3601 ps.scopedBox(PP::ibox2, [&] {
3602 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3603 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3607 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3608 return {PropertyPrecedence::Clocking};
3611 EmittedProperty PropertyEmitter::visitLTL(ltl::DisableOp op) {
3612 ps <<
"disable iff" << PP::nbsp <<
"(";
3613 ps.scopedBox(PP::ibox2, [&] {
3614 emitNestedProperty(op.getCondition(), PropertyPrecedence::Lowest);
3618 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3619 return {PropertyPrecedence::Clocking};
3629 class NameCollector {
3631 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3635 void collectNames(Block &block);
3637 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3638 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3641 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3642 ModuleEmitter &moduleEmitter;
3647 static constexpr
size_t maxTypeWidthBound = 32;
3652 void NameCollector::collectNames(Block &block) {
3655 for (
auto &op : block) {
3659 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp>(op))
3661 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3665 for (
auto result : op.getResults()) {
3666 StringRef declName =
3668 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3669 SmallString<16> typeString;
3673 llvm::raw_svector_ostream stringStream(typeString);
3675 stringStream, op.getLoc());
3677 if (typeString.size() <= maxTypeWidthBound)
3678 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3685 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3686 for (
auto ®ion : op.getRegions()) {
3687 if (!region.empty())
3688 collectNames(region.front());
3702 class StmtEmitter :
public EmitterBase,
3703 public hw::StmtVisitor<StmtEmitter, LogicalResult>,
3704 public sv::Visitor<StmtEmitter, LogicalResult>,
3705 public verif::Visitor<StmtEmitter, LogicalResult> {
3710 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3712 void emitStatement(Operation *op);
3713 void emitStatementBlock(Block &body);
3716 LogicalResult emitDeclaration(Operation *op);
3719 void collectNamesAndCalculateDeclarationWidths(Block &block);
3722 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
3723 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
3724 bool isAssignmentLikeContext =
false);
3725 void emitSVAttributes(Operation *op);
3727 using hw::StmtVisitor<StmtEmitter, LogicalResult>::visitStmt;
3728 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
3729 using verif::Visitor<StmtEmitter, LogicalResult>::visitVerif;
3730 friend class hw::StmtVisitor<StmtEmitter, LogicalResult>;
3731 friend class sv::Visitor<StmtEmitter, LogicalResult>;
3732 friend class verif::Visitor<StmtEmitter, LogicalResult>;
3735 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
3736 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
3737 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
3738 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
3739 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
3740 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
3742 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
3743 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
3744 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
3745 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
3746 template <
typename Op>
3749 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3750 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
3751 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
3753 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3754 LogicalResult visitSV(
AssignOp op);
3755 LogicalResult visitSV(BPAssignOp op);
3756 LogicalResult visitSV(PAssignOp op);
3757 LogicalResult visitSV(ForceOp op);
3758 LogicalResult visitSV(ReleaseOp op);
3759 LogicalResult visitSV(AliasOp op);
3760 LogicalResult visitSV(InterfaceInstanceOp op);
3761 LogicalResult visitStmt(OutputOp op);
3763 LogicalResult visitStmt(InstanceOp op);
3764 LogicalResult visitStmt(InstanceChoiceOp op);
3765 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
3766 ArrayRef<Value> instPortValues);
3771 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
3772 LogicalResult visitSV(OrderedOutputOp op);
3773 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
3774 LogicalResult visitSV(IfDefProceduralOp op) {
3775 return emitIfDef(op, op.getCond());
3777 LogicalResult visitSV(IfOp op);
3778 LogicalResult visitSV(AlwaysOp op);
3779 LogicalResult visitSV(AlwaysCombOp op);
3780 LogicalResult visitSV(AlwaysFFOp op);
3781 LogicalResult visitSV(InitialOp op);
3782 LogicalResult visitSV(CaseOp op);
3783 LogicalResult visitSV(FWriteOp op);
3784 LogicalResult visitSV(VerbatimOp op);
3786 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
3787 std::optional<unsigned> verbosity);
3788 LogicalResult visitSV(StopOp op);
3789 LogicalResult visitSV(FinishOp op);
3790 LogicalResult visitSV(ExitOp op);
3792 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
3793 std::optional<unsigned> verbosity,
3795 ValueRange operands);
3796 LogicalResult visitSV(FatalOp op);
3797 LogicalResult visitSV(ErrorOp op);
3798 LogicalResult visitSV(WarningOp op);
3799 LogicalResult visitSV(InfoOp op);
3801 LogicalResult visitSV(ReadMemOp op);
3803 LogicalResult visitSV(GenerateOp op);
3804 LogicalResult visitSV(GenerateCaseOp op);
3806 LogicalResult visitSV(ForOp op);
3808 void emitAssertionLabel(Operation *op);
3809 void emitAssertionMessage(StringAttr message, ValueRange args,
3810 SmallPtrSetImpl<Operation *> &ops,
3812 template <
typename Op>
3813 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
3814 LogicalResult visitSV(AssertOp op);
3815 LogicalResult visitSV(AssumeOp op);
3816 LogicalResult visitSV(CoverOp op);
3817 template <
typename Op>
3818 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
3819 LogicalResult visitSV(AssertConcurrentOp op);
3820 LogicalResult visitSV(AssumeConcurrentOp op);
3821 LogicalResult visitSV(CoverConcurrentOp op);
3823 LogicalResult visitSV(BindOp op);
3824 LogicalResult visitSV(InterfaceOp op);
3825 LogicalResult visitSV(InterfaceSignalOp op);
3826 LogicalResult visitSV(InterfaceModportOp op);
3827 LogicalResult visitSV(AssignInterfaceSignalOp op);
3828 LogicalResult visitSV(MacroDefOp op);
3830 void emitBlockAsStatement(Block *block,
3831 const SmallPtrSetImpl<Operation *> &locationOps,
3832 StringRef multiLineComment = StringRef());
3834 LogicalResult emitVerifAssertLike(Operation *op, Value property,
3836 LogicalResult visitVerif(verif::AssertOp op);
3837 LogicalResult visitVerif(verif::AssumeOp op);
3838 LogicalResult visitVerif(verif::CoverOp op);
3841 ModuleEmitter &emitter;
3846 size_t maxDeclNameWidth = 0;
3847 size_t maxTypeWidth = 0;
3858 void StmtEmitter::emitExpression(Value exp,
3859 SmallPtrSetImpl<Operation *> &emittedExprs,
3860 VerilogPrecedence parenthesizeIfLooserThan,
3861 bool isAssignmentLikeContext) {
3862 ExprEmitter(emitter, emittedExprs)
3863 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
3868 void StmtEmitter::emitSVAttributes(Operation *op) {
3876 setPendingNewline();
3879 void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
3880 llvm::function_ref<
void()> emitRHS,
3882 std::optional<PPExtString> wordBeforeLHS) {
3884 ps.scopedBox(PP::ibox2, [&]() {
3885 if (wordBeforeLHS) {
3886 ps << *wordBeforeLHS << PP::space;
3890 ps << PP::space << syntax << PP::space;
3892 ps.scopedBox(PP::ibox0, [&]() {
3899 template <
typename Op>
3901 StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
3902 std::optional<PPExtString> wordBeforeLHS) {
3903 SmallPtrSet<Operation *, 8> ops;
3907 ps.addCallback({op,
true});
3908 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
3910 emitExpression(op.getSrc(), ops, LowestPrecedence,
3915 ps.addCallback({op,
false});
3916 emitLocationInfoAndNewLine(ops);
3920 LogicalResult StmtEmitter::visitSV(
AssignOp op) {
3923 if (dyn_cast_or_null<HWInstanceLike>(op.getSrc().getDefiningOp()))
3926 if (emitter.assignsInlined.count(op))
3930 emitSVAttributes(op);
3935 LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
3937 if (emitter.assignsInlined.count(op))
3941 emitSVAttributes(op);
3946 LogicalResult StmtEmitter::visitSV(PAssignOp op) {
3948 emitSVAttributes(op);
3953 LogicalResult StmtEmitter::visitSV(ForceOp op) {
3955 emitError(op,
"SV attributes emission is unimplemented for the op");
3960 LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
3962 emitError(op,
"SV attributes emission is unimplemented for the op");
3965 SmallPtrSet<Operation *, 8> ops;
3967 ps.addCallback({op,
true});
3968 ps.scopedBox(PP::ibox2, [&]() {
3969 ps <<
"release" << PP::space;
3970 emitExpression(op.getDest(), ops);
3973 ps.addCallback({op,
false});
3974 emitLocationInfoAndNewLine(ops);
3978 LogicalResult StmtEmitter::visitSV(AliasOp op) {
3980 emitError(op,
"SV attributes emission is unimplemented for the op");
3983 SmallPtrSet<Operation *, 8> ops;
3985 ps.addCallback({op,
true});
3986 ps.scopedBox(PP::ibox2, [&]() {
3987 ps <<
"alias" << PP::space;
3988 ps.scopedBox(PP::cbox0, [&]() {
3990 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
3991 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
3995 ps.addCallback({op,
false});
3996 emitLocationInfoAndNewLine(ops);
4000 LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4001 auto doNotPrint = op->hasAttr(
"doNotPrint");
4002 if (doNotPrint && !state.options.emitBindComments)
4006 emitError(op,
"SV attributes emission is unimplemented for the op");
4009 StringRef prefix =
"";
4010 ps.addCallback({op,
true});
4013 ps <<
"// This interface is elsewhere emitted as a bind statement."
4017 SmallPtrSet<Operation *, 8> ops;
4020 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4021 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4022 "point to an interface");
4025 if (!prefix.empty())
4031 ps.addCallback({op,
false});
4032 emitLocationInfoAndNewLine(ops);
4039 LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4040 SmallPtrSet<Operation *, 8> ops;
4041 auto parent = op->getParentOfType<PortList>();
4043 size_t operandIndex = 0;
4045 for (
PortInfo port : ports.getOutputs()) {
4046 auto operand = op.getOperand(operandIndex);
4050 if (operand.hasOneUse() && operand.getDefiningOp() &&
4051 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4060 ps.addCallback({op,
true});
4062 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
4064 ps <<
"// Zero width: ";
4066 ps <<
"assign" << PP::space;
4068 ps << PP::space <<
"=" << PP::space;
4069 ps.scopedBox(PP::ibox0, [&]() {
4073 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4074 ps <<
"/*Zero width*/";
4076 emitExpression(operand, ops, LowestPrecedence,
4081 ps.addCallback({op,
false});
4082 emitLocationInfoAndNewLine(ops);
4089 LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4091 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4092 ps <<
"`ifndef " << typescopeDef << PP::newline;
4093 ps <<
"`define " << typescopeDef;
4094 setPendingNewline();
4095 emitStatementBlock(*op.getBodyBlock());
4097 ps <<
"`endif // " << typescopeDef;
4098 setPendingNewline();
4102 LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4104 emitError(op,
"SV attributes emission is unimplemented for the op");
4109 ps << PP::neverbox <<
"// ";
4111 SmallPtrSet<Operation *, 8> ops;
4113 ps.scopedBox(PP::ibox2, [&]() {
4114 ps <<
"typedef" << PP::space;
4115 ps.invokeWithStringOS([&](
auto &os) {
4117 op.getAliasType(),
false);
4119 ps << PP::space <<
PPExtString(op.getPreferredName());
4120 ps.invokeWithStringOS(
4121 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4126 emitLocationInfoAndNewLine(ops);
4130 LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4132 emitError(op,
"SV attributes emission is unimplemented for the op");
4135 SmallPtrSet<Operation *, 8> ops;
4138 ps.addCallback({op,
true});
4140 ps.scopedBox(PP::ibox0, [&]() {
4141 emitExpression(op.getFd(), ops);
4143 ps <<
"," << PP::space;
4144 ps.writeQuotedEscaped(op.getFormatString());
4152 for (
auto operand : op.getSubstitutions()) {
4153 ps <<
"," << PP::space;
4154 emitExpression(operand, ops);
4158 ps.addCallback({op,
false});
4159 emitLocationInfoAndNewLine(ops);
4163 LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4165 emitError(op,
"SV attributes emission is unimplemented for the op");
4168 SmallPtrSet<Operation *, 8> ops;
4173 StringRef
string = op.getFormatString();
4174 if (
string.ends_with(
"\n"))
4175 string =
string.drop_back();
4180 bool isFirst =
true;
4183 while (!
string.
empty()) {
4184 auto lhsRhs =
string.split(
'\n');
4188 ps << PP::end << PP::newline << PP::neverbox;
4192 emitTextWithSubstitutions(
4193 ps, lhsRhs.first, op,
4194 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4195 string = lhsRhs.second;
4200 emitLocationInfoAndNewLine(ops);
4206 StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4207 std::optional<unsigned> verbosity) {
4209 emitError(op,
"SV attributes emission is unimplemented for the op");
4212 SmallPtrSet<Operation *, 8> ops;
4214 ps.addCallback({op,
true});
4216 if (verbosity && *verbosity != 1) {
4218 ps.addAsString(*verbosity);
4222 ps.addCallback({op,
false});
4223 emitLocationInfoAndNewLine(ops);
4227 LogicalResult StmtEmitter::visitSV(StopOp op) {
4228 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4231 LogicalResult StmtEmitter::visitSV(FinishOp op) {
4232 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4236 LogicalResult StmtEmitter::visitSV(ExitOp op) {
4237 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4243 StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4244 std::optional<unsigned> verbosity,
4245 StringAttr message, ValueRange operands) {
4247 emitError(op,
"SV attributes emission is unimplemented for the op");
4250 SmallPtrSet<Operation *, 8> ops;
4252 ps.addCallback({op,
true});
4258 if ((verbosity && *verbosity != 1) || message) {
4260 ps.scopedBox(PP::ibox0, [&]() {
4264 ps.addAsString(*verbosity);
4269 ps <<
"," << PP::space;
4270 ps.writeQuotedEscaped(message.getValue());
4272 for (
auto operand : operands) {
4273 ps <<
"," << PP::space;
4274 emitExpression(operand, ops);
4283 ps.addCallback({op,
false});
4284 emitLocationInfoAndNewLine(ops);
4288 LogicalResult StmtEmitter::visitSV(FatalOp op) {
4289 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4290 op.getMessageAttr(), op.getSubstitutions());
4293 LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4294 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4295 op.getMessageAttr(), op.getSubstitutions());
4298 LogicalResult StmtEmitter::visitSV(WarningOp op) {
4299 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4300 op.getMessageAttr(), op.getSubstitutions());
4303 LogicalResult StmtEmitter::visitSV(InfoOp op) {
4304 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4305 op.getMessageAttr(), op.getSubstitutions());
4308 LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4309 SmallPtrSet<Operation *, 8> ops({op});
4312 ps.addCallback({op,
true});
4314 switch (op.getBaseAttr().getValue()) {
4315 case MemBaseTypeAttr::MemBaseBin:
4318 case MemBaseTypeAttr::MemBaseHex:
4323 ps.scopedBox(PP::ibox0, [&]() {
4324 ps.writeQuotedEscaped(op.getFilename());
4325 ps <<
"," << PP::space;
4326 emitExpression(op.getDest(), ops);
4330 ps.addCallback({op,
false});
4331 emitLocationInfoAndNewLine(ops);
4335 LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4336 emitSVAttributes(op);
4339 ps.addCallback({op,
true});
4340 ps <<
"generate" << PP::newline;
4342 setPendingNewline();
4343 emitStatementBlock(op.getBody().getBlocks().front());
4346 ps <<
"endgenerate";
4347 ps.addCallback({op,
false});
4348 setPendingNewline();
4352 LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4353 emitSVAttributes(op);
4356 ps.addCallback({op,
true});
4358 ps.invokeWithStringOS([&](
auto &os) {
4359 emitter.printParamValue(
4360 op.getCond(), os, VerilogPrecedence::Selection,
4361 [&]() { return op->emitOpError(
"invalid case parameter"); });
4364 setPendingNewline();
4367 ArrayAttr
patterns = op.getCasePatterns();
4368 ArrayAttr caseNames = op.getCaseNames();
4369 MutableArrayRef<Region> regions = op.getCaseRegions();
4376 llvm::StringMap<size_t> nextGenIds;
4377 ps.scopedBox(PP::bbox2, [&]() {
4379 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4380 auto ®ion = regions[i];
4381 assert(region.hasOneBlock());
4382 Attribute patternAttr = patterns[i];
4385 if (!patternAttr.isa<mlir::TypedAttr>())
4388 ps.invokeWithStringOS([&](auto &os) {
4389 emitter.printParamValue(
4390 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4391 [&]() { return op->emitOpError(
"invalid case value"); });
4394 StringRef legalName =
4395 legalizeName(caseNames[i].cast<StringAttr>().getValue(), nextGenIds,
4398 setPendingNewline();
4399 emitStatementBlock(region.getBlocks().front());
4402 setPendingNewline();
4408 ps.addCallback({op,
false});
4409 setPendingNewline();
4413 LogicalResult StmtEmitter::visitSV(ForOp op) {
4414 emitSVAttributes(op);
4415 llvm::SmallPtrSet<Operation *, 8> ops;
4416 ps.addCallback({op,
true});
4418 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4421 ps.scopedBox(PP::cbox0, [&]() {
4425 ps <<
"logic" << PP::nbsp;
4426 ps.invokeWithStringOS([&](
auto &os) {
4427 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4432 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4437 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4438 [&]() { emitExpression(op.getUpperBound(), ops); },
4444 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4445 [&]() { emitExpression(op.getStep(), ops); },
4449 ps << PP::neverbreak;
4450 setPendingNewline();
4451 emitStatementBlock(op.getBody().getBlocks().front());
4454 ps.addCallback({op,
false});
4455 emitLocationInfoAndNewLine(ops);
4460 void StmtEmitter::emitAssertionLabel(Operation *op) {
4461 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4467 void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4468 SmallPtrSetImpl<Operation *> &ops,
4469 bool isConcurrent =
false) {
4472 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4473 ps.scopedBox(PP::ibox0, [&]() {
4474 ps.writeQuotedEscaped(message.getValue());
4476 for (
auto arg : args) {
4477 ps <<
"," << PP::space;
4478 emitExpression(arg, ops);
4484 template <
typename Op>
4485 LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4487 emitError(op,
"SV attributes emission is unimplemented for the op");
4490 SmallPtrSet<Operation *, 8> ops;
4492 ps.addCallback({op,
true});
4493 ps.scopedBox(PP::ibox2, [&]() {
4494 emitAssertionLabel(op);
4495 ps.scopedBox(PP::cbox0, [&]() {
4497 switch (op.getDefer()) {
4498 case DeferAssert::Immediate:
4500 case DeferAssert::Observed:
4503 case DeferAssert::Final:
4508 ps.scopedBox(PP::ibox0, [&]() {
4509 emitExpression(op.getExpression(), ops);
4512 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4516 ps.addCallback({op,
false});
4517 emitLocationInfoAndNewLine(ops);
4521 LogicalResult StmtEmitter::visitSV(AssertOp op) {
4522 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4525 LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4526 return emitImmediateAssertion(op,
PPExtString(
"assume"));
4529 LogicalResult StmtEmitter::visitSV(CoverOp op) {
4530 return emitImmediateAssertion(op,
PPExtString(
"cover"));
4533 template <
typename Op>
4534 LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
4536 emitError(op,
"SV attributes emission is unimplemented for the op");
4539 SmallPtrSet<Operation *, 8> ops;
4541 ps.addCallback({op,
true});
4542 ps.scopedBox(PP::ibox2, [&]() {
4543 emitAssertionLabel(op);
4544 ps.scopedBox(PP::cbox0, [&]() {
4545 ps << opName << PP::nbsp <<
"property (";
4546 ps.scopedBox(PP::ibox0, [&]() {
4547 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
4549 emitExpression(op.getClock(), ops);
4550 ps <<
")" << PP::space;
4551 emitExpression(op.getProperty(), ops);
4554 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
4559 ps.addCallback({op,
false});
4560 emitLocationInfoAndNewLine(ops);
4564 LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
4565 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
4568 LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
4569 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
4572 LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
4573 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
4578 LogicalResult StmtEmitter::emitVerifAssertLike(Operation *op, Value property,
4581 emitError(op,
"SV attributes emission is unimplemented for the op");
4591 bool isTemporal = !
property.getType().isSignlessInteger(1);
4592 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4593 bool emitAsImmediate = !isTemporal && isProcedural;
4596 SmallPtrSet<Operation *, 8> ops;
4598 ps.addCallback({op,
true});
4599 ps.scopedBox(PP::ibox2, [&]() {
4600 emitAssertionLabel(op);
4601 ps.scopedBox(PP::cbox0, [&]() {
4602 if (emitAsImmediate)
4603 ps << opName <<
"(";
4605 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
4606 ps.scopedBox(PP::ibox2, [&]() {
4607 PropertyEmitter(emitter, ops).emitProperty(property);
4612 ps.addCallback({op,
false});
4613 emitLocationInfoAndNewLine(ops);
4617 LogicalResult StmtEmitter::visitVerif(verif::AssertOp op) {
4618 return emitVerifAssertLike(op, op.getProperty(),
PPExtString(
"assert"));
4621 LogicalResult StmtEmitter::visitVerif(verif::AssumeOp op) {
4622 return emitVerifAssertLike(op, op.getProperty(),
PPExtString(
"assume"));
4625 LogicalResult StmtEmitter::visitVerif(verif::CoverOp op) {
4626 return emitVerifAssertLike(op, op.getProperty(),
PPExtString(
"cover"));
4629 LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
4631 emitError(op,
"SV attributes emission is unimplemented for the op");
4636 bool hasEmptyThen = op->getRegion(0).front().empty();
4638 ps <<
"`ifndef " << ident;
4640 ps <<
"`ifdef " << ident;
4642 SmallPtrSet<Operation *, 8> ops;
4644 emitLocationInfoAndNewLine(ops);
4647 emitStatementBlock(op->getRegion(0).front());
4649 if (!op->getRegion(1).empty()) {
4650 if (!hasEmptyThen) {
4652 ps <<
"`else // " << ident;
4653 setPendingNewline();
4655 emitStatementBlock(op->getRegion(1).front());
4662 setPendingNewline();
4670 void StmtEmitter::emitBlockAsStatement(
4671 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
4672 StringRef multiLineComment) {
4679 emitLocationInfoAndNewLine(locationOps);
4682 emitStatementBlock(*block);
4684 if (needsBeginEnd) {
4688 if (!multiLineComment.empty())
4689 ps <<
" // " << multiLineComment;
4690 setPendingNewline();
4694 LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
4696 for (
auto &op : ooop.getBody().front())
4701 LogicalResult StmtEmitter::visitSV(IfOp op) {
4702 SmallPtrSet<Operation *, 8> ops;
4704 auto ifcondBox = PP::ibox2;
4706 emitSVAttributes(op);
4708 ps.addCallback({op,
true});
4709 ps <<
"if (" << ifcondBox;
4719 emitExpression(ifOp.getCond(), ops);
4720 ps << PP::end <<
")";
4721 emitBlockAsStatement(ifOp.getThenBlock(), ops);
4723 if (!ifOp.hasElse())
4727 Block *elseBlock = ifOp.getElseBlock();
4729 if (!nestedElseIfOp) {
4734 emitBlockAsStatement(elseBlock, ops);
4740 ifOp = nestedElseIfOp;
4741 ps <<
"else if (" << ifcondBox;
4743 ps.addCallback({op,
false});
4748 LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
4749 emitSVAttributes(op);
4750 SmallPtrSet<Operation *, 8> ops;
4754 auto printEvent = [&](AlwaysOp::Condition cond) {
4755 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
4756 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
4758 ps.addCallback({op,
true});
4760 switch (op.getNumConditions()) {
4766 printEvent(op.getCondition(0));
4771 ps.scopedBox(PP::cbox0, [&]() {
4772 printEvent(op.getCondition(0));
4773 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
4774 ps << PP::space <<
"or" << PP::space;
4775 printEvent(op.getCondition(i));
4784 std::string comment;
4785 if (op.getNumConditions() == 0) {
4786 comment =
"always @*";
4788 comment =
"always @(";
4791 [&](Attribute eventAttr) {
4792 auto event = sv::EventControl(eventAttr.cast<IntegerAttr>().getInt());
4793 comment += stringifyEventControl(event);
4795 [&]() { comment +=
", "; });
4799 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
4800 ps.addCallback({op,
false});
4804 LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
4805 emitSVAttributes(op);
4806 SmallPtrSet<Operation *, 8> ops;
4810 ps.addCallback({op,
true});
4811 StringRef opString =
"always_comb";
4812 if (state.options.noAlwaysComb)
4813 opString =
"always @(*)";
4816 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
4817 ps.addCallback({op,
false});
4821 LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
4822 emitSVAttributes(op);
4824 SmallPtrSet<Operation *, 8> ops;
4828 ps.addCallback({op,
true});
4829 ps <<
"always_ff @(";
4830 ps.scopedBox(PP::cbox0, [&]() {
4831 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
4832 emitExpression(op.getClock(), ops);
4833 if (op.getResetStyle() == ResetType::AsyncReset) {
4834 ps << PP::nbsp <<
"or" << PP::space
4835 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
4836 emitExpression(op.getReset(), ops);
4843 std::string comment;
4844 comment +=
"always_ff @(";
4845 comment += stringifyEventControl(op.getClockEdge());
4846 if (op.getResetStyle() == ResetType::AsyncReset) {
4848 comment += stringifyEventControl(*op.getResetEdge());
4852 if (op.getResetStyle() == ResetType::NoReset)
4853 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
4856 emitLocationInfoAndNewLine(ops);
4857 ps.scopedBox(PP::bbox2, [&]() {
4863 if (op.getResetStyle() == ResetType::AsyncReset &&
4864 *op.getResetEdge() == sv::EventControl::AtNegEdge)
4866 emitExpression(op.getReset(), ops);
4868 emitBlockAsStatement(op.getResetBlock(), ops);
4871 emitBlockAsStatement(op.getBodyBlock(), ops);
4876 ps <<
" // " << comment;
4877 setPendingNewline();
4879 ps.addCallback({op,
false});
4883 LogicalResult StmtEmitter::visitSV(InitialOp op) {
4884 emitSVAttributes(op);
4885 SmallPtrSet<Operation *, 8> ops;
4888 ps.addCallback({op,
true});
4890 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
4891 ps.addCallback({op,
false});
4895 LogicalResult StmtEmitter::visitSV(CaseOp op) {
4896 emitSVAttributes(op);
4897 SmallPtrSet<Operation *, 8> ops, emptyOps;
4900 ps.addCallback({op,
true});
4901 if (op.getValidationQualifier() !=
4902 ValidationQualifierTypeEnum::ValidationQualifierPlain)
4903 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
4904 op.getValidationQualifier()))
4906 const char *opname =
nullptr;
4907 switch (op.getCaseStyle()) {
4908 case CaseStmtType::CaseStmt:
4911 case CaseStmtType::CaseXStmt:
4914 case CaseStmtType::CaseZStmt:
4918 ps << opname <<
" (";
4919 ps.scopedBox(PP::ibox0, [&]() {
4920 emitExpression(op.getCond(), ops);
4923 emitLocationInfoAndNewLine(ops);
4925 ps.scopedBox(PP::bbox2, [&]() {
4926 for (
auto &caseInfo : op.getCases()) {
4928 auto &pattern = caseInfo.pattern;
4930 llvm::TypeSwitch<CasePattern *>(pattern.get())
4931 .Case<CaseBitPattern>([&](auto bitPattern) {
4934 ps.invokeWithStringOS([&](auto &os) {
4935 os << bitPattern->getWidth() <<
"'b";
4936 for (size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
4937 os << getLetter(bitPattern->getBit(e - bit - 1));
4940 .Case<CaseEnumPattern>([&](
auto enumPattern) {
4941 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
4942 enumPattern->attr().template cast<hw::EnumFieldAttr>()));
4944 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
4945 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
4948 emitBlockAsStatement(caseInfo.block, emptyOps);
4954 ps.addCallback({op,
false});
4955 emitLocationInfoAndNewLine(ops);
4959 LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
4960 bool doNotPrint = op->hasAttr(
"doNotPrint");
4961 if (doNotPrint && !state.options.emitBindComments)
4966 emitSVAttributes(op);
4968 ps.addCallback({op,
true});
4971 <<
"/* This instance is elsewhere emitted as a bind statement."
4974 op->emitWarning() <<
"is emitted as a bind statement but has SV "
4975 "attributes. The attributes will not be emitted.";
4978 SmallPtrSet<Operation *, 8> ops;
4983 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
4984 assert(moduleOp &&
"Invalid IR");
4988 if (!op.getParameters().empty()) {
4991 bool printed =
false;
4993 llvm::zip(op.getParameters(),
4994 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
4995 auto param = std::get<0>(params).cast<ParamDeclAttr>();
4996 auto modParam = std::get<1>(params).cast<ParamDeclAttr>();
4998 if (param.getValue() == modParam.getValue())
5003 ps <<
" #(" << PP::bbox2 << PP::newline;
5006 ps <<
"," << PP::newline;
5010 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5012 ps.invokeWithStringOS([&](
auto &os) {
5013 emitter.printParamValue(param.getValue(), os, [&]() {
5014 return op->emitOpError(
"invalid instance parameter '")
5015 << param.getName().getValue() <<
"' value";
5021 ps << PP::end << PP::newline <<
")";
5028 SmallVector<Value> instPortValues(modPortInfo.size());
5029 op.getValues(instPortValues, modPortInfo);
5030 emitInstancePortList(op, modPortInfo, instPortValues);
5032 ps.addCallback({op,
false});
5033 emitLocationInfoAndNewLine(ops);
5038 setPendingNewline();
5043 LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5045 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5046 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5051 Operation *defaultModuleOp =
5052 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5054 SmallVector<Value> instPortValues(modPortInfo.size());
5055 op.getValues(instPortValues, modPortInfo);
5056 emitInstancePortList(op, modPortInfo, instPortValues);
5058 SmallPtrSet<Operation *, 8> ops;
5060 ps.addCallback({op,
false});
5061 emitLocationInfoAndNewLine(ops);
5066 void StmtEmitter::emitInstancePortList(Operation *op,
5068 ArrayRef<Value> instPortValues) {
5069 SmallPtrSet<Operation *, 8> ops;
5072 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5073 ModulePortInfo containingPortList(containingModule.getPortList());
5078 size_t maxNameLength = 0;
5079 for (
auto &elt : modPortInfo) {
5080 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5083 auto getWireForValue = [&](Value result) {
5084 return result.getUsers().begin()->getOperand(0);
5088 bool isFirst =
true;
5089 bool isZeroWidth =
false;
5091 for (
size_t portNum = 0, portEnd = modPortInfo.size(); portNum < portEnd;
5093 auto &modPort = modPortInfo.at(portNum);
5095 Value portVal = instPortValues[portNum];
5100 bool shouldPrintComma =
true;
5102 shouldPrintComma =
false;
5103 for (
size_t i = portNum + 1, e = modPortInfo.size(); i != e; ++i)
5105 shouldPrintComma =
true;
5110 if (shouldPrintComma)
5113 emitLocationInfoAndNewLine(ops);
5128 ps.scopedBox(isZeroWidth ? PP::neverbox : PP::ibox2, [&]() {
5129 auto modPortName = modPort.getVerilogName();
5131 ps.spaces(maxNameLength - modPortName.size() + 1);
5133 ps.scopedBox(PP::ibox0, [&]() {
5140 if (!modPort.isOutput()) {
5142 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5143 ps <<
"/* Zero width */";
5145 emitExpression(portVal, ops, LowestPrecedence);
5146 }
else if (portVal.use_empty()) {
5147 ps <<
"/* unused */";
5148 }
else if (portVal.hasOneUse() &&
5149 (output = dyn_cast_or_null<OutputOp>(
5150 portVal.getUses().begin()->getOwner()))) {
5155 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5157 containingPortList.atOutput(outputPortNo).getVerilogName());
5159 portVal = getWireForValue(portVal);
5160 emitExpression(portVal, ops);
5166 if (!isFirst || isZeroWidth) {
5167 emitLocationInfoAndNewLine(ops);
5180 LogicalResult StmtEmitter::visitSV(BindOp op) {
5181 emitter.emitBind(op);
5182 assert(state.pendingNewline);
5186 LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5187 emitComment(op.getCommentAttr());
5189 emitSVAttributes(op);
5192 ps.addCallback({op,
true});
5194 setPendingNewline();
5196 emitStatementBlock(*op.getBodyBlock());
5198 ps <<
"endinterface" << PP::newline;
5199 ps.addCallback({op,
false});
5200 setPendingNewline();
5204 LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5206 emitSVAttributes(op);
5208 ps.addCallback({op,
true});
5210 ps << PP::neverbox <<
"// ";
5211 ps.invokeWithStringOS([&](
auto &os) {
5216 ps.invokeWithStringOS(
5217 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5221 ps.addCallback({op,
false});
5222 setPendingNewline();
5226 LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5228 ps.addCallback({op,
true});
5232 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5233 auto port = portAttr.cast<ModportStructAttr>();
5234 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5235 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5236 ps << PPExtString(getSymOpName(signalDecl));
5240 ps.addCallback({op,
false});
5241 setPendingNewline();
5245 LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5247 ps.addCallback({op,
true});
5248 SmallPtrSet<Operation *, 8> emitted;
5251 emitExpression(op.getIface(), emitted);
5252 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5253 emitExpression(op.getRhs(), emitted);
5255 ps.addCallback({op,
false});
5256 setPendingNewline();
5260 LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5261 auto decl = op.getReferencedMacro(&state.symbolCache);
5264 ps.addCallback({op,
true});
5266 if (decl.getArgs()) {
5268 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5269 ps << name.cast<StringAttr>();
5273 if (!op.getFormatString().empty()) {
5275 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5278 ps.addCallback({op,
false});
5279 setPendingNewline();
5283 void StmtEmitter::emitStatement(Operation *op) {
5290 if (isa<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5294 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5295 succeeded(dispatchVerifVisitor(op)))
5298 emitOpError(op,
"emission to Verilog not supported");
5299 emitPendingNewlineIfNeeded();
5300 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5301 setPendingNewline();
5312 StmtEmitter &stmtEmitter) {
5319 if (isa<IfDefProceduralOp>(op->getParentOp()))
5327 SmallVector<Value, 8> exprsToScan(op->getOperands());
5332 while (!exprsToScan.empty()) {
5333 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5340 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5341 auto *defOp = readInout.getOperand().getDefiningOp();
5348 if (isa<sv::WireOp>(defOp))
5353 if (!isa<RegOp, LogicOp>(defOp))
5359 if (isa<LogicOp>(defOp) &&
5360 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5364 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5365 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5373 exprsToScan.append(expr->getOperands().begin(),
5374 expr->getOperands().end());
5380 if (expr->getBlock() != op->getBlock())
5385 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5392 template <
class AssignTy>
5394 AssignTy singleAssign;
5395 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5396 if (hasSVAttributes(user))
5399 if (auto assign = dyn_cast<AssignTy>(user)) {
5402 singleAssign = assign;
5406 return isa<ReadInOutOp>(user);
5408 return singleAssign;
5414 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5418 if (op1->getBlock() != user->getBlock())
5424 return op1->isBeforeInBlock(user);
5428 LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5429 emitSVAttributes(op);
5430 auto value = op->getResult(0);
5431 SmallPtrSet<Operation *, 8> opsForLocation;
5432 opsForLocation.insert(op);
5434 ps.addCallback({op,
true});
5437 auto type = value.getType();
5440 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
5441 unsigned targetColumn = 0;
5442 unsigned column = 0;
5445 if (maxDeclNameWidth > 0)
5446 targetColumn += maxDeclNameWidth + 1;
5449 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5450 }
else if (!word.empty()) {
5452 column += word.size();
5453 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5454 ps.spaces(numSpaces);
5455 column += numSpaces;
5458 SmallString<8> typeString;
5461 llvm::raw_svector_ostream stringStream(typeString);
5466 if (maxTypeWidth > 0)
5467 targetColumn += maxTypeWidth + 1;
5468 unsigned numSpaces = 0;
5469 if (!typeString.empty()) {
5471 column += typeString.size();
5474 if (targetColumn > column)
5475 numSpaces = targetColumn - column;
5476 ps.spaces(numSpaces);
5477 column += numSpaces;
5483 ps.invokeWithStringOS(
5484 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5487 if (state.options.printDebugInfo) {
5488 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5489 auto innerSym = innerSymOp.getInnerSymAttr();
5490 if (innerSym && !innerSym.empty()) {
5492 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5498 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5499 ps << PP::space <<
"=" << PP::space;
5500 ps.invokeWithStringOS([&](
auto &os) {
5501 emitter.printParamValue(localparam.getValue(), os, [&]() {
5502 return op->emitOpError(
"invalid localparam value");
5507 if (
auto regOp = dyn_cast<RegOp>(op)) {
5508 if (
auto initValue = regOp.getInit()) {
5509 ps << PP::space <<
"=" << PP::space;
5510 ps.scopedBox(PP::ibox0, [&]() {
5511 emitExpression(initValue, opsForLocation, LowestPrecedence,
5518 if (isa<sv::WireOp, LogicOp>(op) &&
5519 !op->getParentOp()->hasTrait<ProceduralRegion>()) {
5521 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
5522 auto *source = singleAssign.getSrc().getDefiningOp();
5526 if (!source || isa<ConstantOp>(source) ||
5527 op->getNextNode() == singleAssign) {
5528 ps << PP::space <<
"=" << PP::space;
5529 ps.scopedBox(PP::ibox0, [&]() {
5530 emitExpression(singleAssign.getSrc(), opsForLocation,
5534 emitter.assignsInlined.insert(singleAssign);
5540 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>()) {
5542 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
5545 auto *source = singleAssign.getSrc().getDefiningOp();
5549 if (!source || isa<ConstantOp>(source) ||
5552 ps << PP::space <<
"=" << PP::space;
5553 ps.scopedBox(PP::ibox0, [&]() {
5554 emitExpression(singleAssign.getSrc(), opsForLocation,
5559 emitter.assignsInlined.insert(singleAssign);
5560 emitter.expressionsEmittedIntoDecl.insert(op);
5567 ps.addCallback({op,
false});
5568 emitLocationInfoAndNewLine(opsForLocation);
5572 void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
5575 NameCollector collector(emitter);
5576 collector.collectNames(block);
5579 maxDeclNameWidth = collector.getMaxDeclNameWidth();
5580 maxTypeWidth = collector.getMaxTypeWidth();
5583 void StmtEmitter::emitStatementBlock(Block &body) {
5584 ps.scopedBox(PP::bbox2, [&]() {
5589 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
5590 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
5595 if (!isa<IfDefProceduralOp>(body.getParentOp()))
5596 collectNamesAndCalculateDeclarationWidths(body);
5599 for (
auto &op : body) {
5606 void ModuleEmitter::emitStatement(Operation *op) {
5607 StmtEmitter(*
this, state.options).emitStatement(op);
5612 void ModuleEmitter::emitSVAttributes(Operation *op) {
5620 setPendingNewline();
5628 auto verilogName = module.getVerilogModuleNameAttr();
5630 ps.addCallback({module,
true});
5631 ps <<
"// external module " <<
PPExtString(verilogName.getValue())
5633 ps.addCallback({module,
false});
5634 setPendingNewline();
5637 void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
5638 auto verilogName = module.getVerilogModuleNameAttr();
5640 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
5642 setPendingNewline();
5651 void ModuleEmitter::emitBind(BindOp op) {
5653 emitError(op,
"SV attributes emission is unimplemented for the op");
5654 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
5660 Operation *childMod =
5661 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
5665 ps.addCallback({op,
true});
5666 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
5667 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
5669 bool isFirst =
true;
5670 ps.scopedBox(PP::bbox2, [&]() {
5671 auto parentPortInfo = parentMod.getPortList();
5675 size_t maxNameLength = 0;
5676 for (
auto &elt : childPortInfo) {
5677 auto portName = elt.getVerilogName();
5678 elt.name = Builder(inst.getContext()).getStringAttr(portName);
5679 maxNameLength = std::max(maxNameLength, elt.getName().size());
5682 SmallVector<Value> instPortValues(childPortInfo.size());
5683 inst.getValues(instPortValues, childPortInfo);
5685 for (
auto [idx, elt] : llvm::enumerate(childPortInfo)) {
5687 Value portVal = instPortValues[idx];
5693 bool shouldPrintComma =
true;
5695 shouldPrintComma =
false;
5696 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
5698 shouldPrintComma =
true;
5703 if (shouldPrintComma)
5716 ps << PP::neverbox <<
"//";
5720 ps.nbsp(maxNameLength - elt.getName().size());
5722 llvm::SmallPtrSet<Operation *, 4> ops;
5723 if (elt.isOutput()) {
5724 assert((portVal.hasOneUse() || portVal.use_empty()) &&
5725 "output port must have either single or no use");
5726 if (portVal.use_empty()) {
5727 ps <<
"/* unused */";
5728 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
5729 portVal.getUses().begin()->getOwner())) {
5732 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5734 parentPortList.atOutput(outputPortNo).getVerilogName());
5736 portVal = portVal.getUsers().begin()->getOperand(0);
5737 ExprEmitter(*
this, ops)
5738 .emitExpression(portVal, LowestPrecedence,
5742 ExprEmitter(*
this, ops)
5743 .emitExpression(portVal, LowestPrecedence,
5756 ps.addCallback({op,
false});
5757 setPendingNewline();
5760 void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
5762 emitError(op,
"SV attributes emission is unimplemented for the op");
5764 auto instance = op.getReferencedInstance(&state.symbolCache);
5766 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
5767 instance.getInterfaceType().getInterface());
5769 ps.addCallback({op,
true});
5770 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
5771 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
5773 ps.addCallback({op,
false});
5774 setPendingNewline();
5777 void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
5781 auto printParamType = [&](Type type, Attribute defaultValue,
5782 SmallString<8> &result) {
5784 llvm::raw_svector_ostream sstream(result);
5789 if (
auto intAttr = defaultValue.dyn_cast<IntegerAttr>())
5790 if (intAttr.getValue().getBitWidth() == 32)
5792 if (
auto fpAttr = defaultValue.dyn_cast<FloatAttr>())
5793 if (fpAttr.getType().isF64())
5796 if (type.isa<NoneType>())
5803 if (
auto intType = type_dyn_cast<IntegerType>(type))
5804 if (intType.getWidth() == 32) {
5805 sstream <<
"/*integer*/";
5809 printPackedType(type, sstream, module->getLoc(),
5817 size_t maxTypeWidth = 0;
5818 SmallString<8> scratch;
5819 for (
auto param : params) {
5820 auto paramAttr = param.cast<ParamDeclAttr>();
5822 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
5823 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
5826 if (maxTypeWidth > 0)
5829 ps.scopedBox(PP::bbox2, [&]() {
5830 ps << PP::newline <<
"#(";
5831 ps.scopedBox(PP::cbox0, [&]() {
5834 [&](Attribute param) {
5835 auto paramAttr = param.cast<ParamDeclAttr>();
5836 auto defaultValue = paramAttr.getValue();
5838 printParamType(paramAttr.getType(), defaultValue, scratch);
5839 if (!scratch.empty())
5841 if (scratch.size() < maxTypeWidth)
5842 ps.nbsp(maxTypeWidth - scratch.size());
5844 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
5845 module, paramAttr.getName()));
5849 ps.invokeWithStringOS([&](
auto &os) {
5851 return module->emitError(
"parameter '")
5852 << paramAttr.getName().getValue()
5853 <<
"' has invalid value";
5858 [&]() { ps <<
"," << PP::newline; });
5864 void ModuleEmitter::emitPortList(Operation *module,
5867 if (portInfo.
size())
5868 emitLocationInfo(module->getLoc());
5872 bool hasOutputs =
false, hasZeroWidth =
false;
5873 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
5874 SmallVector<SmallString<8>, 16> portTypeStrings;
5876 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
5877 auto port = portInfo.
at(i);
5881 lastNonZeroPort = i;
5884 portTypeStrings.push_back({});
5886 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
5891 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
5894 if (maxTypeWidth > 0)
5898 ps.scopedBox(PP::bbox2, [&]() {
5899 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
5900 auto lastPort = e - 1;
5903 auto portType = portInfo.at(portIdx).type;
5907 bool isZeroWidth = false;
5909 isZeroWidth = isZeroBitType(portType);
5912 ps << (isZeroWidth ?
"// " :
" ");
5916 auto thisPortDirection = portInfo.
at(portIdx).
dir;
5917 switch (thisPortDirection) {
5922 ps << (hasOutputs ?
"input " :
"input ");
5925 ps << (hasOutputs ?
"inout " :
"inout ");
5928 bool emitWireInPorts = state.options.emitWireInPorts;
5929 if (emitWireInPorts)
5933 if (!portTypeStrings[portIdx].
empty())
5934 ps << portTypeStrings[portIdx];
5935 if (portTypeStrings[portIdx].size() < maxTypeWidth)
5936 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
5938 size_t startOfNamePos =
5939 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
5945 ps.invokeWithStringOS(
5946 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
5949 auto innerSym = portInfo.
at(portIdx).
getSym();
5950 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
5952 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5957 if (portIdx != lastNonZeroPort && portIdx != lastPort)
5961 if (
auto loc = portInfo.
at(portIdx).
loc)
5962 emitLocationInfo(loc);
5972 if (!state.options.disallowPortDeclSharing) {
5973 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
5976 auto port = portInfo.
at(portIdx);
5980 bool isZeroWidth =
false;
5985 ps << (isZeroWidth ?
"// " :
" ");
5988 ps.nbsp(startOfNamePos);
5991 StringRef name = port.getVerilogName();
5995 ps.invokeWithStringOS(
5996 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
5999 auto sym = port.getSym();
6000 if (state.options.printDebugInfo && sym && !sym.empty())
6001 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6005 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6009 if (
auto loc = port.loc)
6010 emitLocationInfo(loc);
6021 if (!portInfo.
size()) {
6023 SmallPtrSet<Operation *, 8> moduleOpSet;
6024 moduleOpSet.insert(module);
6025 emitLocationInfoAndNewLine(moduleOpSet);
6028 ps <<
");" << PP::newline;
6029 setPendingNewline();
6033 void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6034 currentModuleOp = module;
6036 emitComment(module.getCommentAttr());
6037 emitSVAttributes(module);
6039 ps.addCallback({module,
true});
6043 emitParameters(module, module.getParameters());
6047 assert(state.pendingNewline);
6050 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6053 ps.addCallback({module,
false});
6055 setPendingNewline();
6057 currentModuleOp =
nullptr;
6066 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6073 void emit(emit::FileListOp op);
6076 void emit(Block *block);
6078 void emitOp(emit::RefOp op);
6079 void emitOp(emit::VerbatimOp op);
6083 for (Operation &op : *block) {
6084 TypeSwitch<Operation *>(&op)
6085 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) { emitOp(op); })
6086 .Case<VerbatimOp, IfDefOp, MacroDefOp>(
6087 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6088 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6089 .Case<BindInterfaceOp>(
6090 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6091 .Case<TypeScopeOp>([&](
auto typedecls) {
6092 ModuleEmitter(state).emitStatement(typedecls);
6095 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6101 for (
auto sym : op.getFiles()) {
6102 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6104 auto it = state.fileMapping.find(fileName);
6105 if (it == state.fileMapping.end()) {
6106 emitOpError(op,
" references an invalid file: ") << sym;
6110 auto file = cast<emit::FileOp>(it->second);
6111 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6118 StringAttr target = op.getTargetAttr().getAttr();
6119 auto *targetOp = state.symbolCache.getDefinition(target);
6120 assert(targetOp->hasTrait<emit::Emittable>() &&
"target must be emittable");
6122 TypeSwitch<Operation *>(targetOp)
6124 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6125 .Case<TypeScopeOp>([&](
auto typedecls) {
6126 ModuleEmitter(state).emitStatement(typedecls);
6129 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6135 SmallPtrSet<Operation *, 8> ops;
6140 StringRef text = op.getText();
6144 const auto &[lhs, rhs] = text.split(
'\n');
6148 ps << PP::end << PP::newline << PP::neverbox;
6150 }
while (!text.empty());
6153 emitLocationInfoAndNewLine(ops);
6164 void SharedEmitterState::gatherFiles(
bool separateModules) {
6171 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6172 moduleOp->walk([&](Operation *op) {
6174 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6177 SymbolTable::getSymbolAttrName()),
6178 name.getSymName(), op);
6179 if (isa<BindOp>(op))
6185 auto collectPorts = [&](
auto moduleOp) {
6186 auto portInfo = moduleOp.getPortList();
6187 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6188 if (!p.attrs || p.attrs.empty())
6190 for (NamedAttribute portAttr : p.attrs) {
6191 if (
auto sym = portAttr.getValue().dyn_cast<InnerSymAttr>()) {
6200 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6201 for (
auto file :
designOp.getOps<emit::FileOp>())
6202 for (
auto refs : file.getOps<emit::RefOp>())
6203 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6205 SmallString<32> outputPath;
6206 for (
auto &op : *
designOp.getBody()) {
6209 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6211 bool hasFileName =
false;
6212 bool emitReplicatedOps = !isFileOp;
6213 bool addToFilelist = !isFileOp;
6219 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6221 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6222 <<
" on " << op <<
"\n";);
6223 if (!attr.isDirectory())
6226 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6227 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6230 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6235 if (!defaultFileName.isTriviallyEmpty()) {
6238 op->emitError(
"file name unspecified");
6245 auto &file =
files[destFile];
6246 file.ops.push_back(info);
6247 file.emitReplicatedOps = emitReplicatedOps;
6248 file.addToFilelist = addToFilelist;
6249 file.isVerilog = outputPath.ends_with(
".sv");
6254 TypeSwitch<Operation *>(&op)
6255 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6257 fileMapping.try_emplace(file.getSymNameAttr(), file);
6258 separateFile(file, file.getFileName());
6260 .Case<emit::FragmentOp>([&](
auto fragment) {
6263 .Case<HWModuleOp>([&](
auto mod) {
6265 auto sym = mod.getNameAttr();
6268 collectInstanceSymbolsAndBinds(mod);
6270 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6271 if (it->second.size() != 1 || attr) {
6274 op.emitError(
"modules can be emitted to a single file");
6282 if (attr || separateModules)
6288 .Case<InterfaceOp>([&](InterfaceOp intf) {
6293 for (
auto &op : *intf.getBodyBlock())
6294 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6295 if (
auto name = symOp.getNameAttr())
6299 if (attr || separateModules)
6300 separateFile(intf, intf.getSymName() +
".sv");
6308 if (separateModules)
6309 separateFile(op,
"extern_modules.sv");
6313 .Case<VerbatimOp, IfDefOp, MacroDefOp>([&](Operation *op) {
6319 separateFile(op,
"");
6321 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6324 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6333 separateFile(op,
"");
6335 .Case<BindOp>([&](
auto op) {
6337 separateFile(op,
"bindfile.sv");
6342 .Case<MacroDeclOp>([&](
auto op) {
6345 .Case<om::ClassLike>([&](
auto op) {
6348 .Case<om::ConstantOp>([&](
auto op) {
6351 .Default([&](
auto *) {
6352 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6372 size_t lastReplicatedOp = 0;
6374 bool emitHeaderInclude =
6377 if (emitHeaderInclude)
6380 size_t numReplicatedOps =
6385 DenseSet<emit::FragmentOp> includedFragments;
6386 for (
const auto &opInfo : file.
ops) {
6387 Operation *op = opInfo.op;
6391 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6397 if (
auto fragments =
6399 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6403 op->emitError(
"cannot find referenced fragment ") << sym;
6406 emit::FragmentOp fragment = it->second;
6407 if (includedFragments.insert(fragment).second) {
6408 thingsToEmit.emplace_back(it->second);
6414 thingsToEmit.emplace_back(op);
6419 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6424 TypeSwitch<Operation *>(op)
6425 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6426 .Case<HWModuleExternOp>(
6427 [&](
auto op) { ModuleEmitter(state).emitHWExternModule(op); })
6428 .Case<HWModuleGeneratedOp>(
6429 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6430 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6431 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6432 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6433 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6434 .Case<TypeScopeOp>([&](
auto typedecls) {
6435 ModuleEmitter(state).emitStatement(typedecls);
6437 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6438 [&](
auto op) { FileEmitter(state).emit(op); })
6440 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6441 .Default([&](
auto *op) {
6442 state.encounteredError =
true;
6443 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6450 llvm::formatted_raw_ostream &os,
6451 StringAttr fileName,
bool parallelize) {
6452 MLIRContext *context =
designOp->getContext();
6456 parallelize &= context->isMultithreadingEnabled();
6467 size_t lineOffset = 0;
6468 for (
auto &entry : thingsToEmit) {
6469 entry.verilogLocs.setStream(os);
6470 if (
auto *op = entry.getOperation()) {
6475 state.addVerilogLocToOps(lineOffset, fileName);
6477 os << entry.getStringData();
6482 if (state.encounteredError)
6500 SmallString<256> buffer;
6501 llvm::raw_svector_ostream tmpStream(buffer);
6502 llvm::formatted_raw_ostream rs(tmpStream);
6513 for (
auto &entry : thingsToEmit) {
6516 auto *op = entry.getOperation();
6518 auto lineOffset = os.getLine() + 1;
6519 os << entry.getStringData();
6523 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
6526 entry.verilogLocs.setStream(os);
6533 state.addVerilogLocToOps(0, fileName);
6549 module.emitWarning()
6550 <<
"`emitReplicatedOpsToHeader` option is enabled but an header is "
6551 "created only at SplitExportVerilog";
6560 for (
const auto &it : emitter.
files) {
6561 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
6562 "\" ----- 8< -----\n\n");
6568 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
6569 "\" ----- 8< -----\n\n");
6570 for (
auto &name : it.second)
6571 contents += name.str() +
"\n";
6572 list.emplace_back(contents);
6575 llvm::formatted_raw_ostream rs(os);
6588 SmallVector<HWModuleOp> modulesToPrepare;
6589 module.walk([&](
HWModuleOp op) { modulesToPrepare.push_back(op); });
6590 if (failed(failableParallelForEach(
6591 module->getContext(), modulesToPrepare,
6592 [&](
auto op) { return prepareHWModule(op, options); })))
6599 struct ExportVerilogPass :
public ExportVerilogBase<ExportVerilogPass> {
6600 ExportVerilogPass(raw_ostream &os) : os(os) {}
6601 void runOnOperation()
override {
6603 mlir::OpPassManager preparePM(
"builtin.module");
6608 if (failed(runPipeline(preparePM, getOperation())))
6609 return signalPassFailure();
6612 return signalPassFailure();
6619 struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
6620 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
6621 : ExportVerilogPass{*os} {
6622 owned = std::move(os);
6626 std::unique_ptr<llvm::raw_ostream> owned;
6630 std::unique_ptr<mlir::Pass>
6632 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
6635 std::unique_ptr<mlir::Pass>
6637 return std::make_unique<ExportVerilogPass>(os);
6648 static std::unique_ptr<llvm::ToolOutputFile>
6652 SmallString<128> outputFilename(dirname);
6654 auto outputDir = llvm::sys::path::parent_path(outputFilename);
6657 std::error_code error = llvm::sys::fs::create_directories(outputDir);
6659 emitter.
designOp.emitError(
"cannot create output directory \"")
6660 << outputDir <<
"\": " << error.message();
6666 std::string errorMessage;
6667 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
6669 emitter.
designOp.emitError(errorMessage);
6686 llvm::formatted_raw_ostream rs(output->os());
6698 StringRef dirname) {
6709 bool insertSuccess =
6717 if (!insertSuccess) {
6718 module.emitError() <<
"tried to emit a heder to " <<
circtHeader
6719 <<
", but the file is used as an output too.";
6725 parallelForEach(module->getContext(), emitter.
files.begin(),
6726 emitter.
files.end(), [&](
auto &it) {
6727 createSplitOutputFile(it.first, it.second, dirname,
6732 SmallString<128> filelistPath(dirname);
6735 std::string errorMessage;
6736 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
6738 module->emitError(errorMessage);
6742 for (
const auto &it : emitter.
files) {
6743 if (it.second.addToFilelist)
6744 output->os() << it.first.str() <<
"\n";
6753 for (
auto &name : it.second)
6754 output->os() << name.str() <<
"\n";
6765 SmallVector<HWModuleOp> modulesToPrepare;
6766 module.walk([&](
HWModuleOp op) { modulesToPrepare.push_back(op); });
6767 if (failed(failableParallelForEach(
6768 module->getContext(), modulesToPrepare,
6769 [&](
auto op) { return prepareHWModule(op, options); })))
6777 struct ExportSplitVerilogPass
6778 :
public ExportSplitVerilogBase<ExportSplitVerilogPass> {
6779 ExportSplitVerilogPass(StringRef directory) {
6780 directoryName = directory.str();
6782 void runOnOperation()
override {
6784 mlir::OpPassManager preparePM(
"builtin.module");
6789 if (failed(runPipeline(preparePM, getOperation())))
6790 return signalPassFailure();
6793 return signalPassFailure();
6798 std::unique_ptr<mlir::Pass>
6800 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static LogicalResult isCombinational(Value value, GroupInterface group)
Verifies the defining operation of a value is combinational.
static bool hasSVAttributes(Operation *op)
static StringRef getVerilogDeclWord(Operation *op, const LoweringOptions &options)
Return the word (e.g. "reg") in Verilog to declare the specified thing.
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 std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef fileName, StringRef dirname, SharedEmitterState &emitter)
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
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 bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={})
Output the basic type that consists of packed and primitive types.
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 ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
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 void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc)
Push this type's dimension into a vector.
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
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 bool haveMatchingDims(Type a, Type b, Location loc)
True iff 'a' and 'b' have the same wire dims.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
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 ....
StringAttr getVerilogModuleName(DIModule &module)
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...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
static InstancePath empty
llvm::SmallVector< StringAttr > inputs
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
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.
This stores lookup tables to make manipulating and working with the IR more efficient.
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=~0ULL)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
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.
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.
LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module)
Generates the macros used by instance choices.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
uint64_t getWidth(Type t)
mlir::Type innerType(mlir::Type type)
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoicesPass()
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
std::unique_ptr< mlir::Pass > createExportVerilogPass(std::unique_ptr< llvm::raw_ostream > os)
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
std::unique_ptr< mlir::Pass > createLegalizeAnonEnumsPass()
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.
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
const char * getCirctVersionComment()
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.
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....
const LoweringOptions & options
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 disallowLocalVariables
If true, do not emit SystemVerilog locally scoped "automatic" or logic declarations - emit top level ...
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)
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
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.