40 #include "mlir/IR/BuiltinOps.h"
41 #include "mlir/IR/ImplicitLocOpBuilder.h"
42 #include "mlir/IR/Location.h"
43 #include "mlir/IR/Threading.h"
44 #include "mlir/Interfaces/FunctionImplementation.h"
45 #include "mlir/Pass/PassManager.h"
46 #include "mlir/Support/FileUtilities.h"
47 #include "llvm/ADT/MapVector.h"
48 #include "llvm/ADT/STLExtras.h"
49 #include "llvm/ADT/StringSet.h"
50 #include "llvm/ADT/TypeSwitch.h"
51 #include "llvm/Support/FileSystem.h"
52 #include "llvm/Support/FormattedStream.h"
53 #include "llvm/Support/Path.h"
54 #include "llvm/Support/SaveAndRestore.h"
55 #include "llvm/Support/ToolOutputFile.h"
56 #include "llvm/Support/raw_ostream.h"
59 #define GEN_PASS_DEF_EXPORTSPLITVERILOG
60 #define GEN_PASS_DEF_EXPORTVERILOG
61 #include "circt/Conversion/Passes.h.inc"
64 using namespace circt;
68 using namespace ExportVerilog;
70 using namespace pretty;
72 #define DEBUG_TYPE "export-verilog"
80 enum VerilogPrecedence {
101 enum SubExprSignResult { IsSigned, IsUnsigned };
107 VerilogPrecedence precedence;
110 SubExprSignResult signedness;
112 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
113 : precedence(precedence), signedness(signedness) {}
123 return Builder(ctx).getI32IntegerAttr(value);
126 static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
127 return Builder(ctx).getIntegerAttr(t, value);
143 if (isa<VerbatimExprOp>(op)) {
144 if (op->getNumOperands() == 0 &&
145 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
150 if (isa<XMRRefOp>(op))
154 if (isa<MacroRefExprOp>(op))
164 if (op->getNumOperands() == 0)
168 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
172 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
173 auto *indexOp = array.getIndex().getDefiningOp();
174 if (!indexOp || isa<ConstantOp>(indexOp))
176 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
177 auto *readSrc = read.getInput().getDefiningOp();
179 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
194 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
195 return attr.getValue();
196 return TypeSwitch<Operation *, StringRef>(symOp)
199 .Case<InterfaceOp>([&](InterfaceOp op) {
202 .Case<InterfaceSignalOp>(
203 [&](InterfaceSignalOp op) {
return op.getSymName(); })
204 .Case<InterfaceModportOp>(
205 [&](InterfaceModportOp op) {
return op.getSymName(); })
206 .Default([&](Operation *op) {
207 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
208 return attr.getValue();
209 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
210 return attr.getValue();
211 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
212 return attr.getValue();
214 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
215 return attr.getValue();
216 return StringRef(
"");
221 template <
typename PPS>
223 os <<
"/*Zero width*/ 1\'b0";
228 auto hml = cast<HWModuleLike>(module);
229 return hml.getPort(portArgNum).getVerilogName();
234 auto hml = cast<HWModuleLike>(module);
235 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
236 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
237 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
238 return updatedName.getValue();
239 return hml.getHWModuleType().getPortName(pId);
248 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
249 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
250 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp,
251 SystemFunctionOp, UnpackedArrayCreateOp, UnpackedOpenArrayCastOp>(op))
261 static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
263 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
264 if (integer.getWidth() != 1)
265 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
268 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
269 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
274 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
275 dims.push_back(intType.getWidth());
279 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
280 return getTypeDims(dims, inout.getElementType(), loc);
281 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
282 return getTypeDims(dims, uarray.getElementType(), loc);
283 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
284 return getTypeDims(dims, uarray.getElementType(), loc);
286 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
289 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
295 SmallVector<Attribute, 4> aDims;
298 SmallVector<Attribute, 4> bDims;
301 return aDims == bDims;
307 if (
auto intType = dyn_cast<IntegerType>(type))
308 return intType.getWidth() == 0;
309 if (
auto inout = dyn_cast<hw::InOutType>(type))
311 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
312 return uarray.getNumElements() == 0 ||
314 if (
auto array = dyn_cast<hw::ArrayType>(type))
315 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
316 if (
auto structType = dyn_cast<hw::StructType>(type))
317 return llvm::all_of(structType.getElements(),
318 [](
auto elem) { return isZeroBitType(elem.type); });
319 if (
auto enumType = dyn_cast<hw::EnumType>(type))
320 return enumType.getFields().empty();
321 if (
auto unionType = dyn_cast<hw::UnionType>(type))
334 return TypeSwitch<Type, Type>(type)
338 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
341 .Default([](Type type) {
return type; });
346 return TypeSwitch<Type, bool>(type)
347 .Case<
InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
350 .Case<StructType>([](
auto) {
return true; })
351 .Default([](
auto) {
return false; });
361 static int compareLocs(Location lhs, Location rhs);
365 if (
auto name = lhs.getName().compare(rhs.getName()))
367 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
372 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
374 if (lhs.getLine() != rhs.getLine())
375 return lhs.getLine() < rhs.getLine() ? -1 : 1;
376 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
381 Location lhsCallee = lhs.getCallee();
382 Location rhsCallee = rhs.getCallee();
386 Location lhsCaller = lhs.getCaller();
387 Location rhsCaller = rhs.getCaller();
391 template <
typename TTargetLoc>
393 auto lhsT = dyn_cast<TTargetLoc>(lhs);
394 auto rhsT = dyn_cast<TTargetLoc>(rhs);
421 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
426 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
431 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
448 SmallPtrSetImpl<Attribute> &locationSet) {
449 llvm::TypeSwitch<Location, void>(loc)
450 .Case<FusedLoc>([&](
auto fusedLoc) {
451 for (
auto subLoc : fusedLoc.getLocations())
454 .Default([&](
auto loc) { locationSet.insert(loc); });
458 template <
typename TVector>
460 llvm::array_pod_sort(
461 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
462 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
470 SmallPtrSet<Attribute, 8> locationSet;
471 locationSet.insert(loc);
472 llvm::raw_string_ostream os(output);
473 emitLocationSetInfo(os, style, locationSet);
478 const SmallPtrSetImpl<Operation *> &ops) {
482 SmallPtrSet<Attribute, 8> locationSet;
485 llvm::raw_string_ostream os(output);
486 emitLocationSetInfo(os, style, locationSet);
494 const SmallPtrSetImpl<Attribute> &locationSet) {
495 if (style == LoweringOptions::LocationInfoStyle::None)
498 llvm::raw_string_ostream sstr(resstr);
500 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
504 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
505 "other styles must be already handled");
506 os <<
"@[" << resstr <<
"]";
515 const SmallPtrSetImpl<Attribute> &locationSet)
516 : os(os), style(style) {
517 emitLocationSetInfoImpl(locationSet);
523 emitLocationInfo(loc.getCallee());
525 emitLocationInfo(loc.getCaller());
531 bool withName = !loc.getName().empty();
533 os <<
"'" << loc.getName().strref() <<
"'(";
534 emitLocationInfo(loc.getChildLoc());
542 os << loc.getFilename().getValue();
543 if (
auto line = loc.getLine()) {
545 if (
auto col = loc.getColumn())
557 StringRef lastFileName;
558 for (
size_t i = 0, e = locVector.size(); i != e;) {
563 auto first = locVector[i];
564 if (first.getFilename() != lastFileName) {
565 lastFileName = first.getFilename();
572 first.getFilename() == locVector[
end].getFilename() &&
573 first.getLine() == locVector[
end].getLine())
578 if (
auto line = first.getLine()) {
580 if (
auto col = first.getColumn())
588 os <<
':' << first.getLine() <<
":{";
590 os << locVector[i++].getColumn();
602 llvm::TypeSwitch<Location, void>(loc)
603 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
604 [&](
auto loc) { emitLocationInfo(loc); })
605 .Case<mlir::FusedLoc>([&](
auto loc) {
606 SmallPtrSet<Attribute, 8> locationSet;
608 emitLocationSetInfoImpl(locationSet);
610 .Default([&](
auto loc) {
622 switch (locationSet.size()) {
624 emitLocationInfo(cast<LocationAttr>(*locationSet.begin()));
633 SmallVector<FileLineColLoc, 8> flcLocs;
634 SmallVector<Attribute, 8> otherLocs;
635 flcLocs.reserve(locationSet.size());
636 otherLocs.reserve(locationSet.size());
637 for (Attribute loc : locationSet) {
638 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
639 flcLocs.push_back(flcLoc);
641 otherLocs.push_back(loc);
652 size_t sstrSize = os.tell();
653 bool emittedAnything =
false;
654 auto recheckEmittedSomething = [&]() {
655 size_t currSize = os.tell();
656 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
657 emittedAnything |= emittedSomethingSinceLastCheck;
659 return emittedSomethingSinceLastCheck;
666 [&](Attribute loc) { emitLocationInfo(cast<LocationAttr>(loc)); },
668 if (recheckEmittedSomething()) {
670 recheckEmittedSomething();
676 if (emittedAnything && !flcLocs.empty())
679 printFileLineColSetInfo(flcLocs);
681 llvm::raw_string_ostream &
os;
693 if (isa<BlockArgument>(v))
702 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
707 if (v.getDefiningOp<ReadInterfaceSignalOp>())
720 if (
auto cast = dyn_cast<BitcastOp>(op))
721 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
725 if (op->hasOneUse() &&
726 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
734 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp>(op))
739 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
743 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
744 if (verbatim.getFormatString().size() > 32)
749 for (
auto &use : op->getUses()) {
750 auto *user = use.getOwner();
760 UnionExtractOp, IndexedPartSelectOp>(user))
761 if (use.getOperandNumber() == 0 &&
772 auto usedInExprControl = [user, &use]() {
773 return TypeSwitch<Operation *, bool>(user)
774 .Case<ltl::ClockOp>([&](
auto clockOp) {
776 return clockOp.getClock() == use.get();
778 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
779 sv::CoverConcurrentOp>(
780 [&](
auto op) {
return op.getClock() == use.get(); })
781 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
782 sv::CoverPropertyOp>([&](
auto op) {
783 return op.getDisable() == use.get() || op.getClock() == use.get();
785 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
790 .Default([](
auto) {
return false; });
793 if (!usedInExprControl())
797 auto read = dyn_cast<ReadInOutOp>(op);
800 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
811 unsigned numStatements = 0;
812 block.walk([&](Operation *op) {
814 return WalkResult::advance();
816 TypeSwitch<Operation *, unsigned>(op)
817 .Case<VerbatimOp>([&](
auto) {
823 .Case<IfOp>([&](
auto) {
834 .Case<
IfDefOp, IfDefProceduralOp>([&](
auto) { return 3; })
835 .Case<OutputOp>([&](OutputOp oop) {
838 return llvm::count_if(oop->getOperands(), [&](auto operand) {
839 Operation *op = operand.getDefiningOp();
840 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
843 .Default([](
auto) {
return 1; });
844 if (numStatements > 1)
845 return WalkResult::interrupt();
846 return WalkResult::advance();
848 if (numStatements == 0)
850 if (numStatements == 1)
860 if (op->getResult(0).use_empty())
865 if (op->hasOneUse() &&
866 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
867 *op->getUsers().begin()))
889 for (
auto &op : *elseBlock) {
890 if (
auto opIf = dyn_cast<IfOp>(op)) {
907 template <
typename PPS>
909 enum Container { NoContainer, InComment, InAttr };
910 Container currentContainer = NoContainer;
912 auto closeContainer = [&] {
913 if (currentContainer == NoContainer)
915 if (currentContainer == InComment)
917 else if (currentContainer == InAttr)
919 ps << PP::end << PP::end;
921 currentContainer = NoContainer;
924 bool isFirstContainer =
true;
925 auto openContainer = [&](Container newContainer) {
926 assert(newContainer != NoContainer);
927 if (currentContainer == newContainer)
931 if (!isFirstContainer)
932 ps << (mayBreak ? PP::space : PP::nbsp);
933 isFirstContainer =
false;
936 if (newContainer == InComment)
938 else if (newContainer == InAttr)
940 currentContainer = newContainer;
948 ps.scopedBox(PP::cbox0, [&]() {
949 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
950 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
952 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
954 if (attr.getExpression())
955 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
964 if (
auto *op = val.getDefiningOp())
967 if (
auto port = dyn_cast<BlockArgument>(val)) {
969 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
970 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
972 port.getArgNumber());
974 assert(
false &&
"unhandled value");
986 class VerilogEmitterState {
988 explicit VerilogEmitterState(ModuleOp designOp,
994 llvm::formatted_raw_ostream &os,
995 StringAttr fileName,
OpLocMap &verilogLocMap)
996 : designOp(designOp), shared(shared), options(options),
997 symbolCache(symbolCache), globalNames(globalNames),
998 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
999 pp(os, options.emittedLineLength), fileName(fileName) {
1000 pp.setListener(&saver);
1023 llvm::formatted_raw_ostream &os;
1025 bool encounteredError =
false;
1034 bool pendingNewline =
false;
1048 StringAttr fileName;
1054 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1057 verilogLocMap.
clear();
1061 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1062 void operator=(
const VerilogEmitterState &) =
delete;
1075 using CallbackDataTy = std::pair<Operation *, bool>;
1079 VerilogEmitterState &state;
1084 explicit EmitterBase(VerilogEmitterState &state)
1086 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1088 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1089 state.encounteredError =
true;
1090 return op->emitError(message);
1093 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1094 state.encounteredError =
true;
1095 return op->emitOpError(message);
1098 void emitLocationImpl(llvm::StringRef location) {
1101 ps << PP::neverbreak;
1102 if (!location.empty())
1103 ps <<
"\t// " << location;
1106 void emitLocationInfo(Location loc) {
1114 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1117 setPendingNewline();
1120 template <
typename PPS>
1121 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1122 llvm::function_ref<
void(Value)> operandEmitter,
1123 ArrayAttr symAttrs);
1129 void emitComment(StringAttr comment);
1133 void emitPendingNewlineIfNeeded() {
1134 if (state.pendingNewline) {
1135 state.pendingNewline =
false;
1139 void setPendingNewline() {
1140 assert(!state.pendingNewline);
1141 state.pendingNewline =
true;
1144 void startStatement() { emitPendingNewlineIfNeeded(); }
1147 void operator=(
const EmitterBase &) =
delete;
1148 EmitterBase(
const EmitterBase &) =
delete;
1152 template <
typename PPS>
1153 void EmitterBase::emitTextWithSubstitutions(
1154 PPS &ps, StringRef
string, Operation *op,
1155 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1166 if (
auto *itemOp = item.getOp()) {
1167 if (item.hasPort()) {
1171 if (!symOpName.empty())
1173 emitError(itemOp,
"cannot get name for symbol ") << sym;
1175 emitError(op,
"cannot get name for symbol ") << sym;
1177 return StringRef(
"<INVALID>");
1183 unsigned numSymOps = symAttrs.size();
1184 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1187 next =
string.find(
"{{", next);
1188 if (next == StringRef::npos)
1195 while (next <
string.size() &&
isdigit(
string[next]))
1198 if (start == next) {
1202 size_t operandNoLength = next - start;
1205 StringRef fmtOptsStr;
1206 if (
string[next] ==
':') {
1207 size_t startFmtOpts = next + 1;
1208 while (next <
string.size() &&
string[next] !=
'}')
1210 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1214 if (!
string.substr(next).starts_with(
"}}"))
1218 unsigned operandNo = 0;
1219 if (
string.drop_front(start)
1220 .take_front(operandNoLength)
1221 .getAsInteger(10, operandNo)) {
1222 emitError(op,
"operand substitution too large");
1228 auto before =
string.take_front(start - 2);
1229 if (!before.empty())
1234 if (operandNo < op->getNumOperands())
1236 operandEmitter(op->getOperand(operandNo));
1237 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1238 unsigned symOpNum = operandNo - op->getNumOperands();
1239 auto sym = symAttrs[symOpNum];
1240 StringRef symVerilogName;
1241 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1242 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1243 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1244 auto namepath = globalRef.getNamepathAttr().getValue();
1245 for (
auto [index, sym] : llvm::enumerate(namepath)) {
1248 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1250 auto innerRef = cast<InnerRefAttr>(sym);
1251 auto ref = state.symbolCache.getInnerDefinition(
1252 innerRef.getModule(), innerRef.getName());
1253 ps << namify(innerRef, ref);
1256 symVerilogName = namify(sym, symOp);
1259 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1260 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1262 symVerilogName = namify(sym, symOp);
1264 if (!symVerilogName.empty())
1267 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1271 string =
string.drop_front(next);
1277 while (emitUntilSubstitution())
1281 if (!
string.
empty())
1285 void EmitterBase::emitComment(StringAttr comment) {
1292 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1296 auto ref = comment.getValue();
1298 while (!ref.empty()) {
1299 std::tie(line, ref) = ref.split(
"\n");
1306 if (line.size() <= lineLength) {
1308 setPendingNewline();
1319 auto breakPos = line.rfind(
' ', lineLength);
1321 if (breakPos == StringRef::npos) {
1322 breakPos = line.find(
' ', lineLength);
1325 if (breakPos == StringRef::npos)
1326 breakPos = line.size();
1333 setPendingNewline();
1334 breakPos = line.find_first_not_of(
' ', breakPos);
1336 if (breakPos == StringRef::npos)
1339 line = line.drop_front(breakPos);
1349 bool addPrefixUnderScore =
true;
1352 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1356 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1358 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1362 }
else if (
auto *op = expr.getDefiningOp()) {
1364 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1368 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1374 addPrefixUnderScore =
false;
1376 TypeSwitch<Operation *>(op)
1379 .Case([&result](VerbatimExprOp verbatim) {
1380 verbatim.getAsmResultNames([&](Value, StringRef name) {
1384 .Case([&result](VerbatimExprSEOp verbatim) {
1385 verbatim.getAsmResultNames([&](Value, StringRef name) {
1392 if (
auto operandName =
1395 cast<IntegerType>(extract.getType()).getWidth();
1398 operandName.strref() +
"_" +
1399 Twine(extract.getLowBit()));
1402 extract.getContext(),
1403 operandName.strref() +
"_" +
1404 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1405 Twine(extract.getLowBit()));
1413 if (!result || result.strref().empty())
1417 if (addPrefixUnderScore && result.strref().front() !=
'_')
1430 class ModuleEmitter :
public EmitterBase {
1432 explicit ModuleEmitter(VerilogEmitterState &state)
1433 : EmitterBase(state), currentModuleOp(nullptr),
1437 emitPendingNewlineIfNeeded();
1441 void emitParameters(Operation *module, ArrayAttr params);
1442 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1443 bool emitAsTwoStateType =
false);
1447 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1448 void emitFunc(FuncOp);
1451 void emitStatement(Operation *op);
1452 void emitBind(BindOp op);
1453 void emitBindInterface(BindInterfaceOp op);
1455 void emitSVAttributes(Operation *op);
1458 StringRef getVerilogStructFieldName(StringAttr field) {
1459 return fieldNameResolver.getRenamedFieldName(field).getValue();
1466 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1478 bool printPackedType(Type type, raw_ostream &os, Location loc,
1479 Type optionalAliasType = {},
bool implicitIntType =
true,
1480 bool singleBitDefaultType =
true,
1481 bool emitAsTwoStateType =
false);
1485 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1493 function_ref<InFlightDiagnostic()> emitError);
1496 VerilogPrecedence parenthesizeIfLooserThan,
1497 function_ref<InFlightDiagnostic()> emitError);
1503 Operation *currentModuleOp;
1509 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1515 SmallPtrSet<Operation *, 16> assignsInlined;
1524 const ModuleEmitter &emitter) {
1525 if (isa<RegOp>(op)) {
1530 cast<InOutType>(op->getResult(0).getType()).getElementType();
1538 while (isa<ArrayType>(
innerType.getElementType()))
1540 if (isa<StructType>(
innerType.getElementType()) ||
1541 isa<TypeAliasType>(
innerType.getElementType()))
1549 if (isa<sv::WireOp>(op))
1551 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1552 return "localparam";
1555 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1556 return interface.getInterfaceType().getInterface().getValue();
1560 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1564 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1566 if (isa<LogicOp>(op)) {
1572 if (isProcedural && !stripAutomatic)
1573 return hasStruct ?
"automatic" :
"automatic logic";
1574 return hasStruct ?
"" :
"logic";
1581 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1584 assert(!emitter.state.options.disallowLocalVariables &&
1585 "automatic variables not allowed");
1589 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1590 :
"automatic logic";
1598 ModuleEmitter &emitter,
bool downTo) {
1600 os <<
"<<invalid type>>";
1603 if (
auto intAttr = dyn_cast<IntegerAttr>(
width)) {
1604 if (intAttr.getValue().isZero()) {
1605 os <<
"/*Zero Width*/";
1610 os << (intAttr.getValue().getZExtValue() - 1);
1620 auto typedAttr = dyn_cast<TypedAttr>(
width);
1622 mlir::emitError(loc,
"untyped dimension attribute ") <<
width;
1626 getIntAttr(loc.getContext(), typedAttr.getType(),
1627 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1632 emitter.printParamValue(
width, os, [loc]() {
1633 return mlir::emitError(loc,
"invalid parameter in type");
1641 static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1642 ModuleEmitter &emitter) {
1643 for (Attribute
width : dims) {
1649 void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1650 SmallVector<Attribute, 4> dims;
1682 SmallVectorImpl<Attribute> &dims,
1683 bool implicitIntType,
bool singleBitDefaultType,
1684 ModuleEmitter &emitter,
1685 Type optionalAliasType = {},
1686 bool emitAsTwoStateType =
false) {
1687 return TypeSwitch<Type, bool>(type)
1688 .Case<IntegerType>([&](IntegerType integerType) {
1689 if (emitAsTwoStateType && dims.empty()) {
1691 if (!typeName.empty()) {
1696 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1698 getInt32Attr(type.getContext(), integerType.getWidth()));
1700 StringRef typeName =
1701 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1702 if (!typeName.empty()) {
1709 return !dims.empty() || !implicitIntType;
1711 .Case<IntType>([&](IntType intType) {
1712 if (!implicitIntType)
1714 dims.push_back(intType.getWidth());
1718 .Case<ArrayType>([&](ArrayType arrayType) {
1719 dims.push_back(arrayType.getSizeAttr());
1721 implicitIntType, singleBitDefaultType,
1723 emitAsTwoStateType);
1725 .Case<InOutType>([&](
InOutType inoutType) {
1727 implicitIntType, singleBitDefaultType,
1729 emitAsTwoStateType);
1731 .Case<EnumType>([&](EnumType enumType) {
1733 if (enumType.getBitWidth() != 32)
1734 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1736 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1737 llvm::interleaveComma(
1738 enumType.getFields().getAsRange<StringAttr>(), os,
1739 [&](
auto enumerator) {
1740 os << emitter.fieldNameResolver.getEnumFieldName(
1741 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1746 .Case<StructType>([&](StructType structType) {
1747 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1748 os <<
"/*Zero Width*/";
1751 os <<
"struct packed {";
1752 for (
auto &element : structType.getElements()) {
1754 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1755 <<
": Zero Width;*/ ";
1758 SmallVector<Attribute, 8> structDims;
1763 {}, emitAsTwoStateType);
1764 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1765 emitter.printUnpackedTypePostfix(element.type, os);
1772 .Case<UnionType>([&](UnionType unionType) {
1773 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1774 os <<
"/*Zero Width*/";
1779 os <<
"union packed {";
1780 for (
auto &element : unionType.getElements()) {
1782 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1783 <<
": Zero Width;*/ ";
1787 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1789 os <<
" struct packed {";
1790 if (element.offset) {
1791 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1792 << element.offset - 1 <<
":0] "
1793 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1797 SmallVector<Attribute, 8> structDims;
1801 true, emitter, {}, emitAsTwoStateType);
1802 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1803 emitter.printUnpackedTypePostfix(element.type, os);
1807 if (elementWidth + (int64_t)element.offset < unionWidth) {
1808 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1809 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1810 <<
"__post_padding_" << element.name.getValue() <<
";";
1812 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1821 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1822 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1823 os <<
"<<unexpected unpacked array>>";
1824 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1828 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1829 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1831 mlir::emitError(loc,
"unresolvable type reference");
1834 if (typedecl.getType() != typeRef.getInnerType()) {
1835 mlir::emitError(loc,
"declared type did not match aliased type");
1839 os << typedecl.getPreferredName();
1840 emitDims(dims, os, typedecl->getLoc(), emitter);
1843 .Default([&](Type type) {
1844 os <<
"<<invalid type '" << type <<
"'>>";
1845 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1861 bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1862 Type optionalAliasType,
1863 bool implicitIntType,
1864 bool singleBitDefaultType,
1865 bool emitAsTwoStateType) {
1866 SmallVector<Attribute, 8> packedDimensions;
1868 singleBitDefaultType, *
this, optionalAliasType,
1869 emitAsTwoStateType);
1875 void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1876 TypeSwitch<Type, void>(type)
1878 printUnpackedTypePostfix(inoutType.getElementType(), os);
1880 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1881 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1882 : state.designOp->getLoc();
1883 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1885 printUnpackedTypePostfix(arrayType.getElementType(), os);
1887 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1889 printUnpackedTypePostfix(arrayType.getElementType(), os);
1891 .Case<InterfaceType>([&](
auto) {
1906 function_ref<InFlightDiagnostic()> emitError) {
1907 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1916 VerilogPrecedence parenthesizeIfLooserThan,
1917 function_ref<InFlightDiagnostic()> emitError) {
1918 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1919 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1920 APInt value = intAttr.getValue();
1924 if (intTy.getWidth() > 32) {
1926 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1930 if (intTy.isSigned())
1931 os << intTy.getWidth() <<
"'sd";
1933 os << intTy.getWidth() <<
"'d";
1935 value.print(os, intTy.isSigned());
1936 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1938 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1940 os.write_escaped(strAttr.getValue());
1942 return {Symbol, IsUnsigned};
1944 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1946 os << fpAttr.getValueAsDouble();
1947 return {Symbol, IsUnsigned};
1949 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1950 os << verbatimParam.getValue().getValue();
1951 return {Symbol, IsUnsigned};
1953 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1955 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1956 parameterRef.getName());
1959 return {Symbol, IsUnsigned};
1963 auto expr = dyn_cast<ParamExprAttr>(value);
1965 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1966 emitError() <<
" = " << value;
1967 return {LowestPrecedence, IsUnsigned};
1970 StringRef operatorStr;
1971 StringRef openStr, closeStr;
1972 VerilogPrecedence subprecedence = LowestPrecedence;
1973 VerilogPrecedence prec;
1974 std::optional<SubExprSignResult> operandSign;
1975 bool isUnary =
false;
1976 bool hasOpenClose =
false;
1978 switch (expr.getOpcode()) {
1980 operatorStr =
" + ";
1981 subprecedence = Addition;
1984 operatorStr =
" * ";
1985 subprecedence = Multiply;
1988 operatorStr =
" & ";
1989 subprecedence = And;
1992 operatorStr =
" | ";
1996 operatorStr =
" ^ ";
1997 subprecedence = Xor;
2000 operatorStr =
" << ";
2001 subprecedence = Shift;
2005 operatorStr =
" >> ";
2006 subprecedence = Shift;
2010 operatorStr =
" >>> ";
2011 subprecedence = Shift;
2012 operandSign = IsSigned;
2015 operatorStr =
" / ";
2016 subprecedence = Multiply;
2017 operandSign = IsUnsigned;
2020 operatorStr =
" / ";
2021 subprecedence = Multiply;
2022 operandSign = IsSigned;
2025 operatorStr =
" % ";
2026 subprecedence = Multiply;
2027 operandSign = IsUnsigned;
2030 operatorStr =
" % ";
2031 subprecedence = Multiply;
2032 operandSign = IsSigned;
2035 openStr =
"$clog2(";
2037 operandSign = IsUnsigned;
2038 hasOpenClose =
true;
2041 case PEO::StrConcat:
2044 hasOpenClose =
true;
2047 subprecedence = LowestPrecedence;
2052 prec = subprecedence;
2055 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2057 assert(isUnary || hasOpenClose ||
2058 !llvm::hasSingleElement(expr.getOperands()));
2065 auto emitOperand = [&](Attribute operand) ->
bool {
2067 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2068 if (operandSign.has_value())
2069 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2072 if (operandSign.has_value()) {
2074 signedness = *operandSign;
2076 return signedness == IsSigned;
2080 if (prec > parenthesizeIfLooserThan)
2089 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2090 for (
auto op : expr.getOperands().drop_front()) {
2093 if (expr.getOpcode() == PEO::Add) {
2094 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2095 const APInt &value = integer.getValue();
2096 if (value.isNegative() && !value.isMinSignedValue()) {
2098 allOperandsSigned &=
2106 allOperandsSigned &= emitOperand(op);
2110 if (prec > parenthesizeIfLooserThan) {
2114 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2129 class ExprEmitter :
public EmitterBase,
2131 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2132 public Visitor<ExprEmitter, SubExprInfo> {
2136 ExprEmitter(ModuleEmitter &emitter,
2137 SmallPtrSetImpl<Operation *> &emittedExprs)
2138 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2140 ExprEmitter(ModuleEmitter &emitter,
2141 SmallPtrSetImpl<Operation *> &emittedExprs,
2143 : EmitterBase(emitter.state), emitter(emitter),
2144 emittedExprs(emittedExprs), buffer(tokens),
2145 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2146 assert(state.pp.getListener() == &state.saver);
2153 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2154 bool isAssignmentLikeContext) {
2155 assert(localTokens.empty());
2157 ps.scopedBox(PP::ibox0, [&]() {
2158 emitSubExpr(exp, parenthesizeIfLooserThan,
2161 isAssignmentLikeContext);
2166 if (&buffer.tokens == &localTokens)
2167 buffer.flush(state.pp);
2172 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2173 friend class Visitor<ExprEmitter, SubExprInfo>;
2175 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2183 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2184 SubExprSignRequirement signReq = NoRequirement,
2185 bool isSelfDeterminedUnsignedValue =
false,
2186 bool isAssignmentLikeContext =
false);
2190 void emitSVAttributes(Operation *op);
2192 SubExprInfo visitUnhandledExpr(Operation *op);
2193 SubExprInfo visitInvalidComb(Operation *op) {
2194 return dispatchTypeOpVisitor(op);
2196 SubExprInfo visitUnhandledComb(Operation *op) {
2197 return visitUnhandledExpr(op);
2199 SubExprInfo visitInvalidTypeOp(Operation *op) {
2200 return dispatchSVVisitor(op);
2202 SubExprInfo visitUnhandledTypeOp(Operation *op) {
2203 return visitUnhandledExpr(op);
2205 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2207 using Visitor::visitSV;
2210 enum EmitBinaryFlags {
2211 EB_RequireSignedOperands = RequireSigned,
2212 EB_RequireUnsignedOperands = RequireUnsigned,
2213 EB_OperandSignRequirementMask = 0x3,
2218 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2222 EB_ForceResultSigned = 0x8,
2227 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2228 const char *syntax,
unsigned emitBinaryFlags = 0);
2230 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2231 bool resultAlwaysUnsigned =
false);
2234 void emitSubExprIBox2(
2235 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2236 ps.scopedBox(PP::ibox2,
2237 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2242 template <
typename Container,
typename EachFn>
2243 void interleaveComma(
const Container &c, EachFn eachFn) {
2244 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2249 void interleaveComma(ValueRange ops) {
2250 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2267 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2269 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2270 CloseFunc closeFn) {
2272 ps.scopedBox(PP::cbox0, [&]() {
2273 interleaveComma(c, eachFn);
2279 template <
typename OpenFunc,
typename CloseFunc>
2280 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2281 return emitBracedList(
2282 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2286 void emitBracedList(ValueRange ops) {
2287 return emitBracedList(
2288 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2292 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2295 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2296 bool printAsPattern, Operation *op);
2298 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2299 ArrayAttr fieldValues,
bool printAsPattern,
2302 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2304 SubExprInfo visitSV(GetModportOp op);
2305 SubExprInfo visitSV(SystemFunctionOp op);
2306 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2307 SubExprInfo visitSV(XMROp op);
2308 SubExprInfo visitSV(XMRRefOp op);
2309 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2310 SubExprInfo visitSV(VerbatimExprOp op) {
2311 return visitVerbatimExprOp(op, op.getSymbols());
2313 SubExprInfo visitSV(VerbatimExprSEOp op) {
2314 return visitVerbatimExprOp(op, op.getSymbols());
2316 SubExprInfo visitSV(MacroRefExprOp op);
2317 SubExprInfo visitSV(MacroRefExprSEOp op);
2318 template <
typename MacroTy>
2319 SubExprInfo emitMacroCall(MacroTy op);
2321 SubExprInfo visitSV(ConstantXOp op);
2322 SubExprInfo visitSV(ConstantZOp op);
2323 SubExprInfo visitSV(ConstantStrOp op);
2325 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2326 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2328 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2333 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2334 emitSVAttributes(op);
2337 SubExprInfo visitSV(ArrayIndexInOutOp op);
2338 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2339 SubExprInfo visitSV(IndexedPartSelectOp op);
2340 SubExprInfo visitSV(StructFieldInOutOp op);
2343 SubExprInfo visitSV(SampledOp op);
2346 using TypeOpVisitor::visitTypeOp;
2348 SubExprInfo visitTypeOp(AggregateConstantOp op);
2350 SubExprInfo visitTypeOp(ParamValueOp op);
2357 SubExprInfo visitTypeOp(StructInjectOp op);
2358 SubExprInfo visitTypeOp(UnionCreateOp op);
2359 SubExprInfo visitTypeOp(UnionExtractOp op);
2360 SubExprInfo visitTypeOp(EnumCmpOp op);
2361 SubExprInfo visitTypeOp(EnumConstantOp op);
2364 using CombinationalVisitor::visitComb;
2365 SubExprInfo visitComb(
MuxOp op);
2366 SubExprInfo visitComb(
AddOp op) {
2367 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2368 return emitBinary(op, Addition,
"+");
2370 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2371 SubExprInfo visitComb(
MulOp op) {
2372 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2373 return emitBinary(op, Multiply,
"*");
2375 SubExprInfo visitComb(
DivUOp op) {
2376 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2378 SubExprInfo visitComb(
DivSOp op) {
2379 return emitBinary(op, Multiply,
"/",
2380 EB_RequireSignedOperands | EB_ForceResultSigned);
2382 SubExprInfo visitComb(
ModUOp op) {
2383 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2385 SubExprInfo visitComb(
ModSOp op) {
2386 return emitBinary(op, Multiply,
"%",
2387 EB_RequireSignedOperands | EB_ForceResultSigned);
2389 SubExprInfo visitComb(
ShlOp op) {
2390 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2392 SubExprInfo visitComb(
ShrUOp op) {
2394 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2396 SubExprInfo visitComb(
ShrSOp op) {
2399 return emitBinary(op, Shift,
">>>",
2400 EB_RequireSignedOperands | EB_ForceResultSigned |
2401 EB_RHS_UnsignedWithSelfDeterminedWidth);
2403 SubExprInfo visitComb(
AndOp op) {
2404 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2405 return emitBinary(op, And,
"&");
2407 SubExprInfo visitComb(
OrOp op) {
2408 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2409 return emitBinary(op, Or,
"|");
2411 SubExprInfo visitComb(
XorOp op) {
2412 if (op.isBinaryNot())
2413 return emitUnary(op,
"~");
2414 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2415 return emitBinary(op, Xor,
"^");
2420 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2422 SubExprInfo visitComb(ReplicateOp op);
2423 SubExprInfo visitComb(
ConcatOp op);
2425 SubExprInfo visitComb(ICmpOp op);
2427 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2428 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2429 "not printed within an assignment-like context");
2430 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2431 "supposed to spill such expressions";
2435 SubExprInfo printStructCreate(
2436 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2438 bool printAsPattern, Operation *op);
2441 ModuleEmitter &emitter;
2448 SubExprSignRequirement signPreference = NoRequirement;
2452 SmallPtrSetImpl<Operation *> &emittedExprs;
2455 SmallVector<Token> localTokens;
2469 bool isAssignmentLikeContext =
false;
2473 SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2475 unsigned emitBinaryFlags) {
2477 emitError(op,
"SV attributes emission is unimplemented for the op");
2488 if (emitBinaryFlags & EB_ForceResultSigned)
2489 ps <<
"$signed(" << PP::ibox0;
2490 auto operandSignReq =
2491 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2492 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2494 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2496 ps << lhsSpace << syntax << PP::nbsp;
2503 auto rhsPrec = prec;
2504 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2505 rhsPrec = VerilogPrecedence(prec - 1);
2510 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2511 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2512 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2513 operandSignReq = NoRequirement;
2516 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2517 rhsIsUnsignedValueWithSelfDeterminedWidth);
2521 SubExprSignResult signedness = IsUnsigned;
2522 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2523 signedness = IsSigned;
2525 if (emitBinaryFlags & EB_ForceResultSigned) {
2526 ps << PP::end <<
")";
2527 signedness = IsSigned;
2531 return {prec, signedness};
2534 SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2535 bool resultAlwaysUnsigned) {
2537 emitError(op,
"SV attributes emission is unimplemented for the op");
2540 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2544 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2545 resultAlwaysUnsigned ? IsUnsigned : signedness};
2550 void ExprEmitter::emitSVAttributes(Operation *op) {
2569 if (constant && constant.getValue().isZero())
2570 return concat.getOperand(1);
2580 SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2581 VerilogPrecedence parenthesizeIfLooserThan,
2582 SubExprSignRequirement signRequirement,
2583 bool isSelfDeterminedUnsignedValue,
2584 bool isAssignmentLikeContext) {
2587 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2592 auto *op = exp.getDefiningOp();
2596 if (!shouldEmitInlineExpr) {
2599 if (signRequirement == RequireSigned) {
2601 return {Symbol, IsSigned};
2605 return {Symbol, IsUnsigned};
2608 unsigned subExprStartIndex = buffer.tokens.size();
2610 ps.addCallback({op,
true});
2611 auto done = llvm::make_scope_exit([&]() {
2613 ps.addCallback({op, false});
2619 signPreference = signRequirement;
2621 bool bitCastAdded =
false;
2622 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2624 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2625 ps.addAsString(inType.getWidth());
2626 ps <<
"'(" << PP::ibox0;
2627 bitCastAdded =
true;
2631 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2632 isAssignmentLikeContext);
2633 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2639 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2641 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2643 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2644 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2647 expInfo.signedness = IsSigned;
2648 expInfo.precedence = Selection;
2649 }
else if (signRequirement == RequireUnsigned &&
2650 expInfo.signedness == IsSigned) {
2653 expInfo.signedness = IsUnsigned;
2654 expInfo.precedence = Selection;
2655 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2662 expInfo.precedence = Selection;
2669 emittedExprs.insert(exp.getDefiningOp());
2673 SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2674 auto openFn = [&]() {
2676 ps.addAsString(op.getMultiple());
2679 auto closeFn = [&]() { ps <<
"}}"; };
2683 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2684 if (op.getOperand().hasOneUse()) {
2685 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2686 return {Symbol, IsUnsigned};
2689 emitBracedList(op.getOperand(), openFn, closeFn);
2690 return {Symbol, IsUnsigned};
2693 SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2694 emitBracedList(op.getOperands());
2695 return {Symbol, IsUnsigned};
2698 SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2702 Type toType = op.getType();
2705 ps.invokeWithStringOS(
2706 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2709 return emitSubExpr(op.getInput(), LowestPrecedence);
2712 SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2713 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2714 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2715 SubExprSignRequirement signop[] = {
2717 NoRequirement, NoRequirement,
2719 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2721 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2723 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2725 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2726 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2729 if (op.isEqualAllOnes())
2730 return emitUnary(op,
"&",
true);
2733 if (op.isNotEqualZero())
2734 return emitUnary(op,
"|",
true);
2736 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2740 result.signedness = IsUnsigned;
2744 SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2746 emitError(op,
"SV attributes emission is unimplemented for the op");
2748 unsigned loBit = op.getLowBit();
2749 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2751 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2752 assert((x.precedence == Symbol ||
2754 "should be handled by isExpressionUnableToInline");
2759 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2763 ps.addAsString(hiBit);
2764 if (hiBit != loBit) {
2766 ps.addAsString(loBit);
2769 return {Unary, IsUnsigned};
2772 SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2774 emitError(op,
"SV attributes emission is unimplemented for the op");
2776 auto decl = op.getReferencedDecl(state.symbolCache);
2779 return {Selection, IsUnsigned};
2782 SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2784 emitError(op,
"SV attributes emission is unimplemented for the op");
2787 ps.scopedBox(PP::ibox0, [&]() {
2789 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2790 [&]() { ps <<
"," << PP::space; });
2793 return {Symbol, IsUnsigned};
2796 SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2798 emitError(op,
"SV attributes emission is unimplemented for the op");
2800 auto decl = op.getReferencedDecl(state.symbolCache);
2804 return {Selection, IsUnsigned};
2807 SubExprInfo ExprEmitter::visitSV(XMROp op) {
2809 emitError(op,
"SV attributes emission is unimplemented for the op");
2811 if (op.getIsRooted())
2813 for (
auto s : op.getPath())
2814 ps << PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2816 return {Selection, IsUnsigned};
2821 SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2823 emitError(op,
"SV attributes emission is unimplemented for the op");
2826 auto globalRef = op.getReferencedPath(&state.symbolCache);
2827 auto namepath = globalRef.getNamepathAttr().getValue();
2828 auto *module = state.symbolCache.getDefinition(
2829 cast<InnerRefAttr>(namepath.front()).getModule());
2831 for (
auto sym : namepath) {
2833 auto innerRef = cast<InnerRefAttr>(sym);
2834 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2835 innerRef.getName());
2836 if (ref.hasPort()) {
2842 auto leaf = op.getVerbatimSuffixAttr();
2843 if (leaf && leaf.size())
2845 return {Selection, IsUnsigned};
2848 SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2850 emitError(op,
"SV attributes emission is unimplemented for the op");
2852 emitTextWithSubstitutions(
2853 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2854 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2856 return {Unary, IsUnsigned};
2859 template <
typename MacroTy>
2860 SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2862 emitError(op,
"SV attributes emission is unimplemented for the op");
2865 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2866 assert(macroOp &&
"Invalid IR");
2868 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2870 if (!op.getInputs().empty()) {
2872 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2873 emitExpression(val, LowestPrecedence, false);
2877 return {LowestPrecedence, IsUnsigned};
2880 SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2881 return emitMacroCall(op);
2884 SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2885 return emitMacroCall(op);
2888 SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2890 emitError(op,
"SV attributes emission is unimplemented for the op");
2892 ps.addAsString(op.getWidth());
2894 return {Unary, IsUnsigned};
2897 SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2899 emitError(op,
"SV attributes emission is unimplemented for the op");
2901 ps.writeQuotedEscaped(op.getStr());
2902 return {Symbol, IsUnsigned};
2905 SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2907 emitError(op,
"SV attributes emission is unimplemented for the op");
2909 ps.addAsString(op.getWidth());
2911 return {Unary, IsUnsigned};
2914 SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2915 bool isNegated =
false;
2918 if (signPreference == RequireSigned && value.isNegative() &&
2919 !value.isMinSignedValue()) {
2924 ps.addAsString(type.getWidth());
2928 if (signPreference == RequireSigned)
2934 SmallString<32> valueStr;
2936 (-value).toStringUnsigned(valueStr, 16);
2938 value.toStringUnsigned(valueStr, 16);
2941 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2944 SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2946 emitError(op,
"SV attributes emission is unimplemented for the op");
2948 auto value = op.getValue();
2952 if (value.getBitWidth() == 0) {
2953 emitOpError(op,
"will not emit zero width constants in the general case");
2954 ps <<
"<<unsupported zero width constant: "
2955 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2956 return {Unary, IsUnsigned};
2959 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2962 void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2963 bool printAsPattern, Operation *op) {
2964 if (printAsPattern && !isAssignmentLikeContext)
2965 emitAssignmentPatternContextError(op);
2966 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2969 elementValues, [&]() { ps << openDelim; },
2970 [&](Attribute elementValue) {
2971 printConstantAggregate(elementValue,
elementType, op);
2973 [&]() { ps <<
"}"; });
2976 void ExprEmitter::printConstantStruct(
2977 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
2978 bool printAsPattern, Operation *op) {
2979 if (printAsPattern && !isAssignmentLikeContext)
2980 emitAssignmentPatternContextError(op);
2987 auto fieldRange = llvm::make_filter_range(
2988 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
2993 if (printAsPattern) {
2995 fieldRange, [&]() { ps <<
"'{"; },
2996 [&](
const auto &fieldAndValue) {
2997 ps.scopedBox(PP::ibox2, [&]() {
2998 const auto &[field, value] = fieldAndValue;
2999 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3000 <<
":" << PP::space;
3001 printConstantAggregate(value, field.type, op);
3004 [&]() { ps <<
"}"; });
3007 fieldRange, [&]() { ps <<
"{"; },
3008 [&](
const auto &fieldAndValue) {
3009 ps.scopedBox(PP::ibox2, [&]() {
3010 const auto &[field, value] = fieldAndValue;
3011 printConstantAggregate(value, field.type, op);
3014 [&]() { ps <<
"}"; });
3018 void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3021 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3022 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3023 isAssignmentLikeContext, op);
3026 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3027 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3031 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3032 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3033 isAssignmentLikeContext, op);
3035 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3036 auto value = cast<IntegerAttr>(attr).getValue();
3037 printConstantScalar(value, intType);
3041 emitOpError(op,
"contains constant of type ")
3042 << type <<
" which cannot be emitted as Verilog";
3045 SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3047 emitError(op,
"SV attributes emission is unimplemented for the op");
3051 "zero-bit types not allowed at this point");
3053 printConstantAggregate(op.getFields(), op.getType(), op);
3054 return {Symbol, IsUnsigned};
3057 SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3059 emitError(op,
"SV attributes emission is unimplemented for the op");
3061 return ps.invokeWithStringOS([&](
auto &os) {
3062 return emitter.printParamValue(op.getValue(), os, [&]() {
3063 return op->emitOpError(
"invalid parameter use");
3070 SubExprInfo ExprEmitter::visitTypeOp(
ArraySliceOp op) {
3072 emitError(op,
"SV attributes emission is unimplemented for the op");
3074 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3076 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3078 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3080 ps.addAsString(dstWidth);
3082 return {Selection, arrayPrec.signedness};
3085 SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3086 emitSubExpr(op.getInput(), Selection);
3091 emitSubExpr(op.getIndex(), LowestPrecedence);
3093 emitSVAttributes(op);
3094 return {Selection, IsUnsigned};
3100 emitError(op,
"SV attributes emission is unimplemented for the op");
3102 if (op.isUniform()) {
3104 ps.addAsString(op.getInputs().size());
3106 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3110 op.getInputs(), [&]() { ps <<
"{"; },
3113 emitSubExprIBox2(v);
3116 [&]() { ps <<
"}"; });
3118 return {Unary, IsUnsigned};
3121 SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3123 emitError(op,
"SV attributes emission is unimplemented for the op");
3126 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3127 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3128 return {Unary, IsUnsigned};
3133 emitError(op,
"SV attributes emission is unimplemented for the op");
3135 emitBracedList(op.getOperands());
3136 return {Unary, IsUnsigned};
3139 SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3141 emitError(op,
"SV attributes emission is unimplemented for the op");
3143 auto index = op.getIndex();
3144 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3149 emitSubExpr(index, LowestPrecedence);
3151 return {Selection, arrayPrec.signedness};
3154 SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3156 emitError(op,
"SV attributes emission is unimplemented for the op");
3158 auto prec = emitSubExpr(op.getInput(), Selection);
3160 emitSubExpr(op.getBase(), LowestPrecedence);
3161 if (op.getDecrement())
3165 ps.addAsString(op.getWidth());
3167 return {Selection, prec.signedness};
3170 SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3172 emitError(op,
"SV attributes emission is unimplemented for the op");
3174 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3176 emitSubExpr(op.getBase(), LowestPrecedence);
3177 if (op.getDecrement())
3181 ps.addAsString(op.getWidth());
3186 SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3188 emitError(op,
"SV attributes emission is unimplemented for the op");
3190 auto prec = emitSubExpr(op.getInput(), Selection);
3192 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3193 return {Selection, prec.signedness};
3196 SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3198 emitError(op,
"SV attributes emission is unimplemented for the op");
3201 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3206 SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3220 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3221 ps.scopedBox(PP::ibox0, [&]() {
3222 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3226 emitSVAttributes(op);
3228 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3229 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3233 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3234 return emitSubExpr(op.getFalseValue(), Conditional);
3237 SubExprSignResult signedness = IsUnsigned;
3238 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3239 signedness = IsSigned;
3241 return {Conditional, signedness};
3245 SubExprInfo ExprEmitter::printStructCreate(
3246 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3248 bool printAsPattern, Operation *op) {
3249 if (printAsPattern && !isAssignmentLikeContext)
3250 emitAssignmentPatternContextError(op);
3253 auto filteredFields = llvm::make_filter_range(
3254 llvm::enumerate(fieldInfos),
3255 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3257 if (printAsPattern) {
3259 filteredFields, [&]() { ps <<
"'{"; },
3260 [&](
const auto &field) {
3261 ps.scopedBox(PP::ibox2, [&]() {
3263 emitter.getVerilogStructFieldName(field.value().name))
3264 <<
":" << PP::space;
3265 fieldFn(field.value(), field.index());
3268 [&]() { ps <<
"}"; });
3271 filteredFields, [&]() { ps <<
"{"; },
3272 [&](
const auto &field) {
3273 ps.scopedBox(PP::ibox2,
3274 [&]() { fieldFn(field.value(), field.index()); });
3276 [&]() { ps <<
"}"; });
3279 return {Selection, IsUnsigned};
3284 emitError(op,
"SV attributes emission is unimplemented for the op");
3288 bool printAsPattern = isAssignmentLikeContext;
3289 StructType structType = op.getType();
3290 return printStructCreate(
3291 structType.getElements(),
3292 [&](
const auto &field,
auto index) {
3293 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3295 isAssignmentLikeContext);
3297 printAsPattern, op);
3302 emitError(op,
"SV attributes emission is unimplemented for the op");
3304 emitSubExpr(op.getInput(), Selection);
3306 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3307 return {Selection, IsUnsigned};
3310 SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3312 emitError(op,
"SV attributes emission is unimplemented for the op");
3316 bool printAsPattern = isAssignmentLikeContext;
3317 StructType structType = op.getType();
3318 return printStructCreate(
3319 structType.getElements(),
3320 [&](
const auto &field,
auto index) {
3321 if (field.name == op.getFieldNameAttr()) {
3322 emitSubExpr(op.getNewValue(), Selection);
3324 emitSubExpr(op.getInput(), Selection);
3326 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3329 printAsPattern, op);
3332 SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3333 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3334 return {Selection, IsUnsigned};
3337 SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3339 emitError(op,
"SV attributes emission is unimplemented for the op");
3340 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3343 result.signedness = IsUnsigned;
3347 SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3349 emitError(op,
"SV attributes emission is unimplemented for the op");
3354 auto &element = unionType.getElements()[op.getFieldIndex()];
3358 if (!elementWidth) {
3359 ps.addAsString(unionWidth);
3361 return {Unary, IsUnsigned};
3365 if (elementWidth == unionWidth) {
3366 emitSubExpr(op.getInput(), LowestPrecedence);
3367 return {Unary, IsUnsigned};
3372 ps.scopedBox(PP::ibox0, [&]() {
3373 if (
auto prePadding = element.offset) {
3374 ps.addAsString(prePadding);
3375 ps <<
"'h0," << PP::space;
3377 emitSubExpr(op.getInput(), Selection);
3378 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3379 ps <<
"," << PP::space;
3380 ps.addAsString(postPadding);
3386 return {Unary, IsUnsigned};
3389 SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3391 emitError(op,
"SV attributes emission is unimplemented for the op");
3392 emitSubExpr(op.getInput(), Selection);
3395 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3397 auto &element = unionType.getElements()[op.getFieldIndex()];
3399 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3400 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3409 return {Selection, IsUnsigned};
3412 SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3413 emitOpError(op,
"cannot emit this expression to Verilog");
3414 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3416 return {Symbol, IsUnsigned};
3432 enum class PropertyPrecedence {
3452 struct EmittedProperty {
3454 PropertyPrecedence precedence;
3459 class PropertyEmitter :
public EmitterBase,
3460 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3464 PropertyEmitter(ModuleEmitter &emitter,
3465 SmallPtrSetImpl<Operation *> &emittedOps)
3466 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3467 PropertyEmitter(ModuleEmitter &emitter,
3468 SmallPtrSetImpl<Operation *> &emittedOps,
3470 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3472 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3473 assert(state.pp.getListener() == &state.saver);
3476 void emitAssertPropertyDisable(
3477 Value property, Value disable,
3478 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3480 void emitAssertPropertyBody(
3481 Value property, Value disable,
3482 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3484 void emitAssertPropertyBody(
3485 Value property, sv::EventControl event, Value clock, Value disable,
3486 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3491 emitNestedProperty(Value property,
3492 PropertyPrecedence parenthesizeIfLooserThan);
3493 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3494 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3496 EmittedProperty visitUnhandledLTL(Operation *op);
3497 EmittedProperty visitLTL(ltl::AndOp op);
3498 EmittedProperty visitLTL(ltl::OrOp op);
3499 EmittedProperty visitLTL(ltl::IntersectOp op);
3500 EmittedProperty visitLTL(ltl::DelayOp op);
3501 EmittedProperty visitLTL(ltl::ConcatOp op);
3502 EmittedProperty visitLTL(ltl::RepeatOp op);
3503 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3504 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3505 EmittedProperty visitLTL(ltl::NotOp op);
3506 EmittedProperty visitLTL(ltl::ImplicationOp op);
3507 EmittedProperty visitLTL(ltl::UntilOp op);
3508 EmittedProperty visitLTL(ltl::EventuallyOp op);
3509 EmittedProperty visitLTL(ltl::ClockOp op);
3511 void emitLTLConcat(ValueRange inputs);
3514 ModuleEmitter &emitter;
3519 SmallPtrSetImpl<Operation *> &emittedOps;
3522 SmallVector<Token> localTokens;
3535 void PropertyEmitter::emitAssertPropertyDisable(
3536 Value property, Value disable,
3537 PropertyPrecedence parenthesizeIfLooserThan) {
3540 ps <<
"disable iff" << PP::nbsp <<
"(";
3542 emitNestedProperty(disable, PropertyPrecedence::Unary);
3548 ps.scopedBox(PP::ibox0,
3549 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3555 void PropertyEmitter::emitAssertPropertyBody(
3556 Value property, Value disable,
3557 PropertyPrecedence parenthesizeIfLooserThan) {
3558 assert(localTokens.empty());
3560 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3565 if (&buffer.tokens == &localTokens)
3566 buffer.flush(state.pp);
3569 void PropertyEmitter::emitAssertPropertyBody(
3570 Value property, sv::EventControl event, Value clock, Value disable,
3571 PropertyPrecedence parenthesizeIfLooserThan) {
3572 assert(localTokens.empty());
3575 ps.scopedBox(PP::ibox2, [&] {
3576 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3577 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3583 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3588 if (&buffer.tokens == &localTokens)
3589 buffer.flush(state.pp);
3592 EmittedProperty PropertyEmitter::emitNestedProperty(
3593 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3603 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3604 ExprEmitter(emitter, emittedOps, buffer.tokens)
3605 .emitExpression(property, LowestPrecedence,
3607 return {PropertyPrecedence::Symbol};
3610 unsigned startIndex = buffer.tokens.size();
3611 auto info = dispatchLTLVisitor(property.getDefiningOp());
3616 if (info.precedence > parenthesizeIfLooserThan) {
3618 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3619 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3621 ps << PP::end <<
")";
3623 info.precedence = PropertyPrecedence::Symbol;
3627 emittedOps.insert(property.getDefiningOp());
3631 EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3632 emitOpError(op,
"emission as Verilog property or sequence not supported");
3633 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3634 return {PropertyPrecedence::Symbol};
3637 EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3640 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3641 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3642 return {PropertyPrecedence::And};
3645 EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3648 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3649 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3650 return {PropertyPrecedence::Or};
3653 EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3657 emitNestedProperty(input, PropertyPrecedence::Intersect);
3659 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3660 return {PropertyPrecedence::Intersect};
3663 EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3665 if (
auto length = op.getLength()) {
3667 ps.addAsString(op.getDelay());
3670 ps.addAsString(op.getDelay());
3672 ps.addAsString(op.getDelay() + *length);
3676 if (op.getDelay() == 0) {
3678 }
else if (op.getDelay() == 1) {
3682 ps.addAsString(op.getDelay());
3687 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3688 return {PropertyPrecedence::Concat};
3691 void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3692 bool addSeparator =
false;
3693 for (
auto input : inputs) {
3696 if (!input.getDefiningOp<ltl::DelayOp>())
3697 ps <<
"##0" << PP::space;
3699 addSeparator =
true;
3700 emitNestedProperty(input, PropertyPrecedence::Concat);
3704 EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3705 emitLTLConcat(op.getInputs());
3706 return {PropertyPrecedence::Concat};
3709 EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3710 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3711 if (
auto more = op.getMore()) {
3713 ps.addAsString(op.getBase());
3716 ps.addAsString(op.getBase() + *more);
3720 if (op.getBase() == 0) {
3722 }
else if (op.getBase() == 1) {
3726 ps.addAsString(op.getBase());
3730 return {PropertyPrecedence::Repeat};
3733 EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3734 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3736 auto more = op.getMore();
3738 ps.addAsString(op.getBase());
3741 ps.addAsString(op.getBase() + more);
3745 return {PropertyPrecedence::Repeat};
3748 EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3749 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3751 auto more = op.getMore();
3753 ps.addAsString(op.getBase());
3756 ps.addAsString(op.getBase() + more);
3760 return {PropertyPrecedence::Repeat};
3763 EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3764 ps <<
"not" << PP::space;
3765 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3766 return {PropertyPrecedence::Unary};
3772 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3773 if (!concatOp || concatOp.getInputs().size() < 2)
3775 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3776 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3778 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3779 if (!constOp || !constOp.getValue().isOne())
3781 return concatOp.getInputs().drop_back();
3784 EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3788 emitLTLConcat(range);
3789 ps << PP::space <<
"|=>" << PP::nbsp;
3791 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3792 ps << PP::space <<
"|->" << PP::nbsp;
3794 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3795 return {PropertyPrecedence::Implication};
3798 EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3799 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3800 ps << PP::space <<
"until" << PP::space;
3801 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3802 return {PropertyPrecedence::Until};
3805 EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3806 ps <<
"s_eventually" << PP::space;
3807 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3808 return {PropertyPrecedence::Qualifier};
3811 EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3813 ps.scopedBox(PP::ibox2, [&] {
3814 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3815 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3819 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3820 return {PropertyPrecedence::Clocking};
3830 class NameCollector {
3832 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3836 void collectNames(Block &block);
3838 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3839 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3842 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3843 ModuleEmitter &moduleEmitter;
3848 static constexpr
size_t maxTypeWidthBound = 32;
3853 void NameCollector::collectNames(Block &block) {
3856 for (
auto &op : block) {
3860 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3861 FuncCallProceduralOp, FuncCallOp>(op))
3863 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3867 for (
auto result : op.getResults()) {
3869 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3870 SmallString<16> typeString;
3874 llvm::raw_svector_ostream stringStream(typeString);
3876 stringStream, op.getLoc());
3878 if (typeString.size() <= maxTypeWidthBound)
3879 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3886 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3887 for (
auto ®ion : op.getRegions()) {
3888 if (!region.empty())
3889 collectNames(region.front());
3903 class StmtEmitter :
public EmitterBase,
3904 public hw::StmtVisitor<StmtEmitter, LogicalResult>,
3905 public sv::Visitor<StmtEmitter, LogicalResult>,
3906 public verif::Visitor<StmtEmitter, LogicalResult> {
3911 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3913 void emitStatement(Operation *op);
3914 void emitStatementBlock(Block &body);
3917 LogicalResult emitDeclaration(Operation *op);
3920 void collectNamesAndCalculateDeclarationWidths(Block &block);
3923 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
3924 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
3925 bool isAssignmentLikeContext =
false);
3926 void emitSVAttributes(Operation *op);
3928 using hw::StmtVisitor<StmtEmitter, LogicalResult>::visitStmt;
3929 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
3930 using verif::Visitor<StmtEmitter, LogicalResult>::visitVerif;
3931 friend class hw::StmtVisitor<StmtEmitter, LogicalResult>;
3932 friend class sv::Visitor<StmtEmitter, LogicalResult>;
3933 friend class verif::Visitor<StmtEmitter, LogicalResult>;
3936 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
3937 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
3938 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
3939 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
3940 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
3941 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
3943 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
3944 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
3945 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
3946 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
3947 template <
typename Op>
3950 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3951 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
3952 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
3954 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3955 LogicalResult visitSV(
AssignOp op);
3956 LogicalResult visitSV(BPAssignOp op);
3957 LogicalResult visitSV(PAssignOp op);
3958 LogicalResult visitSV(ForceOp op);
3959 LogicalResult visitSV(ReleaseOp op);
3960 LogicalResult visitSV(AliasOp op);
3961 LogicalResult visitSV(InterfaceInstanceOp op);
3962 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
3963 LogicalResult visitStmt(OutputOp op);
3965 LogicalResult visitStmt(InstanceOp op);
3966 LogicalResult visitStmt(InstanceChoiceOp op);
3967 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
3968 ArrayRef<Value> instPortValues);
3973 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
3974 LogicalResult visitSV(OrderedOutputOp op);
3975 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
3976 LogicalResult visitSV(IfDefProceduralOp op) {
3977 return emitIfDef(op, op.getCond());
3979 LogicalResult visitSV(IfOp op);
3980 LogicalResult visitSV(AlwaysOp op);
3981 LogicalResult visitSV(AlwaysCombOp op);
3982 LogicalResult visitSV(AlwaysFFOp op);
3983 LogicalResult visitSV(InitialOp op);
3984 LogicalResult visitSV(CaseOp op);
3985 LogicalResult visitSV(FWriteOp op);
3986 LogicalResult visitSV(VerbatimOp op);
3988 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
3989 std::optional<unsigned> verbosity);
3990 LogicalResult visitSV(StopOp op);
3991 LogicalResult visitSV(FinishOp op);
3992 LogicalResult visitSV(ExitOp op);
3994 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
3995 std::optional<unsigned> verbosity,
3997 ValueRange operands);
3998 LogicalResult visitSV(FatalOp op);
3999 LogicalResult visitSV(ErrorOp op);
4000 LogicalResult visitSV(WarningOp op);
4001 LogicalResult visitSV(InfoOp op);
4003 LogicalResult visitSV(ReadMemOp op);
4005 LogicalResult visitSV(GenerateOp op);
4006 LogicalResult visitSV(GenerateCaseOp op);
4008 LogicalResult visitSV(ForOp op);
4010 void emitAssertionLabel(Operation *op);
4011 void emitAssertionMessage(StringAttr message, ValueRange args,
4012 SmallPtrSetImpl<Operation *> &ops,
4014 template <
typename Op>
4015 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4016 LogicalResult visitSV(AssertOp op);
4017 LogicalResult visitSV(AssumeOp op);
4018 LogicalResult visitSV(CoverOp op);
4019 template <
typename Op>
4020 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4021 LogicalResult visitSV(AssertConcurrentOp op);
4022 LogicalResult visitSV(AssumeConcurrentOp op);
4023 LogicalResult visitSV(CoverConcurrentOp op);
4024 template <
typename Op>
4025 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4026 LogicalResult visitSV(AssertPropertyOp op);
4027 LogicalResult visitSV(AssumePropertyOp op);
4028 LogicalResult visitSV(CoverPropertyOp op);
4030 LogicalResult visitSV(BindOp op);
4031 LogicalResult visitSV(InterfaceOp op);
4032 LogicalResult visitSV(InterfaceSignalOp op);
4033 LogicalResult visitSV(InterfaceModportOp op);
4034 LogicalResult visitSV(AssignInterfaceSignalOp op);
4035 LogicalResult visitSV(MacroDefOp op);
4037 void emitBlockAsStatement(Block *block,
4038 const SmallPtrSetImpl<Operation *> &locationOps,
4039 StringRef multiLineComment = StringRef());
4041 LogicalResult visitSV(FuncDPIImportOp op);
4042 template <
typename CallOp>
4043 LogicalResult emitFunctionCall(CallOp callOp);
4044 LogicalResult visitSV(FuncCallProceduralOp op);
4045 LogicalResult visitSV(FuncCallOp op);
4046 LogicalResult visitSV(ReturnOp op);
4049 ModuleEmitter &emitter;
4054 size_t maxDeclNameWidth = 0;
4055 size_t maxTypeWidth = 0;
4066 void StmtEmitter::emitExpression(Value exp,
4067 SmallPtrSetImpl<Operation *> &emittedExprs,
4068 VerilogPrecedence parenthesizeIfLooserThan,
4069 bool isAssignmentLikeContext) {
4070 ExprEmitter(emitter, emittedExprs)
4071 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4076 void StmtEmitter::emitSVAttributes(Operation *op) {
4084 setPendingNewline();
4087 void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4088 llvm::function_ref<
void()> emitRHS,
4090 std::optional<PPExtString> wordBeforeLHS) {
4092 ps.scopedBox(PP::ibox2, [&]() {
4093 if (wordBeforeLHS) {
4094 ps << *wordBeforeLHS << PP::space;
4098 ps << PP::space << syntax << PP::space;
4100 ps.scopedBox(PP::ibox0, [&]() {
4107 template <
typename Op>
4109 StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4110 std::optional<PPExtString> wordBeforeLHS) {
4111 SmallPtrSet<Operation *, 8> ops;
4115 ps.addCallback({op,
true});
4116 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4118 emitExpression(op.getSrc(), ops, LowestPrecedence,
4123 ps.addCallback({op,
false});
4124 emitLocationInfoAndNewLine(ops);
4128 LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4131 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4134 if (emitter.assignsInlined.count(op))
4138 emitSVAttributes(op);
4143 LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4144 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4148 if (emitter.assignsInlined.count(op))
4152 emitSVAttributes(op);
4157 LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4159 emitSVAttributes(op);
4164 LogicalResult StmtEmitter::visitSV(ForceOp op) {
4166 emitError(op,
"SV attributes emission is unimplemented for the op");
4171 LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4173 emitError(op,
"SV attributes emission is unimplemented for the op");
4176 SmallPtrSet<Operation *, 8> ops;
4178 ps.addCallback({op,
true});
4179 ps.scopedBox(PP::ibox2, [&]() {
4180 ps <<
"release" << PP::space;
4181 emitExpression(op.getDest(), ops);
4184 ps.addCallback({op,
false});
4185 emitLocationInfoAndNewLine(ops);
4189 LogicalResult StmtEmitter::visitSV(AliasOp op) {
4191 emitError(op,
"SV attributes emission is unimplemented for the op");
4194 SmallPtrSet<Operation *, 8> ops;
4196 ps.addCallback({op,
true});
4197 ps.scopedBox(PP::ibox2, [&]() {
4198 ps <<
"alias" << PP::space;
4199 ps.scopedBox(PP::cbox0, [&]() {
4201 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4202 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4206 ps.addCallback({op,
false});
4207 emitLocationInfoAndNewLine(ops);
4211 LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4212 auto doNotPrint = op->hasAttr(
"doNotPrint");
4213 if (doNotPrint && !state.options.emitBindComments)
4217 emitError(op,
"SV attributes emission is unimplemented for the op");
4220 StringRef prefix =
"";
4221 ps.addCallback({op,
true});
4224 ps <<
"// This interface is elsewhere emitted as a bind statement."
4228 SmallPtrSet<Operation *, 8> ops;
4231 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4232 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4233 "point to an interface");
4236 if (!prefix.empty())
4242 ps.addCallback({op,
false});
4243 emitLocationInfoAndNewLine(ops);
4251 LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4253 SmallPtrSet<Operation *, 8> ops;
4254 size_t operandIndex = 0;
4255 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4257 auto operand = op->getOperand(operandIndex);
4261 if (operand.hasOneUse() && operand.getDefiningOp() &&
4262 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4271 ps.addCallback({op,
true});
4273 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
4275 ps <<
"// Zero width: ";
4278 ps <<
"assign" << PP::space;
4280 ps << PP::space <<
"=" << PP::space;
4281 ps.scopedBox(PP::ibox0, [&]() {
4285 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4286 ps <<
"/*Zero width*/";
4288 emitExpression(operand, ops, LowestPrecedence,
4293 ps.addCallback({op,
false});
4294 emitLocationInfoAndNewLine(ops);
4301 LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4302 auto parent = op->getParentOfType<PortList>();
4304 return emitOutputLikeOp(op, ports);
4307 LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4309 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4310 ps <<
"`ifndef " << typescopeDef << PP::newline;
4311 ps <<
"`define " << typescopeDef;
4312 setPendingNewline();
4313 emitStatementBlock(*op.getBodyBlock());
4315 ps <<
"`endif // " << typescopeDef;
4316 setPendingNewline();
4320 LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4322 emitError(op,
"SV attributes emission is unimplemented for the op");
4327 ps << PP::neverbox <<
"// ";
4329 SmallPtrSet<Operation *, 8> ops;
4331 ps.scopedBox(PP::ibox2, [&]() {
4332 ps <<
"typedef" << PP::space;
4333 ps.invokeWithStringOS([&](
auto &os) {
4335 op.getAliasType(),
false);
4337 ps << PP::space <<
PPExtString(op.getPreferredName());
4338 ps.invokeWithStringOS(
4339 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4344 emitLocationInfoAndNewLine(ops);
4348 template <
typename CallOpTy>
4349 LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4353 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4355 SmallPtrSet<Operation *, 8> ops;
4359 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4360 if (explicitReturn) {
4361 assert(explicitReturn.hasOneUse());
4362 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4363 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4364 emitExpression(bpassignOp.getDest(), ops);
4366 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4367 ps <<
"assign" << PP::nbsp;
4368 emitExpression(assignOp.getDest(), ops);
4370 ps << PP::nbsp <<
"=" << PP::nbsp;
4373 auto arguments = callee.getPortList(
true);
4377 bool needsComma =
false;
4378 auto printArg = [&](Value value) {
4380 ps <<
"," << PP::space;
4381 emitExpression(value, ops);
4385 ps.scopedBox(PP::ibox0, [&] {
4386 unsigned inputIndex = 0, outputIndex = 0;
4387 for (
auto arg : arguments) {
4390 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4392 printArg(op.getInputs()[inputIndex++]);
4397 emitLocationInfoAndNewLine(ops);
4401 LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4402 return emitFunctionCall(op);
4405 LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4406 return emitFunctionCall(op);
4409 template <
typename PPS>
4411 bool isAutomatic =
false,
4412 bool emitAsTwoStateType =
false) {
4413 ps <<
"function" << PP::nbsp;
4415 ps <<
"automatic" << PP::nbsp;
4416 auto retType = op.getExplicitlyReturnedType();
4418 ps.invokeWithStringOS([&](
auto &os) {
4419 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4420 emitAsTwoStateType);
4426 emitter.emitPortList(
4430 LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4431 auto parent = op->getParentOfType<sv::FuncOp>();
4433 return emitOutputLikeOp(op, ports);
4436 LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4439 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4443 if (
auto linkageName = importOp.getLinkageName())
4444 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4446 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4447 assert(op.isDeclaration() &&
"function must be a declaration");
4450 assert(state.pendingNewline);
4456 LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4458 emitError(op,
"SV attributes emission is unimplemented for the op");
4461 SmallPtrSet<Operation *, 8> ops;
4464 ps.addCallback({op,
true});
4466 ps.scopedBox(PP::ibox0, [&]() {
4467 emitExpression(op.getFd(), ops);
4469 ps <<
"," << PP::space;
4470 ps.writeQuotedEscaped(op.getFormatString());
4478 for (
auto operand : op.getSubstitutions()) {
4479 ps <<
"," << PP::space;
4480 emitExpression(operand, ops);
4484 ps.addCallback({op,
false});
4485 emitLocationInfoAndNewLine(ops);
4489 LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4491 emitError(op,
"SV attributes emission is unimplemented for the op");
4494 SmallPtrSet<Operation *, 8> ops;
4499 StringRef
string = op.getFormatString();
4500 if (
string.ends_with(
"\n"))
4501 string =
string.drop_back();
4506 bool isFirst =
true;
4509 while (!
string.
empty()) {
4510 auto lhsRhs =
string.split(
'\n');
4514 ps << PP::end << PP::newline << PP::neverbox;
4518 emitTextWithSubstitutions(
4519 ps, lhsRhs.first, op,
4520 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4521 string = lhsRhs.second;
4526 emitLocationInfoAndNewLine(ops);
4532 StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4533 std::optional<unsigned> verbosity) {
4535 emitError(op,
"SV attributes emission is unimplemented for the op");
4538 SmallPtrSet<Operation *, 8> ops;
4540 ps.addCallback({op,
true});
4542 if (verbosity && *verbosity != 1) {
4544 ps.addAsString(*verbosity);
4548 ps.addCallback({op,
false});
4549 emitLocationInfoAndNewLine(ops);
4553 LogicalResult StmtEmitter::visitSV(StopOp op) {
4554 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4557 LogicalResult StmtEmitter::visitSV(FinishOp op) {
4558 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4562 LogicalResult StmtEmitter::visitSV(ExitOp op) {
4563 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4569 StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4570 std::optional<unsigned> verbosity,
4571 StringAttr message, ValueRange operands) {
4573 emitError(op,
"SV attributes emission is unimplemented for the op");
4576 SmallPtrSet<Operation *, 8> ops;
4578 ps.addCallback({op,
true});
4584 if ((verbosity && *verbosity != 1) || message) {
4586 ps.scopedBox(PP::ibox0, [&]() {
4590 ps.addAsString(*verbosity);
4595 ps <<
"," << PP::space;
4596 ps.writeQuotedEscaped(message.getValue());
4598 for (
auto operand : operands) {
4599 ps <<
"," << PP::space;
4600 emitExpression(operand, ops);
4609 ps.addCallback({op,
false});
4610 emitLocationInfoAndNewLine(ops);
4614 LogicalResult StmtEmitter::visitSV(FatalOp op) {
4615 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4616 op.getMessageAttr(), op.getSubstitutions());
4619 LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4620 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4621 op.getMessageAttr(), op.getSubstitutions());
4624 LogicalResult StmtEmitter::visitSV(WarningOp op) {
4625 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4626 op.getMessageAttr(), op.getSubstitutions());
4629 LogicalResult StmtEmitter::visitSV(InfoOp op) {
4630 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4631 op.getMessageAttr(), op.getSubstitutions());
4634 LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4635 SmallPtrSet<Operation *, 8> ops({op});
4638 ps.addCallback({op,
true});
4640 switch (op.getBaseAttr().getValue()) {
4641 case MemBaseTypeAttr::MemBaseBin:
4644 case MemBaseTypeAttr::MemBaseHex:
4649 ps.scopedBox(PP::ibox0, [&]() {
4650 ps.writeQuotedEscaped(op.getFilename());
4651 ps <<
"," << PP::space;
4652 emitExpression(op.getDest(), ops);
4656 ps.addCallback({op,
false});
4657 emitLocationInfoAndNewLine(ops);
4661 LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4662 emitSVAttributes(op);
4665 ps.addCallback({op,
true});
4666 ps <<
"generate" << PP::newline;
4668 setPendingNewline();
4669 emitStatementBlock(op.getBody().getBlocks().front());
4672 ps <<
"endgenerate";
4673 ps.addCallback({op,
false});
4674 setPendingNewline();
4678 LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4679 emitSVAttributes(op);
4682 ps.addCallback({op,
true});
4684 ps.invokeWithStringOS([&](
auto &os) {
4685 emitter.printParamValue(
4686 op.getCond(), os, VerilogPrecedence::Selection,
4687 [&]() { return op->emitOpError(
"invalid case parameter"); });
4690 setPendingNewline();
4693 ArrayAttr
patterns = op.getCasePatterns();
4694 ArrayAttr caseNames = op.getCaseNames();
4695 MutableArrayRef<Region> regions = op.getCaseRegions();
4702 llvm::StringMap<size_t> nextGenIds;
4703 ps.scopedBox(PP::bbox2, [&]() {
4705 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4706 auto ®ion = regions[i];
4707 assert(region.hasOneBlock());
4708 Attribute patternAttr = patterns[i];
4711 if (!isa<mlir::TypedAttr>(patternAttr))
4714 ps.invokeWithStringOS([&](auto &os) {
4715 emitter.printParamValue(
4716 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4717 [&]() { return op->emitOpError(
"invalid case value"); });
4720 StringRef legalName =
4721 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4724 setPendingNewline();
4725 emitStatementBlock(region.getBlocks().front());
4728 setPendingNewline();
4734 ps.addCallback({op,
false});
4735 setPendingNewline();
4739 LogicalResult StmtEmitter::visitSV(ForOp op) {
4740 emitSVAttributes(op);
4741 llvm::SmallPtrSet<Operation *, 8> ops;
4742 ps.addCallback({op,
true});
4744 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4747 ps.scopedBox(PP::cbox0, [&]() {
4751 ps <<
"logic" << PP::nbsp;
4752 ps.invokeWithStringOS([&](
auto &os) {
4753 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4758 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4763 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4764 [&]() { emitExpression(op.getUpperBound(), ops); },
4770 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4771 [&]() { emitExpression(op.getStep(), ops); },
4775 ps << PP::neverbreak;
4776 setPendingNewline();
4777 emitStatementBlock(op.getBody().getBlocks().front());
4780 ps.addCallback({op,
false});
4781 emitLocationInfoAndNewLine(ops);
4786 void StmtEmitter::emitAssertionLabel(Operation *op) {
4787 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4793 void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4794 SmallPtrSetImpl<Operation *> &ops,
4795 bool isConcurrent =
false) {
4798 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4799 ps.scopedBox(PP::ibox0, [&]() {
4800 ps.writeQuotedEscaped(message.getValue());
4802 for (
auto arg : args) {
4803 ps <<
"," << PP::space;
4804 emitExpression(arg, ops);
4810 template <
typename Op>
4811 LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4813 emitError(op,
"SV attributes emission is unimplemented for the op");
4816 SmallPtrSet<Operation *, 8> ops;
4818 ps.addCallback({op,
true});
4819 ps.scopedBox(PP::ibox2, [&]() {
4820 emitAssertionLabel(op);
4821 ps.scopedBox(PP::cbox0, [&]() {
4823 switch (op.getDefer()) {
4824 case DeferAssert::Immediate:
4826 case DeferAssert::Observed:
4829 case DeferAssert::Final:
4834 ps.scopedBox(PP::ibox0, [&]() {
4835 emitExpression(op.getExpression(), ops);
4838 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4842 ps.addCallback({op,
false});
4843 emitLocationInfoAndNewLine(ops);
4847 LogicalResult StmtEmitter::visitSV(AssertOp op) {
4848 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4851 LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4852 return emitImmediateAssertion(op,
PPExtString(
"assume"));
4855 LogicalResult StmtEmitter::visitSV(CoverOp op) {
4856 return emitImmediateAssertion(op,
PPExtString(
"cover"));
4859 template <
typename Op>
4860 LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
4862 emitError(op,
"SV attributes emission is unimplemented for the op");
4865 SmallPtrSet<Operation *, 8> ops;
4867 ps.addCallback({op,
true});
4868 ps.scopedBox(PP::ibox2, [&]() {
4869 emitAssertionLabel(op);
4870 ps.scopedBox(PP::cbox0, [&]() {
4871 ps << opName << PP::nbsp <<
"property (";
4872 ps.scopedBox(PP::ibox0, [&]() {
4873 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
4875 emitExpression(op.getClock(), ops);
4876 ps <<
")" << PP::space;
4877 emitExpression(op.getProperty(), ops);
4880 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
4885 ps.addCallback({op,
false});
4886 emitLocationInfoAndNewLine(ops);
4890 LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
4891 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
4894 LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
4895 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
4898 LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
4899 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
4904 template <
typename Op>
4905 LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
4907 emitError(op,
"SV attributes emission is unimplemented for the op");
4917 Operation *parent = op->getParentOp();
4918 Value
property = op.getProperty();
4919 bool isTemporal = !
property.getType().isSignlessInteger(1);
4920 bool isProcedural = parent->hasTrait<ProceduralRegion>();
4921 bool emitAsImmediate = !isTemporal && isProcedural;
4924 SmallPtrSet<Operation *, 8> ops;
4926 ps.addCallback({op,
true});
4927 ps.scopedBox(PP::ibox2, [&]() {
4929 emitAssertionLabel(op);
4931 ps.scopedBox(PP::cbox0, [&]() {
4932 if (emitAsImmediate)
4933 ps << opName <<
"(";
4935 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
4937 Value clock = op.getClock();
4938 auto event = op.getEvent();
4940 ps.scopedBox(PP::ibox2, [&]() {
4941 PropertyEmitter(emitter, ops)
4942 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
4945 ps.scopedBox(PP::ibox2, [&]() {
4946 PropertyEmitter(emitter, ops)
4947 .emitAssertPropertyBody(property, op.getDisable());
4952 ps.addCallback({op,
false});
4953 emitLocationInfoAndNewLine(ops);
4957 LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
4958 return emitPropertyAssertion(op,
PPExtString(
"assert"));
4961 LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
4962 return emitPropertyAssertion(op,
PPExtString(
"assume"));
4965 LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
4966 return emitPropertyAssertion(op,
PPExtString(
"cover"));
4969 LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
4971 emitError(op,
"SV attributes emission is unimplemented for the op");
4976 bool hasEmptyThen = op->getRegion(0).front().empty();
4978 ps <<
"`ifndef " << ident;
4980 ps <<
"`ifdef " << ident;
4982 SmallPtrSet<Operation *, 8> ops;
4984 emitLocationInfoAndNewLine(ops);
4987 emitStatementBlock(op->getRegion(0).front());
4989 if (!op->getRegion(1).empty()) {
4990 if (!hasEmptyThen) {
4992 ps <<
"`else // " << ident;
4993 setPendingNewline();
4995 emitStatementBlock(op->getRegion(1).front());
5002 setPendingNewline();
5010 void StmtEmitter::emitBlockAsStatement(
5011 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5012 StringRef multiLineComment) {
5019 emitLocationInfoAndNewLine(locationOps);
5022 emitStatementBlock(*block);
5024 if (needsBeginEnd) {
5028 if (!multiLineComment.empty())
5029 ps <<
" // " << multiLineComment;
5030 setPendingNewline();
5034 LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5036 for (
auto &op : ooop.getBody().front())
5041 LogicalResult StmtEmitter::visitSV(IfOp op) {
5042 SmallPtrSet<Operation *, 8> ops;
5044 auto ifcondBox = PP::ibox2;
5046 emitSVAttributes(op);
5048 ps.addCallback({op,
true});
5049 ps <<
"if (" << ifcondBox;
5059 emitExpression(ifOp.getCond(), ops);
5060 ps << PP::end <<
")";
5061 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5063 if (!ifOp.hasElse())
5067 Block *elseBlock = ifOp.getElseBlock();
5069 if (!nestedElseIfOp) {
5074 emitBlockAsStatement(elseBlock, ops);
5080 ifOp = nestedElseIfOp;
5081 ps <<
"else if (" << ifcondBox;
5083 ps.addCallback({op,
false});
5088 LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5089 emitSVAttributes(op);
5090 SmallPtrSet<Operation *, 8> ops;
5094 auto printEvent = [&](AlwaysOp::Condition cond) {
5095 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5096 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5098 ps.addCallback({op,
true});
5100 switch (op.getNumConditions()) {
5106 printEvent(op.getCondition(0));
5111 ps.scopedBox(PP::cbox0, [&]() {
5112 printEvent(op.getCondition(0));
5113 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5114 ps << PP::space <<
"or" << PP::space;
5115 printEvent(op.getCondition(i));
5124 std::string comment;
5125 if (op.getNumConditions() == 0) {
5126 comment =
"always @*";
5128 comment =
"always @(";
5131 [&](Attribute eventAttr) {
5132 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5133 comment += stringifyEventControl(event);
5135 [&]() { comment +=
", "; });
5139 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5140 ps.addCallback({op,
false});
5144 LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5145 emitSVAttributes(op);
5146 SmallPtrSet<Operation *, 8> ops;
5150 ps.addCallback({op,
true});
5151 StringRef opString =
"always_comb";
5152 if (state.options.noAlwaysComb)
5153 opString =
"always @(*)";
5156 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5157 ps.addCallback({op,
false});
5161 LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5162 emitSVAttributes(op);
5164 SmallPtrSet<Operation *, 8> ops;
5168 ps.addCallback({op,
true});
5169 ps <<
"always_ff @(";
5170 ps.scopedBox(PP::cbox0, [&]() {
5171 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5172 emitExpression(op.getClock(), ops);
5173 if (op.getResetStyle() == ResetType::AsyncReset) {
5174 ps << PP::nbsp <<
"or" << PP::space
5175 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5176 emitExpression(op.getReset(), ops);
5183 std::string comment;
5184 comment +=
"always_ff @(";
5185 comment += stringifyEventControl(op.getClockEdge());
5186 if (op.getResetStyle() == ResetType::AsyncReset) {
5188 comment += stringifyEventControl(*op.getResetEdge());
5192 if (op.getResetStyle() == ResetType::NoReset)
5193 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5196 emitLocationInfoAndNewLine(ops);
5197 ps.scopedBox(PP::bbox2, [&]() {
5203 if (op.getResetStyle() == ResetType::AsyncReset &&
5204 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5206 emitExpression(op.getReset(), ops);
5208 emitBlockAsStatement(op.getResetBlock(), ops);
5211 emitBlockAsStatement(op.getBodyBlock(), ops);
5216 ps <<
" // " << comment;
5217 setPendingNewline();
5219 ps.addCallback({op,
false});
5223 LogicalResult StmtEmitter::visitSV(InitialOp op) {
5224 emitSVAttributes(op);
5225 SmallPtrSet<Operation *, 8> ops;
5228 ps.addCallback({op,
true});
5230 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5231 ps.addCallback({op,
false});
5235 LogicalResult StmtEmitter::visitSV(CaseOp op) {
5236 emitSVAttributes(op);
5237 SmallPtrSet<Operation *, 8> ops, emptyOps;
5240 ps.addCallback({op,
true});
5241 if (op.getValidationQualifier() !=
5242 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5243 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5244 op.getValidationQualifier()))
5246 const char *opname =
nullptr;
5247 switch (op.getCaseStyle()) {
5248 case CaseStmtType::CaseStmt:
5251 case CaseStmtType::CaseXStmt:
5254 case CaseStmtType::CaseZStmt:
5258 ps << opname <<
" (";
5259 ps.scopedBox(PP::ibox0, [&]() {
5260 emitExpression(op.getCond(), ops);
5263 emitLocationInfoAndNewLine(ops);
5265 ps.scopedBox(PP::bbox2, [&]() {
5266 for (
auto &caseInfo : op.getCases()) {
5268 auto &pattern = caseInfo.pattern;
5270 llvm::TypeSwitch<CasePattern *>(pattern.get())
5271 .Case<CaseBitPattern>([&](auto bitPattern) {
5274 ps.invokeWithStringOS([&](auto &os) {
5275 os << bitPattern->getWidth() <<
"'b";
5276 for (size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5277 os << getLetter(bitPattern->getBit(e - bit - 1));
5280 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5281 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5282 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5284 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5285 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5288 emitBlockAsStatement(caseInfo.block, emptyOps);
5294 ps.addCallback({op,
false});
5295 emitLocationInfoAndNewLine(ops);
5299 LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5300 bool doNotPrint = op->hasAttr(
"doNotPrint");
5301 if (doNotPrint && !state.options.emitBindComments)
5306 emitSVAttributes(op);
5308 ps.addCallback({op,
true});
5311 <<
"/* This instance is elsewhere emitted as a bind statement."
5314 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5315 "attributes. The attributes will not be emitted.";
5318 SmallPtrSet<Operation *, 8> ops;
5323 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5324 assert(moduleOp &&
"Invalid IR");
5328 if (!op.getParameters().empty()) {
5331 bool printed =
false;
5333 llvm::zip(op.getParameters(),
5334 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5335 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5336 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5338 if (param.getValue() == modParam.getValue())
5343 ps <<
" #(" << PP::bbox2 << PP::newline;
5346 ps <<
"," << PP::newline;
5350 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5352 ps.invokeWithStringOS([&](
auto &os) {
5353 emitter.printParamValue(param.getValue(), os, [&]() {
5354 return op->emitOpError(
"invalid instance parameter '")
5355 << param.getName().getValue() <<
"' value";
5361 ps << PP::end << PP::newline <<
")";
5368 SmallVector<Value> instPortValues(modPortInfo.size());
5369 op.getValues(instPortValues, modPortInfo);
5370 emitInstancePortList(op, modPortInfo, instPortValues);
5372 ps.addCallback({op,
false});
5373 emitLocationInfoAndNewLine(ops);
5378 setPendingNewline();
5383 LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5385 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5386 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5391 Operation *defaultModuleOp =
5392 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5394 SmallVector<Value> instPortValues(modPortInfo.size());
5395 op.getValues(instPortValues, modPortInfo);
5396 emitInstancePortList(op, modPortInfo, instPortValues);
5398 SmallPtrSet<Operation *, 8> ops;
5400 ps.addCallback({op,
false});
5401 emitLocationInfoAndNewLine(ops);
5406 void StmtEmitter::emitInstancePortList(Operation *op,
5408 ArrayRef<Value> instPortValues) {
5409 SmallPtrSet<Operation *, 8> ops;
5412 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5413 ModulePortInfo containingPortList(containingModule.getPortList());
5418 size_t maxNameLength = 0;
5419 for (
auto &elt : modPortInfo) {
5420 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5423 auto getWireForValue = [&](Value result) {
5424 return result.getUsers().begin()->getOperand(0);
5428 bool isFirst =
true;
5429 bool isZeroWidth =
false;
5431 for (
size_t portNum = 0, portEnd = modPortInfo.size(); portNum < portEnd;
5433 auto &modPort = modPortInfo.at(portNum);
5435 Value portVal = instPortValues[portNum];
5440 bool shouldPrintComma =
true;
5442 shouldPrintComma =
false;
5443 for (
size_t i = portNum + 1, e = modPortInfo.size(); i != e; ++i)
5445 shouldPrintComma =
true;
5450 if (shouldPrintComma)
5453 emitLocationInfoAndNewLine(ops);
5468 ps.scopedBox(isZeroWidth ? PP::neverbox : PP::ibox2, [&]() {
5469 auto modPortName = modPort.getVerilogName();
5471 ps.spaces(maxNameLength - modPortName.size() + 1);
5473 ps.scopedBox(PP::ibox0, [&]() {
5480 if (!modPort.isOutput()) {
5482 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5483 ps <<
"/* Zero width */";
5485 emitExpression(portVal, ops, LowestPrecedence);
5486 }
else if (portVal.use_empty()) {
5487 ps <<
"/* unused */";
5488 }
else if (portVal.hasOneUse() &&
5489 (output = dyn_cast_or_null<OutputOp>(
5490 portVal.getUses().begin()->getOwner()))) {
5495 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5497 containingPortList.atOutput(outputPortNo).getVerilogName());
5499 portVal = getWireForValue(portVal);
5500 emitExpression(portVal, ops);
5506 if (!isFirst || isZeroWidth) {
5507 emitLocationInfoAndNewLine(ops);
5520 LogicalResult StmtEmitter::visitSV(BindOp op) {
5521 emitter.emitBind(op);
5522 assert(state.pendingNewline);
5526 LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5527 emitComment(op.getCommentAttr());
5529 emitSVAttributes(op);
5532 ps.addCallback({op,
true});
5534 setPendingNewline();
5536 emitStatementBlock(*op.getBodyBlock());
5538 ps <<
"endinterface" << PP::newline;
5539 ps.addCallback({op,
false});
5540 setPendingNewline();
5544 LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5546 emitSVAttributes(op);
5548 ps.addCallback({op,
true});
5550 ps << PP::neverbox <<
"// ";
5551 ps.invokeWithStringOS([&](
auto &os) {
5556 ps.invokeWithStringOS(
5557 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5561 ps.addCallback({op,
false});
5562 setPendingNewline();
5566 LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5568 ps.addCallback({op,
true});
5572 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5573 auto port = cast<ModportStructAttr>(portAttr);
5574 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5575 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5576 ps << PPExtString(getSymOpName(signalDecl));
5580 ps.addCallback({op,
false});
5581 setPendingNewline();
5585 LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5587 ps.addCallback({op,
true});
5588 SmallPtrSet<Operation *, 8> emitted;
5591 emitExpression(op.getIface(), emitted);
5592 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5593 emitExpression(op.getRhs(), emitted);
5595 ps.addCallback({op,
false});
5596 setPendingNewline();
5600 LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5601 auto decl = op.getReferencedMacro(&state.symbolCache);
5604 ps.addCallback({op,
true});
5606 if (decl.getArgs()) {
5608 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5609 ps << cast<StringAttr>(name);
5613 if (!op.getFormatString().empty()) {
5615 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5618 ps.addCallback({op,
false});
5619 setPendingNewline();
5623 void StmtEmitter::emitStatement(Operation *op) {
5630 if (isa<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5634 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5635 succeeded(dispatchVerifVisitor(op)))
5638 emitOpError(op,
"emission to Verilog not supported");
5639 emitPendingNewlineIfNeeded();
5640 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5641 setPendingNewline();
5652 StmtEmitter &stmtEmitter) {
5659 if (isa<IfDefProceduralOp>(op->getParentOp()))
5667 SmallVector<Value, 8> exprsToScan(op->getOperands());
5672 while (!exprsToScan.empty()) {
5673 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5680 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5681 auto *defOp = readInout.getOperand().getDefiningOp();
5688 if (isa<sv::WireOp>(defOp))
5693 if (!isa<RegOp, LogicOp>(defOp))
5699 if (isa<LogicOp>(defOp) &&
5700 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5704 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5705 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5713 exprsToScan.append(expr->getOperands().begin(),
5714 expr->getOperands().end());
5720 if (expr->getBlock() != op->getBlock())
5725 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5732 template <
class AssignTy>
5734 AssignTy singleAssign;
5735 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5736 if (hasSVAttributes(user))
5739 if (auto assign = dyn_cast<AssignTy>(user)) {
5742 singleAssign = assign;
5746 return isa<ReadInOutOp>(user);
5748 return singleAssign;
5754 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5758 if (op1->getBlock() != user->getBlock())
5764 return op1->isBeforeInBlock(user);
5768 LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5769 emitSVAttributes(op);
5770 auto value = op->getResult(0);
5771 SmallPtrSet<Operation *, 8> opsForLocation;
5772 opsForLocation.insert(op);
5774 ps.addCallback({op,
true});
5777 auto type = value.getType();
5780 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
5781 unsigned targetColumn = 0;
5782 unsigned column = 0;
5785 if (maxDeclNameWidth > 0)
5786 targetColumn += maxDeclNameWidth + 1;
5789 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5790 }
else if (!word.empty()) {
5792 column += word.size();
5793 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5794 ps.spaces(numSpaces);
5795 column += numSpaces;
5798 SmallString<8> typeString;
5801 llvm::raw_svector_ostream stringStream(typeString);
5806 if (maxTypeWidth > 0)
5807 targetColumn += maxTypeWidth + 1;
5808 unsigned numSpaces = 0;
5809 if (!typeString.empty()) {
5811 column += typeString.size();
5814 if (targetColumn > column)
5815 numSpaces = targetColumn - column;
5816 ps.spaces(numSpaces);
5817 column += numSpaces;
5823 ps.invokeWithStringOS(
5824 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5827 if (state.options.printDebugInfo) {
5828 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5829 auto innerSym = innerSymOp.getInnerSymAttr();
5830 if (innerSym && !innerSym.empty()) {
5832 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5838 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5839 ps << PP::space <<
"=" << PP::space;
5840 ps.invokeWithStringOS([&](
auto &os) {
5841 emitter.printParamValue(localparam.getValue(), os, [&]() {
5842 return op->emitOpError(
"invalid localparam value");
5847 if (
auto regOp = dyn_cast<RegOp>(op)) {
5848 if (
auto initValue = regOp.getInit()) {
5849 ps << PP::space <<
"=" << PP::space;
5850 ps.scopedBox(PP::ibox0, [&]() {
5851 emitExpression(initValue, opsForLocation, LowestPrecedence,
5858 if (isa<sv::WireOp, LogicOp>(op) &&
5859 !op->getParentOp()->hasTrait<ProceduralRegion>()) {
5861 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
5862 auto *source = singleAssign.getSrc().getDefiningOp();
5866 if (!source || isa<ConstantOp>(source) ||
5867 op->getNextNode() == singleAssign) {
5868 ps << PP::space <<
"=" << PP::space;
5869 ps.scopedBox(PP::ibox0, [&]() {
5870 emitExpression(singleAssign.getSrc(), opsForLocation,
5874 emitter.assignsInlined.insert(singleAssign);
5880 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>()) {
5882 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
5885 auto *source = singleAssign.getSrc().getDefiningOp();
5889 if (!source || isa<ConstantOp>(source) ||
5892 ps << PP::space <<
"=" << PP::space;
5893 ps.scopedBox(PP::ibox0, [&]() {
5894 emitExpression(singleAssign.getSrc(), opsForLocation,
5899 emitter.assignsInlined.insert(singleAssign);
5900 emitter.expressionsEmittedIntoDecl.insert(op);
5907 ps.addCallback({op,
false});
5908 emitLocationInfoAndNewLine(opsForLocation);
5912 void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
5915 NameCollector collector(emitter);
5916 collector.collectNames(block);
5919 maxDeclNameWidth = collector.getMaxDeclNameWidth();
5920 maxTypeWidth = collector.getMaxTypeWidth();
5923 void StmtEmitter::emitStatementBlock(Block &body) {
5924 ps.scopedBox(PP::bbox2, [&]() {
5929 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
5930 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
5935 if (!isa<IfDefProceduralOp>(body.getParentOp()))
5936 collectNamesAndCalculateDeclarationWidths(body);
5939 for (
auto &op : body) {
5946 void ModuleEmitter::emitStatement(Operation *op) {
5947 StmtEmitter(*
this, state.options).emitStatement(op);
5952 void ModuleEmitter::emitSVAttributes(Operation *op) {
5960 setPendingNewline();
5968 auto verilogName = module.getVerilogModuleNameAttr();
5970 ps.addCallback({module,
true});
5971 ps <<
"// external module " <<
PPExtString(verilogName.getValue())
5973 ps.addCallback({module,
false});
5974 setPendingNewline();
5977 void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
5978 auto verilogName = module.getVerilogModuleNameAttr();
5980 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
5982 setPendingNewline();
5991 void ModuleEmitter::emitBind(BindOp op) {
5993 emitError(op,
"SV attributes emission is unimplemented for the op");
5994 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6000 Operation *childMod =
6001 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6005 ps.addCallback({op,
true});
6006 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6007 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6009 bool isFirst =
true;
6010 ps.scopedBox(PP::bbox2, [&]() {
6011 auto parentPortInfo = parentMod.getPortList();
6015 size_t maxNameLength = 0;
6016 for (
auto &elt : childPortInfo) {
6017 auto portName = elt.getVerilogName();
6018 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6019 maxNameLength = std::max(maxNameLength, elt.getName().size());
6022 SmallVector<Value> instPortValues(childPortInfo.size());
6023 inst.getValues(instPortValues, childPortInfo);
6025 for (
auto [idx, elt] : llvm::enumerate(childPortInfo)) {
6027 Value portVal = instPortValues[idx];
6033 bool shouldPrintComma =
true;
6035 shouldPrintComma =
false;
6036 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6038 shouldPrintComma =
true;
6043 if (shouldPrintComma)
6056 ps << PP::neverbox <<
"//";
6060 ps.nbsp(maxNameLength - elt.getName().size());
6062 llvm::SmallPtrSet<Operation *, 4> ops;
6063 if (elt.isOutput()) {
6064 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6065 "output port must have either single or no use");
6066 if (portVal.use_empty()) {
6067 ps <<
"/* unused */";
6068 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6069 portVal.getUses().begin()->getOwner())) {
6072 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6074 parentPortList.atOutput(outputPortNo).getVerilogName());
6076 portVal = portVal.getUsers().begin()->getOperand(0);
6077 ExprEmitter(*
this, ops)
6078 .emitExpression(portVal, LowestPrecedence,
6082 ExprEmitter(*
this, ops)
6083 .emitExpression(portVal, LowestPrecedence,
6096 ps.addCallback({op,
false});
6097 setPendingNewline();
6100 void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6102 emitError(op,
"SV attributes emission is unimplemented for the op");
6104 auto instance = op.getReferencedInstance(&state.symbolCache);
6106 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6107 instance.getInterfaceType().getInterface());
6109 ps.addCallback({op,
true});
6110 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6111 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6113 ps.addCallback({op,
false});
6114 setPendingNewline();
6117 void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6121 auto printParamType = [&](Type type, Attribute defaultValue,
6122 SmallString<8> &result) {
6124 llvm::raw_svector_ostream sstream(result);
6129 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6130 if (intAttr.getValue().getBitWidth() == 32)
6132 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6133 if (fpAttr.getType().isF64())
6136 if (isa<NoneType>(type))
6143 if (
auto intType = type_dyn_cast<IntegerType>(type))
6144 if (intType.getWidth() == 32) {
6145 sstream <<
"/*integer*/";
6149 printPackedType(type, sstream, module->getLoc(),
6157 size_t maxTypeWidth = 0;
6158 SmallString<8> scratch;
6159 for (
auto param : params) {
6160 auto paramAttr = cast<ParamDeclAttr>(param);
6162 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6163 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6166 if (maxTypeWidth > 0)
6169 ps.scopedBox(PP::bbox2, [&]() {
6170 ps << PP::newline <<
"#(";
6171 ps.scopedBox(PP::cbox0, [&]() {
6174 [&](Attribute param) {
6175 auto paramAttr = cast<ParamDeclAttr>(param);
6176 auto defaultValue = paramAttr.getValue();
6178 printParamType(paramAttr.getType(), defaultValue, scratch);
6179 if (!scratch.empty())
6181 if (scratch.size() < maxTypeWidth)
6182 ps.nbsp(maxTypeWidth - scratch.size());
6184 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6185 module, paramAttr.getName()));
6189 ps.invokeWithStringOS([&](
auto &os) {
6191 return module->emitError(
"parameter '")
6192 << paramAttr.getName().getValue()
6193 <<
"' has invalid value";
6198 [&]() { ps <<
"," << PP::newline; });
6204 void ModuleEmitter::emitPortList(Operation *module,
6206 bool emitAsTwoStateType) {
6208 if (portInfo.
size())
6209 emitLocationInfo(module->getLoc());
6213 bool hasOutputs =
false, hasZeroWidth =
false;
6214 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6215 SmallVector<SmallString<8>, 16> portTypeStrings;
6217 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6218 auto port = portInfo.
at(i);
6222 lastNonZeroPort = i;
6225 portTypeStrings.push_back({});
6227 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6229 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6232 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6235 if (maxTypeWidth > 0)
6239 ps.scopedBox(PP::bbox2, [&]() {
6240 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6241 auto lastPort = e - 1;
6244 auto portType = portInfo.at(portIdx).type;
6248 bool isZeroWidth = false;
6250 isZeroWidth = isZeroBitType(portType);
6253 ps << (isZeroWidth ?
"// " :
" ");
6257 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6258 switch (thisPortDirection) {
6263 ps << (hasOutputs ?
"input " :
"input ");
6266 ps << (hasOutputs ?
"inout " :
"inout ");
6269 bool emitWireInPorts = state.options.emitWireInPorts;
6270 if (emitWireInPorts)
6274 if (!portTypeStrings[portIdx].
empty())
6275 ps << portTypeStrings[portIdx];
6276 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6277 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6279 size_t startOfNamePos =
6280 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6286 ps.invokeWithStringOS(
6287 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6290 auto innerSym = portInfo.
at(portIdx).
getSym();
6291 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6293 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6298 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6302 if (
auto loc = portInfo.
at(portIdx).
loc)
6303 emitLocationInfo(loc);
6313 if (!state.options.disallowPortDeclSharing) {
6314 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6317 auto port = portInfo.
at(portIdx);
6321 bool isZeroWidth =
false;
6326 ps << (isZeroWidth ?
"// " :
" ");
6329 ps.nbsp(startOfNamePos);
6332 StringRef name = port.getVerilogName();
6336 ps.invokeWithStringOS(
6337 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6340 auto sym = port.getSym();
6341 if (state.options.printDebugInfo && sym && !sym.empty())
6342 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6346 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6350 if (
auto loc = port.loc)
6351 emitLocationInfo(loc);
6362 if (!portInfo.
size()) {
6364 SmallPtrSet<Operation *, 8> moduleOpSet;
6365 moduleOpSet.insert(module);
6366 emitLocationInfoAndNewLine(moduleOpSet);
6369 ps <<
");" << PP::newline;
6370 setPendingNewline();
6374 void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6375 currentModuleOp = module;
6377 emitComment(module.getCommentAttr());
6378 emitSVAttributes(module);
6380 ps.addCallback({module,
true});
6384 emitParameters(module, module.getParameters());
6388 assert(state.pendingNewline);
6391 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6394 ps.addCallback({module,
false});
6396 setPendingNewline();
6398 currentModuleOp =
nullptr;
6401 void ModuleEmitter::emitFunc(FuncOp func) {
6403 if (func.isDeclaration())
6406 currentModuleOp = func;
6408 ps.addCallback({func,
true});
6412 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6414 ps <<
"endfunction";
6416 currentModuleOp =
nullptr;
6425 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6432 void emit(emit::FileListOp op);
6435 void emit(Block *block);
6437 void emitOp(emit::RefOp op);
6438 void emitOp(emit::VerbatimOp op);
6442 for (Operation &op : *block) {
6443 TypeSwitch<Operation *>(&op)
6444 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) { emitOp(op); })
6445 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6446 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6447 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6448 .Case<BindInterfaceOp>(
6449 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6450 .Case<TypeScopeOp>([&](
auto typedecls) {
6451 ModuleEmitter(state).emitStatement(typedecls);
6454 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6460 for (
auto sym : op.getFiles()) {
6461 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6463 auto it = state.fileMapping.find(fileName);
6464 if (it == state.fileMapping.end()) {
6465 emitOpError(op,
" references an invalid file: ") << sym;
6469 auto file = cast<emit::FileOp>(it->second);
6470 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6477 StringAttr target = op.getTargetAttr().getAttr();
6478 auto *targetOp = state.symbolCache.getDefinition(target);
6479 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6481 TypeSwitch<Operation *>(targetOp)
6482 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6483 .Case<hw::HWModuleOp>(
6484 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6485 .Case<TypeScopeOp>([&](
auto typedecls) {
6486 ModuleEmitter(state).emitStatement(typedecls);
6489 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6495 SmallPtrSet<Operation *, 8> ops;
6500 StringRef text = op.getText();
6504 const auto &[lhs, rhs] = text.split(
'\n');
6508 ps << PP::end << PP::newline << PP::neverbox;
6510 }
while (!text.empty());
6513 emitLocationInfoAndNewLine(ops);
6524 void SharedEmitterState::gatherFiles(
bool separateModules) {
6531 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6532 moduleOp->walk([&](Operation *op) {
6534 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6537 SymbolTable::getSymbolAttrName()),
6538 name.getSymName(), op);
6539 if (isa<BindOp>(op))
6545 auto collectPorts = [&](
auto moduleOp) {
6546 auto portInfo = moduleOp.getPortList();
6547 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6548 if (!p.attrs || p.attrs.empty())
6550 for (NamedAttribute portAttr : p.attrs) {
6551 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6560 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6561 for (
auto file :
designOp.getOps<emit::FileOp>())
6562 for (
auto refs : file.getOps<emit::RefOp>())
6563 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6565 SmallString<32> outputPath;
6566 for (
auto &op : *
designOp.getBody()) {
6569 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6571 bool hasFileName =
false;
6572 bool emitReplicatedOps = !isFileOp;
6573 bool addToFilelist = !isFileOp;
6579 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6581 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6582 <<
" on " << op <<
"\n";);
6583 if (!attr.isDirectory())
6586 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6587 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6590 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6595 if (!defaultFileName.isTriviallyEmpty()) {
6598 op->emitError(
"file name unspecified");
6605 auto &file =
files[destFile];
6606 file.ops.push_back(info);
6607 file.emitReplicatedOps = emitReplicatedOps;
6608 file.addToFilelist = addToFilelist;
6609 file.isVerilog = outputPath.ends_with(
".sv");
6614 TypeSwitch<Operation *>(&op)
6615 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6617 fileMapping.try_emplace(file.getSymNameAttr(), file);
6618 separateFile(file, file.getFileName());
6620 .Case<emit::FragmentOp>([&](
auto fragment) {
6623 .Case<HWModuleOp>([&](
auto mod) {
6625 auto sym = mod.getNameAttr();
6628 collectInstanceSymbolsAndBinds(mod);
6630 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6631 if (it->second.size() != 1 || attr) {
6634 op.emitError(
"modules can be emitted to a single file");
6642 if (attr || separateModules)
6648 .Case<InterfaceOp>([&](InterfaceOp intf) {
6653 for (
auto &op : *intf.getBodyBlock())
6654 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6655 if (
auto name = symOp.getNameAttr())
6659 if (attr || separateModules)
6660 separateFile(intf, intf.getSymName() +
".sv");
6668 if (separateModules)
6669 separateFile(op,
"extern_modules.sv");
6673 .Case<VerbatimOp, IfDefOp, MacroDefOp, FuncDPIImportOp>(
6674 [&](Operation *op) {
6680 separateFile(op,
"");
6682 .Case<FuncOp>([&](
auto op) {
6688 separateFile(op,
"");
6692 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6695 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6704 separateFile(op,
"");
6706 .Case<BindOp>([&](
auto op) {
6708 separateFile(op,
"bindfile.sv");
6713 .Case<MacroDeclOp>([&](
auto op) {
6716 .Case<sv::ReserveNamesOp>([](
auto op) {
6719 .Case<om::ClassLike>([&](
auto op) {
6722 .Case<om::ConstantOp>([&](
auto op) {
6725 .Default([&](
auto *) {
6726 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6746 size_t lastReplicatedOp = 0;
6748 bool emitHeaderInclude =
6751 if (emitHeaderInclude)
6754 size_t numReplicatedOps =
6759 DenseSet<emit::FragmentOp> includedFragments;
6760 for (
const auto &opInfo : file.
ops) {
6761 Operation *op = opInfo.op;
6765 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6771 if (
auto fragments =
6773 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6777 op->emitError(
"cannot find referenced fragment ") << sym;
6780 emit::FragmentOp fragment = it->second;
6781 if (includedFragments.insert(fragment).second) {
6782 thingsToEmit.emplace_back(it->second);
6788 thingsToEmit.emplace_back(op);
6793 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6798 TypeSwitch<Operation *>(op)
6799 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6800 .Case<HWModuleExternOp>(
6801 [&](
auto op) { ModuleEmitter(state).emitHWExternModule(op); })
6802 .Case<HWModuleGeneratedOp>(
6803 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6804 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6805 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6806 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6807 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6808 .Case<TypeScopeOp>([&](
auto typedecls) {
6809 ModuleEmitter(state).emitStatement(typedecls);
6811 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6812 [&](
auto op) { FileEmitter(state).emit(op); })
6813 .Case<MacroDefOp, FuncDPIImportOp>(
6814 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6815 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6816 .Default([&](
auto *op) {
6817 state.encounteredError =
true;
6818 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6825 llvm::formatted_raw_ostream &os,
6826 StringAttr fileName,
bool parallelize) {
6827 MLIRContext *context =
designOp->getContext();
6831 parallelize &= context->isMultithreadingEnabled();
6842 size_t lineOffset = 0;
6843 for (
auto &entry : thingsToEmit) {
6844 entry.verilogLocs.setStream(os);
6845 if (
auto *op = entry.getOperation()) {
6850 state.addVerilogLocToOps(lineOffset, fileName);
6852 os << entry.getStringData();
6857 if (state.encounteredError)
6875 SmallString<256> buffer;
6876 llvm::raw_svector_ostream tmpStream(buffer);
6877 llvm::formatted_raw_ostream rs(tmpStream);
6888 for (
auto &entry : thingsToEmit) {
6891 auto *op = entry.getOperation();
6893 auto lineOffset = os.getLine() + 1;
6894 os << entry.getStringData();
6898 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
6901 entry.verilogLocs.setStream(os);
6908 state.addVerilogLocToOps(0, fileName);
6924 module.emitWarning()
6925 <<
"`emitReplicatedOpsToHeader` option is enabled but an header is "
6926 "created only at SplitExportVerilog";
6935 for (
const auto &it : emitter.
files) {
6936 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
6937 "\" ----- 8< -----\n\n");
6943 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
6944 "\" ----- 8< -----\n\n");
6945 for (
auto &name : it.second)
6946 contents += name.str() +
"\n";
6947 list.emplace_back(contents);
6950 llvm::formatted_raw_ostream rs(os);
6963 SmallVector<HWEmittableModuleLike> modulesToPrepare;
6965 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
6966 if (failed(failableParallelForEach(
6967 module->getContext(), modulesToPrepare,
6968 [&](
auto op) { return prepareHWModule(op, options); })))
6975 struct ExportVerilogPass
6976 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
6977 ExportVerilogPass(raw_ostream &os) : os(os) {}
6978 void runOnOperation()
override {
6980 mlir::OpPassManager preparePM(
"builtin.module");
6983 auto &modulePM = preparePM.nestAny();
6985 if (failed(runPipeline(preparePM, getOperation())))
6986 return signalPassFailure();
6989 return signalPassFailure();
6996 struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
6997 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
6998 : ExportVerilogPass{*os} {
6999 owned = std::move(os);
7003 std::unique_ptr<llvm::raw_ostream> owned;
7007 std::unique_ptr<mlir::Pass>
7009 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7012 std::unique_ptr<mlir::Pass>
7014 return std::make_unique<ExportVerilogPass>(os);
7025 static std::unique_ptr<llvm::ToolOutputFile>
7029 SmallString<128> outputFilename(dirname);
7031 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7034 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7036 emitter.
designOp.emitError(
"cannot create output directory \"")
7037 << outputDir <<
"\": " << error.message();
7043 std::string errorMessage;
7044 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7046 emitter.
designOp.emitError(errorMessage);
7063 llvm::formatted_raw_ostream rs(output->os());
7075 StringRef dirname) {
7086 bool insertSuccess =
7094 if (!insertSuccess) {
7095 module.emitError() <<
"tried to emit a heder to " <<
circtHeader
7096 <<
", but the file is used as an output too.";
7102 parallelForEach(module->getContext(), emitter.
files.begin(),
7103 emitter.
files.end(), [&](
auto &it) {
7104 createSplitOutputFile(it.first, it.second, dirname,
7109 SmallString<128> filelistPath(dirname);
7112 std::string errorMessage;
7113 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7115 module->emitError(errorMessage);
7119 for (
const auto &it : emitter.
files) {
7120 if (it.second.addToFilelist)
7121 output->os() << it.first.str() <<
"\n";
7130 for (
auto &name : it.second)
7131 output->os() << name.str() <<
"\n";
7142 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7144 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7145 if (failed(failableParallelForEach(
7146 module->getContext(), modulesToPrepare,
7147 [&](
auto op) { return prepareHWModule(op, options); })))
7155 struct ExportSplitVerilogPass
7156 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7157 ExportSplitVerilogPass(StringRef directory) {
7158 directoryName = directory.str();
7160 void runOnOperation()
override {
7162 mlir::OpPassManager preparePM(
"builtin.module");
7167 if (failed(runPipeline(preparePM, getOperation())))
7168 return signalPassFailure();
7171 return signalPassFailure();
7176 std::unique_ptr<mlir::Pass>
7178 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 void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static 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 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 StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static 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.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
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
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
esi::cosim::RpcServer::Impl Impl
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.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
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.
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 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)
PortDirectionRange getOutputs()
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.