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"
68using namespace ExportVerilog;
70using namespace pretty;
72#define DEBUG_TYPE "export-verilog"
80enum VerilogPrecedence {
101enum 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);
126static 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(
"");
221template <
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))
255 if (isa<verif::ContractOp>(op))
265static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
267 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
268 if (integer.getWidth() != 1)
269 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
272 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
273 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
278 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
279 dims.push_back(intType.getWidth());
283 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
284 return getTypeDims(dims, inout.getElementType(), loc);
285 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
286 return getTypeDims(dims, uarray.getElementType(), loc);
287 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
288 return getTypeDims(dims, uarray.getElementType(), loc);
290 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
293 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
299 SmallVector<Attribute, 4> aDims;
302 SmallVector<Attribute, 4> bDims;
305 return aDims == bDims;
311 if (
auto intType = dyn_cast<IntegerType>(type))
312 return intType.getWidth() == 0;
313 if (
auto inout = dyn_cast<hw::InOutType>(type))
315 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
316 return uarray.getNumElements() == 0 ||
318 if (
auto array = dyn_cast<hw::ArrayType>(type))
319 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
320 if (
auto structType = dyn_cast<hw::StructType>(type))
321 return llvm::all_of(structType.getElements(),
322 [](
auto elem) { return isZeroBitType(elem.type); });
323 if (
auto enumType = dyn_cast<hw::EnumType>(type))
324 return enumType.getFields().empty();
325 if (
auto unionType = dyn_cast<hw::UnionType>(type))
326 return hw::getBitWidth(unionType) == 0;
338 return TypeSwitch<Type, Type>(type)
339 .Case<InOutType>([](InOutType inoutType) {
342 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
345 .Default([](Type type) {
return type; });
350 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
351 auto elementType = cast<hw::InOutType>(type).getElementType();
357 return TypeSwitch<Type, bool>(type)
358 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
361 .Case<StructType>([](
auto) {
return true; })
362 .Default([](
auto) {
return false; });
376 if (
auto name = lhs.getName().compare(rhs.getName()))
378 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
383 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
385 if (lhs.getLine() != rhs.getLine())
386 return lhs.getLine() < rhs.getLine() ? -1 : 1;
387 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
392 Location lhsCallee = lhs.getCallee();
393 Location rhsCallee = rhs.getCallee();
397 Location lhsCaller = lhs.getCaller();
398 Location rhsCaller = rhs.getCaller();
402template <
typename TTargetLoc>
404 auto lhsT = dyn_cast<TTargetLoc>(lhs);
405 auto rhsT = dyn_cast<TTargetLoc>(rhs);
432 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
437 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
442 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
459 SmallPtrSetImpl<Attribute> &locationSet) {
460 llvm::TypeSwitch<Location, void>(loc)
461 .Case<FusedLoc>([&](
auto fusedLoc) {
462 for (
auto subLoc : fusedLoc.getLocations())
465 .Default([&](
auto loc) { locationSet.insert(loc); });
469template <
typename TVector>
471 llvm::array_pod_sort(
472 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
473 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
481 SmallPtrSet<Attribute, 8> locationSet;
482 locationSet.insert(loc);
483 llvm::raw_string_ostream os(
output);
489 const SmallPtrSetImpl<Operation *> &ops) {
493 SmallPtrSet<Attribute, 8> locationSet;
496 llvm::raw_string_ostream os(
output);
505 const SmallPtrSetImpl<Attribute> &locationSet) {
506 if (style == LoweringOptions::LocationInfoStyle::None)
509 llvm::raw_string_ostream sstr(resstr);
511 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
515 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
516 "other styles must be already handled");
517 os <<
"@[" << resstr <<
"]";
526 const SmallPtrSetImpl<Attribute> &locationSet)
542 bool withName = !loc.getName().empty();
544 os <<
"'" << loc.getName().strref() <<
"'(";
553 os << loc.getFilename().getValue();
554 if (
auto line = loc.getLine()) {
556 if (
auto col = loc.getColumn())
568 StringRef lastFileName;
569 for (
size_t i = 0, e = locVector.size(); i != e;) {
574 auto first = locVector[i];
575 if (first.getFilename() != lastFileName) {
576 lastFileName = first.getFilename();
583 first.getFilename() == locVector[
end].getFilename() &&
584 first.getLine() == locVector[
end].getLine())
589 if (
auto line = first.getLine()) {
591 if (
auto col = first.getColumn())
599 os <<
':' << first.getLine() <<
":{";
601 os << locVector[i++].getColumn();
613 llvm::TypeSwitch<Location, void>(loc)
614 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
616 .Case<mlir::FusedLoc>([&](
auto loc) {
617 SmallPtrSet<Attribute, 8> locationSet;
621 .Default([&](
auto loc) {
633 switch (locationSet.size()) {
644 SmallVector<FileLineColLoc, 8> flcLocs;
645 SmallVector<Attribute, 8> otherLocs;
646 flcLocs.reserve(locationSet.size());
647 otherLocs.reserve(locationSet.size());
648 for (Attribute loc : locationSet) {
649 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
650 flcLocs.push_back(flcLoc);
652 otherLocs.push_back(loc);
663 size_t sstrSize =
os.tell();
664 bool emittedAnything =
false;
665 auto recheckEmittedSomething = [&]() {
666 size_t currSize =
os.tell();
667 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
668 emittedAnything |= emittedSomethingSinceLastCheck;
670 return emittedSomethingSinceLastCheck;
679 if (recheckEmittedSomething()) {
681 recheckEmittedSomething();
687 if (emittedAnything && !flcLocs.empty())
692 llvm::raw_string_ostream &
os;
704 if (isa<BlockArgument>(v))
713 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
718 if (v.getDefiningOp<ReadInterfaceSignalOp>())
731 if (
auto cast = dyn_cast<BitcastOp>(op))
732 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
736 if (op->hasOneUse() &&
737 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
745 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp>(op))
750 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
754 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
755 if (verbatim.getFormatString().size() > 32)
760 for (
auto &use : op->getUses()) {
761 auto *user = use.getOwner();
771 UnionExtractOp, IndexedPartSelectOp>(user))
772 if (use.getOperandNumber() == 0 &&
783 auto usedInExprControl = [user, &use]() {
784 return TypeSwitch<Operation *, bool>(user)
785 .Case<ltl::ClockOp>([&](
auto clockOp) {
787 return clockOp.getClock() == use.get();
789 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
790 sv::CoverConcurrentOp>(
791 [&](
auto op) {
return op.getClock() == use.get(); })
792 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
793 sv::CoverPropertyOp>([&](
auto op) {
794 return op.getDisable() == use.get() || op.getClock() == use.get();
796 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
801 .Default([](
auto) {
return false; });
804 if (!usedInExprControl())
808 auto read = dyn_cast<ReadInOutOp>(op);
811 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
822 unsigned numStatements = 0;
823 block.walk([&](Operation *op) {
825 return WalkResult::advance();
827 TypeSwitch<Operation *, unsigned>(op)
828 .Case<VerbatimOp>([&](
auto) {
834 .Case<IfOp>([&](
auto) {
845 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
846 .Case<OutputOp>([&](OutputOp oop) {
849 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
850 Operation *op = operand.getDefiningOp();
851 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
854 .Default([](
auto) {
return 1; });
855 if (numStatements > 1)
856 return WalkResult::interrupt();
857 return WalkResult::advance();
859 if (numStatements == 0)
861 if (numStatements == 1)
871 if (op->getResult(0).use_empty())
876 if (op->hasOneUse() &&
877 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
878 *op->getUsers().begin()))
900 for (
auto &op : *elseBlock) {
901 if (
auto opIf = dyn_cast<IfOp>(op)) {
918template <
typename PPS>
920 enum Container { NoContainer, InComment, InAttr };
921 Container currentContainer = NoContainer;
923 auto closeContainer = [&] {
924 if (currentContainer == NoContainer)
926 if (currentContainer == InComment)
928 else if (currentContainer == InAttr)
930 ps << PP::end << PP::end;
932 currentContainer = NoContainer;
935 bool isFirstContainer =
true;
936 auto openContainer = [&](Container newContainer) {
937 assert(newContainer != NoContainer);
938 if (currentContainer == newContainer)
942 if (!isFirstContainer)
943 ps << (mayBreak ? PP::space : PP::nbsp);
944 isFirstContainer =
false;
947 if (newContainer == InComment)
949 else if (newContainer == InAttr)
951 currentContainer = newContainer;
959 ps.scopedBox(PP::cbox0, [&]() {
960 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
961 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
963 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
965 if (attr.getExpression())
966 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
975 if (
auto *op = val.getDefiningOp())
978 if (
auto port = dyn_cast<BlockArgument>(val)) {
980 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
981 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
983 port.getArgNumber());
985 assert(
false &&
"unhandled value");
997class VerilogEmitterState {
999 explicit VerilogEmitterState(ModuleOp designOp,
1005 llvm::formatted_raw_ostream &os,
1006 StringAttr fileName,
OpLocMap &verilogLocMap)
1007 : designOp(designOp), shared(shared), options(options),
1008 symbolCache(symbolCache), globalNames(globalNames),
1009 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1010 pp(os, options.emittedLineLength), fileName(fileName) {
1011 pp.setListener(&saver);
1034 llvm::formatted_raw_ostream &os;
1036 bool encounteredError =
false;
1045 bool pendingNewline =
false;
1059 StringAttr fileName;
1065 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1068 verilogLocMap.
clear();
1072 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1073 void operator=(
const VerilogEmitterState &) =
delete;
1086using CallbackDataTy = std::pair<Operation *, bool>;
1090 VerilogEmitterState &state;
1095 explicit EmitterBase(VerilogEmitterState &state)
1097 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1099 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1100 state.encounteredError =
true;
1101 return op->emitError(message);
1104 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1105 state.encounteredError =
true;
1106 return op->emitOpError(message);
1109 void emitLocationImpl(llvm::StringRef location) {
1112 ps << PP::neverbreak;
1113 if (!location.empty())
1114 ps <<
"\t// " << location;
1117 void emitLocationInfo(Location loc) {
1125 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1128 setPendingNewline();
1131 template <
typename PPS>
1132 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1133 llvm::function_ref<
void(Value)> operandEmitter,
1134 ArrayAttr symAttrs);
1140 void emitComment(StringAttr comment);
1144 void emitPendingNewlineIfNeeded() {
1145 if (state.pendingNewline) {
1146 state.pendingNewline =
false;
1150 void setPendingNewline() {
1151 assert(!state.pendingNewline);
1152 state.pendingNewline =
true;
1155 void startStatement() { emitPendingNewlineIfNeeded(); }
1158 void operator=(
const EmitterBase &) =
delete;
1159 EmitterBase(
const EmitterBase &) =
delete;
1163template <
typename PPS>
1164void EmitterBase::emitTextWithSubstitutions(
1165 PPS &ps, StringRef
string, Operation *op,
1166 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1177 if (
auto *itemOp = item.getOp()) {
1178 if (item.hasPort()) {
1182 if (!symOpName.empty())
1184 emitError(itemOp,
"cannot get name for symbol ") << sym;
1186 emitError(op,
"cannot get name for symbol ") << sym;
1188 return StringRef(
"<INVALID>");
1194 unsigned numSymOps = symAttrs.size();
1195 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1198 next =
string.find(
"{{", next);
1199 if (next == StringRef::npos)
1206 while (next <
string.size() &&
isdigit(
string[next]))
1209 if (start == next) {
1213 size_t operandNoLength = next - start;
1216 StringRef fmtOptsStr;
1217 if (
string[next] ==
':') {
1218 size_t startFmtOpts = next + 1;
1219 while (next <
string.size() &&
string[next] !=
'}')
1221 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1225 if (!
string.substr(next).starts_with(
"}}"))
1229 unsigned operandNo = 0;
1230 if (
string.drop_front(start)
1231 .take_front(operandNoLength)
1232 .getAsInteger(10, operandNo)) {
1233 emitError(op,
"operand substitution too large");
1239 auto before =
string.take_front(start - 2);
1240 if (!before.empty())
1245 if (operandNo < op->getNumOperands())
1247 operandEmitter(op->getOperand(operandNo));
1248 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1249 unsigned symOpNum = operandNo - op->getNumOperands();
1250 auto sym = symAttrs[symOpNum];
1251 StringRef symVerilogName;
1252 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1253 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1254 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1255 auto namepath = globalRef.getNamepathAttr().getValue();
1256 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1259 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1261 auto innerRef = cast<InnerRefAttr>(sym);
1262 auto ref = state.symbolCache.getInnerDefinition(
1263 innerRef.getModule(), innerRef.getName());
1264 ps << namify(innerRef, ref);
1267 symVerilogName = namify(sym, symOp);
1270 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1271 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1273 symVerilogName = namify(sym, symOp);
1275 if (!symVerilogName.empty())
1278 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1282 string =
string.drop_front(next);
1288 while (emitUntilSubstitution())
1292 if (!
string.
empty())
1296void EmitterBase::emitComment(StringAttr comment) {
1303 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1307 auto ref = comment.getValue();
1309 while (!ref.empty()) {
1310 std::tie(line, ref) = ref.split(
"\n");
1317 if (line.size() <= lineLength) {
1319 setPendingNewline();
1330 auto breakPos = line.rfind(
' ', lineLength);
1332 if (breakPos == StringRef::npos) {
1333 breakPos = line.find(
' ', lineLength);
1336 if (breakPos == StringRef::npos)
1337 breakPos = line.size();
1344 setPendingNewline();
1345 breakPos = line.find_first_not_of(
' ', breakPos);
1347 if (breakPos == StringRef::npos)
1350 line = line.drop_front(breakPos);
1360 bool addPrefixUnderScore =
true;
1363 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1367 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1369 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1371 result = StringAttr::get(expr.getContext(), name);
1373 }
else if (
auto *op = expr.getDefiningOp()) {
1375 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1377 result = StringAttr::get(expr.getContext(), name);
1379 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1385 addPrefixUnderScore =
false;
1387 TypeSwitch<Operation *>(op)
1390 .Case([&result](VerbatimExprOp verbatim) {
1391 verbatim.getAsmResultNames([&](Value, StringRef name) {
1392 result = StringAttr::get(verbatim.getContext(), name);
1395 .Case([&result](VerbatimExprSEOp verbatim) {
1396 verbatim.getAsmResultNames([&](Value, StringRef name) {
1397 result = StringAttr::get(verbatim.getContext(), name);
1403 if (
auto operandName =
1406 cast<IntegerType>(extract.getType()).getWidth();
1408 result = StringAttr::get(extract.getContext(),
1409 operandName.strref() +
"_" +
1410 Twine(extract.getLowBit()));
1412 result = StringAttr::get(
1413 extract.getContext(),
1414 operandName.strref() +
"_" +
1415 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1416 Twine(extract.getLowBit()));
1424 if (!result || result.strref().empty())
1428 if (addPrefixUnderScore && result.strref().front() !=
'_')
1429 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1441class ModuleEmitter :
public EmitterBase {
1443 explicit ModuleEmitter(VerilogEmitterState &state)
1444 : EmitterBase(state), currentModuleOp(nullptr),
1448 emitPendingNewlineIfNeeded();
1452 void emitParameters(Operation *module, ArrayAttr params);
1453 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1454 bool emitAsTwoStateType =
false);
1457 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1458 void emitFunc(FuncOp);
1461 void emitStatement(Operation *op);
1462 void emitBind(BindOp op);
1463 void emitBindInterface(BindInterfaceOp op);
1465 void emitSVAttributes(Operation *op);
1468 StringRef getVerilogStructFieldName(StringAttr field) {
1469 return fieldNameResolver.getRenamedFieldName(field).getValue();
1476 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1488 bool printPackedType(Type type, raw_ostream &os, Location loc,
1489 Type optionalAliasType = {},
bool implicitIntType =
true,
1490 bool singleBitDefaultType =
true,
1491 bool emitAsTwoStateType =
false);
1495 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1503 function_ref<InFlightDiagnostic()> emitError);
1506 VerilogPrecedence parenthesizeIfLooserThan,
1507 function_ref<InFlightDiagnostic()> emitError);
1513 Operation *currentModuleOp;
1519 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1525 SmallPtrSet<Operation *, 16> assignsInlined;
1534 const ModuleEmitter &emitter) {
1535 if (isa<RegOp>(op)) {
1540 cast<InOutType>(op->getResult(0).getType()).getElementType();
1547 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1548 while (isa<ArrayType>(innerType.getElementType()))
1549 innerType = cast<ArrayType>(innerType.getElementType());
1550 if (isa<StructType>(innerType.getElementType()) ||
1551 isa<TypeAliasType>(innerType.getElementType()))
1559 if (isa<sv::WireOp>(op))
1561 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1562 return "localparam";
1565 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1566 return interface.getInterfaceType().getInterface().getValue();
1570 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1574 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1576 if (isa<LogicOp>(op)) {
1582 if (isProcedural && !stripAutomatic)
1583 return hasStruct ?
"automatic" :
"automatic logic";
1584 return hasStruct ?
"" :
"logic";
1591 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1594 assert(!emitter.state.options.disallowLocalVariables &&
1595 "automatic variables not allowed");
1599 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1600 :
"automatic logic";
1607static void emitDim(Attribute width, raw_ostream &os, Location loc,
1608 ModuleEmitter &emitter,
bool downTo) {
1610 os <<
"<<invalid type>>";
1613 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1614 if (intAttr.getValue().isZero()) {
1615 os <<
"/*Zero Width*/";
1620 os << (intAttr.getValue().getZExtValue() - 1);
1630 auto typedAttr = dyn_cast<TypedAttr>(width);
1632 mlir::emitError(loc,
"untyped dimension attribute ") << width;
1636 getIntAttr(loc.getContext(), typedAttr.getType(),
1637 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1638 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1642 emitter.printParamValue(width, os, [loc]() {
1643 return mlir::emitError(loc,
"invalid parameter in type");
1651static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1652 ModuleEmitter &emitter) {
1653 for (Attribute width : dims) {
1654 emitDim(width, os, loc, emitter,
true);
1659void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1660 SmallVector<Attribute, 4> dims;
1692 SmallVectorImpl<Attribute> &dims,
1693 bool implicitIntType,
bool singleBitDefaultType,
1694 ModuleEmitter &emitter,
1695 Type optionalAliasType = {},
1696 bool emitAsTwoStateType =
false) {
1697 return TypeSwitch<Type, bool>(type)
1698 .Case<IntegerType>([&](IntegerType integerType) {
1699 if (emitAsTwoStateType && dims.empty()) {
1701 if (!typeName.empty()) {
1706 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1708 getInt32Attr(type.getContext(), integerType.getWidth()));
1710 StringRef typeName =
1711 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1712 if (!typeName.empty()) {
1719 return !dims.empty() || !implicitIntType;
1721 .Case<IntType>([&](IntType intType) {
1722 if (!implicitIntType)
1724 dims.push_back(intType.getWidth());
1728 .Case<ArrayType>([&](ArrayType arrayType) {
1729 dims.push_back(arrayType.getSizeAttr());
1731 implicitIntType, singleBitDefaultType,
1733 emitAsTwoStateType);
1735 .Case<InOutType>([&](InOutType inoutType) {
1737 implicitIntType, singleBitDefaultType,
1739 emitAsTwoStateType);
1741 .Case<EnumType>([&](EnumType enumType) {
1743 if (enumType.getBitWidth() != 32)
1744 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1746 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1747 llvm::interleaveComma(
1748 enumType.getFields().getAsRange<StringAttr>(), os,
1749 [&](
auto enumerator) {
1750 os << emitter.fieldNameResolver.getEnumFieldName(
1751 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1756 .Case<StructType>([&](StructType structType) {
1757 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1758 os <<
"/*Zero Width*/";
1761 os <<
"struct packed {";
1762 for (
auto &element : structType.getElements()) {
1764 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1765 <<
": Zero Width;*/ ";
1768 SmallVector<Attribute, 8> structDims;
1773 {}, emitAsTwoStateType);
1774 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1775 emitter.printUnpackedTypePostfix(element.type, os);
1782 .Case<UnionType>([&](UnionType unionType) {
1783 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1784 os <<
"/*Zero Width*/";
1788 int64_t unionWidth = hw::getBitWidth(unionType);
1789 os <<
"union packed {";
1790 for (
auto &element : unionType.getElements()) {
1792 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1793 <<
": Zero Width;*/ ";
1796 int64_t elementWidth = hw::getBitWidth(element.type);
1797 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1799 os <<
" struct packed {";
1800 if (element.offset) {
1801 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1802 << element.offset - 1 <<
":0] "
1803 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1807 SmallVector<Attribute, 8> structDims;
1811 true, emitter, {}, emitAsTwoStateType);
1812 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1813 emitter.printUnpackedTypePostfix(element.type, os);
1817 if (elementWidth + (int64_t)element.offset < unionWidth) {
1818 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1819 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1820 <<
"__post_padding_" << element.name.getValue() <<
";";
1822 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1831 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1832 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1833 os <<
"<<unexpected unpacked array>>";
1834 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1838 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1839 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1841 mlir::emitError(loc,
"unresolvable type reference");
1844 if (typedecl.getType() != typeRef.getInnerType()) {
1845 mlir::emitError(loc,
"declared type did not match aliased type");
1849 os << typedecl.getPreferredName();
1850 emitDims(dims, os, typedecl->getLoc(), emitter);
1853 .Default([&](Type type) {
1854 os <<
"<<invalid type '" << type <<
"'>>";
1855 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1871bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1872 Type optionalAliasType,
1873 bool implicitIntType,
1874 bool singleBitDefaultType,
1875 bool emitAsTwoStateType) {
1876 SmallVector<Attribute, 8> packedDimensions;
1878 singleBitDefaultType, *
this, optionalAliasType,
1879 emitAsTwoStateType);
1885void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1886 TypeSwitch<Type, void>(type)
1888 printUnpackedTypePostfix(inoutType.getElementType(), os);
1890 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1891 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1892 : state.designOp->getLoc();
1893 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1895 printUnpackedTypePostfix(arrayType.getElementType(), os);
1897 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1899 printUnpackedTypePostfix(arrayType.getElementType(), os);
1901 .Case<InterfaceType>([&](
auto) {
1915ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1916 function_ref<InFlightDiagnostic()> emitError) {
1917 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1925ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1926 VerilogPrecedence parenthesizeIfLooserThan,
1927 function_ref<InFlightDiagnostic()> emitError) {
1928 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1929 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1930 APInt value = intAttr.getValue();
1934 if (intTy.getWidth() > 32) {
1936 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1940 if (intTy.isSigned())
1941 os << intTy.getWidth() <<
"'sd";
1943 os << intTy.getWidth() <<
"'d";
1945 value.print(os, intTy.isSigned());
1946 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1948 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1950 os.write_escaped(strAttr.getValue());
1952 return {Symbol, IsUnsigned};
1954 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1956 os << fpAttr.getValueAsDouble();
1957 return {Symbol, IsUnsigned};
1959 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1960 os << verbatimParam.getValue().getValue();
1961 return {Symbol, IsUnsigned};
1963 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1965 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1966 parameterRef.getName());
1969 return {Symbol, IsUnsigned};
1973 auto expr = dyn_cast<ParamExprAttr>(value);
1975 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1976 emitError() <<
" = " << value;
1977 return {LowestPrecedence, IsUnsigned};
1980 StringRef operatorStr;
1981 StringRef openStr, closeStr;
1982 VerilogPrecedence subprecedence = LowestPrecedence;
1983 VerilogPrecedence prec;
1984 std::optional<SubExprSignResult> operandSign;
1985 bool isUnary =
false;
1986 bool hasOpenClose =
false;
1988 switch (expr.getOpcode()) {
1990 operatorStr =
" + ";
1991 subprecedence = Addition;
1994 operatorStr =
" * ";
1995 subprecedence = Multiply;
1998 operatorStr =
" & ";
1999 subprecedence = And;
2002 operatorStr =
" | ";
2006 operatorStr =
" ^ ";
2007 subprecedence = Xor;
2010 operatorStr =
" << ";
2011 subprecedence = Shift;
2015 operatorStr =
" >> ";
2016 subprecedence = Shift;
2020 operatorStr =
" >>> ";
2021 subprecedence = Shift;
2022 operandSign = IsSigned;
2025 operatorStr =
" / ";
2026 subprecedence = Multiply;
2027 operandSign = IsUnsigned;
2030 operatorStr =
" / ";
2031 subprecedence = Multiply;
2032 operandSign = IsSigned;
2035 operatorStr =
" % ";
2036 subprecedence = Multiply;
2037 operandSign = IsUnsigned;
2040 operatorStr =
" % ";
2041 subprecedence = Multiply;
2042 operandSign = IsSigned;
2045 openStr =
"$clog2(";
2047 operandSign = IsUnsigned;
2048 hasOpenClose =
true;
2051 case PEO::StrConcat:
2054 hasOpenClose =
true;
2057 subprecedence = LowestPrecedence;
2062 prec = subprecedence;
2065 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2067 assert(isUnary || hasOpenClose ||
2068 !llvm::hasSingleElement(expr.getOperands()));
2075 auto emitOperand = [&](Attribute operand) ->
bool {
2077 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2078 if (operandSign.has_value())
2079 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2082 if (operandSign.has_value()) {
2084 signedness = *operandSign;
2086 return signedness == IsSigned;
2090 if (prec > parenthesizeIfLooserThan)
2099 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2100 for (
auto op : expr.getOperands().drop_front()) {
2103 if (expr.getOpcode() == PEO::Add) {
2104 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2105 const APInt &value = integer.getValue();
2106 if (value.isNegative() && !value.isMinSignedValue()) {
2108 allOperandsSigned &=
2109 emitOperand(IntegerAttr::get(op.getType(), -value));
2116 allOperandsSigned &= emitOperand(op);
2120 if (prec > parenthesizeIfLooserThan) {
2124 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2139class ExprEmitter :
public EmitterBase,
2141 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2146 ExprEmitter(ModuleEmitter &emitter,
2147 SmallPtrSetImpl<Operation *> &emittedExprs)
2148 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2150 ExprEmitter(ModuleEmitter &emitter,
2151 SmallPtrSetImpl<Operation *> &emittedExprs,
2153 : EmitterBase(emitter.state), emitter(emitter),
2154 emittedExprs(emittedExprs), buffer(tokens),
2155 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2156 assert(state.pp.getListener() == &state.saver);
2163 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2164 bool isAssignmentLikeContext) {
2165 assert(localTokens.empty());
2167 ps.scopedBox(PP::ibox0, [&]() {
2168 emitSubExpr(exp, parenthesizeIfLooserThan,
2171 isAssignmentLikeContext);
2176 if (&buffer.tokens == &localTokens)
2177 buffer.flush(state.pp);
2182 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2183 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2185 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2193 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2194 SubExprSignRequirement signReq = NoRequirement,
2195 bool isSelfDeterminedUnsignedValue =
false,
2196 bool isAssignmentLikeContext =
false);
2200 void emitSVAttributes(Operation *op);
2202 SubExprInfo visitUnhandledExpr(Operation *op);
2203 SubExprInfo visitInvalidComb(Operation *op) {
2206 SubExprInfo visitUnhandledComb(Operation *op) {
2207 return visitUnhandledExpr(op);
2210 return dispatchSVVisitor(op);
2213 return visitUnhandledExpr(op);
2215 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2218 enum EmitBinaryFlags {
2219 EB_RequireSignedOperands = RequireSigned,
2220 EB_RequireUnsignedOperands = RequireUnsigned,
2221 EB_OperandSignRequirementMask = 0x3,
2226 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2230 EB_ForceResultSigned = 0x8,
2235 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2236 const char *syntax,
unsigned emitBinaryFlags = 0);
2238 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2239 bool resultAlwaysUnsigned =
false);
2242 void emitSubExprIBox2(
2243 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2244 ps.scopedBox(PP::ibox2,
2245 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2250 template <
typename Container,
typename EachFn>
2251 void interleaveComma(
const Container &c, EachFn eachFn) {
2252 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2257 void interleaveComma(ValueRange ops) {
2258 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2275 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2277 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2278 CloseFunc closeFn) {
2280 ps.scopedBox(PP::cbox0, [&]() {
2281 interleaveComma(c, eachFn);
2287 template <
typename OpenFunc,
typename CloseFunc>
2288 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2289 return emitBracedList(
2290 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2294 void emitBracedList(ValueRange ops) {
2295 return emitBracedList(
2296 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2300 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2303 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2304 bool printAsPattern, Operation *op);
2306 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2307 ArrayAttr fieldValues,
bool printAsPattern,
2310 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2312 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2313 SubExprInfo visitSV(GetModportOp op);
2314 SubExprInfo visitSV(SystemFunctionOp op);
2315 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2316 SubExprInfo visitSV(XMROp op);
2317 SubExprInfo visitSV(XMRRefOp op);
2318 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2319 SubExprInfo visitSV(VerbatimExprOp op) {
2320 return visitVerbatimExprOp(op, op.getSymbols());
2322 SubExprInfo visitSV(VerbatimExprSEOp op) {
2323 return visitVerbatimExprOp(op, op.getSymbols());
2325 SubExprInfo visitSV(MacroRefExprOp op);
2326 SubExprInfo visitSV(MacroRefExprSEOp op);
2327 template <
typename MacroTy>
2328 SubExprInfo emitMacroCall(MacroTy op);
2330 SubExprInfo visitSV(ConstantXOp op);
2331 SubExprInfo visitSV(ConstantZOp op);
2332 SubExprInfo visitSV(ConstantStrOp op);
2334 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2335 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2337 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2342 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2343 emitSVAttributes(op);
2346 SubExprInfo visitSV(ArrayIndexInOutOp op);
2347 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2348 SubExprInfo visitSV(IndexedPartSelectOp op);
2349 SubExprInfo visitSV(StructFieldInOutOp op);
2352 SubExprInfo visitSV(SampledOp op);
2355 using TypeOpVisitor::visitTypeOp;
2357 SubExprInfo visitTypeOp(AggregateConstantOp op);
2359 SubExprInfo visitTypeOp(ParamValueOp op);
2366 SubExprInfo visitTypeOp(StructInjectOp op);
2367 SubExprInfo visitTypeOp(UnionCreateOp op);
2368 SubExprInfo visitTypeOp(UnionExtractOp op);
2369 SubExprInfo visitTypeOp(EnumCmpOp op);
2370 SubExprInfo visitTypeOp(EnumConstantOp op);
2373 using CombinationalVisitor::visitComb;
2374 SubExprInfo visitComb(
MuxOp op);
2375 SubExprInfo visitComb(
AddOp op) {
2376 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2377 return emitBinary(op, Addition,
"+");
2379 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2380 SubExprInfo visitComb(
MulOp op) {
2381 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2382 return emitBinary(op, Multiply,
"*");
2384 SubExprInfo visitComb(
DivUOp op) {
2385 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2387 SubExprInfo visitComb(
DivSOp op) {
2388 return emitBinary(op, Multiply,
"/",
2389 EB_RequireSignedOperands | EB_ForceResultSigned);
2391 SubExprInfo visitComb(
ModUOp op) {
2392 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2394 SubExprInfo visitComb(
ModSOp op) {
2395 return emitBinary(op, Multiply,
"%",
2396 EB_RequireSignedOperands | EB_ForceResultSigned);
2398 SubExprInfo visitComb(
ShlOp op) {
2399 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2401 SubExprInfo visitComb(
ShrUOp op) {
2403 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2405 SubExprInfo visitComb(
ShrSOp op) {
2408 return emitBinary(op, Shift,
">>>",
2409 EB_RequireSignedOperands | EB_ForceResultSigned |
2410 EB_RHS_UnsignedWithSelfDeterminedWidth);
2412 SubExprInfo visitComb(
AndOp op) {
2413 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2414 return emitBinary(op, And,
"&");
2416 SubExprInfo visitComb(
OrOp op) {
2417 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2418 return emitBinary(op, Or,
"|");
2420 SubExprInfo visitComb(
XorOp op) {
2421 if (op.isBinaryNot())
2422 return emitUnary(op,
"~");
2423 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2424 return emitBinary(op, Xor,
"^");
2429 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2431 SubExprInfo visitComb(ReplicateOp op);
2432 SubExprInfo visitComb(
ConcatOp op);
2434 SubExprInfo visitComb(ICmpOp op);
2436 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2437 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2438 "not printed within an assignment-like context");
2439 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2440 "supposed to spill such expressions";
2444 SubExprInfo printStructCreate(
2445 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2447 bool printAsPattern, Operation *op);
2450 ModuleEmitter &emitter;
2457 SubExprSignRequirement signPreference = NoRequirement;
2461 SmallPtrSetImpl<Operation *> &emittedExprs;
2464 SmallVector<Token> localTokens;
2478 bool isAssignmentLikeContext =
false;
2482SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2484 unsigned emitBinaryFlags) {
2486 emitError(op,
"SV attributes emission is unimplemented for the op");
2497 if (emitBinaryFlags & EB_ForceResultSigned)
2498 ps <<
"$signed(" << PP::ibox0;
2499 auto operandSignReq =
2500 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2501 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2503 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2505 ps << lhsSpace << syntax << PP::nbsp;
2512 auto rhsPrec = prec;
2513 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2514 rhsPrec = VerilogPrecedence(prec - 1);
2519 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2520 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2521 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2522 operandSignReq = NoRequirement;
2525 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2526 rhsIsUnsignedValueWithSelfDeterminedWidth);
2530 SubExprSignResult signedness = IsUnsigned;
2531 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2532 signedness = IsSigned;
2534 if (emitBinaryFlags & EB_ForceResultSigned) {
2535 ps << PP::end <<
")";
2536 signedness = IsSigned;
2540 return {prec, signedness};
2543SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2544 bool resultAlwaysUnsigned) {
2546 emitError(op,
"SV attributes emission is unimplemented for the op");
2549 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2553 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2554 resultAlwaysUnsigned ? IsUnsigned : signedness};
2559void ExprEmitter::emitSVAttributes(Operation *op) {
2578 if (constant && constant.getValue().isZero())
2579 return concat.getOperand(1);
2589SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2590 VerilogPrecedence parenthesizeIfLooserThan,
2591 SubExprSignRequirement signRequirement,
2592 bool isSelfDeterminedUnsignedValue,
2593 bool isAssignmentLikeContext) {
2595 if (
auto result = dyn_cast<OpResult>(exp))
2596 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2597 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2598 parenthesizeIfLooserThan, signRequirement,
2599 isSelfDeterminedUnsignedValue,
2600 isAssignmentLikeContext);
2604 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2609 auto *op = exp.getDefiningOp();
2613 if (!shouldEmitInlineExpr) {
2616 if (signRequirement == RequireSigned) {
2618 return {Symbol, IsSigned};
2622 return {Symbol, IsUnsigned};
2625 unsigned subExprStartIndex = buffer.tokens.size();
2627 ps.addCallback({op,
true});
2628 auto done = llvm::make_scope_exit([&]() {
2630 ps.addCallback({op, false});
2636 signPreference = signRequirement;
2638 bool bitCastAdded =
false;
2639 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2641 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2642 ps.addAsString(inType.getWidth());
2643 ps <<
"'(" << PP::ibox0;
2644 bitCastAdded =
true;
2648 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2649 isAssignmentLikeContext);
2650 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2656 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2658 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2660 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2661 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2664 expInfo.signedness = IsSigned;
2665 expInfo.precedence = Selection;
2666 }
else if (signRequirement == RequireUnsigned &&
2667 expInfo.signedness == IsSigned) {
2670 expInfo.signedness = IsUnsigned;
2671 expInfo.precedence = Selection;
2672 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2679 expInfo.precedence = Selection;
2686 emittedExprs.insert(exp.getDefiningOp());
2690SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2691 auto openFn = [&]() {
2693 ps.addAsString(op.getMultiple());
2696 auto closeFn = [&]() { ps <<
"}}"; };
2700 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2701 if (op.getOperand().hasOneUse()) {
2702 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2703 return {Symbol, IsUnsigned};
2706 emitBracedList(op.getOperand(), openFn, closeFn);
2707 return {Symbol, IsUnsigned};
2710SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2711 emitBracedList(op.getOperands());
2712 return {Symbol, IsUnsigned};
2715SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2719 Type toType = op.getType();
2722 ps.invokeWithStringOS(
2723 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2726 return emitSubExpr(op.getInput(), LowestPrecedence);
2729SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2730 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2731 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2732 SubExprSignRequirement signop[] = {
2734 NoRequirement, NoRequirement,
2736 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2738 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2740 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2742 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2743 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2746 if (op.isEqualAllOnes())
2747 return emitUnary(op,
"&",
true);
2750 if (op.isNotEqualZero())
2751 return emitUnary(op,
"|",
true);
2753 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2757 result.signedness = IsUnsigned;
2761SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2763 emitError(op,
"SV attributes emission is unimplemented for the op");
2765 unsigned loBit = op.getLowBit();
2766 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2768 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2769 assert((x.precedence == Symbol ||
2771 "should be handled by isExpressionUnableToInline");
2776 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2780 ps.addAsString(hiBit);
2781 if (hiBit != loBit) {
2783 ps.addAsString(loBit);
2786 return {Unary, IsUnsigned};
2789SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2791 emitError(op,
"SV attributes emission is unimplemented for the op");
2793 auto decl = op.getReferencedDecl(state.symbolCache);
2796 return {Selection, IsUnsigned};
2799SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2801 emitError(op,
"SV attributes emission is unimplemented for the op");
2804 ps.scopedBox(PP::ibox0, [&]() {
2806 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2807 [&]() { ps <<
"," << PP::space; });
2810 return {Symbol, IsUnsigned};
2813SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2815 emitError(op,
"SV attributes emission is unimplemented for the op");
2817 auto decl = op.getReferencedDecl(state.symbolCache);
2821 return {Selection, IsUnsigned};
2824SubExprInfo ExprEmitter::visitSV(XMROp op) {
2826 emitError(op,
"SV attributes emission is unimplemented for the op");
2828 if (op.getIsRooted())
2830 for (
auto s : op.getPath())
2831 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2833 return {Selection, IsUnsigned};
2838SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2840 emitError(op,
"SV attributes emission is unimplemented for the op");
2843 auto globalRef = op.getReferencedPath(&state.symbolCache);
2844 auto namepath = globalRef.getNamepathAttr().getValue();
2845 auto *
module = state.symbolCache.getDefinition(
2846 cast<InnerRefAttr>(namepath.front()).getModule());
2848 for (
auto sym : namepath) {
2850 auto innerRef = cast<InnerRefAttr>(sym);
2851 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2852 innerRef.getName());
2853 if (ref.hasPort()) {
2859 auto leaf = op.getVerbatimSuffixAttr();
2860 if (leaf && leaf.size())
2862 return {Selection, IsUnsigned};
2865SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2867 emitError(op,
"SV attributes emission is unimplemented for the op");
2869 emitTextWithSubstitutions(
2870 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2871 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2873 return {Unary, IsUnsigned};
2876template <
typename MacroTy>
2877SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2879 emitError(op,
"SV attributes emission is unimplemented for the op");
2882 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2883 assert(macroOp &&
"Invalid IR");
2885 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2887 if (!op.getInputs().empty()) {
2889 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2890 emitExpression(val, LowestPrecedence, false);
2894 return {LowestPrecedence, IsUnsigned};
2897SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2898 return emitMacroCall(op);
2901SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2902 return emitMacroCall(op);
2905SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2907 emitError(op,
"SV attributes emission is unimplemented for the op");
2909 ps.addAsString(op.getWidth());
2911 return {Unary, IsUnsigned};
2914SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2916 emitError(op,
"SV attributes emission is unimplemented for the op");
2918 ps.writeQuotedEscaped(op.getStr());
2919 return {Symbol, IsUnsigned};
2922SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2924 emitError(op,
"SV attributes emission is unimplemented for the op");
2926 ps.addAsString(op.getWidth());
2928 return {Unary, IsUnsigned};
2931SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2932 bool isNegated =
false;
2935 if (signPreference == RequireSigned && value.isNegative() &&
2936 !value.isMinSignedValue()) {
2941 ps.addAsString(type.getWidth());
2945 if (signPreference == RequireSigned)
2951 SmallString<32> valueStr;
2953 (-value).toStringUnsigned(valueStr, 16);
2955 value.toStringUnsigned(valueStr, 16);
2958 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2961SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2963 emitError(op,
"SV attributes emission is unimplemented for the op");
2965 auto value = op.getValue();
2969 if (value.getBitWidth() == 0) {
2970 emitOpError(op,
"will not emit zero width constants in the general case");
2971 ps <<
"<<unsupported zero width constant: "
2972 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2973 return {Unary, IsUnsigned};
2976 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2979void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2980 bool printAsPattern, Operation *op) {
2981 if (printAsPattern && !isAssignmentLikeContext)
2982 emitAssignmentPatternContextError(op);
2983 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2986 elementValues, [&]() { ps << openDelim; },
2987 [&](Attribute elementValue) {
2988 printConstantAggregate(elementValue,
elementType, op);
2990 [&]() { ps <<
"}"; });
2993void ExprEmitter::printConstantStruct(
2994 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
2995 bool printAsPattern, Operation *op) {
2996 if (printAsPattern && !isAssignmentLikeContext)
2997 emitAssignmentPatternContextError(op);
3004 auto fieldRange = llvm::make_filter_range(
3005 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3010 if (printAsPattern) {
3012 fieldRange, [&]() { ps <<
"'{"; },
3013 [&](
const auto &fieldAndValue) {
3014 ps.scopedBox(PP::ibox2, [&]() {
3015 const auto &[field, value] = fieldAndValue;
3016 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3017 <<
":" << PP::space;
3018 printConstantAggregate(value, field.type, op);
3021 [&]() { ps <<
"}"; });
3024 fieldRange, [&]() { ps <<
"{"; },
3025 [&](
const auto &fieldAndValue) {
3026 ps.scopedBox(PP::ibox2, [&]() {
3027 const auto &[field, value] = fieldAndValue;
3028 printConstantAggregate(value, field.type, op);
3031 [&]() { ps <<
"}"; });
3035void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3038 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3039 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3040 isAssignmentLikeContext, op);
3043 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3044 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3048 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3049 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3050 isAssignmentLikeContext, op);
3052 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3053 auto value = cast<IntegerAttr>(attr).getValue();
3054 printConstantScalar(value, intType);
3058 emitOpError(op,
"contains constant of type ")
3059 << type <<
" which cannot be emitted as Verilog";
3062SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3064 emitError(op,
"SV attributes emission is unimplemented for the op");
3068 "zero-bit types not allowed at this point");
3070 printConstantAggregate(op.getFields(), op.getType(), op);
3071 return {Symbol, IsUnsigned};
3074SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3076 emitError(op,
"SV attributes emission is unimplemented for the op");
3078 return ps.invokeWithStringOS([&](
auto &os) {
3079 return emitter.printParamValue(op.getValue(), os, [&]() {
3080 return op->emitOpError(
"invalid parameter use");
3089 emitError(op,
"SV attributes emission is unimplemented for the op");
3091 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3093 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3095 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3097 ps.addAsString(dstWidth);
3099 return {Selection, arrayPrec.signedness};
3102SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3103 emitSubExpr(op.getInput(), Selection);
3108 emitSubExpr(op.getIndex(), LowestPrecedence);
3110 emitSVAttributes(op);
3111 return {Selection, IsUnsigned};
3117 emitError(op,
"SV attributes emission is unimplemented for the op");
3119 if (op.isUniform()) {
3121 ps.addAsString(op.getInputs().size());
3123 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3127 op.getInputs(), [&]() { ps <<
"{"; },
3130 emitSubExprIBox2(v);
3133 [&]() { ps <<
"}"; });
3135 return {Unary, IsUnsigned};
3138SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3140 emitError(op,
"SV attributes emission is unimplemented for the op");
3143 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3144 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3145 return {Unary, IsUnsigned};
3150 emitError(op,
"SV attributes emission is unimplemented for the op");
3152 emitBracedList(op.getOperands());
3153 return {Unary, IsUnsigned};
3156SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3158 emitError(op,
"SV attributes emission is unimplemented for the op");
3160 auto index = op.getIndex();
3161 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3166 emitSubExpr(index, LowestPrecedence);
3168 return {Selection, arrayPrec.signedness};
3171SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3173 emitError(op,
"SV attributes emission is unimplemented for the op");
3175 auto prec = emitSubExpr(op.getInput(), Selection);
3177 emitSubExpr(op.getBase(), LowestPrecedence);
3178 if (op.getDecrement())
3182 ps.addAsString(op.getWidth());
3184 return {Selection, prec.signedness};
3187SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3189 emitError(op,
"SV attributes emission is unimplemented for the op");
3191 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3193 emitSubExpr(op.getBase(), LowestPrecedence);
3194 if (op.getDecrement())
3198 ps.addAsString(op.getWidth());
3203SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3205 emitError(op,
"SV attributes emission is unimplemented for the op");
3207 auto prec = emitSubExpr(op.getInput(), Selection);
3209 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3210 return {Selection, prec.signedness};
3213SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3215 emitError(op,
"SV attributes emission is unimplemented for the op");
3218 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3223SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3237 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3238 ps.scopedBox(PP::ibox0, [&]() {
3239 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3243 emitSVAttributes(op);
3245 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3246 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3250 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3251 return emitSubExpr(op.getFalseValue(), Conditional);
3254 SubExprSignResult signedness = IsUnsigned;
3255 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3256 signedness = IsSigned;
3258 return {Conditional, signedness};
3262SubExprInfo ExprEmitter::printStructCreate(
3263 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3265 bool printAsPattern, Operation *op) {
3266 if (printAsPattern && !isAssignmentLikeContext)
3267 emitAssignmentPatternContextError(op);
3270 auto filteredFields = llvm::make_filter_range(
3271 llvm::enumerate(fieldInfos),
3272 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3274 if (printAsPattern) {
3276 filteredFields, [&]() { ps <<
"'{"; },
3277 [&](
const auto &field) {
3278 ps.scopedBox(PP::ibox2, [&]() {
3280 emitter.getVerilogStructFieldName(field.value().name))
3281 <<
":" << PP::space;
3282 fieldFn(field.value(), field.index());
3285 [&]() { ps <<
"}"; });
3288 filteredFields, [&]() { ps <<
"{"; },
3289 [&](
const auto &field) {
3290 ps.scopedBox(PP::ibox2,
3291 [&]() { fieldFn(field.value(), field.index()); });
3293 [&]() { ps <<
"}"; });
3296 return {Selection, IsUnsigned};
3301 emitError(op,
"SV attributes emission is unimplemented for the op");
3305 bool printAsPattern = isAssignmentLikeContext;
3306 StructType structType = op.getType();
3307 return printStructCreate(
3308 structType.getElements(),
3309 [&](
const auto &field,
auto index) {
3310 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3312 isAssignmentLikeContext);
3314 printAsPattern, op);
3319 emitError(op,
"SV attributes emission is unimplemented for the op");
3321 emitSubExpr(op.getInput(), Selection);
3323 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3324 return {Selection, IsUnsigned};
3327SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3329 emitError(op,
"SV attributes emission is unimplemented for the op");
3333 bool printAsPattern = isAssignmentLikeContext;
3334 StructType structType = op.getType();
3335 return printStructCreate(
3336 structType.getElements(),
3337 [&](
const auto &field,
auto index) {
3338 if (field.name == op.getFieldNameAttr()) {
3339 emitSubExpr(op.getNewValue(), Selection);
3341 emitSubExpr(op.getInput(), Selection);
3343 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3346 printAsPattern, op);
3349SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3350 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3351 return {Selection, IsUnsigned};
3354SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3356 emitError(op,
"SV attributes emission is unimplemented for the op");
3357 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3360 result.signedness = IsUnsigned;
3364SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3366 emitError(op,
"SV attributes emission is unimplemented for the op");
3370 auto unionWidth = hw::getBitWidth(unionType);
3371 auto &element = unionType.getElements()[op.getFieldIndex()];
3372 auto elementWidth = hw::getBitWidth(element.type);
3375 if (!elementWidth) {
3376 ps.addAsString(unionWidth);
3378 return {Unary, IsUnsigned};
3382 if (elementWidth == unionWidth) {
3383 emitSubExpr(op.getInput(), LowestPrecedence);
3384 return {Unary, IsUnsigned};
3389 ps.scopedBox(PP::ibox0, [&]() {
3390 if (
auto prePadding = element.offset) {
3391 ps.addAsString(prePadding);
3392 ps <<
"'h0," << PP::space;
3394 emitSubExpr(op.getInput(), Selection);
3395 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3396 ps <<
"," << PP::space;
3397 ps.addAsString(postPadding);
3403 return {Unary, IsUnsigned};
3406SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3408 emitError(op,
"SV attributes emission is unimplemented for the op");
3409 emitSubExpr(op.getInput(), Selection);
3412 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3413 auto unionWidth = hw::getBitWidth(unionType);
3414 auto &element = unionType.getElements()[op.getFieldIndex()];
3415 auto elementWidth = hw::getBitWidth(element.type);
3416 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3417 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3426 return {Selection, IsUnsigned};
3429SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3430 emitOpError(op,
"cannot emit this expression to Verilog");
3431 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3433 return {Symbol, IsUnsigned};
3449enum class PropertyPrecedence {
3469struct EmittedProperty {
3471 PropertyPrecedence precedence;
3476class PropertyEmitter :
public EmitterBase,
3477 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3481 PropertyEmitter(ModuleEmitter &emitter,
3482 SmallPtrSetImpl<Operation *> &emittedOps)
3483 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3484 PropertyEmitter(ModuleEmitter &emitter,
3485 SmallPtrSetImpl<Operation *> &emittedOps,
3487 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3489 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3490 assert(state.pp.getListener() == &state.saver);
3493 void emitAssertPropertyDisable(
3494 Value property, Value disable,
3495 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3497 void emitAssertPropertyBody(
3498 Value property, Value disable,
3499 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3501 void emitAssertPropertyBody(
3502 Value property, sv::EventControl event, Value clock, Value disable,
3503 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3508 emitNestedProperty(Value property,
3509 PropertyPrecedence parenthesizeIfLooserThan);
3510 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3511 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3513 EmittedProperty visitUnhandledLTL(Operation *op);
3514 EmittedProperty visitLTL(ltl::AndOp op);
3515 EmittedProperty visitLTL(ltl::OrOp op);
3516 EmittedProperty visitLTL(ltl::IntersectOp op);
3517 EmittedProperty visitLTL(ltl::DelayOp op);
3518 EmittedProperty visitLTL(ltl::ConcatOp op);
3519 EmittedProperty visitLTL(ltl::RepeatOp op);
3520 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3521 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3522 EmittedProperty visitLTL(ltl::NotOp op);
3523 EmittedProperty visitLTL(ltl::ImplicationOp op);
3524 EmittedProperty visitLTL(ltl::UntilOp op);
3525 EmittedProperty visitLTL(ltl::EventuallyOp op);
3526 EmittedProperty visitLTL(ltl::ClockOp op);
3528 void emitLTLConcat(ValueRange inputs);
3531 ModuleEmitter &emitter;
3536 SmallPtrSetImpl<Operation *> &emittedOps;
3539 SmallVector<Token> localTokens;
3552void PropertyEmitter::emitAssertPropertyDisable(
3553 Value property, Value disable,
3554 PropertyPrecedence parenthesizeIfLooserThan) {
3557 ps <<
"disable iff" << PP::nbsp <<
"(";
3559 emitNestedProperty(disable, PropertyPrecedence::Unary);
3565 ps.scopedBox(PP::ibox0,
3566 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3572void PropertyEmitter::emitAssertPropertyBody(
3573 Value property, Value disable,
3574 PropertyPrecedence parenthesizeIfLooserThan) {
3575 assert(localTokens.empty());
3577 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3582 if (&buffer.tokens == &localTokens)
3583 buffer.flush(state.pp);
3586void PropertyEmitter::emitAssertPropertyBody(
3587 Value property, sv::EventControl event, Value clock, Value disable,
3588 PropertyPrecedence parenthesizeIfLooserThan) {
3589 assert(localTokens.empty());
3592 ps.scopedBox(PP::ibox2, [&] {
3593 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3594 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3600 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3605 if (&buffer.tokens == &localTokens)
3606 buffer.flush(state.pp);
3609EmittedProperty PropertyEmitter::emitNestedProperty(
3610 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3620 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3621 ExprEmitter(emitter, emittedOps, buffer.tokens)
3622 .emitExpression(property, LowestPrecedence,
3624 return {PropertyPrecedence::Symbol};
3627 unsigned startIndex = buffer.tokens.size();
3628 auto info = dispatchLTLVisitor(property.getDefiningOp());
3633 if (info.precedence > parenthesizeIfLooserThan) {
3635 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3636 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3638 ps << PP::end <<
")";
3640 info.precedence = PropertyPrecedence::Symbol;
3644 emittedOps.insert(property.getDefiningOp());
3648EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3649 emitOpError(op,
"emission as Verilog property or sequence not supported");
3650 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3651 return {PropertyPrecedence::Symbol};
3654EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3657 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3658 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3659 return {PropertyPrecedence::And};
3662EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3665 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3666 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3667 return {PropertyPrecedence::Or};
3670EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3674 emitNestedProperty(input, PropertyPrecedence::Intersect);
3676 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3677 return {PropertyPrecedence::Intersect};
3680EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3682 if (
auto length = op.getLength()) {
3684 ps.addAsString(op.getDelay());
3687 ps.addAsString(op.getDelay());
3689 ps.addAsString(op.getDelay() + *length);
3693 if (op.getDelay() == 0) {
3695 }
else if (op.getDelay() == 1) {
3699 ps.addAsString(op.getDelay());
3704 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3705 return {PropertyPrecedence::Concat};
3708void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3709 bool addSeparator =
false;
3710 for (
auto input : inputs) {
3713 if (!input.getDefiningOp<ltl::DelayOp>())
3714 ps <<
"##0" << PP::space;
3716 addSeparator =
true;
3717 emitNestedProperty(input, PropertyPrecedence::Concat);
3721EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3722 emitLTLConcat(op.getInputs());
3723 return {PropertyPrecedence::Concat};
3726EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3727 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3728 if (
auto more = op.getMore()) {
3730 ps.addAsString(op.getBase());
3733 ps.addAsString(op.getBase() + *more);
3737 if (op.getBase() == 0) {
3739 }
else if (op.getBase() == 1) {
3743 ps.addAsString(op.getBase());
3747 return {PropertyPrecedence::Repeat};
3750EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3751 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3753 auto more = op.getMore();
3755 ps.addAsString(op.getBase());
3758 ps.addAsString(op.getBase() + more);
3762 return {PropertyPrecedence::Repeat};
3765EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3766 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3768 auto more = op.getMore();
3770 ps.addAsString(op.getBase());
3773 ps.addAsString(op.getBase() + more);
3777 return {PropertyPrecedence::Repeat};
3780EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3781 ps <<
"not" << PP::space;
3782 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3783 return {PropertyPrecedence::Unary};
3789 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3790 if (!concatOp || concatOp.getInputs().size() < 2)
3792 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3793 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3795 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3796 if (!constOp || !constOp.getValue().isOne())
3798 return concatOp.getInputs().drop_back();
3801EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3805 emitLTLConcat(range);
3806 ps << PP::space <<
"|=>" << PP::nbsp;
3808 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3809 ps << PP::space <<
"|->" << PP::nbsp;
3811 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3812 return {PropertyPrecedence::Implication};
3815EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3816 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3817 ps << PP::space <<
"until" << PP::space;
3818 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3819 return {PropertyPrecedence::Until};
3822EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3823 ps <<
"s_eventually" << PP::space;
3824 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3825 return {PropertyPrecedence::Qualifier};
3828EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3830 ps.scopedBox(PP::ibox2, [&] {
3831 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3832 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3836 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3837 return {PropertyPrecedence::Clocking};
3847class NameCollector {
3849 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3853 void collectNames(Block &block);
3855 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3856 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3859 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3860 ModuleEmitter &moduleEmitter;
3865 static constexpr size_t maxTypeWidthBound = 32;
3870void NameCollector::collectNames(Block &block) {
3873 for (
auto &op : block) {
3877 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3878 FuncCallProceduralOp, FuncCallOp>(op))
3880 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3884 for (
auto result : op.getResults()) {
3886 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3887 SmallString<16> typeString;
3891 llvm::raw_svector_ostream stringStream(typeString);
3893 stringStream, op.getLoc());
3895 if (typeString.size() <= maxTypeWidthBound)
3896 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3903 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3904 for (
auto ®ion : op.getRegions()) {
3905 if (!region.empty())
3906 collectNames(region.front());
3920class StmtEmitter :
public EmitterBase,
3928 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3930 void emitStatement(Operation *op);
3931 void emitStatementBlock(Block &body);
3934 LogicalResult emitDeclaration(Operation *op);
3937 void collectNamesAndCalculateDeclarationWidths(Block &block);
3940 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
3941 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
3942 bool isAssignmentLikeContext =
false);
3943 void emitSVAttributes(Operation *op);
3946 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
3949 friend class sv::Visitor<StmtEmitter, LogicalResult>;
3953 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
3954 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
3955 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
3956 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
3957 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
3958 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
3960 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
3961 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
3962 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
3963 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
3964 template <
typename Op>
3967 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3968 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
3969 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
3971 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3972 LogicalResult visitSV(
AssignOp op);
3973 LogicalResult visitSV(BPAssignOp op);
3974 LogicalResult visitSV(PAssignOp op);
3975 LogicalResult visitSV(ForceOp op);
3976 LogicalResult visitSV(ReleaseOp op);
3977 LogicalResult visitSV(AliasOp op);
3978 LogicalResult visitSV(InterfaceInstanceOp op);
3979 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
3980 LogicalResult visitStmt(OutputOp op);
3982 LogicalResult visitStmt(InstanceOp op);
3983 LogicalResult visitStmt(InstanceChoiceOp op);
3984 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
3985 ArrayRef<Value> instPortValues);
3990 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
3991 LogicalResult visitSV(OrderedOutputOp op);
3992 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
3993 LogicalResult visitSV(IfDefProceduralOp op) {
3994 return emitIfDef(op, op.getCond());
3996 LogicalResult visitSV(IfOp op);
3997 LogicalResult visitSV(AlwaysOp op);
3998 LogicalResult visitSV(AlwaysCombOp op);
3999 LogicalResult visitSV(AlwaysFFOp op);
4000 LogicalResult visitSV(InitialOp op);
4001 LogicalResult visitSV(CaseOp op);
4002 LogicalResult visitSV(FWriteOp op);
4003 LogicalResult visitSV(VerbatimOp op);
4004 LogicalResult visitSV(MacroRefOp op);
4006 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4007 std::optional<unsigned> verbosity);
4008 LogicalResult visitSV(StopOp op);
4009 LogicalResult visitSV(FinishOp op);
4010 LogicalResult visitSV(ExitOp op);
4012 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4013 std::optional<unsigned> verbosity,
4015 ValueRange operands);
4016 LogicalResult visitSV(FatalOp op);
4017 LogicalResult visitSV(ErrorOp op);
4018 LogicalResult visitSV(WarningOp op);
4019 LogicalResult visitSV(InfoOp op);
4021 LogicalResult visitSV(ReadMemOp op);
4023 LogicalResult visitSV(GenerateOp op);
4024 LogicalResult visitSV(GenerateCaseOp op);
4026 LogicalResult visitSV(
ForOp op);
4028 void emitAssertionLabel(Operation *op);
4029 void emitAssertionMessage(StringAttr message, ValueRange args,
4030 SmallPtrSetImpl<Operation *> &ops,
4032 template <
typename Op>
4033 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4034 LogicalResult visitSV(AssertOp op);
4035 LogicalResult visitSV(AssumeOp op);
4036 LogicalResult visitSV(CoverOp op);
4037 template <
typename Op>
4038 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4039 LogicalResult visitSV(AssertConcurrentOp op);
4040 LogicalResult visitSV(AssumeConcurrentOp op);
4041 LogicalResult visitSV(CoverConcurrentOp op);
4042 template <
typename Op>
4043 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4044 LogicalResult visitSV(AssertPropertyOp op);
4045 LogicalResult visitSV(AssumePropertyOp op);
4046 LogicalResult visitSV(CoverPropertyOp op);
4048 LogicalResult visitSV(BindOp op);
4049 LogicalResult visitSV(InterfaceOp op);
4050 LogicalResult visitSV(InterfaceSignalOp op);
4051 LogicalResult visitSV(InterfaceModportOp op);
4052 LogicalResult visitSV(AssignInterfaceSignalOp op);
4053 LogicalResult visitSV(MacroDefOp op);
4055 void emitBlockAsStatement(Block *block,
4056 const SmallPtrSetImpl<Operation *> &locationOps,
4057 StringRef multiLineComment = StringRef());
4059 LogicalResult visitSV(FuncDPIImportOp op);
4060 template <
typename CallOp>
4061 LogicalResult emitFunctionCall(CallOp callOp);
4062 LogicalResult visitSV(FuncCallProceduralOp op);
4063 LogicalResult visitSV(FuncCallOp op);
4064 LogicalResult visitSV(ReturnOp op);
4067 ModuleEmitter &emitter;
4072 size_t maxDeclNameWidth = 0;
4073 size_t maxTypeWidth = 0;
4084void StmtEmitter::emitExpression(Value exp,
4085 SmallPtrSetImpl<Operation *> &emittedExprs,
4086 VerilogPrecedence parenthesizeIfLooserThan,
4087 bool isAssignmentLikeContext) {
4088 ExprEmitter(emitter, emittedExprs)
4089 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4094void StmtEmitter::emitSVAttributes(Operation *op) {
4102 setPendingNewline();
4105void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4106 llvm::function_ref<
void()> emitRHS,
4108 std::optional<PPExtString> wordBeforeLHS) {
4110 ps.scopedBox(PP::ibox2, [&]() {
4111 if (wordBeforeLHS) {
4112 ps << *wordBeforeLHS << PP::space;
4116 ps << PP::space << syntax << PP::space;
4118 ps.scopedBox(PP::ibox0, [&]() {
4125template <
typename Op>
4127StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4128 std::optional<PPExtString> wordBeforeLHS) {
4129 SmallPtrSet<Operation *, 8> ops;
4133 ps.addCallback({op,
true});
4134 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4136 emitExpression(op.getSrc(), ops, LowestPrecedence,
4141 ps.addCallback({op,
false});
4142 emitLocationInfoAndNewLine(ops);
4146LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4149 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4152 if (emitter.assignsInlined.count(op))
4156 emitSVAttributes(op);
4161LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4162 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4166 if (emitter.assignsInlined.count(op))
4170 emitSVAttributes(op);
4175LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4177 emitSVAttributes(op);
4182LogicalResult StmtEmitter::visitSV(ForceOp op) {
4184 emitError(op,
"SV attributes emission is unimplemented for the op");
4189LogicalResult StmtEmitter::visitSV(ReleaseOp 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 <<
"release" << PP::space;
4199 emitExpression(op.getDest(), ops);
4202 ps.addCallback({op,
false});
4203 emitLocationInfoAndNewLine(ops);
4207LogicalResult StmtEmitter::visitSV(AliasOp op) {
4209 emitError(op,
"SV attributes emission is unimplemented for the op");
4212 SmallPtrSet<Operation *, 8> ops;
4214 ps.addCallback({op,
true});
4215 ps.scopedBox(PP::ibox2, [&]() {
4216 ps <<
"alias" << PP::space;
4217 ps.scopedBox(PP::cbox0, [&]() {
4219 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4220 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4224 ps.addCallback({op,
false});
4225 emitLocationInfoAndNewLine(ops);
4229LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4230 auto doNotPrint = op.getDoNotPrint();
4231 if (doNotPrint && !state.options.emitBindComments)
4235 emitError(op,
"SV attributes emission is unimplemented for the op");
4238 StringRef prefix =
"";
4239 ps.addCallback({op,
true});
4242 ps <<
"// This interface is elsewhere emitted as a bind statement."
4246 SmallPtrSet<Operation *, 8> ops;
4249 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4250 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4251 "point to an interface");
4254 if (!prefix.empty())
4260 ps.addCallback({op,
false});
4261 emitLocationInfoAndNewLine(ops);
4269LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4271 SmallPtrSet<Operation *, 8> ops;
4272 size_t operandIndex = 0;
4273 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4274 for (
PortInfo port : ports.getOutputs()) {
4275 auto operand = op->getOperand(operandIndex);
4279 if (operand.hasOneUse() && operand.getDefiningOp() &&
4280 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4289 ps.addCallback({op,
true});
4291 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4293 ps <<
"// Zero width: ";
4296 ps <<
"assign" << PP::space;
4298 ps << PP::space <<
"=" << PP::space;
4299 ps.scopedBox(PP::ibox0, [&]() {
4303 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4304 ps <<
"/*Zero width*/";
4306 emitExpression(operand, ops, LowestPrecedence,
4311 ps.addCallback({op,
false});
4312 emitLocationInfoAndNewLine(ops);
4319LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4320 auto parent = op->getParentOfType<PortList>();
4322 return emitOutputLikeOp(op, ports);
4325LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4327 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4328 ps <<
"`ifndef " << typescopeDef << PP::newline;
4329 ps <<
"`define " << typescopeDef;
4330 setPendingNewline();
4331 emitStatementBlock(*op.getBodyBlock());
4333 ps <<
"`endif // " << typescopeDef;
4334 setPendingNewline();
4338LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4340 emitError(op,
"SV attributes emission is unimplemented for the op");
4345 ps << PP::neverbox <<
"// ";
4347 SmallPtrSet<Operation *, 8> ops;
4349 ps.scopedBox(PP::ibox2, [&]() {
4350 ps <<
"typedef" << PP::space;
4351 ps.invokeWithStringOS([&](
auto &os) {
4353 op.getAliasType(),
false);
4355 ps << PP::space <<
PPExtString(op.getPreferredName());
4356 ps.invokeWithStringOS(
4357 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4362 emitLocationInfoAndNewLine(ops);
4366template <
typename CallOpTy>
4367LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4371 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4373 SmallPtrSet<Operation *, 8> ops;
4377 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4378 if (explicitReturn) {
4379 assert(explicitReturn.hasOneUse());
4380 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4381 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4382 emitExpression(bpassignOp.getDest(), ops);
4384 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4385 ps <<
"assign" << PP::nbsp;
4386 emitExpression(assignOp.getDest(), ops);
4388 ps << PP::nbsp <<
"=" << PP::nbsp;
4391 auto arguments = callee.getPortList(
true);
4395 bool needsComma =
false;
4396 auto printArg = [&](Value value) {
4398 ps <<
"," << PP::space;
4399 emitExpression(value, ops);
4403 ps.scopedBox(PP::ibox0, [&] {
4404 unsigned inputIndex = 0, outputIndex = 0;
4405 for (
auto arg : arguments) {
4408 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4410 printArg(op.getInputs()[inputIndex++]);
4415 emitLocationInfoAndNewLine(ops);
4419LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4420 return emitFunctionCall(op);
4423LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4424 return emitFunctionCall(op);
4427template <
typename PPS>
4429 bool isAutomatic =
false,
4430 bool emitAsTwoStateType =
false) {
4431 ps <<
"function" << PP::nbsp;
4433 ps <<
"automatic" << PP::nbsp;
4434 auto retType = op.getExplicitlyReturnedType();
4436 ps.invokeWithStringOS([&](
auto &os) {
4437 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4438 emitAsTwoStateType);
4444 emitter.emitPortList(
4448LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4449 auto parent = op->getParentOfType<sv::FuncOp>();
4451 return emitOutputLikeOp(op, ports);
4454LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4457 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4461 if (
auto linkageName = importOp.getLinkageName())
4462 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4464 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4465 assert(op.isDeclaration() &&
"function must be a declaration");
4468 assert(state.pendingNewline);
4474LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4476 emitError(op,
"SV attributes emission is unimplemented for the op");
4479 SmallPtrSet<Operation *, 8> ops;
4482 ps.addCallback({op,
true});
4484 ps.scopedBox(PP::ibox0, [&]() {
4485 emitExpression(op.getFd(), ops);
4487 ps <<
"," << PP::space;
4488 ps.writeQuotedEscaped(op.getFormatString());
4496 for (
auto operand : op.getSubstitutions()) {
4497 ps <<
"," << PP::space;
4498 emitExpression(operand, ops);
4502 ps.addCallback({op,
false});
4503 emitLocationInfoAndNewLine(ops);
4507LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4509 emitError(op,
"SV attributes emission is unimplemented for the op");
4512 SmallPtrSet<Operation *, 8> ops;
4517 StringRef
string = op.getFormatString();
4518 if (
string.ends_with(
"\n"))
4519 string =
string.drop_back();
4524 bool isFirst =
true;
4527 while (!
string.
empty()) {
4528 auto lhsRhs =
string.split(
'\n');
4532 ps << PP::end << PP::newline << PP::neverbox;
4536 emitTextWithSubstitutions(
4537 ps, lhsRhs.first, op,
4538 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4539 string = lhsRhs.second;
4544 emitLocationInfoAndNewLine(ops);
4549LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4551 emitError(op,
"SV attributes emission is unimplemented for the op");
4555 SmallPtrSet<Operation *, 8> ops;
4560 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4561 assert(macroOp &&
"Invalid IR");
4563 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4565 if (!op.getInputs().empty()) {
4567 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4568 emitExpression(val, ops, LowestPrecedence,
4574 emitLocationInfoAndNewLine(ops);
4580StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4581 std::optional<unsigned> verbosity) {
4583 emitError(op,
"SV attributes emission is unimplemented for the op");
4586 SmallPtrSet<Operation *, 8> ops;
4588 ps.addCallback({op,
true});
4590 if (verbosity && *verbosity != 1) {
4592 ps.addAsString(*verbosity);
4596 ps.addCallback({op,
false});
4597 emitLocationInfoAndNewLine(ops);
4601LogicalResult StmtEmitter::visitSV(StopOp op) {
4602 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4605LogicalResult StmtEmitter::visitSV(FinishOp op) {
4606 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4610LogicalResult StmtEmitter::visitSV(ExitOp op) {
4611 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4617StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4618 std::optional<unsigned> verbosity,
4619 StringAttr message, ValueRange operands) {
4621 emitError(op,
"SV attributes emission is unimplemented for the op");
4624 SmallPtrSet<Operation *, 8> ops;
4626 ps.addCallback({op,
true});
4632 if ((verbosity && *verbosity != 1) || message) {
4634 ps.scopedBox(PP::ibox0, [&]() {
4638 ps.addAsString(*verbosity);
4643 ps <<
"," << PP::space;
4644 ps.writeQuotedEscaped(message.getValue());
4646 for (
auto operand : operands) {
4647 ps <<
"," << PP::space;
4648 emitExpression(operand, ops);
4657 ps.addCallback({op,
false});
4658 emitLocationInfoAndNewLine(ops);
4662LogicalResult StmtEmitter::visitSV(FatalOp op) {
4663 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4664 op.getMessageAttr(), op.getSubstitutions());
4667LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4668 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4669 op.getMessageAttr(), op.getSubstitutions());
4672LogicalResult StmtEmitter::visitSV(WarningOp op) {
4673 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4674 op.getMessageAttr(), op.getSubstitutions());
4677LogicalResult StmtEmitter::visitSV(InfoOp op) {
4678 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4679 op.getMessageAttr(), op.getSubstitutions());
4682LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4683 SmallPtrSet<Operation *, 8> ops({op});
4686 ps.addCallback({op,
true});
4688 switch (op.getBaseAttr().getValue()) {
4689 case MemBaseTypeAttr::MemBaseBin:
4692 case MemBaseTypeAttr::MemBaseHex:
4697 ps.scopedBox(PP::ibox0, [&]() {
4698 ps.writeQuotedEscaped(op.getFilename());
4699 ps <<
"," << PP::space;
4700 emitExpression(op.getDest(), ops);
4704 ps.addCallback({op,
false});
4705 emitLocationInfoAndNewLine(ops);
4709LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4710 emitSVAttributes(op);
4713 ps.addCallback({op,
true});
4714 ps <<
"generate" << PP::newline;
4716 setPendingNewline();
4717 emitStatementBlock(op.getBody().getBlocks().front());
4720 ps <<
"endgenerate";
4721 ps.addCallback({op,
false});
4722 setPendingNewline();
4726LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4727 emitSVAttributes(op);
4730 ps.addCallback({op,
true});
4732 ps.invokeWithStringOS([&](
auto &os) {
4733 emitter.printParamValue(
4734 op.getCond(), os, VerilogPrecedence::Selection,
4735 [&]() { return op->emitOpError(
"invalid case parameter"); });
4738 setPendingNewline();
4741 ArrayAttr
patterns = op.getCasePatterns();
4742 ArrayAttr caseNames = op.getCaseNames();
4743 MutableArrayRef<Region> regions = op.getCaseRegions();
4750 llvm::StringMap<size_t> nextGenIds;
4751 ps.scopedBox(PP::bbox2, [&]() {
4753 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4754 auto ®ion = regions[i];
4755 assert(region.hasOneBlock());
4756 Attribute patternAttr =
patterns[i];
4759 if (!isa<mlir::TypedAttr>(patternAttr))
4762 ps.invokeWithStringOS([&](
auto &os) {
4763 emitter.printParamValue(
4764 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4765 [&]() {
return op->emitOpError(
"invalid case value"); });
4768 StringRef legalName =
4769 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4772 setPendingNewline();
4773 emitStatementBlock(region.getBlocks().front());
4776 setPendingNewline();
4782 ps.addCallback({op,
false});
4783 setPendingNewline();
4787LogicalResult StmtEmitter::visitSV(
ForOp op) {
4788 emitSVAttributes(op);
4789 llvm::SmallPtrSet<Operation *, 8> ops;
4790 ps.addCallback({op,
true});
4792 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4795 ps.scopedBox(PP::cbox0, [&]() {
4799 ps <<
"logic" << PP::nbsp;
4800 ps.invokeWithStringOS([&](
auto &os) {
4801 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4806 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4811 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4812 [&]() { emitExpression(op.getUpperBound(), ops); },
4818 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4819 [&]() { emitExpression(op.getStep(), ops); },
4823 ps << PP::neverbreak;
4824 setPendingNewline();
4825 emitStatementBlock(op.getBody().getBlocks().front());
4828 ps.addCallback({op,
false});
4829 emitLocationInfoAndNewLine(ops);
4834void StmtEmitter::emitAssertionLabel(Operation *op) {
4835 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4841void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4842 SmallPtrSetImpl<Operation *> &ops,
4843 bool isConcurrent =
false) {
4846 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4847 ps.scopedBox(PP::ibox0, [&]() {
4848 ps.writeQuotedEscaped(message.getValue());
4850 for (
auto arg : args) {
4851 ps <<
"," << PP::space;
4852 emitExpression(arg, ops);
4858template <
typename Op>
4859LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4861 emitError(op,
"SV attributes emission is unimplemented for the op");
4864 SmallPtrSet<Operation *, 8> ops;
4866 ps.addCallback({op,
true});
4867 ps.scopedBox(PP::ibox2, [&]() {
4868 emitAssertionLabel(op);
4869 ps.scopedBox(PP::cbox0, [&]() {
4871 switch (op.getDefer()) {
4872 case DeferAssert::Immediate:
4874 case DeferAssert::Observed:
4877 case DeferAssert::Final:
4882 ps.scopedBox(PP::ibox0, [&]() {
4883 emitExpression(op.getExpression(), ops);
4886 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4890 ps.addCallback({op,
false});
4891 emitLocationInfoAndNewLine(ops);
4895LogicalResult StmtEmitter::visitSV(AssertOp op) {
4896 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4899LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4900 return emitImmediateAssertion(op,
PPExtString(
"assume"));
4903LogicalResult StmtEmitter::visitSV(CoverOp op) {
4904 return emitImmediateAssertion(op,
PPExtString(
"cover"));
4907template <
typename Op>
4908LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
4910 emitError(op,
"SV attributes emission is unimplemented for the op");
4913 SmallPtrSet<Operation *, 8> ops;
4915 ps.addCallback({op,
true});
4916 ps.scopedBox(PP::ibox2, [&]() {
4917 emitAssertionLabel(op);
4918 ps.scopedBox(PP::cbox0, [&]() {
4919 ps << opName << PP::nbsp <<
"property (";
4920 ps.scopedBox(PP::ibox0, [&]() {
4921 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
4923 emitExpression(op.getClock(), ops);
4924 ps <<
")" << PP::space;
4925 emitExpression(op.getProperty(), ops);
4928 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
4933 ps.addCallback({op,
false});
4934 emitLocationInfoAndNewLine(ops);
4938LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
4939 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
4942LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
4943 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
4946LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
4947 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
4952template <
typename Op>
4953LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
4955 emitError(op,
"SV attributes emission is unimplemented for the op");
4965 Operation *parent = op->getParentOp();
4966 Value
property = op.getProperty();
4967 bool isTemporal = !
property.getType().isSignlessInteger(1);
4968 bool isProcedural = parent->hasTrait<ProceduralRegion>();
4969 bool emitAsImmediate = !isTemporal && isProcedural;
4972 SmallPtrSet<Operation *, 8> ops;
4974 ps.addCallback({op,
true});
4975 ps.scopedBox(PP::ibox2, [&]() {
4977 emitAssertionLabel(op);
4979 ps.scopedBox(PP::cbox0, [&]() {
4980 if (emitAsImmediate)
4981 ps << opName <<
"(";
4983 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
4985 Value clock = op.getClock();
4986 auto event = op.getEvent();
4988 ps.scopedBox(PP::ibox2, [&]() {
4989 PropertyEmitter(emitter, ops)
4990 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
4993 ps.scopedBox(PP::ibox2, [&]() {
4994 PropertyEmitter(emitter, ops)
4995 .emitAssertPropertyBody(property, op.getDisable());
5000 ps.addCallback({op,
false});
5001 emitLocationInfoAndNewLine(ops);
5005LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5006 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5009LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5010 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5013LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5014 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5017LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5019 emitError(op,
"SV attributes emission is unimplemented for the op");
5022 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5023 .getMacroIdentifier());
5026 bool hasEmptyThen = op->getRegion(0).front().empty();
5028 ps <<
"`ifndef " << ident;
5030 ps <<
"`ifdef " << ident;
5032 SmallPtrSet<Operation *, 8> ops;
5034 emitLocationInfoAndNewLine(ops);
5037 emitStatementBlock(op->getRegion(0).front());
5039 if (!op->getRegion(1).empty()) {
5040 if (!hasEmptyThen) {
5042 ps <<
"`else // " << ident;
5043 setPendingNewline();
5045 emitStatementBlock(op->getRegion(1).front());
5052 setPendingNewline();
5060void StmtEmitter::emitBlockAsStatement(
5061 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5062 StringRef multiLineComment) {
5069 emitLocationInfoAndNewLine(locationOps);
5072 emitStatementBlock(*block);
5074 if (needsBeginEnd) {
5078 if (!multiLineComment.empty())
5079 ps <<
" // " << multiLineComment;
5080 setPendingNewline();
5084LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5086 for (
auto &op : ooop.getBody().front())
5091LogicalResult StmtEmitter::visitSV(IfOp op) {
5092 SmallPtrSet<Operation *, 8> ops;
5094 auto ifcondBox = PP::ibox2;
5096 emitSVAttributes(op);
5098 ps.addCallback({op,
true});
5099 ps <<
"if (" << ifcondBox;
5109 emitExpression(ifOp.getCond(), ops);
5110 ps << PP::end <<
")";
5111 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5113 if (!ifOp.hasElse())
5117 Block *elseBlock = ifOp.getElseBlock();
5119 if (!nestedElseIfOp) {
5124 emitBlockAsStatement(elseBlock, ops);
5130 ifOp = nestedElseIfOp;
5131 ps <<
"else if (" << ifcondBox;
5133 ps.addCallback({op,
false});
5138LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5139 emitSVAttributes(op);
5140 SmallPtrSet<Operation *, 8> ops;
5144 auto printEvent = [&](AlwaysOp::Condition cond) {
5145 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5146 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5148 ps.addCallback({op,
true});
5150 switch (op.getNumConditions()) {
5156 printEvent(op.getCondition(0));
5161 ps.scopedBox(PP::cbox0, [&]() {
5162 printEvent(op.getCondition(0));
5163 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5164 ps << PP::space <<
"or" << PP::space;
5165 printEvent(op.getCondition(i));
5174 std::string comment;
5175 if (op.getNumConditions() == 0) {
5176 comment =
"always @*";
5178 comment =
"always @(";
5181 [&](Attribute eventAttr) {
5182 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5183 comment += stringifyEventControl(event);
5185 [&]() { comment +=
", "; });
5189 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5190 ps.addCallback({op,
false});
5194LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5195 emitSVAttributes(op);
5196 SmallPtrSet<Operation *, 8> ops;
5200 ps.addCallback({op,
true});
5201 StringRef opString =
"always_comb";
5202 if (state.options.noAlwaysComb)
5203 opString =
"always @(*)";
5206 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5207 ps.addCallback({op,
false});
5211LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5212 emitSVAttributes(op);
5214 SmallPtrSet<Operation *, 8> ops;
5218 ps.addCallback({op,
true});
5219 ps <<
"always_ff @(";
5220 ps.scopedBox(PP::cbox0, [&]() {
5221 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5222 emitExpression(op.getClock(), ops);
5223 if (op.getResetStyle() == ResetType::AsyncReset) {
5224 ps << PP::nbsp <<
"or" << PP::space
5225 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5226 emitExpression(op.getReset(), ops);
5233 std::string comment;
5234 comment +=
"always_ff @(";
5235 comment += stringifyEventControl(op.getClockEdge());
5236 if (op.getResetStyle() == ResetType::AsyncReset) {
5238 comment += stringifyEventControl(*op.getResetEdge());
5242 if (op.getResetStyle() == ResetType::NoReset)
5243 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5246 emitLocationInfoAndNewLine(ops);
5247 ps.scopedBox(PP::bbox2, [&]() {
5253 if (op.getResetStyle() == ResetType::AsyncReset &&
5254 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5256 emitExpression(op.getReset(), ops);
5258 emitBlockAsStatement(op.getResetBlock(), ops);
5261 emitBlockAsStatement(op.getBodyBlock(), ops);
5266 ps <<
" // " << comment;
5267 setPendingNewline();
5269 ps.addCallback({op,
false});
5273LogicalResult StmtEmitter::visitSV(InitialOp op) {
5274 emitSVAttributes(op);
5275 SmallPtrSet<Operation *, 8> ops;
5278 ps.addCallback({op,
true});
5280 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5281 ps.addCallback({op,
false});
5285LogicalResult StmtEmitter::visitSV(CaseOp op) {
5286 emitSVAttributes(op);
5287 SmallPtrSet<Operation *, 8> ops, emptyOps;
5290 ps.addCallback({op,
true});
5291 if (op.getValidationQualifier() !=
5292 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5293 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5294 op.getValidationQualifier()))
5296 const char *opname =
nullptr;
5297 switch (op.getCaseStyle()) {
5298 case CaseStmtType::CaseStmt:
5301 case CaseStmtType::CaseXStmt:
5304 case CaseStmtType::CaseZStmt:
5308 ps << opname <<
" (";
5309 ps.scopedBox(PP::ibox0, [&]() {
5310 emitExpression(op.getCond(), ops);
5313 emitLocationInfoAndNewLine(ops);
5315 ps.scopedBox(PP::bbox2, [&]() {
5316 for (
auto &caseInfo : op.getCases()) {
5318 auto &
pattern = caseInfo.pattern;
5320 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5321 .Case<CaseBitPattern>([&](
auto bitPattern) {
5324 ps.invokeWithStringOS([&](
auto &os) {
5325 os << bitPattern->getWidth() <<
"'b";
5326 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5327 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5330 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5331 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5332 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5334 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5335 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5338 emitBlockAsStatement(caseInfo.block, emptyOps);
5344 ps.addCallback({op,
false});
5345 emitLocationInfoAndNewLine(ops);
5349LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5350 bool doNotPrint = op.getDoNotPrint();
5351 if (doNotPrint && !state.options.emitBindComments)
5356 emitSVAttributes(op);
5358 ps.addCallback({op,
true});
5361 <<
"/* This instance is elsewhere emitted as a bind statement."
5364 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5365 "attributes. The attributes will not be emitted.";
5368 SmallPtrSet<Operation *, 8> ops;
5373 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5374 assert(moduleOp &&
"Invalid IR");
5378 if (!op.getParameters().empty()) {
5381 bool printed =
false;
5383 llvm::zip(op.getParameters(),
5384 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5385 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5386 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5388 if (param.getValue() == modParam.getValue())
5393 ps <<
" #(" << PP::bbox2 << PP::newline;
5396 ps <<
"," << PP::newline;
5400 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5402 ps.invokeWithStringOS([&](
auto &os) {
5403 emitter.printParamValue(param.getValue(), os, [&]() {
5404 return op->emitOpError(
"invalid instance parameter '")
5405 << param.getName().getValue() <<
"' value";
5411 ps << PP::end << PP::newline <<
")";
5418 SmallVector<Value> instPortValues(modPortInfo.size());
5419 op.getValues(instPortValues, modPortInfo);
5420 emitInstancePortList(op, modPortInfo, instPortValues);
5422 ps.addCallback({op,
false});
5423 emitLocationInfoAndNewLine(ops);
5428 setPendingNewline();
5433LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5435 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5436 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5441 Operation *defaultModuleOp =
5442 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5444 SmallVector<Value> instPortValues(modPortInfo.size());
5445 op.getValues(instPortValues, modPortInfo);
5446 emitInstancePortList(op, modPortInfo, instPortValues);
5448 SmallPtrSet<Operation *, 8> ops;
5450 ps.addCallback({op,
false});
5451 emitLocationInfoAndNewLine(ops);
5456void StmtEmitter::emitInstancePortList(Operation *op,
5458 ArrayRef<Value> instPortValues) {
5459 SmallPtrSet<Operation *, 8> ops;
5462 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5463 ModulePortInfo containingPortList(containingModule.getPortList());
5468 size_t maxNameLength = 0;
5469 for (
auto &elt : modPortInfo) {
5470 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5473 auto getWireForValue = [&](Value result) {
5474 return result.getUsers().begin()->getOperand(0);
5478 bool isFirst =
true;
5479 bool isZeroWidth =
false;
5481 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5483 auto &modPort = modPortInfo.
at(portNum);
5485 Value portVal = instPortValues[portNum];
5490 bool shouldPrintComma =
true;
5492 shouldPrintComma =
false;
5493 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5495 shouldPrintComma =
true;
5500 if (shouldPrintComma)
5503 emitLocationInfoAndNewLine(ops);
5518 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5519 auto modPortName = modPort.getVerilogName();
5521 ps.spaces(maxNameLength - modPortName.size() + 1);
5523 ps.scopedBox(PP::ibox0, [&]() {
5530 if (!modPort.isOutput()) {
5532 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5533 ps <<
"/* Zero width */";
5535 emitExpression(portVal, ops, LowestPrecedence);
5536 }
else if (portVal.use_empty()) {
5537 ps <<
"/* unused */";
5538 }
else if (portVal.hasOneUse() &&
5539 (output = dyn_cast_or_null<OutputOp>(
5540 portVal.getUses().begin()->getOwner()))) {
5545 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5547 containingPortList.atOutput(outputPortNo).getVerilogName());
5549 portVal = getWireForValue(portVal);
5550 emitExpression(portVal, ops);
5556 if (!isFirst || isZeroWidth) {
5557 emitLocationInfoAndNewLine(ops);
5570LogicalResult StmtEmitter::visitSV(BindOp op) {
5571 emitter.emitBind(op);
5572 assert(state.pendingNewline);
5576LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5577 emitComment(op.getCommentAttr());
5579 emitSVAttributes(op);
5582 ps.addCallback({op,
true});
5584 setPendingNewline();
5586 emitStatementBlock(*op.getBodyBlock());
5588 ps <<
"endinterface" << PP::newline;
5589 ps.addCallback({op,
false});
5590 setPendingNewline();
5594LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5596 emitSVAttributes(op);
5598 ps.addCallback({op,
true});
5600 ps << PP::neverbox <<
"// ";
5601 ps.invokeWithStringOS([&](
auto &os) {
5606 ps.invokeWithStringOS(
5607 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5611 ps.addCallback({op,
false});
5612 setPendingNewline();
5616LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5618 ps.addCallback({op,
true});
5622 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5623 auto port = cast<ModportStructAttr>(portAttr);
5624 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5625 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5626 ps << PPExtString(getSymOpName(signalDecl));
5630 ps.addCallback({op,
false});
5631 setPendingNewline();
5635LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5637 ps.addCallback({op,
true});
5638 SmallPtrSet<Operation *, 8> emitted;
5641 emitExpression(op.getIface(), emitted);
5642 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5643 emitExpression(op.getRhs(), emitted);
5645 ps.addCallback({op,
false});
5646 setPendingNewline();
5650LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5651 auto decl = op.getReferencedMacro(&state.symbolCache);
5654 ps.addCallback({op,
true});
5656 if (decl.getArgs()) {
5658 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5659 ps << cast<StringAttr>(name);
5663 if (!op.getFormatString().empty()) {
5665 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5668 ps.addCallback({op,
false});
5669 setPendingNewline();
5673void StmtEmitter::emitStatement(Operation *op) {
5680 if (isa<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5684 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5685 succeeded(dispatchVerifVisitor(op)))
5688 emitOpError(op,
"emission to Verilog not supported");
5689 emitPendingNewlineIfNeeded();
5690 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5691 setPendingNewline();
5702 StmtEmitter &stmtEmitter) {
5709 if (isa<IfDefProceduralOp>(op->getParentOp()))
5717 SmallVector<Value, 8> exprsToScan(op->getOperands());
5722 while (!exprsToScan.empty()) {
5723 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5730 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5731 auto *defOp = readInout.getOperand().getDefiningOp();
5738 if (isa<sv::WireOp>(defOp))
5743 if (!isa<RegOp, LogicOp>(defOp))
5749 if (isa<LogicOp>(defOp) &&
5750 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5754 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5755 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5763 exprsToScan.append(expr->getOperands().begin(),
5764 expr->getOperands().end());
5770 if (expr->getBlock() != op->getBlock())
5775 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5782template <
class AssignTy>
5784 AssignTy singleAssign;
5785 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5786 if (hasSVAttributes(user))
5789 if (auto assign = dyn_cast<AssignTy>(user)) {
5792 singleAssign = assign;
5796 return isa<ReadInOutOp>(user);
5798 return singleAssign;
5804 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5808 if (op1->getBlock() != user->getBlock())
5814 return op1->isBeforeInBlock(user);
5818LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5819 emitSVAttributes(op);
5820 auto value = op->getResult(0);
5821 SmallPtrSet<Operation *, 8> opsForLocation;
5822 opsForLocation.insert(op);
5824 ps.addCallback({op,
true});
5827 auto type = value.getType();
5830 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5831 unsigned targetColumn = 0;
5832 unsigned column = 0;
5835 if (maxDeclNameWidth > 0)
5836 targetColumn += maxDeclNameWidth + 1;
5839 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5840 }
else if (!word.empty()) {
5842 column += word.size();
5843 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5844 ps.spaces(numSpaces);
5845 column += numSpaces;
5848 SmallString<8> typeString;
5851 llvm::raw_svector_ostream stringStream(typeString);
5856 if (maxTypeWidth > 0)
5857 targetColumn += maxTypeWidth + 1;
5858 unsigned numSpaces = 0;
5859 if (!typeString.empty()) {
5861 column += typeString.size();
5864 if (targetColumn > column)
5865 numSpaces = targetColumn - column;
5866 ps.spaces(numSpaces);
5867 column += numSpaces;
5873 ps.invokeWithStringOS(
5874 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5877 if (state.options.printDebugInfo) {
5878 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5879 auto innerSym = innerSymOp.getInnerSymAttr();
5880 if (innerSym && !innerSym.empty()) {
5882 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5888 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5889 ps << PP::space <<
"=" << PP::space;
5890 ps.invokeWithStringOS([&](
auto &os) {
5891 emitter.printParamValue(localparam.getValue(), os, [&]() {
5892 return op->emitOpError(
"invalid localparam value");
5897 if (
auto regOp = dyn_cast<RegOp>(op)) {
5898 if (
auto initValue = regOp.getInit()) {
5899 ps << PP::space <<
"=" << PP::space;
5900 ps.scopedBox(PP::ibox0, [&]() {
5901 emitExpression(initValue, opsForLocation, LowestPrecedence,
5910 if (isa<sv::WireOp>(op) &&
5911 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
5914 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
5915 auto *source = singleAssign.getSrc().getDefiningOp();
5919 if (!source || isa<ConstantOp>(source) ||
5920 op->getNextNode() == singleAssign) {
5921 ps << PP::space <<
"=" << PP::space;
5922 ps.scopedBox(PP::ibox0, [&]() {
5923 emitExpression(singleAssign.getSrc(), opsForLocation,
5927 emitter.assignsInlined.insert(singleAssign);
5935 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
5938 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
5941 auto *source = singleAssign.getSrc().getDefiningOp();
5945 if (!source || isa<ConstantOp>(source) ||
5948 ps << PP::space <<
"=" << PP::space;
5949 ps.scopedBox(PP::ibox0, [&]() {
5950 emitExpression(singleAssign.getSrc(), opsForLocation,
5955 emitter.assignsInlined.insert(singleAssign);
5956 emitter.expressionsEmittedIntoDecl.insert(op);
5963 ps.addCallback({op,
false});
5964 emitLocationInfoAndNewLine(opsForLocation);
5968void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
5971 NameCollector collector(emitter);
5972 collector.collectNames(block);
5975 maxDeclNameWidth = collector.getMaxDeclNameWidth();
5976 maxTypeWidth = collector.getMaxTypeWidth();
5979void StmtEmitter::emitStatementBlock(Block &body) {
5980 ps.scopedBox(PP::bbox2, [&]() {
5985 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
5986 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
5991 if (!isa<IfDefProceduralOp>(body.getParentOp()))
5992 collectNamesAndCalculateDeclarationWidths(body);
5995 for (
auto &op : body) {
6002void ModuleEmitter::emitStatement(Operation *op) {
6003 StmtEmitter(*
this, state.options).emitStatement(op);
6008void ModuleEmitter::emitSVAttributes(Operation *op) {
6016 setPendingNewline();
6023void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6024 auto verilogName =
module.getVerilogModuleNameAttr();
6026 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6028 setPendingNewline();
6037void ModuleEmitter::emitBind(BindOp op) {
6039 emitError(op,
"SV attributes emission is unimplemented for the op");
6040 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6046 Operation *childMod =
6047 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6051 ps.addCallback({op,
true});
6052 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6053 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6055 bool isFirst =
true;
6056 ps.scopedBox(PP::bbox2, [&]() {
6057 auto parentPortInfo = parentMod.getPortList();
6061 size_t maxNameLength = 0;
6062 for (
auto &elt : childPortInfo) {
6063 auto portName = elt.getVerilogName();
6064 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6065 maxNameLength = std::max(maxNameLength, elt.getName().size());
6068 SmallVector<Value> instPortValues(childPortInfo.size());
6069 inst.getValues(instPortValues, childPortInfo);
6071 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6073 Value portVal = instPortValues[idx];
6079 bool shouldPrintComma =
true;
6081 shouldPrintComma =
false;
6082 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6084 shouldPrintComma =
true;
6089 if (shouldPrintComma)
6102 ps << PP::neverbox <<
"//";
6106 ps.nbsp(maxNameLength - elt.getName().size());
6108 llvm::SmallPtrSet<Operation *, 4> ops;
6109 if (elt.isOutput()) {
6110 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6111 "output port must have either single or no use");
6112 if (portVal.use_empty()) {
6113 ps <<
"/* unused */";
6114 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6115 portVal.getUses().begin()->getOwner())) {
6118 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6120 parentPortList.atOutput(outputPortNo).getVerilogName());
6122 portVal = portVal.getUsers().begin()->getOperand(0);
6123 ExprEmitter(*
this, ops)
6124 .emitExpression(portVal, LowestPrecedence,
6128 ExprEmitter(*
this, ops)
6129 .emitExpression(portVal, LowestPrecedence,
6142 ps.addCallback({op,
false});
6143 setPendingNewline();
6146void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6148 emitError(op,
"SV attributes emission is unimplemented for the op");
6150 auto instance = op.getReferencedInstance(&state.symbolCache);
6152 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6153 instance.getInterfaceType().getInterface());
6155 ps.addCallback({op,
true});
6156 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6157 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6159 ps.addCallback({op,
false});
6160 setPendingNewline();
6163void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6167 auto printParamType = [&](Type type, Attribute defaultValue,
6168 SmallString<8> &result) {
6170 llvm::raw_svector_ostream sstream(result);
6175 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6176 if (intAttr.getValue().getBitWidth() == 32)
6178 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6179 if (fpAttr.getType().isF64())
6182 if (isa<NoneType>(type))
6189 if (
auto intType = type_dyn_cast<IntegerType>(type))
6190 if (intType.getWidth() == 32) {
6191 sstream <<
"/*integer*/";
6195 printPackedType(type, sstream, module->getLoc(),
6203 size_t maxTypeWidth = 0;
6204 SmallString<8> scratch;
6205 for (
auto param : params) {
6206 auto paramAttr = cast<ParamDeclAttr>(param);
6208 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6209 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6212 if (maxTypeWidth > 0)
6215 ps.scopedBox(PP::bbox2, [&]() {
6216 ps << PP::newline <<
"#(";
6217 ps.scopedBox(PP::cbox0, [&]() {
6220 [&](Attribute param) {
6221 auto paramAttr = cast<ParamDeclAttr>(param);
6222 auto defaultValue = paramAttr.getValue();
6224 printParamType(paramAttr.getType(), defaultValue, scratch);
6225 if (!scratch.empty())
6227 if (scratch.size() < maxTypeWidth)
6228 ps.nbsp(maxTypeWidth - scratch.size());
6230 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6231 module, paramAttr.getName()));
6235 ps.invokeWithStringOS([&](
auto &os) {
6237 return module->emitError("parameter '")
6238 << paramAttr.getName().getValue()
6239 << "' has invalid value";
6244 [&]() { ps <<
"," << PP::newline; });
6250void ModuleEmitter::emitPortList(Operation *module,
6252 bool emitAsTwoStateType) {
6254 if (portInfo.
size())
6255 emitLocationInfo(module->getLoc());
6259 bool hasOutputs =
false, hasZeroWidth =
false;
6260 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6261 SmallVector<SmallString<8>, 16> portTypeStrings;
6263 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6264 auto port = portInfo.
at(i);
6268 lastNonZeroPort = i;
6271 portTypeStrings.push_back({});
6273 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6275 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6278 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6281 if (maxTypeWidth > 0)
6285 ps.scopedBox(PP::bbox2, [&]() {
6286 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6287 auto lastPort = e - 1;
6290 auto portType = portInfo.
at(portIdx).
type;
6294 bool isZeroWidth =
false;
6299 ps << (isZeroWidth ?
"// " :
" ");
6303 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6304 switch (thisPortDirection) {
6305 case ModulePort::Direction::Output:
6308 case ModulePort::Direction::Input:
6309 ps << (hasOutputs ?
"input " :
"input ");
6311 case ModulePort::Direction::InOut:
6312 ps << (hasOutputs ?
"inout " :
"inout ");
6315 bool emitWireInPorts = state.options.emitWireInPorts;
6316 if (emitWireInPorts)
6320 if (!portTypeStrings[portIdx].
empty())
6321 ps << portTypeStrings[portIdx];
6322 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6323 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6325 size_t startOfNamePos =
6326 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6332 ps.invokeWithStringOS(
6333 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6336 auto innerSym = portInfo.
at(portIdx).
getSym();
6337 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6339 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6344 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6348 if (
auto loc = portInfo.
at(portIdx).
loc)
6349 emitLocationInfo(loc);
6359 if (!state.options.disallowPortDeclSharing) {
6360 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6363 auto port = portInfo.
at(portIdx);
6367 bool isZeroWidth =
false;
6372 ps << (isZeroWidth ?
"// " :
" ");
6375 ps.nbsp(startOfNamePos);
6378 StringRef name = port.getVerilogName();
6382 ps.invokeWithStringOS(
6383 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6386 auto sym = port.getSym();
6387 if (state.options.printDebugInfo && sym && !sym.empty())
6388 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6392 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6396 if (
auto loc = port.loc)
6397 emitLocationInfo(loc);
6408 if (!portInfo.
size()) {
6410 SmallPtrSet<Operation *, 8> moduleOpSet;
6411 moduleOpSet.insert(module);
6412 emitLocationInfoAndNewLine(moduleOpSet);
6415 ps <<
");" << PP::newline;
6416 setPendingNewline();
6420void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6421 currentModuleOp =
module;
6423 emitComment(module.getCommentAttr());
6424 emitSVAttributes(module);
6426 ps.addCallback({module,
true});
6430 emitParameters(module, module.getParameters());
6434 assert(state.pendingNewline);
6437 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6440 ps.addCallback({module,
false});
6442 setPendingNewline();
6444 currentModuleOp =
nullptr;
6447void ModuleEmitter::emitFunc(FuncOp func) {
6449 if (func.isDeclaration())
6452 currentModuleOp = func;
6454 ps.addCallback({func,
true});
6458 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6460 ps <<
"endfunction";
6462 currentModuleOp =
nullptr;
6471 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6478 void emit(emit::FileListOp op);
6481 void emit(Block *block);
6483 void emitOp(emit::RefOp op);
6484 void emitOp(emit::VerbatimOp op);
6488 for (Operation &op : *block) {
6489 TypeSwitch<Operation *>(&op)
6490 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6491 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6492 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6493 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6494 .Case<BindInterfaceOp>(
6495 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6496 .Case<TypeScopeOp>([&](
auto typedecls) {
6497 ModuleEmitter(state).emitStatement(typedecls);
6500 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6506 for (
auto sym : op.getFiles()) {
6507 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6509 auto it = state.fileMapping.find(fileName);
6510 if (it == state.fileMapping.end()) {
6511 emitOpError(op,
" references an invalid file: ") << sym;
6515 auto file = cast<emit::FileOp>(it->second);
6516 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6523 StringAttr target = op.getTargetAttr().getAttr();
6524 auto *targetOp = state.symbolCache.getDefinition(target);
6525 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6527 TypeSwitch<Operation *>(targetOp)
6528 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6529 .Case<hw::HWModuleOp>(
6530 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6531 .Case<TypeScopeOp>([&](
auto typedecls) {
6532 ModuleEmitter(state).emitStatement(typedecls);
6535 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6541 SmallPtrSet<Operation *, 8> ops;
6546 StringRef text = op.getText();
6550 const auto &[lhs, rhs] = text.split(
'\n');
6554 ps << PP::end << PP::newline << PP::neverbox;
6556 }
while (!text.empty());
6559 emitLocationInfoAndNewLine(ops);
6577 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6578 moduleOp->walk([&](Operation *op) {
6580 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6583 SymbolTable::getSymbolAttrName()),
6584 name.getSymName(), op);
6585 if (isa<BindOp>(op))
6591 auto collectPorts = [&](
auto moduleOp) {
6592 auto portInfo = moduleOp.getPortList();
6593 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6594 if (!p.attrs || p.attrs.empty())
6596 for (NamedAttribute portAttr : p.attrs) {
6597 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6606 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6607 for (
auto file :
designOp.getOps<emit::FileOp>())
6608 for (
auto refs : file.getOps<emit::RefOp>())
6609 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6611 SmallString<32> outputPath;
6612 for (
auto &op : *
designOp.getBody()) {
6615 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6617 bool hasFileName =
false;
6618 bool emitReplicatedOps = !isFileOp;
6619 bool addToFilelist = !isFileOp;
6625 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6627 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6628 <<
" on " << op <<
"\n";);
6629 if (!attr.isDirectory())
6632 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6633 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6636 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6641 if (!defaultFileName.isTriviallyEmpty()) {
6642 llvm::sys::path::append(outputPath, defaultFileName);
6644 op->emitError(
"file name unspecified");
6646 llvm::sys::path::append(outputPath,
"error.out");
6650 auto destFile = StringAttr::get(op->getContext(), outputPath);
6651 auto &file =
files[destFile];
6652 file.ops.push_back(info);
6653 file.emitReplicatedOps = emitReplicatedOps;
6654 file.addToFilelist = addToFilelist;
6655 file.isVerilog = outputPath.ends_with(
".sv");
6660 TypeSwitch<Operation *>(&op)
6661 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6663 fileMapping.try_emplace(file.getSymNameAttr(), file);
6664 separateFile(file, file.getFileName());
6666 .Case<emit::FragmentOp>([&](
auto fragment) {
6669 .Case<HWModuleOp>([&](
auto mod) {
6671 auto sym = mod.getNameAttr();
6674 collectInstanceSymbolsAndBinds(mod);
6676 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6677 if (it->second.size() != 1 || attr) {
6680 op.emitError(
"modules can be emitted to a single file");
6688 if (attr || separateModules)
6694 .Case<InterfaceOp>([&](InterfaceOp intf) {
6699 for (
auto &op : *intf.getBodyBlock())
6700 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6701 if (
auto name = symOp.getNameAttr())
6705 if (attr || separateModules)
6706 separateFile(intf, intf.getSymName() +
".sv");
6716 .Case<VerbatimOp, IfDefOp, MacroDefOp, FuncDPIImportOp>(
6717 [&](Operation *op) {
6723 separateFile(op,
"");
6725 .Case<FuncOp>([&](
auto op) {
6731 separateFile(op,
"");
6735 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6738 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6747 separateFile(op,
"");
6749 .Case<BindOp>([&](
auto op) {
6751 separateFile(op,
"bindfile.sv");
6756 .Case<MacroDeclOp>([&](
auto op) {
6759 .Case<sv::ReserveNamesOp>([](
auto op) {
6762 .Case<om::ClassLike>([&](
auto op) {
6765 .Case<om::ConstantOp>([&](
auto op) {
6768 .Default([&](
auto *) {
6769 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6789 size_t lastReplicatedOp = 0;
6791 bool emitHeaderInclude =
6794 if (emitHeaderInclude)
6797 size_t numReplicatedOps =
6802 DenseSet<emit::FragmentOp> includedFragments;
6803 for (
const auto &opInfo : file.
ops) {
6804 Operation *op = opInfo.op;
6808 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6814 if (
auto fragments =
6816 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6820 op->emitError(
"cannot find referenced fragment ") << sym;
6823 emit::FragmentOp fragment = it->second;
6824 if (includedFragments.insert(fragment).second) {
6825 thingsToEmit.emplace_back(it->second);
6831 thingsToEmit.emplace_back(op);
6836 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6841 TypeSwitch<Operation *>(op)
6842 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6843 .Case<HWModuleExternOp>([&](
auto op) {
6846 .Case<HWModuleGeneratedOp>(
6847 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6848 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6849 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6850 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6851 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6852 .Case<TypeScopeOp>([&](
auto typedecls) {
6853 ModuleEmitter(state).emitStatement(typedecls);
6855 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6857 .Case<MacroDefOp, FuncDPIImportOp>(
6858 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6859 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6860 .Default([&](
auto *op) {
6861 state.encounteredError =
true;
6862 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6869 llvm::formatted_raw_ostream &os,
6870 StringAttr fileName,
bool parallelize) {
6871 MLIRContext *context =
designOp->getContext();
6875 parallelize &= context->isMultithreadingEnabled();
6886 size_t lineOffset = 0;
6887 for (
auto &entry : thingsToEmit) {
6888 entry.verilogLocs.setStream(os);
6889 if (
auto *op = entry.getOperation()) {
6894 state.addVerilogLocToOps(lineOffset, fileName);
6896 os << entry.getStringData();
6901 if (state.encounteredError)
6919 SmallString<256> buffer;
6920 llvm::raw_svector_ostream tmpStream(buffer);
6921 llvm::formatted_raw_ostream rs(tmpStream);
6932 for (
auto &entry : thingsToEmit) {
6935 auto *op = entry.getOperation();
6937 auto lineOffset = os.getLine() + 1;
6938 os << entry.getStringData();
6942 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
6945 entry.verilogLocs.setStream(os);
6952 state.addVerilogLocToOps(0, fileName);
6968 module.emitWarning()
6969 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
6970 "created only at SplitExportVerilog";
6979 for (
const auto &it : emitter.
files) {
6980 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
6981 "\" ----- 8< -----\n\n");
6987 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
6988 "\" ----- 8< -----\n\n");
6989 for (
auto &name : it.second)
6990 contents += name.str() +
"\n";
6991 list.emplace_back(contents);
6994 llvm::formatted_raw_ostream rs(os);
6998 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7007 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7009 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7010 if (failed(failableParallelForEach(
7011 module->getContext(), modulesToPrepare,
7012 [&](
auto op) { return prepareHWModule(op, options); })))
7019struct ExportVerilogPass
7020 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7021 ExportVerilogPass(raw_ostream &os) : os(os) {}
7022 void runOnOperation()
override {
7024 mlir::OpPassManager preparePM(
"builtin.module");
7027 auto &modulePM = preparePM.nestAny();
7029 if (failed(runPipeline(preparePM, getOperation())))
7030 return signalPassFailure();
7033 return signalPassFailure();
7040struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7041 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7042 : ExportVerilogPass{*os} {
7043 owned = std::move(os);
7047 std::unique_ptr<llvm::raw_ostream> owned;
7051std::unique_ptr<mlir::Pass>
7053 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7056std::unique_ptr<mlir::Pass>
7058 return std::make_unique<ExportVerilogPass>(os);
7069static std::unique_ptr<llvm::ToolOutputFile>
7073 SmallString<128> outputFilename(dirname);
7075 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7078 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7080 emitter.
designOp.emitError(
"cannot create output directory \"")
7081 << outputDir <<
"\": " << error.message();
7087 std::string errorMessage;
7088 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7090 emitter.
designOp.emitError(errorMessage);
7107 llvm::formatted_raw_ostream rs(output->os());
7113 StringAttr::get(fileName.getContext(), output->getFilename()),
7119 StringRef dirname) {
7130 bool insertSuccess =
7132 .insert({StringAttr::get(module.getContext(),
circtHeader),
7138 if (!insertSuccess) {
7139 module.emitError() << "tried to emit a heder to " << circtHeader
7140 << ", but the file is used as an output too.";
7146 parallelForEach(module->getContext(), emitter.
files.begin(),
7147 emitter.
files.end(), [&](
auto &it) {
7148 createSplitOutputFile(it.first, it.second, dirname,
7153 SmallString<128> filelistPath(dirname);
7154 llvm::sys::path::append(filelistPath,
"filelist.f");
7156 std::string errorMessage;
7157 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7159 module->emitError(errorMessage);
7163 for (
const auto &it : emitter.
files) {
7164 if (it.second.addToFilelist)
7165 output->os() << it.first.str() <<
"\n";
7174 for (
auto &name : it.second)
7175 output->os() << name.str() <<
"\n";
7186 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7188 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7189 if (failed(failableParallelForEach(
7190 module->getContext(), modulesToPrepare,
7191 [&](
auto op) { return prepareHWModule(op, options); })))
7199struct ExportSplitVerilogPass
7200 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7201 ExportSplitVerilogPass(StringRef directory) {
7202 directoryName = directory.str();
7204 void runOnOperation()
override {
7206 mlir::OpPassManager preparePM(
"builtin.module");
7211 if (failed(runPipeline(preparePM, getOperation())))
7212 return signalPassFailure();
7215 return signalPassFailure();
7220std::unique_ptr<mlir::Pass>
7222 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 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 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 std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef fileName, StringRef dirname, SharedEmitterState &emitter)
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.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static 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
RewritePatternSet pattern
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.
This helps visit TypeOp nodes.
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
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.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
PP
Send one of these to TokenStream to add the corresponding token.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
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.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< mlir::Pass > createLegalizeAnonEnumsPass()
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
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)
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.