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 .Case<GenerateOp>([](GenerateOp op) {
return op.getSymName(); })
213 .Default([&](Operation *op) {
214 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
215 return attr.getValue();
216 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
217 return attr.getValue();
218 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
219 return attr.getValue();
220 if (
auto symbol = dyn_cast<mlir::SymbolOpInterface>(op))
221 return symbol.getName();
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, ConcatStrOp>(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 StructInjectOp, StructExplodeOp, UnionExtractOp,
783 IndexedPartSelectOp>(user))
784 if (use.getOperandNumber() == 0 &&
795 auto usedInExprControl = [user, &use]() {
796 return TypeSwitch<Operation *, bool>(user)
797 .Case<ltl::ClockOp>([&](
auto clockOp) {
799 return clockOp.getClock() == use.get();
801 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
802 sv::CoverConcurrentOp>(
803 [&](
auto op) {
return op.getClock() == use.get(); })
804 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
805 sv::CoverPropertyOp>([&](
auto op) {
806 return op.getDisable() == use.get() || op.getClock() == use.get();
808 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
813 .Default([](
auto) {
return false; });
816 if (!usedInExprControl())
820 auto read = dyn_cast<ReadInOutOp>(op);
823 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
834 unsigned numStatements = 0;
835 block.walk([&](Operation *op) {
837 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
838 return WalkResult::advance();
840 TypeSwitch<Operation *, unsigned>(op)
841 .Case<VerbatimOp>([&](
auto) {
847 .Case<IfOp>([&](
auto) {
858 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
859 .Case<OutputOp>([&](OutputOp oop) {
862 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
863 Operation *op = operand.getDefiningOp();
864 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
867 .Default([](
auto) {
return 1; });
868 if (numStatements > 1)
869 return WalkResult::interrupt();
870 return WalkResult::advance();
872 if (numStatements == 0)
874 if (numStatements == 1)
884 if (op->getResult(0).use_empty())
889 if (op->hasOneUse() &&
890 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
891 *op->getUsers().begin()))
913 for (
auto &op : *elseBlock) {
914 if (
auto opIf = dyn_cast<IfOp>(op)) {
931template <
typename PPS>
933 enum Container { NoContainer, InComment, InAttr };
934 Container currentContainer = NoContainer;
936 auto closeContainer = [&] {
937 if (currentContainer == NoContainer)
939 if (currentContainer == InComment)
941 else if (currentContainer == InAttr)
943 ps << PP::end << PP::end;
945 currentContainer = NoContainer;
948 bool isFirstContainer =
true;
949 auto openContainer = [&](Container newContainer) {
950 assert(newContainer != NoContainer);
951 if (currentContainer == newContainer)
955 if (!isFirstContainer)
956 ps << (mayBreak ? PP::space : PP::nbsp);
957 isFirstContainer =
false;
960 if (newContainer == InComment)
962 else if (newContainer == InAttr)
964 currentContainer = newContainer;
972 ps.scopedBox(PP::cbox0, [&]() {
973 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
974 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
976 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
978 if (attr.getExpression())
979 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
988 if (
auto *op = val.getDefiningOp())
991 if (
auto port = dyn_cast<BlockArgument>(val)) {
993 auto parent = port.getParentBlock()->getParentOp();
994 if (isa<ForOp, GenerateForOp>(parent))
995 return parent->getAttrOfType<StringAttr>(
"hw.verilogName");
997 port.getArgNumber());
999 assert(
false &&
"unhandled value");
1011class VerilogEmitterState {
1013 explicit VerilogEmitterState(ModuleOp designOp,
1019 llvm::formatted_raw_ostream &os,
1020 StringAttr fileName,
OpLocMap &verilogLocMap)
1021 : designOp(designOp), shared(shared), options(options),
1022 symbolCache(symbolCache), globalNames(globalNames),
1023 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1024 pp(os, options.getEmittedLineLength().value_or(0)), fileName(fileName) {
1025 pp.setListener(&saver);
1048 llvm::formatted_raw_ostream &os;
1050 bool encounteredError =
false;
1059 bool pendingNewline =
false;
1073 StringAttr fileName;
1079 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1082 verilogLocMap.
clear();
1086 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1087 void operator=(
const VerilogEmitterState &) =
delete;
1100using CallbackDataTy = std::pair<Operation *, bool>;
1104 VerilogEmitterState &state;
1109 explicit EmitterBase(VerilogEmitterState &state)
1111 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1113 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1114 state.encounteredError =
true;
1115 return op->emitError(message);
1118 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1119 state.encounteredError =
true;
1120 return op->emitOpError(message);
1123 InFlightDiagnostic emitError(Location loc,
const Twine &message =
"") {
1124 state.encounteredError =
true;
1125 return mlir::emitError(loc, message);
1128 void emitLocationImpl(llvm::StringRef location) {
1131 ps << PP::neverbreak;
1132 if (!location.empty())
1133 ps <<
"\t// " << location;
1136 void emitLocationInfo(Location loc) {
1144 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1147 setPendingNewline();
1150 template <
typename PPS>
1151 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1152 llvm::function_ref<
void(Value)> operandEmitter,
1153 ArrayAttr symAttrs);
1159 void emitComment(StringAttr comment);
1163 void emitPendingNewlineIfNeeded() {
1164 if (state.pendingNewline) {
1165 state.pendingNewline =
false;
1169 void setPendingNewline() {
1170 assert(!state.pendingNewline);
1171 state.pendingNewline =
true;
1174 void startStatement() { emitPendingNewlineIfNeeded(); }
1177 void operator=(
const EmitterBase &) =
delete;
1178 EmitterBase(
const EmitterBase &) =
delete;
1182template <
typename PPS>
1183void EmitterBase::emitTextWithSubstitutions(
1184 PPS &ps, StringRef
string, Operation *op,
1185 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1196 if (
auto *itemOp = item.getOp()) {
1197 if (item.hasPort()) {
1201 if (!symOpName.empty())
1203 emitError(itemOp,
"cannot get name for symbol ") << sym;
1205 emitError(op,
"cannot get name for symbol ") << sym;
1207 return StringRef(
"<INVALID>");
1213 unsigned numSymOps = symAttrs.size();
1214 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1217 next =
string.find(
"{{", next);
1218 if (next == StringRef::npos)
1225 while (next <
string.size() &&
isdigit(
string[next]))
1228 if (start == next) {
1232 size_t operandNoLength = next - start;
1235 StringRef fmtOptsStr;
1236 if (
string[next] ==
':') {
1237 size_t startFmtOpts = next + 1;
1238 while (next <
string.size() &&
string[next] !=
'}')
1240 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1244 if (!
string.substr(next).starts_with(
"}}"))
1248 unsigned operandNo = 0;
1249 if (
string.drop_front(start)
1250 .take_front(operandNoLength)
1251 .getAsInteger(10, operandNo)) {
1252 emitError(op,
"operand substitution too large");
1258 auto before =
string.take_front(start - 2);
1259 if (!before.empty())
1264 if (operandNo < op->getNumOperands())
1266 operandEmitter(op->getOperand(operandNo));
1267 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1268 unsigned symOpNum = operandNo - op->getNumOperands();
1269 auto sym = symAttrs[symOpNum];
1270 StringRef symVerilogName;
1271 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1272 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1273 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1274 auto namepath = globalRef.getNamepathAttr().getValue();
1275 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1278 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1280 auto innerRef = cast<InnerRefAttr>(sym);
1281 auto ref = state.symbolCache.getInnerDefinition(
1282 innerRef.getModule(), innerRef.getName());
1283 ps << namify(innerRef, ref);
1286 symVerilogName = namify(sym, symOp);
1289 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1290 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1292 symVerilogName = namify(sym, symOp);
1294 if (!symVerilogName.empty())
1297 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1301 string =
string.drop_front(next);
1307 while (emitUntilSubstitution())
1311 if (!
string.
empty())
1315void EmitterBase::emitComment(StringAttr comment) {
1322 std::optional<size_t> lineLength = state.options.getEmittedLineLength();
1324 lineLength = std::max<size_t>(*lineLength, 3) - 3;
1328 auto ref = comment.getValue();
1330 while (!ref.empty()) {
1331 std::tie(line, ref) = ref.split(
"\n");
1338 if (!lineLength || line.size() <= lineLength) {
1340 setPendingNewline();
1351 auto breakPos = line.rfind(
' ', *lineLength);
1353 if (breakPos == StringRef::npos) {
1354 breakPos = line.find(
' ', *lineLength);
1357 if (breakPos == StringRef::npos)
1358 breakPos = line.size();
1365 setPendingNewline();
1366 breakPos = line.find_first_not_of(
' ', breakPos);
1368 if (breakPos == StringRef::npos)
1371 line = line.drop_front(breakPos);
1381 bool addPrefixUnderScore =
true;
1384 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1388 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1390 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1392 result = StringAttr::get(expr.getContext(), name);
1394 }
else if (
auto *op = expr.getDefiningOp()) {
1396 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1398 result = StringAttr::get(expr.getContext(), name);
1400 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1406 addPrefixUnderScore =
false;
1408 TypeSwitch<Operation *>(op)
1411 .Case([&result](VerbatimExprOp verbatim) {
1412 verbatim.getAsmResultNames([&](Value, StringRef name) {
1413 result = StringAttr::get(verbatim.getContext(), name);
1416 .Case([&result](VerbatimExprSEOp verbatim) {
1417 verbatim.getAsmResultNames([&](Value, StringRef name) {
1418 result = StringAttr::get(verbatim.getContext(), name);
1424 if (
auto operandName =
1427 cast<IntegerType>(extract.getType()).getWidth();
1429 result = StringAttr::get(extract.getContext(),
1430 operandName.strref() +
"_" +
1431 Twine(extract.getLowBit()));
1433 result = StringAttr::get(
1434 extract.getContext(),
1435 operandName.strref() +
"_" +
1436 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1437 Twine(extract.getLowBit()));
1445 if (!result || result.strref().empty())
1449 if (addPrefixUnderScore && result.strref().front() !=
'_')
1450 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1462class ModuleEmitter :
public EmitterBase {
1464 explicit ModuleEmitter(VerilogEmitterState &state)
1465 : EmitterBase(state), currentModuleOp(nullptr),
1469 emitPendingNewlineIfNeeded();
1473 void emitParameters(Operation *module, ArrayAttr params);
1474 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1475 bool emitAsTwoStateType =
false);
1478 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1479 void emitFunc(FuncOp);
1482 void emitStatement(Operation *op);
1483 void emitBind(BindOp op);
1484 void emitBindInterface(BindInterfaceOp op);
1486 void emitSVAttributes(Operation *op);
1489 StringRef getVerilogStructFieldName(StringAttr field) {
1490 return fieldNameResolver.getRenamedFieldName(field).getValue();
1497 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1509 bool printPackedType(Type type, raw_ostream &os, Location loc,
1510 Type optionalAliasType = {},
bool implicitIntType =
true,
1511 bool singleBitDefaultType =
true,
1512 bool emitAsTwoStateType =
false);
1516 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1524 function_ref<InFlightDiagnostic()> emitError);
1527 VerilogPrecedence parenthesizeIfLooserThan,
1528 function_ref<InFlightDiagnostic()> emitError);
1534 Operation *currentModuleOp;
1535 Operation *currentPackage =
nullptr;
1541 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1547 SmallPtrSet<Operation *, 16> assignsInlined;
1556 const ModuleEmitter &emitter) {
1557 if (isa<RegOp>(op)) {
1562 cast<InOutType>(op->getResult(0).getType()).getElementType();
1565 while (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(
elementType))
1567 while (
auto arrayType = hw::type_dyn_cast<ArrayType>(
elementType))
1570 if (isa<StructType, UnionType, EnumType, TypeAliasType>(
elementType))
1575 if (isa<sv::WireOp>(op))
1577 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1578 return "localparam";
1581 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1582 return interface.getInterfaceType().getInterface().getValue();
1590 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1592 if (isa<LogicOp>(op)) {
1598 if (isProcedural && !stripAutomatic)
1599 return hasStruct ?
"automatic" :
"automatic logic";
1600 return hasStruct ?
"" :
"logic";
1607 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1610 assert(!emitter.state.options.disallowLocalVariables &&
1611 "automatic variables not allowed");
1615 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1616 :
"automatic logic";
1623static void emitDim(Attribute width, raw_ostream &os, Location loc,
1624 ModuleEmitter &emitter,
bool downTo) {
1626 os <<
"<<invalid type>>";
1629 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1630 if (intAttr.getValue().isZero()) {
1631 os <<
"/*Zero Width*/";
1636 os << (intAttr.getValue().getZExtValue() - 1);
1646 auto typedAttr = dyn_cast<TypedAttr>(width);
1648 emitter.emitError(loc,
"untyped dimension attribute ") << width;
1652 getIntAttr(loc.getContext(), typedAttr.getType(),
1653 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1654 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1658 emitter.printParamValue(width, os, [loc, &emitter]() {
1659 return emitter.emitError(loc,
"invalid parameter in type");
1667static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1668 ModuleEmitter &emitter) {
1669 for (Attribute width : dims) {
1670 emitDim(width, os, loc, emitter,
true);
1675void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1676 SmallVector<Attribute, 4> dims;
1678 [&](Location loc) {
return this->emitError(loc); });
1709 SmallVectorImpl<Attribute> &dims,
1710 bool implicitIntType,
bool singleBitDefaultType,
1711 ModuleEmitter &emitter,
1712 Type optionalAliasType = {},
1713 bool emitAsTwoStateType =
false) {
1714 return TypeSwitch<Type, bool>(type)
1715 .Case<IntegerType>([&](IntegerType integerType) ->
bool {
1716 if (emitAsTwoStateType && dims.empty()) {
1718 if (!typeName.empty()) {
1723 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1725 getInt32Attr(type.getContext(), integerType.getWidth()));
1727 StringRef typeName =
1728 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1729 if (!typeName.empty()) {
1736 return !dims.empty() || !implicitIntType;
1738 .Case<IntType>([&](IntType intType) {
1739 if (!implicitIntType)
1741 dims.push_back(intType.getWidth());
1745 .Case<ArrayType>([&](ArrayType arrayType) {
1746 dims.push_back(arrayType.getSizeAttr());
1748 implicitIntType, singleBitDefaultType,
1750 emitAsTwoStateType);
1752 .Case<InOutType>([&](InOutType inoutType) {
1754 implicitIntType, singleBitDefaultType,
1756 emitAsTwoStateType);
1758 .Case<EnumType>([&](EnumType enumType) {
1759 assert(enumType.getBitWidth().has_value() &&
1760 "enum type must have bitwidth");
1762 if (enumType.getBitWidth() != 32)
1763 os <<
"bit [" << *enumType.getBitWidth() - 1 <<
":0] ";
1765 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1766 llvm::interleaveComma(
1767 enumType.getFields().getAsRange<StringAttr>(), os,
1768 [&](
auto enumerator) {
1769 os << emitter.fieldNameResolver.getEnumFieldName(
1770 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType),
1771 emitter.currentPackage);
1776 .Case<StructType>([&](StructType structType) {
1777 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1778 os <<
"/*Zero Width*/";
1781 os <<
"struct packed {";
1782 for (
auto &element : structType.getElements()) {
1784 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1785 <<
": Zero Width;*/ ";
1788 SmallVector<Attribute, 8> structDims;
1793 {}, emitAsTwoStateType);
1794 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1795 emitter.printUnpackedTypePostfix(element.type, os);
1802 .Case<UnionType>([&](UnionType unionType) {
1803 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1804 os <<
"/*Zero Width*/";
1808 int64_t unionWidth = hw::getBitWidth(unionType);
1809 os <<
"union packed {";
1810 for (
auto &element : unionType.getElements()) {
1812 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1813 <<
": Zero Width;*/ ";
1816 int64_t elementWidth = hw::getBitWidth(element.type);
1817 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1819 os <<
" struct packed {";
1820 if (element.offset) {
1821 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1822 << element.offset - 1 <<
":0] "
1823 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1827 SmallVector<Attribute, 8> structDims;
1831 true, emitter, {}, emitAsTwoStateType);
1832 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1833 emitter.printUnpackedTypePostfix(element.type, os);
1837 if (elementWidth + (int64_t)element.offset < unionWidth) {
1838 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1839 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1840 <<
"__post_padding_" << element.name.getValue() <<
";";
1842 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1851 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1852 .Case<ModportType>([&](ModportType modportType) {
1853 auto modportAttr = modportType.getModport();
1854 os << modportAttr.getRootReference().getValue() <<
"."
1855 << modportAttr.getNestedReferences().front().getValue();
1858 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1859 os <<
"<<unexpected unpacked array>>";
1860 emitter.emitError(loc,
"Unexpected unpacked array in packed type ")
1864 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1865 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1867 emitter.emitError(loc,
"unresolvable type reference");
1870 if (typedecl.getType() != typeRef.getInnerType()) {
1871 emitter.emitError(loc,
"declared type did not match aliased type");
1875 if (
auto package = dyn_cast<PackageOp>(typedecl->getParentOp())) {
1876 if (package != emitter.currentPackage)
1879 os << typedecl.getPreferredName();
1880 emitDims(dims, os, typedecl->getLoc(), emitter);
1883 .Default([&](Type type) {
1884 os <<
"<<invalid type '" << type <<
"'>>";
1885 emitter.emitError(loc,
"value has an unsupported verilog type ")
1902bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1903 Type optionalAliasType,
1904 bool implicitIntType,
1905 bool singleBitDefaultType,
1906 bool emitAsTwoStateType) {
1907 SmallVector<Attribute, 8> packedDimensions;
1909 singleBitDefaultType, *
this, optionalAliasType,
1910 emitAsTwoStateType);
1916void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1917 TypeSwitch<Type, void>(type)
1919 printUnpackedTypePostfix(inoutType.getElementType(), os);
1921 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1922 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1923 : state.designOp->getLoc();
1924 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1926 printUnpackedTypePostfix(arrayType.getElementType(), os);
1928 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1930 printUnpackedTypePostfix(arrayType.getElementType(), os);
1932 .Case<InterfaceType>([&](
auto) {
1946ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1947 function_ref<InFlightDiagnostic()> emitError) {
1948 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1956ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1957 VerilogPrecedence parenthesizeIfLooserThan,
1958 function_ref<InFlightDiagnostic()> emitError) {
1959 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1960 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1961 APInt value = intAttr.getValue();
1965 if (intTy.getWidth() > 32) {
1967 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1971 if (intTy.isSigned())
1972 os << intTy.getWidth() <<
"'sd";
1974 os << intTy.getWidth() <<
"'d";
1976 value.print(os, intTy.isSigned());
1977 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1979 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1981 os.write_escaped(strAttr.getValue());
1983 return {Symbol, IsUnsigned};
1985 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1987 os << fpAttr.getValueAsDouble();
1988 return {Symbol, IsUnsigned};
1990 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1991 os << verbatimParam.getValue().getValue();
1992 return {Symbol, IsUnsigned};
1994 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1996 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1997 parameterRef.getName());
2000 return {Symbol, IsUnsigned};
2004 auto expr = dyn_cast<ParamExprAttr>(value);
2006 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
2007 emitError() <<
" = " << value;
2008 return {LowestPrecedence, IsUnsigned};
2011 StringRef operatorStr;
2012 StringRef openStr, closeStr;
2013 VerilogPrecedence subprecedence = LowestPrecedence;
2014 VerilogPrecedence prec;
2015 std::optional<SubExprSignResult> operandSign;
2016 bool isUnary =
false;
2017 bool hasOpenClose =
false;
2019 switch (expr.getOpcode()) {
2021 operatorStr =
" + ";
2022 subprecedence = Addition;
2025 operatorStr =
" * ";
2026 subprecedence = Multiply;
2029 operatorStr =
" & ";
2030 subprecedence = And;
2033 operatorStr =
" | ";
2037 operatorStr =
" ^ ";
2038 subprecedence = Xor;
2041 operatorStr =
" << ";
2042 subprecedence = Shift;
2046 operatorStr =
" >> ";
2047 subprecedence = Shift;
2051 operatorStr =
" >>> ";
2052 subprecedence = Shift;
2053 operandSign = IsSigned;
2056 operatorStr =
" / ";
2057 subprecedence = Multiply;
2058 operandSign = IsUnsigned;
2061 operatorStr =
" / ";
2062 subprecedence = Multiply;
2063 operandSign = IsSigned;
2066 operatorStr =
" % ";
2067 subprecedence = Multiply;
2068 operandSign = IsUnsigned;
2071 operatorStr =
" % ";
2072 subprecedence = Multiply;
2073 operandSign = IsSigned;
2076 openStr =
"$clog2(";
2078 operandSign = IsUnsigned;
2079 hasOpenClose =
true;
2082 case PEO::StrConcat:
2085 hasOpenClose =
true;
2088 subprecedence = LowestPrecedence;
2093 prec = subprecedence;
2096 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2098 assert(isUnary || hasOpenClose ||
2099 !llvm::hasSingleElement(expr.getOperands()));
2106 auto emitOperand = [&](Attribute operand) ->
bool {
2108 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2109 if (operandSign.has_value())
2110 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2113 if (operandSign.has_value()) {
2115 signedness = *operandSign;
2117 return signedness == IsSigned;
2121 if (prec > parenthesizeIfLooserThan)
2130 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2131 for (
auto op : expr.getOperands().drop_front()) {
2134 if (expr.getOpcode() == PEO::Add) {
2135 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2136 const APInt &value = integer.getValue();
2137 if (value.isNegative() && !value.isMinSignedValue()) {
2139 allOperandsSigned &=
2140 emitOperand(IntegerAttr::get(op.getType(), -value));
2147 allOperandsSigned &= emitOperand(op);
2151 if (prec > parenthesizeIfLooserThan) {
2155 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2170class ExprEmitter :
public EmitterBase,
2172 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2177 ExprEmitter(ModuleEmitter &emitter,
2178 SmallPtrSetImpl<Operation *> &emittedExprs)
2179 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2181 ExprEmitter(ModuleEmitter &emitter,
2182 SmallPtrSetImpl<Operation *> &emittedExprs,
2184 : EmitterBase(emitter.state), emitter(emitter),
2185 emittedExprs(emittedExprs), buffer(tokens),
2186 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2187 assert(state.pp.getListener() == &state.saver);
2194 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2195 bool isAssignmentLikeContext) {
2196 assert(localTokens.empty());
2198 ps.scopedBox(PP::ibox0, [&]() {
2201 emitSubExpr(exp, parenthesizeIfLooserThan,
2203 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2205 isAssignmentLikeContext);
2210 if (&buffer.tokens == &localTokens)
2211 buffer.flush(state.pp);
2216 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2217 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2219 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2227 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2228 SubExprSignRequirement signReq = NoRequirement,
2229 bool isSelfDeterminedUnsignedValue =
false,
2230 bool isAssignmentLikeContext =
false);
2234 void emitSVAttributes(Operation *op);
2236 SubExprInfo visitUnhandledExpr(Operation *op);
2237 SubExprInfo visitInvalidComb(Operation *op) {
2240 SubExprInfo visitUnhandledComb(Operation *op) {
2241 return visitUnhandledExpr(op);
2244 return dispatchSVVisitor(op);
2247 return visitUnhandledExpr(op);
2249 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2252 enum EmitBinaryFlags {
2253 EB_RequireSignedOperands = RequireSigned,
2254 EB_RequireUnsignedOperands = RequireUnsigned,
2255 EB_OperandSignRequirementMask = 0x3,
2260 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2264 EB_ForceResultSigned = 0x8,
2269 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2270 const char *syntax,
unsigned emitBinaryFlags = 0);
2272 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2273 bool resultAlwaysUnsigned =
false);
2276 void emitSubExprIBox2(
2277 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2278 ps.scopedBox(PP::ibox2,
2279 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2284 template <
typename Container,
typename EachFn>
2285 void interleaveComma(
const Container &c, EachFn eachFn) {
2286 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2291 void interleaveComma(ValueRange ops) {
2292 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2309 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2311 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2312 CloseFunc closeFn) {
2314 ps.scopedBox(PP::cbox0, [&]() {
2315 interleaveComma(c, eachFn);
2321 template <
typename OpenFunc,
typename CloseFunc>
2322 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2323 return emitBracedList(
2324 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2328 void emitBracedList(ValueRange ops) {
2329 return emitBracedList(
2330 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2334 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2337 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2338 bool printAsPattern, Operation *op);
2340 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2341 ArrayAttr fieldValues,
bool printAsPattern,
2344 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2346 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2347 SubExprInfo visitSV(GetModportOp op);
2348 SubExprInfo visitSV(SystemFunctionOp op);
2349 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2350 SubExprInfo visitSV(XMROp op);
2351 SubExprInfo visitSV(SFormatFOp op);
2352 SubExprInfo visitSV(XMRRefOp op);
2353 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2354 SubExprInfo visitSV(VerbatimExprOp op) {
2355 return visitVerbatimExprOp(op, op.getSymbols());
2357 SubExprInfo visitSV(VerbatimExprSEOp op) {
2358 return visitVerbatimExprOp(op, op.getSymbols());
2360 SubExprInfo visitSV(MacroRefExprOp op);
2361 SubExprInfo visitSV(MacroRefExprSEOp op);
2362 template <
typename MacroTy>
2363 SubExprInfo emitMacroCall(MacroTy op);
2365 SubExprInfo visitSV(ConstantXOp op);
2366 SubExprInfo visitSV(ConstantZOp op);
2367 SubExprInfo visitSV(ConstantStrOp op);
2368 SubExprInfo visitSV(ConcatStrOp op);
2370 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2371 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2373 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2378 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2379 emitSVAttributes(op);
2382 SubExprInfo visitSV(ArrayIndexInOutOp op);
2383 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2384 SubExprInfo visitSV(IndexedPartSelectOp op);
2385 SubExprInfo visitSV(StructFieldInOutOp op);
2388 SubExprInfo visitSV(SampledOp op);
2391 SubExprInfo visitSV(TimeOp op);
2392 SubExprInfo visitSV(STimeOp op);
2395 using TypeOpVisitor::visitTypeOp;
2397 SubExprInfo visitTypeOp(AggregateConstantOp op);
2399 SubExprInfo visitTypeOp(ParamValueOp op);
2406 SubExprInfo visitTypeOp(StructInjectOp op);
2407 SubExprInfo visitTypeOp(UnionCreateOp op);
2408 SubExprInfo visitTypeOp(UnionExtractOp op);
2409 SubExprInfo visitTypeOp(EnumCmpOp op);
2410 SubExprInfo visitTypeOp(EnumConstantOp op);
2413 using CombinationalVisitor::visitComb;
2414 SubExprInfo visitComb(
MuxOp op);
2415 SubExprInfo visitComb(ReverseOp op);
2416 SubExprInfo visitComb(
AddOp op) {
2417 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2418 return emitBinary(op, Addition,
"+");
2420 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2421 SubExprInfo visitComb(
MulOp op) {
2422 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2423 return emitBinary(op, Multiply,
"*");
2425 SubExprInfo visitComb(
DivUOp op) {
2426 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2428 SubExprInfo visitComb(
DivSOp op) {
2429 return emitBinary(op, Multiply,
"/",
2430 EB_RequireSignedOperands | EB_ForceResultSigned);
2432 SubExprInfo visitComb(
ModUOp op) {
2433 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2435 SubExprInfo visitComb(
ModSOp op) {
2436 return emitBinary(op, Multiply,
"%",
2437 EB_RequireSignedOperands | EB_ForceResultSigned);
2439 SubExprInfo visitComb(
ShlOp op) {
2440 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2442 SubExprInfo visitComb(
ShrUOp op) {
2444 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2446 SubExprInfo visitComb(
ShrSOp op) {
2449 return emitBinary(op, Shift,
">>>",
2450 EB_RequireSignedOperands | EB_ForceResultSigned |
2451 EB_RHS_UnsignedWithSelfDeterminedWidth);
2453 SubExprInfo visitComb(
AndOp op) {
2454 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2455 return emitBinary(op, And,
"&");
2457 SubExprInfo visitComb(
OrOp op) {
2458 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2459 return emitBinary(op, Or,
"|");
2461 SubExprInfo visitComb(
XorOp op) {
2462 if (op.isBinaryNot())
2463 return emitUnary(op,
"~");
2464 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2465 return emitBinary(op, Xor,
"^");
2470 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2472 SubExprInfo visitComb(ReplicateOp op);
2473 SubExprInfo visitComb(
ConcatOp op);
2475 SubExprInfo visitComb(ICmpOp op);
2477 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2478 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2479 "not printed within an assignment-like context");
2480 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2481 "supposed to spill such expressions";
2485 SubExprInfo printStructCreate(
2486 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2488 bool printAsPattern, Operation *op);
2491 ModuleEmitter &emitter;
2498 SubExprSignRequirement signPreference = NoRequirement;
2502 SmallPtrSetImpl<Operation *> &emittedExprs;
2505 SmallVector<Token> localTokens;
2519 bool isAssignmentLikeContext =
false;
2523SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2525 unsigned emitBinaryFlags) {
2527 emitError(op,
"SV attributes emission is unimplemented for the op");
2538 if (emitBinaryFlags & EB_ForceResultSigned)
2539 ps <<
"$signed(" << PP::ibox0;
2540 auto operandSignReq =
2541 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2542 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2545 auto lhsSpace = (prec == VerilogPrecedence::Comparison ||
2546 prec == VerilogPrecedence::Equality)
2550 ps << lhsSpace << syntax << PP::nbsp;
2557 auto rhsPrec = prec;
2558 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2559 rhsPrec = VerilogPrecedence(prec - 1);
2564 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2565 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2566 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2567 operandSignReq = NoRequirement;
2570 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2571 rhsIsUnsignedValueWithSelfDeterminedWidth);
2575 SubExprSignResult signedness = IsUnsigned;
2576 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2577 signedness = IsSigned;
2579 if (emitBinaryFlags & EB_ForceResultSigned) {
2580 ps << PP::end <<
")";
2581 signedness = IsSigned;
2585 return {prec, signedness};
2588SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2589 bool resultAlwaysUnsigned) {
2591 emitError(op,
"SV attributes emission is unimplemented for the op");
2594 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2598 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2599 resultAlwaysUnsigned ? IsUnsigned : signedness};
2604void ExprEmitter::emitSVAttributes(Operation *op) {
2618 auto concat = value.getDefiningOp<
ConcatOp>();
2619 if (!concat || concat.getNumOperands() != 2)
2622 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2623 if (constant && constant.getValue().isZero())
2624 return concat.getOperand(1);
2634SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2635 VerilogPrecedence parenthesizeIfLooserThan,
2636 SubExprSignRequirement signRequirement,
2637 bool isSelfDeterminedUnsignedValue,
2638 bool isAssignmentLikeContext) {
2640 if (
auto result = dyn_cast<OpResult>(exp))
2641 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2642 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2643 parenthesizeIfLooserThan, signRequirement,
2644 isSelfDeterminedUnsignedValue,
2645 isAssignmentLikeContext);
2649 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2654 auto *op = exp.getDefiningOp();
2658 if (!shouldEmitInlineExpr) {
2661 if (signRequirement == RequireSigned) {
2663 return {Symbol, IsSigned};
2667 return {Symbol, IsUnsigned};
2670 unsigned subExprStartIndex = buffer.tokens.size();
2672 ps.addCallback({op,
true});
2673 llvm::scope_exit done([&]() {
2675 ps.addCallback({op, false});
2681 signPreference = signRequirement;
2683 bool bitCastAdded =
false;
2684 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2686 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2687 ps.addAsString(inType.getWidth());
2688 ps <<
"'(" << PP::ibox0;
2689 bitCastAdded =
true;
2693 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2694 isAssignmentLikeContext);
2695 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2701 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2703 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2705 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2706 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2709 expInfo.signedness = IsSigned;
2710 expInfo.precedence = Selection;
2711 }
else if (signRequirement == RequireUnsigned &&
2712 expInfo.signedness == IsSigned) {
2715 expInfo.signedness = IsUnsigned;
2716 expInfo.precedence = Selection;
2717 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2724 expInfo.precedence = Selection;
2731 emittedExprs.insert(exp.getDefiningOp());
2735SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2736 auto openFn = [&]() {
2738 ps.addAsString(op.getMultiple());
2741 auto closeFn = [&]() { ps <<
"}}"; };
2745 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2746 if (op.getOperand().hasOneUse()) {
2747 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2748 return {Symbol, IsUnsigned};
2751 emitBracedList(op.getOperand(), openFn, closeFn);
2752 return {Symbol, IsUnsigned};
2755SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2756 emitBracedList(op.getOperands());
2757 return {Symbol, IsUnsigned};
2760SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2764 Type toType = op.getType();
2766 toType, op.getInput().getType(), op.getLoc(),
2767 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2769 ps.invokeWithStringOS(
2770 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2773 return emitSubExpr(op.getInput(), LowestPrecedence);
2776SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2777 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2778 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2779 SubExprSignRequirement signop[] = {
2781 NoRequirement, NoRequirement,
2783 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2785 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2787 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2789 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2790 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2793 if (op.isEqualAllOnes())
2794 return emitUnary(op,
"&",
true);
2797 if (op.isNotEqualZero())
2798 return emitUnary(op,
"|",
true);
2800 VerilogPrecedence precedence = Comparison;
2801 switch (op.getPredicate()) {
2802 case ICmpPredicate::eq:
2803 case ICmpPredicate::ne:
2804 case ICmpPredicate::ceq:
2805 case ICmpPredicate::cne:
2806 case ICmpPredicate::weq:
2807 case ICmpPredicate::wne:
2808 precedence = Equality;
2811 precedence = Comparison;
2814 auto result = emitBinary(op, precedence, symop[pred], signop[pred]);
2818 result.signedness = IsUnsigned;
2822SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2824 emitError(op,
"SV attributes emission is unimplemented for the op");
2826 unsigned loBit = op.getLowBit();
2827 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2829 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2830 assert((x.precedence == Symbol ||
2832 "should be handled by isExpressionUnableToInline");
2837 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2841 ps.addAsString(hiBit);
2842 if (hiBit != loBit) {
2844 ps.addAsString(loBit);
2847 return {Unary, IsUnsigned};
2850SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2852 emitError(op,
"SV attributes emission is unimplemented for the op");
2854 auto decl = op.getReferencedDecl(state.symbolCache);
2857 return {Selection, IsUnsigned};
2860SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2862 emitError(op,
"SV attributes emission is unimplemented for the op");
2865 ps.scopedBox(PP::ibox0, [&]() {
2867 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2868 [&]() { ps <<
"," << PP::space; });
2871 return {Symbol, IsUnsigned};
2874SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2876 emitError(op,
"SV attributes emission is unimplemented for the op");
2878 auto decl = op.getReferencedDecl(state.symbolCache);
2882 return {Selection, IsUnsigned};
2885SubExprInfo ExprEmitter::visitSV(XMROp op) {
2887 emitError(op,
"SV attributes emission is unimplemented for the op");
2889 if (op.getIsRooted())
2891 for (
auto s : op.getPath())
2892 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2894 return {Selection, IsUnsigned};
2899SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2901 emitError(op,
"SV attributes emission is unimplemented for the op");
2904 auto globalRef = op.getReferencedPath(&state.symbolCache);
2905 auto namepath = globalRef.getNamepathAttr().getValue();
2906 auto *
module = state.symbolCache.getDefinition(
2907 cast<InnerRefAttr>(namepath.front()).getModule());
2909 for (
auto sym : namepath) {
2911 auto innerRef = cast<InnerRefAttr>(sym);
2912 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2913 innerRef.getName());
2914 if (ref.hasPort()) {
2920 auto leaf = op.getVerbatimSuffixAttr();
2921 if (leaf && leaf.size())
2923 return {Selection, IsUnsigned};
2926SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2928 emitError(op,
"SV attributes emission is unimplemented for the op");
2930 emitTextWithSubstitutions(
2931 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2932 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2934 return {Unary, IsUnsigned};
2937template <
typename MacroTy>
2938SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2940 emitError(op,
"SV attributes emission is unimplemented for the op");
2943 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2944 assert(macroOp &&
"Invalid IR");
2946 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2948 if (!op.getInputs().empty()) {
2950 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2951 emitExpression(val, LowestPrecedence, false);
2955 return {LowestPrecedence, IsUnsigned};
2958SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2959 return emitMacroCall(op);
2962SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2963 return emitMacroCall(op);
2966SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2968 emitError(op,
"SV attributes emission is unimplemented for the op");
2970 ps.addAsString(op.getWidth());
2972 return {Unary, IsUnsigned};
2975SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2977 emitError(op,
"SV attributes emission is unimplemented for the op");
2979 ps.writeQuotedEscaped(op.getStr());
2980 return {Symbol, IsUnsigned};
2983SubExprInfo ExprEmitter::visitSV(ConcatStrOp op) {
2985 emitError(op,
"SV attributes emission is unimplemented for the op");
2989 emitBracedList(op.getInputs());
2990 return {Symbol, IsUnsigned};
2993SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2995 emitError(op,
"SV attributes emission is unimplemented for the op");
2997 ps.addAsString(op.getWidth());
2999 return {Unary, IsUnsigned};
3002SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
3003 bool isNegated =
false;
3006 if (signPreference == RequireSigned && value.isNegative() &&
3007 !value.isMinSignedValue()) {
3012 ps.addAsString(type.getWidth());
3016 if (signPreference == RequireSigned)
3022 SmallString<32> valueStr;
3024 (-value).toStringUnsigned(valueStr, 16);
3026 value.toStringUnsigned(valueStr, 16);
3029 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
3032SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
3034 emitError(op,
"SV attributes emission is unimplemented for the op");
3036 auto value = op.getValue();
3040 if (value.getBitWidth() == 0) {
3041 emitOpError(op,
"will not emit zero width constants in the general case");
3042 ps <<
"<<unsupported zero width constant: "
3043 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3044 return {Unary, IsUnsigned};
3047 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3050void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3051 bool printAsPattern, Operation *op) {
3052 if (printAsPattern && !isAssignmentLikeContext)
3053 emitAssignmentPatternContextError(op);
3054 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3057 elementValues, [&]() { ps << openDelim; },
3058 [&](Attribute elementValue) {
3059 printConstantAggregate(elementValue,
elementType, op);
3061 [&]() { ps <<
"}"; });
3064void ExprEmitter::printConstantStruct(
3065 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3066 bool printAsPattern, Operation *op) {
3067 if (printAsPattern && !isAssignmentLikeContext)
3068 emitAssignmentPatternContextError(op);
3075 auto fieldRange = llvm::make_filter_range(
3076 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3081 if (printAsPattern) {
3083 fieldRange, [&]() { ps <<
"'{"; },
3084 [&](
const auto &fieldAndValue) {
3085 ps.scopedBox(PP::ibox2, [&]() {
3086 const auto &[field, value] = fieldAndValue;
3087 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3088 <<
":" << PP::space;
3089 printConstantAggregate(value, field.type, op);
3092 [&]() { ps <<
"}"; });
3095 fieldRange, [&]() { ps <<
"{"; },
3096 [&](
const auto &fieldAndValue) {
3097 ps.scopedBox(PP::ibox2, [&]() {
3098 const auto &[field, value] = fieldAndValue;
3099 printConstantAggregate(value, field.type, op);
3102 [&]() { ps <<
"}"; });
3106void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3109 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3110 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3111 isAssignmentLikeContext, op);
3114 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3115 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3119 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3120 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3121 isAssignmentLikeContext, op);
3123 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3124 auto value = cast<IntegerAttr>(attr).getValue();
3125 printConstantScalar(value, intType);
3129 emitOpError(op,
"contains constant of type ")
3130 << type <<
" which cannot be emitted as Verilog";
3133SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3135 emitError(op,
"SV attributes emission is unimplemented for the op");
3139 "zero-bit types not allowed at this point");
3141 printConstantAggregate(op.getFields(), op.getType(), op);
3142 return {Symbol, IsUnsigned};
3145SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3147 emitError(op,
"SV attributes emission is unimplemented for the op");
3149 return ps.invokeWithStringOS([&](
auto &os) {
3150 return emitter.printParamValue(op.getValue(), os, [&]() {
3151 return op->emitOpError(
"invalid parameter use");
3160 emitError(op,
"SV attributes emission is unimplemented for the op");
3162 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3164 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3166 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3168 ps.addAsString(dstWidth);
3170 return {Selection, arrayPrec.signedness};
3173SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3174 emitSubExpr(op.getInput(), Selection);
3179 emitSubExpr(op.getIndex(), LowestPrecedence);
3181 emitSVAttributes(op);
3182 return {Selection, IsUnsigned};
3188 emitError(op,
"SV attributes emission is unimplemented for the op");
3190 if (op.isUniform()) {
3192 ps.addAsString(op.getInputs().size());
3194 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3198 op.getInputs(), [&]() { ps <<
"{"; },
3201 emitSubExprIBox2(v);
3204 [&]() { ps <<
"}"; });
3206 return {Unary, IsUnsigned};
3209SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3211 emitError(op,
"SV attributes emission is unimplemented for the op");
3214 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3215 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3216 return {Unary, IsUnsigned};
3221 emitError(op,
"SV attributes emission is unimplemented for the op");
3223 emitBracedList(op.getOperands());
3224 return {Unary, IsUnsigned};
3227SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3229 emitError(op,
"SV attributes emission is unimplemented for the op");
3231 auto index = op.getIndex();
3232 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3237 emitSubExpr(index, LowestPrecedence);
3239 return {Selection, arrayPrec.signedness};
3242SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3244 emitError(op,
"SV attributes emission is unimplemented for the op");
3246 auto prec = emitSubExpr(op.getInput(), Selection);
3248 emitSubExpr(op.getBase(), LowestPrecedence);
3249 if (op.getDecrement())
3253 ps.addAsString(op.getWidth());
3255 return {Selection, prec.signedness};
3258SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3260 emitError(op,
"SV attributes emission is unimplemented for the op");
3262 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3264 emitSubExpr(op.getBase(), LowestPrecedence);
3265 if (op.getDecrement())
3269 ps.addAsString(op.getWidth());
3274SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3276 emitError(op,
"SV attributes emission is unimplemented for the op");
3278 auto prec = emitSubExpr(op.getInput(), Selection);
3280 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3281 return {Selection, prec.signedness};
3284SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3286 emitError(op,
"SV attributes emission is unimplemented for the op");
3289 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3294SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3296 emitError(op,
"SV attributes emission is unimplemented for the op");
3299 ps.scopedBox(PP::ibox0, [&]() {
3300 ps.writeQuotedEscaped(op.getFormatString());
3307 for (
auto operand : op.getSubstitutions()) {
3308 ps <<
"," << PP::space;
3309 emitSubExpr(operand, LowestPrecedence);
3313 return {Symbol, IsUnsigned};
3316SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3318 emitError(op,
"SV attributes emission is unimplemented for the op");
3321 return {Symbol, IsUnsigned};
3324SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3326 emitError(op,
"SV attributes emission is unimplemented for the op");
3329 return {Symbol, IsUnsigned};
3332SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3346 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3347 ps.scopedBox(PP::ibox0, [&]() {
3348 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3352 emitSVAttributes(op);
3354 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3355 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3359 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3360 return emitSubExpr(op.getFalseValue(), Conditional);
3363 SubExprSignResult signedness = IsUnsigned;
3364 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3365 signedness = IsSigned;
3367 return {Conditional, signedness};
3371SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3373 emitError(op,
"SV attributes emission is unimplemented for the op");
3376 emitSubExpr(op.getInput(), LowestPrecedence);
3379 return {Symbol, IsUnsigned};
3382SubExprInfo ExprEmitter::printStructCreate(
3383 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3385 bool printAsPattern, Operation *op) {
3386 if (printAsPattern && !isAssignmentLikeContext)
3387 emitAssignmentPatternContextError(op);
3390 auto filteredFields = llvm::make_filter_range(
3391 llvm::enumerate(fieldInfos),
3392 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3394 if (printAsPattern) {
3396 filteredFields, [&]() { ps <<
"'{"; },
3397 [&](
const auto &field) {
3398 ps.scopedBox(PP::ibox2, [&]() {
3400 emitter.getVerilogStructFieldName(field.value().name))
3401 <<
":" << PP::space;
3402 fieldFn(field.value(), field.index());
3405 [&]() { ps <<
"}"; });
3408 filteredFields, [&]() { ps <<
"{"; },
3409 [&](
const auto &field) {
3410 ps.scopedBox(PP::ibox2,
3411 [&]() { fieldFn(field.value(), field.index()); });
3413 [&]() { ps <<
"}"; });
3416 return {Selection, IsUnsigned};
3421 emitError(op,
"SV attributes emission is unimplemented for the op");
3425 bool printAsPattern = isAssignmentLikeContext;
3426 StructType structType = op.getType();
3427 return printStructCreate(
3428 structType.getElements(),
3429 [&](
const auto &field,
auto index) {
3430 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3432 isAssignmentLikeContext);
3434 printAsPattern, op);
3439 emitError(op,
"SV attributes emission is unimplemented for the op");
3441 emitSubExpr(op.getInput(), Selection);
3443 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3444 return {Selection, IsUnsigned};
3447SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3449 emitError(op,
"SV attributes emission is unimplemented for the op");
3453 bool printAsPattern = isAssignmentLikeContext;
3454 StructType structType = op.getType();
3455 return printStructCreate(
3456 structType.getElements(),
3457 [&](
const auto &field,
auto index) {
3458 if (field.name == op.getFieldNameAttr()) {
3459 emitSubExpr(op.getNewValue(), Selection);
3461 emitSubExpr(op.getInput(), Selection);
3463 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3466 printAsPattern, op);
3469SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3470 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3471 return {Selection, IsUnsigned};
3474SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3476 emitError(op,
"SV attributes emission is unimplemented for the op");
3477 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3480 result.signedness = IsUnsigned;
3484SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3486 emitError(op,
"SV attributes emission is unimplemented for the op");
3490 auto unionWidth = hw::getBitWidth(unionType);
3491 auto &element = unionType.getElements()[op.getFieldIndex()];
3492 auto elementWidth = hw::getBitWidth(element.type);
3495 if (!elementWidth) {
3496 ps.addAsString(unionWidth);
3498 return {Unary, IsUnsigned};
3502 if (elementWidth == unionWidth) {
3503 emitSubExpr(op.getInput(), LowestPrecedence);
3504 return {Unary, IsUnsigned};
3509 ps.scopedBox(PP::ibox0, [&]() {
3510 if (
auto prePadding = element.offset) {
3511 ps.addAsString(prePadding);
3512 ps <<
"'h0," << PP::space;
3514 emitSubExpr(op.getInput(), Selection);
3515 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3516 ps <<
"," << PP::space;
3517 ps.addAsString(postPadding);
3523 return {Unary, IsUnsigned};
3526SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3528 emitError(op,
"SV attributes emission is unimplemented for the op");
3529 emitSubExpr(op.getInput(), Selection);
3532 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3533 auto unionWidth = hw::getBitWidth(unionType);
3534 auto &element = unionType.getElements()[op.getFieldIndex()];
3535 auto elementWidth = hw::getBitWidth(element.type);
3536 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3537 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3546 return {Selection, IsUnsigned};
3549SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3550 emitOpError(op,
"cannot emit this expression to Verilog");
3551 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3553 return {Symbol, IsUnsigned};
3569enum class PropertyPrecedence {
3589struct EmittedProperty {
3591 PropertyPrecedence precedence;
3596class PropertyEmitter :
public EmitterBase,
3597 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3601 PropertyEmitter(ModuleEmitter &emitter,
3602 SmallPtrSetImpl<Operation *> &emittedOps)
3603 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3604 PropertyEmitter(ModuleEmitter &emitter,
3605 SmallPtrSetImpl<Operation *> &emittedOps,
3607 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3609 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3610 assert(state.pp.getListener() == &state.saver);
3613 void emitAssertPropertyDisable(
3614 Value property, Value disable,
3615 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3617 void emitAssertPropertyBody(
3618 Value property, Value disable,
3619 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3621 void emitAssertPropertyBody(
3622 Value property, sv::EventControl event, Value clock, Value disable,
3623 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3628 emitNestedProperty(Value property,
3629 PropertyPrecedence parenthesizeIfLooserThan);
3630 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3631 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3633 EmittedProperty visitUnhandledLTL(Operation *op);
3634 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3635 EmittedProperty visitLTL(ltl::AndOp op);
3636 EmittedProperty visitLTL(ltl::OrOp op);
3637 EmittedProperty visitLTL(ltl::IntersectOp op);
3638 EmittedProperty visitLTL(ltl::DelayOp op);
3639 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3640 EmittedProperty visitLTL(ltl::ConcatOp op);
3641 EmittedProperty visitLTL(ltl::RepeatOp op);
3642 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3643 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3644 EmittedProperty visitLTL(ltl::NotOp op);
3645 EmittedProperty visitLTL(ltl::ImplicationOp op);
3646 EmittedProperty visitLTL(ltl::UntilOp op);
3647 EmittedProperty visitLTL(ltl::EventuallyOp op);
3648 EmittedProperty visitLTL(ltl::ClockOp op);
3649 EmittedProperty visitLTL(ltl::WeakOp op);
3650 EmittedProperty visitLTL(ltl::StrongOp op);
3652 EmittedProperty emitWeakStrongOp(StringRef mnemonic, Value input);
3653 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3654 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3655 void emitLTLConcat(ValueRange inputs);
3658 ModuleEmitter &emitter;
3663 SmallPtrSetImpl<Operation *> &emittedOps;
3666 SmallVector<Token> localTokens;
3679void PropertyEmitter::emitAssertPropertyDisable(
3680 Value property, Value disable,
3681 PropertyPrecedence parenthesizeIfLooserThan) {
3684 ps <<
"disable iff" << PP::nbsp <<
"(";
3686 emitNestedProperty(disable, PropertyPrecedence::Unary);
3692 ps.scopedBox(PP::ibox0,
3693 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3699void PropertyEmitter::emitAssertPropertyBody(
3700 Value property, Value disable,
3701 PropertyPrecedence parenthesizeIfLooserThan) {
3702 assert(localTokens.empty());
3704 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3709 if (&buffer.tokens == &localTokens)
3710 buffer.flush(state.pp);
3713void PropertyEmitter::emitAssertPropertyBody(
3714 Value property, sv::EventControl event, Value clock, Value disable,
3715 PropertyPrecedence parenthesizeIfLooserThan) {
3716 assert(localTokens.empty());
3719 ps.scopedBox(PP::ibox2, [&] {
3720 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3721 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3727 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3732 if (&buffer.tokens == &localTokens)
3733 buffer.flush(state.pp);
3736EmittedProperty PropertyEmitter::emitNestedProperty(
3737 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3747 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3748 ExprEmitter(emitter, emittedOps, buffer.tokens)
3749 .emitExpression(property, LowestPrecedence,
3751 return {PropertyPrecedence::Symbol};
3754 unsigned startIndex = buffer.tokens.size();
3755 auto info = dispatchLTLVisitor(property.getDefiningOp());
3760 if (
info.precedence > parenthesizeIfLooserThan) {
3762 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3763 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3765 ps << PP::end <<
")";
3767 info.precedence = PropertyPrecedence::Symbol;
3771 emittedOps.insert(property.getDefiningOp());
3775EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3776 emitOpError(op,
"emission as Verilog property or sequence not supported");
3777 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3778 return {PropertyPrecedence::Symbol};
3781EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3783 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3784 return {PropertyPrecedence::Symbol};
3787EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3790 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3791 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3792 return {PropertyPrecedence::And};
3795EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3798 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3799 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3800 return {PropertyPrecedence::Or};
3803EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3807 emitNestedProperty(input, PropertyPrecedence::Intersect);
3809 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3810 return {PropertyPrecedence::Intersect};
3813void PropertyEmitter::emitLTLDelay(int64_t delay,
3814 std::optional<int64_t> length) {
3818 ps.addAsString(delay);
3821 ps.addAsString(delay);
3823 ps.addAsString(delay + *length);
3829 }
else if (delay == 1) {
3833 ps.addAsString(delay);
3839void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3841 ps.scopedBox(PP::ibox2, [&] {
3842 ps <<
PPExtString(stringifyClockEdge(edge)) << PP::space;
3843 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3848EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3849 emitLTLDelay(op.getDelay(), op.getLength());
3851 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3852 return {PropertyPrecedence::Concat};
3855EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3856 emitLTLClockingEvent(op.getEdge(), op.getClock());
3858 emitLTLDelay(op.getDelay(), op.getLength());
3860 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3861 return {PropertyPrecedence::Clocking};
3864void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3865 bool addSeparator =
false;
3866 for (
auto input : inputs) {
3869 if (!input.getDefiningOp<ltl::DelayOp>())
3870 ps <<
"##0" << PP::space;
3872 addSeparator =
true;
3873 emitNestedProperty(input, PropertyPrecedence::Concat);
3877EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3878 emitLTLConcat(op.getInputs());
3879 return {PropertyPrecedence::Concat};
3882EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3883 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3884 if (
auto more = op.getMore()) {
3886 ps.addAsString(op.getBase());
3889 ps.addAsString(op.getBase() + *more);
3893 if (op.getBase() == 0) {
3895 }
else if (op.getBase() == 1) {
3899 ps.addAsString(op.getBase());
3903 return {PropertyPrecedence::Repeat};
3906EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3907 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3909 auto more = op.getMore();
3911 ps.addAsString(op.getBase());
3914 ps.addAsString(op.getBase() + more);
3918 return {PropertyPrecedence::Repeat};
3921EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3922 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3924 auto more = op.getMore();
3926 ps.addAsString(op.getBase());
3929 ps.addAsString(op.getBase() + more);
3933 return {PropertyPrecedence::Repeat};
3936EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3939 if (
auto ev = op.getInput().getDefiningOp<ltl::EventuallyOp>()) {
3940 ps <<
"always" << PP::space;
3941 if (
auto innerNot = ev.getInput().getDefiningOp<ltl::NotOp>()) {
3943 emitNestedProperty(innerNot.getInput(), PropertyPrecedence::Qualifier);
3946 ps <<
"not" << PP::space;
3947 emitNestedProperty(ev.getInput(), PropertyPrecedence::Unary);
3949 return {PropertyPrecedence::Qualifier};
3951 ps <<
"not" << PP::space;
3952 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3953 return {PropertyPrecedence::Unary};
3959 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3960 if (!concatOp || concatOp.getInputs().size() < 2)
3962 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3963 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3965 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3966 if (!constOp || !constOp.getValue().isOne())
3968 return concatOp.getInputs().drop_back();
3971EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3975 emitLTLConcat(range);
3976 ps << PP::space <<
"|=>" << PP::nbsp;
3978 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3979 ps << PP::space <<
"|->" << PP::nbsp;
3981 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3982 return {PropertyPrecedence::Implication};
3985EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3986 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3987 ps << PP::space <<
"until" << PP::space;
3988 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3989 return {PropertyPrecedence::Until};
3992EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3993 ps <<
"s_eventually" << PP::space;
3994 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3995 return {PropertyPrecedence::Qualifier};
3998EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3999 emitLTLClockingEvent(op.getEdge(), op.getClock());
4001 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
4002 return {PropertyPrecedence::Clocking};
4006EmittedProperty PropertyEmitter::emitWeakStrongOp(StringRef mnemonic,
4008 ps << mnemonic << PP::space <<
"(";
4009 ps.scopedBox(PP::ibox2, [&] {
4010 emitNestedProperty(input, PropertyPrecedence::Unary);
4013 return {PropertyPrecedence::Lowest};
4016EmittedProperty PropertyEmitter::visitLTL(ltl::WeakOp op) {
4017 return emitWeakStrongOp(
"weak", op.getInput());
4020EmittedProperty PropertyEmitter::visitLTL(ltl::StrongOp op) {
4021 return emitWeakStrongOp(
"strong", op.getInput());
4031class NameCollector {
4033 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
4037 void collectNames(Block &block);
4039 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
4040 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
4043 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
4044 ModuleEmitter &moduleEmitter;
4049 static constexpr size_t maxTypeWidthBound = 32;
4054void NameCollector::collectNames(Block &block) {
4057 for (
auto &op : block) {
4061 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
4064 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
4068 for (
auto result : op.getResults()) {
4070 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
4071 SmallString<16> typeString;
4075 llvm::raw_svector_ostream stringStream(typeString);
4077 stringStream, op.getLoc());
4079 if (typeString.size() <= maxTypeWidthBound)
4080 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4087 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4088 for (
auto ®ion : op.getRegions()) {
4089 if (!region.empty())
4090 collectNames(region.front());
4104class StmtEmitter :
public EmitterBase,
4112 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4114 void emitStatement(Operation *op);
4115 void emitStatementBlock(Block &body);
4118 LogicalResult emitDeclaration(Operation *op);
4121 void collectNamesAndCalculateDeclarationWidths(Block &block);
4124 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4125 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4126 bool isAssignmentLikeContext =
false);
4127 void emitSVAttributes(Operation *op);
4130 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4133 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4137 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4138 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4139 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4140 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4141 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4142 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4144 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4145 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4146 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4147 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4148 template <
typename Op>
4151 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4152 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4153 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4155 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4156 LogicalResult visitSV(
AssignOp op);
4157 LogicalResult visitSV(BPAssignOp op);
4158 LogicalResult visitSV(PAssignOp op);
4159 LogicalResult visitSV(ForceOp op);
4160 LogicalResult visitSV(ReleaseOp op);
4161 LogicalResult visitSV(AliasOp op);
4162 LogicalResult visitSV(InterfaceInstanceOp op);
4163 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4164 LogicalResult visitStmt(OutputOp op);
4166 LogicalResult visitStmt(InstanceOp op);
4167 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4168 ArrayRef<Value> instPortValues);
4172 LogicalResult visitSV(PackageOp op);
4174 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4175 LogicalResult visitSV(OrderedOutputOp op);
4176 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4177 LogicalResult visitSV(IfDefProceduralOp op) {
4178 return emitIfDef(op, op.getCond());
4180 LogicalResult visitSV(IfOp op);
4181 LogicalResult visitSV(AlwaysOp op);
4182 LogicalResult visitSV(AlwaysCombOp op);
4183 LogicalResult visitSV(AlwaysFFOp op);
4184 LogicalResult visitSV(InitialOp op);
4185 LogicalResult visitSV(CaseOp op);
4186 template <
typename OpTy,
typename EmitPrefixFn>
4188 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4189 ValueRange substitutions, EmitPrefixFn emitPrefix);
4190 LogicalResult visitSV(WriteOp op);
4191 LogicalResult visitSV(FWriteOp op);
4192 LogicalResult visitSV(FFlushOp op);
4193 LogicalResult visitSV(FCloseOp op);
4194 LogicalResult visitSV(VerbatimOp op);
4195 LogicalResult visitSV(MacroRefOp op);
4197 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4198 std::optional<unsigned> verbosity);
4199 LogicalResult visitSV(StopOp op);
4200 LogicalResult visitSV(FinishOp op);
4201 LogicalResult visitSV(ExitOp op);
4203 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4204 std::optional<unsigned> verbosity,
4206 ValueRange operands);
4209 template <
typename OpTy>
4210 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4211 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4212 op.getMessageAttr(), op.getSubstitutions());
4216 template <
typename OpTy>
4217 LogicalResult emitFatalMessageOp(OpTy op) {
4218 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4219 op.getMessageAttr(), op.getSubstitutions());
4222 LogicalResult visitSV(FatalProceduralOp op);
4223 LogicalResult visitSV(FatalOp op);
4224 LogicalResult visitSV(ErrorProceduralOp op);
4225 LogicalResult visitSV(WarningProceduralOp op);
4226 LogicalResult visitSV(InfoProceduralOp op);
4227 LogicalResult visitSV(ErrorOp op);
4228 LogicalResult visitSV(WarningOp op);
4229 LogicalResult visitSV(InfoOp op);
4231 LogicalResult visitSV(ReadMemOp op);
4233 LogicalResult visitSV(GenerateOp op);
4234 LogicalResult visitSV(GenerateCaseOp op);
4235 LogicalResult visitSV(GenerateForOp op);
4237 LogicalResult visitSV(
ForOp op);
4239 void emitAssertionLabel(Operation *op);
4240 void emitAssertionMessage(StringAttr message, ValueRange args,
4241 SmallPtrSetImpl<Operation *> &ops,
4243 template <
typename Op>
4244 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4245 LogicalResult visitSV(AssertOp op);
4246 LogicalResult visitSV(AssumeOp op);
4247 LogicalResult visitSV(CoverOp op);
4248 template <
typename Op>
4249 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4250 LogicalResult visitSV(AssertConcurrentOp op);
4251 LogicalResult visitSV(AssumeConcurrentOp op);
4252 LogicalResult visitSV(CoverConcurrentOp op);
4253 template <
typename Op>
4254 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4255 LogicalResult visitSV(AssertPropertyOp op);
4256 LogicalResult visitSV(AssumePropertyOp op);
4257 LogicalResult visitSV(CoverPropertyOp op);
4259 LogicalResult visitSV(BindOp op);
4260 LogicalResult visitSV(InterfaceOp op);
4262 LogicalResult visitSV(InterfaceSignalOp op);
4263 LogicalResult visitSV(InterfaceModportOp op);
4264 LogicalResult visitSV(AssignInterfaceSignalOp op);
4265 LogicalResult visitSV(MacroErrorOp op);
4266 LogicalResult visitSV(MacroDefOp op);
4268 void emitBlockAsStatement(Block *block,
4269 const SmallPtrSetImpl<Operation *> &locationOps,
4270 StringRef multiLineComment = StringRef());
4272 LogicalResult visitSV(FuncDPIImportOp op);
4273 template <
typename CallOp>
4274 LogicalResult emitFunctionCall(CallOp callOp);
4275 LogicalResult visitSV(FuncCallProceduralOp op);
4276 LogicalResult visitSV(FuncCallOp op);
4277 LogicalResult visitSV(ReturnOp op);
4278 LogicalResult visitSV(IncludeOp op);
4281 ModuleEmitter &emitter;
4286 size_t maxDeclNameWidth = 0;
4287 size_t maxTypeWidth = 0;
4298void StmtEmitter::emitExpression(Value exp,
4299 SmallPtrSetImpl<Operation *> &emittedExprs,
4300 VerilogPrecedence parenthesizeIfLooserThan,
4301 bool isAssignmentLikeContext) {
4302 ExprEmitter(emitter, emittedExprs)
4303 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4308void StmtEmitter::emitSVAttributes(Operation *op) {
4316 setPendingNewline();
4319void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4320 llvm::function_ref<
void()> emitRHS,
4322 std::optional<PPExtString> wordBeforeLHS) {
4324 ps.scopedBox(PP::ibox2, [&]() {
4325 if (wordBeforeLHS) {
4326 ps << *wordBeforeLHS << PP::space;
4330 ps << PP::space << syntax << PP::space;
4332 ps.scopedBox(PP::ibox0, [&]() {
4339template <
typename Op>
4341StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4342 std::optional<PPExtString> wordBeforeLHS) {
4343 SmallPtrSet<Operation *, 8> ops;
4347 ps.addCallback({op,
true});
4348 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4350 emitExpression(op.getSrc(), ops, LowestPrecedence,
4355 ps.addCallback({op,
false});
4356 emitLocationInfoAndNewLine(ops);
4360LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4363 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4366 if (emitter.assignsInlined.count(op))
4370 emitSVAttributes(op);
4375LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4376 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4380 if (emitter.assignsInlined.count(op))
4384 emitSVAttributes(op);
4389LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4391 emitSVAttributes(op);
4396LogicalResult StmtEmitter::visitSV(ForceOp op) {
4398 emitError(op,
"SV attributes emission is unimplemented for the op");
4403LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4405 emitError(op,
"SV attributes emission is unimplemented for the op");
4408 SmallPtrSet<Operation *, 8> ops;
4410 ps.addCallback({op,
true});
4411 ps.scopedBox(PP::ibox2, [&]() {
4412 ps <<
"release" << PP::space;
4413 emitExpression(op.getDest(), ops);
4416 ps.addCallback({op,
false});
4417 emitLocationInfoAndNewLine(ops);
4421LogicalResult StmtEmitter::visitSV(AliasOp op) {
4423 emitError(op,
"SV attributes emission is unimplemented for the op");
4426 SmallPtrSet<Operation *, 8> ops;
4428 ps.addCallback({op,
true});
4429 ps.scopedBox(PP::ibox2, [&]() {
4430 ps <<
"alias" << PP::space;
4431 ps.scopedBox(PP::cbox0, [&]() {
4433 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4434 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4438 ps.addCallback({op,
false});
4439 emitLocationInfoAndNewLine(ops);
4443LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4444 auto doNotPrint = op.getDoNotPrint();
4445 if (doNotPrint && !state.options.emitBindComments)
4449 emitError(op,
"SV attributes emission is unimplemented for the op");
4452 StringRef prefix =
"";
4453 ps.addCallback({op,
true});
4456 ps <<
"// This interface is elsewhere emitted as a bind statement."
4460 SmallPtrSet<Operation *, 8> ops;
4463 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4464 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4465 "point to an interface");
4468 if (!prefix.empty())
4474 ps.addCallback({op,
false});
4475 emitLocationInfoAndNewLine(ops);
4483LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4485 SmallPtrSet<Operation *, 8> ops;
4486 size_t operandIndex = 0;
4488 for (
PortInfo port : ports.getOutputs()) {
4489 auto operand = op->getOperand(operandIndex);
4493 if (operand.hasOneUse() && operand.getDefiningOp() &&
4494 isa<InstanceOp>(operand.getDefiningOp())) {
4503 ps.addCallback({op,
true});
4505 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4507 ps <<
"// Zero width: ";
4510 ps <<
"assign" << PP::space;
4512 ps << PP::space <<
"=" << PP::space;
4513 ps.scopedBox(PP::ibox0, [&]() {
4517 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4518 ps <<
"/*Zero width*/";
4520 emitExpression(operand, ops, LowestPrecedence,
4525 ps.addCallback({op,
false});
4526 emitLocationInfoAndNewLine(ops);
4533LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4534 auto parent = op->getParentOfType<PortList>();
4536 return emitOutputLikeOp(op, ports);
4539LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4541 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4542 ps <<
"`ifndef " << typescopeDef << PP::newline;
4543 ps <<
"`define " << typescopeDef;
4544 setPendingNewline();
4545 emitStatementBlock(*op.getBodyBlock());
4547 ps <<
"`endif // " << typescopeDef;
4548 setPendingNewline();
4552LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4554 emitError(op,
"SV attributes emission is unimplemented for the op");
4559 ps << PP::neverbox <<
"// ";
4561 SmallPtrSet<Operation *, 8> ops;
4563 ps.scopedBox(PP::ibox2, [&]() {
4564 ps <<
"typedef" << PP::space;
4565 ps.invokeWithStringOS([&](
auto &os) {
4567 op.getAliasType(),
false);
4569 ps << PP::space <<
PPExtString(op.getPreferredName());
4570 ps.invokeWithStringOS(
4571 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4576 emitLocationInfoAndNewLine(ops);
4580LogicalResult StmtEmitter::visitSV(PackageOp op) {
4581 llvm::SaveAndRestore<Operation *> package(emitter.currentPackage, op);
4583 ps.addCallback({op,
true});
4585 setPendingNewline();
4586 ps.scopedBox(PP::bbox2, [&]() { emitStatementBlock(*op.getBodyBlock()); });
4589 ps.addCallback({op,
false});
4590 setPendingNewline();
4594template <
typename CallOpTy>
4595LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4599 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4601 SmallPtrSet<Operation *, 8> ops;
4605 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4606 if (explicitReturn) {
4607 assert(explicitReturn.hasOneUse());
4608 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4609 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4610 emitExpression(bpassignOp.getDest(), ops);
4612 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4613 ps <<
"assign" << PP::nbsp;
4614 emitExpression(assignOp.getDest(), ops);
4616 ps << PP::nbsp <<
"=" << PP::nbsp;
4619 auto arguments = callee.getPortList(
true);
4623 bool needsComma =
false;
4624 auto printArg = [&](Value value) {
4626 ps <<
"," << PP::space;
4627 emitExpression(value, ops);
4631 ps.scopedBox(PP::ibox0, [&] {
4632 unsigned inputIndex = 0, outputIndex = 0;
4633 for (
auto arg : arguments) {
4636 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4638 printArg(op.getInputs()[inputIndex++]);
4643 emitLocationInfoAndNewLine(ops);
4647LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4648 return emitFunctionCall(op);
4651LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4652 return emitFunctionCall(op);
4655template <
typename PPS>
4657 bool isAutomatic =
false,
4658 bool emitAsTwoStateType =
false) {
4659 ps <<
"function" << PP::nbsp;
4661 ps <<
"automatic" << PP::nbsp;
4662 auto retType = op.getExplicitlyReturnedType();
4664 ps.invokeWithStringOS([&](
auto &os) {
4665 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4666 emitAsTwoStateType);
4672 emitter.emitPortList(
4676LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4677 auto parent = op->getParentOfType<sv::FuncOp>();
4679 return emitOutputLikeOp(op, ports);
4682LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4684 ps <<
"`include" << PP::nbsp;
4686 if (op.getStyle() == IncludeStyle::System)
4687 ps <<
"<" << op.getTarget() <<
">";
4689 ps <<
"\"" << op.getTarget() <<
"\"";
4691 emitLocationInfo(op.getLoc());
4692 setPendingNewline();
4696LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4699 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4703 if (
auto linkageName = importOp.getLinkageName())
4704 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4706 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4707 assert(op.isDeclaration() &&
"function must be a declaration");
4710 assert(state.pendingNewline);
4716LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4718 emitError(op,
"SV attributes emission is unimplemented for the op");
4721 SmallPtrSet<Operation *, 8> ops;
4724 ps.addCallback({op,
true});
4726 if (
auto fd = op.getFd())
4727 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4730 ps.addCallback({op,
false});
4731 emitLocationInfoAndNewLine(ops);
4735LogicalResult StmtEmitter::visitSV(FCloseOp op) {
4737 emitError(op,
"SV attributes emission is unimplemented for the op");
4740 SmallPtrSet<Operation *, 8> ops;
4743 ps.addCallback({op,
true});
4745 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4747 ps.addCallback({op,
false});
4748 emitLocationInfoAndNewLine(ops);
4752template <
typename OpTy,
typename EmitPrefixFn>
4753LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4754 StringRef formatString,
4755 ValueRange substitutions,
4756 EmitPrefixFn emitPrefix) {
4758 emitError(op,
"SV attributes emission is unimplemented for the op");
4761 SmallPtrSet<Operation *, 8> ops;
4764 ps.addCallback({op,
true});
4766 ps.scopedBox(PP::ibox0, [&]() {
4768 ps.writeQuotedEscaped(formatString);
4775 for (
auto operand : substitutions) {
4776 ps <<
"," << PP::space;
4777 emitExpression(operand, ops);
4781 ps.addCallback({op,
false});
4782 emitLocationInfoAndNewLine(ops);
4786LogicalResult StmtEmitter::visitSV(WriteOp op) {
4787 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4788 op.getSubstitutions(),
4789 [&](SmallPtrSetImpl<Operation *> &) {});
4792LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4793 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4794 op.getSubstitutions(),
4795 [&](SmallPtrSetImpl<Operation *> &ops) {
4796 emitExpression(op.getFd(), ops);
4797 ps <<
"," << PP::space;
4801LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4803 emitError(op,
"SV attributes emission is unimplemented for the op");
4806 SmallPtrSet<Operation *, 8> ops;
4811 StringRef
string = op.getFormatString();
4812 if (
string.ends_with(
"\n"))
4813 string =
string.drop_back();
4818 bool isFirst =
true;
4821 while (!
string.
empty()) {
4822 auto lhsRhs =
string.split(
'\n');
4826 ps << PP::end << PP::newline << PP::neverbox;
4830 emitTextWithSubstitutions(
4831 ps, lhsRhs.first, op,
4832 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4833 string = lhsRhs.second;
4838 emitLocationInfoAndNewLine(ops);
4843LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4845 emitError(op,
"SV attributes emission is unimplemented for the op");
4849 SmallPtrSet<Operation *, 8> ops;
4854 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4855 assert(macroOp &&
"Invalid IR");
4857 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4859 if (!op.getInputs().empty()) {
4861 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4862 emitExpression(val, ops, LowestPrecedence,
4868 emitLocationInfoAndNewLine(ops);
4874StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4875 std::optional<unsigned> verbosity) {
4877 emitError(op,
"SV attributes emission is unimplemented for the op");
4880 SmallPtrSet<Operation *, 8> ops;
4882 ps.addCallback({op,
true});
4884 if (verbosity && *verbosity != 1) {
4886 ps.addAsString(*verbosity);
4890 ps.addCallback({op,
false});
4891 emitLocationInfoAndNewLine(ops);
4895LogicalResult StmtEmitter::visitSV(StopOp op) {
4896 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4899LogicalResult StmtEmitter::visitSV(FinishOp op) {
4900 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4904LogicalResult StmtEmitter::visitSV(ExitOp op) {
4905 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4911StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4912 std::optional<unsigned> verbosity,
4913 StringAttr message, ValueRange operands) {
4915 emitError(op,
"SV attributes emission is unimplemented for the op");
4918 SmallPtrSet<Operation *, 8> ops;
4920 ps.addCallback({op,
true});
4926 if ((verbosity && *verbosity != 1) || message) {
4928 ps.scopedBox(PP::ibox0, [&]() {
4932 ps.addAsString(*verbosity);
4937 ps <<
"," << PP::space;
4938 ps.writeQuotedEscaped(message.getValue());
4940 for (
auto operand : operands) {
4941 ps <<
"," << PP::space;
4942 emitExpression(operand, ops);
4951 ps.addCallback({op,
false});
4952 emitLocationInfoAndNewLine(ops);
4956LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4957 return emitFatalMessageOp(op);
4960LogicalResult StmtEmitter::visitSV(FatalOp op) {
4961 return emitFatalMessageOp(op);
4964LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4965 return emitNonfatalMessageOp(op,
"$error");
4968LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4969 return emitNonfatalMessageOp(op,
"$warning");
4972LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4973 return emitNonfatalMessageOp(op,
"$info");
4976LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4977 return emitNonfatalMessageOp(op,
"$error");
4980LogicalResult StmtEmitter::visitSV(WarningOp op) {
4981 return emitNonfatalMessageOp(op,
"$warning");
4984LogicalResult StmtEmitter::visitSV(InfoOp op) {
4985 return emitNonfatalMessageOp(op,
"$info");
4988LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4989 SmallPtrSet<Operation *, 8> ops({op});
4992 ps.addCallback({op,
true});
4994 switch (op.getBaseAttr().getValue()) {
4995 case MemBaseTypeAttr::MemBaseBin:
4998 case MemBaseTypeAttr::MemBaseHex:
5003 ps.scopedBox(PP::ibox0, [&]() {
5004 ps.writeQuotedEscaped(op.getFilename());
5005 ps <<
"," << PP::space;
5006 emitExpression(op.getDest(), ops);
5010 ps.addCallback({op,
false});
5011 emitLocationInfoAndNewLine(ops);
5015LogicalResult StmtEmitter::visitSV(GenerateOp op) {
5016 emitSVAttributes(op);
5019 ps.addCallback({op,
true});
5020 ps <<
"generate" << PP::newline;
5022 setPendingNewline();
5023 emitStatementBlock(op.getBody().getBlocks().front());
5026 ps <<
"endgenerate";
5027 ps.addCallback({op,
false});
5028 setPendingNewline();
5032LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
5033 emitSVAttributes(op);
5036 ps.addCallback({op,
true});
5038 ps.invokeWithStringOS([&](
auto &os) {
5039 emitter.printParamValue(
5040 op.getCond(), os, VerilogPrecedence::Selection,
5041 [&]() { return op->emitOpError(
"invalid case parameter"); });
5044 setPendingNewline();
5047 ArrayAttr
patterns = op.getCasePatterns();
5048 ArrayAttr caseNames = op.getCaseNames();
5049 MutableArrayRef<Region> regions = op.getCaseRegions();
5056 llvm::StringMap<size_t> nextGenIds;
5057 ps.scopedBox(PP::bbox2, [&]() {
5059 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
5060 auto ®ion = regions[i];
5061 assert(region.hasOneBlock());
5062 Attribute patternAttr =
patterns[i];
5065 if (!isa<mlir::TypedAttr>(patternAttr))
5068 ps.invokeWithStringOS([&](
auto &os) {
5069 emitter.printParamValue(
5070 patternAttr, os, VerilogPrecedence::LowestPrecedence,
5071 [&]() {
return op->emitOpError(
"invalid case value"); });
5074 StringRef legalName =
5075 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
5078 setPendingNewline();
5079 emitStatementBlock(region.getBlocks().front());
5082 setPendingNewline();
5088 ps.addCallback({op,
false});
5089 setPendingNewline();
5093LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
5094 emitSVAttributes(op);
5095 llvm::SmallPtrSet<Operation *, 8> ops;
5096 ps.addCallback({op,
true});
5099 StringRef inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5102 ps.scopedBox(PP::cbox0, [&]() {
5104 [&]() { ps <<
"genvar" << PP::nbsp <<
PPExtString(inductionVarName); },
5106 ps.invokeWithStringOS([&](
auto &os) {
5107 emitter.printParamValue(
5108 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5109 [&]() { return op->emitOpError(
"invalid lower bound"); });
5118 ps.invokeWithStringOS([&](
auto &os) {
5119 emitter.printParamValue(
5120 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5121 [&]() { return op->emitOpError(
"invalid upper bound"); });
5127 ps <<
PPExtString(inductionVarName) << PP::nbsp <<
"+=" << PP::nbsp;
5128 ps.invokeWithStringOS([&](
auto &os) {
5129 emitter.printParamValue(
5130 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5131 [&]() { return op->emitOpError(
"invalid step"); });
5134 StringRef blockName = op.getGenBlockName();
5135 if (!blockName.empty())
5139 ps << PP::neverbreak;
5140 setPendingNewline();
5141 emitStatementBlock(op.getBody().getBlocks().front());
5144 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5146 ps.addCallback({op,
false});
5147 setPendingNewline();
5151LogicalResult StmtEmitter::visitSV(
ForOp op) {
5152 emitSVAttributes(op);
5153 llvm::SmallPtrSet<Operation *, 8> ops;
5154 ps.addCallback({op,
true});
5156 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5159 ps.scopedBox(PP::cbox0, [&]() {
5163 ps <<
"logic" << PP::nbsp;
5164 ps.invokeWithStringOS([&](
auto &os) {
5165 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5170 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
5175 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5176 [&]() { emitExpression(op.getUpperBound(), ops); },
5182 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5183 [&]() { emitExpression(op.getStep(), ops); },
5187 ps << PP::neverbreak;
5188 setPendingNewline();
5189 emitStatementBlock(op.getBody().getBlocks().front());
5192 ps.addCallback({op,
false});
5193 emitLocationInfoAndNewLine(ops);
5198void StmtEmitter::emitAssertionLabel(Operation *op) {
5199 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5205void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5206 SmallPtrSetImpl<Operation *> &ops,
5207 bool isConcurrent =
false) {
5210 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5211 ps.scopedBox(PP::ibox0, [&]() {
5212 ps.writeQuotedEscaped(message.getValue());
5214 for (
auto arg : args) {
5215 ps <<
"," << PP::space;
5216 emitExpression(arg, ops);
5222template <
typename Op>
5223LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5225 emitError(op,
"SV attributes emission is unimplemented for the op");
5228 SmallPtrSet<Operation *, 8> ops;
5230 ps.addCallback({op,
true});
5231 ps.scopedBox(PP::ibox2, [&]() {
5232 emitAssertionLabel(op);
5233 ps.scopedBox(PP::cbox0, [&]() {
5235 switch (op.getDefer()) {
5236 case DeferAssert::Immediate:
5238 case DeferAssert::Observed:
5241 case DeferAssert::Final:
5246 ps.scopedBox(PP::ibox0, [&]() {
5247 emitExpression(op.getExpression(), ops);
5250 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5254 ps.addCallback({op,
false});
5255 emitLocationInfoAndNewLine(ops);
5259LogicalResult StmtEmitter::visitSV(AssertOp op) {
5260 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5263LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5264 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5267LogicalResult StmtEmitter::visitSV(CoverOp op) {
5268 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5271template <
typename Op>
5272LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5274 emitError(op,
"SV attributes emission is unimplemented for the op");
5277 SmallPtrSet<Operation *, 8> ops;
5279 ps.addCallback({op,
true});
5280 ps.scopedBox(PP::ibox2, [&]() {
5281 emitAssertionLabel(op);
5282 ps.scopedBox(PP::cbox0, [&]() {
5283 ps << opName << PP::nbsp <<
"property (";
5284 ps.scopedBox(PP::ibox0, [&]() {
5285 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5287 emitExpression(op.getClock(), ops);
5288 ps <<
")" << PP::space;
5289 emitExpression(op.getProperty(), ops);
5292 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5297 ps.addCallback({op,
false});
5298 emitLocationInfoAndNewLine(ops);
5302LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5303 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5306LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5307 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5310LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5311 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5316template <
typename Op>
5317LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5319 emitError(op,
"SV attributes emission is unimplemented for the op");
5329 Operation *parent = op->getParentOp();
5330 Value
property = op.getProperty();
5331 bool isTemporal = !
property.getType().isSignlessInteger(1);
5333 bool emitAsImmediate = !isTemporal && isProcedural;
5336 SmallPtrSet<Operation *, 8> ops;
5338 ps.addCallback({op,
true});
5339 ps.scopedBox(PP::ibox2, [&]() {
5341 emitAssertionLabel(op);
5343 ps.scopedBox(PP::cbox0, [&]() {
5344 if (emitAsImmediate)
5345 ps << opName <<
"(";
5347 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5349 Value clock = op.getClock();
5350 auto event = op.getEvent();
5352 ps.scopedBox(PP::ibox2, [&]() {
5353 PropertyEmitter(emitter, ops)
5354 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5357 ps.scopedBox(PP::ibox2, [&]() {
5358 PropertyEmitter(emitter, ops)
5359 .emitAssertPropertyBody(property, op.getDisable());
5364 ps.addCallback({op,
false});
5365 emitLocationInfoAndNewLine(ops);
5369LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5370 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5373LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5374 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5377LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5378 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5381LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5383 emitError(op,
"SV attributes emission is unimplemented for the op");
5386 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5387 .getMacroIdentifier());
5390 bool hasEmptyThen = op->getRegion(0).front().empty();
5392 ps <<
"`ifndef " << ident;
5394 ps <<
"`ifdef " << ident;
5396 SmallPtrSet<Operation *, 8> ops;
5398 emitLocationInfoAndNewLine(ops);
5401 emitStatementBlock(op->getRegion(0).front());
5403 if (!op->getRegion(1).empty()) {
5404 if (!hasEmptyThen) {
5406 ps <<
"`else // " << ident;
5407 setPendingNewline();
5409 emitStatementBlock(op->getRegion(1).front());
5416 setPendingNewline();
5424void StmtEmitter::emitBlockAsStatement(
5425 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5426 StringRef multiLineComment) {
5430 auto needsBeginEnd =
5434 emitLocationInfoAndNewLine(locationOps);
5437 emitStatementBlock(*block);
5439 if (needsBeginEnd) {
5443 if (!multiLineComment.empty())
5444 ps <<
" // " << multiLineComment;
5445 setPendingNewline();
5449LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5451 for (
auto &op : ooop.getBody().front())
5456LogicalResult StmtEmitter::visitSV(IfOp op) {
5457 SmallPtrSet<Operation *, 8> ops;
5459 auto ifcondBox = PP::ibox2;
5461 emitSVAttributes(op);
5463 ps.addCallback({op,
true});
5464 ps <<
"if (" << ifcondBox;
5474 emitExpression(ifOp.getCond(), ops);
5475 ps << PP::end <<
")";
5476 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5478 if (!ifOp.hasElse())
5482 Block *elseBlock = ifOp.getElseBlock();
5484 if (!nestedElseIfOp) {
5489 emitBlockAsStatement(elseBlock, ops);
5495 ifOp = nestedElseIfOp;
5496 ps <<
"else if (" << ifcondBox;
5498 ps.addCallback({op,
false});
5503LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5504 emitSVAttributes(op);
5505 SmallPtrSet<Operation *, 8> ops;
5509 auto printEvent = [&](AlwaysOp::Condition cond) {
5510 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5511 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5513 ps.addCallback({op,
true});
5515 switch (op.getNumConditions()) {
5521 printEvent(op.getCondition(0));
5526 ps.scopedBox(PP::cbox0, [&]() {
5527 printEvent(op.getCondition(0));
5528 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5529 ps << PP::space <<
"or" << PP::space;
5530 printEvent(op.getCondition(i));
5539 std::string comment;
5540 if (op.getNumConditions() == 0) {
5541 comment =
"always @*";
5543 comment =
"always @(";
5546 [&](Attribute eventAttr) {
5547 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5548 comment += stringifyEventControl(event);
5550 [&]() { comment +=
", "; });
5554 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5555 ps.addCallback({op,
false});
5559LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5560 emitSVAttributes(op);
5561 SmallPtrSet<Operation *, 8> ops;
5565 ps.addCallback({op,
true});
5566 StringRef opString =
"always_comb";
5567 if (state.options.noAlwaysComb)
5568 opString =
"always @(*)";
5571 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5572 ps.addCallback({op,
false});
5576LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5577 emitSVAttributes(op);
5579 SmallPtrSet<Operation *, 8> ops;
5583 ps.addCallback({op,
true});
5584 ps <<
"always_ff @(";
5585 ps.scopedBox(PP::cbox0, [&]() {
5586 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5587 emitExpression(op.getClock(), ops);
5588 if (op.getResetStyle() == ResetType::AsyncReset) {
5589 ps << PP::nbsp <<
"or" << PP::space
5590 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5591 emitExpression(op.getReset(), ops);
5598 std::string comment;
5599 comment +=
"always_ff @(";
5600 comment += stringifyEventControl(op.getClockEdge());
5601 if (op.getResetStyle() == ResetType::AsyncReset) {
5603 comment += stringifyEventControl(*op.getResetEdge());
5607 if (op.getResetStyle() == ResetType::NoReset)
5608 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5611 emitLocationInfoAndNewLine(ops);
5612 ps.scopedBox(PP::bbox2, [&]() {
5618 if (op.getResetStyle() == ResetType::AsyncReset &&
5619 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5621 emitExpression(op.getReset(), ops);
5623 emitBlockAsStatement(op.getResetBlock(), ops);
5626 emitBlockAsStatement(op.getBodyBlock(), ops);
5631 ps <<
" // " << comment;
5632 setPendingNewline();
5634 ps.addCallback({op,
false});
5638LogicalResult StmtEmitter::visitSV(InitialOp op) {
5639 emitSVAttributes(op);
5640 SmallPtrSet<Operation *, 8> ops;
5643 ps.addCallback({op,
true});
5645 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5646 ps.addCallback({op,
false});
5650LogicalResult StmtEmitter::visitSV(CaseOp op) {
5651 emitSVAttributes(op);
5652 SmallPtrSet<Operation *, 8> ops, emptyOps;
5655 ps.addCallback({op,
true});
5656 if (op.getValidationQualifier() !=
5657 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5658 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5659 op.getValidationQualifier()))
5661 const char *opname =
nullptr;
5662 switch (op.getCaseStyle()) {
5663 case CaseStmtType::CaseStmt:
5666 case CaseStmtType::CaseXStmt:
5669 case CaseStmtType::CaseZStmt:
5673 ps << opname <<
" (";
5674 ps.scopedBox(PP::ibox0, [&]() {
5675 emitExpression(op.getCond(), ops);
5678 emitLocationInfoAndNewLine(ops);
5680 size_t caseValueIndex = 0;
5681 ps.scopedBox(PP::bbox2, [&]() {
5682 for (
auto &caseInfo : op.getCases()) {
5684 auto &
pattern = caseInfo.pattern;
5686 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5687 .Case<CaseBitPattern>([&](
auto bitPattern) {
5690 ps.invokeWithStringOS([&](
auto &os) {
5691 os << bitPattern->getWidth() <<
"'b";
5692 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5693 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5696 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5697 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5698 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5700 .Case<CaseExprPattern>([&](
auto) {
5701 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5703 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5704 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5707 emitBlockAsStatement(caseInfo.block, emptyOps);
5713 ps.addCallback({op,
false});
5714 emitLocationInfoAndNewLine(ops);
5718LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5719 bool doNotPrint = op.getDoNotPrint();
5720 if (doNotPrint && !state.options.emitBindComments)
5725 emitSVAttributes(op);
5727 ps.addCallback({op,
true});
5730 <<
"/* This instance is elsewhere emitted as a bind statement."
5733 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5734 "attributes. The attributes will not be emitted.";
5737 SmallPtrSet<Operation *, 8> ops;
5742 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5743 assert(moduleOp &&
"Invalid IR");
5747 if (!op.getParameters().empty()) {
5750 bool printed =
false;
5752 llvm::zip(op.getParameters(),
5753 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5754 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5755 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5757 if (param.getValue() == modParam.getValue())
5762 ps <<
" #(" << PP::bbox2 << PP::newline;
5765 ps <<
"," << PP::newline;
5769 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5771 ps.invokeWithStringOS([&](
auto &os) {
5772 emitter.printParamValue(param.getValue(), os, [&]() {
5773 return op->emitOpError(
"invalid instance parameter '")
5774 << param.getName().getValue() <<
"' value";
5780 ps << PP::end << PP::newline <<
")";
5787 SmallVector<Value> instPortValues(modPortInfo.size());
5788 op.getValues(instPortValues, modPortInfo);
5789 emitInstancePortList(op, modPortInfo, instPortValues);
5791 ps.addCallback({op,
false});
5792 emitLocationInfoAndNewLine(ops);
5797 setPendingNewline();
5802void StmtEmitter::emitInstancePortList(Operation *op,
5804 ArrayRef<Value> instPortValues) {
5805 SmallPtrSet<Operation *, 8> ops;
5808 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5809 ModulePortInfo containingPortList(containingModule.getPortList());
5815 size_t maxNameLength = 0;
5816 auto lineLength = state.options.getEmittedLineLength();
5817 for (
auto &elt : modPortInfo) {
5818 size_t nameLength = elt.getVerilogName().size();
5819 if (!lineLength || nameLength <= *lineLength / 3)
5820 maxNameLength = std::max(maxNameLength, nameLength);
5823 auto getWireForValue = [&](Value result) {
5824 return result.getUsers().begin()->getOperand(0);
5828 bool isFirst =
true;
5829 bool isZeroWidth =
false;
5831 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5833 auto &modPort = modPortInfo.
at(portNum);
5835 Value portVal = instPortValues[portNum];
5840 bool shouldPrintComma =
true;
5842 shouldPrintComma =
false;
5843 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5845 shouldPrintComma =
true;
5850 if (shouldPrintComma)
5853 emitLocationInfoAndNewLine(ops);
5868 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5869 auto modPortName = modPort.getVerilogName();
5872 if (modPortName.size() <= maxNameLength)
5873 ps.spaces(maxNameLength - modPortName.size() + 1);
5877 ps.scopedBox(PP::ibox0, [&]() {
5884 if (!modPort.isOutput()) {
5886 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5887 ps <<
"/* Zero width */";
5889 emitExpression(portVal, ops, LowestPrecedence);
5890 }
else if (portVal.use_empty()) {
5891 ps <<
"/* unused */";
5892 }
else if (portVal.hasOneUse() &&
5893 (output = dyn_cast_or_null<OutputOp>(
5894 portVal.getUses().begin()->getOwner()))) {
5899 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5901 containingPortList.atOutput(outputPortNo).getVerilogName());
5903 portVal = getWireForValue(portVal);
5904 emitExpression(portVal, ops);
5910 if (!isFirst || isZeroWidth) {
5911 emitLocationInfoAndNewLine(ops);
5924LogicalResult StmtEmitter::visitSV(BindOp op) {
5925 emitter.emitBind(op);
5926 assert(state.pendingNewline);
5930LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5931 emitComment(op.getCommentAttr());
5933 emitSVAttributes(op);
5936 ps.addCallback({op,
true});
5938 setPendingNewline();
5940 emitStatementBlock(*op.getBodyBlock());
5942 ps <<
"endinterface" << PP::newline;
5943 ps.addCallback({op,
false});
5944 setPendingNewline();
5949 emitSVAttributes(op);
5951 ps.addCallback({op,
true});
5953 ps << op.getContent();
5955 ps.addCallback({op,
false});
5956 setPendingNewline();
5960LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5962 emitSVAttributes(op);
5964 ps.addCallback({op,
true});
5966 ps << PP::neverbox <<
"// ";
5967 ps.invokeWithStringOS([&](
auto &os) {
5972 ps.invokeWithStringOS(
5973 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5977 ps.addCallback({op,
false});
5978 setPendingNewline();
5982LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5984 ps.addCallback({op,
true});
5988 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5989 auto port = cast<ModportStructAttr>(portAttr);
5990 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5991 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5992 ps << PPExtString(getSymOpName(signalDecl));
5996 ps.addCallback({op,
false});
5997 setPendingNewline();
6001LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
6003 ps.addCallback({op,
true});
6004 SmallPtrSet<Operation *, 8> emitted;
6007 emitExpression(op.getIface(), emitted);
6008 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
6009 emitExpression(op.getRhs(), emitted);
6011 ps.addCallback({op,
false});
6012 setPendingNewline();
6016LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
6018 ps <<
"`" << op.getMacroIdentifier();
6019 setPendingNewline();
6023LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
6024 auto decl = op.getReferencedMacro(&state.symbolCache);
6027 ps.addCallback({op,
true});
6029 if (decl.getArgs()) {
6031 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
6032 ps << cast<StringAttr>(name);
6036 if (!op.getFormatString().empty()) {
6038 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
6041 ps.addCallback({op,
false});
6042 setPendingNewline();
6046void StmtEmitter::emitStatement(Operation *op) {
6053 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
6057 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
6058 succeeded(dispatchVerifVisitor(op)))
6061 emitOpError(op,
"emission to Verilog not supported");
6062 emitPendingNewlineIfNeeded();
6063 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
6064 setPendingNewline();
6075 StmtEmitter &stmtEmitter) {
6082 if (isa<IfDefProceduralOp>(op->getParentOp()))
6090 SmallVector<Value, 8> exprsToScan(op->getOperands());
6095 while (!exprsToScan.empty()) {
6096 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
6103 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
6104 auto *defOp = readInout.getOperand().getDefiningOp();
6111 if (isa<sv::WireOp>(defOp))
6116 if (!isa<RegOp, LogicOp>(defOp))
6122 if (isa<LogicOp>(defOp) &&
6123 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6127 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6128 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6136 exprsToScan.append(expr->getOperands().begin(),
6137 expr->getOperands().end());
6143 if (expr->getBlock() != op->getBlock())
6148 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6155template <
class AssignTy>
6157 AssignTy singleAssign;
6158 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6159 if (hasSVAttributes(user))
6162 if (auto assign = dyn_cast<AssignTy>(user)) {
6165 singleAssign = assign;
6169 return isa<ReadInOutOp>(user);
6171 return singleAssign;
6177 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6181 if (op1->getBlock() != user->getBlock())
6187 return op1->isBeforeInBlock(user);
6191LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6192 emitSVAttributes(op);
6193 auto value = op->getResult(0);
6194 SmallPtrSet<Operation *, 8> opsForLocation;
6195 opsForLocation.insert(op);
6197 ps.addCallback({op,
true});
6200 auto type = value.getType();
6206 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6208 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6209 unsigned targetColumn = 0;
6210 unsigned column = 0;
6213 if (maxDeclNameWidth > 0)
6214 targetColumn += maxDeclNameWidth + 1;
6217 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6218 }
else if (!word.empty()) {
6220 column += word.size();
6221 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6222 ps.spaces(numSpaces);
6223 column += numSpaces;
6226 SmallString<8> typeString;
6229 llvm::raw_svector_ostream stringStream(typeString);
6232 true, singleBitDefaultType);
6235 if (maxTypeWidth > 0)
6236 targetColumn += maxTypeWidth + 1;
6237 unsigned numSpaces = 0;
6238 if (!typeString.empty()) {
6240 column += typeString.size();
6243 if (targetColumn > column)
6244 numSpaces = targetColumn - column;
6245 ps.spaces(numSpaces);
6246 column += numSpaces;
6252 ps.invokeWithStringOS(
6253 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6256 if (state.options.printDebugInfo) {
6257 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6258 auto innerSym = innerSymOp.getInnerSymAttr();
6259 if (innerSym && !innerSym.empty()) {
6261 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6267 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6268 ps << PP::space <<
"=" << PP::space;
6269 ps.invokeWithStringOS([&](
auto &os) {
6270 emitter.printParamValue(localparam.getValue(), os, [&]() {
6271 return op->emitOpError(
"invalid localparam value");
6276 if (
auto regOp = dyn_cast<RegOp>(op)) {
6277 if (
auto initValue = regOp.getInit()) {
6278 ps << PP::space <<
"=" << PP::space;
6279 ps.scopedBox(PP::ibox0, [&]() {
6280 emitExpression(initValue, opsForLocation, LowestPrecedence,
6289 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6293 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6294 auto *source = singleAssign.getSrc().getDefiningOp();
6298 if (!source || isa<ConstantOp>(source) ||
6299 op->getNextNode() == singleAssign) {
6300 ps << PP::space <<
"=" << PP::space;
6301 ps.scopedBox(PP::ibox0, [&]() {
6302 emitExpression(singleAssign.getSrc(), opsForLocation,
6306 emitter.assignsInlined.insert(singleAssign);
6314 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6318 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6321 auto *source = singleAssign.getSrc().getDefiningOp();
6325 if (!source || isa<ConstantOp>(source) ||
6328 ps << PP::space <<
"=" << PP::space;
6329 ps.scopedBox(PP::ibox0, [&]() {
6330 emitExpression(singleAssign.getSrc(), opsForLocation,
6335 emitter.assignsInlined.insert(singleAssign);
6336 emitter.expressionsEmittedIntoDecl.insert(op);
6343 ps.addCallback({op,
false});
6344 emitLocationInfoAndNewLine(opsForLocation);
6348void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6351 NameCollector collector(emitter);
6352 collector.collectNames(block);
6355 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6356 maxTypeWidth = collector.getMaxTypeWidth();
6359void StmtEmitter::emitStatementBlock(Block &body) {
6360 ps.scopedBox(PP::bbox2, [&]() {
6365 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6366 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6371 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6372 collectNamesAndCalculateDeclarationWidths(body);
6375 for (
auto &op : body) {
6382void ModuleEmitter::emitStatement(Operation *op) {
6383 StmtEmitter(*
this, state.options).emitStatement(op);
6388void ModuleEmitter::emitSVAttributes(Operation *op) {
6396 setPendingNewline();
6403void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6404 auto verilogName =
module.getVerilogModuleNameAttr();
6406 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6408 setPendingNewline();
6417void ModuleEmitter::emitBind(BindOp op) {
6419 emitError(op,
"SV attributes emission is unimplemented for the op");
6420 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6426 Operation *childMod =
6427 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6431 ps.addCallback({op,
true});
6432 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6433 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6435 bool isFirst =
true;
6436 ps.scopedBox(PP::bbox2, [&]() {
6437 auto parentPortInfo = parentMod.getPortList();
6442 size_t maxNameLength = 0;
6443 auto lineLength = state.options.getEmittedLineLength();
6444 for (
auto &elt : childPortInfo) {
6445 auto portName = elt.getVerilogName();
6446 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6447 size_t nameLength = elt.getName().size();
6448 if (!lineLength || nameLength <= *lineLength / 3)
6449 maxNameLength = std::max(maxNameLength, nameLength);
6452 SmallVector<Value> instPortValues(childPortInfo.size());
6453 inst.getValues(instPortValues, childPortInfo);
6455 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6457 Value portVal = instPortValues[idx];
6463 bool shouldPrintComma =
true;
6465 shouldPrintComma =
false;
6466 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6468 shouldPrintComma =
true;
6473 if (shouldPrintComma)
6486 ps << PP::neverbox <<
"//";
6491 if (elt.getName().size() <= maxNameLength)
6492 ps.nbsp(maxNameLength - elt.getName().size());
6494 llvm::SmallPtrSet<Operation *, 4> ops;
6495 if (elt.isOutput()) {
6496 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6497 "output port must have either single or no use");
6498 if (portVal.use_empty()) {
6499 ps <<
"/* unused */";
6500 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6501 portVal.getUses().begin()->getOwner())) {
6504 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6506 parentPortList.atOutput(outputPortNo).getVerilogName());
6508 portVal = portVal.getUsers().begin()->getOperand(0);
6509 ExprEmitter(*
this, ops)
6510 .emitExpression(portVal, LowestPrecedence,
6514 ExprEmitter(*
this, ops)
6515 .emitExpression(portVal, LowestPrecedence,
6528 ps.addCallback({op,
false});
6529 setPendingNewline();
6532void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6534 emitError(op,
"SV attributes emission is unimplemented for the op");
6536 auto instance = op.getReferencedInstance(&state.symbolCache);
6538 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6539 instance.getInterfaceType().getInterface());
6541 ps.addCallback({op,
true});
6542 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6543 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6545 ps.addCallback({op,
false});
6546 setPendingNewline();
6549void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6553 auto printParamType = [&](Type type, Attribute defaultValue,
6554 SmallString<8> &result) {
6556 llvm::raw_svector_ostream sstream(result);
6561 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6562 if (intAttr.getValue().getBitWidth() == 32)
6564 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6565 if (fpAttr.getType().isF64())
6568 if (isa<NoneType>(type))
6575 if (
auto intType = type_dyn_cast<IntegerType>(type))
6576 if (intType.getWidth() == 32) {
6577 sstream <<
"/*integer*/";
6581 printPackedType(type, sstream, module->getLoc(),
6589 size_t maxTypeWidth = 0;
6590 SmallString<8> scratch;
6591 for (
auto param : params) {
6592 auto paramAttr = cast<ParamDeclAttr>(param);
6594 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6595 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6598 if (maxTypeWidth > 0)
6601 ps.scopedBox(PP::bbox2, [&]() {
6602 ps << PP::newline <<
"#(";
6603 ps.scopedBox(PP::cbox0, [&]() {
6606 [&](Attribute param) {
6607 auto paramAttr = cast<ParamDeclAttr>(param);
6608 auto defaultValue = paramAttr.getValue();
6610 printParamType(paramAttr.getType(), defaultValue, scratch);
6611 if (!scratch.empty())
6613 if (scratch.size() < maxTypeWidth)
6614 ps.nbsp(maxTypeWidth - scratch.size());
6616 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6617 module, paramAttr.getName()));
6621 ps.invokeWithStringOS([&](
auto &os) {
6623 return module->emitError("parameter '")
6624 << paramAttr.getName().getValue()
6625 << "' has invalid value";
6630 [&]() { ps <<
"," << PP::newline; });
6636void ModuleEmitter::emitPortList(Operation *module,
6638 bool emitAsTwoStateType) {
6640 if (portInfo.
size())
6641 emitLocationInfo(module->getLoc());
6645 bool hasOutputs =
false, hasZeroWidth =
false;
6646 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6647 SmallVector<SmallString<8>, 16> portTypeStrings;
6649 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6650 auto port = portInfo.
at(i);
6654 lastNonZeroPort = i;
6657 portTypeStrings.push_back({});
6659 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6661 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6664 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6667 if (maxTypeWidth > 0)
6671 ps.scopedBox(PP::bbox2, [&]() {
6672 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6673 auto lastPort = e - 1;
6676 auto portType = portInfo.
at(portIdx).
type;
6680 bool isZeroWidth =
false;
6685 ps << (isZeroWidth ?
"// " :
" ");
6689 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6690 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6691 (state.options.emitWireInPorts ? 5 : 0) +
6696 if (!isa<ModportType>(portType)) {
6697 switch (thisPortDirection) {
6698 case ModulePort::Direction::Output:
6701 case ModulePort::Direction::Input:
6702 ps << (hasOutputs ?
"input " :
"input ");
6704 case ModulePort::Direction::InOut:
6705 ps << (hasOutputs ?
"inout " :
"inout ");
6708 if (state.options.emitWireInPorts)
6710 if (!portTypeStrings[portIdx].
empty())
6711 ps << portTypeStrings[portIdx];
6712 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6713 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6715 ps << portTypeStrings[portIdx];
6716 if (portTypeStrings[portIdx].size() < startOfNamePos)
6717 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6724 ps.invokeWithStringOS(
6725 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6728 auto innerSym = portInfo.
at(portIdx).
getSym();
6729 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6731 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6736 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6740 if (
auto loc = portInfo.
at(portIdx).
loc)
6741 emitLocationInfo(loc);
6751 if (!state.options.disallowPortDeclSharing) {
6752 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6755 auto port = portInfo.
at(portIdx);
6759 bool isZeroWidth =
false;
6764 ps << (isZeroWidth ?
"// " :
" ");
6767 ps.nbsp(startOfNamePos);
6770 StringRef name = port.getVerilogName();
6774 ps.invokeWithStringOS(
6775 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6778 auto sym = port.getSym();
6779 if (state.options.printDebugInfo && sym && !sym.empty())
6780 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6784 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6788 if (
auto loc = port.loc)
6789 emitLocationInfo(loc);
6800 if (!portInfo.
size()) {
6802 SmallPtrSet<Operation *, 8> moduleOpSet;
6803 moduleOpSet.insert(module);
6804 emitLocationInfoAndNewLine(moduleOpSet);
6807 ps <<
");" << PP::newline;
6808 setPendingNewline();
6812void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6813 currentModuleOp =
module;
6815 emitComment(module.getCommentAttr());
6816 emitSVAttributes(module);
6818 ps.addCallback({module,
true});
6822 emitParameters(module, module.getParameters());
6826 assert(state.pendingNewline);
6829 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6832 ps.addCallback({module,
false});
6834 setPendingNewline();
6836 currentModuleOp =
nullptr;
6839void ModuleEmitter::emitFunc(FuncOp func) {
6841 if (func.isDeclaration())
6844 currentModuleOp = func;
6846 ps.addCallback({func,
true});
6850 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6852 ps <<
"endfunction";
6854 currentModuleOp =
nullptr;
6863 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6870 void emit(emit::FileListOp op);
6873 void emit(Block *block);
6875 void emitOp(emit::RefOp op);
6876 void emitOp(emit::VerbatimOp op);
6880 for (Operation &op : *block) {
6881 TypeSwitch<Operation *>(&op)
6882 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6883 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6884 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6885 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6886 .Case<BindInterfaceOp>(
6887 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6888 .Case<TypeScopeOp>([&](
auto typedecls) {
6889 ModuleEmitter(state).emitStatement(typedecls);
6892 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6898 for (
auto sym : op.getFiles()) {
6899 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6901 auto it = state.fileMapping.find(fileName);
6902 if (it == state.fileMapping.end()) {
6903 emitOpError(op,
" references an invalid file: ") << sym;
6907 auto file = cast<emit::FileOp>(it->second);
6908 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6915 StringAttr target = op.getTargetAttr().getAttr();
6916 auto *targetOp = state.symbolCache.getDefinition(target);
6917 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6919 TypeSwitch<Operation *>(targetOp)
6920 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6921 .Case<hw::HWModuleOp>(
6922 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6923 .Case<TypeScopeOp, PackageOp>([&](
auto typedecls) {
6924 ModuleEmitter(state).emitStatement(typedecls);
6927 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6933 SmallPtrSet<Operation *, 8> ops;
6938 StringRef text = op.getText();
6942 const auto &[lhs, rhs] = text.split(
'\n');
6946 ps << PP::end << PP::newline << PP::neverbox;
6948 }
while (!text.empty());
6951 emitLocationInfoAndNewLine(ops);
6969 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6970 moduleOp->walk([&](Operation *op) {
6972 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6975 cast<mlir::SymbolOpInterface>(moduleOp).getNameAttr(),
6976 name.getSymName(), op);
6977 if (isa<BindOp>(op))
6983 auto collectPorts = [&](
auto moduleOp) {
6984 auto portInfo = moduleOp.getPortList();
6985 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6986 if (!p.attrs || p.attrs.empty())
6988 for (NamedAttribute portAttr : p.attrs) {
6989 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6998 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6999 for (
auto file :
designOp.getOps<emit::FileOp>())
7000 for (
auto refs : file.getOps<emit::RefOp>())
7001 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
7003 SmallString<32> outputPath;
7004 for (
auto &op : *
designOp.getBody()) {
7007 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
7009 bool hasFileName =
false;
7010 bool emitReplicatedOps = !isFileOp;
7011 bool addToFilelist = !isFileOp;
7017 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
7019 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
7020 <<
" on " << op <<
"\n";);
7021 if (!attr.isDirectory())
7024 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
7025 addToFilelist = !attr.getExcludeFromFilelist().getValue();
7028 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
7033 if (!defaultFileName.isTriviallyEmpty()) {
7034 llvm::sys::path::append(outputPath, defaultFileName);
7036 op->emitError(
"file name unspecified");
7038 llvm::sys::path::append(outputPath,
"error.out");
7042 auto destFile = StringAttr::get(op->getContext(), outputPath);
7043 auto &file =
files[destFile];
7044 file.ops.push_back(info);
7045 file.emitReplicatedOps = emitReplicatedOps;
7046 file.addToFilelist = addToFilelist;
7047 file.isVerilog = outputPath.ends_with(
".sv");
7052 if (!attr || attr.isDirectory()) {
7053 auto excludeFromFileListAttr =
7054 BoolAttr::get(op->getContext(), !addToFilelist);
7055 auto includeReplicatedOpsAttr =
7056 BoolAttr::get(op->getContext(), emitReplicatedOps);
7057 auto outputFileAttr = hw::OutputFileAttr::get(
7058 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
7059 op->setAttr(
"output_file", outputFileAttr);
7065 TypeSwitch<Operation *>(&op)
7066 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
7068 fileMapping.try_emplace(file.getSymNameAttr(), file);
7069 separateFile(file, file.getFileName());
7071 .Case<emit::FragmentOp>([&](
auto fragment) {
7074 .Case<HWModuleOp>([&](
auto mod) {
7076 auto sym = mod.getNameAttr();
7079 collectInstanceSymbolsAndBinds(mod);
7081 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
7082 if (it->second.size() != 1 || attr) {
7085 op.emitError(
"modules can be emitted to a single file");
7093 if (attr || separateModules)
7099 .Case<InterfaceOp>([&](InterfaceOp intf) {
7104 for (
auto &op : *intf.getBodyBlock())
7105 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
7106 if (
auto name = symOp.getNameAttr())
7110 if (attr || separateModules)
7111 separateFile(intf, intf.getSymName() +
".sv");
7115 .Case<PackageOp>([&](PackageOp package) {
7117 auto sym = package.getSymNameAttr();
7120 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
7121 if (it->second.size() != 1 || attr) {
7123 "packages can only be emitted to a single file");
7132 if (attr || separateModules)
7139 separateFile(op, op.getOutputFile().getFilename().getValue());
7141 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7147 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7148 [&](Operation *op) {
7154 separateFile(op,
"");
7156 .Case<FuncOp>([&](
auto op) {
7162 separateFile(op,
"");
7166 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7169 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7178 separateFile(op,
"");
7180 .Case<BindOp>([&](
auto op) {
7182 separateFile(op,
"bindfile.sv");
7187 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
7188 .Case<MacroDeclOp>([&](
auto op) {
7191 .Case<sv::ReserveNamesOp>([](
auto op) {
7194 .Case<om::ClassLike>([&](
auto op) {
7197 .Case<om::ConstantOp>([&](
auto op) {
7200 .Default([&](
auto *) {
7201 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7221 size_t lastReplicatedOp = 0;
7223 bool emitHeaderInclude =
7226 if (emitHeaderInclude)
7229 size_t numReplicatedOps =
7234 DenseSet<emit::FragmentOp> includedFragments;
7235 for (
const auto &opInfo : file.
ops) {
7236 Operation *op = opInfo.op;
7240 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7246 if (
auto fragments =
7248 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7252 op->emitError(
"cannot find referenced fragment ") << sym;
7255 emit::FragmentOp fragment = it->second;
7256 if (includedFragments.insert(fragment).second) {
7257 thingsToEmit.emplace_back(it->second);
7263 thingsToEmit.emplace_back(op);
7268 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7273 TypeSwitch<Operation *>(op)
7274 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7275 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7278 .Case<HWModuleGeneratedOp>(
7279 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7280 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7281 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7282 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7283 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7284 .Case<TypeScopeOp, PackageOp>([&](
auto typedecls) {
7285 ModuleEmitter(state).emitStatement(typedecls);
7287 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7289 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7290 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7291 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7292 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7293 .Default([&](
auto *op) {
7294 state.encounteredError =
true;
7295 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7302 llvm::formatted_raw_ostream &os,
7303 StringAttr fileName,
bool parallelize) {
7308 parallelize &=
context->isMultithreadingEnabled();
7319 size_t lineOffset = 0;
7320 for (
auto &entry : thingsToEmit) {
7321 entry.verilogLocs.setStream(os);
7322 if (
auto *op = entry.getOperation()) {
7327 state.addVerilogLocToOps(lineOffset, fileName);
7329 os << entry.getStringData();
7334 if (state.encounteredError)
7352 SmallString<256> buffer;
7353 llvm::raw_svector_ostream tmpStream(buffer);
7354 llvm::formatted_raw_ostream rs(tmpStream);
7362 if (state.encounteredError)
7367 for (
auto &entry : thingsToEmit) {
7370 auto *op = entry.getOperation();
7372 auto lineOffset = os.getLine() + 1;
7373 os << entry.getStringData();
7377 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7380 entry.verilogLocs.setStream(os);
7387 state.addVerilogLocToOps(0, fileName);
7388 if (state.encounteredError) {
7407 module.emitWarning()
7408 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7409 "created only at SplitExportVerilog";
7418 for (
const auto &it : emitter.
files) {
7419 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7420 "\" ----- 8< -----\n\n");
7426 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7427 "\" ----- 8< -----\n\n");
7428 for (
auto &name : it.second)
7429 contents += name.str() +
"\n";
7430 list.emplace_back(contents);
7433 llvm::formatted_raw_ostream rs(os);
7437 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7444 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7446 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7447 if (failed(failableParallelForEach(
7448 module->getContext(), modulesToPrepare,
7449 [&](
auto op) { return prepareHWModule(op, options); })))
7456struct ExportVerilogPass
7457 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7458 ExportVerilogPass(raw_ostream &os) : os(os) {}
7459 void runOnOperation()
override {
7461 mlir::OpPassManager preparePM(
"builtin.module");
7462 preparePM.addPass(createLegalizeAnonEnums());
7463 auto &modulePM = preparePM.nestAny();
7464 modulePM.addPass(createPrepareForEmission());
7465 if (failed(runPipeline(preparePM, getOperation())))
7466 return signalPassFailure();
7469 return signalPassFailure();
7476struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7477 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7478 : ExportVerilogPass{*os} {
7479 owned = std::move(os);
7483 std::unique_ptr<llvm::raw_ostream> owned;
7487std::unique_ptr<mlir::Pass>
7489 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7492std::unique_ptr<mlir::Pass>
7494 return std::make_unique<ExportVerilogPass>(os);
7505static std::unique_ptr<llvm::ToolOutputFile>
7509 SmallString<128> outputFilename(dirname);
7511 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7514 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7516 emitter.
designOp.emitError(
"cannot create output directory \"")
7517 << outputDir <<
"\": " << error.message();
7523 std::string errorMessage;
7524 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7526 emitter.
designOp.emitError(errorMessage);
7543 llvm::formatted_raw_ostream rs(output->os());
7549 StringAttr::get(fileName.getContext(), output->getFilename()),
7555 StringRef dirname) {
7566 bool insertSuccess =
7568 .insert({StringAttr::get(module.getContext(),
circtHeader),
7574 if (!insertSuccess) {
7575 module.emitError() << "tried to emit a heder to " << circtHeader
7576 << ", but the file is used as an output too.";
7582 parallelForEach(module->getContext(), emitter.
files.begin(),
7583 emitter.
files.end(), [&](
auto &it) {
7584 createSplitOutputFile(it.first, it.second, dirname,
7589 SmallString<128> filelistPath(dirname);
7590 llvm::sys::path::append(filelistPath,
"filelist.f");
7592 std::string errorMessage;
7593 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7595 module->emitError(errorMessage);
7599 for (
const auto &it : emitter.
files) {
7600 if (it.second.addToFilelist)
7601 output->os() << it.first.str() <<
"\n";
7610 for (
auto &name : it.second)
7611 output->os() << name.str() <<
"\n";
7620 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7622 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7623 if (failed(failableParallelForEach(
7624 module->getContext(), modulesToPrepare,
7625 [&](
auto op) { return prepareHWModule(op, options); })))
7633struct ExportSplitVerilogPass
7634 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7635 ExportSplitVerilogPass(StringRef directory) {
7636 directoryName = directory.str();
7638 void runOnOperation()
override {
7640 mlir::OpPassManager preparePM(
"builtin.module");
7643 modulePM.addPass(createPrepareForEmission());
7644 if (failed(runPipeline(preparePM, getOperation())))
7645 return signalPassFailure();
7648 return signalPassFailure();
7653std::unique_ptr<mlir::Pass>
7655 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)
Recursively remove HW type aliases from a type and its subelements.
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.