40#include "mlir/IR/BuiltinOps.h"
41#include "mlir/IR/ImplicitLocOpBuilder.h"
42#include "mlir/IR/Location.h"
43#include "mlir/IR/Threading.h"
44#include "mlir/Interfaces/FunctionImplementation.h"
45#include "mlir/Pass/PassManager.h"
46#include "mlir/Support/FileUtilities.h"
47#include "llvm/ADT/MapVector.h"
48#include "llvm/ADT/STLExtras.h"
49#include "llvm/ADT/StringSet.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/Support/FileSystem.h"
52#include "llvm/Support/FormattedStream.h"
53#include "llvm/Support/Path.h"
54#include "llvm/Support/SaveAndRestore.h"
55#include "llvm/Support/ToolOutputFile.h"
56#include "llvm/Support/raw_ostream.h"
59#define GEN_PASS_DEF_EXPORTSPLITVERILOG
60#define GEN_PASS_DEF_EXPORTVERILOG
61#include "circt/Conversion/Passes.h.inc"
68using namespace ExportVerilog;
70using namespace pretty;
72#define DEBUG_TYPE "export-verilog"
80enum VerilogPrecedence {
101enum SubExprSignResult { IsSigned, IsUnsigned };
107 VerilogPrecedence precedence;
110 SubExprSignResult signedness;
112 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
113 : precedence(precedence), signedness(signedness) {}
123 return Builder(ctx).getI32IntegerAttr(value);
126static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
127 return Builder(ctx).getIntegerAttr(t, value);
143 if (isa<VerbatimExprOp>(op)) {
144 if (op->getNumOperands() == 0 &&
145 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
150 if (isa<XMRRefOp>(op))
154 if (isa<MacroRefExprOp>(op))
164 if (op->getNumOperands() == 0)
168 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
172 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
173 auto *indexOp = array.getIndex().getDefiningOp();
174 if (!indexOp || isa<ConstantOp>(indexOp))
176 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
177 auto *readSrc = read.getInput().getDefiningOp();
179 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
194 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
195 return attr.getValue();
196 return TypeSwitch<Operation *, StringRef>(symOp)
201 return op.getVerilogNameAttr().getValue();
203 .Case<InterfaceOp>([&](InterfaceOp op) {
206 .Case<InterfaceSignalOp>(
207 [&](InterfaceSignalOp op) {
return op.getSymName(); })
208 .Case<InterfaceModportOp>(
209 [&](InterfaceModportOp op) {
return op.getSymName(); })
210 .Default([&](Operation *op) {
211 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
212 return attr.getValue();
213 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
214 return attr.getValue();
215 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
216 return attr.getValue();
218 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
219 return attr.getValue();
220 return StringRef(
"");
225template <
typename PPS>
227 os <<
"/*Zero width*/ 1\'b0";
232 auto hml = cast<HWModuleLike>(module);
233 return hml.getPort(portArgNum).getVerilogName();
238 auto hml = cast<HWModuleLike>(module);
239 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
240 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
241 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
242 return updatedName.getValue();
243 return hml.getHWModuleType().getPortName(pId);
252 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
253 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
254 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
255 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
256 UnpackedOpenArrayCastOp>(op))
260 if (isa<verif::ContractOp>(op))
271 SmallVectorImpl<Attribute> &dims, Type type, Location loc,
272 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
273 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
274 if (integer.getWidth() != 1)
275 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
278 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
279 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
280 getTypeDims(dims, array.getElementType(), loc, errorHandler);
284 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
285 dims.push_back(intType.getWidth());
289 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
290 return getTypeDims(dims, inout.getElementType(), loc, errorHandler);
291 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
292 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
293 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
294 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
295 if (hw::type_isa<InterfaceType, StructType, EnumType, UnionType>(type))
298 errorHandler(loc) <<
"value has an unsupported verilog type " << type;
304 Type a, Type b, Location loc,
305 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
306 SmallVector<Attribute, 4> aDims;
309 SmallVector<Attribute, 4> bDims;
312 return aDims == bDims;
318 if (
auto intType = dyn_cast<IntegerType>(type))
319 return intType.getWidth() == 0;
320 if (
auto inout = dyn_cast<hw::InOutType>(type))
322 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
323 return uarray.getNumElements() == 0 ||
325 if (
auto array = dyn_cast<hw::ArrayType>(type))
326 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
327 if (
auto structType = dyn_cast<hw::StructType>(type))
328 return llvm::all_of(structType.getElements(),
329 [](
auto elem) { return isZeroBitType(elem.type); });
330 if (
auto enumType = dyn_cast<hw::EnumType>(type))
331 return enumType.getFields().empty();
332 if (
auto unionType = dyn_cast<hw::UnionType>(type))
333 return hw::getBitWidth(unionType) == 0;
345 return TypeSwitch<Type, Type>(type)
346 .Case<InOutType>([](InOutType inoutType) {
349 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
352 .Default([](Type type) {
return type; });
357 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
358 auto elementType = cast<hw::InOutType>(type).getElementType();
364 return TypeSwitch<Type, bool>(type)
365 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
368 .Case<StructType>([](
auto) {
return true; })
369 .Default([](
auto) {
return false; });
383 if (
auto name = lhs.getName().compare(rhs.getName()))
385 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
390 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
392 if (lhs.getLine() != rhs.getLine())
393 return lhs.getLine() < rhs.getLine() ? -1 : 1;
394 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
399 Location lhsCallee = lhs.getCallee();
400 Location rhsCallee = rhs.getCallee();
404 Location lhsCaller = lhs.getCaller();
405 Location rhsCaller = rhs.getCaller();
409template <
typename TTargetLoc>
411 auto lhsT = dyn_cast<TTargetLoc>(lhs);
412 auto rhsT = dyn_cast<TTargetLoc>(rhs);
439 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
444 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
449 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
466 SmallPtrSetImpl<Attribute> &locationSet) {
467 llvm::TypeSwitch<Location, void>(loc)
468 .Case<FusedLoc>([&](
auto fusedLoc) {
469 for (
auto subLoc : fusedLoc.getLocations())
472 .Default([&](
auto loc) { locationSet.insert(loc); });
476template <
typename TVector>
478 llvm::array_pod_sort(
479 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
480 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
488 SmallPtrSet<Attribute, 8> locationSet;
489 locationSet.insert(loc);
490 llvm::raw_string_ostream os(
output);
496 const SmallPtrSetImpl<Operation *> &ops) {
500 SmallPtrSet<Attribute, 8> locationSet;
503 llvm::raw_string_ostream os(
output);
512 const SmallPtrSetImpl<Attribute> &locationSet) {
513 if (style == LoweringOptions::LocationInfoStyle::None)
516 llvm::raw_string_ostream sstr(resstr);
518 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
522 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
523 "other styles must be already handled");
524 os <<
"@[" << resstr <<
"]";
533 const SmallPtrSetImpl<Attribute> &locationSet)
549 bool withName = !loc.getName().empty();
551 os <<
"'" << loc.getName().strref() <<
"'(";
560 os << loc.getFilename().getValue();
561 if (
auto line = loc.getLine()) {
563 if (
auto col = loc.getColumn())
575 StringRef lastFileName;
576 for (
size_t i = 0, e = locVector.size(); i != e;) {
581 auto first = locVector[i];
582 if (first.getFilename() != lastFileName) {
583 lastFileName = first.getFilename();
590 first.getFilename() == locVector[
end].getFilename() &&
591 first.getLine() == locVector[
end].getLine())
596 if (
auto line = first.getLine()) {
598 if (
auto col = first.getColumn())
606 os <<
':' << first.getLine() <<
":{";
608 os << locVector[i++].getColumn();
620 llvm::TypeSwitch<Location, void>(loc)
621 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
623 .Case<mlir::FusedLoc>([&](
auto loc) {
624 SmallPtrSet<Attribute, 8> locationSet;
628 .Default([&](
auto loc) {
640 switch (locationSet.size()) {
651 SmallVector<FileLineColLoc, 8> flcLocs;
652 SmallVector<Attribute, 8> otherLocs;
653 flcLocs.reserve(locationSet.size());
654 otherLocs.reserve(locationSet.size());
655 for (Attribute loc : locationSet) {
656 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
657 flcLocs.push_back(flcLoc);
659 otherLocs.push_back(loc);
670 size_t sstrSize =
os.tell();
671 bool emittedAnything =
false;
672 auto recheckEmittedSomething = [&]() {
673 size_t currSize =
os.tell();
674 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
675 emittedAnything |= emittedSomethingSinceLastCheck;
677 return emittedSomethingSinceLastCheck;
686 if (recheckEmittedSomething()) {
688 recheckEmittedSomething();
694 if (emittedAnything && !flcLocs.empty())
699 llvm::raw_string_ostream &
os;
711 if (isa<BlockArgument>(v))
720 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
725 if (v.getDefiningOp<ReadInterfaceSignalOp>())
738 if (
auto cast = dyn_cast<BitcastOp>(op))
739 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
741 [&](Location loc) { return emitError(loc); })) {
744 if (op->hasOneUse() &&
745 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
753 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
759 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
763 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
764 if (verbatim.getFormatString().size() > 32)
769 for (
auto &use : op->getUses()) {
770 auto *user = use.getOwner();
780 UnionExtractOp, IndexedPartSelectOp>(user))
781 if (use.getOperandNumber() == 0 &&
792 auto usedInExprControl = [user, &use]() {
793 return TypeSwitch<Operation *, bool>(user)
794 .Case<ltl::ClockOp>([&](
auto clockOp) {
796 return clockOp.getClock() == use.get();
798 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
799 sv::CoverConcurrentOp>(
800 [&](
auto op) {
return op.getClock() == use.get(); })
801 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
802 sv::CoverPropertyOp>([&](
auto op) {
803 return op.getDisable() == use.get() || op.getClock() == use.get();
805 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
810 .Default([](
auto) {
return false; });
813 if (!usedInExprControl())
817 auto read = dyn_cast<ReadInOutOp>(op);
820 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
831 unsigned numStatements = 0;
832 block.walk([&](Operation *op) {
834 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
835 return WalkResult::advance();
837 TypeSwitch<Operation *, unsigned>(op)
838 .Case<VerbatimOp>([&](
auto) {
844 .Case<IfOp>([&](
auto) {
855 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
856 .Case<OutputOp>([&](OutputOp oop) {
859 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
860 Operation *op = operand.getDefiningOp();
861 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
864 .Default([](
auto) {
return 1; });
865 if (numStatements > 1)
866 return WalkResult::interrupt();
867 return WalkResult::advance();
869 if (numStatements == 0)
871 if (numStatements == 1)
881 if (op->getResult(0).use_empty())
886 if (op->hasOneUse() &&
887 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
888 *op->getUsers().begin()))
910 for (
auto &op : *elseBlock) {
911 if (
auto opIf = dyn_cast<IfOp>(op)) {
928template <
typename PPS>
930 enum Container { NoContainer, InComment, InAttr };
931 Container currentContainer = NoContainer;
933 auto closeContainer = [&] {
934 if (currentContainer == NoContainer)
936 if (currentContainer == InComment)
938 else if (currentContainer == InAttr)
940 ps << PP::end << PP::end;
942 currentContainer = NoContainer;
945 bool isFirstContainer =
true;
946 auto openContainer = [&](Container newContainer) {
947 assert(newContainer != NoContainer);
948 if (currentContainer == newContainer)
952 if (!isFirstContainer)
953 ps << (mayBreak ? PP::space : PP::nbsp);
954 isFirstContainer =
false;
957 if (newContainer == InComment)
959 else if (newContainer == InAttr)
961 currentContainer = newContainer;
969 ps.scopedBox(PP::cbox0, [&]() {
970 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
971 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
973 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
975 if (attr.getExpression())
976 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
985 if (
auto *op = val.getDefiningOp())
988 if (
auto port = dyn_cast<BlockArgument>(val)) {
990 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
991 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
993 port.getArgNumber());
995 assert(
false &&
"unhandled value");
1007class VerilogEmitterState {
1009 explicit VerilogEmitterState(ModuleOp designOp,
1015 llvm::formatted_raw_ostream &os,
1016 StringAttr fileName,
OpLocMap &verilogLocMap)
1017 : designOp(designOp), shared(shared), options(options),
1018 symbolCache(symbolCache), globalNames(globalNames),
1019 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1020 pp(os, options.emittedLineLength), fileName(fileName) {
1021 pp.setListener(&saver);
1044 llvm::formatted_raw_ostream &os;
1046 bool encounteredError =
false;
1055 bool pendingNewline =
false;
1069 StringAttr fileName;
1075 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1078 verilogLocMap.
clear();
1082 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1083 void operator=(
const VerilogEmitterState &) =
delete;
1096using CallbackDataTy = std::pair<Operation *, bool>;
1100 VerilogEmitterState &state;
1105 explicit EmitterBase(VerilogEmitterState &state)
1107 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1109 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1110 state.encounteredError =
true;
1111 return op->emitError(message);
1114 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1115 state.encounteredError =
true;
1116 return op->emitOpError(message);
1119 InFlightDiagnostic emitError(Location loc,
const Twine &message =
"") {
1120 state.encounteredError =
true;
1121 return mlir::emitError(loc, message);
1124 void emitLocationImpl(llvm::StringRef location) {
1127 ps << PP::neverbreak;
1128 if (!location.empty())
1129 ps <<
"\t// " << location;
1132 void emitLocationInfo(Location loc) {
1140 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1143 setPendingNewline();
1146 template <
typename PPS>
1147 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1148 llvm::function_ref<
void(Value)> operandEmitter,
1149 ArrayAttr symAttrs);
1155 void emitComment(StringAttr comment);
1159 void emitPendingNewlineIfNeeded() {
1160 if (state.pendingNewline) {
1161 state.pendingNewline =
false;
1165 void setPendingNewline() {
1166 assert(!state.pendingNewline);
1167 state.pendingNewline =
true;
1170 void startStatement() { emitPendingNewlineIfNeeded(); }
1173 void operator=(
const EmitterBase &) =
delete;
1174 EmitterBase(
const EmitterBase &) =
delete;
1178template <
typename PPS>
1179void EmitterBase::emitTextWithSubstitutions(
1180 PPS &ps, StringRef
string, Operation *op,
1181 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1192 if (
auto *itemOp = item.getOp()) {
1193 if (item.hasPort()) {
1197 if (!symOpName.empty())
1199 emitError(itemOp,
"cannot get name for symbol ") << sym;
1201 emitError(op,
"cannot get name for symbol ") << sym;
1203 return StringRef(
"<INVALID>");
1209 unsigned numSymOps = symAttrs.size();
1210 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1213 next =
string.find(
"{{", next);
1214 if (next == StringRef::npos)
1221 while (next <
string.size() &&
isdigit(
string[next]))
1224 if (start == next) {
1228 size_t operandNoLength = next - start;
1231 StringRef fmtOptsStr;
1232 if (
string[next] ==
':') {
1233 size_t startFmtOpts = next + 1;
1234 while (next <
string.size() &&
string[next] !=
'}')
1236 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1240 if (!
string.substr(next).starts_with(
"}}"))
1244 unsigned operandNo = 0;
1245 if (
string.drop_front(start)
1246 .take_front(operandNoLength)
1247 .getAsInteger(10, operandNo)) {
1248 emitError(op,
"operand substitution too large");
1254 auto before =
string.take_front(start - 2);
1255 if (!before.empty())
1260 if (operandNo < op->getNumOperands())
1262 operandEmitter(op->getOperand(operandNo));
1263 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1264 unsigned symOpNum = operandNo - op->getNumOperands();
1265 auto sym = symAttrs[symOpNum];
1266 StringRef symVerilogName;
1267 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1268 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1269 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1270 auto namepath = globalRef.getNamepathAttr().getValue();
1271 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1274 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1276 auto innerRef = cast<InnerRefAttr>(sym);
1277 auto ref = state.symbolCache.getInnerDefinition(
1278 innerRef.getModule(), innerRef.getName());
1279 ps << namify(innerRef, ref);
1282 symVerilogName = namify(sym, symOp);
1285 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1286 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1288 symVerilogName = namify(sym, symOp);
1290 if (!symVerilogName.empty())
1293 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1297 string =
string.drop_front(next);
1303 while (emitUntilSubstitution())
1307 if (!
string.
empty())
1311void EmitterBase::emitComment(StringAttr comment) {
1318 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1322 auto ref = comment.getValue();
1324 while (!ref.empty()) {
1325 std::tie(line, ref) = ref.split(
"\n");
1332 if (line.size() <= lineLength) {
1334 setPendingNewline();
1345 auto breakPos = line.rfind(
' ', lineLength);
1347 if (breakPos == StringRef::npos) {
1348 breakPos = line.find(
' ', lineLength);
1351 if (breakPos == StringRef::npos)
1352 breakPos = line.size();
1359 setPendingNewline();
1360 breakPos = line.find_first_not_of(
' ', breakPos);
1362 if (breakPos == StringRef::npos)
1365 line = line.drop_front(breakPos);
1375 bool addPrefixUnderScore =
true;
1378 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1382 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1384 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1386 result = StringAttr::get(expr.getContext(), name);
1388 }
else if (
auto *op = expr.getDefiningOp()) {
1390 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1392 result = StringAttr::get(expr.getContext(), name);
1394 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1400 addPrefixUnderScore =
false;
1402 TypeSwitch<Operation *>(op)
1405 .Case([&result](VerbatimExprOp verbatim) {
1406 verbatim.getAsmResultNames([&](Value, StringRef name) {
1407 result = StringAttr::get(verbatim.getContext(), name);
1410 .Case([&result](VerbatimExprSEOp verbatim) {
1411 verbatim.getAsmResultNames([&](Value, StringRef name) {
1412 result = StringAttr::get(verbatim.getContext(), name);
1418 if (
auto operandName =
1421 cast<IntegerType>(extract.getType()).getWidth();
1423 result = StringAttr::get(extract.getContext(),
1424 operandName.strref() +
"_" +
1425 Twine(extract.getLowBit()));
1427 result = StringAttr::get(
1428 extract.getContext(),
1429 operandName.strref() +
"_" +
1430 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1431 Twine(extract.getLowBit()));
1439 if (!result || result.strref().empty())
1443 if (addPrefixUnderScore && result.strref().front() !=
'_')
1444 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1456class ModuleEmitter :
public EmitterBase {
1458 explicit ModuleEmitter(VerilogEmitterState &state)
1459 : EmitterBase(state), currentModuleOp(nullptr),
1463 emitPendingNewlineIfNeeded();
1467 void emitParameters(Operation *module, ArrayAttr params);
1468 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1469 bool emitAsTwoStateType =
false);
1472 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1473 void emitFunc(FuncOp);
1476 void emitStatement(Operation *op);
1477 void emitBind(BindOp op);
1478 void emitBindInterface(BindInterfaceOp op);
1480 void emitSVAttributes(Operation *op);
1483 StringRef getVerilogStructFieldName(StringAttr field) {
1484 return fieldNameResolver.getRenamedFieldName(field).getValue();
1491 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1503 bool printPackedType(Type type, raw_ostream &os, Location loc,
1504 Type optionalAliasType = {},
bool implicitIntType =
true,
1505 bool singleBitDefaultType =
true,
1506 bool emitAsTwoStateType =
false);
1510 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1518 function_ref<InFlightDiagnostic()> emitError);
1521 VerilogPrecedence parenthesizeIfLooserThan,
1522 function_ref<InFlightDiagnostic()> emitError);
1528 Operation *currentModuleOp;
1534 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1540 SmallPtrSet<Operation *, 16> assignsInlined;
1549 const ModuleEmitter &emitter) {
1550 if (isa<RegOp>(op)) {
1555 cast<InOutType>(op->getResult(0).getType()).getElementType();
1562 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1563 while (isa<ArrayType>(innerType.getElementType()))
1564 innerType = cast<ArrayType>(innerType.getElementType());
1565 if (isa<StructType>(innerType.getElementType()) ||
1566 isa<TypeAliasType>(innerType.getElementType()))
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();
1585 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
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<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1851 os <<
"<<unexpected unpacked array>>";
1852 emitter.emitError(loc,
"Unexpected unpacked array in packed type ")
1856 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1857 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1859 emitter.emitError(loc,
"unresolvable type reference");
1862 if (typedecl.getType() != typeRef.getInnerType()) {
1863 emitter.emitError(loc,
"declared type did not match aliased type");
1867 os << typedecl.getPreferredName();
1868 emitDims(dims, os, typedecl->getLoc(), emitter);
1871 .Default([&](Type type) {
1872 os <<
"<<invalid type '" << type <<
"'>>";
1873 emitter.emitError(loc,
"value has an unsupported verilog type ")
1890bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1891 Type optionalAliasType,
1892 bool implicitIntType,
1893 bool singleBitDefaultType,
1894 bool emitAsTwoStateType) {
1895 SmallVector<Attribute, 8> packedDimensions;
1897 singleBitDefaultType, *
this, optionalAliasType,
1898 emitAsTwoStateType);
1904void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1905 TypeSwitch<Type, void>(type)
1907 printUnpackedTypePostfix(inoutType.getElementType(), os);
1909 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1910 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1911 : state.designOp->getLoc();
1912 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1914 printUnpackedTypePostfix(arrayType.getElementType(), os);
1916 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1918 printUnpackedTypePostfix(arrayType.getElementType(), os);
1920 .Case<InterfaceType>([&](
auto) {
1934ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1935 function_ref<InFlightDiagnostic()> emitError) {
1936 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1944ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1945 VerilogPrecedence parenthesizeIfLooserThan,
1946 function_ref<InFlightDiagnostic()> emitError) {
1947 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1948 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1949 APInt value = intAttr.getValue();
1953 if (intTy.getWidth() > 32) {
1955 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1959 if (intTy.isSigned())
1960 os << intTy.getWidth() <<
"'sd";
1962 os << intTy.getWidth() <<
"'d";
1964 value.print(os, intTy.isSigned());
1965 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1967 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1969 os.write_escaped(strAttr.getValue());
1971 return {Symbol, IsUnsigned};
1973 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1975 os << fpAttr.getValueAsDouble();
1976 return {Symbol, IsUnsigned};
1978 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1979 os << verbatimParam.getValue().getValue();
1980 return {Symbol, IsUnsigned};
1982 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1984 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1985 parameterRef.getName());
1988 return {Symbol, IsUnsigned};
1992 auto expr = dyn_cast<ParamExprAttr>(value);
1994 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1995 emitError() <<
" = " << value;
1996 return {LowestPrecedence, IsUnsigned};
1999 StringRef operatorStr;
2000 StringRef openStr, closeStr;
2001 VerilogPrecedence subprecedence = LowestPrecedence;
2002 VerilogPrecedence prec;
2003 std::optional<SubExprSignResult> operandSign;
2004 bool isUnary =
false;
2005 bool hasOpenClose =
false;
2007 switch (expr.getOpcode()) {
2009 operatorStr =
" + ";
2010 subprecedence = Addition;
2013 operatorStr =
" * ";
2014 subprecedence = Multiply;
2017 operatorStr =
" & ";
2018 subprecedence = And;
2021 operatorStr =
" | ";
2025 operatorStr =
" ^ ";
2026 subprecedence = Xor;
2029 operatorStr =
" << ";
2030 subprecedence = Shift;
2034 operatorStr =
" >> ";
2035 subprecedence = Shift;
2039 operatorStr =
" >>> ";
2040 subprecedence = Shift;
2041 operandSign = IsSigned;
2044 operatorStr =
" / ";
2045 subprecedence = Multiply;
2046 operandSign = IsUnsigned;
2049 operatorStr =
" / ";
2050 subprecedence = Multiply;
2051 operandSign = IsSigned;
2054 operatorStr =
" % ";
2055 subprecedence = Multiply;
2056 operandSign = IsUnsigned;
2059 operatorStr =
" % ";
2060 subprecedence = Multiply;
2061 operandSign = IsSigned;
2064 openStr =
"$clog2(";
2066 operandSign = IsUnsigned;
2067 hasOpenClose =
true;
2070 case PEO::StrConcat:
2073 hasOpenClose =
true;
2076 subprecedence = LowestPrecedence;
2081 prec = subprecedence;
2084 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2086 assert(isUnary || hasOpenClose ||
2087 !llvm::hasSingleElement(expr.getOperands()));
2094 auto emitOperand = [&](Attribute operand) ->
bool {
2096 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2097 if (operandSign.has_value())
2098 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2101 if (operandSign.has_value()) {
2103 signedness = *operandSign;
2105 return signedness == IsSigned;
2109 if (prec > parenthesizeIfLooserThan)
2118 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2119 for (
auto op : expr.getOperands().drop_front()) {
2122 if (expr.getOpcode() == PEO::Add) {
2123 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2124 const APInt &value = integer.getValue();
2125 if (value.isNegative() && !value.isMinSignedValue()) {
2127 allOperandsSigned &=
2128 emitOperand(IntegerAttr::get(op.getType(), -value));
2135 allOperandsSigned &= emitOperand(op);
2139 if (prec > parenthesizeIfLooserThan) {
2143 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2158class ExprEmitter :
public EmitterBase,
2160 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2165 ExprEmitter(ModuleEmitter &emitter,
2166 SmallPtrSetImpl<Operation *> &emittedExprs)
2167 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2169 ExprEmitter(ModuleEmitter &emitter,
2170 SmallPtrSetImpl<Operation *> &emittedExprs,
2172 : EmitterBase(emitter.state), emitter(emitter),
2173 emittedExprs(emittedExprs), buffer(tokens),
2174 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2175 assert(state.pp.getListener() == &state.saver);
2182 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2183 bool isAssignmentLikeContext) {
2184 assert(localTokens.empty());
2186 ps.scopedBox(PP::ibox0, [&]() {
2189 emitSubExpr(exp, parenthesizeIfLooserThan,
2191 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2193 isAssignmentLikeContext);
2198 if (&buffer.tokens == &localTokens)
2199 buffer.flush(state.pp);
2204 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2205 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2207 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2215 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2216 SubExprSignRequirement signReq = NoRequirement,
2217 bool isSelfDeterminedUnsignedValue =
false,
2218 bool isAssignmentLikeContext =
false);
2222 void emitSVAttributes(Operation *op);
2224 SubExprInfo visitUnhandledExpr(Operation *op);
2225 SubExprInfo visitInvalidComb(Operation *op) {
2228 SubExprInfo visitUnhandledComb(Operation *op) {
2229 return visitUnhandledExpr(op);
2232 return dispatchSVVisitor(op);
2235 return visitUnhandledExpr(op);
2237 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2240 enum EmitBinaryFlags {
2241 EB_RequireSignedOperands = RequireSigned,
2242 EB_RequireUnsignedOperands = RequireUnsigned,
2243 EB_OperandSignRequirementMask = 0x3,
2248 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2252 EB_ForceResultSigned = 0x8,
2257 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2258 const char *syntax,
unsigned emitBinaryFlags = 0);
2260 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2261 bool resultAlwaysUnsigned =
false);
2264 void emitSubExprIBox2(
2265 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2266 ps.scopedBox(PP::ibox2,
2267 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2272 template <
typename Container,
typename EachFn>
2273 void interleaveComma(
const Container &c, EachFn eachFn) {
2274 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2279 void interleaveComma(ValueRange ops) {
2280 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2297 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2299 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2300 CloseFunc closeFn) {
2302 ps.scopedBox(PP::cbox0, [&]() {
2303 interleaveComma(c, eachFn);
2309 template <
typename OpenFunc,
typename CloseFunc>
2310 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2311 return emitBracedList(
2312 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2316 void emitBracedList(ValueRange ops) {
2317 return emitBracedList(
2318 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2322 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2325 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2326 bool printAsPattern, Operation *op);
2328 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2329 ArrayAttr fieldValues,
bool printAsPattern,
2332 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2334 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2335 SubExprInfo visitSV(GetModportOp op);
2336 SubExprInfo visitSV(SystemFunctionOp op);
2337 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2338 SubExprInfo visitSV(XMROp op);
2339 SubExprInfo visitSV(SFormatFOp op);
2340 SubExprInfo visitSV(XMRRefOp op);
2341 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2342 SubExprInfo visitSV(VerbatimExprOp op) {
2343 return visitVerbatimExprOp(op, op.getSymbols());
2345 SubExprInfo visitSV(VerbatimExprSEOp op) {
2346 return visitVerbatimExprOp(op, op.getSymbols());
2348 SubExprInfo visitSV(MacroRefExprOp op);
2349 SubExprInfo visitSV(MacroRefExprSEOp op);
2350 template <
typename MacroTy>
2351 SubExprInfo emitMacroCall(MacroTy op);
2353 SubExprInfo visitSV(ConstantXOp op);
2354 SubExprInfo visitSV(ConstantZOp op);
2355 SubExprInfo visitSV(ConstantStrOp op);
2357 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2358 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2360 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2365 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2366 emitSVAttributes(op);
2369 SubExprInfo visitSV(ArrayIndexInOutOp op);
2370 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2371 SubExprInfo visitSV(IndexedPartSelectOp op);
2372 SubExprInfo visitSV(StructFieldInOutOp op);
2375 SubExprInfo visitSV(SampledOp op);
2378 SubExprInfo visitSV(TimeOp op);
2379 SubExprInfo visitSV(STimeOp op);
2382 using TypeOpVisitor::visitTypeOp;
2384 SubExprInfo visitTypeOp(AggregateConstantOp op);
2386 SubExprInfo visitTypeOp(ParamValueOp op);
2393 SubExprInfo visitTypeOp(StructInjectOp op);
2394 SubExprInfo visitTypeOp(UnionCreateOp op);
2395 SubExprInfo visitTypeOp(UnionExtractOp op);
2396 SubExprInfo visitTypeOp(EnumCmpOp op);
2397 SubExprInfo visitTypeOp(EnumConstantOp op);
2400 using CombinationalVisitor::visitComb;
2401 SubExprInfo visitComb(
MuxOp op);
2402 SubExprInfo visitComb(ReverseOp op);
2403 SubExprInfo visitComb(
AddOp op) {
2404 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2405 return emitBinary(op, Addition,
"+");
2407 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2408 SubExprInfo visitComb(
MulOp op) {
2409 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2410 return emitBinary(op, Multiply,
"*");
2412 SubExprInfo visitComb(
DivUOp op) {
2413 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2415 SubExprInfo visitComb(
DivSOp op) {
2416 return emitBinary(op, Multiply,
"/",
2417 EB_RequireSignedOperands | EB_ForceResultSigned);
2419 SubExprInfo visitComb(
ModUOp op) {
2420 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2422 SubExprInfo visitComb(
ModSOp op) {
2423 return emitBinary(op, Multiply,
"%",
2424 EB_RequireSignedOperands | EB_ForceResultSigned);
2426 SubExprInfo visitComb(
ShlOp op) {
2427 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2429 SubExprInfo visitComb(
ShrUOp op) {
2431 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2433 SubExprInfo visitComb(
ShrSOp op) {
2436 return emitBinary(op, Shift,
">>>",
2437 EB_RequireSignedOperands | EB_ForceResultSigned |
2438 EB_RHS_UnsignedWithSelfDeterminedWidth);
2440 SubExprInfo visitComb(
AndOp op) {
2441 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2442 return emitBinary(op, And,
"&");
2444 SubExprInfo visitComb(
OrOp op) {
2445 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2446 return emitBinary(op, Or,
"|");
2448 SubExprInfo visitComb(
XorOp op) {
2449 if (op.isBinaryNot())
2450 return emitUnary(op,
"~");
2451 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2452 return emitBinary(op, Xor,
"^");
2457 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2459 SubExprInfo visitComb(ReplicateOp op);
2460 SubExprInfo visitComb(
ConcatOp op);
2462 SubExprInfo visitComb(ICmpOp op);
2464 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2465 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2466 "not printed within an assignment-like context");
2467 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2468 "supposed to spill such expressions";
2472 SubExprInfo printStructCreate(
2473 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2475 bool printAsPattern, Operation *op);
2478 ModuleEmitter &emitter;
2485 SubExprSignRequirement signPreference = NoRequirement;
2489 SmallPtrSetImpl<Operation *> &emittedExprs;
2492 SmallVector<Token> localTokens;
2506 bool isAssignmentLikeContext =
false;
2510SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2512 unsigned emitBinaryFlags) {
2514 emitError(op,
"SV attributes emission is unimplemented for the op");
2525 if (emitBinaryFlags & EB_ForceResultSigned)
2526 ps <<
"$signed(" << PP::ibox0;
2527 auto operandSignReq =
2528 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2529 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2531 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2533 ps << lhsSpace << syntax << PP::nbsp;
2540 auto rhsPrec = prec;
2541 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2542 rhsPrec = VerilogPrecedence(prec - 1);
2547 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2548 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2549 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2550 operandSignReq = NoRequirement;
2553 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2554 rhsIsUnsignedValueWithSelfDeterminedWidth);
2558 SubExprSignResult signedness = IsUnsigned;
2559 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2560 signedness = IsSigned;
2562 if (emitBinaryFlags & EB_ForceResultSigned) {
2563 ps << PP::end <<
")";
2564 signedness = IsSigned;
2568 return {prec, signedness};
2571SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2572 bool resultAlwaysUnsigned) {
2574 emitError(op,
"SV attributes emission is unimplemented for the op");
2577 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2581 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2582 resultAlwaysUnsigned ? IsUnsigned : signedness};
2587void ExprEmitter::emitSVAttributes(Operation *op) {
2601 auto concat = value.getDefiningOp<
ConcatOp>();
2602 if (!concat || concat.getNumOperands() != 2)
2605 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2606 if (constant && constant.getValue().isZero())
2607 return concat.getOperand(1);
2617SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2618 VerilogPrecedence parenthesizeIfLooserThan,
2619 SubExprSignRequirement signRequirement,
2620 bool isSelfDeterminedUnsignedValue,
2621 bool isAssignmentLikeContext) {
2623 if (
auto result = dyn_cast<OpResult>(exp))
2624 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2625 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2626 parenthesizeIfLooserThan, signRequirement,
2627 isSelfDeterminedUnsignedValue,
2628 isAssignmentLikeContext);
2632 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2637 auto *op = exp.getDefiningOp();
2641 if (!shouldEmitInlineExpr) {
2644 if (signRequirement == RequireSigned) {
2646 return {Symbol, IsSigned};
2650 return {Symbol, IsUnsigned};
2653 unsigned subExprStartIndex = buffer.tokens.size();
2655 ps.addCallback({op,
true});
2656 llvm::scope_exit done([&]() {
2658 ps.addCallback({op, false});
2664 signPreference = signRequirement;
2666 bool bitCastAdded =
false;
2667 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2669 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2670 ps.addAsString(inType.getWidth());
2671 ps <<
"'(" << PP::ibox0;
2672 bitCastAdded =
true;
2676 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2677 isAssignmentLikeContext);
2678 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2684 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2686 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2688 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2689 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2692 expInfo.signedness = IsSigned;
2693 expInfo.precedence = Selection;
2694 }
else if (signRequirement == RequireUnsigned &&
2695 expInfo.signedness == IsSigned) {
2698 expInfo.signedness = IsUnsigned;
2699 expInfo.precedence = Selection;
2700 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2707 expInfo.precedence = Selection;
2714 emittedExprs.insert(exp.getDefiningOp());
2718SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2719 auto openFn = [&]() {
2721 ps.addAsString(op.getMultiple());
2724 auto closeFn = [&]() { ps <<
"}}"; };
2728 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2729 if (op.getOperand().hasOneUse()) {
2730 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2731 return {Symbol, IsUnsigned};
2734 emitBracedList(op.getOperand(), openFn, closeFn);
2735 return {Symbol, IsUnsigned};
2738SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2739 emitBracedList(op.getOperands());
2740 return {Symbol, IsUnsigned};
2743SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2747 Type toType = op.getType();
2749 toType, op.getInput().getType(), op.getLoc(),
2750 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2752 ps.invokeWithStringOS(
2753 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2756 return emitSubExpr(op.getInput(), LowestPrecedence);
2759SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2760 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2761 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2762 SubExprSignRequirement signop[] = {
2764 NoRequirement, NoRequirement,
2766 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2768 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2770 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2772 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2773 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2776 if (op.isEqualAllOnes())
2777 return emitUnary(op,
"&",
true);
2780 if (op.isNotEqualZero())
2781 return emitUnary(op,
"|",
true);
2783 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2787 result.signedness = IsUnsigned;
2791SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2793 emitError(op,
"SV attributes emission is unimplemented for the op");
2795 unsigned loBit = op.getLowBit();
2796 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2798 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2799 assert((x.precedence == Symbol ||
2801 "should be handled by isExpressionUnableToInline");
2806 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2810 ps.addAsString(hiBit);
2811 if (hiBit != loBit) {
2813 ps.addAsString(loBit);
2816 return {Unary, IsUnsigned};
2819SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2821 emitError(op,
"SV attributes emission is unimplemented for the op");
2823 auto decl = op.getReferencedDecl(state.symbolCache);
2826 return {Selection, IsUnsigned};
2829SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2831 emitError(op,
"SV attributes emission is unimplemented for the op");
2834 ps.scopedBox(PP::ibox0, [&]() {
2836 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2837 [&]() { ps <<
"," << PP::space; });
2840 return {Symbol, IsUnsigned};
2843SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2845 emitError(op,
"SV attributes emission is unimplemented for the op");
2847 auto decl = op.getReferencedDecl(state.symbolCache);
2851 return {Selection, IsUnsigned};
2854SubExprInfo ExprEmitter::visitSV(XMROp op) {
2856 emitError(op,
"SV attributes emission is unimplemented for the op");
2858 if (op.getIsRooted())
2860 for (
auto s : op.getPath())
2861 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2863 return {Selection, IsUnsigned};
2868SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2870 emitError(op,
"SV attributes emission is unimplemented for the op");
2873 auto globalRef = op.getReferencedPath(&state.symbolCache);
2874 auto namepath = globalRef.getNamepathAttr().getValue();
2875 auto *
module = state.symbolCache.getDefinition(
2876 cast<InnerRefAttr>(namepath.front()).getModule());
2878 for (
auto sym : namepath) {
2880 auto innerRef = cast<InnerRefAttr>(sym);
2881 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2882 innerRef.getName());
2883 if (ref.hasPort()) {
2889 auto leaf = op.getVerbatimSuffixAttr();
2890 if (leaf && leaf.size())
2892 return {Selection, IsUnsigned};
2895SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2897 emitError(op,
"SV attributes emission is unimplemented for the op");
2899 emitTextWithSubstitutions(
2900 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2901 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2903 return {Unary, IsUnsigned};
2906template <
typename MacroTy>
2907SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2909 emitError(op,
"SV attributes emission is unimplemented for the op");
2912 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2913 assert(macroOp &&
"Invalid IR");
2915 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2917 if (!op.getInputs().empty()) {
2919 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2920 emitExpression(val, LowestPrecedence, false);
2924 return {LowestPrecedence, IsUnsigned};
2927SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2928 return emitMacroCall(op);
2931SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2932 return emitMacroCall(op);
2935SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2937 emitError(op,
"SV attributes emission is unimplemented for the op");
2939 ps.addAsString(op.getWidth());
2941 return {Unary, IsUnsigned};
2944SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2946 emitError(op,
"SV attributes emission is unimplemented for the op");
2948 ps.writeQuotedEscaped(op.getStr());
2949 return {Symbol, IsUnsigned};
2952SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2954 emitError(op,
"SV attributes emission is unimplemented for the op");
2956 ps.addAsString(op.getWidth());
2958 return {Unary, IsUnsigned};
2961SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2962 bool isNegated =
false;
2965 if (signPreference == RequireSigned && value.isNegative() &&
2966 !value.isMinSignedValue()) {
2971 ps.addAsString(type.getWidth());
2975 if (signPreference == RequireSigned)
2981 SmallString<32> valueStr;
2983 (-value).toStringUnsigned(valueStr, 16);
2985 value.toStringUnsigned(valueStr, 16);
2988 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2991SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2993 emitError(op,
"SV attributes emission is unimplemented for the op");
2995 auto value = op.getValue();
2999 if (value.getBitWidth() == 0) {
3000 emitOpError(op,
"will not emit zero width constants in the general case");
3001 ps <<
"<<unsupported zero width constant: "
3002 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3003 return {Unary, IsUnsigned};
3006 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3009void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3010 bool printAsPattern, Operation *op) {
3011 if (printAsPattern && !isAssignmentLikeContext)
3012 emitAssignmentPatternContextError(op);
3013 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3016 elementValues, [&]() { ps << openDelim; },
3017 [&](Attribute elementValue) {
3018 printConstantAggregate(elementValue,
elementType, op);
3020 [&]() { ps <<
"}"; });
3023void ExprEmitter::printConstantStruct(
3024 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3025 bool printAsPattern, Operation *op) {
3026 if (printAsPattern && !isAssignmentLikeContext)
3027 emitAssignmentPatternContextError(op);
3034 auto fieldRange = llvm::make_filter_range(
3035 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3040 if (printAsPattern) {
3042 fieldRange, [&]() { ps <<
"'{"; },
3043 [&](
const auto &fieldAndValue) {
3044 ps.scopedBox(PP::ibox2, [&]() {
3045 const auto &[field, value] = fieldAndValue;
3046 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3047 <<
":" << PP::space;
3048 printConstantAggregate(value, field.type, op);
3051 [&]() { ps <<
"}"; });
3054 fieldRange, [&]() { ps <<
"{"; },
3055 [&](
const auto &fieldAndValue) {
3056 ps.scopedBox(PP::ibox2, [&]() {
3057 const auto &[field, value] = fieldAndValue;
3058 printConstantAggregate(value, field.type, op);
3061 [&]() { ps <<
"}"; });
3065void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3068 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3069 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3070 isAssignmentLikeContext, op);
3073 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3074 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3078 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3079 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3080 isAssignmentLikeContext, op);
3082 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3083 auto value = cast<IntegerAttr>(attr).getValue();
3084 printConstantScalar(value, intType);
3088 emitOpError(op,
"contains constant of type ")
3089 << type <<
" which cannot be emitted as Verilog";
3092SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3094 emitError(op,
"SV attributes emission is unimplemented for the op");
3098 "zero-bit types not allowed at this point");
3100 printConstantAggregate(op.getFields(), op.getType(), op);
3101 return {Symbol, IsUnsigned};
3104SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3106 emitError(op,
"SV attributes emission is unimplemented for the op");
3108 return ps.invokeWithStringOS([&](
auto &os) {
3109 return emitter.printParamValue(op.getValue(), os, [&]() {
3110 return op->emitOpError(
"invalid parameter use");
3119 emitError(op,
"SV attributes emission is unimplemented for the op");
3121 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3123 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3125 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3127 ps.addAsString(dstWidth);
3129 return {Selection, arrayPrec.signedness};
3132SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3133 emitSubExpr(op.getInput(), Selection);
3138 emitSubExpr(op.getIndex(), LowestPrecedence);
3140 emitSVAttributes(op);
3141 return {Selection, IsUnsigned};
3147 emitError(op,
"SV attributes emission is unimplemented for the op");
3149 if (op.isUniform()) {
3151 ps.addAsString(op.getInputs().size());
3153 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3157 op.getInputs(), [&]() { ps <<
"{"; },
3160 emitSubExprIBox2(v);
3163 [&]() { ps <<
"}"; });
3165 return {Unary, IsUnsigned};
3168SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3170 emitError(op,
"SV attributes emission is unimplemented for the op");
3173 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3174 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3175 return {Unary, IsUnsigned};
3180 emitError(op,
"SV attributes emission is unimplemented for the op");
3182 emitBracedList(op.getOperands());
3183 return {Unary, IsUnsigned};
3186SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3188 emitError(op,
"SV attributes emission is unimplemented for the op");
3190 auto index = op.getIndex();
3191 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3196 emitSubExpr(index, LowestPrecedence);
3198 return {Selection, arrayPrec.signedness};
3201SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3203 emitError(op,
"SV attributes emission is unimplemented for the op");
3205 auto prec = emitSubExpr(op.getInput(), Selection);
3207 emitSubExpr(op.getBase(), LowestPrecedence);
3208 if (op.getDecrement())
3212 ps.addAsString(op.getWidth());
3214 return {Selection, prec.signedness};
3217SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3219 emitError(op,
"SV attributes emission is unimplemented for the op");
3221 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3223 emitSubExpr(op.getBase(), LowestPrecedence);
3224 if (op.getDecrement())
3228 ps.addAsString(op.getWidth());
3233SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3235 emitError(op,
"SV attributes emission is unimplemented for the op");
3237 auto prec = emitSubExpr(op.getInput(), Selection);
3239 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3240 return {Selection, prec.signedness};
3243SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3245 emitError(op,
"SV attributes emission is unimplemented for the op");
3248 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3253SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3255 emitError(op,
"SV attributes emission is unimplemented for the op");
3258 ps.scopedBox(PP::ibox0, [&]() {
3259 ps.writeQuotedEscaped(op.getFormatString());
3266 for (
auto operand : op.getSubstitutions()) {
3267 ps <<
"," << PP::space;
3268 emitSubExpr(operand, LowestPrecedence);
3272 return {Symbol, IsUnsigned};
3275SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3277 emitError(op,
"SV attributes emission is unimplemented for the op");
3280 return {Symbol, IsUnsigned};
3283SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3285 emitError(op,
"SV attributes emission is unimplemented for the op");
3288 return {Symbol, IsUnsigned};
3291SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3305 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3306 ps.scopedBox(PP::ibox0, [&]() {
3307 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3311 emitSVAttributes(op);
3313 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3314 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3318 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3319 return emitSubExpr(op.getFalseValue(), Conditional);
3322 SubExprSignResult signedness = IsUnsigned;
3323 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3324 signedness = IsSigned;
3326 return {Conditional, signedness};
3330SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3332 emitError(op,
"SV attributes emission is unimplemented for the op");
3335 emitSubExpr(op.getInput(), LowestPrecedence);
3338 return {Symbol, IsUnsigned};
3341SubExprInfo ExprEmitter::printStructCreate(
3342 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3344 bool printAsPattern, Operation *op) {
3345 if (printAsPattern && !isAssignmentLikeContext)
3346 emitAssignmentPatternContextError(op);
3349 auto filteredFields = llvm::make_filter_range(
3350 llvm::enumerate(fieldInfos),
3351 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3353 if (printAsPattern) {
3355 filteredFields, [&]() { ps <<
"'{"; },
3356 [&](
const auto &field) {
3357 ps.scopedBox(PP::ibox2, [&]() {
3359 emitter.getVerilogStructFieldName(field.value().name))
3360 <<
":" << PP::space;
3361 fieldFn(field.value(), field.index());
3364 [&]() { ps <<
"}"; });
3367 filteredFields, [&]() { ps <<
"{"; },
3368 [&](
const auto &field) {
3369 ps.scopedBox(PP::ibox2,
3370 [&]() { fieldFn(field.value(), field.index()); });
3372 [&]() { ps <<
"}"; });
3375 return {Selection, IsUnsigned};
3380 emitError(op,
"SV attributes emission is unimplemented for the op");
3384 bool printAsPattern = isAssignmentLikeContext;
3385 StructType structType = op.getType();
3386 return printStructCreate(
3387 structType.getElements(),
3388 [&](
const auto &field,
auto index) {
3389 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3391 isAssignmentLikeContext);
3393 printAsPattern, op);
3398 emitError(op,
"SV attributes emission is unimplemented for the op");
3400 emitSubExpr(op.getInput(), Selection);
3402 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3403 return {Selection, IsUnsigned};
3406SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3408 emitError(op,
"SV attributes emission is unimplemented for the op");
3412 bool printAsPattern = isAssignmentLikeContext;
3413 StructType structType = op.getType();
3414 return printStructCreate(
3415 structType.getElements(),
3416 [&](
const auto &field,
auto index) {
3417 if (field.name == op.getFieldNameAttr()) {
3418 emitSubExpr(op.getNewValue(), Selection);
3420 emitSubExpr(op.getInput(), Selection);
3422 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3425 printAsPattern, op);
3428SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3429 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3430 return {Selection, IsUnsigned};
3433SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3435 emitError(op,
"SV attributes emission is unimplemented for the op");
3436 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3439 result.signedness = IsUnsigned;
3443SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3445 emitError(op,
"SV attributes emission is unimplemented for the op");
3449 auto unionWidth = hw::getBitWidth(unionType);
3450 auto &element = unionType.getElements()[op.getFieldIndex()];
3451 auto elementWidth = hw::getBitWidth(element.type);
3454 if (!elementWidth) {
3455 ps.addAsString(unionWidth);
3457 return {Unary, IsUnsigned};
3461 if (elementWidth == unionWidth) {
3462 emitSubExpr(op.getInput(), LowestPrecedence);
3463 return {Unary, IsUnsigned};
3468 ps.scopedBox(PP::ibox0, [&]() {
3469 if (
auto prePadding = element.offset) {
3470 ps.addAsString(prePadding);
3471 ps <<
"'h0," << PP::space;
3473 emitSubExpr(op.getInput(), Selection);
3474 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3475 ps <<
"," << PP::space;
3476 ps.addAsString(postPadding);
3482 return {Unary, IsUnsigned};
3485SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3487 emitError(op,
"SV attributes emission is unimplemented for the op");
3488 emitSubExpr(op.getInput(), Selection);
3491 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3492 auto unionWidth = hw::getBitWidth(unionType);
3493 auto &element = unionType.getElements()[op.getFieldIndex()];
3494 auto elementWidth = hw::getBitWidth(element.type);
3495 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3496 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3505 return {Selection, IsUnsigned};
3508SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3509 emitOpError(op,
"cannot emit this expression to Verilog");
3510 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3512 return {Symbol, IsUnsigned};
3528enum class PropertyPrecedence {
3548struct EmittedProperty {
3550 PropertyPrecedence precedence;
3555class PropertyEmitter :
public EmitterBase,
3556 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3560 PropertyEmitter(ModuleEmitter &emitter,
3561 SmallPtrSetImpl<Operation *> &emittedOps)
3562 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3563 PropertyEmitter(ModuleEmitter &emitter,
3564 SmallPtrSetImpl<Operation *> &emittedOps,
3566 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3568 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3569 assert(state.pp.getListener() == &state.saver);
3572 void emitAssertPropertyDisable(
3573 Value property, Value disable,
3574 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3576 void emitAssertPropertyBody(
3577 Value property, Value disable,
3578 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3580 void emitAssertPropertyBody(
3581 Value property, sv::EventControl event, Value clock, Value disable,
3582 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3587 emitNestedProperty(Value property,
3588 PropertyPrecedence parenthesizeIfLooserThan);
3589 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3590 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3592 EmittedProperty visitUnhandledLTL(Operation *op);
3593 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3594 EmittedProperty visitLTL(ltl::AndOp op);
3595 EmittedProperty visitLTL(ltl::OrOp op);
3596 EmittedProperty visitLTL(ltl::IntersectOp op);
3597 EmittedProperty visitLTL(ltl::DelayOp op);
3598 EmittedProperty visitLTL(ltl::ConcatOp op);
3599 EmittedProperty visitLTL(ltl::RepeatOp op);
3600 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3601 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3602 EmittedProperty visitLTL(ltl::NotOp op);
3603 EmittedProperty visitLTL(ltl::ImplicationOp op);
3604 EmittedProperty visitLTL(ltl::UntilOp op);
3605 EmittedProperty visitLTL(ltl::EventuallyOp op);
3606 EmittedProperty visitLTL(ltl::ClockOp op);
3608 void emitLTLConcat(ValueRange inputs);
3611 ModuleEmitter &emitter;
3616 SmallPtrSetImpl<Operation *> &emittedOps;
3619 SmallVector<Token> localTokens;
3632void PropertyEmitter::emitAssertPropertyDisable(
3633 Value property, Value disable,
3634 PropertyPrecedence parenthesizeIfLooserThan) {
3637 ps <<
"disable iff" << PP::nbsp <<
"(";
3639 emitNestedProperty(disable, PropertyPrecedence::Unary);
3645 ps.scopedBox(PP::ibox0,
3646 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3652void PropertyEmitter::emitAssertPropertyBody(
3653 Value property, Value disable,
3654 PropertyPrecedence parenthesizeIfLooserThan) {
3655 assert(localTokens.empty());
3657 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3662 if (&buffer.tokens == &localTokens)
3663 buffer.flush(state.pp);
3666void PropertyEmitter::emitAssertPropertyBody(
3667 Value property, sv::EventControl event, Value clock, Value disable,
3668 PropertyPrecedence parenthesizeIfLooserThan) {
3669 assert(localTokens.empty());
3672 ps.scopedBox(PP::ibox2, [&] {
3673 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3674 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3680 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3685 if (&buffer.tokens == &localTokens)
3686 buffer.flush(state.pp);
3689EmittedProperty PropertyEmitter::emitNestedProperty(
3690 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3700 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3701 ExprEmitter(emitter, emittedOps, buffer.tokens)
3702 .emitExpression(property, LowestPrecedence,
3704 return {PropertyPrecedence::Symbol};
3707 unsigned startIndex = buffer.tokens.size();
3708 auto info = dispatchLTLVisitor(property.getDefiningOp());
3713 if (
info.precedence > parenthesizeIfLooserThan) {
3715 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3716 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3718 ps << PP::end <<
")";
3720 info.precedence = PropertyPrecedence::Symbol;
3724 emittedOps.insert(property.getDefiningOp());
3728EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3729 emitOpError(op,
"emission as Verilog property or sequence not supported");
3730 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3731 return {PropertyPrecedence::Symbol};
3734EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3736 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3737 return {PropertyPrecedence::Symbol};
3740EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3743 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3744 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3745 return {PropertyPrecedence::And};
3748EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3751 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3752 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3753 return {PropertyPrecedence::Or};
3756EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3760 emitNestedProperty(input, PropertyPrecedence::Intersect);
3762 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3763 return {PropertyPrecedence::Intersect};
3766EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3768 if (
auto length = op.getLength()) {
3770 ps.addAsString(op.getDelay());
3773 ps.addAsString(op.getDelay());
3775 ps.addAsString(op.getDelay() + *length);
3779 if (op.getDelay() == 0) {
3781 }
else if (op.getDelay() == 1) {
3785 ps.addAsString(op.getDelay());
3790 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3791 return {PropertyPrecedence::Concat};
3794void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3795 bool addSeparator =
false;
3796 for (
auto input : inputs) {
3799 if (!input.getDefiningOp<ltl::DelayOp>())
3800 ps <<
"##0" << PP::space;
3802 addSeparator =
true;
3803 emitNestedProperty(input, PropertyPrecedence::Concat);
3807EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3808 emitLTLConcat(op.getInputs());
3809 return {PropertyPrecedence::Concat};
3812EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3813 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3814 if (
auto more = op.getMore()) {
3816 ps.addAsString(op.getBase());
3819 ps.addAsString(op.getBase() + *more);
3823 if (op.getBase() == 0) {
3825 }
else if (op.getBase() == 1) {
3829 ps.addAsString(op.getBase());
3833 return {PropertyPrecedence::Repeat};
3836EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3837 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3839 auto more = op.getMore();
3841 ps.addAsString(op.getBase());
3844 ps.addAsString(op.getBase() + more);
3848 return {PropertyPrecedence::Repeat};
3851EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3852 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3854 auto more = op.getMore();
3856 ps.addAsString(op.getBase());
3859 ps.addAsString(op.getBase() + more);
3863 return {PropertyPrecedence::Repeat};
3866EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3867 ps <<
"not" << PP::space;
3868 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3869 return {PropertyPrecedence::Unary};
3875 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3876 if (!concatOp || concatOp.getInputs().size() < 2)
3878 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3879 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3881 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3882 if (!constOp || !constOp.getValue().isOne())
3884 return concatOp.getInputs().drop_back();
3887EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3891 emitLTLConcat(range);
3892 ps << PP::space <<
"|=>" << PP::nbsp;
3894 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3895 ps << PP::space <<
"|->" << PP::nbsp;
3897 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3898 return {PropertyPrecedence::Implication};
3901EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3902 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3903 ps << PP::space <<
"until" << PP::space;
3904 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3905 return {PropertyPrecedence::Until};
3908EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3909 ps <<
"s_eventually" << PP::space;
3910 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3911 return {PropertyPrecedence::Qualifier};
3914EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3916 ps.scopedBox(PP::ibox2, [&] {
3917 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3918 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3922 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3923 return {PropertyPrecedence::Clocking};
3933class NameCollector {
3935 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3939 void collectNames(Block &block);
3941 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3942 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3945 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3946 ModuleEmitter &moduleEmitter;
3951 static constexpr size_t maxTypeWidthBound = 32;
3956void NameCollector::collectNames(Block &block) {
3959 for (
auto &op : block) {
3963 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
3966 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3970 for (
auto result : op.getResults()) {
3972 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3973 SmallString<16> typeString;
3977 llvm::raw_svector_ostream stringStream(typeString);
3979 stringStream, op.getLoc());
3981 if (typeString.size() <= maxTypeWidthBound)
3982 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3989 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3990 for (
auto ®ion : op.getRegions()) {
3991 if (!region.empty())
3992 collectNames(region.front());
4006class StmtEmitter :
public EmitterBase,
4014 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4016 void emitStatement(Operation *op);
4017 void emitStatementBlock(Block &body);
4020 LogicalResult emitDeclaration(Operation *op);
4023 void collectNamesAndCalculateDeclarationWidths(Block &block);
4026 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4027 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4028 bool isAssignmentLikeContext =
false);
4029 void emitSVAttributes(Operation *op);
4032 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4035 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4039 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4040 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4041 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4042 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4043 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4044 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4046 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4047 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4048 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4049 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4050 template <
typename Op>
4053 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4054 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4055 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4057 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4058 LogicalResult visitSV(
AssignOp op);
4059 LogicalResult visitSV(BPAssignOp op);
4060 LogicalResult visitSV(PAssignOp op);
4061 LogicalResult visitSV(ForceOp op);
4062 LogicalResult visitSV(ReleaseOp op);
4063 LogicalResult visitSV(AliasOp op);
4064 LogicalResult visitSV(InterfaceInstanceOp op);
4065 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4066 LogicalResult visitStmt(OutputOp op);
4068 LogicalResult visitStmt(InstanceOp op);
4069 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4070 ArrayRef<Value> instPortValues);
4075 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4076 LogicalResult visitSV(OrderedOutputOp op);
4077 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4078 LogicalResult visitSV(IfDefProceduralOp op) {
4079 return emitIfDef(op, op.getCond());
4081 LogicalResult visitSV(IfOp op);
4082 LogicalResult visitSV(AlwaysOp op);
4083 LogicalResult visitSV(AlwaysCombOp op);
4084 LogicalResult visitSV(AlwaysFFOp op);
4085 LogicalResult visitSV(InitialOp op);
4086 LogicalResult visitSV(CaseOp op);
4087 LogicalResult visitSV(FWriteOp op);
4088 LogicalResult visitSV(FFlushOp op);
4089 LogicalResult visitSV(VerbatimOp op);
4090 LogicalResult visitSV(MacroRefOp op);
4092 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4093 std::optional<unsigned> verbosity);
4094 LogicalResult visitSV(StopOp op);
4095 LogicalResult visitSV(FinishOp op);
4096 LogicalResult visitSV(ExitOp op);
4098 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4099 std::optional<unsigned> verbosity,
4101 ValueRange operands);
4104 template <
typename OpTy>
4105 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4106 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4107 op.getMessageAttr(), op.getSubstitutions());
4111 template <
typename OpTy>
4112 LogicalResult emitFatalMessageOp(OpTy op) {
4113 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4114 op.getMessageAttr(), op.getSubstitutions());
4117 LogicalResult visitSV(FatalProceduralOp op);
4118 LogicalResult visitSV(FatalOp op);
4119 LogicalResult visitSV(ErrorProceduralOp op);
4120 LogicalResult visitSV(WarningProceduralOp op);
4121 LogicalResult visitSV(InfoProceduralOp op);
4122 LogicalResult visitSV(ErrorOp op);
4123 LogicalResult visitSV(WarningOp op);
4124 LogicalResult visitSV(InfoOp op);
4126 LogicalResult visitSV(ReadMemOp op);
4128 LogicalResult visitSV(GenerateOp op);
4129 LogicalResult visitSV(GenerateCaseOp op);
4131 LogicalResult visitSV(
ForOp op);
4133 void emitAssertionLabel(Operation *op);
4134 void emitAssertionMessage(StringAttr message, ValueRange args,
4135 SmallPtrSetImpl<Operation *> &ops,
4137 template <
typename Op>
4138 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4139 LogicalResult visitSV(AssertOp op);
4140 LogicalResult visitSV(AssumeOp op);
4141 LogicalResult visitSV(CoverOp op);
4142 template <
typename Op>
4143 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4144 LogicalResult visitSV(AssertConcurrentOp op);
4145 LogicalResult visitSV(AssumeConcurrentOp op);
4146 LogicalResult visitSV(CoverConcurrentOp op);
4147 template <
typename Op>
4148 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4149 LogicalResult visitSV(AssertPropertyOp op);
4150 LogicalResult visitSV(AssumePropertyOp op);
4151 LogicalResult visitSV(CoverPropertyOp op);
4153 LogicalResult visitSV(BindOp op);
4154 LogicalResult visitSV(InterfaceOp op);
4156 LogicalResult visitSV(InterfaceSignalOp op);
4157 LogicalResult visitSV(InterfaceModportOp op);
4158 LogicalResult visitSV(AssignInterfaceSignalOp op);
4159 LogicalResult visitSV(MacroErrorOp op);
4160 LogicalResult visitSV(MacroDefOp op);
4162 void emitBlockAsStatement(Block *block,
4163 const SmallPtrSetImpl<Operation *> &locationOps,
4164 StringRef multiLineComment = StringRef());
4166 LogicalResult visitSV(FuncDPIImportOp op);
4167 template <
typename CallOp>
4168 LogicalResult emitFunctionCall(CallOp callOp);
4169 LogicalResult visitSV(FuncCallProceduralOp op);
4170 LogicalResult visitSV(FuncCallOp op);
4171 LogicalResult visitSV(ReturnOp op);
4172 LogicalResult visitSV(IncludeOp op);
4175 ModuleEmitter &emitter;
4180 size_t maxDeclNameWidth = 0;
4181 size_t maxTypeWidth = 0;
4192void StmtEmitter::emitExpression(Value exp,
4193 SmallPtrSetImpl<Operation *> &emittedExprs,
4194 VerilogPrecedence parenthesizeIfLooserThan,
4195 bool isAssignmentLikeContext) {
4196 ExprEmitter(emitter, emittedExprs)
4197 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4202void StmtEmitter::emitSVAttributes(Operation *op) {
4210 setPendingNewline();
4213void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4214 llvm::function_ref<
void()> emitRHS,
4216 std::optional<PPExtString> wordBeforeLHS) {
4218 ps.scopedBox(PP::ibox2, [&]() {
4219 if (wordBeforeLHS) {
4220 ps << *wordBeforeLHS << PP::space;
4224 ps << PP::space << syntax << PP::space;
4226 ps.scopedBox(PP::ibox0, [&]() {
4233template <
typename Op>
4235StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4236 std::optional<PPExtString> wordBeforeLHS) {
4237 SmallPtrSet<Operation *, 8> ops;
4241 ps.addCallback({op,
true});
4242 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4244 emitExpression(op.getSrc(), ops, LowestPrecedence,
4249 ps.addCallback({op,
false});
4250 emitLocationInfoAndNewLine(ops);
4254LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4257 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4260 if (emitter.assignsInlined.count(op))
4264 emitSVAttributes(op);
4269LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4270 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4274 if (emitter.assignsInlined.count(op))
4278 emitSVAttributes(op);
4283LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4285 emitSVAttributes(op);
4290LogicalResult StmtEmitter::visitSV(ForceOp op) {
4292 emitError(op,
"SV attributes emission is unimplemented for the op");
4297LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4299 emitError(op,
"SV attributes emission is unimplemented for the op");
4302 SmallPtrSet<Operation *, 8> ops;
4304 ps.addCallback({op,
true});
4305 ps.scopedBox(PP::ibox2, [&]() {
4306 ps <<
"release" << PP::space;
4307 emitExpression(op.getDest(), ops);
4310 ps.addCallback({op,
false});
4311 emitLocationInfoAndNewLine(ops);
4315LogicalResult StmtEmitter::visitSV(AliasOp op) {
4317 emitError(op,
"SV attributes emission is unimplemented for the op");
4320 SmallPtrSet<Operation *, 8> ops;
4322 ps.addCallback({op,
true});
4323 ps.scopedBox(PP::ibox2, [&]() {
4324 ps <<
"alias" << PP::space;
4325 ps.scopedBox(PP::cbox0, [&]() {
4327 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4328 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4332 ps.addCallback({op,
false});
4333 emitLocationInfoAndNewLine(ops);
4337LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4338 auto doNotPrint = op.getDoNotPrint();
4339 if (doNotPrint && !state.options.emitBindComments)
4343 emitError(op,
"SV attributes emission is unimplemented for the op");
4346 StringRef prefix =
"";
4347 ps.addCallback({op,
true});
4350 ps <<
"// This interface is elsewhere emitted as a bind statement."
4354 SmallPtrSet<Operation *, 8> ops;
4357 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4358 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4359 "point to an interface");
4362 if (!prefix.empty())
4368 ps.addCallback({op,
false});
4369 emitLocationInfoAndNewLine(ops);
4377LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4379 SmallPtrSet<Operation *, 8> ops;
4380 size_t operandIndex = 0;
4381 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4382 for (
PortInfo port : ports.getOutputs()) {
4383 auto operand = op->getOperand(operandIndex);
4387 if (operand.hasOneUse() && operand.getDefiningOp() &&
4388 isa<InstanceOp>(operand.getDefiningOp())) {
4397 ps.addCallback({op,
true});
4399 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4401 ps <<
"// Zero width: ";
4404 ps <<
"assign" << PP::space;
4406 ps << PP::space <<
"=" << PP::space;
4407 ps.scopedBox(PP::ibox0, [&]() {
4411 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4412 ps <<
"/*Zero width*/";
4414 emitExpression(operand, ops, LowestPrecedence,
4419 ps.addCallback({op,
false});
4420 emitLocationInfoAndNewLine(ops);
4427LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4428 auto parent = op->getParentOfType<PortList>();
4430 return emitOutputLikeOp(op, ports);
4433LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4435 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4436 ps <<
"`ifndef " << typescopeDef << PP::newline;
4437 ps <<
"`define " << typescopeDef;
4438 setPendingNewline();
4439 emitStatementBlock(*op.getBodyBlock());
4441 ps <<
"`endif // " << typescopeDef;
4442 setPendingNewline();
4446LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4448 emitError(op,
"SV attributes emission is unimplemented for the op");
4453 ps << PP::neverbox <<
"// ";
4455 SmallPtrSet<Operation *, 8> ops;
4457 ps.scopedBox(PP::ibox2, [&]() {
4458 ps <<
"typedef" << PP::space;
4459 ps.invokeWithStringOS([&](
auto &os) {
4461 op.getAliasType(),
false);
4463 ps << PP::space <<
PPExtString(op.getPreferredName());
4464 ps.invokeWithStringOS(
4465 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4470 emitLocationInfoAndNewLine(ops);
4474template <
typename CallOpTy>
4475LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4479 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4481 SmallPtrSet<Operation *, 8> ops;
4485 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4486 if (explicitReturn) {
4487 assert(explicitReturn.hasOneUse());
4488 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4489 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4490 emitExpression(bpassignOp.getDest(), ops);
4492 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4493 ps <<
"assign" << PP::nbsp;
4494 emitExpression(assignOp.getDest(), ops);
4496 ps << PP::nbsp <<
"=" << PP::nbsp;
4499 auto arguments = callee.getPortList(
true);
4503 bool needsComma =
false;
4504 auto printArg = [&](Value value) {
4506 ps <<
"," << PP::space;
4507 emitExpression(value, ops);
4511 ps.scopedBox(PP::ibox0, [&] {
4512 unsigned inputIndex = 0, outputIndex = 0;
4513 for (
auto arg : arguments) {
4516 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4518 printArg(op.getInputs()[inputIndex++]);
4523 emitLocationInfoAndNewLine(ops);
4527LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4528 return emitFunctionCall(op);
4531LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4532 return emitFunctionCall(op);
4535template <
typename PPS>
4537 bool isAutomatic =
false,
4538 bool emitAsTwoStateType =
false) {
4539 ps <<
"function" << PP::nbsp;
4541 ps <<
"automatic" << PP::nbsp;
4542 auto retType = op.getExplicitlyReturnedType();
4544 ps.invokeWithStringOS([&](
auto &os) {
4545 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4546 emitAsTwoStateType);
4552 emitter.emitPortList(
4556LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4557 auto parent = op->getParentOfType<sv::FuncOp>();
4559 return emitOutputLikeOp(op, ports);
4562LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4564 ps <<
"`include" << PP::nbsp;
4566 if (op.getStyle() == IncludeStyle::System)
4567 ps <<
"<" << op.getTarget() <<
">";
4569 ps <<
"\"" << op.getTarget() <<
"\"";
4571 emitLocationInfo(op.getLoc());
4572 setPendingNewline();
4576LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4579 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4583 if (
auto linkageName = importOp.getLinkageName())
4584 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4586 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4587 assert(op.isDeclaration() &&
"function must be a declaration");
4590 assert(state.pendingNewline);
4596LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4598 emitError(op,
"SV attributes emission is unimplemented for the op");
4601 SmallPtrSet<Operation *, 8> ops;
4604 ps.addCallback({op,
true});
4606 if (
auto fd = op.getFd())
4607 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4610 ps.addCallback({op,
false});
4611 emitLocationInfoAndNewLine(ops);
4615LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4617 emitError(op,
"SV attributes emission is unimplemented for the op");
4620 SmallPtrSet<Operation *, 8> ops;
4623 ps.addCallback({op,
true});
4625 ps.scopedBox(PP::ibox0, [&]() {
4626 emitExpression(op.getFd(), ops);
4628 ps <<
"," << PP::space;
4629 ps.writeQuotedEscaped(op.getFormatString());
4637 for (
auto operand : op.getSubstitutions()) {
4638 ps <<
"," << PP::space;
4639 emitExpression(operand, ops);
4643 ps.addCallback({op,
false});
4644 emitLocationInfoAndNewLine(ops);
4648LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4650 emitError(op,
"SV attributes emission is unimplemented for the op");
4653 SmallPtrSet<Operation *, 8> ops;
4658 StringRef
string = op.getFormatString();
4659 if (
string.ends_with(
"\n"))
4660 string =
string.drop_back();
4665 bool isFirst =
true;
4668 while (!
string.
empty()) {
4669 auto lhsRhs =
string.split(
'\n');
4673 ps << PP::end << PP::newline << PP::neverbox;
4677 emitTextWithSubstitutions(
4678 ps, lhsRhs.first, op,
4679 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4680 string = lhsRhs.second;
4685 emitLocationInfoAndNewLine(ops);
4690LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4692 emitError(op,
"SV attributes emission is unimplemented for the op");
4696 SmallPtrSet<Operation *, 8> ops;
4701 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4702 assert(macroOp &&
"Invalid IR");
4704 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4706 if (!op.getInputs().empty()) {
4708 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4709 emitExpression(val, ops, LowestPrecedence,
4715 emitLocationInfoAndNewLine(ops);
4721StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4722 std::optional<unsigned> verbosity) {
4724 emitError(op,
"SV attributes emission is unimplemented for the op");
4727 SmallPtrSet<Operation *, 8> ops;
4729 ps.addCallback({op,
true});
4731 if (verbosity && *verbosity != 1) {
4733 ps.addAsString(*verbosity);
4737 ps.addCallback({op,
false});
4738 emitLocationInfoAndNewLine(ops);
4742LogicalResult StmtEmitter::visitSV(StopOp op) {
4743 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4746LogicalResult StmtEmitter::visitSV(FinishOp op) {
4747 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4751LogicalResult StmtEmitter::visitSV(ExitOp op) {
4752 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4758StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4759 std::optional<unsigned> verbosity,
4760 StringAttr message, ValueRange operands) {
4762 emitError(op,
"SV attributes emission is unimplemented for the op");
4765 SmallPtrSet<Operation *, 8> ops;
4767 ps.addCallback({op,
true});
4773 if ((verbosity && *verbosity != 1) || message) {
4775 ps.scopedBox(PP::ibox0, [&]() {
4779 ps.addAsString(*verbosity);
4784 ps <<
"," << PP::space;
4785 ps.writeQuotedEscaped(message.getValue());
4787 for (
auto operand : operands) {
4788 ps <<
"," << PP::space;
4789 emitExpression(operand, ops);
4798 ps.addCallback({op,
false});
4799 emitLocationInfoAndNewLine(ops);
4803LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4804 return emitFatalMessageOp(op);
4807LogicalResult StmtEmitter::visitSV(FatalOp op) {
4808 return emitFatalMessageOp(op);
4811LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4812 return emitNonfatalMessageOp(op,
"$error");
4815LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4816 return emitNonfatalMessageOp(op,
"$warning");
4819LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4820 return emitNonfatalMessageOp(op,
"$info");
4823LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4824 return emitNonfatalMessageOp(op,
"$error");
4827LogicalResult StmtEmitter::visitSV(WarningOp op) {
4828 return emitNonfatalMessageOp(op,
"$warning");
4831LogicalResult StmtEmitter::visitSV(InfoOp op) {
4832 return emitNonfatalMessageOp(op,
"$info");
4835LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4836 SmallPtrSet<Operation *, 8> ops({op});
4839 ps.addCallback({op,
true});
4841 switch (op.getBaseAttr().getValue()) {
4842 case MemBaseTypeAttr::MemBaseBin:
4845 case MemBaseTypeAttr::MemBaseHex:
4850 ps.scopedBox(PP::ibox0, [&]() {
4851 ps.writeQuotedEscaped(op.getFilename());
4852 ps <<
"," << PP::space;
4853 emitExpression(op.getDest(), ops);
4857 ps.addCallback({op,
false});
4858 emitLocationInfoAndNewLine(ops);
4862LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4863 emitSVAttributes(op);
4866 ps.addCallback({op,
true});
4867 ps <<
"generate" << PP::newline;
4869 setPendingNewline();
4870 emitStatementBlock(op.getBody().getBlocks().front());
4873 ps <<
"endgenerate";
4874 ps.addCallback({op,
false});
4875 setPendingNewline();
4879LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4880 emitSVAttributes(op);
4883 ps.addCallback({op,
true});
4885 ps.invokeWithStringOS([&](
auto &os) {
4886 emitter.printParamValue(
4887 op.getCond(), os, VerilogPrecedence::Selection,
4888 [&]() { return op->emitOpError(
"invalid case parameter"); });
4891 setPendingNewline();
4894 ArrayAttr
patterns = op.getCasePatterns();
4895 ArrayAttr caseNames = op.getCaseNames();
4896 MutableArrayRef<Region> regions = op.getCaseRegions();
4903 llvm::StringMap<size_t> nextGenIds;
4904 ps.scopedBox(PP::bbox2, [&]() {
4906 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4907 auto ®ion = regions[i];
4908 assert(region.hasOneBlock());
4909 Attribute patternAttr =
patterns[i];
4912 if (!isa<mlir::TypedAttr>(patternAttr))
4915 ps.invokeWithStringOS([&](
auto &os) {
4916 emitter.printParamValue(
4917 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4918 [&]() {
return op->emitOpError(
"invalid case value"); });
4921 StringRef legalName =
4922 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4925 setPendingNewline();
4926 emitStatementBlock(region.getBlocks().front());
4929 setPendingNewline();
4935 ps.addCallback({op,
false});
4936 setPendingNewline();
4940LogicalResult StmtEmitter::visitSV(
ForOp op) {
4941 emitSVAttributes(op);
4942 llvm::SmallPtrSet<Operation *, 8> ops;
4943 ps.addCallback({op,
true});
4945 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4948 ps.scopedBox(PP::cbox0, [&]() {
4952 ps <<
"logic" << PP::nbsp;
4953 ps.invokeWithStringOS([&](
auto &os) {
4954 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4959 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4964 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4965 [&]() { emitExpression(op.getUpperBound(), ops); },
4971 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4972 [&]() { emitExpression(op.getStep(), ops); },
4976 ps << PP::neverbreak;
4977 setPendingNewline();
4978 emitStatementBlock(op.getBody().getBlocks().front());
4981 ps.addCallback({op,
false});
4982 emitLocationInfoAndNewLine(ops);
4987void StmtEmitter::emitAssertionLabel(Operation *op) {
4988 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4994void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4995 SmallPtrSetImpl<Operation *> &ops,
4996 bool isConcurrent =
false) {
4999 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5000 ps.scopedBox(PP::ibox0, [&]() {
5001 ps.writeQuotedEscaped(message.getValue());
5003 for (
auto arg : args) {
5004 ps <<
"," << PP::space;
5005 emitExpression(arg, ops);
5011template <
typename Op>
5012LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5014 emitError(op,
"SV attributes emission is unimplemented for the op");
5017 SmallPtrSet<Operation *, 8> ops;
5019 ps.addCallback({op,
true});
5020 ps.scopedBox(PP::ibox2, [&]() {
5021 emitAssertionLabel(op);
5022 ps.scopedBox(PP::cbox0, [&]() {
5024 switch (op.getDefer()) {
5025 case DeferAssert::Immediate:
5027 case DeferAssert::Observed:
5030 case DeferAssert::Final:
5035 ps.scopedBox(PP::ibox0, [&]() {
5036 emitExpression(op.getExpression(), ops);
5039 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5043 ps.addCallback({op,
false});
5044 emitLocationInfoAndNewLine(ops);
5048LogicalResult StmtEmitter::visitSV(AssertOp op) {
5049 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5052LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5053 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5056LogicalResult StmtEmitter::visitSV(CoverOp op) {
5057 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5060template <
typename Op>
5061LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5063 emitError(op,
"SV attributes emission is unimplemented for the op");
5066 SmallPtrSet<Operation *, 8> ops;
5068 ps.addCallback({op,
true});
5069 ps.scopedBox(PP::ibox2, [&]() {
5070 emitAssertionLabel(op);
5071 ps.scopedBox(PP::cbox0, [&]() {
5072 ps << opName << PP::nbsp <<
"property (";
5073 ps.scopedBox(PP::ibox0, [&]() {
5074 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5076 emitExpression(op.getClock(), ops);
5077 ps <<
")" << PP::space;
5078 emitExpression(op.getProperty(), ops);
5081 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5086 ps.addCallback({op,
false});
5087 emitLocationInfoAndNewLine(ops);
5091LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5092 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5095LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5096 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5099LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5100 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5105template <
typename Op>
5106LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5108 emitError(op,
"SV attributes emission is unimplemented for the op");
5118 Operation *parent = op->getParentOp();
5119 Value
property = op.getProperty();
5120 bool isTemporal = !
property.getType().isSignlessInteger(1);
5121 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5122 bool emitAsImmediate = !isTemporal && isProcedural;
5125 SmallPtrSet<Operation *, 8> ops;
5127 ps.addCallback({op,
true});
5128 ps.scopedBox(PP::ibox2, [&]() {
5130 emitAssertionLabel(op);
5132 ps.scopedBox(PP::cbox0, [&]() {
5133 if (emitAsImmediate)
5134 ps << opName <<
"(";
5136 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5138 Value clock = op.getClock();
5139 auto event = op.getEvent();
5141 ps.scopedBox(PP::ibox2, [&]() {
5142 PropertyEmitter(emitter, ops)
5143 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5146 ps.scopedBox(PP::ibox2, [&]() {
5147 PropertyEmitter(emitter, ops)
5148 .emitAssertPropertyBody(property, op.getDisable());
5153 ps.addCallback({op,
false});
5154 emitLocationInfoAndNewLine(ops);
5158LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5159 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5162LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5163 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5166LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5167 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5170LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5172 emitError(op,
"SV attributes emission is unimplemented for the op");
5175 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5176 .getMacroIdentifier());
5179 bool hasEmptyThen = op->getRegion(0).front().empty();
5181 ps <<
"`ifndef " << ident;
5183 ps <<
"`ifdef " << ident;
5185 SmallPtrSet<Operation *, 8> ops;
5187 emitLocationInfoAndNewLine(ops);
5190 emitStatementBlock(op->getRegion(0).front());
5192 if (!op->getRegion(1).empty()) {
5193 if (!hasEmptyThen) {
5195 ps <<
"`else // " << ident;
5196 setPendingNewline();
5198 emitStatementBlock(op->getRegion(1).front());
5205 setPendingNewline();
5213void StmtEmitter::emitBlockAsStatement(
5214 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5215 StringRef multiLineComment) {
5222 emitLocationInfoAndNewLine(locationOps);
5225 emitStatementBlock(*block);
5227 if (needsBeginEnd) {
5231 if (!multiLineComment.empty())
5232 ps <<
" // " << multiLineComment;
5233 setPendingNewline();
5237LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5239 for (
auto &op : ooop.getBody().front())
5244LogicalResult StmtEmitter::visitSV(IfOp op) {
5245 SmallPtrSet<Operation *, 8> ops;
5247 auto ifcondBox = PP::ibox2;
5249 emitSVAttributes(op);
5251 ps.addCallback({op,
true});
5252 ps <<
"if (" << ifcondBox;
5262 emitExpression(ifOp.getCond(), ops);
5263 ps << PP::end <<
")";
5264 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5266 if (!ifOp.hasElse())
5270 Block *elseBlock = ifOp.getElseBlock();
5272 if (!nestedElseIfOp) {
5277 emitBlockAsStatement(elseBlock, ops);
5283 ifOp = nestedElseIfOp;
5284 ps <<
"else if (" << ifcondBox;
5286 ps.addCallback({op,
false});
5291LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5292 emitSVAttributes(op);
5293 SmallPtrSet<Operation *, 8> ops;
5297 auto printEvent = [&](AlwaysOp::Condition cond) {
5298 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5299 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5301 ps.addCallback({op,
true});
5303 switch (op.getNumConditions()) {
5309 printEvent(op.getCondition(0));
5314 ps.scopedBox(PP::cbox0, [&]() {
5315 printEvent(op.getCondition(0));
5316 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5317 ps << PP::space <<
"or" << PP::space;
5318 printEvent(op.getCondition(i));
5327 std::string comment;
5328 if (op.getNumConditions() == 0) {
5329 comment =
"always @*";
5331 comment =
"always @(";
5334 [&](Attribute eventAttr) {
5335 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5336 comment += stringifyEventControl(event);
5338 [&]() { comment +=
", "; });
5342 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5343 ps.addCallback({op,
false});
5347LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5348 emitSVAttributes(op);
5349 SmallPtrSet<Operation *, 8> ops;
5353 ps.addCallback({op,
true});
5354 StringRef opString =
"always_comb";
5355 if (state.options.noAlwaysComb)
5356 opString =
"always @(*)";
5359 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5360 ps.addCallback({op,
false});
5364LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5365 emitSVAttributes(op);
5367 SmallPtrSet<Operation *, 8> ops;
5371 ps.addCallback({op,
true});
5372 ps <<
"always_ff @(";
5373 ps.scopedBox(PP::cbox0, [&]() {
5374 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5375 emitExpression(op.getClock(), ops);
5376 if (op.getResetStyle() == ResetType::AsyncReset) {
5377 ps << PP::nbsp <<
"or" << PP::space
5378 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5379 emitExpression(op.getReset(), ops);
5386 std::string comment;
5387 comment +=
"always_ff @(";
5388 comment += stringifyEventControl(op.getClockEdge());
5389 if (op.getResetStyle() == ResetType::AsyncReset) {
5391 comment += stringifyEventControl(*op.getResetEdge());
5395 if (op.getResetStyle() == ResetType::NoReset)
5396 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5399 emitLocationInfoAndNewLine(ops);
5400 ps.scopedBox(PP::bbox2, [&]() {
5406 if (op.getResetStyle() == ResetType::AsyncReset &&
5407 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5409 emitExpression(op.getReset(), ops);
5411 emitBlockAsStatement(op.getResetBlock(), ops);
5414 emitBlockAsStatement(op.getBodyBlock(), ops);
5419 ps <<
" // " << comment;
5420 setPendingNewline();
5422 ps.addCallback({op,
false});
5426LogicalResult StmtEmitter::visitSV(InitialOp op) {
5427 emitSVAttributes(op);
5428 SmallPtrSet<Operation *, 8> ops;
5431 ps.addCallback({op,
true});
5433 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5434 ps.addCallback({op,
false});
5438LogicalResult StmtEmitter::visitSV(CaseOp op) {
5439 emitSVAttributes(op);
5440 SmallPtrSet<Operation *, 8> ops, emptyOps;
5443 ps.addCallback({op,
true});
5444 if (op.getValidationQualifier() !=
5445 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5446 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5447 op.getValidationQualifier()))
5449 const char *opname =
nullptr;
5450 switch (op.getCaseStyle()) {
5451 case CaseStmtType::CaseStmt:
5454 case CaseStmtType::CaseXStmt:
5457 case CaseStmtType::CaseZStmt:
5461 ps << opname <<
" (";
5462 ps.scopedBox(PP::ibox0, [&]() {
5463 emitExpression(op.getCond(), ops);
5466 emitLocationInfoAndNewLine(ops);
5468 size_t caseValueIndex = 0;
5469 ps.scopedBox(PP::bbox2, [&]() {
5470 for (
auto &caseInfo : op.getCases()) {
5472 auto &
pattern = caseInfo.pattern;
5474 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5475 .Case<CaseBitPattern>([&](
auto bitPattern) {
5478 ps.invokeWithStringOS([&](
auto &os) {
5479 os << bitPattern->getWidth() <<
"'b";
5480 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5481 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5484 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5485 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5486 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5488 .Case<CaseExprPattern>([&](
auto) {
5489 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5491 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5492 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5495 emitBlockAsStatement(caseInfo.block, emptyOps);
5501 ps.addCallback({op,
false});
5502 emitLocationInfoAndNewLine(ops);
5506LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5507 bool doNotPrint = op.getDoNotPrint();
5508 if (doNotPrint && !state.options.emitBindComments)
5513 emitSVAttributes(op);
5515 ps.addCallback({op,
true});
5518 <<
"/* This instance is elsewhere emitted as a bind statement."
5521 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5522 "attributes. The attributes will not be emitted.";
5525 SmallPtrSet<Operation *, 8> ops;
5530 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5531 assert(moduleOp &&
"Invalid IR");
5535 if (!op.getParameters().empty()) {
5538 bool printed =
false;
5540 llvm::zip(op.getParameters(),
5541 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5542 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5543 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5545 if (param.getValue() == modParam.getValue())
5550 ps <<
" #(" << PP::bbox2 << PP::newline;
5553 ps <<
"," << PP::newline;
5557 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5559 ps.invokeWithStringOS([&](
auto &os) {
5560 emitter.printParamValue(param.getValue(), os, [&]() {
5561 return op->emitOpError(
"invalid instance parameter '")
5562 << param.getName().getValue() <<
"' value";
5568 ps << PP::end << PP::newline <<
")";
5575 SmallVector<Value> instPortValues(modPortInfo.size());
5576 op.getValues(instPortValues, modPortInfo);
5577 emitInstancePortList(op, modPortInfo, instPortValues);
5579 ps.addCallback({op,
false});
5580 emitLocationInfoAndNewLine(ops);
5585 setPendingNewline();
5590void StmtEmitter::emitInstancePortList(Operation *op,
5592 ArrayRef<Value> instPortValues) {
5593 SmallPtrSet<Operation *, 8> ops;
5596 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5597 ModulePortInfo containingPortList(containingModule.getPortList());
5602 size_t maxNameLength = 0;
5603 for (
auto &elt : modPortInfo) {
5604 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5607 auto getWireForValue = [&](Value result) {
5608 return result.getUsers().begin()->getOperand(0);
5612 bool isFirst =
true;
5613 bool isZeroWidth =
false;
5615 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5617 auto &modPort = modPortInfo.
at(portNum);
5619 Value portVal = instPortValues[portNum];
5624 bool shouldPrintComma =
true;
5626 shouldPrintComma =
false;
5627 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5629 shouldPrintComma =
true;
5634 if (shouldPrintComma)
5637 emitLocationInfoAndNewLine(ops);
5652 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5653 auto modPortName = modPort.getVerilogName();
5655 ps.spaces(maxNameLength - modPortName.size() + 1);
5657 ps.scopedBox(PP::ibox0, [&]() {
5664 if (!modPort.isOutput()) {
5666 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5667 ps <<
"/* Zero width */";
5669 emitExpression(portVal, ops, LowestPrecedence);
5670 }
else if (portVal.use_empty()) {
5671 ps <<
"/* unused */";
5672 }
else if (portVal.hasOneUse() &&
5673 (output = dyn_cast_or_null<OutputOp>(
5674 portVal.getUses().begin()->getOwner()))) {
5679 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5681 containingPortList.atOutput(outputPortNo).getVerilogName());
5683 portVal = getWireForValue(portVal);
5684 emitExpression(portVal, ops);
5690 if (!isFirst || isZeroWidth) {
5691 emitLocationInfoAndNewLine(ops);
5704LogicalResult StmtEmitter::visitSV(BindOp op) {
5705 emitter.emitBind(op);
5706 assert(state.pendingNewline);
5710LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5711 emitComment(op.getCommentAttr());
5713 emitSVAttributes(op);
5716 ps.addCallback({op,
true});
5718 setPendingNewline();
5720 emitStatementBlock(*op.getBodyBlock());
5722 ps <<
"endinterface" << PP::newline;
5723 ps.addCallback({op,
false});
5724 setPendingNewline();
5729 emitSVAttributes(op);
5731 ps.addCallback({op,
true});
5733 ps << op.getContent();
5735 ps.addCallback({op,
false});
5736 setPendingNewline();
5740LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5742 emitSVAttributes(op);
5744 ps.addCallback({op,
true});
5746 ps << PP::neverbox <<
"// ";
5747 ps.invokeWithStringOS([&](
auto &os) {
5752 ps.invokeWithStringOS(
5753 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5757 ps.addCallback({op,
false});
5758 setPendingNewline();
5762LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5764 ps.addCallback({op,
true});
5768 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5769 auto port = cast<ModportStructAttr>(portAttr);
5770 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5771 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5772 ps << PPExtString(getSymOpName(signalDecl));
5776 ps.addCallback({op,
false});
5777 setPendingNewline();
5781LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5783 ps.addCallback({op,
true});
5784 SmallPtrSet<Operation *, 8> emitted;
5787 emitExpression(op.getIface(), emitted);
5788 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5789 emitExpression(op.getRhs(), emitted);
5791 ps.addCallback({op,
false});
5792 setPendingNewline();
5796LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5798 ps <<
"`" << op.getMacroIdentifier();
5799 setPendingNewline();
5803LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5804 auto decl = op.getReferencedMacro(&state.symbolCache);
5807 ps.addCallback({op,
true});
5809 if (decl.getArgs()) {
5811 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5812 ps << cast<StringAttr>(name);
5816 if (!op.getFormatString().empty()) {
5818 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5821 ps.addCallback({op,
false});
5822 setPendingNewline();
5826void StmtEmitter::emitStatement(Operation *op) {
5833 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5837 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5838 succeeded(dispatchVerifVisitor(op)))
5841 emitOpError(op,
"emission to Verilog not supported");
5842 emitPendingNewlineIfNeeded();
5843 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5844 setPendingNewline();
5855 StmtEmitter &stmtEmitter) {
5862 if (isa<IfDefProceduralOp>(op->getParentOp()))
5870 SmallVector<Value, 8> exprsToScan(op->getOperands());
5875 while (!exprsToScan.empty()) {
5876 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5883 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5884 auto *defOp = readInout.getOperand().getDefiningOp();
5891 if (isa<sv::WireOp>(defOp))
5896 if (!isa<RegOp, LogicOp>(defOp))
5902 if (isa<LogicOp>(defOp) &&
5903 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5907 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5908 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5916 exprsToScan.append(expr->getOperands().begin(),
5917 expr->getOperands().end());
5923 if (expr->getBlock() != op->getBlock())
5928 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5935template <
class AssignTy>
5937 AssignTy singleAssign;
5938 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5939 if (hasSVAttributes(user))
5942 if (auto assign = dyn_cast<AssignTy>(user)) {
5945 singleAssign = assign;
5949 return isa<ReadInOutOp>(user);
5951 return singleAssign;
5957 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5961 if (op1->getBlock() != user->getBlock())
5967 return op1->isBeforeInBlock(user);
5971LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5972 emitSVAttributes(op);
5973 auto value = op->getResult(0);
5974 SmallPtrSet<Operation *, 8> opsForLocation;
5975 opsForLocation.insert(op);
5977 ps.addCallback({op,
true});
5980 auto type = value.getType();
5986 bool singleBitDefaultType = !isa<LocalParamOp>(op);
5988 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5989 unsigned targetColumn = 0;
5990 unsigned column = 0;
5993 if (maxDeclNameWidth > 0)
5994 targetColumn += maxDeclNameWidth + 1;
5997 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5998 }
else if (!word.empty()) {
6000 column += word.size();
6001 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6002 ps.spaces(numSpaces);
6003 column += numSpaces;
6006 SmallString<8> typeString;
6009 llvm::raw_svector_ostream stringStream(typeString);
6012 true, singleBitDefaultType);
6015 if (maxTypeWidth > 0)
6016 targetColumn += maxTypeWidth + 1;
6017 unsigned numSpaces = 0;
6018 if (!typeString.empty()) {
6020 column += typeString.size();
6023 if (targetColumn > column)
6024 numSpaces = targetColumn - column;
6025 ps.spaces(numSpaces);
6026 column += numSpaces;
6032 ps.invokeWithStringOS(
6033 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6036 if (state.options.printDebugInfo) {
6037 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6038 auto innerSym = innerSymOp.getInnerSymAttr();
6039 if (innerSym && !innerSym.empty()) {
6041 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6047 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6048 ps << PP::space <<
"=" << PP::space;
6049 ps.invokeWithStringOS([&](
auto &os) {
6050 emitter.printParamValue(localparam.getValue(), os, [&]() {
6051 return op->emitOpError(
"invalid localparam value");
6056 if (
auto regOp = dyn_cast<RegOp>(op)) {
6057 if (
auto initValue = regOp.getInit()) {
6058 ps << PP::space <<
"=" << PP::space;
6059 ps.scopedBox(PP::ibox0, [&]() {
6060 emitExpression(initValue, opsForLocation, LowestPrecedence,
6069 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6070 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6073 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6074 auto *source = singleAssign.getSrc().getDefiningOp();
6078 if (!source || isa<ConstantOp>(source) ||
6079 op->getNextNode() == singleAssign) {
6080 ps << PP::space <<
"=" << PP::space;
6081 ps.scopedBox(PP::ibox0, [&]() {
6082 emitExpression(singleAssign.getSrc(), opsForLocation,
6086 emitter.assignsInlined.insert(singleAssign);
6094 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6095 op->getParentOp()->hasTrait<ProceduralRegion>() &&
6098 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6101 auto *source = singleAssign.getSrc().getDefiningOp();
6105 if (!source || isa<ConstantOp>(source) ||
6108 ps << PP::space <<
"=" << PP::space;
6109 ps.scopedBox(PP::ibox0, [&]() {
6110 emitExpression(singleAssign.getSrc(), opsForLocation,
6115 emitter.assignsInlined.insert(singleAssign);
6116 emitter.expressionsEmittedIntoDecl.insert(op);
6123 ps.addCallback({op,
false});
6124 emitLocationInfoAndNewLine(opsForLocation);
6128void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6131 NameCollector collector(emitter);
6132 collector.collectNames(block);
6135 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6136 maxTypeWidth = collector.getMaxTypeWidth();
6139void StmtEmitter::emitStatementBlock(Block &body) {
6140 ps.scopedBox(PP::bbox2, [&]() {
6145 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6146 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6151 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6152 collectNamesAndCalculateDeclarationWidths(body);
6155 for (
auto &op : body) {
6162void ModuleEmitter::emitStatement(Operation *op) {
6163 StmtEmitter(*
this, state.options).emitStatement(op);
6168void ModuleEmitter::emitSVAttributes(Operation *op) {
6176 setPendingNewline();
6183void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6184 auto verilogName =
module.getVerilogModuleNameAttr();
6186 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6188 setPendingNewline();
6197void ModuleEmitter::emitBind(BindOp op) {
6199 emitError(op,
"SV attributes emission is unimplemented for the op");
6200 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6206 Operation *childMod =
6207 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6211 ps.addCallback({op,
true});
6212 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6213 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6215 bool isFirst =
true;
6216 ps.scopedBox(PP::bbox2, [&]() {
6217 auto parentPortInfo = parentMod.getPortList();
6221 size_t maxNameLength = 0;
6222 for (
auto &elt : childPortInfo) {
6223 auto portName = elt.getVerilogName();
6224 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6225 maxNameLength = std::max(maxNameLength, elt.getName().size());
6228 SmallVector<Value> instPortValues(childPortInfo.size());
6229 inst.getValues(instPortValues, childPortInfo);
6231 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6233 Value portVal = instPortValues[idx];
6239 bool shouldPrintComma =
true;
6241 shouldPrintComma =
false;
6242 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6244 shouldPrintComma =
true;
6249 if (shouldPrintComma)
6262 ps << PP::neverbox <<
"//";
6266 ps.nbsp(maxNameLength - elt.getName().size());
6268 llvm::SmallPtrSet<Operation *, 4> ops;
6269 if (elt.isOutput()) {
6270 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6271 "output port must have either single or no use");
6272 if (portVal.use_empty()) {
6273 ps <<
"/* unused */";
6274 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6275 portVal.getUses().begin()->getOwner())) {
6278 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6280 parentPortList.atOutput(outputPortNo).getVerilogName());
6282 portVal = portVal.getUsers().begin()->getOperand(0);
6283 ExprEmitter(*
this, ops)
6284 .emitExpression(portVal, LowestPrecedence,
6288 ExprEmitter(*
this, ops)
6289 .emitExpression(portVal, LowestPrecedence,
6302 ps.addCallback({op,
false});
6303 setPendingNewline();
6306void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6308 emitError(op,
"SV attributes emission is unimplemented for the op");
6310 auto instance = op.getReferencedInstance(&state.symbolCache);
6312 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6313 instance.getInterfaceType().getInterface());
6315 ps.addCallback({op,
true});
6316 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6317 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6319 ps.addCallback({op,
false});
6320 setPendingNewline();
6323void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6327 auto printParamType = [&](Type type, Attribute defaultValue,
6328 SmallString<8> &result) {
6330 llvm::raw_svector_ostream sstream(result);
6335 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6336 if (intAttr.getValue().getBitWidth() == 32)
6338 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6339 if (fpAttr.getType().isF64())
6342 if (isa<NoneType>(type))
6349 if (
auto intType = type_dyn_cast<IntegerType>(type))
6350 if (intType.getWidth() == 32) {
6351 sstream <<
"/*integer*/";
6355 printPackedType(type, sstream, module->getLoc(),
6363 size_t maxTypeWidth = 0;
6364 SmallString<8> scratch;
6365 for (
auto param : params) {
6366 auto paramAttr = cast<ParamDeclAttr>(param);
6368 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6369 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6372 if (maxTypeWidth > 0)
6375 ps.scopedBox(PP::bbox2, [&]() {
6376 ps << PP::newline <<
"#(";
6377 ps.scopedBox(PP::cbox0, [&]() {
6380 [&](Attribute param) {
6381 auto paramAttr = cast<ParamDeclAttr>(param);
6382 auto defaultValue = paramAttr.getValue();
6384 printParamType(paramAttr.getType(), defaultValue, scratch);
6385 if (!scratch.empty())
6387 if (scratch.size() < maxTypeWidth)
6388 ps.nbsp(maxTypeWidth - scratch.size());
6390 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6391 module, paramAttr.getName()));
6395 ps.invokeWithStringOS([&](
auto &os) {
6397 return module->emitError("parameter '")
6398 << paramAttr.getName().getValue()
6399 << "' has invalid value";
6404 [&]() { ps <<
"," << PP::newline; });
6410void ModuleEmitter::emitPortList(Operation *module,
6412 bool emitAsTwoStateType) {
6414 if (portInfo.
size())
6415 emitLocationInfo(module->getLoc());
6419 bool hasOutputs =
false, hasZeroWidth =
false;
6420 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6421 SmallVector<SmallString<8>, 16> portTypeStrings;
6423 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6424 auto port = portInfo.
at(i);
6428 lastNonZeroPort = i;
6431 portTypeStrings.push_back({});
6433 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6435 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6438 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6441 if (maxTypeWidth > 0)
6445 ps.scopedBox(PP::bbox2, [&]() {
6446 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6447 auto lastPort = e - 1;
6450 auto portType = portInfo.
at(portIdx).
type;
6454 bool isZeroWidth =
false;
6459 ps << (isZeroWidth ?
"// " :
" ");
6463 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6464 switch (thisPortDirection) {
6465 case ModulePort::Direction::Output:
6468 case ModulePort::Direction::Input:
6469 ps << (hasOutputs ?
"input " :
"input ");
6471 case ModulePort::Direction::InOut:
6472 ps << (hasOutputs ?
"inout " :
"inout ");
6475 bool emitWireInPorts = state.options.emitWireInPorts;
6476 if (emitWireInPorts)
6480 if (!portTypeStrings[portIdx].
empty())
6481 ps << portTypeStrings[portIdx];
6482 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6483 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6485 size_t startOfNamePos =
6486 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6492 ps.invokeWithStringOS(
6493 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6496 auto innerSym = portInfo.
at(portIdx).
getSym();
6497 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6499 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6504 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6508 if (
auto loc = portInfo.
at(portIdx).
loc)
6509 emitLocationInfo(loc);
6519 if (!state.options.disallowPortDeclSharing) {
6520 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6523 auto port = portInfo.
at(portIdx);
6527 bool isZeroWidth =
false;
6532 ps << (isZeroWidth ?
"// " :
" ");
6535 ps.nbsp(startOfNamePos);
6538 StringRef name = port.getVerilogName();
6542 ps.invokeWithStringOS(
6543 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6546 auto sym = port.getSym();
6547 if (state.options.printDebugInfo && sym && !sym.empty())
6548 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6552 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6556 if (
auto loc = port.loc)
6557 emitLocationInfo(loc);
6568 if (!portInfo.
size()) {
6570 SmallPtrSet<Operation *, 8> moduleOpSet;
6571 moduleOpSet.insert(module);
6572 emitLocationInfoAndNewLine(moduleOpSet);
6575 ps <<
");" << PP::newline;
6576 setPendingNewline();
6580void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6581 currentModuleOp =
module;
6583 emitComment(module.getCommentAttr());
6584 emitSVAttributes(module);
6586 ps.addCallback({module,
true});
6590 emitParameters(module, module.getParameters());
6594 assert(state.pendingNewline);
6597 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6600 ps.addCallback({module,
false});
6602 setPendingNewline();
6604 currentModuleOp =
nullptr;
6607void ModuleEmitter::emitFunc(FuncOp func) {
6609 if (func.isDeclaration())
6612 currentModuleOp = func;
6614 ps.addCallback({func,
true});
6618 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6620 ps <<
"endfunction";
6622 currentModuleOp =
nullptr;
6631 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6638 void emit(emit::FileListOp op);
6641 void emit(Block *block);
6643 void emitOp(emit::RefOp op);
6644 void emitOp(emit::VerbatimOp op);
6648 for (Operation &op : *block) {
6649 TypeSwitch<Operation *>(&op)
6650 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6651 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6652 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6653 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6654 .Case<BindInterfaceOp>(
6655 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6656 .Case<TypeScopeOp>([&](
auto typedecls) {
6657 ModuleEmitter(state).emitStatement(typedecls);
6660 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6666 for (
auto sym : op.getFiles()) {
6667 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6669 auto it = state.fileMapping.find(fileName);
6670 if (it == state.fileMapping.end()) {
6671 emitOpError(op,
" references an invalid file: ") << sym;
6675 auto file = cast<emit::FileOp>(it->second);
6676 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6683 StringAttr target = op.getTargetAttr().getAttr();
6684 auto *targetOp = state.symbolCache.getDefinition(target);
6685 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6687 TypeSwitch<Operation *>(targetOp)
6688 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6689 .Case<hw::HWModuleOp>(
6690 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6691 .Case<TypeScopeOp>([&](
auto typedecls) {
6692 ModuleEmitter(state).emitStatement(typedecls);
6695 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6701 SmallPtrSet<Operation *, 8> ops;
6706 StringRef text = op.getText();
6710 const auto &[lhs, rhs] = text.split(
'\n');
6714 ps << PP::end << PP::newline << PP::neverbox;
6716 }
while (!text.empty());
6719 emitLocationInfoAndNewLine(ops);
6737 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6738 moduleOp->walk([&](Operation *op) {
6740 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6743 SymbolTable::getSymbolAttrName()),
6744 name.getSymName(), op);
6745 if (isa<BindOp>(op))
6751 auto collectPorts = [&](
auto moduleOp) {
6752 auto portInfo = moduleOp.getPortList();
6753 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6754 if (!p.attrs || p.attrs.empty())
6756 for (NamedAttribute portAttr : p.attrs) {
6757 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6766 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6767 for (
auto file :
designOp.getOps<emit::FileOp>())
6768 for (
auto refs : file.getOps<emit::RefOp>())
6769 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6771 SmallString<32> outputPath;
6772 for (
auto &op : *
designOp.getBody()) {
6775 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6777 bool hasFileName =
false;
6778 bool emitReplicatedOps = !isFileOp;
6779 bool addToFilelist = !isFileOp;
6785 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6787 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6788 <<
" on " << op <<
"\n";);
6789 if (!attr.isDirectory())
6792 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6793 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6796 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6801 if (!defaultFileName.isTriviallyEmpty()) {
6802 llvm::sys::path::append(outputPath, defaultFileName);
6804 op->emitError(
"file name unspecified");
6806 llvm::sys::path::append(outputPath,
"error.out");
6810 auto destFile = StringAttr::get(op->getContext(), outputPath);
6811 auto &file =
files[destFile];
6812 file.ops.push_back(info);
6813 file.emitReplicatedOps = emitReplicatedOps;
6814 file.addToFilelist = addToFilelist;
6815 file.isVerilog = outputPath.ends_with(
".sv");
6820 if (!attr || attr.isDirectory()) {
6821 auto excludeFromFileListAttr =
6822 BoolAttr::get(op->getContext(), !addToFilelist);
6823 auto includeReplicatedOpsAttr =
6824 BoolAttr::get(op->getContext(), emitReplicatedOps);
6825 auto outputFileAttr = hw::OutputFileAttr::get(
6826 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6827 op->setAttr(
"output_file", outputFileAttr);
6833 TypeSwitch<Operation *>(&op)
6834 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6836 fileMapping.try_emplace(file.getSymNameAttr(), file);
6837 separateFile(file, file.getFileName());
6839 .Case<emit::FragmentOp>([&](
auto fragment) {
6842 .Case<HWModuleOp>([&](
auto mod) {
6844 auto sym = mod.getNameAttr();
6847 collectInstanceSymbolsAndBinds(mod);
6849 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6850 if (it->second.size() != 1 || attr) {
6853 op.emitError(
"modules can be emitted to a single file");
6861 if (attr || separateModules)
6867 .Case<InterfaceOp>([&](InterfaceOp intf) {
6872 for (
auto &op : *intf.getBodyBlock())
6873 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6874 if (
auto name = symOp.getNameAttr())
6878 if (attr || separateModules)
6879 separateFile(intf, intf.getSymName() +
".sv");
6885 separateFile(op, op.getOutputFile().getFilename().getValue());
6887 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
6893 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6894 [&](Operation *op) {
6900 separateFile(op,
"");
6902 .Case<FuncOp>([&](
auto op) {
6908 separateFile(op,
"");
6912 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6915 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6924 separateFile(op,
"");
6926 .Case<BindOp>([&](
auto op) {
6928 separateFile(op,
"bindfile.sv");
6933 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
6934 .Case<MacroDeclOp>([&](
auto op) {
6937 .Case<sv::ReserveNamesOp>([](
auto op) {
6940 .Case<om::ClassLike>([&](
auto op) {
6943 .Case<om::ConstantOp>([&](
auto op) {
6946 .Default([&](
auto *) {
6947 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6967 size_t lastReplicatedOp = 0;
6969 bool emitHeaderInclude =
6972 if (emitHeaderInclude)
6975 size_t numReplicatedOps =
6980 DenseSet<emit::FragmentOp> includedFragments;
6981 for (
const auto &opInfo : file.
ops) {
6982 Operation *op = opInfo.op;
6986 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6992 if (
auto fragments =
6994 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6998 op->emitError(
"cannot find referenced fragment ") << sym;
7001 emit::FragmentOp fragment = it->second;
7002 if (includedFragments.insert(fragment).second) {
7003 thingsToEmit.emplace_back(it->second);
7009 thingsToEmit.emplace_back(op);
7014 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7019 TypeSwitch<Operation *>(op)
7020 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7021 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7024 .Case<HWModuleGeneratedOp>(
7025 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7026 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7027 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7028 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7029 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7030 .Case<TypeScopeOp>([&](
auto typedecls) {
7031 ModuleEmitter(state).emitStatement(typedecls);
7033 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7035 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7036 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7037 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7038 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7039 .Default([&](
auto *op) {
7040 state.encounteredError =
true;
7041 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7048 llvm::formatted_raw_ostream &os,
7049 StringAttr fileName,
bool parallelize) {
7054 parallelize &=
context->isMultithreadingEnabled();
7065 size_t lineOffset = 0;
7066 for (
auto &entry : thingsToEmit) {
7067 entry.verilogLocs.setStream(os);
7068 if (
auto *op = entry.getOperation()) {
7073 state.addVerilogLocToOps(lineOffset, fileName);
7075 os << entry.getStringData();
7080 if (state.encounteredError)
7098 SmallString<256> buffer;
7099 llvm::raw_svector_ostream tmpStream(buffer);
7100 llvm::formatted_raw_ostream rs(tmpStream);
7108 if (state.encounteredError)
7113 for (
auto &entry : thingsToEmit) {
7116 auto *op = entry.getOperation();
7118 auto lineOffset = os.getLine() + 1;
7119 os << entry.getStringData();
7123 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7126 entry.verilogLocs.setStream(os);
7133 state.addVerilogLocToOps(0, fileName);
7134 if (state.encounteredError) {
7153 module.emitWarning()
7154 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7155 "created only at SplitExportVerilog";
7164 for (
const auto &it : emitter.
files) {
7165 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7166 "\" ----- 8< -----\n\n");
7172 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7173 "\" ----- 8< -----\n\n");
7174 for (
auto &name : it.second)
7175 contents += name.str() +
"\n";
7176 list.emplace_back(contents);
7179 llvm::formatted_raw_ostream rs(os);
7183 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7190 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7192 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7193 if (failed(failableParallelForEach(
7194 module->getContext(), modulesToPrepare,
7195 [&](
auto op) { return prepareHWModule(op, options); })))
7202struct ExportVerilogPass
7203 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7204 ExportVerilogPass(raw_ostream &os) : os(os) {}
7205 void runOnOperation()
override {
7207 mlir::OpPassManager preparePM(
"builtin.module");
7208 preparePM.addPass(createLegalizeAnonEnums());
7209 auto &modulePM = preparePM.nestAny();
7210 modulePM.addPass(createPrepareForEmission());
7211 if (failed(runPipeline(preparePM, getOperation())))
7212 return signalPassFailure();
7215 return signalPassFailure();
7222struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7223 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7224 : ExportVerilogPass{*os} {
7225 owned = std::move(os);
7229 std::unique_ptr<llvm::raw_ostream> owned;
7233std::unique_ptr<mlir::Pass>
7235 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7238std::unique_ptr<mlir::Pass>
7240 return std::make_unique<ExportVerilogPass>(os);
7251static std::unique_ptr<llvm::ToolOutputFile>
7255 SmallString<128> outputFilename(dirname);
7257 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7260 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7262 emitter.
designOp.emitError(
"cannot create output directory \"")
7263 << outputDir <<
"\": " << error.message();
7269 std::string errorMessage;
7270 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7272 emitter.
designOp.emitError(errorMessage);
7289 llvm::formatted_raw_ostream rs(output->os());
7295 StringAttr::get(fileName.getContext(), output->getFilename()),
7301 StringRef dirname) {
7312 bool insertSuccess =
7314 .insert({StringAttr::get(module.getContext(),
circtHeader),
7320 if (!insertSuccess) {
7321 module.emitError() << "tried to emit a heder to " << circtHeader
7322 << ", but the file is used as an output too.";
7328 parallelForEach(module->getContext(), emitter.
files.begin(),
7329 emitter.
files.end(), [&](
auto &it) {
7330 createSplitOutputFile(it.first, it.second, dirname,
7335 SmallString<128> filelistPath(dirname);
7336 llvm::sys::path::append(filelistPath,
"filelist.f");
7338 std::string errorMessage;
7339 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7341 module->emitError(errorMessage);
7345 for (
const auto &it : emitter.
files) {
7346 if (it.second.addToFilelist)
7347 output->os() << it.first.str() <<
"\n";
7356 for (
auto &name : it.second)
7357 output->os() << name.str() <<
"\n";
7366 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7368 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7369 if (failed(failableParallelForEach(
7370 module->getContext(), modulesToPrepare,
7371 [&](
auto op) { return prepareHWModule(op, options); })))
7379struct ExportSplitVerilogPass
7380 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7381 ExportSplitVerilogPass(StringRef directory) {
7382 directoryName = directory.str();
7384 void runOnOperation()
override {
7386 mlir::OpPassManager preparePM(
"builtin.module");
7389 modulePM.addPass(createPrepareForEmission());
7390 if (failed(runPipeline(preparePM, getOperation())))
7391 return signalPassFailure();
7394 return signalPassFailure();
7399std::unique_ptr<mlir::Pass>
7401 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.
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.