42#include "mlir/IR/BuiltinOps.h"
43#include "mlir/IR/ImplicitLocOpBuilder.h"
44#include "mlir/IR/Location.h"
45#include "mlir/IR/Threading.h"
46#include "mlir/Interfaces/FunctionImplementation.h"
47#include "mlir/Pass/PassManager.h"
48#include "mlir/Support/FileUtilities.h"
49#include "llvm/ADT/MapVector.h"
50#include "llvm/ADT/STLExtras.h"
51#include "llvm/ADT/StringSet.h"
52#include "llvm/ADT/TypeSwitch.h"
53#include "llvm/Support/FileSystem.h"
54#include "llvm/Support/FormattedStream.h"
55#include "llvm/Support/Path.h"
56#include "llvm/Support/SaveAndRestore.h"
57#include "llvm/Support/ToolOutputFile.h"
58#include "llvm/Support/raw_ostream.h"
61#define GEN_PASS_DEF_EXPORTSPLITVERILOG
62#define GEN_PASS_DEF_EXPORTVERILOG
63#include "circt/Conversion/Passes.h.inc"
70using namespace ExportVerilog;
72using namespace pretty;
74#define DEBUG_TYPE "export-verilog"
82enum VerilogPrecedence {
103enum SubExprSignResult { IsSigned, IsUnsigned };
109 VerilogPrecedence precedence;
112 SubExprSignResult signedness;
114 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
115 : precedence(precedence), signedness(signedness) {}
125 return Builder(ctx).getI32IntegerAttr(value);
128static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
129 return Builder(ctx).getIntegerAttr(t, value);
145 if (isa<VerbatimExprOp>(op)) {
146 if (op->getNumOperands() == 0 &&
147 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
152 if (isa<XMRRefOp>(op))
156 if (isa<MacroRefExprOp>(op))
166 if (op->getNumOperands() == 0)
170 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
174 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
175 auto *indexOp = array.getIndex().getDefiningOp();
176 if (!indexOp || isa<ConstantOp>(indexOp))
178 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
179 auto *readSrc = read.getInput().getDefiningOp();
181 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
196 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
197 return attr.getValue();
198 return TypeSwitch<Operation *, StringRef>(symOp)
203 return op.getVerilogNameAttr().getValue();
205 .Case<InterfaceOp>([&](InterfaceOp op) {
208 .Case<InterfaceSignalOp>(
209 [&](InterfaceSignalOp op) {
return op.getSymName(); })
210 .Case<InterfaceModportOp>(
211 [&](InterfaceModportOp op) {
return op.getSymName(); })
212 .Default([&](Operation *op) {
213 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
214 return attr.getValue();
215 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
216 return attr.getValue();
217 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
218 return attr.getValue();
220 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
221 return attr.getValue();
222 return StringRef(
"");
227template <
typename PPS>
229 os <<
"/*Zero width*/ 1\'b0";
234 auto hml = cast<HWModuleLike>(module);
235 return hml.getPort(portArgNum).getVerilogName();
240 auto hml = cast<HWModuleLike>(module);
241 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
242 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
243 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
244 return updatedName.getValue();
245 return hml.getHWModuleType().getPortName(pId);
254 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
255 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
256 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
257 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
258 UnpackedOpenArrayCastOp, 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;
1540 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1546 SmallPtrSet<Operation *, 16> assignsInlined;
1555 const ModuleEmitter &emitter) {
1556 if (isa<RegOp>(op)) {
1561 cast<InOutType>(op->getResult(0).getType()).getElementType();
1564 while (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(
elementType))
1566 while (
auto arrayType = hw::type_dyn_cast<ArrayType>(
elementType))
1569 if (isa<StructType, UnionType, EnumType, TypeAliasType>(
elementType))
1574 if (isa<sv::WireOp>(op))
1576 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1577 return "localparam";
1580 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1581 return interface.getInterfaceType().getInterface().getValue();
1589 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1591 if (isa<LogicOp>(op)) {
1597 if (isProcedural && !stripAutomatic)
1598 return hasStruct ?
"automatic" :
"automatic logic";
1599 return hasStruct ?
"" :
"logic";
1606 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1609 assert(!emitter.state.options.disallowLocalVariables &&
1610 "automatic variables not allowed");
1614 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1615 :
"automatic logic";
1622static void emitDim(Attribute width, raw_ostream &os, Location loc,
1623 ModuleEmitter &emitter,
bool downTo) {
1625 os <<
"<<invalid type>>";
1628 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1629 if (intAttr.getValue().isZero()) {
1630 os <<
"/*Zero Width*/";
1635 os << (intAttr.getValue().getZExtValue() - 1);
1645 auto typedAttr = dyn_cast<TypedAttr>(width);
1647 emitter.emitError(loc,
"untyped dimension attribute ") << width;
1651 getIntAttr(loc.getContext(), typedAttr.getType(),
1652 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1653 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1657 emitter.printParamValue(width, os, [loc, &emitter]() {
1658 return emitter.emitError(loc,
"invalid parameter in type");
1666static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1667 ModuleEmitter &emitter) {
1668 for (Attribute width : dims) {
1669 emitDim(width, os, loc, emitter,
true);
1674void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1675 SmallVector<Attribute, 4> dims;
1677 [&](Location loc) {
return this->emitError(loc); });
1708 SmallVectorImpl<Attribute> &dims,
1709 bool implicitIntType,
bool singleBitDefaultType,
1710 ModuleEmitter &emitter,
1711 Type optionalAliasType = {},
1712 bool emitAsTwoStateType =
false) {
1713 return TypeSwitch<Type, bool>(type)
1714 .Case<IntegerType>([&](IntegerType integerType) ->
bool {
1715 if (emitAsTwoStateType && dims.empty()) {
1717 if (!typeName.empty()) {
1722 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1724 getInt32Attr(type.getContext(), integerType.getWidth()));
1726 StringRef typeName =
1727 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1728 if (!typeName.empty()) {
1735 return !dims.empty() || !implicitIntType;
1737 .Case<IntType>([&](IntType intType) {
1738 if (!implicitIntType)
1740 dims.push_back(intType.getWidth());
1744 .Case<ArrayType>([&](ArrayType arrayType) {
1745 dims.push_back(arrayType.getSizeAttr());
1747 implicitIntType, singleBitDefaultType,
1749 emitAsTwoStateType);
1751 .Case<InOutType>([&](InOutType inoutType) {
1753 implicitIntType, singleBitDefaultType,
1755 emitAsTwoStateType);
1757 .Case<EnumType>([&](EnumType enumType) {
1758 assert(enumType.getBitWidth().has_value() &&
1759 "enum type must have bitwidth");
1761 if (enumType.getBitWidth() != 32)
1762 os <<
"bit [" << *enumType.getBitWidth() - 1 <<
":0] ";
1764 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1765 llvm::interleaveComma(
1766 enumType.getFields().getAsRange<StringAttr>(), os,
1767 [&](
auto enumerator) {
1768 os << emitter.fieldNameResolver.getEnumFieldName(
1769 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1774 .Case<StructType>([&](StructType structType) {
1775 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1776 os <<
"/*Zero Width*/";
1779 os <<
"struct packed {";
1780 for (
auto &element : structType.getElements()) {
1782 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1783 <<
": Zero Width;*/ ";
1786 SmallVector<Attribute, 8> structDims;
1791 {}, emitAsTwoStateType);
1792 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1793 emitter.printUnpackedTypePostfix(element.type, os);
1800 .Case<UnionType>([&](UnionType unionType) {
1801 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1802 os <<
"/*Zero Width*/";
1806 int64_t unionWidth = hw::getBitWidth(unionType);
1807 os <<
"union packed {";
1808 for (
auto &element : unionType.getElements()) {
1810 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1811 <<
": Zero Width;*/ ";
1814 int64_t elementWidth = hw::getBitWidth(element.type);
1815 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1817 os <<
" struct packed {";
1818 if (element.offset) {
1819 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1820 << element.offset - 1 <<
":0] "
1821 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1825 SmallVector<Attribute, 8> structDims;
1829 true, emitter, {}, emitAsTwoStateType);
1830 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1831 emitter.printUnpackedTypePostfix(element.type, os);
1835 if (elementWidth + (int64_t)element.offset < unionWidth) {
1836 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1837 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1838 <<
"__post_padding_" << element.name.getValue() <<
";";
1840 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1849 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1850 .Case<ModportType>([&](ModportType modportType) {
1851 auto modportAttr = modportType.getModport();
1852 os << modportAttr.getRootReference().getValue() <<
"."
1853 << modportAttr.getNestedReferences().front().getValue();
1856 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1857 os <<
"<<unexpected unpacked array>>";
1858 emitter.emitError(loc,
"Unexpected unpacked array in packed type ")
1862 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1863 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1865 emitter.emitError(loc,
"unresolvable type reference");
1868 if (typedecl.getType() != typeRef.getInnerType()) {
1869 emitter.emitError(loc,
"declared type did not match aliased type");
1873 os << typedecl.getPreferredName();
1874 emitDims(dims, os, typedecl->getLoc(), emitter);
1877 .Default([&](Type type) {
1878 os <<
"<<invalid type '" << type <<
"'>>";
1879 emitter.emitError(loc,
"value has an unsupported verilog type ")
1896bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1897 Type optionalAliasType,
1898 bool implicitIntType,
1899 bool singleBitDefaultType,
1900 bool emitAsTwoStateType) {
1901 SmallVector<Attribute, 8> packedDimensions;
1903 singleBitDefaultType, *
this, optionalAliasType,
1904 emitAsTwoStateType);
1910void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1911 TypeSwitch<Type, void>(type)
1913 printUnpackedTypePostfix(inoutType.getElementType(), os);
1915 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1916 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1917 : state.designOp->getLoc();
1918 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1920 printUnpackedTypePostfix(arrayType.getElementType(), os);
1922 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1924 printUnpackedTypePostfix(arrayType.getElementType(), os);
1926 .Case<InterfaceType>([&](
auto) {
1940ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1941 function_ref<InFlightDiagnostic()> emitError) {
1942 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1950ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1951 VerilogPrecedence parenthesizeIfLooserThan,
1952 function_ref<InFlightDiagnostic()> emitError) {
1953 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1954 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1955 APInt value = intAttr.getValue();
1959 if (intTy.getWidth() > 32) {
1961 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1965 if (intTy.isSigned())
1966 os << intTy.getWidth() <<
"'sd";
1968 os << intTy.getWidth() <<
"'d";
1970 value.print(os, intTy.isSigned());
1971 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1973 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1975 os.write_escaped(strAttr.getValue());
1977 return {Symbol, IsUnsigned};
1979 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1981 os << fpAttr.getValueAsDouble();
1982 return {Symbol, IsUnsigned};
1984 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1985 os << verbatimParam.getValue().getValue();
1986 return {Symbol, IsUnsigned};
1988 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1990 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1991 parameterRef.getName());
1994 return {Symbol, IsUnsigned};
1998 auto expr = dyn_cast<ParamExprAttr>(value);
2000 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
2001 emitError() <<
" = " << value;
2002 return {LowestPrecedence, IsUnsigned};
2005 StringRef operatorStr;
2006 StringRef openStr, closeStr;
2007 VerilogPrecedence subprecedence = LowestPrecedence;
2008 VerilogPrecedence prec;
2009 std::optional<SubExprSignResult> operandSign;
2010 bool isUnary =
false;
2011 bool hasOpenClose =
false;
2013 switch (expr.getOpcode()) {
2015 operatorStr =
" + ";
2016 subprecedence = Addition;
2019 operatorStr =
" * ";
2020 subprecedence = Multiply;
2023 operatorStr =
" & ";
2024 subprecedence = And;
2027 operatorStr =
" | ";
2031 operatorStr =
" ^ ";
2032 subprecedence = Xor;
2035 operatorStr =
" << ";
2036 subprecedence = Shift;
2040 operatorStr =
" >> ";
2041 subprecedence = Shift;
2045 operatorStr =
" >>> ";
2046 subprecedence = Shift;
2047 operandSign = IsSigned;
2050 operatorStr =
" / ";
2051 subprecedence = Multiply;
2052 operandSign = IsUnsigned;
2055 operatorStr =
" / ";
2056 subprecedence = Multiply;
2057 operandSign = IsSigned;
2060 operatorStr =
" % ";
2061 subprecedence = Multiply;
2062 operandSign = IsUnsigned;
2065 operatorStr =
" % ";
2066 subprecedence = Multiply;
2067 operandSign = IsSigned;
2070 openStr =
"$clog2(";
2072 operandSign = IsUnsigned;
2073 hasOpenClose =
true;
2076 case PEO::StrConcat:
2079 hasOpenClose =
true;
2082 subprecedence = LowestPrecedence;
2087 prec = subprecedence;
2090 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2092 assert(isUnary || hasOpenClose ||
2093 !llvm::hasSingleElement(expr.getOperands()));
2100 auto emitOperand = [&](Attribute operand) ->
bool {
2102 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2103 if (operandSign.has_value())
2104 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2107 if (operandSign.has_value()) {
2109 signedness = *operandSign;
2111 return signedness == IsSigned;
2115 if (prec > parenthesizeIfLooserThan)
2124 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2125 for (
auto op : expr.getOperands().drop_front()) {
2128 if (expr.getOpcode() == PEO::Add) {
2129 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2130 const APInt &value = integer.getValue();
2131 if (value.isNegative() && !value.isMinSignedValue()) {
2133 allOperandsSigned &=
2134 emitOperand(IntegerAttr::get(op.getType(), -value));
2141 allOperandsSigned &= emitOperand(op);
2145 if (prec > parenthesizeIfLooserThan) {
2149 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2164class ExprEmitter :
public EmitterBase,
2166 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2171 ExprEmitter(ModuleEmitter &emitter,
2172 SmallPtrSetImpl<Operation *> &emittedExprs)
2173 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2175 ExprEmitter(ModuleEmitter &emitter,
2176 SmallPtrSetImpl<Operation *> &emittedExprs,
2178 : EmitterBase(emitter.state), emitter(emitter),
2179 emittedExprs(emittedExprs), buffer(tokens),
2180 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2181 assert(state.pp.getListener() == &state.saver);
2188 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2189 bool isAssignmentLikeContext) {
2190 assert(localTokens.empty());
2192 ps.scopedBox(PP::ibox0, [&]() {
2195 emitSubExpr(exp, parenthesizeIfLooserThan,
2197 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2199 isAssignmentLikeContext);
2204 if (&buffer.tokens == &localTokens)
2205 buffer.flush(state.pp);
2210 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2211 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2213 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2221 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2222 SubExprSignRequirement signReq = NoRequirement,
2223 bool isSelfDeterminedUnsignedValue =
false,
2224 bool isAssignmentLikeContext =
false);
2228 void emitSVAttributes(Operation *op);
2230 SubExprInfo visitUnhandledExpr(Operation *op);
2231 SubExprInfo visitInvalidComb(Operation *op) {
2234 SubExprInfo visitUnhandledComb(Operation *op) {
2235 return visitUnhandledExpr(op);
2238 return dispatchSVVisitor(op);
2241 return visitUnhandledExpr(op);
2243 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2246 enum EmitBinaryFlags {
2247 EB_RequireSignedOperands = RequireSigned,
2248 EB_RequireUnsignedOperands = RequireUnsigned,
2249 EB_OperandSignRequirementMask = 0x3,
2254 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2258 EB_ForceResultSigned = 0x8,
2263 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2264 const char *syntax,
unsigned emitBinaryFlags = 0);
2266 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2267 bool resultAlwaysUnsigned =
false);
2270 void emitSubExprIBox2(
2271 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2272 ps.scopedBox(PP::ibox2,
2273 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2278 template <
typename Container,
typename EachFn>
2279 void interleaveComma(
const Container &c, EachFn eachFn) {
2280 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2285 void interleaveComma(ValueRange ops) {
2286 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2303 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2305 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2306 CloseFunc closeFn) {
2308 ps.scopedBox(PP::cbox0, [&]() {
2309 interleaveComma(c, eachFn);
2315 template <
typename OpenFunc,
typename CloseFunc>
2316 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2317 return emitBracedList(
2318 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2322 void emitBracedList(ValueRange ops) {
2323 return emitBracedList(
2324 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2328 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2331 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2332 bool printAsPattern, Operation *op);
2334 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2335 ArrayAttr fieldValues,
bool printAsPattern,
2338 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2340 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2341 SubExprInfo visitSV(GetModportOp op);
2342 SubExprInfo visitSV(SystemFunctionOp op);
2343 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2344 SubExprInfo visitSV(XMROp op);
2345 SubExprInfo visitSV(SFormatFOp op);
2346 SubExprInfo visitSV(XMRRefOp op);
2347 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2348 SubExprInfo visitSV(VerbatimExprOp op) {
2349 return visitVerbatimExprOp(op, op.getSymbols());
2351 SubExprInfo visitSV(VerbatimExprSEOp op) {
2352 return visitVerbatimExprOp(op, op.getSymbols());
2354 SubExprInfo visitSV(MacroRefExprOp op);
2355 SubExprInfo visitSV(MacroRefExprSEOp op);
2356 template <
typename MacroTy>
2357 SubExprInfo emitMacroCall(MacroTy op);
2359 SubExprInfo visitSV(ConstantXOp op);
2360 SubExprInfo visitSV(ConstantZOp op);
2361 SubExprInfo visitSV(ConstantStrOp op);
2362 SubExprInfo visitSV(ConcatStrOp op);
2364 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2365 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2367 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2372 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2373 emitSVAttributes(op);
2376 SubExprInfo visitSV(ArrayIndexInOutOp op);
2377 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2378 SubExprInfo visitSV(IndexedPartSelectOp op);
2379 SubExprInfo visitSV(StructFieldInOutOp op);
2382 SubExprInfo visitSV(SampledOp op);
2385 SubExprInfo visitSV(TimeOp op);
2386 SubExprInfo visitSV(STimeOp op);
2389 using TypeOpVisitor::visitTypeOp;
2391 SubExprInfo visitTypeOp(AggregateConstantOp op);
2393 SubExprInfo visitTypeOp(ParamValueOp op);
2400 SubExprInfo visitTypeOp(StructInjectOp op);
2401 SubExprInfo visitTypeOp(UnionCreateOp op);
2402 SubExprInfo visitTypeOp(UnionExtractOp op);
2403 SubExprInfo visitTypeOp(EnumCmpOp op);
2404 SubExprInfo visitTypeOp(EnumConstantOp op);
2407 using CombinationalVisitor::visitComb;
2408 SubExprInfo visitComb(
MuxOp op);
2409 SubExprInfo visitComb(ReverseOp op);
2410 SubExprInfo visitComb(
AddOp op) {
2411 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2412 return emitBinary(op, Addition,
"+");
2414 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2415 SubExprInfo visitComb(
MulOp op) {
2416 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2417 return emitBinary(op, Multiply,
"*");
2419 SubExprInfo visitComb(
DivUOp op) {
2420 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2422 SubExprInfo visitComb(
DivSOp op) {
2423 return emitBinary(op, Multiply,
"/",
2424 EB_RequireSignedOperands | EB_ForceResultSigned);
2426 SubExprInfo visitComb(
ModUOp op) {
2427 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2429 SubExprInfo visitComb(
ModSOp op) {
2430 return emitBinary(op, Multiply,
"%",
2431 EB_RequireSignedOperands | EB_ForceResultSigned);
2433 SubExprInfo visitComb(
ShlOp op) {
2434 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2436 SubExprInfo visitComb(
ShrUOp op) {
2438 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2440 SubExprInfo visitComb(
ShrSOp op) {
2443 return emitBinary(op, Shift,
">>>",
2444 EB_RequireSignedOperands | EB_ForceResultSigned |
2445 EB_RHS_UnsignedWithSelfDeterminedWidth);
2447 SubExprInfo visitComb(
AndOp op) {
2448 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2449 return emitBinary(op, And,
"&");
2451 SubExprInfo visitComb(
OrOp op) {
2452 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2453 return emitBinary(op, Or,
"|");
2455 SubExprInfo visitComb(
XorOp op) {
2456 if (op.isBinaryNot())
2457 return emitUnary(op,
"~");
2458 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2459 return emitBinary(op, Xor,
"^");
2464 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2466 SubExprInfo visitComb(ReplicateOp op);
2467 SubExprInfo visitComb(
ConcatOp op);
2469 SubExprInfo visitComb(ICmpOp op);
2471 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2472 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2473 "not printed within an assignment-like context");
2474 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2475 "supposed to spill such expressions";
2479 SubExprInfo printStructCreate(
2480 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2482 bool printAsPattern, Operation *op);
2485 ModuleEmitter &emitter;
2492 SubExprSignRequirement signPreference = NoRequirement;
2496 SmallPtrSetImpl<Operation *> &emittedExprs;
2499 SmallVector<Token> localTokens;
2513 bool isAssignmentLikeContext =
false;
2517SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2519 unsigned emitBinaryFlags) {
2521 emitError(op,
"SV attributes emission is unimplemented for the op");
2532 if (emitBinaryFlags & EB_ForceResultSigned)
2533 ps <<
"$signed(" << PP::ibox0;
2534 auto operandSignReq =
2535 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2536 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2539 auto lhsSpace = (prec == VerilogPrecedence::Comparison ||
2540 prec == VerilogPrecedence::Equality)
2544 ps << lhsSpace << syntax << PP::nbsp;
2551 auto rhsPrec = prec;
2552 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2553 rhsPrec = VerilogPrecedence(prec - 1);
2558 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2559 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2560 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2561 operandSignReq = NoRequirement;
2564 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2565 rhsIsUnsignedValueWithSelfDeterminedWidth);
2569 SubExprSignResult signedness = IsUnsigned;
2570 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2571 signedness = IsSigned;
2573 if (emitBinaryFlags & EB_ForceResultSigned) {
2574 ps << PP::end <<
")";
2575 signedness = IsSigned;
2579 return {prec, signedness};
2582SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2583 bool resultAlwaysUnsigned) {
2585 emitError(op,
"SV attributes emission is unimplemented for the op");
2588 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2592 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2593 resultAlwaysUnsigned ? IsUnsigned : signedness};
2598void ExprEmitter::emitSVAttributes(Operation *op) {
2612 auto concat = value.getDefiningOp<
ConcatOp>();
2613 if (!concat || concat.getNumOperands() != 2)
2616 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2617 if (constant && constant.getValue().isZero())
2618 return concat.getOperand(1);
2628SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2629 VerilogPrecedence parenthesizeIfLooserThan,
2630 SubExprSignRequirement signRequirement,
2631 bool isSelfDeterminedUnsignedValue,
2632 bool isAssignmentLikeContext) {
2634 if (
auto result = dyn_cast<OpResult>(exp))
2635 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2636 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2637 parenthesizeIfLooserThan, signRequirement,
2638 isSelfDeterminedUnsignedValue,
2639 isAssignmentLikeContext);
2643 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2648 auto *op = exp.getDefiningOp();
2652 if (!shouldEmitInlineExpr) {
2655 if (signRequirement == RequireSigned) {
2657 return {Symbol, IsSigned};
2661 return {Symbol, IsUnsigned};
2664 unsigned subExprStartIndex = buffer.tokens.size();
2666 ps.addCallback({op,
true});
2667 llvm::scope_exit done([&]() {
2669 ps.addCallback({op, false});
2675 signPreference = signRequirement;
2677 bool bitCastAdded =
false;
2678 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2680 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2681 ps.addAsString(inType.getWidth());
2682 ps <<
"'(" << PP::ibox0;
2683 bitCastAdded =
true;
2687 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2688 isAssignmentLikeContext);
2689 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2695 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2697 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2699 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2700 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2703 expInfo.signedness = IsSigned;
2704 expInfo.precedence = Selection;
2705 }
else if (signRequirement == RequireUnsigned &&
2706 expInfo.signedness == IsSigned) {
2709 expInfo.signedness = IsUnsigned;
2710 expInfo.precedence = Selection;
2711 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2718 expInfo.precedence = Selection;
2725 emittedExprs.insert(exp.getDefiningOp());
2729SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2730 auto openFn = [&]() {
2732 ps.addAsString(op.getMultiple());
2735 auto closeFn = [&]() { ps <<
"}}"; };
2739 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2740 if (op.getOperand().hasOneUse()) {
2741 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2742 return {Symbol, IsUnsigned};
2745 emitBracedList(op.getOperand(), openFn, closeFn);
2746 return {Symbol, IsUnsigned};
2749SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2750 emitBracedList(op.getOperands());
2751 return {Symbol, IsUnsigned};
2754SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2758 Type toType = op.getType();
2760 toType, op.getInput().getType(), op.getLoc(),
2761 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2763 ps.invokeWithStringOS(
2764 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2767 return emitSubExpr(op.getInput(), LowestPrecedence);
2770SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2771 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2772 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2773 SubExprSignRequirement signop[] = {
2775 NoRequirement, NoRequirement,
2777 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2779 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2781 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2783 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2784 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2787 if (op.isEqualAllOnes())
2788 return emitUnary(op,
"&",
true);
2791 if (op.isNotEqualZero())
2792 return emitUnary(op,
"|",
true);
2794 VerilogPrecedence precedence = Comparison;
2795 switch (op.getPredicate()) {
2796 case ICmpPredicate::eq:
2797 case ICmpPredicate::ne:
2798 case ICmpPredicate::ceq:
2799 case ICmpPredicate::cne:
2800 case ICmpPredicate::weq:
2801 case ICmpPredicate::wne:
2802 precedence = Equality;
2805 precedence = Comparison;
2808 auto result = emitBinary(op, precedence, symop[pred], signop[pred]);
2812 result.signedness = IsUnsigned;
2816SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2818 emitError(op,
"SV attributes emission is unimplemented for the op");
2820 unsigned loBit = op.getLowBit();
2821 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2823 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2824 assert((x.precedence == Symbol ||
2826 "should be handled by isExpressionUnableToInline");
2831 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2835 ps.addAsString(hiBit);
2836 if (hiBit != loBit) {
2838 ps.addAsString(loBit);
2841 return {Unary, IsUnsigned};
2844SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2846 emitError(op,
"SV attributes emission is unimplemented for the op");
2848 auto decl = op.getReferencedDecl(state.symbolCache);
2851 return {Selection, IsUnsigned};
2854SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2856 emitError(op,
"SV attributes emission is unimplemented for the op");
2859 ps.scopedBox(PP::ibox0, [&]() {
2861 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2862 [&]() { ps <<
"," << PP::space; });
2865 return {Symbol, IsUnsigned};
2868SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2870 emitError(op,
"SV attributes emission is unimplemented for the op");
2872 auto decl = op.getReferencedDecl(state.symbolCache);
2876 return {Selection, IsUnsigned};
2879SubExprInfo ExprEmitter::visitSV(XMROp op) {
2881 emitError(op,
"SV attributes emission is unimplemented for the op");
2883 if (op.getIsRooted())
2885 for (
auto s : op.getPath())
2886 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2888 return {Selection, IsUnsigned};
2893SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2895 emitError(op,
"SV attributes emission is unimplemented for the op");
2898 auto globalRef = op.getReferencedPath(&state.symbolCache);
2899 auto namepath = globalRef.getNamepathAttr().getValue();
2900 auto *
module = state.symbolCache.getDefinition(
2901 cast<InnerRefAttr>(namepath.front()).getModule());
2903 for (
auto sym : namepath) {
2905 auto innerRef = cast<InnerRefAttr>(sym);
2906 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2907 innerRef.getName());
2908 if (ref.hasPort()) {
2914 auto leaf = op.getVerbatimSuffixAttr();
2915 if (leaf && leaf.size())
2917 return {Selection, IsUnsigned};
2920SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2922 emitError(op,
"SV attributes emission is unimplemented for the op");
2924 emitTextWithSubstitutions(
2925 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2926 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2928 return {Unary, IsUnsigned};
2931template <
typename MacroTy>
2932SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2934 emitError(op,
"SV attributes emission is unimplemented for the op");
2937 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2938 assert(macroOp &&
"Invalid IR");
2940 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2942 if (!op.getInputs().empty()) {
2944 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2945 emitExpression(val, LowestPrecedence, false);
2949 return {LowestPrecedence, IsUnsigned};
2952SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2953 return emitMacroCall(op);
2956SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2957 return emitMacroCall(op);
2960SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2962 emitError(op,
"SV attributes emission is unimplemented for the op");
2964 ps.addAsString(op.getWidth());
2966 return {Unary, IsUnsigned};
2969SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2971 emitError(op,
"SV attributes emission is unimplemented for the op");
2973 ps.writeQuotedEscaped(op.getStr());
2974 return {Symbol, IsUnsigned};
2977SubExprInfo ExprEmitter::visitSV(ConcatStrOp op) {
2979 emitError(op,
"SV attributes emission is unimplemented for the op");
2983 emitBracedList(op.getInputs());
2984 return {Symbol, IsUnsigned};
2987SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2989 emitError(op,
"SV attributes emission is unimplemented for the op");
2991 ps.addAsString(op.getWidth());
2993 return {Unary, IsUnsigned};
2996SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2997 bool isNegated =
false;
3000 if (signPreference == RequireSigned && value.isNegative() &&
3001 !value.isMinSignedValue()) {
3006 ps.addAsString(type.getWidth());
3010 if (signPreference == RequireSigned)
3016 SmallString<32> valueStr;
3018 (-value).toStringUnsigned(valueStr, 16);
3020 value.toStringUnsigned(valueStr, 16);
3023 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
3026SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
3028 emitError(op,
"SV attributes emission is unimplemented for the op");
3030 auto value = op.getValue();
3034 if (value.getBitWidth() == 0) {
3035 emitOpError(op,
"will not emit zero width constants in the general case");
3036 ps <<
"<<unsupported zero width constant: "
3037 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3038 return {Unary, IsUnsigned};
3041 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3044void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3045 bool printAsPattern, Operation *op) {
3046 if (printAsPattern && !isAssignmentLikeContext)
3047 emitAssignmentPatternContextError(op);
3048 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3051 elementValues, [&]() { ps << openDelim; },
3052 [&](Attribute elementValue) {
3053 printConstantAggregate(elementValue,
elementType, op);
3055 [&]() { ps <<
"}"; });
3058void ExprEmitter::printConstantStruct(
3059 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3060 bool printAsPattern, Operation *op) {
3061 if (printAsPattern && !isAssignmentLikeContext)
3062 emitAssignmentPatternContextError(op);
3069 auto fieldRange = llvm::make_filter_range(
3070 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3075 if (printAsPattern) {
3077 fieldRange, [&]() { ps <<
"'{"; },
3078 [&](
const auto &fieldAndValue) {
3079 ps.scopedBox(PP::ibox2, [&]() {
3080 const auto &[field, value] = fieldAndValue;
3081 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3082 <<
":" << PP::space;
3083 printConstantAggregate(value, field.type, op);
3086 [&]() { ps <<
"}"; });
3089 fieldRange, [&]() { ps <<
"{"; },
3090 [&](
const auto &fieldAndValue) {
3091 ps.scopedBox(PP::ibox2, [&]() {
3092 const auto &[field, value] = fieldAndValue;
3093 printConstantAggregate(value, field.type, op);
3096 [&]() { ps <<
"}"; });
3100void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3103 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3104 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3105 isAssignmentLikeContext, op);
3108 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3109 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3113 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3114 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3115 isAssignmentLikeContext, op);
3117 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3118 auto value = cast<IntegerAttr>(attr).getValue();
3119 printConstantScalar(value, intType);
3123 emitOpError(op,
"contains constant of type ")
3124 << type <<
" which cannot be emitted as Verilog";
3127SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3129 emitError(op,
"SV attributes emission is unimplemented for the op");
3133 "zero-bit types not allowed at this point");
3135 printConstantAggregate(op.getFields(), op.getType(), op);
3136 return {Symbol, IsUnsigned};
3139SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3141 emitError(op,
"SV attributes emission is unimplemented for the op");
3143 return ps.invokeWithStringOS([&](
auto &os) {
3144 return emitter.printParamValue(op.getValue(), os, [&]() {
3145 return op->emitOpError(
"invalid parameter use");
3154 emitError(op,
"SV attributes emission is unimplemented for the op");
3156 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3158 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3160 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3162 ps.addAsString(dstWidth);
3164 return {Selection, arrayPrec.signedness};
3167SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3168 emitSubExpr(op.getInput(), Selection);
3173 emitSubExpr(op.getIndex(), LowestPrecedence);
3175 emitSVAttributes(op);
3176 return {Selection, IsUnsigned};
3182 emitError(op,
"SV attributes emission is unimplemented for the op");
3184 if (op.isUniform()) {
3186 ps.addAsString(op.getInputs().size());
3188 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3192 op.getInputs(), [&]() { ps <<
"{"; },
3195 emitSubExprIBox2(v);
3198 [&]() { ps <<
"}"; });
3200 return {Unary, IsUnsigned};
3203SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3205 emitError(op,
"SV attributes emission is unimplemented for the op");
3208 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3209 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3210 return {Unary, IsUnsigned};
3215 emitError(op,
"SV attributes emission is unimplemented for the op");
3217 emitBracedList(op.getOperands());
3218 return {Unary, IsUnsigned};
3221SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3223 emitError(op,
"SV attributes emission is unimplemented for the op");
3225 auto index = op.getIndex();
3226 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3231 emitSubExpr(index, LowestPrecedence);
3233 return {Selection, arrayPrec.signedness};
3236SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3238 emitError(op,
"SV attributes emission is unimplemented for the op");
3240 auto prec = emitSubExpr(op.getInput(), Selection);
3242 emitSubExpr(op.getBase(), LowestPrecedence);
3243 if (op.getDecrement())
3247 ps.addAsString(op.getWidth());
3249 return {Selection, prec.signedness};
3252SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3254 emitError(op,
"SV attributes emission is unimplemented for the op");
3256 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3258 emitSubExpr(op.getBase(), LowestPrecedence);
3259 if (op.getDecrement())
3263 ps.addAsString(op.getWidth());
3268SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3270 emitError(op,
"SV attributes emission is unimplemented for the op");
3272 auto prec = emitSubExpr(op.getInput(), Selection);
3274 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3275 return {Selection, prec.signedness};
3278SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3280 emitError(op,
"SV attributes emission is unimplemented for the op");
3283 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3288SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3290 emitError(op,
"SV attributes emission is unimplemented for the op");
3293 ps.scopedBox(PP::ibox0, [&]() {
3294 ps.writeQuotedEscaped(op.getFormatString());
3301 for (
auto operand : op.getSubstitutions()) {
3302 ps <<
"," << PP::space;
3303 emitSubExpr(operand, LowestPrecedence);
3307 return {Symbol, IsUnsigned};
3310SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3312 emitError(op,
"SV attributes emission is unimplemented for the op");
3315 return {Symbol, IsUnsigned};
3318SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3320 emitError(op,
"SV attributes emission is unimplemented for the op");
3323 return {Symbol, IsUnsigned};
3326SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3340 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3341 ps.scopedBox(PP::ibox0, [&]() {
3342 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3346 emitSVAttributes(op);
3348 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3349 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3353 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3354 return emitSubExpr(op.getFalseValue(), Conditional);
3357 SubExprSignResult signedness = IsUnsigned;
3358 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3359 signedness = IsSigned;
3361 return {Conditional, signedness};
3365SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3367 emitError(op,
"SV attributes emission is unimplemented for the op");
3370 emitSubExpr(op.getInput(), LowestPrecedence);
3373 return {Symbol, IsUnsigned};
3376SubExprInfo ExprEmitter::printStructCreate(
3377 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3379 bool printAsPattern, Operation *op) {
3380 if (printAsPattern && !isAssignmentLikeContext)
3381 emitAssignmentPatternContextError(op);
3384 auto filteredFields = llvm::make_filter_range(
3385 llvm::enumerate(fieldInfos),
3386 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3388 if (printAsPattern) {
3390 filteredFields, [&]() { ps <<
"'{"; },
3391 [&](
const auto &field) {
3392 ps.scopedBox(PP::ibox2, [&]() {
3394 emitter.getVerilogStructFieldName(field.value().name))
3395 <<
":" << PP::space;
3396 fieldFn(field.value(), field.index());
3399 [&]() { ps <<
"}"; });
3402 filteredFields, [&]() { ps <<
"{"; },
3403 [&](
const auto &field) {
3404 ps.scopedBox(PP::ibox2,
3405 [&]() { fieldFn(field.value(), field.index()); });
3407 [&]() { ps <<
"}"; });
3410 return {Selection, IsUnsigned};
3415 emitError(op,
"SV attributes emission is unimplemented for the op");
3419 bool printAsPattern = isAssignmentLikeContext;
3420 StructType structType = op.getType();
3421 return printStructCreate(
3422 structType.getElements(),
3423 [&](
const auto &field,
auto index) {
3424 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3426 isAssignmentLikeContext);
3428 printAsPattern, op);
3433 emitError(op,
"SV attributes emission is unimplemented for the op");
3435 emitSubExpr(op.getInput(), Selection);
3437 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3438 return {Selection, IsUnsigned};
3441SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3443 emitError(op,
"SV attributes emission is unimplemented for the op");
3447 bool printAsPattern = isAssignmentLikeContext;
3448 StructType structType = op.getType();
3449 return printStructCreate(
3450 structType.getElements(),
3451 [&](
const auto &field,
auto index) {
3452 if (field.name == op.getFieldNameAttr()) {
3453 emitSubExpr(op.getNewValue(), Selection);
3455 emitSubExpr(op.getInput(), Selection);
3457 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3460 printAsPattern, op);
3463SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3464 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3465 return {Selection, IsUnsigned};
3468SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3470 emitError(op,
"SV attributes emission is unimplemented for the op");
3471 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3474 result.signedness = IsUnsigned;
3478SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3480 emitError(op,
"SV attributes emission is unimplemented for the op");
3484 auto unionWidth = hw::getBitWidth(unionType);
3485 auto &element = unionType.getElements()[op.getFieldIndex()];
3486 auto elementWidth = hw::getBitWidth(element.type);
3489 if (!elementWidth) {
3490 ps.addAsString(unionWidth);
3492 return {Unary, IsUnsigned};
3496 if (elementWidth == unionWidth) {
3497 emitSubExpr(op.getInput(), LowestPrecedence);
3498 return {Unary, IsUnsigned};
3503 ps.scopedBox(PP::ibox0, [&]() {
3504 if (
auto prePadding = element.offset) {
3505 ps.addAsString(prePadding);
3506 ps <<
"'h0," << PP::space;
3508 emitSubExpr(op.getInput(), Selection);
3509 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3510 ps <<
"," << PP::space;
3511 ps.addAsString(postPadding);
3517 return {Unary, IsUnsigned};
3520SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3522 emitError(op,
"SV attributes emission is unimplemented for the op");
3523 emitSubExpr(op.getInput(), Selection);
3526 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3527 auto unionWidth = hw::getBitWidth(unionType);
3528 auto &element = unionType.getElements()[op.getFieldIndex()];
3529 auto elementWidth = hw::getBitWidth(element.type);
3530 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3531 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3540 return {Selection, IsUnsigned};
3543SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3544 emitOpError(op,
"cannot emit this expression to Verilog");
3545 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3547 return {Symbol, IsUnsigned};
3563enum class PropertyPrecedence {
3583struct EmittedProperty {
3585 PropertyPrecedence precedence;
3590class PropertyEmitter :
public EmitterBase,
3591 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3595 PropertyEmitter(ModuleEmitter &emitter,
3596 SmallPtrSetImpl<Operation *> &emittedOps)
3597 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3598 PropertyEmitter(ModuleEmitter &emitter,
3599 SmallPtrSetImpl<Operation *> &emittedOps,
3601 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3603 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3604 assert(state.pp.getListener() == &state.saver);
3607 void emitAssertPropertyDisable(
3608 Value property, Value disable,
3609 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3611 void emitAssertPropertyBody(
3612 Value property, Value disable,
3613 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3615 void emitAssertPropertyBody(
3616 Value property, sv::EventControl event, Value clock, Value disable,
3617 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3622 emitNestedProperty(Value property,
3623 PropertyPrecedence parenthesizeIfLooserThan);
3624 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3625 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3627 EmittedProperty visitUnhandledLTL(Operation *op);
3628 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3629 EmittedProperty visitLTL(ltl::AndOp op);
3630 EmittedProperty visitLTL(ltl::OrOp op);
3631 EmittedProperty visitLTL(ltl::IntersectOp op);
3632 EmittedProperty visitLTL(ltl::DelayOp op);
3633 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3634 EmittedProperty visitLTL(ltl::ConcatOp op);
3635 EmittedProperty visitLTL(ltl::RepeatOp op);
3636 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3637 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3638 EmittedProperty visitLTL(ltl::NotOp op);
3639 EmittedProperty visitLTL(ltl::ImplicationOp op);
3640 EmittedProperty visitLTL(ltl::UntilOp op);
3641 EmittedProperty visitLTL(ltl::EventuallyOp op);
3642 EmittedProperty visitLTL(ltl::ClockOp op);
3643 EmittedProperty visitLTL(ltl::WeakOp op);
3644 EmittedProperty visitLTL(ltl::StrongOp op);
3646 EmittedProperty emitWeakStrongOp(StringRef mnemonic, Value input);
3647 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3648 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3649 void emitLTLConcat(ValueRange inputs);
3652 ModuleEmitter &emitter;
3657 SmallPtrSetImpl<Operation *> &emittedOps;
3660 SmallVector<Token> localTokens;
3673void PropertyEmitter::emitAssertPropertyDisable(
3674 Value property, Value disable,
3675 PropertyPrecedence parenthesizeIfLooserThan) {
3678 ps <<
"disable iff" << PP::nbsp <<
"(";
3680 emitNestedProperty(disable, PropertyPrecedence::Unary);
3686 ps.scopedBox(PP::ibox0,
3687 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3693void PropertyEmitter::emitAssertPropertyBody(
3694 Value property, Value disable,
3695 PropertyPrecedence parenthesizeIfLooserThan) {
3696 assert(localTokens.empty());
3698 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3703 if (&buffer.tokens == &localTokens)
3704 buffer.flush(state.pp);
3707void PropertyEmitter::emitAssertPropertyBody(
3708 Value property, sv::EventControl event, Value clock, Value disable,
3709 PropertyPrecedence parenthesizeIfLooserThan) {
3710 assert(localTokens.empty());
3713 ps.scopedBox(PP::ibox2, [&] {
3714 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3715 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3721 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3726 if (&buffer.tokens == &localTokens)
3727 buffer.flush(state.pp);
3730EmittedProperty PropertyEmitter::emitNestedProperty(
3731 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3741 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3742 ExprEmitter(emitter, emittedOps, buffer.tokens)
3743 .emitExpression(property, LowestPrecedence,
3745 return {PropertyPrecedence::Symbol};
3748 unsigned startIndex = buffer.tokens.size();
3749 auto info = dispatchLTLVisitor(property.getDefiningOp());
3754 if (
info.precedence > parenthesizeIfLooserThan) {
3756 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3757 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3759 ps << PP::end <<
")";
3761 info.precedence = PropertyPrecedence::Symbol;
3765 emittedOps.insert(property.getDefiningOp());
3769EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3770 emitOpError(op,
"emission as Verilog property or sequence not supported");
3771 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3772 return {PropertyPrecedence::Symbol};
3775EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3777 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3778 return {PropertyPrecedence::Symbol};
3781EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3784 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3785 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3786 return {PropertyPrecedence::And};
3789EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3792 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3793 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3794 return {PropertyPrecedence::Or};
3797EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3801 emitNestedProperty(input, PropertyPrecedence::Intersect);
3803 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3804 return {PropertyPrecedence::Intersect};
3807void PropertyEmitter::emitLTLDelay(int64_t delay,
3808 std::optional<int64_t> length) {
3812 ps.addAsString(delay);
3815 ps.addAsString(delay);
3817 ps.addAsString(delay + *length);
3823 }
else if (delay == 1) {
3827 ps.addAsString(delay);
3833void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3835 ps.scopedBox(PP::ibox2, [&] {
3836 ps <<
PPExtString(stringifyClockEdge(edge)) << PP::space;
3837 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3842EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3843 emitLTLDelay(op.getDelay(), op.getLength());
3845 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3846 return {PropertyPrecedence::Concat};
3849EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3850 emitLTLClockingEvent(op.getEdge(), op.getClock());
3852 emitLTLDelay(op.getDelay(), op.getLength());
3854 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3855 return {PropertyPrecedence::Clocking};
3858void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3859 bool addSeparator =
false;
3860 for (
auto input : inputs) {
3863 if (!input.getDefiningOp<ltl::DelayOp>())
3864 ps <<
"##0" << PP::space;
3866 addSeparator =
true;
3867 emitNestedProperty(input, PropertyPrecedence::Concat);
3871EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3872 emitLTLConcat(op.getInputs());
3873 return {PropertyPrecedence::Concat};
3876EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3877 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3878 if (
auto more = op.getMore()) {
3880 ps.addAsString(op.getBase());
3883 ps.addAsString(op.getBase() + *more);
3887 if (op.getBase() == 0) {
3889 }
else if (op.getBase() == 1) {
3893 ps.addAsString(op.getBase());
3897 return {PropertyPrecedence::Repeat};
3900EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3901 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3903 auto more = op.getMore();
3905 ps.addAsString(op.getBase());
3908 ps.addAsString(op.getBase() + more);
3912 return {PropertyPrecedence::Repeat};
3915EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3916 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3918 auto more = op.getMore();
3920 ps.addAsString(op.getBase());
3923 ps.addAsString(op.getBase() + more);
3927 return {PropertyPrecedence::Repeat};
3930EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3933 if (
auto ev = op.getInput().getDefiningOp<ltl::EventuallyOp>()) {
3934 ps <<
"always" << PP::space;
3935 if (
auto innerNot = ev.getInput().getDefiningOp<ltl::NotOp>()) {
3937 emitNestedProperty(innerNot.getInput(), PropertyPrecedence::Qualifier);
3940 ps <<
"not" << PP::space;
3941 emitNestedProperty(ev.getInput(), PropertyPrecedence::Unary);
3943 return {PropertyPrecedence::Qualifier};
3945 ps <<
"not" << PP::space;
3946 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3947 return {PropertyPrecedence::Unary};
3953 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3954 if (!concatOp || concatOp.getInputs().size() < 2)
3956 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3957 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3959 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3960 if (!constOp || !constOp.getValue().isOne())
3962 return concatOp.getInputs().drop_back();
3965EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3969 emitLTLConcat(range);
3970 ps << PP::space <<
"|=>" << PP::nbsp;
3972 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3973 ps << PP::space <<
"|->" << PP::nbsp;
3975 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3976 return {PropertyPrecedence::Implication};
3979EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3980 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3981 ps << PP::space <<
"until" << PP::space;
3982 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3983 return {PropertyPrecedence::Until};
3986EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3987 ps <<
"s_eventually" << PP::space;
3988 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3989 return {PropertyPrecedence::Qualifier};
3992EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3993 emitLTLClockingEvent(op.getEdge(), op.getClock());
3995 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3996 return {PropertyPrecedence::Clocking};
4000EmittedProperty PropertyEmitter::emitWeakStrongOp(StringRef mnemonic,
4002 ps << mnemonic << PP::space <<
"(";
4003 ps.scopedBox(PP::ibox2, [&] {
4004 emitNestedProperty(input, PropertyPrecedence::Unary);
4007 return {PropertyPrecedence::Lowest};
4010EmittedProperty PropertyEmitter::visitLTL(ltl::WeakOp op) {
4011 return emitWeakStrongOp(
"weak", op.getInput());
4014EmittedProperty PropertyEmitter::visitLTL(ltl::StrongOp op) {
4015 return emitWeakStrongOp(
"strong", op.getInput());
4025class NameCollector {
4027 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
4031 void collectNames(Block &block);
4033 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
4034 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
4037 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
4038 ModuleEmitter &moduleEmitter;
4043 static constexpr size_t maxTypeWidthBound = 32;
4048void NameCollector::collectNames(Block &block) {
4051 for (
auto &op : block) {
4055 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
4058 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
4062 for (
auto result : op.getResults()) {
4064 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
4065 SmallString<16> typeString;
4069 llvm::raw_svector_ostream stringStream(typeString);
4071 stringStream, op.getLoc());
4073 if (typeString.size() <= maxTypeWidthBound)
4074 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4081 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4082 for (
auto ®ion : op.getRegions()) {
4083 if (!region.empty())
4084 collectNames(region.front());
4098class StmtEmitter :
public EmitterBase,
4106 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4108 void emitStatement(Operation *op);
4109 void emitStatementBlock(Block &body);
4112 LogicalResult emitDeclaration(Operation *op);
4115 void collectNamesAndCalculateDeclarationWidths(Block &block);
4118 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4119 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4120 bool isAssignmentLikeContext =
false);
4121 void emitSVAttributes(Operation *op);
4124 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4127 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4131 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4132 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4133 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4134 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4135 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4136 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4138 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4139 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4140 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4141 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4142 template <
typename Op>
4145 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4146 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4147 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4149 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4150 LogicalResult visitSV(
AssignOp op);
4151 LogicalResult visitSV(BPAssignOp op);
4152 LogicalResult visitSV(PAssignOp op);
4153 LogicalResult visitSV(ForceOp op);
4154 LogicalResult visitSV(ReleaseOp op);
4155 LogicalResult visitSV(AliasOp op);
4156 LogicalResult visitSV(InterfaceInstanceOp op);
4157 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4158 LogicalResult visitStmt(OutputOp op);
4160 LogicalResult visitStmt(InstanceOp op);
4161 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4162 ArrayRef<Value> instPortValues);
4167 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4168 LogicalResult visitSV(OrderedOutputOp op);
4169 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4170 LogicalResult visitSV(IfDefProceduralOp op) {
4171 return emitIfDef(op, op.getCond());
4173 LogicalResult visitSV(IfOp op);
4174 LogicalResult visitSV(AlwaysOp op);
4175 LogicalResult visitSV(AlwaysCombOp op);
4176 LogicalResult visitSV(AlwaysFFOp op);
4177 LogicalResult visitSV(InitialOp op);
4178 LogicalResult visitSV(CaseOp op);
4179 template <
typename OpTy,
typename EmitPrefixFn>
4181 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4182 ValueRange substitutions, EmitPrefixFn emitPrefix);
4183 LogicalResult visitSV(WriteOp op);
4184 LogicalResult visitSV(FWriteOp op);
4185 LogicalResult visitSV(FFlushOp op);
4186 LogicalResult visitSV(FCloseOp op);
4187 LogicalResult visitSV(VerbatimOp op);
4188 LogicalResult visitSV(MacroRefOp op);
4190 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4191 std::optional<unsigned> verbosity);
4192 LogicalResult visitSV(StopOp op);
4193 LogicalResult visitSV(FinishOp op);
4194 LogicalResult visitSV(ExitOp op);
4196 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4197 std::optional<unsigned> verbosity,
4199 ValueRange operands);
4202 template <
typename OpTy>
4203 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4204 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4205 op.getMessageAttr(), op.getSubstitutions());
4209 template <
typename OpTy>
4210 LogicalResult emitFatalMessageOp(OpTy op) {
4211 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4212 op.getMessageAttr(), op.getSubstitutions());
4215 LogicalResult visitSV(FatalProceduralOp op);
4216 LogicalResult visitSV(FatalOp op);
4217 LogicalResult visitSV(ErrorProceduralOp op);
4218 LogicalResult visitSV(WarningProceduralOp op);
4219 LogicalResult visitSV(InfoProceduralOp op);
4220 LogicalResult visitSV(ErrorOp op);
4221 LogicalResult visitSV(WarningOp op);
4222 LogicalResult visitSV(InfoOp op);
4224 LogicalResult visitSV(ReadMemOp op);
4226 LogicalResult visitSV(GenerateOp op);
4227 LogicalResult visitSV(GenerateCaseOp op);
4228 LogicalResult visitSV(GenerateForOp op);
4230 LogicalResult visitSV(
ForOp op);
4232 void emitAssertionLabel(Operation *op);
4233 void emitAssertionMessage(StringAttr message, ValueRange args,
4234 SmallPtrSetImpl<Operation *> &ops,
4236 template <
typename Op>
4237 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4238 LogicalResult visitSV(AssertOp op);
4239 LogicalResult visitSV(AssumeOp op);
4240 LogicalResult visitSV(CoverOp op);
4241 template <
typename Op>
4242 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4243 LogicalResult visitSV(AssertConcurrentOp op);
4244 LogicalResult visitSV(AssumeConcurrentOp op);
4245 LogicalResult visitSV(CoverConcurrentOp op);
4246 template <
typename Op>
4247 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4248 LogicalResult visitSV(AssertPropertyOp op);
4249 LogicalResult visitSV(AssumePropertyOp op);
4250 LogicalResult visitSV(CoverPropertyOp op);
4252 LogicalResult visitSV(BindOp op);
4253 LogicalResult visitSV(InterfaceOp op);
4255 LogicalResult visitSV(InterfaceSignalOp op);
4256 LogicalResult visitSV(InterfaceModportOp op);
4257 LogicalResult visitSV(AssignInterfaceSignalOp op);
4258 LogicalResult visitSV(MacroErrorOp op);
4259 LogicalResult visitSV(MacroDefOp op);
4261 void emitBlockAsStatement(Block *block,
4262 const SmallPtrSetImpl<Operation *> &locationOps,
4263 StringRef multiLineComment = StringRef());
4265 LogicalResult visitSV(FuncDPIImportOp op);
4266 template <
typename CallOp>
4267 LogicalResult emitFunctionCall(CallOp callOp);
4268 LogicalResult visitSV(FuncCallProceduralOp op);
4269 LogicalResult visitSV(FuncCallOp op);
4270 LogicalResult visitSV(ReturnOp op);
4271 LogicalResult visitSV(IncludeOp op);
4274 ModuleEmitter &emitter;
4279 size_t maxDeclNameWidth = 0;
4280 size_t maxTypeWidth = 0;
4291void StmtEmitter::emitExpression(Value exp,
4292 SmallPtrSetImpl<Operation *> &emittedExprs,
4293 VerilogPrecedence parenthesizeIfLooserThan,
4294 bool isAssignmentLikeContext) {
4295 ExprEmitter(emitter, emittedExprs)
4296 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4301void StmtEmitter::emitSVAttributes(Operation *op) {
4309 setPendingNewline();
4312void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4313 llvm::function_ref<
void()> emitRHS,
4315 std::optional<PPExtString> wordBeforeLHS) {
4317 ps.scopedBox(PP::ibox2, [&]() {
4318 if (wordBeforeLHS) {
4319 ps << *wordBeforeLHS << PP::space;
4323 ps << PP::space << syntax << PP::space;
4325 ps.scopedBox(PP::ibox0, [&]() {
4332template <
typename Op>
4334StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4335 std::optional<PPExtString> wordBeforeLHS) {
4336 SmallPtrSet<Operation *, 8> ops;
4340 ps.addCallback({op,
true});
4341 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4343 emitExpression(op.getSrc(), ops, LowestPrecedence,
4348 ps.addCallback({op,
false});
4349 emitLocationInfoAndNewLine(ops);
4353LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4356 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4359 if (emitter.assignsInlined.count(op))
4363 emitSVAttributes(op);
4368LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4369 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4373 if (emitter.assignsInlined.count(op))
4377 emitSVAttributes(op);
4382LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4384 emitSVAttributes(op);
4389LogicalResult StmtEmitter::visitSV(ForceOp op) {
4391 emitError(op,
"SV attributes emission is unimplemented for the op");
4396LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4398 emitError(op,
"SV attributes emission is unimplemented for the op");
4401 SmallPtrSet<Operation *, 8> ops;
4403 ps.addCallback({op,
true});
4404 ps.scopedBox(PP::ibox2, [&]() {
4405 ps <<
"release" << PP::space;
4406 emitExpression(op.getDest(), ops);
4409 ps.addCallback({op,
false});
4410 emitLocationInfoAndNewLine(ops);
4414LogicalResult StmtEmitter::visitSV(AliasOp op) {
4416 emitError(op,
"SV attributes emission is unimplemented for the op");
4419 SmallPtrSet<Operation *, 8> ops;
4421 ps.addCallback({op,
true});
4422 ps.scopedBox(PP::ibox2, [&]() {
4423 ps <<
"alias" << PP::space;
4424 ps.scopedBox(PP::cbox0, [&]() {
4426 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4427 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4431 ps.addCallback({op,
false});
4432 emitLocationInfoAndNewLine(ops);
4436LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4437 auto doNotPrint = op.getDoNotPrint();
4438 if (doNotPrint && !state.options.emitBindComments)
4442 emitError(op,
"SV attributes emission is unimplemented for the op");
4445 StringRef prefix =
"";
4446 ps.addCallback({op,
true});
4449 ps <<
"// This interface is elsewhere emitted as a bind statement."
4453 SmallPtrSet<Operation *, 8> ops;
4456 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4457 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4458 "point to an interface");
4461 if (!prefix.empty())
4467 ps.addCallback({op,
false});
4468 emitLocationInfoAndNewLine(ops);
4476LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4478 SmallPtrSet<Operation *, 8> ops;
4479 size_t operandIndex = 0;
4481 for (
PortInfo port : ports.getOutputs()) {
4482 auto operand = op->getOperand(operandIndex);
4486 if (operand.hasOneUse() && operand.getDefiningOp() &&
4487 isa<InstanceOp>(operand.getDefiningOp())) {
4496 ps.addCallback({op,
true});
4498 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4500 ps <<
"// Zero width: ";
4503 ps <<
"assign" << PP::space;
4505 ps << PP::space <<
"=" << PP::space;
4506 ps.scopedBox(PP::ibox0, [&]() {
4510 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4511 ps <<
"/*Zero width*/";
4513 emitExpression(operand, ops, LowestPrecedence,
4518 ps.addCallback({op,
false});
4519 emitLocationInfoAndNewLine(ops);
4526LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4527 auto parent = op->getParentOfType<PortList>();
4529 return emitOutputLikeOp(op, ports);
4532LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4534 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4535 ps <<
"`ifndef " << typescopeDef << PP::newline;
4536 ps <<
"`define " << typescopeDef;
4537 setPendingNewline();
4538 emitStatementBlock(*op.getBodyBlock());
4540 ps <<
"`endif // " << typescopeDef;
4541 setPendingNewline();
4545LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4547 emitError(op,
"SV attributes emission is unimplemented for the op");
4552 ps << PP::neverbox <<
"// ";
4554 SmallPtrSet<Operation *, 8> ops;
4556 ps.scopedBox(PP::ibox2, [&]() {
4557 ps <<
"typedef" << PP::space;
4558 ps.invokeWithStringOS([&](
auto &os) {
4560 op.getAliasType(),
false);
4562 ps << PP::space <<
PPExtString(op.getPreferredName());
4563 ps.invokeWithStringOS(
4564 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4569 emitLocationInfoAndNewLine(ops);
4573template <
typename CallOpTy>
4574LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4578 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4580 SmallPtrSet<Operation *, 8> ops;
4584 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4585 if (explicitReturn) {
4586 assert(explicitReturn.hasOneUse());
4587 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4588 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4589 emitExpression(bpassignOp.getDest(), ops);
4591 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4592 ps <<
"assign" << PP::nbsp;
4593 emitExpression(assignOp.getDest(), ops);
4595 ps << PP::nbsp <<
"=" << PP::nbsp;
4598 auto arguments = callee.getPortList(
true);
4602 bool needsComma =
false;
4603 auto printArg = [&](Value value) {
4605 ps <<
"," << PP::space;
4606 emitExpression(value, ops);
4610 ps.scopedBox(PP::ibox0, [&] {
4611 unsigned inputIndex = 0, outputIndex = 0;
4612 for (
auto arg : arguments) {
4615 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4617 printArg(op.getInputs()[inputIndex++]);
4622 emitLocationInfoAndNewLine(ops);
4626LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4627 return emitFunctionCall(op);
4630LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4631 return emitFunctionCall(op);
4634template <
typename PPS>
4636 bool isAutomatic =
false,
4637 bool emitAsTwoStateType =
false) {
4638 ps <<
"function" << PP::nbsp;
4640 ps <<
"automatic" << PP::nbsp;
4641 auto retType = op.getExplicitlyReturnedType();
4643 ps.invokeWithStringOS([&](
auto &os) {
4644 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4645 emitAsTwoStateType);
4651 emitter.emitPortList(
4655LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4656 auto parent = op->getParentOfType<sv::FuncOp>();
4658 return emitOutputLikeOp(op, ports);
4661LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4663 ps <<
"`include" << PP::nbsp;
4665 if (op.getStyle() == IncludeStyle::System)
4666 ps <<
"<" << op.getTarget() <<
">";
4668 ps <<
"\"" << op.getTarget() <<
"\"";
4670 emitLocationInfo(op.getLoc());
4671 setPendingNewline();
4675LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4678 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4682 if (
auto linkageName = importOp.getLinkageName())
4683 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4685 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4686 assert(op.isDeclaration() &&
"function must be a declaration");
4689 assert(state.pendingNewline);
4695LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4697 emitError(op,
"SV attributes emission is unimplemented for the op");
4700 SmallPtrSet<Operation *, 8> ops;
4703 ps.addCallback({op,
true});
4705 if (
auto fd = op.getFd())
4706 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4709 ps.addCallback({op,
false});
4710 emitLocationInfoAndNewLine(ops);
4714LogicalResult StmtEmitter::visitSV(FCloseOp op) {
4716 emitError(op,
"SV attributes emission is unimplemented for the op");
4719 SmallPtrSet<Operation *, 8> ops;
4722 ps.addCallback({op,
true});
4724 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4726 ps.addCallback({op,
false});
4727 emitLocationInfoAndNewLine(ops);
4731template <
typename OpTy,
typename EmitPrefixFn>
4732LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4733 StringRef formatString,
4734 ValueRange substitutions,
4735 EmitPrefixFn emitPrefix) {
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, [&]() {
4747 ps.writeQuotedEscaped(formatString);
4754 for (
auto operand : substitutions) {
4755 ps <<
"," << PP::space;
4756 emitExpression(operand, ops);
4760 ps.addCallback({op,
false});
4761 emitLocationInfoAndNewLine(ops);
4765LogicalResult StmtEmitter::visitSV(WriteOp op) {
4766 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4767 op.getSubstitutions(),
4768 [&](SmallPtrSetImpl<Operation *> &) {});
4771LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4772 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4773 op.getSubstitutions(),
4774 [&](SmallPtrSetImpl<Operation *> &ops) {
4775 emitExpression(op.getFd(), ops);
4776 ps <<
"," << PP::space;
4780LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4782 emitError(op,
"SV attributes emission is unimplemented for the op");
4785 SmallPtrSet<Operation *, 8> ops;
4790 StringRef
string = op.getFormatString();
4791 if (
string.ends_with(
"\n"))
4792 string =
string.drop_back();
4797 bool isFirst =
true;
4800 while (!
string.
empty()) {
4801 auto lhsRhs =
string.split(
'\n');
4805 ps << PP::end << PP::newline << PP::neverbox;
4809 emitTextWithSubstitutions(
4810 ps, lhsRhs.first, op,
4811 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4812 string = lhsRhs.second;
4817 emitLocationInfoAndNewLine(ops);
4822LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4824 emitError(op,
"SV attributes emission is unimplemented for the op");
4828 SmallPtrSet<Operation *, 8> ops;
4833 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4834 assert(macroOp &&
"Invalid IR");
4836 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4838 if (!op.getInputs().empty()) {
4840 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4841 emitExpression(val, ops, LowestPrecedence,
4847 emitLocationInfoAndNewLine(ops);
4853StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4854 std::optional<unsigned> verbosity) {
4856 emitError(op,
"SV attributes emission is unimplemented for the op");
4859 SmallPtrSet<Operation *, 8> ops;
4861 ps.addCallback({op,
true});
4863 if (verbosity && *verbosity != 1) {
4865 ps.addAsString(*verbosity);
4869 ps.addCallback({op,
false});
4870 emitLocationInfoAndNewLine(ops);
4874LogicalResult StmtEmitter::visitSV(StopOp op) {
4875 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4878LogicalResult StmtEmitter::visitSV(FinishOp op) {
4879 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4883LogicalResult StmtEmitter::visitSV(ExitOp op) {
4884 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4890StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4891 std::optional<unsigned> verbosity,
4892 StringAttr message, ValueRange operands) {
4894 emitError(op,
"SV attributes emission is unimplemented for the op");
4897 SmallPtrSet<Operation *, 8> ops;
4899 ps.addCallback({op,
true});
4905 if ((verbosity && *verbosity != 1) || message) {
4907 ps.scopedBox(PP::ibox0, [&]() {
4911 ps.addAsString(*verbosity);
4916 ps <<
"," << PP::space;
4917 ps.writeQuotedEscaped(message.getValue());
4919 for (
auto operand : operands) {
4920 ps <<
"," << PP::space;
4921 emitExpression(operand, ops);
4930 ps.addCallback({op,
false});
4931 emitLocationInfoAndNewLine(ops);
4935LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4936 return emitFatalMessageOp(op);
4939LogicalResult StmtEmitter::visitSV(FatalOp op) {
4940 return emitFatalMessageOp(op);
4943LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4944 return emitNonfatalMessageOp(op,
"$error");
4947LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4948 return emitNonfatalMessageOp(op,
"$warning");
4951LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4952 return emitNonfatalMessageOp(op,
"$info");
4955LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4956 return emitNonfatalMessageOp(op,
"$error");
4959LogicalResult StmtEmitter::visitSV(WarningOp op) {
4960 return emitNonfatalMessageOp(op,
"$warning");
4963LogicalResult StmtEmitter::visitSV(InfoOp op) {
4964 return emitNonfatalMessageOp(op,
"$info");
4967LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4968 SmallPtrSet<Operation *, 8> ops({op});
4971 ps.addCallback({op,
true});
4973 switch (op.getBaseAttr().getValue()) {
4974 case MemBaseTypeAttr::MemBaseBin:
4977 case MemBaseTypeAttr::MemBaseHex:
4982 ps.scopedBox(PP::ibox0, [&]() {
4983 ps.writeQuotedEscaped(op.getFilename());
4984 ps <<
"," << PP::space;
4985 emitExpression(op.getDest(), ops);
4989 ps.addCallback({op,
false});
4990 emitLocationInfoAndNewLine(ops);
4994LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4995 emitSVAttributes(op);
4998 ps.addCallback({op,
true});
4999 ps <<
"generate" << PP::newline;
5001 setPendingNewline();
5002 emitStatementBlock(op.getBody().getBlocks().front());
5005 ps <<
"endgenerate";
5006 ps.addCallback({op,
false});
5007 setPendingNewline();
5011LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
5012 emitSVAttributes(op);
5015 ps.addCallback({op,
true});
5017 ps.invokeWithStringOS([&](
auto &os) {
5018 emitter.printParamValue(
5019 op.getCond(), os, VerilogPrecedence::Selection,
5020 [&]() { return op->emitOpError(
"invalid case parameter"); });
5023 setPendingNewline();
5026 ArrayAttr
patterns = op.getCasePatterns();
5027 ArrayAttr caseNames = op.getCaseNames();
5028 MutableArrayRef<Region> regions = op.getCaseRegions();
5035 llvm::StringMap<size_t> nextGenIds;
5036 ps.scopedBox(PP::bbox2, [&]() {
5038 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
5039 auto ®ion = regions[i];
5040 assert(region.hasOneBlock());
5041 Attribute patternAttr =
patterns[i];
5044 if (!isa<mlir::TypedAttr>(patternAttr))
5047 ps.invokeWithStringOS([&](
auto &os) {
5048 emitter.printParamValue(
5049 patternAttr, os, VerilogPrecedence::LowestPrecedence,
5050 [&]() {
return op->emitOpError(
"invalid case value"); });
5053 StringRef legalName =
5054 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
5057 setPendingNewline();
5058 emitStatementBlock(region.getBlocks().front());
5061 setPendingNewline();
5067 ps.addCallback({op,
false});
5068 setPendingNewline();
5072LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
5073 emitSVAttributes(op);
5074 llvm::SmallPtrSet<Operation *, 8> ops;
5075 ps.addCallback({op,
true});
5078 StringRef inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5081 ps.scopedBox(PP::cbox0, [&]() {
5083 [&]() { ps <<
"genvar" << PP::nbsp <<
PPExtString(inductionVarName); },
5085 ps.invokeWithStringOS([&](
auto &os) {
5086 emitter.printParamValue(
5087 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5088 [&]() { return op->emitOpError(
"invalid lower bound"); });
5097 ps.invokeWithStringOS([&](
auto &os) {
5098 emitter.printParamValue(
5099 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5100 [&]() { return op->emitOpError(
"invalid upper bound"); });
5106 ps <<
PPExtString(inductionVarName) << PP::nbsp <<
"+=" << PP::nbsp;
5107 ps.invokeWithStringOS([&](
auto &os) {
5108 emitter.printParamValue(
5109 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5110 [&]() { return op->emitOpError(
"invalid step"); });
5113 StringRef blockName = op.getGenBlockName();
5114 if (!blockName.empty())
5118 ps << PP::neverbreak;
5119 setPendingNewline();
5120 emitStatementBlock(op.getBody().getBlocks().front());
5123 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5125 ps.addCallback({op,
false});
5126 setPendingNewline();
5130LogicalResult StmtEmitter::visitSV(
ForOp op) {
5131 emitSVAttributes(op);
5132 llvm::SmallPtrSet<Operation *, 8> ops;
5133 ps.addCallback({op,
true});
5135 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5138 ps.scopedBox(PP::cbox0, [&]() {
5142 ps <<
"logic" << PP::nbsp;
5143 ps.invokeWithStringOS([&](
auto &os) {
5144 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5149 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
5154 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5155 [&]() { emitExpression(op.getUpperBound(), ops); },
5161 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5162 [&]() { emitExpression(op.getStep(), ops); },
5166 ps << PP::neverbreak;
5167 setPendingNewline();
5168 emitStatementBlock(op.getBody().getBlocks().front());
5171 ps.addCallback({op,
false});
5172 emitLocationInfoAndNewLine(ops);
5177void StmtEmitter::emitAssertionLabel(Operation *op) {
5178 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5184void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5185 SmallPtrSetImpl<Operation *> &ops,
5186 bool isConcurrent =
false) {
5189 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5190 ps.scopedBox(PP::ibox0, [&]() {
5191 ps.writeQuotedEscaped(message.getValue());
5193 for (
auto arg : args) {
5194 ps <<
"," << PP::space;
5195 emitExpression(arg, ops);
5201template <
typename Op>
5202LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5204 emitError(op,
"SV attributes emission is unimplemented for the op");
5207 SmallPtrSet<Operation *, 8> ops;
5209 ps.addCallback({op,
true});
5210 ps.scopedBox(PP::ibox2, [&]() {
5211 emitAssertionLabel(op);
5212 ps.scopedBox(PP::cbox0, [&]() {
5214 switch (op.getDefer()) {
5215 case DeferAssert::Immediate:
5217 case DeferAssert::Observed:
5220 case DeferAssert::Final:
5225 ps.scopedBox(PP::ibox0, [&]() {
5226 emitExpression(op.getExpression(), ops);
5229 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5233 ps.addCallback({op,
false});
5234 emitLocationInfoAndNewLine(ops);
5238LogicalResult StmtEmitter::visitSV(AssertOp op) {
5239 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5242LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5243 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5246LogicalResult StmtEmitter::visitSV(CoverOp op) {
5247 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5250template <
typename Op>
5251LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5253 emitError(op,
"SV attributes emission is unimplemented for the op");
5256 SmallPtrSet<Operation *, 8> ops;
5258 ps.addCallback({op,
true});
5259 ps.scopedBox(PP::ibox2, [&]() {
5260 emitAssertionLabel(op);
5261 ps.scopedBox(PP::cbox0, [&]() {
5262 ps << opName << PP::nbsp <<
"property (";
5263 ps.scopedBox(PP::ibox0, [&]() {
5264 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5266 emitExpression(op.getClock(), ops);
5267 ps <<
")" << PP::space;
5268 emitExpression(op.getProperty(), ops);
5271 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5276 ps.addCallback({op,
false});
5277 emitLocationInfoAndNewLine(ops);
5281LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5282 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5285LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5286 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5289LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5290 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5295template <
typename Op>
5296LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5298 emitError(op,
"SV attributes emission is unimplemented for the op");
5308 Operation *parent = op->getParentOp();
5309 Value
property = op.getProperty();
5310 bool isTemporal = !
property.getType().isSignlessInteger(1);
5312 bool emitAsImmediate = !isTemporal && isProcedural;
5315 SmallPtrSet<Operation *, 8> ops;
5317 ps.addCallback({op,
true});
5318 ps.scopedBox(PP::ibox2, [&]() {
5320 emitAssertionLabel(op);
5322 ps.scopedBox(PP::cbox0, [&]() {
5323 if (emitAsImmediate)
5324 ps << opName <<
"(";
5326 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5328 Value clock = op.getClock();
5329 auto event = op.getEvent();
5331 ps.scopedBox(PP::ibox2, [&]() {
5332 PropertyEmitter(emitter, ops)
5333 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5336 ps.scopedBox(PP::ibox2, [&]() {
5337 PropertyEmitter(emitter, ops)
5338 .emitAssertPropertyBody(property, op.getDisable());
5343 ps.addCallback({op,
false});
5344 emitLocationInfoAndNewLine(ops);
5348LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5349 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5352LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5353 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5356LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5357 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5360LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5362 emitError(op,
"SV attributes emission is unimplemented for the op");
5365 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5366 .getMacroIdentifier());
5369 bool hasEmptyThen = op->getRegion(0).front().empty();
5371 ps <<
"`ifndef " << ident;
5373 ps <<
"`ifdef " << ident;
5375 SmallPtrSet<Operation *, 8> ops;
5377 emitLocationInfoAndNewLine(ops);
5380 emitStatementBlock(op->getRegion(0).front());
5382 if (!op->getRegion(1).empty()) {
5383 if (!hasEmptyThen) {
5385 ps <<
"`else // " << ident;
5386 setPendingNewline();
5388 emitStatementBlock(op->getRegion(1).front());
5395 setPendingNewline();
5403void StmtEmitter::emitBlockAsStatement(
5404 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5405 StringRef multiLineComment) {
5409 auto needsBeginEnd =
5413 emitLocationInfoAndNewLine(locationOps);
5416 emitStatementBlock(*block);
5418 if (needsBeginEnd) {
5422 if (!multiLineComment.empty())
5423 ps <<
" // " << multiLineComment;
5424 setPendingNewline();
5428LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5430 for (
auto &op : ooop.getBody().front())
5435LogicalResult StmtEmitter::visitSV(IfOp op) {
5436 SmallPtrSet<Operation *, 8> ops;
5438 auto ifcondBox = PP::ibox2;
5440 emitSVAttributes(op);
5442 ps.addCallback({op,
true});
5443 ps <<
"if (" << ifcondBox;
5453 emitExpression(ifOp.getCond(), ops);
5454 ps << PP::end <<
")";
5455 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5457 if (!ifOp.hasElse())
5461 Block *elseBlock = ifOp.getElseBlock();
5463 if (!nestedElseIfOp) {
5468 emitBlockAsStatement(elseBlock, ops);
5474 ifOp = nestedElseIfOp;
5475 ps <<
"else if (" << ifcondBox;
5477 ps.addCallback({op,
false});
5482LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5483 emitSVAttributes(op);
5484 SmallPtrSet<Operation *, 8> ops;
5488 auto printEvent = [&](AlwaysOp::Condition cond) {
5489 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5490 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5492 ps.addCallback({op,
true});
5494 switch (op.getNumConditions()) {
5500 printEvent(op.getCondition(0));
5505 ps.scopedBox(PP::cbox0, [&]() {
5506 printEvent(op.getCondition(0));
5507 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5508 ps << PP::space <<
"or" << PP::space;
5509 printEvent(op.getCondition(i));
5518 std::string comment;
5519 if (op.getNumConditions() == 0) {
5520 comment =
"always @*";
5522 comment =
"always @(";
5525 [&](Attribute eventAttr) {
5526 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5527 comment += stringifyEventControl(event);
5529 [&]() { comment +=
", "; });
5533 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5534 ps.addCallback({op,
false});
5538LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5539 emitSVAttributes(op);
5540 SmallPtrSet<Operation *, 8> ops;
5544 ps.addCallback({op,
true});
5545 StringRef opString =
"always_comb";
5546 if (state.options.noAlwaysComb)
5547 opString =
"always @(*)";
5550 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5551 ps.addCallback({op,
false});
5555LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5556 emitSVAttributes(op);
5558 SmallPtrSet<Operation *, 8> ops;
5562 ps.addCallback({op,
true});
5563 ps <<
"always_ff @(";
5564 ps.scopedBox(PP::cbox0, [&]() {
5565 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5566 emitExpression(op.getClock(), ops);
5567 if (op.getResetStyle() == ResetType::AsyncReset) {
5568 ps << PP::nbsp <<
"or" << PP::space
5569 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5570 emitExpression(op.getReset(), ops);
5577 std::string comment;
5578 comment +=
"always_ff @(";
5579 comment += stringifyEventControl(op.getClockEdge());
5580 if (op.getResetStyle() == ResetType::AsyncReset) {
5582 comment += stringifyEventControl(*op.getResetEdge());
5586 if (op.getResetStyle() == ResetType::NoReset)
5587 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5590 emitLocationInfoAndNewLine(ops);
5591 ps.scopedBox(PP::bbox2, [&]() {
5597 if (op.getResetStyle() == ResetType::AsyncReset &&
5598 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5600 emitExpression(op.getReset(), ops);
5602 emitBlockAsStatement(op.getResetBlock(), ops);
5605 emitBlockAsStatement(op.getBodyBlock(), ops);
5610 ps <<
" // " << comment;
5611 setPendingNewline();
5613 ps.addCallback({op,
false});
5617LogicalResult StmtEmitter::visitSV(InitialOp op) {
5618 emitSVAttributes(op);
5619 SmallPtrSet<Operation *, 8> ops;
5622 ps.addCallback({op,
true});
5624 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5625 ps.addCallback({op,
false});
5629LogicalResult StmtEmitter::visitSV(CaseOp op) {
5630 emitSVAttributes(op);
5631 SmallPtrSet<Operation *, 8> ops, emptyOps;
5634 ps.addCallback({op,
true});
5635 if (op.getValidationQualifier() !=
5636 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5637 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5638 op.getValidationQualifier()))
5640 const char *opname =
nullptr;
5641 switch (op.getCaseStyle()) {
5642 case CaseStmtType::CaseStmt:
5645 case CaseStmtType::CaseXStmt:
5648 case CaseStmtType::CaseZStmt:
5652 ps << opname <<
" (";
5653 ps.scopedBox(PP::ibox0, [&]() {
5654 emitExpression(op.getCond(), ops);
5657 emitLocationInfoAndNewLine(ops);
5659 size_t caseValueIndex = 0;
5660 ps.scopedBox(PP::bbox2, [&]() {
5661 for (
auto &caseInfo : op.getCases()) {
5663 auto &
pattern = caseInfo.pattern;
5665 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5666 .Case<CaseBitPattern>([&](
auto bitPattern) {
5669 ps.invokeWithStringOS([&](
auto &os) {
5670 os << bitPattern->getWidth() <<
"'b";
5671 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5672 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5675 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5676 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5677 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5679 .Case<CaseExprPattern>([&](
auto) {
5680 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5682 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5683 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5686 emitBlockAsStatement(caseInfo.block, emptyOps);
5692 ps.addCallback({op,
false});
5693 emitLocationInfoAndNewLine(ops);
5697LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5698 bool doNotPrint = op.getDoNotPrint();
5699 if (doNotPrint && !state.options.emitBindComments)
5704 emitSVAttributes(op);
5706 ps.addCallback({op,
true});
5709 <<
"/* This instance is elsewhere emitted as a bind statement."
5712 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5713 "attributes. The attributes will not be emitted.";
5716 SmallPtrSet<Operation *, 8> ops;
5721 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5722 assert(moduleOp &&
"Invalid IR");
5726 if (!op.getParameters().empty()) {
5729 bool printed =
false;
5731 llvm::zip(op.getParameters(),
5732 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5733 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5734 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5736 if (param.getValue() == modParam.getValue())
5741 ps <<
" #(" << PP::bbox2 << PP::newline;
5744 ps <<
"," << PP::newline;
5748 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5750 ps.invokeWithStringOS([&](
auto &os) {
5751 emitter.printParamValue(param.getValue(), os, [&]() {
5752 return op->emitOpError(
"invalid instance parameter '")
5753 << param.getName().getValue() <<
"' value";
5759 ps << PP::end << PP::newline <<
")";
5766 SmallVector<Value> instPortValues(modPortInfo.size());
5767 op.getValues(instPortValues, modPortInfo);
5768 emitInstancePortList(op, modPortInfo, instPortValues);
5770 ps.addCallback({op,
false});
5771 emitLocationInfoAndNewLine(ops);
5776 setPendingNewline();
5781void StmtEmitter::emitInstancePortList(Operation *op,
5783 ArrayRef<Value> instPortValues) {
5784 SmallPtrSet<Operation *, 8> ops;
5787 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5788 ModulePortInfo containingPortList(containingModule.getPortList());
5794 size_t maxNameLength = 0;
5795 auto lineLength = state.options.getEmittedLineLength();
5796 for (
auto &elt : modPortInfo) {
5797 size_t nameLength = elt.getVerilogName().size();
5798 if (!lineLength || nameLength <= *lineLength / 3)
5799 maxNameLength = std::max(maxNameLength, nameLength);
5802 auto getWireForValue = [&](Value result) {
5803 return result.getUsers().begin()->getOperand(0);
5807 bool isFirst =
true;
5808 bool isZeroWidth =
false;
5810 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5812 auto &modPort = modPortInfo.
at(portNum);
5814 Value portVal = instPortValues[portNum];
5819 bool shouldPrintComma =
true;
5821 shouldPrintComma =
false;
5822 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5824 shouldPrintComma =
true;
5829 if (shouldPrintComma)
5832 emitLocationInfoAndNewLine(ops);
5847 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5848 auto modPortName = modPort.getVerilogName();
5851 if (modPortName.size() <= maxNameLength)
5852 ps.spaces(maxNameLength - modPortName.size() + 1);
5856 ps.scopedBox(PP::ibox0, [&]() {
5863 if (!modPort.isOutput()) {
5865 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5866 ps <<
"/* Zero width */";
5868 emitExpression(portVal, ops, LowestPrecedence);
5869 }
else if (portVal.use_empty()) {
5870 ps <<
"/* unused */";
5871 }
else if (portVal.hasOneUse() &&
5872 (output = dyn_cast_or_null<OutputOp>(
5873 portVal.getUses().begin()->getOwner()))) {
5878 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5880 containingPortList.atOutput(outputPortNo).getVerilogName());
5882 portVal = getWireForValue(portVal);
5883 emitExpression(portVal, ops);
5889 if (!isFirst || isZeroWidth) {
5890 emitLocationInfoAndNewLine(ops);
5903LogicalResult StmtEmitter::visitSV(BindOp op) {
5904 emitter.emitBind(op);
5905 assert(state.pendingNewline);
5909LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5910 emitComment(op.getCommentAttr());
5912 emitSVAttributes(op);
5915 ps.addCallback({op,
true});
5917 setPendingNewline();
5919 emitStatementBlock(*op.getBodyBlock());
5921 ps <<
"endinterface" << PP::newline;
5922 ps.addCallback({op,
false});
5923 setPendingNewline();
5928 emitSVAttributes(op);
5930 ps.addCallback({op,
true});
5932 ps << op.getContent();
5934 ps.addCallback({op,
false});
5935 setPendingNewline();
5939LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5941 emitSVAttributes(op);
5943 ps.addCallback({op,
true});
5945 ps << PP::neverbox <<
"// ";
5946 ps.invokeWithStringOS([&](
auto &os) {
5951 ps.invokeWithStringOS(
5952 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5956 ps.addCallback({op,
false});
5957 setPendingNewline();
5961LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5963 ps.addCallback({op,
true});
5967 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5968 auto port = cast<ModportStructAttr>(portAttr);
5969 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5970 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5971 ps << PPExtString(getSymOpName(signalDecl));
5975 ps.addCallback({op,
false});
5976 setPendingNewline();
5980LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5982 ps.addCallback({op,
true});
5983 SmallPtrSet<Operation *, 8> emitted;
5986 emitExpression(op.getIface(), emitted);
5987 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5988 emitExpression(op.getRhs(), emitted);
5990 ps.addCallback({op,
false});
5991 setPendingNewline();
5995LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5997 ps <<
"`" << op.getMacroIdentifier();
5998 setPendingNewline();
6002LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
6003 auto decl = op.getReferencedMacro(&state.symbolCache);
6006 ps.addCallback({op,
true});
6008 if (decl.getArgs()) {
6010 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
6011 ps << cast<StringAttr>(name);
6015 if (!op.getFormatString().empty()) {
6017 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
6020 ps.addCallback({op,
false});
6021 setPendingNewline();
6025void StmtEmitter::emitStatement(Operation *op) {
6032 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
6036 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
6037 succeeded(dispatchVerifVisitor(op)))
6040 emitOpError(op,
"emission to Verilog not supported");
6041 emitPendingNewlineIfNeeded();
6042 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
6043 setPendingNewline();
6054 StmtEmitter &stmtEmitter) {
6061 if (isa<IfDefProceduralOp>(op->getParentOp()))
6069 SmallVector<Value, 8> exprsToScan(op->getOperands());
6074 while (!exprsToScan.empty()) {
6075 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
6082 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
6083 auto *defOp = readInout.getOperand().getDefiningOp();
6090 if (isa<sv::WireOp>(defOp))
6095 if (!isa<RegOp, LogicOp>(defOp))
6101 if (isa<LogicOp>(defOp) &&
6102 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6106 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6107 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6115 exprsToScan.append(expr->getOperands().begin(),
6116 expr->getOperands().end());
6122 if (expr->getBlock() != op->getBlock())
6127 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6134template <
class AssignTy>
6136 AssignTy singleAssign;
6137 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6138 if (hasSVAttributes(user))
6141 if (auto assign = dyn_cast<AssignTy>(user)) {
6144 singleAssign = assign;
6148 return isa<ReadInOutOp>(user);
6150 return singleAssign;
6156 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6160 if (op1->getBlock() != user->getBlock())
6166 return op1->isBeforeInBlock(user);
6170LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6171 emitSVAttributes(op);
6172 auto value = op->getResult(0);
6173 SmallPtrSet<Operation *, 8> opsForLocation;
6174 opsForLocation.insert(op);
6176 ps.addCallback({op,
true});
6179 auto type = value.getType();
6185 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6187 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6188 unsigned targetColumn = 0;
6189 unsigned column = 0;
6192 if (maxDeclNameWidth > 0)
6193 targetColumn += maxDeclNameWidth + 1;
6196 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6197 }
else if (!word.empty()) {
6199 column += word.size();
6200 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6201 ps.spaces(numSpaces);
6202 column += numSpaces;
6205 SmallString<8> typeString;
6208 llvm::raw_svector_ostream stringStream(typeString);
6211 true, singleBitDefaultType);
6214 if (maxTypeWidth > 0)
6215 targetColumn += maxTypeWidth + 1;
6216 unsigned numSpaces = 0;
6217 if (!typeString.empty()) {
6219 column += typeString.size();
6222 if (targetColumn > column)
6223 numSpaces = targetColumn - column;
6224 ps.spaces(numSpaces);
6225 column += numSpaces;
6231 ps.invokeWithStringOS(
6232 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6235 if (state.options.printDebugInfo) {
6236 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6237 auto innerSym = innerSymOp.getInnerSymAttr();
6238 if (innerSym && !innerSym.empty()) {
6240 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6246 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6247 ps << PP::space <<
"=" << PP::space;
6248 ps.invokeWithStringOS([&](
auto &os) {
6249 emitter.printParamValue(localparam.getValue(), os, [&]() {
6250 return op->emitOpError(
"invalid localparam value");
6255 if (
auto regOp = dyn_cast<RegOp>(op)) {
6256 if (
auto initValue = regOp.getInit()) {
6257 ps << PP::space <<
"=" << PP::space;
6258 ps.scopedBox(PP::ibox0, [&]() {
6259 emitExpression(initValue, opsForLocation, LowestPrecedence,
6268 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6272 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6273 auto *source = singleAssign.getSrc().getDefiningOp();
6277 if (!source || isa<ConstantOp>(source) ||
6278 op->getNextNode() == singleAssign) {
6279 ps << PP::space <<
"=" << PP::space;
6280 ps.scopedBox(PP::ibox0, [&]() {
6281 emitExpression(singleAssign.getSrc(), opsForLocation,
6285 emitter.assignsInlined.insert(singleAssign);
6293 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6297 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6300 auto *source = singleAssign.getSrc().getDefiningOp();
6304 if (!source || isa<ConstantOp>(source) ||
6307 ps << PP::space <<
"=" << PP::space;
6308 ps.scopedBox(PP::ibox0, [&]() {
6309 emitExpression(singleAssign.getSrc(), opsForLocation,
6314 emitter.assignsInlined.insert(singleAssign);
6315 emitter.expressionsEmittedIntoDecl.insert(op);
6322 ps.addCallback({op,
false});
6323 emitLocationInfoAndNewLine(opsForLocation);
6327void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6330 NameCollector collector(emitter);
6331 collector.collectNames(block);
6334 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6335 maxTypeWidth = collector.getMaxTypeWidth();
6338void StmtEmitter::emitStatementBlock(Block &body) {
6339 ps.scopedBox(PP::bbox2, [&]() {
6344 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6345 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6350 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6351 collectNamesAndCalculateDeclarationWidths(body);
6354 for (
auto &op : body) {
6361void ModuleEmitter::emitStatement(Operation *op) {
6362 StmtEmitter(*
this, state.options).emitStatement(op);
6367void ModuleEmitter::emitSVAttributes(Operation *op) {
6375 setPendingNewline();
6382void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6383 auto verilogName =
module.getVerilogModuleNameAttr();
6385 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6387 setPendingNewline();
6396void ModuleEmitter::emitBind(BindOp op) {
6398 emitError(op,
"SV attributes emission is unimplemented for the op");
6399 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6405 Operation *childMod =
6406 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6410 ps.addCallback({op,
true});
6411 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6412 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6414 bool isFirst =
true;
6415 ps.scopedBox(PP::bbox2, [&]() {
6416 auto parentPortInfo = parentMod.getPortList();
6421 size_t maxNameLength = 0;
6422 auto lineLength = state.options.getEmittedLineLength();
6423 for (
auto &elt : childPortInfo) {
6424 auto portName = elt.getVerilogName();
6425 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6426 size_t nameLength = elt.getName().size();
6427 if (!lineLength || nameLength <= *lineLength / 3)
6428 maxNameLength = std::max(maxNameLength, nameLength);
6431 SmallVector<Value> instPortValues(childPortInfo.size());
6432 inst.getValues(instPortValues, childPortInfo);
6434 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6436 Value portVal = instPortValues[idx];
6442 bool shouldPrintComma =
true;
6444 shouldPrintComma =
false;
6445 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6447 shouldPrintComma =
true;
6452 if (shouldPrintComma)
6465 ps << PP::neverbox <<
"//";
6470 if (elt.getName().size() <= maxNameLength)
6471 ps.nbsp(maxNameLength - elt.getName().size());
6473 llvm::SmallPtrSet<Operation *, 4> ops;
6474 if (elt.isOutput()) {
6475 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6476 "output port must have either single or no use");
6477 if (portVal.use_empty()) {
6478 ps <<
"/* unused */";
6479 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6480 portVal.getUses().begin()->getOwner())) {
6483 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6485 parentPortList.atOutput(outputPortNo).getVerilogName());
6487 portVal = portVal.getUsers().begin()->getOperand(0);
6488 ExprEmitter(*
this, ops)
6489 .emitExpression(portVal, LowestPrecedence,
6493 ExprEmitter(*
this, ops)
6494 .emitExpression(portVal, LowestPrecedence,
6507 ps.addCallback({op,
false});
6508 setPendingNewline();
6511void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6513 emitError(op,
"SV attributes emission is unimplemented for the op");
6515 auto instance = op.getReferencedInstance(&state.symbolCache);
6517 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6518 instance.getInterfaceType().getInterface());
6520 ps.addCallback({op,
true});
6521 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6522 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6524 ps.addCallback({op,
false});
6525 setPendingNewline();
6528void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6532 auto printParamType = [&](Type type, Attribute defaultValue,
6533 SmallString<8> &result) {
6535 llvm::raw_svector_ostream sstream(result);
6540 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6541 if (intAttr.getValue().getBitWidth() == 32)
6543 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6544 if (fpAttr.getType().isF64())
6547 if (isa<NoneType>(type))
6554 if (
auto intType = type_dyn_cast<IntegerType>(type))
6555 if (intType.getWidth() == 32) {
6556 sstream <<
"/*integer*/";
6560 printPackedType(type, sstream, module->getLoc(),
6568 size_t maxTypeWidth = 0;
6569 SmallString<8> scratch;
6570 for (
auto param : params) {
6571 auto paramAttr = cast<ParamDeclAttr>(param);
6573 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6574 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6577 if (maxTypeWidth > 0)
6580 ps.scopedBox(PP::bbox2, [&]() {
6581 ps << PP::newline <<
"#(";
6582 ps.scopedBox(PP::cbox0, [&]() {
6585 [&](Attribute param) {
6586 auto paramAttr = cast<ParamDeclAttr>(param);
6587 auto defaultValue = paramAttr.getValue();
6589 printParamType(paramAttr.getType(), defaultValue, scratch);
6590 if (!scratch.empty())
6592 if (scratch.size() < maxTypeWidth)
6593 ps.nbsp(maxTypeWidth - scratch.size());
6595 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6596 module, paramAttr.getName()));
6600 ps.invokeWithStringOS([&](
auto &os) {
6602 return module->emitError("parameter '")
6603 << paramAttr.getName().getValue()
6604 << "' has invalid value";
6609 [&]() { ps <<
"," << PP::newline; });
6615void ModuleEmitter::emitPortList(Operation *module,
6617 bool emitAsTwoStateType) {
6619 if (portInfo.
size())
6620 emitLocationInfo(module->getLoc());
6624 bool hasOutputs =
false, hasZeroWidth =
false;
6625 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6626 SmallVector<SmallString<8>, 16> portTypeStrings;
6628 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6629 auto port = portInfo.
at(i);
6633 lastNonZeroPort = i;
6636 portTypeStrings.push_back({});
6638 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6640 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6643 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6646 if (maxTypeWidth > 0)
6650 ps.scopedBox(PP::bbox2, [&]() {
6651 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6652 auto lastPort = e - 1;
6655 auto portType = portInfo.
at(portIdx).
type;
6659 bool isZeroWidth =
false;
6664 ps << (isZeroWidth ?
"// " :
" ");
6668 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6669 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6670 (state.options.emitWireInPorts ? 5 : 0) +
6675 if (!isa<ModportType>(portType)) {
6676 switch (thisPortDirection) {
6677 case ModulePort::Direction::Output:
6680 case ModulePort::Direction::Input:
6681 ps << (hasOutputs ?
"input " :
"input ");
6683 case ModulePort::Direction::InOut:
6684 ps << (hasOutputs ?
"inout " :
"inout ");
6687 if (state.options.emitWireInPorts)
6689 if (!portTypeStrings[portIdx].
empty())
6690 ps << portTypeStrings[portIdx];
6691 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6692 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6694 ps << portTypeStrings[portIdx];
6695 if (portTypeStrings[portIdx].size() < startOfNamePos)
6696 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6703 ps.invokeWithStringOS(
6704 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6707 auto innerSym = portInfo.
at(portIdx).
getSym();
6708 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6710 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6715 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6719 if (
auto loc = portInfo.
at(portIdx).
loc)
6720 emitLocationInfo(loc);
6730 if (!state.options.disallowPortDeclSharing) {
6731 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6734 auto port = portInfo.
at(portIdx);
6738 bool isZeroWidth =
false;
6743 ps << (isZeroWidth ?
"// " :
" ");
6746 ps.nbsp(startOfNamePos);
6749 StringRef name = port.getVerilogName();
6753 ps.invokeWithStringOS(
6754 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6757 auto sym = port.getSym();
6758 if (state.options.printDebugInfo && sym && !sym.empty())
6759 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6763 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6767 if (
auto loc = port.loc)
6768 emitLocationInfo(loc);
6779 if (!portInfo.
size()) {
6781 SmallPtrSet<Operation *, 8> moduleOpSet;
6782 moduleOpSet.insert(module);
6783 emitLocationInfoAndNewLine(moduleOpSet);
6786 ps <<
");" << PP::newline;
6787 setPendingNewline();
6791void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6792 currentModuleOp =
module;
6794 emitComment(module.getCommentAttr());
6795 emitSVAttributes(module);
6797 ps.addCallback({module,
true});
6801 emitParameters(module, module.getParameters());
6805 assert(state.pendingNewline);
6808 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6811 ps.addCallback({module,
false});
6813 setPendingNewline();
6815 currentModuleOp =
nullptr;
6818void ModuleEmitter::emitFunc(FuncOp func) {
6820 if (func.isDeclaration())
6823 currentModuleOp = func;
6825 ps.addCallback({func,
true});
6829 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6831 ps <<
"endfunction";
6833 currentModuleOp =
nullptr;
6842 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6849 void emit(emit::FileListOp op);
6852 void emit(Block *block);
6854 void emitOp(emit::RefOp op);
6855 void emitOp(emit::VerbatimOp op);
6859 for (Operation &op : *block) {
6860 TypeSwitch<Operation *>(&op)
6861 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6862 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6863 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6864 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6865 .Case<BindInterfaceOp>(
6866 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6867 .Case<TypeScopeOp>([&](
auto typedecls) {
6868 ModuleEmitter(state).emitStatement(typedecls);
6871 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6877 for (
auto sym : op.getFiles()) {
6878 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6880 auto it = state.fileMapping.find(fileName);
6881 if (it == state.fileMapping.end()) {
6882 emitOpError(op,
" references an invalid file: ") << sym;
6886 auto file = cast<emit::FileOp>(it->second);
6887 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6894 StringAttr target = op.getTargetAttr().getAttr();
6895 auto *targetOp = state.symbolCache.getDefinition(target);
6896 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6898 TypeSwitch<Operation *>(targetOp)
6899 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6900 .Case<hw::HWModuleOp>(
6901 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6902 .Case<TypeScopeOp>([&](
auto typedecls) {
6903 ModuleEmitter(state).emitStatement(typedecls);
6906 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6912 SmallPtrSet<Operation *, 8> ops;
6917 StringRef text = op.getText();
6921 const auto &[lhs, rhs] = text.split(
'\n');
6925 ps << PP::end << PP::newline << PP::neverbox;
6927 }
while (!text.empty());
6930 emitLocationInfoAndNewLine(ops);
6948 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6949 moduleOp->walk([&](Operation *op) {
6951 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6954 SymbolTable::getSymbolAttrName()),
6955 name.getSymName(), op);
6956 if (isa<BindOp>(op))
6962 auto collectPorts = [&](
auto moduleOp) {
6963 auto portInfo = moduleOp.getPortList();
6964 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6965 if (!p.attrs || p.attrs.empty())
6967 for (NamedAttribute portAttr : p.attrs) {
6968 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6977 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6978 for (
auto file :
designOp.getOps<emit::FileOp>())
6979 for (
auto refs : file.getOps<emit::RefOp>())
6980 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6982 SmallString<32> outputPath;
6983 for (
auto &op : *
designOp.getBody()) {
6986 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6988 bool hasFileName =
false;
6989 bool emitReplicatedOps = !isFileOp;
6990 bool addToFilelist = !isFileOp;
6996 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6998 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6999 <<
" on " << op <<
"\n";);
7000 if (!attr.isDirectory())
7003 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
7004 addToFilelist = !attr.getExcludeFromFilelist().getValue();
7007 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
7012 if (!defaultFileName.isTriviallyEmpty()) {
7013 llvm::sys::path::append(outputPath, defaultFileName);
7015 op->emitError(
"file name unspecified");
7017 llvm::sys::path::append(outputPath,
"error.out");
7021 auto destFile = StringAttr::get(op->getContext(), outputPath);
7022 auto &file =
files[destFile];
7023 file.ops.push_back(info);
7024 file.emitReplicatedOps = emitReplicatedOps;
7025 file.addToFilelist = addToFilelist;
7026 file.isVerilog = outputPath.ends_with(
".sv");
7031 if (!attr || attr.isDirectory()) {
7032 auto excludeFromFileListAttr =
7033 BoolAttr::get(op->getContext(), !addToFilelist);
7034 auto includeReplicatedOpsAttr =
7035 BoolAttr::get(op->getContext(), emitReplicatedOps);
7036 auto outputFileAttr = hw::OutputFileAttr::get(
7037 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
7038 op->setAttr(
"output_file", outputFileAttr);
7044 TypeSwitch<Operation *>(&op)
7045 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
7047 fileMapping.try_emplace(file.getSymNameAttr(), file);
7048 separateFile(file, file.getFileName());
7050 .Case<emit::FragmentOp>([&](
auto fragment) {
7053 .Case<HWModuleOp>([&](
auto mod) {
7055 auto sym = mod.getNameAttr();
7058 collectInstanceSymbolsAndBinds(mod);
7060 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
7061 if (it->second.size() != 1 || attr) {
7064 op.emitError(
"modules can be emitted to a single file");
7072 if (attr || separateModules)
7078 .Case<InterfaceOp>([&](InterfaceOp intf) {
7083 for (
auto &op : *intf.getBodyBlock())
7084 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
7085 if (
auto name = symOp.getNameAttr())
7089 if (attr || separateModules)
7090 separateFile(intf, intf.getSymName() +
".sv");
7096 separateFile(op, op.getOutputFile().getFilename().getValue());
7098 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7104 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7105 [&](Operation *op) {
7111 separateFile(op,
"");
7113 .Case<FuncOp>([&](
auto op) {
7119 separateFile(op,
"");
7123 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7126 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7135 separateFile(op,
"");
7137 .Case<BindOp>([&](
auto op) {
7139 separateFile(op,
"bindfile.sv");
7144 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
7145 .Case<MacroDeclOp>([&](
auto op) {
7148 .Case<sv::ReserveNamesOp>([](
auto op) {
7151 .Case<om::ClassLike>([&](
auto op) {
7154 .Case<om::ConstantOp>([&](
auto op) {
7157 .Default([&](
auto *) {
7158 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7178 size_t lastReplicatedOp = 0;
7180 bool emitHeaderInclude =
7183 if (emitHeaderInclude)
7186 size_t numReplicatedOps =
7191 DenseSet<emit::FragmentOp> includedFragments;
7192 for (
const auto &opInfo : file.
ops) {
7193 Operation *op = opInfo.op;
7197 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7203 if (
auto fragments =
7205 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7209 op->emitError(
"cannot find referenced fragment ") << sym;
7212 emit::FragmentOp fragment = it->second;
7213 if (includedFragments.insert(fragment).second) {
7214 thingsToEmit.emplace_back(it->second);
7220 thingsToEmit.emplace_back(op);
7225 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7230 TypeSwitch<Operation *>(op)
7231 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7232 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7235 .Case<HWModuleGeneratedOp>(
7236 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7237 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7238 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7239 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7240 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7241 .Case<TypeScopeOp>([&](
auto typedecls) {
7242 ModuleEmitter(state).emitStatement(typedecls);
7244 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7246 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7247 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7248 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7249 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7250 .Default([&](
auto *op) {
7251 state.encounteredError =
true;
7252 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7259 llvm::formatted_raw_ostream &os,
7260 StringAttr fileName,
bool parallelize) {
7265 parallelize &=
context->isMultithreadingEnabled();
7276 size_t lineOffset = 0;
7277 for (
auto &entry : thingsToEmit) {
7278 entry.verilogLocs.setStream(os);
7279 if (
auto *op = entry.getOperation()) {
7284 state.addVerilogLocToOps(lineOffset, fileName);
7286 os << entry.getStringData();
7291 if (state.encounteredError)
7309 SmallString<256> buffer;
7310 llvm::raw_svector_ostream tmpStream(buffer);
7311 llvm::formatted_raw_ostream rs(tmpStream);
7319 if (state.encounteredError)
7324 for (
auto &entry : thingsToEmit) {
7327 auto *op = entry.getOperation();
7329 auto lineOffset = os.getLine() + 1;
7330 os << entry.getStringData();
7334 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7337 entry.verilogLocs.setStream(os);
7344 state.addVerilogLocToOps(0, fileName);
7345 if (state.encounteredError) {
7364 module.emitWarning()
7365 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7366 "created only at SplitExportVerilog";
7375 for (
const auto &it : emitter.
files) {
7376 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7377 "\" ----- 8< -----\n\n");
7383 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7384 "\" ----- 8< -----\n\n");
7385 for (
auto &name : it.second)
7386 contents += name.str() +
"\n";
7387 list.emplace_back(contents);
7390 llvm::formatted_raw_ostream rs(os);
7394 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7401 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7403 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7404 if (failed(failableParallelForEach(
7405 module->getContext(), modulesToPrepare,
7406 [&](
auto op) { return prepareHWModule(op, options); })))
7413struct ExportVerilogPass
7414 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7415 ExportVerilogPass(raw_ostream &os) : os(os) {}
7416 void runOnOperation()
override {
7418 mlir::OpPassManager preparePM(
"builtin.module");
7419 preparePM.addPass(createLegalizeAnonEnums());
7420 auto &modulePM = preparePM.nestAny();
7421 modulePM.addPass(createPrepareForEmission());
7422 if (failed(runPipeline(preparePM, getOperation())))
7423 return signalPassFailure();
7426 return signalPassFailure();
7433struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7434 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7435 : ExportVerilogPass{*os} {
7436 owned = std::move(os);
7440 std::unique_ptr<llvm::raw_ostream> owned;
7444std::unique_ptr<mlir::Pass>
7446 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7449std::unique_ptr<mlir::Pass>
7451 return std::make_unique<ExportVerilogPass>(os);
7462static std::unique_ptr<llvm::ToolOutputFile>
7466 SmallString<128> outputFilename(dirname);
7468 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7471 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7473 emitter.
designOp.emitError(
"cannot create output directory \"")
7474 << outputDir <<
"\": " << error.message();
7480 std::string errorMessage;
7481 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7483 emitter.
designOp.emitError(errorMessage);
7500 llvm::formatted_raw_ostream rs(output->os());
7506 StringAttr::get(fileName.getContext(), output->getFilename()),
7512 StringRef dirname) {
7523 bool insertSuccess =
7525 .insert({StringAttr::get(module.getContext(),
circtHeader),
7531 if (!insertSuccess) {
7532 module.emitError() << "tried to emit a heder to " << circtHeader
7533 << ", but the file is used as an output too.";
7539 parallelForEach(module->getContext(), emitter.
files.begin(),
7540 emitter.
files.end(), [&](
auto &it) {
7541 createSplitOutputFile(it.first, it.second, dirname,
7546 SmallString<128> filelistPath(dirname);
7547 llvm::sys::path::append(filelistPath,
"filelist.f");
7549 std::string errorMessage;
7550 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7552 module->emitError(errorMessage);
7556 for (
const auto &it : emitter.
files) {
7557 if (it.second.addToFilelist)
7558 output->os() << it.first.str() <<
"\n";
7567 for (
auto &name : it.second)
7568 output->os() << name.str() <<
"\n";
7577 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7579 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7580 if (failed(failableParallelForEach(
7581 module->getContext(), modulesToPrepare,
7582 [&](
auto op) { return prepareHWModule(op, options); })))
7590struct ExportSplitVerilogPass
7591 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7592 ExportSplitVerilogPass(StringRef directory) {
7593 directoryName = directory.str();
7595 void runOnOperation()
override {
7597 mlir::OpPassManager preparePM(
"builtin.module");
7600 modulePM.addPass(createPrepareForEmission());
7601 if (failed(runPipeline(preparePM, getOperation())))
7602 return signalPassFailure();
7605 return signalPassFailure();
7610std::unique_ptr<mlir::Pass>
7612 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static bool hasSVAttributes(Operation *op)
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool haveMatchingDims(Type a, Type b, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
True iff 'a' and 'b' have the same wire dims.
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
Push this type's dimension into a vector.
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static std::unique_ptr< Context > context
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
Signals that an operation's regions are procedural.
This stores lookup tables to make manipulating and working with the IR more efficient.
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=invalidPort)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
This helps visit TypeOp nodes.
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
PP
Send one of these to TokenStream to add the corresponding token.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
Information to control the emission of a single operation into a file.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
const LoweringOptions & options
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.