42#include "mlir/IR/BuiltinOps.h"
43#include "mlir/IR/ImplicitLocOpBuilder.h"
44#include "mlir/IR/Location.h"
45#include "mlir/IR/Threading.h"
46#include "mlir/Interfaces/FunctionImplementation.h"
47#include "mlir/Pass/PassManager.h"
48#include "mlir/Support/FileUtilities.h"
49#include "llvm/ADT/MapVector.h"
50#include "llvm/ADT/STLExtras.h"
51#include "llvm/ADT/StringSet.h"
52#include "llvm/ADT/TypeSwitch.h"
53#include "llvm/Support/FileSystem.h"
54#include "llvm/Support/FormattedStream.h"
55#include "llvm/Support/Path.h"
56#include "llvm/Support/SaveAndRestore.h"
57#include "llvm/Support/ToolOutputFile.h"
58#include "llvm/Support/raw_ostream.h"
61#define GEN_PASS_DEF_EXPORTSPLITVERILOG
62#define GEN_PASS_DEF_EXPORTVERILOG
63#include "circt/Conversion/Passes.h.inc"
70using namespace ExportVerilog;
72using namespace pretty;
74#define DEBUG_TYPE "export-verilog"
82enum VerilogPrecedence {
103enum SubExprSignResult { IsSigned, IsUnsigned };
109 VerilogPrecedence precedence;
112 SubExprSignResult signedness;
114 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
115 : precedence(precedence), signedness(signedness) {}
125 return Builder(ctx).getI32IntegerAttr(value);
128static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
129 return Builder(ctx).getIntegerAttr(t, value);
145 if (isa<VerbatimExprOp>(op)) {
146 if (op->getNumOperands() == 0 &&
147 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
152 if (isa<XMRRefOp>(op))
156 if (isa<MacroRefExprOp>(op))
166 if (op->getNumOperands() == 0)
170 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
174 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
175 auto *indexOp = array.getIndex().getDefiningOp();
176 if (!indexOp || isa<ConstantOp>(indexOp))
178 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
179 auto *readSrc = read.getInput().getDefiningOp();
181 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
196 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
197 return attr.getValue();
198 return TypeSwitch<Operation *, StringRef>(symOp)
203 return op.getVerilogNameAttr().getValue();
205 .Case<InterfaceOp>([&](InterfaceOp op) {
208 .Case<InterfaceSignalOp>(
209 [&](InterfaceSignalOp op) {
return op.getSymName(); })
210 .Case<InterfaceModportOp>(
211 [&](InterfaceModportOp op) {
return op.getSymName(); })
212 .Default([&](Operation *op) {
213 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
214 return attr.getValue();
215 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
216 return attr.getValue();
217 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
218 return attr.getValue();
220 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
221 return attr.getValue();
222 return StringRef(
"");
227template <
typename PPS>
229 os <<
"/*Zero width*/ 1\'b0";
234 auto hml = cast<HWModuleLike>(module);
235 return hml.getPort(portArgNum).getVerilogName();
240 auto hml = cast<HWModuleLike>(module);
241 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
242 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
243 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
244 return updatedName.getValue();
245 return hml.getHWModuleType().getPortName(pId);
254 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
255 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
256 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
257 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
258 UnpackedOpenArrayCastOp>(op))
262 if (isa<verif::ContractOp>(op))
273 SmallVectorImpl<Attribute> &dims, Type type, Location loc,
274 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
275 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
276 if (integer.getWidth() != 1)
277 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
280 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
281 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
282 getTypeDims(dims, array.getElementType(), loc, errorHandler);
286 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
287 dims.push_back(intType.getWidth());
291 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
292 return getTypeDims(dims, inout.getElementType(), loc, errorHandler);
293 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
294 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
295 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
296 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
297 if (hw::type_isa<InterfaceType, StructType, EnumType, UnionType>(type))
300 errorHandler(loc) <<
"value has an unsupported verilog type " << type;
306 Type a, Type b, Location loc,
307 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
308 SmallVector<Attribute, 4> aDims;
311 SmallVector<Attribute, 4> bDims;
314 return aDims == bDims;
320 if (
auto intType = dyn_cast<IntegerType>(type))
321 return intType.getWidth() == 0;
322 if (
auto inout = dyn_cast<hw::InOutType>(type))
324 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
325 return uarray.getNumElements() == 0 ||
327 if (
auto array = dyn_cast<hw::ArrayType>(type))
328 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
329 if (
auto structType = dyn_cast<hw::StructType>(type))
330 return llvm::all_of(structType.getElements(),
331 [](
auto elem) { return isZeroBitType(elem.type); });
332 if (
auto enumType = dyn_cast<hw::EnumType>(type))
333 return enumType.getFields().empty();
334 if (
auto unionType = dyn_cast<hw::UnionType>(type))
335 return hw::getBitWidth(unionType) == 0;
347 return TypeSwitch<Type, Type>(type)
348 .Case<InOutType>([](InOutType inoutType) {
351 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
354 .Default([](Type type) {
return type; });
359 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
360 auto elementType = cast<hw::InOutType>(type).getElementType();
366 return TypeSwitch<Type, bool>(type)
367 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
370 .Case<StructType>([](
auto) {
return true; })
371 .Default([](
auto) {
return false; });
385 if (
auto name = lhs.getName().compare(rhs.getName()))
387 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
392 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
394 if (lhs.getLine() != rhs.getLine())
395 return lhs.getLine() < rhs.getLine() ? -1 : 1;
396 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
401 Location lhsCallee = lhs.getCallee();
402 Location rhsCallee = rhs.getCallee();
406 Location lhsCaller = lhs.getCaller();
407 Location rhsCaller = rhs.getCaller();
411template <
typename TTargetLoc>
413 auto lhsT = dyn_cast<TTargetLoc>(lhs);
414 auto rhsT = dyn_cast<TTargetLoc>(rhs);
441 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
446 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
451 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
468 SmallPtrSetImpl<Attribute> &locationSet) {
469 llvm::TypeSwitch<Location, void>(loc)
470 .Case<FusedLoc>([&](
auto fusedLoc) {
471 for (
auto subLoc : fusedLoc.getLocations())
474 .Default([&](
auto loc) { locationSet.insert(loc); });
478template <
typename TVector>
480 llvm::array_pod_sort(
481 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
482 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
490 SmallPtrSet<Attribute, 8> locationSet;
491 locationSet.insert(loc);
492 llvm::raw_string_ostream os(
output);
498 const SmallPtrSetImpl<Operation *> &ops) {
502 SmallPtrSet<Attribute, 8> locationSet;
505 llvm::raw_string_ostream os(
output);
514 const SmallPtrSetImpl<Attribute> &locationSet) {
515 if (style == LoweringOptions::LocationInfoStyle::None)
518 llvm::raw_string_ostream sstr(resstr);
520 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
524 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
525 "other styles must be already handled");
526 os <<
"@[" << resstr <<
"]";
535 const SmallPtrSetImpl<Attribute> &locationSet)
551 bool withName = !loc.getName().empty();
553 os <<
"'" << loc.getName().strref() <<
"'(";
562 os << loc.getFilename().getValue();
563 if (
auto line = loc.getLine()) {
565 if (
auto col = loc.getColumn())
577 StringRef lastFileName;
578 for (
size_t i = 0, e = locVector.size(); i != e;) {
583 auto first = locVector[i];
584 if (first.getFilename() != lastFileName) {
585 lastFileName = first.getFilename();
592 first.getFilename() == locVector[
end].getFilename() &&
593 first.getLine() == locVector[
end].getLine())
598 if (
auto line = first.getLine()) {
600 if (
auto col = first.getColumn())
608 os <<
':' << first.getLine() <<
":{";
610 os << locVector[i++].getColumn();
622 llvm::TypeSwitch<Location, void>(loc)
623 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
625 .Case<mlir::FusedLoc>([&](
auto loc) {
626 SmallPtrSet<Attribute, 8> locationSet;
630 .Default([&](
auto loc) {
642 switch (locationSet.size()) {
653 SmallVector<FileLineColLoc, 8> flcLocs;
654 SmallVector<Attribute, 8> otherLocs;
655 flcLocs.reserve(locationSet.size());
656 otherLocs.reserve(locationSet.size());
657 for (Attribute loc : locationSet) {
658 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
659 flcLocs.push_back(flcLoc);
661 otherLocs.push_back(loc);
672 size_t sstrSize =
os.tell();
673 bool emittedAnything =
false;
674 auto recheckEmittedSomething = [&]() {
675 size_t currSize =
os.tell();
676 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
677 emittedAnything |= emittedSomethingSinceLastCheck;
679 return emittedSomethingSinceLastCheck;
688 if (recheckEmittedSomething()) {
690 recheckEmittedSomething();
696 if (emittedAnything && !flcLocs.empty())
701 llvm::raw_string_ostream &
os;
713 if (isa<BlockArgument>(v))
722 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
727 if (v.getDefiningOp<ReadInterfaceSignalOp>())
740 if (
auto cast = dyn_cast<BitcastOp>(op))
741 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
743 [&](Location loc) { return emitError(loc); })) {
746 if (op->hasOneUse() &&
747 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
755 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
761 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
765 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
766 if (verbatim.getFormatString().size() > 32)
771 for (
auto &use : op->getUses()) {
772 auto *user = use.getOwner();
782 UnionExtractOp, IndexedPartSelectOp>(user))
783 if (use.getOperandNumber() == 0 &&
794 auto usedInExprControl = [user, &use]() {
795 return TypeSwitch<Operation *, bool>(user)
796 .Case<ltl::ClockOp>([&](
auto clockOp) {
798 return clockOp.getClock() == use.get();
800 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
801 sv::CoverConcurrentOp>(
802 [&](
auto op) {
return op.getClock() == use.get(); })
803 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
804 sv::CoverPropertyOp>([&](
auto op) {
805 return op.getDisable() == use.get() || op.getClock() == use.get();
807 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
812 .Default([](
auto) {
return false; });
815 if (!usedInExprControl())
819 auto read = dyn_cast<ReadInOutOp>(op);
822 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
833 unsigned numStatements = 0;
834 block.walk([&](Operation *op) {
836 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
837 return WalkResult::advance();
839 TypeSwitch<Operation *, unsigned>(op)
840 .Case<VerbatimOp>([&](
auto) {
846 .Case<IfOp>([&](
auto) {
857 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
858 .Case<OutputOp>([&](OutputOp oop) {
861 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
862 Operation *op = operand.getDefiningOp();
863 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
866 .Default([](
auto) {
return 1; });
867 if (numStatements > 1)
868 return WalkResult::interrupt();
869 return WalkResult::advance();
871 if (numStatements == 0)
873 if (numStatements == 1)
883 if (op->getResult(0).use_empty())
888 if (op->hasOneUse() &&
889 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
890 *op->getUsers().begin()))
912 for (
auto &op : *elseBlock) {
913 if (
auto opIf = dyn_cast<IfOp>(op)) {
930template <
typename PPS>
932 enum Container { NoContainer, InComment, InAttr };
933 Container currentContainer = NoContainer;
935 auto closeContainer = [&] {
936 if (currentContainer == NoContainer)
938 if (currentContainer == InComment)
940 else if (currentContainer == InAttr)
942 ps << PP::end << PP::end;
944 currentContainer = NoContainer;
947 bool isFirstContainer =
true;
948 auto openContainer = [&](Container newContainer) {
949 assert(newContainer != NoContainer);
950 if (currentContainer == newContainer)
954 if (!isFirstContainer)
955 ps << (mayBreak ? PP::space : PP::nbsp);
956 isFirstContainer =
false;
959 if (newContainer == InComment)
961 else if (newContainer == InAttr)
963 currentContainer = newContainer;
971 ps.scopedBox(PP::cbox0, [&]() {
972 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
973 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
975 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
977 if (attr.getExpression())
978 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
987 if (
auto *op = val.getDefiningOp())
990 if (
auto port = dyn_cast<BlockArgument>(val)) {
992 auto parent = port.getParentBlock()->getParentOp();
993 if (isa<ForOp, GenerateForOp>(parent))
994 return parent->getAttrOfType<StringAttr>(
"hw.verilogName");
996 port.getArgNumber());
998 assert(
false &&
"unhandled value");
1010class VerilogEmitterState {
1012 explicit VerilogEmitterState(ModuleOp designOp,
1018 llvm::formatted_raw_ostream &os,
1019 StringAttr fileName,
OpLocMap &verilogLocMap)
1020 : designOp(designOp), shared(shared), options(options),
1021 symbolCache(symbolCache), globalNames(globalNames),
1022 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1023 pp(os, options.emittedLineLength), fileName(fileName) {
1024 pp.setListener(&saver);
1047 llvm::formatted_raw_ostream &os;
1049 bool encounteredError =
false;
1058 bool pendingNewline =
false;
1072 StringAttr fileName;
1078 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1081 verilogLocMap.
clear();
1085 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1086 void operator=(
const VerilogEmitterState &) =
delete;
1099using CallbackDataTy = std::pair<Operation *, bool>;
1103 VerilogEmitterState &state;
1108 explicit EmitterBase(VerilogEmitterState &state)
1110 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1112 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1113 state.encounteredError =
true;
1114 return op->emitError(message);
1117 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1118 state.encounteredError =
true;
1119 return op->emitOpError(message);
1122 InFlightDiagnostic emitError(Location loc,
const Twine &message =
"") {
1123 state.encounteredError =
true;
1124 return mlir::emitError(loc, message);
1127 void emitLocationImpl(llvm::StringRef location) {
1130 ps << PP::neverbreak;
1131 if (!location.empty())
1132 ps <<
"\t// " << location;
1135 void emitLocationInfo(Location loc) {
1143 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1146 setPendingNewline();
1149 template <
typename PPS>
1150 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1151 llvm::function_ref<
void(Value)> operandEmitter,
1152 ArrayAttr symAttrs);
1158 void emitComment(StringAttr comment);
1162 void emitPendingNewlineIfNeeded() {
1163 if (state.pendingNewline) {
1164 state.pendingNewline =
false;
1168 void setPendingNewline() {
1169 assert(!state.pendingNewline);
1170 state.pendingNewline =
true;
1173 void startStatement() { emitPendingNewlineIfNeeded(); }
1176 void operator=(
const EmitterBase &) =
delete;
1177 EmitterBase(
const EmitterBase &) =
delete;
1181template <
typename PPS>
1182void EmitterBase::emitTextWithSubstitutions(
1183 PPS &ps, StringRef
string, Operation *op,
1184 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1195 if (
auto *itemOp = item.getOp()) {
1196 if (item.hasPort()) {
1200 if (!symOpName.empty())
1202 emitError(itemOp,
"cannot get name for symbol ") << sym;
1204 emitError(op,
"cannot get name for symbol ") << sym;
1206 return StringRef(
"<INVALID>");
1212 unsigned numSymOps = symAttrs.size();
1213 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1216 next =
string.find(
"{{", next);
1217 if (next == StringRef::npos)
1224 while (next <
string.size() &&
isdigit(
string[next]))
1227 if (start == next) {
1231 size_t operandNoLength = next - start;
1234 StringRef fmtOptsStr;
1235 if (
string[next] ==
':') {
1236 size_t startFmtOpts = next + 1;
1237 while (next <
string.size() &&
string[next] !=
'}')
1239 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1243 if (!
string.substr(next).starts_with(
"}}"))
1247 unsigned operandNo = 0;
1248 if (
string.drop_front(start)
1249 .take_front(operandNoLength)
1250 .getAsInteger(10, operandNo)) {
1251 emitError(op,
"operand substitution too large");
1257 auto before =
string.take_front(start - 2);
1258 if (!before.empty())
1263 if (operandNo < op->getNumOperands())
1265 operandEmitter(op->getOperand(operandNo));
1266 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1267 unsigned symOpNum = operandNo - op->getNumOperands();
1268 auto sym = symAttrs[symOpNum];
1269 StringRef symVerilogName;
1270 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1271 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1272 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1273 auto namepath = globalRef.getNamepathAttr().getValue();
1274 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1277 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1279 auto innerRef = cast<InnerRefAttr>(sym);
1280 auto ref = state.symbolCache.getInnerDefinition(
1281 innerRef.getModule(), innerRef.getName());
1282 ps << namify(innerRef, ref);
1285 symVerilogName = namify(sym, symOp);
1288 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1289 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1291 symVerilogName = namify(sym, symOp);
1293 if (!symVerilogName.empty())
1296 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1300 string =
string.drop_front(next);
1306 while (emitUntilSubstitution())
1310 if (!
string.
empty())
1314void EmitterBase::emitComment(StringAttr comment) {
1321 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1325 auto ref = comment.getValue();
1327 while (!ref.empty()) {
1328 std::tie(line, ref) = ref.split(
"\n");
1335 if (line.size() <= lineLength) {
1337 setPendingNewline();
1348 auto breakPos = line.rfind(
' ', lineLength);
1350 if (breakPos == StringRef::npos) {
1351 breakPos = line.find(
' ', lineLength);
1354 if (breakPos == StringRef::npos)
1355 breakPos = line.size();
1362 setPendingNewline();
1363 breakPos = line.find_first_not_of(
' ', breakPos);
1365 if (breakPos == StringRef::npos)
1368 line = line.drop_front(breakPos);
1378 bool addPrefixUnderScore =
true;
1381 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1385 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1387 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1389 result = StringAttr::get(expr.getContext(), name);
1391 }
else if (
auto *op = expr.getDefiningOp()) {
1393 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1395 result = StringAttr::get(expr.getContext(), name);
1397 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1403 addPrefixUnderScore =
false;
1405 TypeSwitch<Operation *>(op)
1408 .Case([&result](VerbatimExprOp verbatim) {
1409 verbatim.getAsmResultNames([&](Value, StringRef name) {
1410 result = StringAttr::get(verbatim.getContext(), name);
1413 .Case([&result](VerbatimExprSEOp verbatim) {
1414 verbatim.getAsmResultNames([&](Value, StringRef name) {
1415 result = StringAttr::get(verbatim.getContext(), name);
1421 if (
auto operandName =
1424 cast<IntegerType>(extract.getType()).getWidth();
1426 result = StringAttr::get(extract.getContext(),
1427 operandName.strref() +
"_" +
1428 Twine(extract.getLowBit()));
1430 result = StringAttr::get(
1431 extract.getContext(),
1432 operandName.strref() +
"_" +
1433 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1434 Twine(extract.getLowBit()));
1442 if (!result || result.strref().empty())
1446 if (addPrefixUnderScore && result.strref().front() !=
'_')
1447 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1459class ModuleEmitter :
public EmitterBase {
1461 explicit ModuleEmitter(VerilogEmitterState &state)
1462 : EmitterBase(state), currentModuleOp(nullptr),
1466 emitPendingNewlineIfNeeded();
1470 void emitParameters(Operation *module, ArrayAttr params);
1471 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1472 bool emitAsTwoStateType =
false);
1475 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1476 void emitFunc(FuncOp);
1479 void emitStatement(Operation *op);
1480 void emitBind(BindOp op);
1481 void emitBindInterface(BindInterfaceOp op);
1483 void emitSVAttributes(Operation *op);
1486 StringRef getVerilogStructFieldName(StringAttr field) {
1487 return fieldNameResolver.getRenamedFieldName(field).getValue();
1494 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1506 bool printPackedType(Type type, raw_ostream &os, Location loc,
1507 Type optionalAliasType = {},
bool implicitIntType =
true,
1508 bool singleBitDefaultType =
true,
1509 bool emitAsTwoStateType =
false);
1513 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1521 function_ref<InFlightDiagnostic()> emitError);
1524 VerilogPrecedence parenthesizeIfLooserThan,
1525 function_ref<InFlightDiagnostic()> emitError);
1531 Operation *currentModuleOp;
1537 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1543 SmallPtrSet<Operation *, 16> assignsInlined;
1552 const ModuleEmitter &emitter) {
1553 if (isa<RegOp>(op)) {
1558 cast<InOutType>(op->getResult(0).getType()).getElementType();
1561 while (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(
elementType))
1563 while (
auto arrayType = hw::type_dyn_cast<ArrayType>(
elementType))
1566 if (isa<StructType, UnionType, EnumType, TypeAliasType>(
elementType))
1571 if (isa<sv::WireOp>(op))
1573 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1574 return "localparam";
1577 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1578 return interface.getInterfaceType().getInterface().getValue();
1586 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1588 if (isa<LogicOp>(op)) {
1594 if (isProcedural && !stripAutomatic)
1595 return hasStruct ?
"automatic" :
"automatic logic";
1596 return hasStruct ?
"" :
"logic";
1603 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1606 assert(!emitter.state.options.disallowLocalVariables &&
1607 "automatic variables not allowed");
1611 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1612 :
"automatic logic";
1619static void emitDim(Attribute width, raw_ostream &os, Location loc,
1620 ModuleEmitter &emitter,
bool downTo) {
1622 os <<
"<<invalid type>>";
1625 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1626 if (intAttr.getValue().isZero()) {
1627 os <<
"/*Zero Width*/";
1632 os << (intAttr.getValue().getZExtValue() - 1);
1642 auto typedAttr = dyn_cast<TypedAttr>(width);
1644 emitter.emitError(loc,
"untyped dimension attribute ") << width;
1648 getIntAttr(loc.getContext(), typedAttr.getType(),
1649 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1650 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1654 emitter.printParamValue(width, os, [loc, &emitter]() {
1655 return emitter.emitError(loc,
"invalid parameter in type");
1663static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1664 ModuleEmitter &emitter) {
1665 for (Attribute width : dims) {
1666 emitDim(width, os, loc, emitter,
true);
1671void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1672 SmallVector<Attribute, 4> dims;
1674 [&](Location loc) {
return this->emitError(loc); });
1705 SmallVectorImpl<Attribute> &dims,
1706 bool implicitIntType,
bool singleBitDefaultType,
1707 ModuleEmitter &emitter,
1708 Type optionalAliasType = {},
1709 bool emitAsTwoStateType =
false) {
1710 return TypeSwitch<Type, bool>(type)
1711 .Case<IntegerType>([&](IntegerType integerType) ->
bool {
1712 if (emitAsTwoStateType && dims.empty()) {
1714 if (!typeName.empty()) {
1719 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1721 getInt32Attr(type.getContext(), integerType.getWidth()));
1723 StringRef typeName =
1724 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1725 if (!typeName.empty()) {
1732 return !dims.empty() || !implicitIntType;
1734 .Case<IntType>([&](IntType intType) {
1735 if (!implicitIntType)
1737 dims.push_back(intType.getWidth());
1741 .Case<ArrayType>([&](ArrayType arrayType) {
1742 dims.push_back(arrayType.getSizeAttr());
1744 implicitIntType, singleBitDefaultType,
1746 emitAsTwoStateType);
1748 .Case<InOutType>([&](InOutType inoutType) {
1750 implicitIntType, singleBitDefaultType,
1752 emitAsTwoStateType);
1754 .Case<EnumType>([&](EnumType enumType) {
1755 assert(enumType.getBitWidth().has_value() &&
1756 "enum type must have bitwidth");
1758 if (enumType.getBitWidth() != 32)
1759 os <<
"bit [" << *enumType.getBitWidth() - 1 <<
":0] ";
1761 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1762 llvm::interleaveComma(
1763 enumType.getFields().getAsRange<StringAttr>(), os,
1764 [&](
auto enumerator) {
1765 os << emitter.fieldNameResolver.getEnumFieldName(
1766 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1771 .Case<StructType>([&](StructType structType) {
1772 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1773 os <<
"/*Zero Width*/";
1776 os <<
"struct packed {";
1777 for (
auto &element : structType.getElements()) {
1779 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1780 <<
": Zero Width;*/ ";
1783 SmallVector<Attribute, 8> structDims;
1788 {}, emitAsTwoStateType);
1789 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1790 emitter.printUnpackedTypePostfix(element.type, os);
1797 .Case<UnionType>([&](UnionType unionType) {
1798 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1799 os <<
"/*Zero Width*/";
1803 int64_t unionWidth = hw::getBitWidth(unionType);
1804 os <<
"union packed {";
1805 for (
auto &element : unionType.getElements()) {
1807 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1808 <<
": Zero Width;*/ ";
1811 int64_t elementWidth = hw::getBitWidth(element.type);
1812 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1814 os <<
" struct packed {";
1815 if (element.offset) {
1816 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1817 << element.offset - 1 <<
":0] "
1818 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1822 SmallVector<Attribute, 8> structDims;
1826 true, emitter, {}, emitAsTwoStateType);
1827 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1828 emitter.printUnpackedTypePostfix(element.type, os);
1832 if (elementWidth + (int64_t)element.offset < unionWidth) {
1833 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1834 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1835 <<
"__post_padding_" << element.name.getValue() <<
";";
1837 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1846 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1847 .Case<ModportType>([&](ModportType modportType) {
1848 auto modportAttr = modportType.getModport();
1849 os << modportAttr.getRootReference().getValue() <<
"."
1850 << modportAttr.getNestedReferences().front().getValue();
1853 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1854 os <<
"<<unexpected unpacked array>>";
1855 emitter.emitError(loc,
"Unexpected unpacked array in packed type ")
1859 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1860 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1862 emitter.emitError(loc,
"unresolvable type reference");
1865 if (typedecl.getType() != typeRef.getInnerType()) {
1866 emitter.emitError(loc,
"declared type did not match aliased type");
1870 os << typedecl.getPreferredName();
1871 emitDims(dims, os, typedecl->getLoc(), emitter);
1874 .Default([&](Type type) {
1875 os <<
"<<invalid type '" << type <<
"'>>";
1876 emitter.emitError(loc,
"value has an unsupported verilog type ")
1893bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1894 Type optionalAliasType,
1895 bool implicitIntType,
1896 bool singleBitDefaultType,
1897 bool emitAsTwoStateType) {
1898 SmallVector<Attribute, 8> packedDimensions;
1900 singleBitDefaultType, *
this, optionalAliasType,
1901 emitAsTwoStateType);
1907void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1908 TypeSwitch<Type, void>(type)
1910 printUnpackedTypePostfix(inoutType.getElementType(), os);
1912 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1913 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1914 : state.designOp->getLoc();
1915 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1917 printUnpackedTypePostfix(arrayType.getElementType(), os);
1919 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1921 printUnpackedTypePostfix(arrayType.getElementType(), os);
1923 .Case<InterfaceType>([&](
auto) {
1937ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1938 function_ref<InFlightDiagnostic()> emitError) {
1939 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1947ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1948 VerilogPrecedence parenthesizeIfLooserThan,
1949 function_ref<InFlightDiagnostic()> emitError) {
1950 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1951 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1952 APInt value = intAttr.getValue();
1956 if (intTy.getWidth() > 32) {
1958 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1962 if (intTy.isSigned())
1963 os << intTy.getWidth() <<
"'sd";
1965 os << intTy.getWidth() <<
"'d";
1967 value.print(os, intTy.isSigned());
1968 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1970 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1972 os.write_escaped(strAttr.getValue());
1974 return {Symbol, IsUnsigned};
1976 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1978 os << fpAttr.getValueAsDouble();
1979 return {Symbol, IsUnsigned};
1981 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1982 os << verbatimParam.getValue().getValue();
1983 return {Symbol, IsUnsigned};
1985 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1987 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1988 parameterRef.getName());
1991 return {Symbol, IsUnsigned};
1995 auto expr = dyn_cast<ParamExprAttr>(value);
1997 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1998 emitError() <<
" = " << value;
1999 return {LowestPrecedence, IsUnsigned};
2002 StringRef operatorStr;
2003 StringRef openStr, closeStr;
2004 VerilogPrecedence subprecedence = LowestPrecedence;
2005 VerilogPrecedence prec;
2006 std::optional<SubExprSignResult> operandSign;
2007 bool isUnary =
false;
2008 bool hasOpenClose =
false;
2010 switch (expr.getOpcode()) {
2012 operatorStr =
" + ";
2013 subprecedence = Addition;
2016 operatorStr =
" * ";
2017 subprecedence = Multiply;
2020 operatorStr =
" & ";
2021 subprecedence = And;
2024 operatorStr =
" | ";
2028 operatorStr =
" ^ ";
2029 subprecedence = Xor;
2032 operatorStr =
" << ";
2033 subprecedence = Shift;
2037 operatorStr =
" >> ";
2038 subprecedence = Shift;
2042 operatorStr =
" >>> ";
2043 subprecedence = Shift;
2044 operandSign = IsSigned;
2047 operatorStr =
" / ";
2048 subprecedence = Multiply;
2049 operandSign = IsUnsigned;
2052 operatorStr =
" / ";
2053 subprecedence = Multiply;
2054 operandSign = IsSigned;
2057 operatorStr =
" % ";
2058 subprecedence = Multiply;
2059 operandSign = IsUnsigned;
2062 operatorStr =
" % ";
2063 subprecedence = Multiply;
2064 operandSign = IsSigned;
2067 openStr =
"$clog2(";
2069 operandSign = IsUnsigned;
2070 hasOpenClose =
true;
2073 case PEO::StrConcat:
2076 hasOpenClose =
true;
2079 subprecedence = LowestPrecedence;
2084 prec = subprecedence;
2087 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2089 assert(isUnary || hasOpenClose ||
2090 !llvm::hasSingleElement(expr.getOperands()));
2097 auto emitOperand = [&](Attribute operand) ->
bool {
2099 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2100 if (operandSign.has_value())
2101 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2104 if (operandSign.has_value()) {
2106 signedness = *operandSign;
2108 return signedness == IsSigned;
2112 if (prec > parenthesizeIfLooserThan)
2121 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2122 for (
auto op : expr.getOperands().drop_front()) {
2125 if (expr.getOpcode() == PEO::Add) {
2126 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2127 const APInt &value = integer.getValue();
2128 if (value.isNegative() && !value.isMinSignedValue()) {
2130 allOperandsSigned &=
2131 emitOperand(IntegerAttr::get(op.getType(), -value));
2138 allOperandsSigned &= emitOperand(op);
2142 if (prec > parenthesizeIfLooserThan) {
2146 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2161class ExprEmitter :
public EmitterBase,
2163 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2168 ExprEmitter(ModuleEmitter &emitter,
2169 SmallPtrSetImpl<Operation *> &emittedExprs)
2170 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2172 ExprEmitter(ModuleEmitter &emitter,
2173 SmallPtrSetImpl<Operation *> &emittedExprs,
2175 : EmitterBase(emitter.state), emitter(emitter),
2176 emittedExprs(emittedExprs), buffer(tokens),
2177 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2178 assert(state.pp.getListener() == &state.saver);
2185 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2186 bool isAssignmentLikeContext) {
2187 assert(localTokens.empty());
2189 ps.scopedBox(PP::ibox0, [&]() {
2192 emitSubExpr(exp, parenthesizeIfLooserThan,
2194 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2196 isAssignmentLikeContext);
2201 if (&buffer.tokens == &localTokens)
2202 buffer.flush(state.pp);
2207 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2208 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2210 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2218 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2219 SubExprSignRequirement signReq = NoRequirement,
2220 bool isSelfDeterminedUnsignedValue =
false,
2221 bool isAssignmentLikeContext =
false);
2225 void emitSVAttributes(Operation *op);
2227 SubExprInfo visitUnhandledExpr(Operation *op);
2228 SubExprInfo visitInvalidComb(Operation *op) {
2231 SubExprInfo visitUnhandledComb(Operation *op) {
2232 return visitUnhandledExpr(op);
2235 return dispatchSVVisitor(op);
2238 return visitUnhandledExpr(op);
2240 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2243 enum EmitBinaryFlags {
2244 EB_RequireSignedOperands = RequireSigned,
2245 EB_RequireUnsignedOperands = RequireUnsigned,
2246 EB_OperandSignRequirementMask = 0x3,
2251 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2255 EB_ForceResultSigned = 0x8,
2260 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2261 const char *syntax,
unsigned emitBinaryFlags = 0);
2263 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2264 bool resultAlwaysUnsigned =
false);
2267 void emitSubExprIBox2(
2268 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2269 ps.scopedBox(PP::ibox2,
2270 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2275 template <
typename Container,
typename EachFn>
2276 void interleaveComma(
const Container &c, EachFn eachFn) {
2277 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2282 void interleaveComma(ValueRange ops) {
2283 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2300 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2302 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2303 CloseFunc closeFn) {
2305 ps.scopedBox(PP::cbox0, [&]() {
2306 interleaveComma(c, eachFn);
2312 template <
typename OpenFunc,
typename CloseFunc>
2313 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2314 return emitBracedList(
2315 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2319 void emitBracedList(ValueRange ops) {
2320 return emitBracedList(
2321 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2325 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2328 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2329 bool printAsPattern, Operation *op);
2331 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2332 ArrayAttr fieldValues,
bool printAsPattern,
2335 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2337 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2338 SubExprInfo visitSV(GetModportOp op);
2339 SubExprInfo visitSV(SystemFunctionOp op);
2340 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2341 SubExprInfo visitSV(XMROp op);
2342 SubExprInfo visitSV(SFormatFOp op);
2343 SubExprInfo visitSV(XMRRefOp op);
2344 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2345 SubExprInfo visitSV(VerbatimExprOp op) {
2346 return visitVerbatimExprOp(op, op.getSymbols());
2348 SubExprInfo visitSV(VerbatimExprSEOp op) {
2349 return visitVerbatimExprOp(op, op.getSymbols());
2351 SubExprInfo visitSV(MacroRefExprOp op);
2352 SubExprInfo visitSV(MacroRefExprSEOp op);
2353 template <
typename MacroTy>
2354 SubExprInfo emitMacroCall(MacroTy op);
2356 SubExprInfo visitSV(ConstantXOp op);
2357 SubExprInfo visitSV(ConstantZOp op);
2358 SubExprInfo visitSV(ConstantStrOp op);
2360 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2361 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2363 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2368 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2369 emitSVAttributes(op);
2372 SubExprInfo visitSV(ArrayIndexInOutOp op);
2373 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2374 SubExprInfo visitSV(IndexedPartSelectOp op);
2375 SubExprInfo visitSV(StructFieldInOutOp op);
2378 SubExprInfo visitSV(SampledOp op);
2381 SubExprInfo visitSV(TimeOp op);
2382 SubExprInfo visitSV(STimeOp op);
2385 using TypeOpVisitor::visitTypeOp;
2387 SubExprInfo visitTypeOp(AggregateConstantOp op);
2389 SubExprInfo visitTypeOp(ParamValueOp op);
2396 SubExprInfo visitTypeOp(StructInjectOp op);
2397 SubExprInfo visitTypeOp(UnionCreateOp op);
2398 SubExprInfo visitTypeOp(UnionExtractOp op);
2399 SubExprInfo visitTypeOp(EnumCmpOp op);
2400 SubExprInfo visitTypeOp(EnumConstantOp op);
2403 using CombinationalVisitor::visitComb;
2404 SubExprInfo visitComb(
MuxOp op);
2405 SubExprInfo visitComb(ReverseOp op);
2406 SubExprInfo visitComb(
AddOp op) {
2407 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2408 return emitBinary(op, Addition,
"+");
2410 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2411 SubExprInfo visitComb(
MulOp op) {
2412 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2413 return emitBinary(op, Multiply,
"*");
2415 SubExprInfo visitComb(
DivUOp op) {
2416 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2418 SubExprInfo visitComb(
DivSOp op) {
2419 return emitBinary(op, Multiply,
"/",
2420 EB_RequireSignedOperands | EB_ForceResultSigned);
2422 SubExprInfo visitComb(
ModUOp op) {
2423 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2425 SubExprInfo visitComb(
ModSOp op) {
2426 return emitBinary(op, Multiply,
"%",
2427 EB_RequireSignedOperands | EB_ForceResultSigned);
2429 SubExprInfo visitComb(
ShlOp op) {
2430 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2432 SubExprInfo visitComb(
ShrUOp op) {
2434 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2436 SubExprInfo visitComb(
ShrSOp op) {
2439 return emitBinary(op, Shift,
">>>",
2440 EB_RequireSignedOperands | EB_ForceResultSigned |
2441 EB_RHS_UnsignedWithSelfDeterminedWidth);
2443 SubExprInfo visitComb(
AndOp op) {
2444 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2445 return emitBinary(op, And,
"&");
2447 SubExprInfo visitComb(
OrOp op) {
2448 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2449 return emitBinary(op, Or,
"|");
2451 SubExprInfo visitComb(
XorOp op) {
2452 if (op.isBinaryNot())
2453 return emitUnary(op,
"~");
2454 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2455 return emitBinary(op, Xor,
"^");
2460 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2462 SubExprInfo visitComb(ReplicateOp op);
2463 SubExprInfo visitComb(
ConcatOp op);
2465 SubExprInfo visitComb(ICmpOp op);
2467 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2468 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2469 "not printed within an assignment-like context");
2470 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2471 "supposed to spill such expressions";
2475 SubExprInfo printStructCreate(
2476 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2478 bool printAsPattern, Operation *op);
2481 ModuleEmitter &emitter;
2488 SubExprSignRequirement signPreference = NoRequirement;
2492 SmallPtrSetImpl<Operation *> &emittedExprs;
2495 SmallVector<Token> localTokens;
2509 bool isAssignmentLikeContext =
false;
2513SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2515 unsigned emitBinaryFlags) {
2517 emitError(op,
"SV attributes emission is unimplemented for the op");
2528 if (emitBinaryFlags & EB_ForceResultSigned)
2529 ps <<
"$signed(" << PP::ibox0;
2530 auto operandSignReq =
2531 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2532 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2534 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2536 ps << lhsSpace << syntax << PP::nbsp;
2543 auto rhsPrec = prec;
2544 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2545 rhsPrec = VerilogPrecedence(prec - 1);
2550 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2551 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2552 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2553 operandSignReq = NoRequirement;
2556 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2557 rhsIsUnsignedValueWithSelfDeterminedWidth);
2561 SubExprSignResult signedness = IsUnsigned;
2562 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2563 signedness = IsSigned;
2565 if (emitBinaryFlags & EB_ForceResultSigned) {
2566 ps << PP::end <<
")";
2567 signedness = IsSigned;
2571 return {prec, signedness};
2574SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2575 bool resultAlwaysUnsigned) {
2577 emitError(op,
"SV attributes emission is unimplemented for the op");
2580 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2584 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2585 resultAlwaysUnsigned ? IsUnsigned : signedness};
2590void ExprEmitter::emitSVAttributes(Operation *op) {
2604 auto concat = value.getDefiningOp<
ConcatOp>();
2605 if (!concat || concat.getNumOperands() != 2)
2608 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2609 if (constant && constant.getValue().isZero())
2610 return concat.getOperand(1);
2620SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2621 VerilogPrecedence parenthesizeIfLooserThan,
2622 SubExprSignRequirement signRequirement,
2623 bool isSelfDeterminedUnsignedValue,
2624 bool isAssignmentLikeContext) {
2626 if (
auto result = dyn_cast<OpResult>(exp))
2627 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2628 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2629 parenthesizeIfLooserThan, signRequirement,
2630 isSelfDeterminedUnsignedValue,
2631 isAssignmentLikeContext);
2635 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2640 auto *op = exp.getDefiningOp();
2644 if (!shouldEmitInlineExpr) {
2647 if (signRequirement == RequireSigned) {
2649 return {Symbol, IsSigned};
2653 return {Symbol, IsUnsigned};
2656 unsigned subExprStartIndex = buffer.tokens.size();
2658 ps.addCallback({op,
true});
2659 llvm::scope_exit done([&]() {
2661 ps.addCallback({op, false});
2667 signPreference = signRequirement;
2669 bool bitCastAdded =
false;
2670 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2672 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2673 ps.addAsString(inType.getWidth());
2674 ps <<
"'(" << PP::ibox0;
2675 bitCastAdded =
true;
2679 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2680 isAssignmentLikeContext);
2681 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2687 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2689 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2691 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2692 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2695 expInfo.signedness = IsSigned;
2696 expInfo.precedence = Selection;
2697 }
else if (signRequirement == RequireUnsigned &&
2698 expInfo.signedness == IsSigned) {
2701 expInfo.signedness = IsUnsigned;
2702 expInfo.precedence = Selection;
2703 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2710 expInfo.precedence = Selection;
2717 emittedExprs.insert(exp.getDefiningOp());
2721SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2722 auto openFn = [&]() {
2724 ps.addAsString(op.getMultiple());
2727 auto closeFn = [&]() { ps <<
"}}"; };
2731 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2732 if (op.getOperand().hasOneUse()) {
2733 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2734 return {Symbol, IsUnsigned};
2737 emitBracedList(op.getOperand(), openFn, closeFn);
2738 return {Symbol, IsUnsigned};
2741SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2742 emitBracedList(op.getOperands());
2743 return {Symbol, IsUnsigned};
2746SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2750 Type toType = op.getType();
2752 toType, op.getInput().getType(), op.getLoc(),
2753 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2755 ps.invokeWithStringOS(
2756 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2759 return emitSubExpr(op.getInput(), LowestPrecedence);
2762SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2763 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2764 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2765 SubExprSignRequirement signop[] = {
2767 NoRequirement, NoRequirement,
2769 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2771 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2773 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2775 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2776 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2779 if (op.isEqualAllOnes())
2780 return emitUnary(op,
"&",
true);
2783 if (op.isNotEqualZero())
2784 return emitUnary(op,
"|",
true);
2786 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2790 result.signedness = IsUnsigned;
2794SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2796 emitError(op,
"SV attributes emission is unimplemented for the op");
2798 unsigned loBit = op.getLowBit();
2799 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2801 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2802 assert((x.precedence == Symbol ||
2804 "should be handled by isExpressionUnableToInline");
2809 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2813 ps.addAsString(hiBit);
2814 if (hiBit != loBit) {
2816 ps.addAsString(loBit);
2819 return {Unary, IsUnsigned};
2822SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2824 emitError(op,
"SV attributes emission is unimplemented for the op");
2826 auto decl = op.getReferencedDecl(state.symbolCache);
2829 return {Selection, IsUnsigned};
2832SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2834 emitError(op,
"SV attributes emission is unimplemented for the op");
2837 ps.scopedBox(PP::ibox0, [&]() {
2839 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2840 [&]() { ps <<
"," << PP::space; });
2843 return {Symbol, IsUnsigned};
2846SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2848 emitError(op,
"SV attributes emission is unimplemented for the op");
2850 auto decl = op.getReferencedDecl(state.symbolCache);
2854 return {Selection, IsUnsigned};
2857SubExprInfo ExprEmitter::visitSV(XMROp op) {
2859 emitError(op,
"SV attributes emission is unimplemented for the op");
2861 if (op.getIsRooted())
2863 for (
auto s : op.getPath())
2864 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2866 return {Selection, IsUnsigned};
2871SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2873 emitError(op,
"SV attributes emission is unimplemented for the op");
2876 auto globalRef = op.getReferencedPath(&state.symbolCache);
2877 auto namepath = globalRef.getNamepathAttr().getValue();
2878 auto *
module = state.symbolCache.getDefinition(
2879 cast<InnerRefAttr>(namepath.front()).getModule());
2881 for (
auto sym : namepath) {
2883 auto innerRef = cast<InnerRefAttr>(sym);
2884 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2885 innerRef.getName());
2886 if (ref.hasPort()) {
2892 auto leaf = op.getVerbatimSuffixAttr();
2893 if (leaf && leaf.size())
2895 return {Selection, IsUnsigned};
2898SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2900 emitError(op,
"SV attributes emission is unimplemented for the op");
2902 emitTextWithSubstitutions(
2903 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2904 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2906 return {Unary, IsUnsigned};
2909template <
typename MacroTy>
2910SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2912 emitError(op,
"SV attributes emission is unimplemented for the op");
2915 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2916 assert(macroOp &&
"Invalid IR");
2918 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2920 if (!op.getInputs().empty()) {
2922 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2923 emitExpression(val, LowestPrecedence, false);
2927 return {LowestPrecedence, IsUnsigned};
2930SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2931 return emitMacroCall(op);
2934SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2935 return emitMacroCall(op);
2938SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2940 emitError(op,
"SV attributes emission is unimplemented for the op");
2942 ps.addAsString(op.getWidth());
2944 return {Unary, IsUnsigned};
2947SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2949 emitError(op,
"SV attributes emission is unimplemented for the op");
2951 ps.writeQuotedEscaped(op.getStr());
2952 return {Symbol, IsUnsigned};
2955SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2957 emitError(op,
"SV attributes emission is unimplemented for the op");
2959 ps.addAsString(op.getWidth());
2961 return {Unary, IsUnsigned};
2964SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2965 bool isNegated =
false;
2968 if (signPreference == RequireSigned && value.isNegative() &&
2969 !value.isMinSignedValue()) {
2974 ps.addAsString(type.getWidth());
2978 if (signPreference == RequireSigned)
2984 SmallString<32> valueStr;
2986 (-value).toStringUnsigned(valueStr, 16);
2988 value.toStringUnsigned(valueStr, 16);
2991 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2994SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2996 emitError(op,
"SV attributes emission is unimplemented for the op");
2998 auto value = op.getValue();
3002 if (value.getBitWidth() == 0) {
3003 emitOpError(op,
"will not emit zero width constants in the general case");
3004 ps <<
"<<unsupported zero width constant: "
3005 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3006 return {Unary, IsUnsigned};
3009 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3012void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3013 bool printAsPattern, Operation *op) {
3014 if (printAsPattern && !isAssignmentLikeContext)
3015 emitAssignmentPatternContextError(op);
3016 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3019 elementValues, [&]() { ps << openDelim; },
3020 [&](Attribute elementValue) {
3021 printConstantAggregate(elementValue,
elementType, op);
3023 [&]() { ps <<
"}"; });
3026void ExprEmitter::printConstantStruct(
3027 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3028 bool printAsPattern, Operation *op) {
3029 if (printAsPattern && !isAssignmentLikeContext)
3030 emitAssignmentPatternContextError(op);
3037 auto fieldRange = llvm::make_filter_range(
3038 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3043 if (printAsPattern) {
3045 fieldRange, [&]() { ps <<
"'{"; },
3046 [&](
const auto &fieldAndValue) {
3047 ps.scopedBox(PP::ibox2, [&]() {
3048 const auto &[field, value] = fieldAndValue;
3049 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3050 <<
":" << PP::space;
3051 printConstantAggregate(value, field.type, op);
3054 [&]() { ps <<
"}"; });
3057 fieldRange, [&]() { ps <<
"{"; },
3058 [&](
const auto &fieldAndValue) {
3059 ps.scopedBox(PP::ibox2, [&]() {
3060 const auto &[field, value] = fieldAndValue;
3061 printConstantAggregate(value, field.type, op);
3064 [&]() { ps <<
"}"; });
3068void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3071 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3072 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3073 isAssignmentLikeContext, op);
3076 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3077 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3081 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3082 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3083 isAssignmentLikeContext, op);
3085 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3086 auto value = cast<IntegerAttr>(attr).getValue();
3087 printConstantScalar(value, intType);
3091 emitOpError(op,
"contains constant of type ")
3092 << type <<
" which cannot be emitted as Verilog";
3095SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3097 emitError(op,
"SV attributes emission is unimplemented for the op");
3101 "zero-bit types not allowed at this point");
3103 printConstantAggregate(op.getFields(), op.getType(), op);
3104 return {Symbol, IsUnsigned};
3107SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3109 emitError(op,
"SV attributes emission is unimplemented for the op");
3111 return ps.invokeWithStringOS([&](
auto &os) {
3112 return emitter.printParamValue(op.getValue(), os, [&]() {
3113 return op->emitOpError(
"invalid parameter use");
3122 emitError(op,
"SV attributes emission is unimplemented for the op");
3124 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3126 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3128 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3130 ps.addAsString(dstWidth);
3132 return {Selection, arrayPrec.signedness};
3135SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3136 emitSubExpr(op.getInput(), Selection);
3141 emitSubExpr(op.getIndex(), LowestPrecedence);
3143 emitSVAttributes(op);
3144 return {Selection, IsUnsigned};
3150 emitError(op,
"SV attributes emission is unimplemented for the op");
3152 if (op.isUniform()) {
3154 ps.addAsString(op.getInputs().size());
3156 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3160 op.getInputs(), [&]() { ps <<
"{"; },
3163 emitSubExprIBox2(v);
3166 [&]() { ps <<
"}"; });
3168 return {Unary, IsUnsigned};
3171SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3173 emitError(op,
"SV attributes emission is unimplemented for the op");
3176 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3177 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3178 return {Unary, IsUnsigned};
3183 emitError(op,
"SV attributes emission is unimplemented for the op");
3185 emitBracedList(op.getOperands());
3186 return {Unary, IsUnsigned};
3189SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3191 emitError(op,
"SV attributes emission is unimplemented for the op");
3193 auto index = op.getIndex();
3194 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3199 emitSubExpr(index, LowestPrecedence);
3201 return {Selection, arrayPrec.signedness};
3204SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3206 emitError(op,
"SV attributes emission is unimplemented for the op");
3208 auto prec = emitSubExpr(op.getInput(), Selection);
3210 emitSubExpr(op.getBase(), LowestPrecedence);
3211 if (op.getDecrement())
3215 ps.addAsString(op.getWidth());
3217 return {Selection, prec.signedness};
3220SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3222 emitError(op,
"SV attributes emission is unimplemented for the op");
3224 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3226 emitSubExpr(op.getBase(), LowestPrecedence);
3227 if (op.getDecrement())
3231 ps.addAsString(op.getWidth());
3236SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3238 emitError(op,
"SV attributes emission is unimplemented for the op");
3240 auto prec = emitSubExpr(op.getInput(), Selection);
3242 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3243 return {Selection, prec.signedness};
3246SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3248 emitError(op,
"SV attributes emission is unimplemented for the op");
3251 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3256SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3258 emitError(op,
"SV attributes emission is unimplemented for the op");
3261 ps.scopedBox(PP::ibox0, [&]() {
3262 ps.writeQuotedEscaped(op.getFormatString());
3269 for (
auto operand : op.getSubstitutions()) {
3270 ps <<
"," << PP::space;
3271 emitSubExpr(operand, LowestPrecedence);
3275 return {Symbol, IsUnsigned};
3278SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3280 emitError(op,
"SV attributes emission is unimplemented for the op");
3283 return {Symbol, IsUnsigned};
3286SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3288 emitError(op,
"SV attributes emission is unimplemented for the op");
3291 return {Symbol, IsUnsigned};
3294SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3308 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3309 ps.scopedBox(PP::ibox0, [&]() {
3310 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3314 emitSVAttributes(op);
3316 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3317 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3321 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3322 return emitSubExpr(op.getFalseValue(), Conditional);
3325 SubExprSignResult signedness = IsUnsigned;
3326 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3327 signedness = IsSigned;
3329 return {Conditional, signedness};
3333SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3335 emitError(op,
"SV attributes emission is unimplemented for the op");
3338 emitSubExpr(op.getInput(), LowestPrecedence);
3341 return {Symbol, IsUnsigned};
3344SubExprInfo ExprEmitter::printStructCreate(
3345 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3347 bool printAsPattern, Operation *op) {
3348 if (printAsPattern && !isAssignmentLikeContext)
3349 emitAssignmentPatternContextError(op);
3352 auto filteredFields = llvm::make_filter_range(
3353 llvm::enumerate(fieldInfos),
3354 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3356 if (printAsPattern) {
3358 filteredFields, [&]() { ps <<
"'{"; },
3359 [&](
const auto &field) {
3360 ps.scopedBox(PP::ibox2, [&]() {
3362 emitter.getVerilogStructFieldName(field.value().name))
3363 <<
":" << PP::space;
3364 fieldFn(field.value(), field.index());
3367 [&]() { ps <<
"}"; });
3370 filteredFields, [&]() { ps <<
"{"; },
3371 [&](
const auto &field) {
3372 ps.scopedBox(PP::ibox2,
3373 [&]() { fieldFn(field.value(), field.index()); });
3375 [&]() { ps <<
"}"; });
3378 return {Selection, IsUnsigned};
3383 emitError(op,
"SV attributes emission is unimplemented for the op");
3387 bool printAsPattern = isAssignmentLikeContext;
3388 StructType structType = op.getType();
3389 return printStructCreate(
3390 structType.getElements(),
3391 [&](
const auto &field,
auto index) {
3392 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3394 isAssignmentLikeContext);
3396 printAsPattern, op);
3401 emitError(op,
"SV attributes emission is unimplemented for the op");
3403 emitSubExpr(op.getInput(), Selection);
3405 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3406 return {Selection, IsUnsigned};
3409SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3411 emitError(op,
"SV attributes emission is unimplemented for the op");
3415 bool printAsPattern = isAssignmentLikeContext;
3416 StructType structType = op.getType();
3417 return printStructCreate(
3418 structType.getElements(),
3419 [&](
const auto &field,
auto index) {
3420 if (field.name == op.getFieldNameAttr()) {
3421 emitSubExpr(op.getNewValue(), Selection);
3423 emitSubExpr(op.getInput(), Selection);
3425 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3428 printAsPattern, op);
3431SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3432 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3433 return {Selection, IsUnsigned};
3436SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3438 emitError(op,
"SV attributes emission is unimplemented for the op");
3439 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3442 result.signedness = IsUnsigned;
3446SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3448 emitError(op,
"SV attributes emission is unimplemented for the op");
3452 auto unionWidth = hw::getBitWidth(unionType);
3453 auto &element = unionType.getElements()[op.getFieldIndex()];
3454 auto elementWidth = hw::getBitWidth(element.type);
3457 if (!elementWidth) {
3458 ps.addAsString(unionWidth);
3460 return {Unary, IsUnsigned};
3464 if (elementWidth == unionWidth) {
3465 emitSubExpr(op.getInput(), LowestPrecedence);
3466 return {Unary, IsUnsigned};
3471 ps.scopedBox(PP::ibox0, [&]() {
3472 if (
auto prePadding = element.offset) {
3473 ps.addAsString(prePadding);
3474 ps <<
"'h0," << PP::space;
3476 emitSubExpr(op.getInput(), Selection);
3477 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3478 ps <<
"," << PP::space;
3479 ps.addAsString(postPadding);
3485 return {Unary, IsUnsigned};
3488SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3490 emitError(op,
"SV attributes emission is unimplemented for the op");
3491 emitSubExpr(op.getInput(), Selection);
3494 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3495 auto unionWidth = hw::getBitWidth(unionType);
3496 auto &element = unionType.getElements()[op.getFieldIndex()];
3497 auto elementWidth = hw::getBitWidth(element.type);
3498 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3499 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3508 return {Selection, IsUnsigned};
3511SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3512 emitOpError(op,
"cannot emit this expression to Verilog");
3513 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3515 return {Symbol, IsUnsigned};
3531enum class PropertyPrecedence {
3551struct EmittedProperty {
3553 PropertyPrecedence precedence;
3558class PropertyEmitter :
public EmitterBase,
3559 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3563 PropertyEmitter(ModuleEmitter &emitter,
3564 SmallPtrSetImpl<Operation *> &emittedOps)
3565 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3566 PropertyEmitter(ModuleEmitter &emitter,
3567 SmallPtrSetImpl<Operation *> &emittedOps,
3569 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3571 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3572 assert(state.pp.getListener() == &state.saver);
3575 void emitAssertPropertyDisable(
3576 Value property, Value disable,
3577 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3579 void emitAssertPropertyBody(
3580 Value property, Value disable,
3581 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3583 void emitAssertPropertyBody(
3584 Value property, sv::EventControl event, Value clock, Value disable,
3585 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3590 emitNestedProperty(Value property,
3591 PropertyPrecedence parenthesizeIfLooserThan);
3592 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3593 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3595 EmittedProperty visitUnhandledLTL(Operation *op);
3596 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3597 EmittedProperty visitLTL(ltl::AndOp op);
3598 EmittedProperty visitLTL(ltl::OrOp op);
3599 EmittedProperty visitLTL(ltl::IntersectOp op);
3600 EmittedProperty visitLTL(ltl::DelayOp op);
3601 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3602 EmittedProperty visitLTL(ltl::ConcatOp op);
3603 EmittedProperty visitLTL(ltl::RepeatOp op);
3604 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3605 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3606 EmittedProperty visitLTL(ltl::NotOp op);
3607 EmittedProperty visitLTL(ltl::ImplicationOp op);
3608 EmittedProperty visitLTL(ltl::UntilOp op);
3609 EmittedProperty visitLTL(ltl::EventuallyOp op);
3610 EmittedProperty visitLTL(ltl::ClockOp op);
3612 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3613 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3614 void emitLTLConcat(ValueRange inputs);
3617 ModuleEmitter &emitter;
3622 SmallPtrSetImpl<Operation *> &emittedOps;
3625 SmallVector<Token> localTokens;
3638void PropertyEmitter::emitAssertPropertyDisable(
3639 Value property, Value disable,
3640 PropertyPrecedence parenthesizeIfLooserThan) {
3643 ps <<
"disable iff" << PP::nbsp <<
"(";
3645 emitNestedProperty(disable, PropertyPrecedence::Unary);
3651 ps.scopedBox(PP::ibox0,
3652 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3658void PropertyEmitter::emitAssertPropertyBody(
3659 Value property, Value disable,
3660 PropertyPrecedence parenthesizeIfLooserThan) {
3661 assert(localTokens.empty());
3663 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3668 if (&buffer.tokens == &localTokens)
3669 buffer.flush(state.pp);
3672void PropertyEmitter::emitAssertPropertyBody(
3673 Value property, sv::EventControl event, Value clock, Value disable,
3674 PropertyPrecedence parenthesizeIfLooserThan) {
3675 assert(localTokens.empty());
3678 ps.scopedBox(PP::ibox2, [&] {
3679 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3680 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3686 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3691 if (&buffer.tokens == &localTokens)
3692 buffer.flush(state.pp);
3695EmittedProperty PropertyEmitter::emitNestedProperty(
3696 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3706 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3707 ExprEmitter(emitter, emittedOps, buffer.tokens)
3708 .emitExpression(property, LowestPrecedence,
3710 return {PropertyPrecedence::Symbol};
3713 unsigned startIndex = buffer.tokens.size();
3714 auto info = dispatchLTLVisitor(property.getDefiningOp());
3719 if (
info.precedence > parenthesizeIfLooserThan) {
3721 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3722 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3724 ps << PP::end <<
")";
3726 info.precedence = PropertyPrecedence::Symbol;
3730 emittedOps.insert(property.getDefiningOp());
3734EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3735 emitOpError(op,
"emission as Verilog property or sequence not supported");
3736 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3737 return {PropertyPrecedence::Symbol};
3740EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3742 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3743 return {PropertyPrecedence::Symbol};
3746EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3749 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3750 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3751 return {PropertyPrecedence::And};
3754EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3757 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3758 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3759 return {PropertyPrecedence::Or};
3762EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3766 emitNestedProperty(input, PropertyPrecedence::Intersect);
3768 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3769 return {PropertyPrecedence::Intersect};
3772void PropertyEmitter::emitLTLDelay(int64_t delay,
3773 std::optional<int64_t> length) {
3777 ps.addAsString(delay);
3780 ps.addAsString(delay);
3782 ps.addAsString(delay + *length);
3788 }
else if (delay == 1) {
3792 ps.addAsString(delay);
3798void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3800 ps.scopedBox(PP::ibox2, [&] {
3801 ps <<
PPExtString(stringifyClockEdge(edge)) << PP::space;
3802 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3807EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3808 emitLTLDelay(op.getDelay(), op.getLength());
3810 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3811 return {PropertyPrecedence::Concat};
3814EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3815 emitLTLClockingEvent(op.getEdge(), op.getClock());
3817 emitLTLDelay(op.getDelay(), op.getLength());
3819 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3820 return {PropertyPrecedence::Clocking};
3823void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3824 bool addSeparator =
false;
3825 for (
auto input : inputs) {
3828 if (!input.getDefiningOp<ltl::DelayOp>())
3829 ps <<
"##0" << PP::space;
3831 addSeparator =
true;
3832 emitNestedProperty(input, PropertyPrecedence::Concat);
3836EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3837 emitLTLConcat(op.getInputs());
3838 return {PropertyPrecedence::Concat};
3841EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3842 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3843 if (
auto more = op.getMore()) {
3845 ps.addAsString(op.getBase());
3848 ps.addAsString(op.getBase() + *more);
3852 if (op.getBase() == 0) {
3854 }
else if (op.getBase() == 1) {
3858 ps.addAsString(op.getBase());
3862 return {PropertyPrecedence::Repeat};
3865EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3866 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3868 auto more = op.getMore();
3870 ps.addAsString(op.getBase());
3873 ps.addAsString(op.getBase() + more);
3877 return {PropertyPrecedence::Repeat};
3880EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3881 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3883 auto more = op.getMore();
3885 ps.addAsString(op.getBase());
3888 ps.addAsString(op.getBase() + more);
3892 return {PropertyPrecedence::Repeat};
3895EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3896 ps <<
"not" << PP::space;
3897 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3898 return {PropertyPrecedence::Unary};
3904 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3905 if (!concatOp || concatOp.getInputs().size() < 2)
3907 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3908 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3910 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3911 if (!constOp || !constOp.getValue().isOne())
3913 return concatOp.getInputs().drop_back();
3916EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3920 emitLTLConcat(range);
3921 ps << PP::space <<
"|=>" << PP::nbsp;
3923 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3924 ps << PP::space <<
"|->" << PP::nbsp;
3926 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3927 return {PropertyPrecedence::Implication};
3930EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3931 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3932 ps << PP::space <<
"until" << PP::space;
3933 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3934 return {PropertyPrecedence::Until};
3937EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3938 ps <<
"s_eventually" << PP::space;
3939 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3940 return {PropertyPrecedence::Qualifier};
3943EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3944 emitLTLClockingEvent(op.getEdge(), op.getClock());
3946 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3947 return {PropertyPrecedence::Clocking};
3957class NameCollector {
3959 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3963 void collectNames(Block &block);
3965 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3966 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3969 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3970 ModuleEmitter &moduleEmitter;
3975 static constexpr size_t maxTypeWidthBound = 32;
3980void NameCollector::collectNames(Block &block) {
3983 for (
auto &op : block) {
3987 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
3990 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3994 for (
auto result : op.getResults()) {
3996 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3997 SmallString<16> typeString;
4001 llvm::raw_svector_ostream stringStream(typeString);
4003 stringStream, op.getLoc());
4005 if (typeString.size() <= maxTypeWidthBound)
4006 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4013 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4014 for (
auto ®ion : op.getRegions()) {
4015 if (!region.empty())
4016 collectNames(region.front());
4030class StmtEmitter :
public EmitterBase,
4038 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4040 void emitStatement(Operation *op);
4041 void emitStatementBlock(Block &body);
4044 LogicalResult emitDeclaration(Operation *op);
4047 void collectNamesAndCalculateDeclarationWidths(Block &block);
4050 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4051 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4052 bool isAssignmentLikeContext =
false);
4053 void emitSVAttributes(Operation *op);
4056 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4059 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4063 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4064 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4065 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4066 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4067 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4068 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4070 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4071 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4072 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4073 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4074 template <
typename Op>
4077 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4078 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4079 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4081 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4082 LogicalResult visitSV(
AssignOp op);
4083 LogicalResult visitSV(BPAssignOp op);
4084 LogicalResult visitSV(PAssignOp op);
4085 LogicalResult visitSV(ForceOp op);
4086 LogicalResult visitSV(ReleaseOp op);
4087 LogicalResult visitSV(AliasOp op);
4088 LogicalResult visitSV(InterfaceInstanceOp op);
4089 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4090 LogicalResult visitStmt(OutputOp op);
4092 LogicalResult visitStmt(InstanceOp op);
4093 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4094 ArrayRef<Value> instPortValues);
4099 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4100 LogicalResult visitSV(OrderedOutputOp op);
4101 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4102 LogicalResult visitSV(IfDefProceduralOp op) {
4103 return emitIfDef(op, op.getCond());
4105 LogicalResult visitSV(IfOp op);
4106 LogicalResult visitSV(AlwaysOp op);
4107 LogicalResult visitSV(AlwaysCombOp op);
4108 LogicalResult visitSV(AlwaysFFOp op);
4109 LogicalResult visitSV(InitialOp op);
4110 LogicalResult visitSV(CaseOp op);
4111 template <
typename OpTy,
typename EmitPrefixFn>
4113 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4114 ValueRange substitutions, EmitPrefixFn emitPrefix);
4115 LogicalResult visitSV(WriteOp op);
4116 LogicalResult visitSV(FWriteOp op);
4117 LogicalResult visitSV(FFlushOp op);
4118 LogicalResult visitSV(VerbatimOp op);
4119 LogicalResult visitSV(MacroRefOp op);
4121 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4122 std::optional<unsigned> verbosity);
4123 LogicalResult visitSV(StopOp op);
4124 LogicalResult visitSV(FinishOp op);
4125 LogicalResult visitSV(ExitOp op);
4127 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4128 std::optional<unsigned> verbosity,
4130 ValueRange operands);
4133 template <
typename OpTy>
4134 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4135 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4136 op.getMessageAttr(), op.getSubstitutions());
4140 template <
typename OpTy>
4141 LogicalResult emitFatalMessageOp(OpTy op) {
4142 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4143 op.getMessageAttr(), op.getSubstitutions());
4146 LogicalResult visitSV(FatalProceduralOp op);
4147 LogicalResult visitSV(FatalOp op);
4148 LogicalResult visitSV(ErrorProceduralOp op);
4149 LogicalResult visitSV(WarningProceduralOp op);
4150 LogicalResult visitSV(InfoProceduralOp op);
4151 LogicalResult visitSV(ErrorOp op);
4152 LogicalResult visitSV(WarningOp op);
4153 LogicalResult visitSV(InfoOp op);
4155 LogicalResult visitSV(ReadMemOp op);
4157 LogicalResult visitSV(GenerateOp op);
4158 LogicalResult visitSV(GenerateCaseOp op);
4159 LogicalResult visitSV(GenerateForOp op);
4161 LogicalResult visitSV(
ForOp op);
4163 void emitAssertionLabel(Operation *op);
4164 void emitAssertionMessage(StringAttr message, ValueRange args,
4165 SmallPtrSetImpl<Operation *> &ops,
4167 template <
typename Op>
4168 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4169 LogicalResult visitSV(AssertOp op);
4170 LogicalResult visitSV(AssumeOp op);
4171 LogicalResult visitSV(CoverOp op);
4172 template <
typename Op>
4173 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4174 LogicalResult visitSV(AssertConcurrentOp op);
4175 LogicalResult visitSV(AssumeConcurrentOp op);
4176 LogicalResult visitSV(CoverConcurrentOp op);
4177 template <
typename Op>
4178 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4179 LogicalResult visitSV(AssertPropertyOp op);
4180 LogicalResult visitSV(AssumePropertyOp op);
4181 LogicalResult visitSV(CoverPropertyOp op);
4183 LogicalResult visitSV(BindOp op);
4184 LogicalResult visitSV(InterfaceOp op);
4186 LogicalResult visitSV(InterfaceSignalOp op);
4187 LogicalResult visitSV(InterfaceModportOp op);
4188 LogicalResult visitSV(AssignInterfaceSignalOp op);
4189 LogicalResult visitSV(MacroErrorOp op);
4190 LogicalResult visitSV(MacroDefOp op);
4192 void emitBlockAsStatement(Block *block,
4193 const SmallPtrSetImpl<Operation *> &locationOps,
4194 StringRef multiLineComment = StringRef());
4196 LogicalResult visitSV(FuncDPIImportOp op);
4197 template <
typename CallOp>
4198 LogicalResult emitFunctionCall(CallOp callOp);
4199 LogicalResult visitSV(FuncCallProceduralOp op);
4200 LogicalResult visitSV(FuncCallOp op);
4201 LogicalResult visitSV(ReturnOp op);
4202 LogicalResult visitSV(IncludeOp op);
4205 ModuleEmitter &emitter;
4210 size_t maxDeclNameWidth = 0;
4211 size_t maxTypeWidth = 0;
4222void StmtEmitter::emitExpression(Value exp,
4223 SmallPtrSetImpl<Operation *> &emittedExprs,
4224 VerilogPrecedence parenthesizeIfLooserThan,
4225 bool isAssignmentLikeContext) {
4226 ExprEmitter(emitter, emittedExprs)
4227 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4232void StmtEmitter::emitSVAttributes(Operation *op) {
4240 setPendingNewline();
4243void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4244 llvm::function_ref<
void()> emitRHS,
4246 std::optional<PPExtString> wordBeforeLHS) {
4248 ps.scopedBox(PP::ibox2, [&]() {
4249 if (wordBeforeLHS) {
4250 ps << *wordBeforeLHS << PP::space;
4254 ps << PP::space << syntax << PP::space;
4256 ps.scopedBox(PP::ibox0, [&]() {
4263template <
typename Op>
4265StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4266 std::optional<PPExtString> wordBeforeLHS) {
4267 SmallPtrSet<Operation *, 8> ops;
4271 ps.addCallback({op,
true});
4272 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4274 emitExpression(op.getSrc(), ops, LowestPrecedence,
4279 ps.addCallback({op,
false});
4280 emitLocationInfoAndNewLine(ops);
4284LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4287 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4290 if (emitter.assignsInlined.count(op))
4294 emitSVAttributes(op);
4299LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4300 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4304 if (emitter.assignsInlined.count(op))
4308 emitSVAttributes(op);
4313LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4315 emitSVAttributes(op);
4320LogicalResult StmtEmitter::visitSV(ForceOp op) {
4322 emitError(op,
"SV attributes emission is unimplemented for the op");
4327LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4329 emitError(op,
"SV attributes emission is unimplemented for the op");
4332 SmallPtrSet<Operation *, 8> ops;
4334 ps.addCallback({op,
true});
4335 ps.scopedBox(PP::ibox2, [&]() {
4336 ps <<
"release" << PP::space;
4337 emitExpression(op.getDest(), ops);
4340 ps.addCallback({op,
false});
4341 emitLocationInfoAndNewLine(ops);
4345LogicalResult StmtEmitter::visitSV(AliasOp op) {
4347 emitError(op,
"SV attributes emission is unimplemented for the op");
4350 SmallPtrSet<Operation *, 8> ops;
4352 ps.addCallback({op,
true});
4353 ps.scopedBox(PP::ibox2, [&]() {
4354 ps <<
"alias" << PP::space;
4355 ps.scopedBox(PP::cbox0, [&]() {
4357 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4358 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4362 ps.addCallback({op,
false});
4363 emitLocationInfoAndNewLine(ops);
4367LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4368 auto doNotPrint = op.getDoNotPrint();
4369 if (doNotPrint && !state.options.emitBindComments)
4373 emitError(op,
"SV attributes emission is unimplemented for the op");
4376 StringRef prefix =
"";
4377 ps.addCallback({op,
true});
4380 ps <<
"// This interface is elsewhere emitted as a bind statement."
4384 SmallPtrSet<Operation *, 8> ops;
4387 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4388 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4389 "point to an interface");
4392 if (!prefix.empty())
4398 ps.addCallback({op,
false});
4399 emitLocationInfoAndNewLine(ops);
4407LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4409 SmallPtrSet<Operation *, 8> ops;
4410 size_t operandIndex = 0;
4412 for (
PortInfo port : ports.getOutputs()) {
4413 auto operand = op->getOperand(operandIndex);
4417 if (operand.hasOneUse() && operand.getDefiningOp() &&
4418 isa<InstanceOp>(operand.getDefiningOp())) {
4427 ps.addCallback({op,
true});
4429 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4431 ps <<
"// Zero width: ";
4434 ps <<
"assign" << PP::space;
4436 ps << PP::space <<
"=" << PP::space;
4437 ps.scopedBox(PP::ibox0, [&]() {
4441 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4442 ps <<
"/*Zero width*/";
4444 emitExpression(operand, ops, LowestPrecedence,
4449 ps.addCallback({op,
false});
4450 emitLocationInfoAndNewLine(ops);
4457LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4458 auto parent = op->getParentOfType<PortList>();
4460 return emitOutputLikeOp(op, ports);
4463LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4465 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4466 ps <<
"`ifndef " << typescopeDef << PP::newline;
4467 ps <<
"`define " << typescopeDef;
4468 setPendingNewline();
4469 emitStatementBlock(*op.getBodyBlock());
4471 ps <<
"`endif // " << typescopeDef;
4472 setPendingNewline();
4476LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4478 emitError(op,
"SV attributes emission is unimplemented for the op");
4483 ps << PP::neverbox <<
"// ";
4485 SmallPtrSet<Operation *, 8> ops;
4487 ps.scopedBox(PP::ibox2, [&]() {
4488 ps <<
"typedef" << PP::space;
4489 ps.invokeWithStringOS([&](
auto &os) {
4491 op.getAliasType(),
false);
4493 ps << PP::space <<
PPExtString(op.getPreferredName());
4494 ps.invokeWithStringOS(
4495 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4500 emitLocationInfoAndNewLine(ops);
4504template <
typename CallOpTy>
4505LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4509 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4511 SmallPtrSet<Operation *, 8> ops;
4515 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4516 if (explicitReturn) {
4517 assert(explicitReturn.hasOneUse());
4518 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4519 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4520 emitExpression(bpassignOp.getDest(), ops);
4522 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4523 ps <<
"assign" << PP::nbsp;
4524 emitExpression(assignOp.getDest(), ops);
4526 ps << PP::nbsp <<
"=" << PP::nbsp;
4529 auto arguments = callee.getPortList(
true);
4533 bool needsComma =
false;
4534 auto printArg = [&](Value value) {
4536 ps <<
"," << PP::space;
4537 emitExpression(value, ops);
4541 ps.scopedBox(PP::ibox0, [&] {
4542 unsigned inputIndex = 0, outputIndex = 0;
4543 for (
auto arg : arguments) {
4546 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4548 printArg(op.getInputs()[inputIndex++]);
4553 emitLocationInfoAndNewLine(ops);
4557LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4558 return emitFunctionCall(op);
4561LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4562 return emitFunctionCall(op);
4565template <
typename PPS>
4567 bool isAutomatic =
false,
4568 bool emitAsTwoStateType =
false) {
4569 ps <<
"function" << PP::nbsp;
4571 ps <<
"automatic" << PP::nbsp;
4572 auto retType = op.getExplicitlyReturnedType();
4574 ps.invokeWithStringOS([&](
auto &os) {
4575 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4576 emitAsTwoStateType);
4582 emitter.emitPortList(
4586LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4587 auto parent = op->getParentOfType<sv::FuncOp>();
4589 return emitOutputLikeOp(op, ports);
4592LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4594 ps <<
"`include" << PP::nbsp;
4596 if (op.getStyle() == IncludeStyle::System)
4597 ps <<
"<" << op.getTarget() <<
">";
4599 ps <<
"\"" << op.getTarget() <<
"\"";
4601 emitLocationInfo(op.getLoc());
4602 setPendingNewline();
4606LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4609 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4613 if (
auto linkageName = importOp.getLinkageName())
4614 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4616 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4617 assert(op.isDeclaration() &&
"function must be a declaration");
4620 assert(state.pendingNewline);
4626LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4628 emitError(op,
"SV attributes emission is unimplemented for the op");
4631 SmallPtrSet<Operation *, 8> ops;
4634 ps.addCallback({op,
true});
4636 if (
auto fd = op.getFd())
4637 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4640 ps.addCallback({op,
false});
4641 emitLocationInfoAndNewLine(ops);
4645template <
typename OpTy,
typename EmitPrefixFn>
4646LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4647 StringRef formatString,
4648 ValueRange substitutions,
4649 EmitPrefixFn emitPrefix) {
4651 emitError(op,
"SV attributes emission is unimplemented for the op");
4654 SmallPtrSet<Operation *, 8> ops;
4657 ps.addCallback({op,
true});
4659 ps.scopedBox(PP::ibox0, [&]() {
4661 ps.writeQuotedEscaped(formatString);
4668 for (
auto operand : substitutions) {
4669 ps <<
"," << PP::space;
4670 emitExpression(operand, ops);
4674 ps.addCallback({op,
false});
4675 emitLocationInfoAndNewLine(ops);
4679LogicalResult StmtEmitter::visitSV(WriteOp op) {
4680 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4681 op.getSubstitutions(),
4682 [&](SmallPtrSetImpl<Operation *> &) {});
4685LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4686 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4687 op.getSubstitutions(),
4688 [&](SmallPtrSetImpl<Operation *> &ops) {
4689 emitExpression(op.getFd(), ops);
4690 ps <<
"," << PP::space;
4694LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4696 emitError(op,
"SV attributes emission is unimplemented for the op");
4699 SmallPtrSet<Operation *, 8> ops;
4704 StringRef
string = op.getFormatString();
4705 if (
string.ends_with(
"\n"))
4706 string =
string.drop_back();
4711 bool isFirst =
true;
4714 while (!
string.
empty()) {
4715 auto lhsRhs =
string.split(
'\n');
4719 ps << PP::end << PP::newline << PP::neverbox;
4723 emitTextWithSubstitutions(
4724 ps, lhsRhs.first, op,
4725 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4726 string = lhsRhs.second;
4731 emitLocationInfoAndNewLine(ops);
4736LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4738 emitError(op,
"SV attributes emission is unimplemented for the op");
4742 SmallPtrSet<Operation *, 8> ops;
4747 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4748 assert(macroOp &&
"Invalid IR");
4750 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4752 if (!op.getInputs().empty()) {
4754 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4755 emitExpression(val, ops, LowestPrecedence,
4761 emitLocationInfoAndNewLine(ops);
4767StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4768 std::optional<unsigned> verbosity) {
4770 emitError(op,
"SV attributes emission is unimplemented for the op");
4773 SmallPtrSet<Operation *, 8> ops;
4775 ps.addCallback({op,
true});
4777 if (verbosity && *verbosity != 1) {
4779 ps.addAsString(*verbosity);
4783 ps.addCallback({op,
false});
4784 emitLocationInfoAndNewLine(ops);
4788LogicalResult StmtEmitter::visitSV(StopOp op) {
4789 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4792LogicalResult StmtEmitter::visitSV(FinishOp op) {
4793 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4797LogicalResult StmtEmitter::visitSV(ExitOp op) {
4798 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4804StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4805 std::optional<unsigned> verbosity,
4806 StringAttr message, ValueRange operands) {
4808 emitError(op,
"SV attributes emission is unimplemented for the op");
4811 SmallPtrSet<Operation *, 8> ops;
4813 ps.addCallback({op,
true});
4819 if ((verbosity && *verbosity != 1) || message) {
4821 ps.scopedBox(PP::ibox0, [&]() {
4825 ps.addAsString(*verbosity);
4830 ps <<
"," << PP::space;
4831 ps.writeQuotedEscaped(message.getValue());
4833 for (
auto operand : operands) {
4834 ps <<
"," << PP::space;
4835 emitExpression(operand, ops);
4844 ps.addCallback({op,
false});
4845 emitLocationInfoAndNewLine(ops);
4849LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4850 return emitFatalMessageOp(op);
4853LogicalResult StmtEmitter::visitSV(FatalOp op) {
4854 return emitFatalMessageOp(op);
4857LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4858 return emitNonfatalMessageOp(op,
"$error");
4861LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4862 return emitNonfatalMessageOp(op,
"$warning");
4865LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4866 return emitNonfatalMessageOp(op,
"$info");
4869LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4870 return emitNonfatalMessageOp(op,
"$error");
4873LogicalResult StmtEmitter::visitSV(WarningOp op) {
4874 return emitNonfatalMessageOp(op,
"$warning");
4877LogicalResult StmtEmitter::visitSV(InfoOp op) {
4878 return emitNonfatalMessageOp(op,
"$info");
4881LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4882 SmallPtrSet<Operation *, 8> ops({op});
4885 ps.addCallback({op,
true});
4887 switch (op.getBaseAttr().getValue()) {
4888 case MemBaseTypeAttr::MemBaseBin:
4891 case MemBaseTypeAttr::MemBaseHex:
4896 ps.scopedBox(PP::ibox0, [&]() {
4897 ps.writeQuotedEscaped(op.getFilename());
4898 ps <<
"," << PP::space;
4899 emitExpression(op.getDest(), ops);
4903 ps.addCallback({op,
false});
4904 emitLocationInfoAndNewLine(ops);
4908LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4909 emitSVAttributes(op);
4912 ps.addCallback({op,
true});
4913 ps <<
"generate" << PP::newline;
4915 setPendingNewline();
4916 emitStatementBlock(op.getBody().getBlocks().front());
4919 ps <<
"endgenerate";
4920 ps.addCallback({op,
false});
4921 setPendingNewline();
4925LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4926 emitSVAttributes(op);
4929 ps.addCallback({op,
true});
4931 ps.invokeWithStringOS([&](
auto &os) {
4932 emitter.printParamValue(
4933 op.getCond(), os, VerilogPrecedence::Selection,
4934 [&]() { return op->emitOpError(
"invalid case parameter"); });
4937 setPendingNewline();
4940 ArrayAttr
patterns = op.getCasePatterns();
4941 ArrayAttr caseNames = op.getCaseNames();
4942 MutableArrayRef<Region> regions = op.getCaseRegions();
4949 llvm::StringMap<size_t> nextGenIds;
4950 ps.scopedBox(PP::bbox2, [&]() {
4952 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4953 auto ®ion = regions[i];
4954 assert(region.hasOneBlock());
4955 Attribute patternAttr =
patterns[i];
4958 if (!isa<mlir::TypedAttr>(patternAttr))
4961 ps.invokeWithStringOS([&](
auto &os) {
4962 emitter.printParamValue(
4963 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4964 [&]() {
return op->emitOpError(
"invalid case value"); });
4967 StringRef legalName =
4968 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4971 setPendingNewline();
4972 emitStatementBlock(region.getBlocks().front());
4975 setPendingNewline();
4981 ps.addCallback({op,
false});
4982 setPendingNewline();
4986LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
4987 emitSVAttributes(op);
4988 llvm::SmallPtrSet<Operation *, 8> ops;
4989 ps.addCallback({op,
true});
4992 StringRef inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4995 ps.scopedBox(PP::cbox0, [&]() {
4997 [&]() { ps <<
"genvar" << PP::nbsp <<
PPExtString(inductionVarName); },
4999 ps.invokeWithStringOS([&](
auto &os) {
5000 emitter.printParamValue(
5001 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5002 [&]() { return op->emitOpError(
"invalid lower bound"); });
5011 ps.invokeWithStringOS([&](
auto &os) {
5012 emitter.printParamValue(
5013 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5014 [&]() { return op->emitOpError(
"invalid upper bound"); });
5020 ps <<
PPExtString(inductionVarName) << PP::nbsp <<
"+=" << PP::nbsp;
5021 ps.invokeWithStringOS([&](
auto &os) {
5022 emitter.printParamValue(
5023 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5024 [&]() { return op->emitOpError(
"invalid step"); });
5027 StringRef blockName = op.getGenBlockName();
5028 if (!blockName.empty())
5032 ps << PP::neverbreak;
5033 setPendingNewline();
5034 emitStatementBlock(op.getBody().getBlocks().front());
5037 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5039 ps.addCallback({op,
false});
5040 setPendingNewline();
5044LogicalResult StmtEmitter::visitSV(
ForOp op) {
5045 emitSVAttributes(op);
5046 llvm::SmallPtrSet<Operation *, 8> ops;
5047 ps.addCallback({op,
true});
5049 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5052 ps.scopedBox(PP::cbox0, [&]() {
5056 ps <<
"logic" << PP::nbsp;
5057 ps.invokeWithStringOS([&](
auto &os) {
5058 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5063 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
5068 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5069 [&]() { emitExpression(op.getUpperBound(), ops); },
5075 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5076 [&]() { emitExpression(op.getStep(), ops); },
5080 ps << PP::neverbreak;
5081 setPendingNewline();
5082 emitStatementBlock(op.getBody().getBlocks().front());
5085 ps.addCallback({op,
false});
5086 emitLocationInfoAndNewLine(ops);
5091void StmtEmitter::emitAssertionLabel(Operation *op) {
5092 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5098void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5099 SmallPtrSetImpl<Operation *> &ops,
5100 bool isConcurrent =
false) {
5103 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5104 ps.scopedBox(PP::ibox0, [&]() {
5105 ps.writeQuotedEscaped(message.getValue());
5107 for (
auto arg : args) {
5108 ps <<
"," << PP::space;
5109 emitExpression(arg, ops);
5115template <
typename Op>
5116LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5118 emitError(op,
"SV attributes emission is unimplemented for the op");
5121 SmallPtrSet<Operation *, 8> ops;
5123 ps.addCallback({op,
true});
5124 ps.scopedBox(PP::ibox2, [&]() {
5125 emitAssertionLabel(op);
5126 ps.scopedBox(PP::cbox0, [&]() {
5128 switch (op.getDefer()) {
5129 case DeferAssert::Immediate:
5131 case DeferAssert::Observed:
5134 case DeferAssert::Final:
5139 ps.scopedBox(PP::ibox0, [&]() {
5140 emitExpression(op.getExpression(), ops);
5143 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5147 ps.addCallback({op,
false});
5148 emitLocationInfoAndNewLine(ops);
5152LogicalResult StmtEmitter::visitSV(AssertOp op) {
5153 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5156LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5157 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5160LogicalResult StmtEmitter::visitSV(CoverOp op) {
5161 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5164template <
typename Op>
5165LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5167 emitError(op,
"SV attributes emission is unimplemented for the op");
5170 SmallPtrSet<Operation *, 8> ops;
5172 ps.addCallback({op,
true});
5173 ps.scopedBox(PP::ibox2, [&]() {
5174 emitAssertionLabel(op);
5175 ps.scopedBox(PP::cbox0, [&]() {
5176 ps << opName << PP::nbsp <<
"property (";
5177 ps.scopedBox(PP::ibox0, [&]() {
5178 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5180 emitExpression(op.getClock(), ops);
5181 ps <<
")" << PP::space;
5182 emitExpression(op.getProperty(), ops);
5185 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5190 ps.addCallback({op,
false});
5191 emitLocationInfoAndNewLine(ops);
5195LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5196 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5199LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5200 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5203LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5204 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5209template <
typename Op>
5210LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5212 emitError(op,
"SV attributes emission is unimplemented for the op");
5222 Operation *parent = op->getParentOp();
5223 Value
property = op.getProperty();
5224 bool isTemporal = !
property.getType().isSignlessInteger(1);
5226 bool emitAsImmediate = !isTemporal && isProcedural;
5229 SmallPtrSet<Operation *, 8> ops;
5231 ps.addCallback({op,
true});
5232 ps.scopedBox(PP::ibox2, [&]() {
5234 emitAssertionLabel(op);
5236 ps.scopedBox(PP::cbox0, [&]() {
5237 if (emitAsImmediate)
5238 ps << opName <<
"(";
5240 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5242 Value clock = op.getClock();
5243 auto event = op.getEvent();
5245 ps.scopedBox(PP::ibox2, [&]() {
5246 PropertyEmitter(emitter, ops)
5247 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5250 ps.scopedBox(PP::ibox2, [&]() {
5251 PropertyEmitter(emitter, ops)
5252 .emitAssertPropertyBody(property, op.getDisable());
5257 ps.addCallback({op,
false});
5258 emitLocationInfoAndNewLine(ops);
5262LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5263 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5266LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5267 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5270LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5271 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5274LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5276 emitError(op,
"SV attributes emission is unimplemented for the op");
5279 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5280 .getMacroIdentifier());
5283 bool hasEmptyThen = op->getRegion(0).front().empty();
5285 ps <<
"`ifndef " << ident;
5287 ps <<
"`ifdef " << ident;
5289 SmallPtrSet<Operation *, 8> ops;
5291 emitLocationInfoAndNewLine(ops);
5294 emitStatementBlock(op->getRegion(0).front());
5296 if (!op->getRegion(1).empty()) {
5297 if (!hasEmptyThen) {
5299 ps <<
"`else // " << ident;
5300 setPendingNewline();
5302 emitStatementBlock(op->getRegion(1).front());
5309 setPendingNewline();
5317void StmtEmitter::emitBlockAsStatement(
5318 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5319 StringRef multiLineComment) {
5326 emitLocationInfoAndNewLine(locationOps);
5329 emitStatementBlock(*block);
5331 if (needsBeginEnd) {
5335 if (!multiLineComment.empty())
5336 ps <<
" // " << multiLineComment;
5337 setPendingNewline();
5341LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5343 for (
auto &op : ooop.getBody().front())
5348LogicalResult StmtEmitter::visitSV(IfOp op) {
5349 SmallPtrSet<Operation *, 8> ops;
5351 auto ifcondBox = PP::ibox2;
5353 emitSVAttributes(op);
5355 ps.addCallback({op,
true});
5356 ps <<
"if (" << ifcondBox;
5366 emitExpression(ifOp.getCond(), ops);
5367 ps << PP::end <<
")";
5368 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5370 if (!ifOp.hasElse())
5374 Block *elseBlock = ifOp.getElseBlock();
5376 if (!nestedElseIfOp) {
5381 emitBlockAsStatement(elseBlock, ops);
5387 ifOp = nestedElseIfOp;
5388 ps <<
"else if (" << ifcondBox;
5390 ps.addCallback({op,
false});
5395LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5396 emitSVAttributes(op);
5397 SmallPtrSet<Operation *, 8> ops;
5401 auto printEvent = [&](AlwaysOp::Condition cond) {
5402 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5403 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5405 ps.addCallback({op,
true});
5407 switch (op.getNumConditions()) {
5413 printEvent(op.getCondition(0));
5418 ps.scopedBox(PP::cbox0, [&]() {
5419 printEvent(op.getCondition(0));
5420 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5421 ps << PP::space <<
"or" << PP::space;
5422 printEvent(op.getCondition(i));
5431 std::string comment;
5432 if (op.getNumConditions() == 0) {
5433 comment =
"always @*";
5435 comment =
"always @(";
5438 [&](Attribute eventAttr) {
5439 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5440 comment += stringifyEventControl(event);
5442 [&]() { comment +=
", "; });
5446 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5447 ps.addCallback({op,
false});
5451LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5452 emitSVAttributes(op);
5453 SmallPtrSet<Operation *, 8> ops;
5457 ps.addCallback({op,
true});
5458 StringRef opString =
"always_comb";
5459 if (state.options.noAlwaysComb)
5460 opString =
"always @(*)";
5463 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5464 ps.addCallback({op,
false});
5468LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5469 emitSVAttributes(op);
5471 SmallPtrSet<Operation *, 8> ops;
5475 ps.addCallback({op,
true});
5476 ps <<
"always_ff @(";
5477 ps.scopedBox(PP::cbox0, [&]() {
5478 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5479 emitExpression(op.getClock(), ops);
5480 if (op.getResetStyle() == ResetType::AsyncReset) {
5481 ps << PP::nbsp <<
"or" << PP::space
5482 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5483 emitExpression(op.getReset(), ops);
5490 std::string comment;
5491 comment +=
"always_ff @(";
5492 comment += stringifyEventControl(op.getClockEdge());
5493 if (op.getResetStyle() == ResetType::AsyncReset) {
5495 comment += stringifyEventControl(*op.getResetEdge());
5499 if (op.getResetStyle() == ResetType::NoReset)
5500 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5503 emitLocationInfoAndNewLine(ops);
5504 ps.scopedBox(PP::bbox2, [&]() {
5510 if (op.getResetStyle() == ResetType::AsyncReset &&
5511 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5513 emitExpression(op.getReset(), ops);
5515 emitBlockAsStatement(op.getResetBlock(), ops);
5518 emitBlockAsStatement(op.getBodyBlock(), ops);
5523 ps <<
" // " << comment;
5524 setPendingNewline();
5526 ps.addCallback({op,
false});
5530LogicalResult StmtEmitter::visitSV(InitialOp op) {
5531 emitSVAttributes(op);
5532 SmallPtrSet<Operation *, 8> ops;
5535 ps.addCallback({op,
true});
5537 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5538 ps.addCallback({op,
false});
5542LogicalResult StmtEmitter::visitSV(CaseOp op) {
5543 emitSVAttributes(op);
5544 SmallPtrSet<Operation *, 8> ops, emptyOps;
5547 ps.addCallback({op,
true});
5548 if (op.getValidationQualifier() !=
5549 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5550 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5551 op.getValidationQualifier()))
5553 const char *opname =
nullptr;
5554 switch (op.getCaseStyle()) {
5555 case CaseStmtType::CaseStmt:
5558 case CaseStmtType::CaseXStmt:
5561 case CaseStmtType::CaseZStmt:
5565 ps << opname <<
" (";
5566 ps.scopedBox(PP::ibox0, [&]() {
5567 emitExpression(op.getCond(), ops);
5570 emitLocationInfoAndNewLine(ops);
5572 size_t caseValueIndex = 0;
5573 ps.scopedBox(PP::bbox2, [&]() {
5574 for (
auto &caseInfo : op.getCases()) {
5576 auto &
pattern = caseInfo.pattern;
5578 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5579 .Case<CaseBitPattern>([&](
auto bitPattern) {
5582 ps.invokeWithStringOS([&](
auto &os) {
5583 os << bitPattern->getWidth() <<
"'b";
5584 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5585 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5588 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5589 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5590 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5592 .Case<CaseExprPattern>([&](
auto) {
5593 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5595 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5596 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5599 emitBlockAsStatement(caseInfo.block, emptyOps);
5605 ps.addCallback({op,
false});
5606 emitLocationInfoAndNewLine(ops);
5610LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5611 bool doNotPrint = op.getDoNotPrint();
5612 if (doNotPrint && !state.options.emitBindComments)
5617 emitSVAttributes(op);
5619 ps.addCallback({op,
true});
5622 <<
"/* This instance is elsewhere emitted as a bind statement."
5625 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5626 "attributes. The attributes will not be emitted.";
5629 SmallPtrSet<Operation *, 8> ops;
5634 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5635 assert(moduleOp &&
"Invalid IR");
5639 if (!op.getParameters().empty()) {
5642 bool printed =
false;
5644 llvm::zip(op.getParameters(),
5645 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5646 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5647 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5649 if (param.getValue() == modParam.getValue())
5654 ps <<
" #(" << PP::bbox2 << PP::newline;
5657 ps <<
"," << PP::newline;
5661 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5663 ps.invokeWithStringOS([&](
auto &os) {
5664 emitter.printParamValue(param.getValue(), os, [&]() {
5665 return op->emitOpError(
"invalid instance parameter '")
5666 << param.getName().getValue() <<
"' value";
5672 ps << PP::end << PP::newline <<
")";
5679 SmallVector<Value> instPortValues(modPortInfo.size());
5680 op.getValues(instPortValues, modPortInfo);
5681 emitInstancePortList(op, modPortInfo, instPortValues);
5683 ps.addCallback({op,
false});
5684 emitLocationInfoAndNewLine(ops);
5689 setPendingNewline();
5694void StmtEmitter::emitInstancePortList(Operation *op,
5696 ArrayRef<Value> instPortValues) {
5697 SmallPtrSet<Operation *, 8> ops;
5700 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5701 ModulePortInfo containingPortList(containingModule.getPortList());
5706 size_t maxNameLength = 0;
5707 for (
auto &elt : modPortInfo) {
5708 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5711 auto getWireForValue = [&](Value result) {
5712 return result.getUsers().begin()->getOperand(0);
5716 bool isFirst =
true;
5717 bool isZeroWidth =
false;
5719 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5721 auto &modPort = modPortInfo.
at(portNum);
5723 Value portVal = instPortValues[portNum];
5728 bool shouldPrintComma =
true;
5730 shouldPrintComma =
false;
5731 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5733 shouldPrintComma =
true;
5738 if (shouldPrintComma)
5741 emitLocationInfoAndNewLine(ops);
5756 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5757 auto modPortName = modPort.getVerilogName();
5759 ps.spaces(maxNameLength - modPortName.size() + 1);
5761 ps.scopedBox(PP::ibox0, [&]() {
5768 if (!modPort.isOutput()) {
5770 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5771 ps <<
"/* Zero width */";
5773 emitExpression(portVal, ops, LowestPrecedence);
5774 }
else if (portVal.use_empty()) {
5775 ps <<
"/* unused */";
5776 }
else if (portVal.hasOneUse() &&
5777 (output = dyn_cast_or_null<OutputOp>(
5778 portVal.getUses().begin()->getOwner()))) {
5783 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5785 containingPortList.atOutput(outputPortNo).getVerilogName());
5787 portVal = getWireForValue(portVal);
5788 emitExpression(portVal, ops);
5794 if (!isFirst || isZeroWidth) {
5795 emitLocationInfoAndNewLine(ops);
5808LogicalResult StmtEmitter::visitSV(BindOp op) {
5809 emitter.emitBind(op);
5810 assert(state.pendingNewline);
5814LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5815 emitComment(op.getCommentAttr());
5817 emitSVAttributes(op);
5820 ps.addCallback({op,
true});
5822 setPendingNewline();
5824 emitStatementBlock(*op.getBodyBlock());
5826 ps <<
"endinterface" << PP::newline;
5827 ps.addCallback({op,
false});
5828 setPendingNewline();
5833 emitSVAttributes(op);
5835 ps.addCallback({op,
true});
5837 ps << op.getContent();
5839 ps.addCallback({op,
false});
5840 setPendingNewline();
5844LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5846 emitSVAttributes(op);
5848 ps.addCallback({op,
true});
5850 ps << PP::neverbox <<
"// ";
5851 ps.invokeWithStringOS([&](
auto &os) {
5856 ps.invokeWithStringOS(
5857 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5861 ps.addCallback({op,
false});
5862 setPendingNewline();
5866LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5868 ps.addCallback({op,
true});
5872 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5873 auto port = cast<ModportStructAttr>(portAttr);
5874 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5875 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5876 ps << PPExtString(getSymOpName(signalDecl));
5880 ps.addCallback({op,
false});
5881 setPendingNewline();
5885LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5887 ps.addCallback({op,
true});
5888 SmallPtrSet<Operation *, 8> emitted;
5891 emitExpression(op.getIface(), emitted);
5892 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5893 emitExpression(op.getRhs(), emitted);
5895 ps.addCallback({op,
false});
5896 setPendingNewline();
5900LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5902 ps <<
"`" << op.getMacroIdentifier();
5903 setPendingNewline();
5907LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5908 auto decl = op.getReferencedMacro(&state.symbolCache);
5911 ps.addCallback({op,
true});
5913 if (decl.getArgs()) {
5915 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5916 ps << cast<StringAttr>(name);
5920 if (!op.getFormatString().empty()) {
5922 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5925 ps.addCallback({op,
false});
5926 setPendingNewline();
5930void StmtEmitter::emitStatement(Operation *op) {
5937 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5941 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5942 succeeded(dispatchVerifVisitor(op)))
5945 emitOpError(op,
"emission to Verilog not supported");
5946 emitPendingNewlineIfNeeded();
5947 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5948 setPendingNewline();
5959 StmtEmitter &stmtEmitter) {
5966 if (isa<IfDefProceduralOp>(op->getParentOp()))
5974 SmallVector<Value, 8> exprsToScan(op->getOperands());
5979 while (!exprsToScan.empty()) {
5980 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5987 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5988 auto *defOp = readInout.getOperand().getDefiningOp();
5995 if (isa<sv::WireOp>(defOp))
6000 if (!isa<RegOp, LogicOp>(defOp))
6006 if (isa<LogicOp>(defOp) &&
6007 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6011 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6012 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6020 exprsToScan.append(expr->getOperands().begin(),
6021 expr->getOperands().end());
6027 if (expr->getBlock() != op->getBlock())
6032 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6039template <
class AssignTy>
6041 AssignTy singleAssign;
6042 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6043 if (hasSVAttributes(user))
6046 if (auto assign = dyn_cast<AssignTy>(user)) {
6049 singleAssign = assign;
6053 return isa<ReadInOutOp>(user);
6055 return singleAssign;
6061 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6065 if (op1->getBlock() != user->getBlock())
6071 return op1->isBeforeInBlock(user);
6075LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6076 emitSVAttributes(op);
6077 auto value = op->getResult(0);
6078 SmallPtrSet<Operation *, 8> opsForLocation;
6079 opsForLocation.insert(op);
6081 ps.addCallback({op,
true});
6084 auto type = value.getType();
6090 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6092 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6093 unsigned targetColumn = 0;
6094 unsigned column = 0;
6097 if (maxDeclNameWidth > 0)
6098 targetColumn += maxDeclNameWidth + 1;
6101 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6102 }
else if (!word.empty()) {
6104 column += word.size();
6105 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6106 ps.spaces(numSpaces);
6107 column += numSpaces;
6110 SmallString<8> typeString;
6113 llvm::raw_svector_ostream stringStream(typeString);
6116 true, singleBitDefaultType);
6119 if (maxTypeWidth > 0)
6120 targetColumn += maxTypeWidth + 1;
6121 unsigned numSpaces = 0;
6122 if (!typeString.empty()) {
6124 column += typeString.size();
6127 if (targetColumn > column)
6128 numSpaces = targetColumn - column;
6129 ps.spaces(numSpaces);
6130 column += numSpaces;
6136 ps.invokeWithStringOS(
6137 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6140 if (state.options.printDebugInfo) {
6141 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6142 auto innerSym = innerSymOp.getInnerSymAttr();
6143 if (innerSym && !innerSym.empty()) {
6145 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6151 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6152 ps << PP::space <<
"=" << PP::space;
6153 ps.invokeWithStringOS([&](
auto &os) {
6154 emitter.printParamValue(localparam.getValue(), os, [&]() {
6155 return op->emitOpError(
"invalid localparam value");
6160 if (
auto regOp = dyn_cast<RegOp>(op)) {
6161 if (
auto initValue = regOp.getInit()) {
6162 ps << PP::space <<
"=" << PP::space;
6163 ps.scopedBox(PP::ibox0, [&]() {
6164 emitExpression(initValue, opsForLocation, LowestPrecedence,
6173 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6177 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6178 auto *source = singleAssign.getSrc().getDefiningOp();
6182 if (!source || isa<ConstantOp>(source) ||
6183 op->getNextNode() == singleAssign) {
6184 ps << PP::space <<
"=" << PP::space;
6185 ps.scopedBox(PP::ibox0, [&]() {
6186 emitExpression(singleAssign.getSrc(), opsForLocation,
6190 emitter.assignsInlined.insert(singleAssign);
6198 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6202 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6205 auto *source = singleAssign.getSrc().getDefiningOp();
6209 if (!source || isa<ConstantOp>(source) ||
6212 ps << PP::space <<
"=" << PP::space;
6213 ps.scopedBox(PP::ibox0, [&]() {
6214 emitExpression(singleAssign.getSrc(), opsForLocation,
6219 emitter.assignsInlined.insert(singleAssign);
6220 emitter.expressionsEmittedIntoDecl.insert(op);
6227 ps.addCallback({op,
false});
6228 emitLocationInfoAndNewLine(opsForLocation);
6232void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6235 NameCollector collector(emitter);
6236 collector.collectNames(block);
6239 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6240 maxTypeWidth = collector.getMaxTypeWidth();
6243void StmtEmitter::emitStatementBlock(Block &body) {
6244 ps.scopedBox(PP::bbox2, [&]() {
6249 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6250 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6255 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6256 collectNamesAndCalculateDeclarationWidths(body);
6259 for (
auto &op : body) {
6266void ModuleEmitter::emitStatement(Operation *op) {
6267 StmtEmitter(*
this, state.options).emitStatement(op);
6272void ModuleEmitter::emitSVAttributes(Operation *op) {
6280 setPendingNewline();
6287void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6288 auto verilogName =
module.getVerilogModuleNameAttr();
6290 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6292 setPendingNewline();
6301void ModuleEmitter::emitBind(BindOp op) {
6303 emitError(op,
"SV attributes emission is unimplemented for the op");
6304 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6310 Operation *childMod =
6311 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6315 ps.addCallback({op,
true});
6316 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6317 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6319 bool isFirst =
true;
6320 ps.scopedBox(PP::bbox2, [&]() {
6321 auto parentPortInfo = parentMod.getPortList();
6325 size_t maxNameLength = 0;
6326 for (
auto &elt : childPortInfo) {
6327 auto portName = elt.getVerilogName();
6328 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6329 maxNameLength = std::max(maxNameLength, elt.getName().size());
6332 SmallVector<Value> instPortValues(childPortInfo.size());
6333 inst.getValues(instPortValues, childPortInfo);
6335 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6337 Value portVal = instPortValues[idx];
6343 bool shouldPrintComma =
true;
6345 shouldPrintComma =
false;
6346 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6348 shouldPrintComma =
true;
6353 if (shouldPrintComma)
6366 ps << PP::neverbox <<
"//";
6370 ps.nbsp(maxNameLength - elt.getName().size());
6372 llvm::SmallPtrSet<Operation *, 4> ops;
6373 if (elt.isOutput()) {
6374 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6375 "output port must have either single or no use");
6376 if (portVal.use_empty()) {
6377 ps <<
"/* unused */";
6378 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6379 portVal.getUses().begin()->getOwner())) {
6382 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6384 parentPortList.atOutput(outputPortNo).getVerilogName());
6386 portVal = portVal.getUsers().begin()->getOperand(0);
6387 ExprEmitter(*
this, ops)
6388 .emitExpression(portVal, LowestPrecedence,
6392 ExprEmitter(*
this, ops)
6393 .emitExpression(portVal, LowestPrecedence,
6406 ps.addCallback({op,
false});
6407 setPendingNewline();
6410void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6412 emitError(op,
"SV attributes emission is unimplemented for the op");
6414 auto instance = op.getReferencedInstance(&state.symbolCache);
6416 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6417 instance.getInterfaceType().getInterface());
6419 ps.addCallback({op,
true});
6420 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6421 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6423 ps.addCallback({op,
false});
6424 setPendingNewline();
6427void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6431 auto printParamType = [&](Type type, Attribute defaultValue,
6432 SmallString<8> &result) {
6434 llvm::raw_svector_ostream sstream(result);
6439 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6440 if (intAttr.getValue().getBitWidth() == 32)
6442 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6443 if (fpAttr.getType().isF64())
6446 if (isa<NoneType>(type))
6453 if (
auto intType = type_dyn_cast<IntegerType>(type))
6454 if (intType.getWidth() == 32) {
6455 sstream <<
"/*integer*/";
6459 printPackedType(type, sstream, module->getLoc(),
6467 size_t maxTypeWidth = 0;
6468 SmallString<8> scratch;
6469 for (
auto param : params) {
6470 auto paramAttr = cast<ParamDeclAttr>(param);
6472 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6473 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6476 if (maxTypeWidth > 0)
6479 ps.scopedBox(PP::bbox2, [&]() {
6480 ps << PP::newline <<
"#(";
6481 ps.scopedBox(PP::cbox0, [&]() {
6484 [&](Attribute param) {
6485 auto paramAttr = cast<ParamDeclAttr>(param);
6486 auto defaultValue = paramAttr.getValue();
6488 printParamType(paramAttr.getType(), defaultValue, scratch);
6489 if (!scratch.empty())
6491 if (scratch.size() < maxTypeWidth)
6492 ps.nbsp(maxTypeWidth - scratch.size());
6494 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6495 module, paramAttr.getName()));
6499 ps.invokeWithStringOS([&](
auto &os) {
6501 return module->emitError("parameter '")
6502 << paramAttr.getName().getValue()
6503 << "' has invalid value";
6508 [&]() { ps <<
"," << PP::newline; });
6514void ModuleEmitter::emitPortList(Operation *module,
6516 bool emitAsTwoStateType) {
6518 if (portInfo.
size())
6519 emitLocationInfo(module->getLoc());
6523 bool hasOutputs =
false, hasZeroWidth =
false;
6524 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6525 SmallVector<SmallString<8>, 16> portTypeStrings;
6527 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6528 auto port = portInfo.
at(i);
6532 lastNonZeroPort = i;
6535 portTypeStrings.push_back({});
6537 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6539 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6542 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6545 if (maxTypeWidth > 0)
6549 ps.scopedBox(PP::bbox2, [&]() {
6550 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6551 auto lastPort = e - 1;
6554 auto portType = portInfo.
at(portIdx).
type;
6558 bool isZeroWidth =
false;
6563 ps << (isZeroWidth ?
"// " :
" ");
6567 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6568 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6569 (state.options.emitWireInPorts ? 5 : 0) +
6574 if (!isa<ModportType>(portType)) {
6575 switch (thisPortDirection) {
6576 case ModulePort::Direction::Output:
6579 case ModulePort::Direction::Input:
6580 ps << (hasOutputs ?
"input " :
"input ");
6582 case ModulePort::Direction::InOut:
6583 ps << (hasOutputs ?
"inout " :
"inout ");
6586 if (state.options.emitWireInPorts)
6588 if (!portTypeStrings[portIdx].
empty())
6589 ps << portTypeStrings[portIdx];
6590 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6591 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6593 ps << portTypeStrings[portIdx];
6594 if (portTypeStrings[portIdx].size() < startOfNamePos)
6595 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6602 ps.invokeWithStringOS(
6603 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6606 auto innerSym = portInfo.
at(portIdx).
getSym();
6607 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6609 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6614 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6618 if (
auto loc = portInfo.
at(portIdx).
loc)
6619 emitLocationInfo(loc);
6629 if (!state.options.disallowPortDeclSharing) {
6630 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6633 auto port = portInfo.
at(portIdx);
6637 bool isZeroWidth =
false;
6642 ps << (isZeroWidth ?
"// " :
" ");
6645 ps.nbsp(startOfNamePos);
6648 StringRef name = port.getVerilogName();
6652 ps.invokeWithStringOS(
6653 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6656 auto sym = port.getSym();
6657 if (state.options.printDebugInfo && sym && !sym.empty())
6658 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6662 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6666 if (
auto loc = port.loc)
6667 emitLocationInfo(loc);
6678 if (!portInfo.
size()) {
6680 SmallPtrSet<Operation *, 8> moduleOpSet;
6681 moduleOpSet.insert(module);
6682 emitLocationInfoAndNewLine(moduleOpSet);
6685 ps <<
");" << PP::newline;
6686 setPendingNewline();
6690void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6691 currentModuleOp =
module;
6693 emitComment(module.getCommentAttr());
6694 emitSVAttributes(module);
6696 ps.addCallback({module,
true});
6700 emitParameters(module, module.getParameters());
6704 assert(state.pendingNewline);
6707 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6710 ps.addCallback({module,
false});
6712 setPendingNewline();
6714 currentModuleOp =
nullptr;
6717void ModuleEmitter::emitFunc(FuncOp func) {
6719 if (func.isDeclaration())
6722 currentModuleOp = func;
6724 ps.addCallback({func,
true});
6728 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6730 ps <<
"endfunction";
6732 currentModuleOp =
nullptr;
6741 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6748 void emit(emit::FileListOp op);
6751 void emit(Block *block);
6753 void emitOp(emit::RefOp op);
6754 void emitOp(emit::VerbatimOp op);
6758 for (Operation &op : *block) {
6759 TypeSwitch<Operation *>(&op)
6760 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6761 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6762 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6763 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6764 .Case<BindInterfaceOp>(
6765 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6766 .Case<TypeScopeOp>([&](
auto typedecls) {
6767 ModuleEmitter(state).emitStatement(typedecls);
6770 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6776 for (
auto sym : op.getFiles()) {
6777 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6779 auto it = state.fileMapping.find(fileName);
6780 if (it == state.fileMapping.end()) {
6781 emitOpError(op,
" references an invalid file: ") << sym;
6785 auto file = cast<emit::FileOp>(it->second);
6786 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6793 StringAttr target = op.getTargetAttr().getAttr();
6794 auto *targetOp = state.symbolCache.getDefinition(target);
6795 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6797 TypeSwitch<Operation *>(targetOp)
6798 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6799 .Case<hw::HWModuleOp>(
6800 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6801 .Case<TypeScopeOp>([&](
auto typedecls) {
6802 ModuleEmitter(state).emitStatement(typedecls);
6805 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6811 SmallPtrSet<Operation *, 8> ops;
6816 StringRef text = op.getText();
6820 const auto &[lhs, rhs] = text.split(
'\n');
6824 ps << PP::end << PP::newline << PP::neverbox;
6826 }
while (!text.empty());
6829 emitLocationInfoAndNewLine(ops);
6847 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6848 moduleOp->walk([&](Operation *op) {
6850 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6853 SymbolTable::getSymbolAttrName()),
6854 name.getSymName(), op);
6855 if (isa<BindOp>(op))
6861 auto collectPorts = [&](
auto moduleOp) {
6862 auto portInfo = moduleOp.getPortList();
6863 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6864 if (!p.attrs || p.attrs.empty())
6866 for (NamedAttribute portAttr : p.attrs) {
6867 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6876 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6877 for (
auto file :
designOp.getOps<emit::FileOp>())
6878 for (
auto refs : file.getOps<emit::RefOp>())
6879 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6881 SmallString<32> outputPath;
6882 for (
auto &op : *
designOp.getBody()) {
6885 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6887 bool hasFileName =
false;
6888 bool emitReplicatedOps = !isFileOp;
6889 bool addToFilelist = !isFileOp;
6895 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6897 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6898 <<
" on " << op <<
"\n";);
6899 if (!attr.isDirectory())
6902 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6903 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6906 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6911 if (!defaultFileName.isTriviallyEmpty()) {
6912 llvm::sys::path::append(outputPath, defaultFileName);
6914 op->emitError(
"file name unspecified");
6916 llvm::sys::path::append(outputPath,
"error.out");
6920 auto destFile = StringAttr::get(op->getContext(), outputPath);
6921 auto &file =
files[destFile];
6922 file.ops.push_back(info);
6923 file.emitReplicatedOps = emitReplicatedOps;
6924 file.addToFilelist = addToFilelist;
6925 file.isVerilog = outputPath.ends_with(
".sv");
6930 if (!attr || attr.isDirectory()) {
6931 auto excludeFromFileListAttr =
6932 BoolAttr::get(op->getContext(), !addToFilelist);
6933 auto includeReplicatedOpsAttr =
6934 BoolAttr::get(op->getContext(), emitReplicatedOps);
6935 auto outputFileAttr = hw::OutputFileAttr::get(
6936 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6937 op->setAttr(
"output_file", outputFileAttr);
6943 TypeSwitch<Operation *>(&op)
6944 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6946 fileMapping.try_emplace(file.getSymNameAttr(), file);
6947 separateFile(file, file.getFileName());
6949 .Case<emit::FragmentOp>([&](
auto fragment) {
6952 .Case<HWModuleOp>([&](
auto mod) {
6954 auto sym = mod.getNameAttr();
6957 collectInstanceSymbolsAndBinds(mod);
6959 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6960 if (it->second.size() != 1 || attr) {
6963 op.emitError(
"modules can be emitted to a single file");
6971 if (attr || separateModules)
6977 .Case<InterfaceOp>([&](InterfaceOp intf) {
6982 for (
auto &op : *intf.getBodyBlock())
6983 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6984 if (
auto name = symOp.getNameAttr())
6988 if (attr || separateModules)
6989 separateFile(intf, intf.getSymName() +
".sv");
6995 separateFile(op, op.getOutputFile().getFilename().getValue());
6997 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7003 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7004 [&](Operation *op) {
7010 separateFile(op,
"");
7012 .Case<FuncOp>([&](
auto op) {
7018 separateFile(op,
"");
7022 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7025 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7034 separateFile(op,
"");
7036 .Case<BindOp>([&](
auto op) {
7038 separateFile(op,
"bindfile.sv");
7043 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
7044 .Case<MacroDeclOp>([&](
auto op) {
7047 .Case<sv::ReserveNamesOp>([](
auto op) {
7050 .Case<om::ClassLike>([&](
auto op) {
7053 .Case<om::ConstantOp>([&](
auto op) {
7056 .Default([&](
auto *) {
7057 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7077 size_t lastReplicatedOp = 0;
7079 bool emitHeaderInclude =
7082 if (emitHeaderInclude)
7085 size_t numReplicatedOps =
7090 DenseSet<emit::FragmentOp> includedFragments;
7091 for (
const auto &opInfo : file.
ops) {
7092 Operation *op = opInfo.op;
7096 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7102 if (
auto fragments =
7104 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7108 op->emitError(
"cannot find referenced fragment ") << sym;
7111 emit::FragmentOp fragment = it->second;
7112 if (includedFragments.insert(fragment).second) {
7113 thingsToEmit.emplace_back(it->second);
7119 thingsToEmit.emplace_back(op);
7124 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7129 TypeSwitch<Operation *>(op)
7130 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7131 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7134 .Case<HWModuleGeneratedOp>(
7135 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7136 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7137 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7138 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7139 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7140 .Case<TypeScopeOp>([&](
auto typedecls) {
7141 ModuleEmitter(state).emitStatement(typedecls);
7143 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7145 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7146 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7147 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7148 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7149 .Default([&](
auto *op) {
7150 state.encounteredError =
true;
7151 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7158 llvm::formatted_raw_ostream &os,
7159 StringAttr fileName,
bool parallelize) {
7164 parallelize &=
context->isMultithreadingEnabled();
7175 size_t lineOffset = 0;
7176 for (
auto &entry : thingsToEmit) {
7177 entry.verilogLocs.setStream(os);
7178 if (
auto *op = entry.getOperation()) {
7183 state.addVerilogLocToOps(lineOffset, fileName);
7185 os << entry.getStringData();
7190 if (state.encounteredError)
7208 SmallString<256> buffer;
7209 llvm::raw_svector_ostream tmpStream(buffer);
7210 llvm::formatted_raw_ostream rs(tmpStream);
7218 if (state.encounteredError)
7223 for (
auto &entry : thingsToEmit) {
7226 auto *op = entry.getOperation();
7228 auto lineOffset = os.getLine() + 1;
7229 os << entry.getStringData();
7233 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7236 entry.verilogLocs.setStream(os);
7243 state.addVerilogLocToOps(0, fileName);
7244 if (state.encounteredError) {
7263 module.emitWarning()
7264 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7265 "created only at SplitExportVerilog";
7274 for (
const auto &it : emitter.
files) {
7275 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7276 "\" ----- 8< -----\n\n");
7282 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7283 "\" ----- 8< -----\n\n");
7284 for (
auto &name : it.second)
7285 contents += name.str() +
"\n";
7286 list.emplace_back(contents);
7289 llvm::formatted_raw_ostream rs(os);
7293 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7300 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7302 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7303 if (failed(failableParallelForEach(
7304 module->getContext(), modulesToPrepare,
7305 [&](
auto op) { return prepareHWModule(op, options); })))
7312struct ExportVerilogPass
7313 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7314 ExportVerilogPass(raw_ostream &os) : os(os) {}
7315 void runOnOperation()
override {
7317 mlir::OpPassManager preparePM(
"builtin.module");
7318 preparePM.addPass(createLegalizeAnonEnums());
7319 auto &modulePM = preparePM.nestAny();
7320 modulePM.addPass(createPrepareForEmission());
7321 if (failed(runPipeline(preparePM, getOperation())))
7322 return signalPassFailure();
7325 return signalPassFailure();
7332struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7333 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7334 : ExportVerilogPass{*os} {
7335 owned = std::move(os);
7339 std::unique_ptr<llvm::raw_ostream> owned;
7343std::unique_ptr<mlir::Pass>
7345 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7348std::unique_ptr<mlir::Pass>
7350 return std::make_unique<ExportVerilogPass>(os);
7361static std::unique_ptr<llvm::ToolOutputFile>
7365 SmallString<128> outputFilename(dirname);
7367 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7370 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7372 emitter.
designOp.emitError(
"cannot create output directory \"")
7373 << outputDir <<
"\": " << error.message();
7379 std::string errorMessage;
7380 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7382 emitter.
designOp.emitError(errorMessage);
7399 llvm::formatted_raw_ostream rs(output->os());
7405 StringAttr::get(fileName.getContext(), output->getFilename()),
7411 StringRef dirname) {
7422 bool insertSuccess =
7424 .insert({StringAttr::get(module.getContext(),
circtHeader),
7430 if (!insertSuccess) {
7431 module.emitError() << "tried to emit a heder to " << circtHeader
7432 << ", but the file is used as an output too.";
7438 parallelForEach(module->getContext(), emitter.
files.begin(),
7439 emitter.
files.end(), [&](
auto &it) {
7440 createSplitOutputFile(it.first, it.second, dirname,
7445 SmallString<128> filelistPath(dirname);
7446 llvm::sys::path::append(filelistPath,
"filelist.f");
7448 std::string errorMessage;
7449 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7451 module->emitError(errorMessage);
7455 for (
const auto &it : emitter.
files) {
7456 if (it.second.addToFilelist)
7457 output->os() << it.first.str() <<
"\n";
7466 for (
auto &name : it.second)
7467 output->os() << name.str() <<
"\n";
7476 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7478 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7479 if (failed(failableParallelForEach(
7480 module->getContext(), modulesToPrepare,
7481 [&](
auto op) { return prepareHWModule(op, options); })))
7489struct ExportSplitVerilogPass
7490 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7491 ExportSplitVerilogPass(StringRef directory) {
7492 directoryName = directory.str();
7494 void runOnOperation()
override {
7496 mlir::OpPassManager preparePM(
"builtin.module");
7499 modulePM.addPass(createPrepareForEmission());
7500 if (failed(runPipeline(preparePM, getOperation())))
7501 return signalPassFailure();
7504 return signalPassFailure();
7509std::unique_ptr<mlir::Pass>
7511 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
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 void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool haveMatchingDims(Type a, Type b, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
True iff 'a' and 'b' have the same wire dims.
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
Push this type's dimension into a vector.
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static std::unique_ptr< Context > context
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
Signals that an operation's regions are procedural.
This stores lookup tables to make manipulating and working with the IR more efficient.
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=invalidPort)
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.
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="./")
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
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.
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.