40#include "mlir/IR/BuiltinOps.h"
41#include "mlir/IR/ImplicitLocOpBuilder.h"
42#include "mlir/IR/Location.h"
43#include "mlir/IR/Threading.h"
44#include "mlir/Interfaces/FunctionImplementation.h"
45#include "mlir/Pass/PassManager.h"
46#include "mlir/Support/FileUtilities.h"
47#include "llvm/ADT/MapVector.h"
48#include "llvm/ADT/STLExtras.h"
49#include "llvm/ADT/StringSet.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/Support/FileSystem.h"
52#include "llvm/Support/FormattedStream.h"
53#include "llvm/Support/Path.h"
54#include "llvm/Support/SaveAndRestore.h"
55#include "llvm/Support/ToolOutputFile.h"
56#include "llvm/Support/raw_ostream.h"
59#define GEN_PASS_DEF_EXPORTSPLITVERILOG
60#define GEN_PASS_DEF_EXPORTVERILOG
61#include "circt/Conversion/Passes.h.inc"
68using namespace ExportVerilog;
70using namespace pretty;
72#define DEBUG_TYPE "export-verilog"
80enum VerilogPrecedence {
101enum SubExprSignResult { IsSigned, IsUnsigned };
107 VerilogPrecedence precedence;
110 SubExprSignResult signedness;
112 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
113 : precedence(precedence), signedness(signedness) {}
123 return Builder(ctx).getI32IntegerAttr(value);
126static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
127 return Builder(ctx).getIntegerAttr(t, value);
143 if (isa<VerbatimExprOp>(op)) {
144 if (op->getNumOperands() == 0 &&
145 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
150 if (isa<XMRRefOp>(op))
154 if (isa<MacroRefExprOp>(op))
164 if (op->getNumOperands() == 0)
168 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
172 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
173 auto *indexOp = array.getIndex().getDefiningOp();
174 if (!indexOp || isa<ConstantOp>(indexOp))
176 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
177 auto *readSrc = read.getInput().getDefiningOp();
179 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
194 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
195 return attr.getValue();
196 return TypeSwitch<Operation *, StringRef>(symOp)
199 .Case<InterfaceOp>([&](InterfaceOp op) {
202 .Case<InterfaceSignalOp>(
203 [&](InterfaceSignalOp op) {
return op.getSymName(); })
204 .Case<InterfaceModportOp>(
205 [&](InterfaceModportOp op) {
return op.getSymName(); })
206 .Default([&](Operation *op) {
207 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
208 return attr.getValue();
209 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
210 return attr.getValue();
211 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
212 return attr.getValue();
214 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
215 return attr.getValue();
216 return StringRef(
"");
221template <
typename PPS>
223 os <<
"/*Zero width*/ 1\'b0";
228 auto hml = cast<HWModuleLike>(module);
229 return hml.getPort(portArgNum).getVerilogName();
234 auto hml = cast<HWModuleLike>(module);
235 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
236 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
237 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
238 return updatedName.getValue();
239 return hml.getHWModuleType().getPortName(pId);
248 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
249 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
250 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
251 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
252 UnpackedOpenArrayCastOp>(op))
256 if (isa<verif::ContractOp>(op))
266static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
268 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
269 if (integer.getWidth() != 1)
270 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
273 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
274 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
279 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
280 dims.push_back(intType.getWidth());
284 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
285 return getTypeDims(dims, inout.getElementType(), loc);
286 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
287 return getTypeDims(dims, uarray.getElementType(), loc);
288 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
289 return getTypeDims(dims, uarray.getElementType(), loc);
291 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
294 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
300 SmallVector<Attribute, 4> aDims;
303 SmallVector<Attribute, 4> bDims;
306 return aDims == bDims;
312 if (
auto intType = dyn_cast<IntegerType>(type))
313 return intType.getWidth() == 0;
314 if (
auto inout = dyn_cast<hw::InOutType>(type))
316 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
317 return uarray.getNumElements() == 0 ||
319 if (
auto array = dyn_cast<hw::ArrayType>(type))
320 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
321 if (
auto structType = dyn_cast<hw::StructType>(type))
322 return llvm::all_of(structType.getElements(),
323 [](
auto elem) { return isZeroBitType(elem.type); });
324 if (
auto enumType = dyn_cast<hw::EnumType>(type))
325 return enumType.getFields().empty();
326 if (
auto unionType = dyn_cast<hw::UnionType>(type))
327 return hw::getBitWidth(unionType) == 0;
339 return TypeSwitch<Type, Type>(type)
340 .Case<InOutType>([](InOutType inoutType) {
343 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
346 .Default([](Type type) {
return type; });
351 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
352 auto elementType = cast<hw::InOutType>(type).getElementType();
358 return TypeSwitch<Type, bool>(type)
359 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
362 .Case<StructType>([](
auto) {
return true; })
363 .Default([](
auto) {
return false; });
377 if (
auto name = lhs.getName().compare(rhs.getName()))
379 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
384 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
386 if (lhs.getLine() != rhs.getLine())
387 return lhs.getLine() < rhs.getLine() ? -1 : 1;
388 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
393 Location lhsCallee = lhs.getCallee();
394 Location rhsCallee = rhs.getCallee();
398 Location lhsCaller = lhs.getCaller();
399 Location rhsCaller = rhs.getCaller();
403template <
typename TTargetLoc>
405 auto lhsT = dyn_cast<TTargetLoc>(lhs);
406 auto rhsT = dyn_cast<TTargetLoc>(rhs);
433 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
438 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
443 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
460 SmallPtrSetImpl<Attribute> &locationSet) {
461 llvm::TypeSwitch<Location, void>(loc)
462 .Case<FusedLoc>([&](
auto fusedLoc) {
463 for (
auto subLoc : fusedLoc.getLocations())
466 .Default([&](
auto loc) { locationSet.insert(loc); });
470template <
typename TVector>
472 llvm::array_pod_sort(
473 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
474 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
482 SmallPtrSet<Attribute, 8> locationSet;
483 locationSet.insert(loc);
484 llvm::raw_string_ostream os(
output);
490 const SmallPtrSetImpl<Operation *> &ops) {
494 SmallPtrSet<Attribute, 8> locationSet;
497 llvm::raw_string_ostream os(
output);
506 const SmallPtrSetImpl<Attribute> &locationSet) {
507 if (style == LoweringOptions::LocationInfoStyle::None)
510 llvm::raw_string_ostream sstr(resstr);
512 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
516 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
517 "other styles must be already handled");
518 os <<
"@[" << resstr <<
"]";
527 const SmallPtrSetImpl<Attribute> &locationSet)
543 bool withName = !loc.getName().empty();
545 os <<
"'" << loc.getName().strref() <<
"'(";
554 os << loc.getFilename().getValue();
555 if (
auto line = loc.getLine()) {
557 if (
auto col = loc.getColumn())
569 StringRef lastFileName;
570 for (
size_t i = 0, e = locVector.size(); i != e;) {
575 auto first = locVector[i];
576 if (first.getFilename() != lastFileName) {
577 lastFileName = first.getFilename();
584 first.getFilename() == locVector[
end].getFilename() &&
585 first.getLine() == locVector[
end].getLine())
590 if (
auto line = first.getLine()) {
592 if (
auto col = first.getColumn())
600 os <<
':' << first.getLine() <<
":{";
602 os << locVector[i++].getColumn();
614 llvm::TypeSwitch<Location, void>(loc)
615 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
617 .Case<mlir::FusedLoc>([&](
auto loc) {
618 SmallPtrSet<Attribute, 8> locationSet;
622 .Default([&](
auto loc) {
634 switch (locationSet.size()) {
645 SmallVector<FileLineColLoc, 8> flcLocs;
646 SmallVector<Attribute, 8> otherLocs;
647 flcLocs.reserve(locationSet.size());
648 otherLocs.reserve(locationSet.size());
649 for (Attribute loc : locationSet) {
650 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
651 flcLocs.push_back(flcLoc);
653 otherLocs.push_back(loc);
664 size_t sstrSize =
os.tell();
665 bool emittedAnything =
false;
666 auto recheckEmittedSomething = [&]() {
667 size_t currSize =
os.tell();
668 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
669 emittedAnything |= emittedSomethingSinceLastCheck;
671 return emittedSomethingSinceLastCheck;
680 if (recheckEmittedSomething()) {
682 recheckEmittedSomething();
688 if (emittedAnything && !flcLocs.empty())
693 llvm::raw_string_ostream &
os;
705 if (isa<BlockArgument>(v))
714 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
719 if (v.getDefiningOp<ReadInterfaceSignalOp>())
732 if (
auto cast = dyn_cast<BitcastOp>(op))
733 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
737 if (op->hasOneUse() &&
738 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
746 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
752 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
756 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
757 if (verbatim.getFormatString().size() > 32)
762 for (
auto &use : op->getUses()) {
763 auto *user = use.getOwner();
773 UnionExtractOp, IndexedPartSelectOp>(user))
774 if (use.getOperandNumber() == 0 &&
785 auto usedInExprControl = [user, &use]() {
786 return TypeSwitch<Operation *, bool>(user)
787 .Case<ltl::ClockOp>([&](
auto clockOp) {
789 return clockOp.getClock() == use.get();
791 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
792 sv::CoverConcurrentOp>(
793 [&](
auto op) {
return op.getClock() == use.get(); })
794 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
795 sv::CoverPropertyOp>([&](
auto op) {
796 return op.getDisable() == use.get() || op.getClock() == use.get();
798 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
803 .Default([](
auto) {
return false; });
806 if (!usedInExprControl())
810 auto read = dyn_cast<ReadInOutOp>(op);
813 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
824 unsigned numStatements = 0;
825 block.walk([&](Operation *op) {
827 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
828 return WalkResult::advance();
830 TypeSwitch<Operation *, unsigned>(op)
831 .Case<VerbatimOp>([&](
auto) {
837 .Case<IfOp>([&](
auto) {
848 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
849 .Case<OutputOp>([&](OutputOp oop) {
852 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
853 Operation *op = operand.getDefiningOp();
854 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
857 .Default([](
auto) {
return 1; });
858 if (numStatements > 1)
859 return WalkResult::interrupt();
860 return WalkResult::advance();
862 if (numStatements == 0)
864 if (numStatements == 1)
874 if (op->getResult(0).use_empty())
879 if (op->hasOneUse() &&
880 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
881 *op->getUsers().begin()))
903 for (
auto &op : *elseBlock) {
904 if (
auto opIf = dyn_cast<IfOp>(op)) {
921template <
typename PPS>
923 enum Container { NoContainer, InComment, InAttr };
924 Container currentContainer = NoContainer;
926 auto closeContainer = [&] {
927 if (currentContainer == NoContainer)
929 if (currentContainer == InComment)
931 else if (currentContainer == InAttr)
933 ps << PP::end << PP::end;
935 currentContainer = NoContainer;
938 bool isFirstContainer =
true;
939 auto openContainer = [&](Container newContainer) {
940 assert(newContainer != NoContainer);
941 if (currentContainer == newContainer)
945 if (!isFirstContainer)
946 ps << (mayBreak ? PP::space : PP::nbsp);
947 isFirstContainer =
false;
950 if (newContainer == InComment)
952 else if (newContainer == InAttr)
954 currentContainer = newContainer;
962 ps.scopedBox(PP::cbox0, [&]() {
963 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
964 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
966 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
968 if (attr.getExpression())
969 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
978 if (
auto *op = val.getDefiningOp())
981 if (
auto port = dyn_cast<BlockArgument>(val)) {
983 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
984 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
986 port.getArgNumber());
988 assert(
false &&
"unhandled value");
1000class VerilogEmitterState {
1002 explicit VerilogEmitterState(ModuleOp designOp,
1008 llvm::formatted_raw_ostream &os,
1009 StringAttr fileName,
OpLocMap &verilogLocMap)
1010 : designOp(designOp), shared(shared), options(options),
1011 symbolCache(symbolCache), globalNames(globalNames),
1012 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1013 pp(os, options.emittedLineLength), fileName(fileName) {
1014 pp.setListener(&saver);
1037 llvm::formatted_raw_ostream &os;
1039 bool encounteredError =
false;
1048 bool pendingNewline =
false;
1062 StringAttr fileName;
1068 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1071 verilogLocMap.
clear();
1075 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1076 void operator=(
const VerilogEmitterState &) =
delete;
1089using CallbackDataTy = std::pair<Operation *, bool>;
1093 VerilogEmitterState &state;
1098 explicit EmitterBase(VerilogEmitterState &state)
1100 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1102 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1103 state.encounteredError =
true;
1104 return op->emitError(message);
1107 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1108 state.encounteredError =
true;
1109 return op->emitOpError(message);
1112 void emitLocationImpl(llvm::StringRef location) {
1115 ps << PP::neverbreak;
1116 if (!location.empty())
1117 ps <<
"\t// " << location;
1120 void emitLocationInfo(Location loc) {
1128 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1131 setPendingNewline();
1134 template <
typename PPS>
1135 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1136 llvm::function_ref<
void(Value)> operandEmitter,
1137 ArrayAttr symAttrs);
1143 void emitComment(StringAttr comment);
1147 void emitPendingNewlineIfNeeded() {
1148 if (state.pendingNewline) {
1149 state.pendingNewline =
false;
1153 void setPendingNewline() {
1154 assert(!state.pendingNewline);
1155 state.pendingNewline =
true;
1158 void startStatement() { emitPendingNewlineIfNeeded(); }
1161 void operator=(
const EmitterBase &) =
delete;
1162 EmitterBase(
const EmitterBase &) =
delete;
1166template <
typename PPS>
1167void EmitterBase::emitTextWithSubstitutions(
1168 PPS &ps, StringRef
string, Operation *op,
1169 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1180 if (
auto *itemOp = item.getOp()) {
1181 if (item.hasPort()) {
1185 if (!symOpName.empty())
1187 emitError(itemOp,
"cannot get name for symbol ") << sym;
1189 emitError(op,
"cannot get name for symbol ") << sym;
1191 return StringRef(
"<INVALID>");
1197 unsigned numSymOps = symAttrs.size();
1198 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1201 next =
string.find(
"{{", next);
1202 if (next == StringRef::npos)
1209 while (next <
string.size() &&
isdigit(
string[next]))
1212 if (start == next) {
1216 size_t operandNoLength = next - start;
1219 StringRef fmtOptsStr;
1220 if (
string[next] ==
':') {
1221 size_t startFmtOpts = next + 1;
1222 while (next <
string.size() &&
string[next] !=
'}')
1224 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1228 if (!
string.substr(next).starts_with(
"}}"))
1232 unsigned operandNo = 0;
1233 if (
string.drop_front(start)
1234 .take_front(operandNoLength)
1235 .getAsInteger(10, operandNo)) {
1236 emitError(op,
"operand substitution too large");
1242 auto before =
string.take_front(start - 2);
1243 if (!before.empty())
1248 if (operandNo < op->getNumOperands())
1250 operandEmitter(op->getOperand(operandNo));
1251 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1252 unsigned symOpNum = operandNo - op->getNumOperands();
1253 auto sym = symAttrs[symOpNum];
1254 StringRef symVerilogName;
1255 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1256 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1257 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1258 auto namepath = globalRef.getNamepathAttr().getValue();
1259 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1262 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1264 auto innerRef = cast<InnerRefAttr>(sym);
1265 auto ref = state.symbolCache.getInnerDefinition(
1266 innerRef.getModule(), innerRef.getName());
1267 ps << namify(innerRef, ref);
1270 symVerilogName = namify(sym, symOp);
1273 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1274 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1276 symVerilogName = namify(sym, symOp);
1278 if (!symVerilogName.empty())
1281 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1285 string =
string.drop_front(next);
1291 while (emitUntilSubstitution())
1295 if (!
string.
empty())
1299void EmitterBase::emitComment(StringAttr comment) {
1306 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1310 auto ref = comment.getValue();
1312 while (!ref.empty()) {
1313 std::tie(line, ref) = ref.split(
"\n");
1320 if (line.size() <= lineLength) {
1322 setPendingNewline();
1333 auto breakPos = line.rfind(
' ', lineLength);
1335 if (breakPos == StringRef::npos) {
1336 breakPos = line.find(
' ', lineLength);
1339 if (breakPos == StringRef::npos)
1340 breakPos = line.size();
1347 setPendingNewline();
1348 breakPos = line.find_first_not_of(
' ', breakPos);
1350 if (breakPos == StringRef::npos)
1353 line = line.drop_front(breakPos);
1363 bool addPrefixUnderScore =
true;
1366 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1370 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1372 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1374 result = StringAttr::get(expr.getContext(), name);
1376 }
else if (
auto *op = expr.getDefiningOp()) {
1378 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1380 result = StringAttr::get(expr.getContext(), name);
1382 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1388 addPrefixUnderScore =
false;
1390 TypeSwitch<Operation *>(op)
1393 .Case([&result](VerbatimExprOp verbatim) {
1394 verbatim.getAsmResultNames([&](Value, StringRef name) {
1395 result = StringAttr::get(verbatim.getContext(), name);
1398 .Case([&result](VerbatimExprSEOp verbatim) {
1399 verbatim.getAsmResultNames([&](Value, StringRef name) {
1400 result = StringAttr::get(verbatim.getContext(), name);
1406 if (
auto operandName =
1409 cast<IntegerType>(extract.getType()).getWidth();
1411 result = StringAttr::get(extract.getContext(),
1412 operandName.strref() +
"_" +
1413 Twine(extract.getLowBit()));
1415 result = StringAttr::get(
1416 extract.getContext(),
1417 operandName.strref() +
"_" +
1418 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1419 Twine(extract.getLowBit()));
1427 if (!result || result.strref().empty())
1431 if (addPrefixUnderScore && result.strref().front() !=
'_')
1432 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1444class ModuleEmitter :
public EmitterBase {
1446 explicit ModuleEmitter(VerilogEmitterState &state)
1447 : EmitterBase(state), currentModuleOp(nullptr),
1451 emitPendingNewlineIfNeeded();
1455 void emitParameters(Operation *module, ArrayAttr params);
1456 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1457 bool emitAsTwoStateType =
false);
1460 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1461 void emitFunc(FuncOp);
1464 void emitStatement(Operation *op);
1465 void emitBind(BindOp op);
1466 void emitBindInterface(BindInterfaceOp op);
1468 void emitSVAttributes(Operation *op);
1471 StringRef getVerilogStructFieldName(StringAttr field) {
1472 return fieldNameResolver.getRenamedFieldName(field).getValue();
1479 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1491 bool printPackedType(Type type, raw_ostream &os, Location loc,
1492 Type optionalAliasType = {},
bool implicitIntType =
true,
1493 bool singleBitDefaultType =
true,
1494 bool emitAsTwoStateType =
false);
1498 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1506 function_ref<InFlightDiagnostic()> emitError);
1509 VerilogPrecedence parenthesizeIfLooserThan,
1510 function_ref<InFlightDiagnostic()> emitError);
1516 Operation *currentModuleOp;
1522 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1528 SmallPtrSet<Operation *, 16> assignsInlined;
1537 const ModuleEmitter &emitter) {
1538 if (isa<RegOp>(op)) {
1543 cast<InOutType>(op->getResult(0).getType()).getElementType();
1550 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1551 while (isa<ArrayType>(innerType.getElementType()))
1552 innerType = cast<ArrayType>(innerType.getElementType());
1553 if (isa<StructType>(innerType.getElementType()) ||
1554 isa<TypeAliasType>(innerType.getElementType()))
1562 if (isa<sv::WireOp>(op))
1564 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1565 return "localparam";
1568 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1569 return interface.getInterfaceType().getInterface().getValue();
1573 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1577 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1579 if (isa<LogicOp>(op)) {
1585 if (isProcedural && !stripAutomatic)
1586 return hasStruct ?
"automatic" :
"automatic logic";
1587 return hasStruct ?
"" :
"logic";
1594 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1597 assert(!emitter.state.options.disallowLocalVariables &&
1598 "automatic variables not allowed");
1602 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1603 :
"automatic logic";
1610static void emitDim(Attribute width, raw_ostream &os, Location loc,
1611 ModuleEmitter &emitter,
bool downTo) {
1613 os <<
"<<invalid type>>";
1616 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1617 if (intAttr.getValue().isZero()) {
1618 os <<
"/*Zero Width*/";
1623 os << (intAttr.getValue().getZExtValue() - 1);
1633 auto typedAttr = dyn_cast<TypedAttr>(width);
1635 mlir::emitError(loc,
"untyped dimension attribute ") << width;
1639 getIntAttr(loc.getContext(), typedAttr.getType(),
1640 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1641 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1645 emitter.printParamValue(width, os, [loc]() {
1646 return mlir::emitError(loc,
"invalid parameter in type");
1654static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1655 ModuleEmitter &emitter) {
1656 for (Attribute width : dims) {
1657 emitDim(width, os, loc, emitter,
true);
1662void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1663 SmallVector<Attribute, 4> dims;
1695 SmallVectorImpl<Attribute> &dims,
1696 bool implicitIntType,
bool singleBitDefaultType,
1697 ModuleEmitter &emitter,
1698 Type optionalAliasType = {},
1699 bool emitAsTwoStateType =
false) {
1700 return TypeSwitch<Type, bool>(type)
1701 .Case<IntegerType>([&](IntegerType integerType) {
1702 if (emitAsTwoStateType && dims.empty()) {
1704 if (!typeName.empty()) {
1709 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1711 getInt32Attr(type.getContext(), integerType.getWidth()));
1713 StringRef typeName =
1714 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1715 if (!typeName.empty()) {
1722 return !dims.empty() || !implicitIntType;
1724 .Case<IntType>([&](IntType intType) {
1725 if (!implicitIntType)
1727 dims.push_back(intType.getWidth());
1731 .Case<ArrayType>([&](ArrayType arrayType) {
1732 dims.push_back(arrayType.getSizeAttr());
1734 implicitIntType, singleBitDefaultType,
1736 emitAsTwoStateType);
1738 .Case<InOutType>([&](InOutType inoutType) {
1740 implicitIntType, singleBitDefaultType,
1742 emitAsTwoStateType);
1744 .Case<EnumType>([&](EnumType enumType) {
1746 if (enumType.getBitWidth() != 32)
1747 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1749 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1750 llvm::interleaveComma(
1751 enumType.getFields().getAsRange<StringAttr>(), os,
1752 [&](
auto enumerator) {
1753 os << emitter.fieldNameResolver.getEnumFieldName(
1754 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1759 .Case<StructType>([&](StructType structType) {
1760 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1761 os <<
"/*Zero Width*/";
1764 os <<
"struct packed {";
1765 for (
auto &element : structType.getElements()) {
1767 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1768 <<
": Zero Width;*/ ";
1771 SmallVector<Attribute, 8> structDims;
1776 {}, emitAsTwoStateType);
1777 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1778 emitter.printUnpackedTypePostfix(element.type, os);
1785 .Case<UnionType>([&](UnionType unionType) {
1786 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1787 os <<
"/*Zero Width*/";
1791 int64_t unionWidth = hw::getBitWidth(unionType);
1792 os <<
"union packed {";
1793 for (
auto &element : unionType.getElements()) {
1795 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1796 <<
": Zero Width;*/ ";
1799 int64_t elementWidth = hw::getBitWidth(element.type);
1800 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1802 os <<
" struct packed {";
1803 if (element.offset) {
1804 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1805 << element.offset - 1 <<
":0] "
1806 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1810 SmallVector<Attribute, 8> structDims;
1814 true, emitter, {}, emitAsTwoStateType);
1815 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1816 emitter.printUnpackedTypePostfix(element.type, os);
1820 if (elementWidth + (int64_t)element.offset < unionWidth) {
1821 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1822 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1823 <<
"__post_padding_" << element.name.getValue() <<
";";
1825 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1834 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1835 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1836 os <<
"<<unexpected unpacked array>>";
1837 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1841 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1842 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1844 mlir::emitError(loc,
"unresolvable type reference");
1847 if (typedecl.getType() != typeRef.getInnerType()) {
1848 mlir::emitError(loc,
"declared type did not match aliased type");
1852 os << typedecl.getPreferredName();
1853 emitDims(dims, os, typedecl->getLoc(), emitter);
1856 .Default([&](Type type) {
1857 os <<
"<<invalid type '" << type <<
"'>>";
1858 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1874bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1875 Type optionalAliasType,
1876 bool implicitIntType,
1877 bool singleBitDefaultType,
1878 bool emitAsTwoStateType) {
1879 SmallVector<Attribute, 8> packedDimensions;
1881 singleBitDefaultType, *
this, optionalAliasType,
1882 emitAsTwoStateType);
1888void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1889 TypeSwitch<Type, void>(type)
1891 printUnpackedTypePostfix(inoutType.getElementType(), os);
1893 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1894 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1895 : state.designOp->getLoc();
1896 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1898 printUnpackedTypePostfix(arrayType.getElementType(), os);
1900 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1902 printUnpackedTypePostfix(arrayType.getElementType(), os);
1904 .Case<InterfaceType>([&](
auto) {
1918ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1919 function_ref<InFlightDiagnostic()> emitError) {
1920 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1928ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1929 VerilogPrecedence parenthesizeIfLooserThan,
1930 function_ref<InFlightDiagnostic()> emitError) {
1931 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1932 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1933 APInt value = intAttr.getValue();
1937 if (intTy.getWidth() > 32) {
1939 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1943 if (intTy.isSigned())
1944 os << intTy.getWidth() <<
"'sd";
1946 os << intTy.getWidth() <<
"'d";
1948 value.print(os, intTy.isSigned());
1949 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1951 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1953 os.write_escaped(strAttr.getValue());
1955 return {Symbol, IsUnsigned};
1957 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1959 os << fpAttr.getValueAsDouble();
1960 return {Symbol, IsUnsigned};
1962 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1963 os << verbatimParam.getValue().getValue();
1964 return {Symbol, IsUnsigned};
1966 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1968 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1969 parameterRef.getName());
1972 return {Symbol, IsUnsigned};
1976 auto expr = dyn_cast<ParamExprAttr>(value);
1978 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1979 emitError() <<
" = " << value;
1980 return {LowestPrecedence, IsUnsigned};
1983 StringRef operatorStr;
1984 StringRef openStr, closeStr;
1985 VerilogPrecedence subprecedence = LowestPrecedence;
1986 VerilogPrecedence prec;
1987 std::optional<SubExprSignResult> operandSign;
1988 bool isUnary =
false;
1989 bool hasOpenClose =
false;
1991 switch (expr.getOpcode()) {
1993 operatorStr =
" + ";
1994 subprecedence = Addition;
1997 operatorStr =
" * ";
1998 subprecedence = Multiply;
2001 operatorStr =
" & ";
2002 subprecedence = And;
2005 operatorStr =
" | ";
2009 operatorStr =
" ^ ";
2010 subprecedence = Xor;
2013 operatorStr =
" << ";
2014 subprecedence = Shift;
2018 operatorStr =
" >> ";
2019 subprecedence = Shift;
2023 operatorStr =
" >>> ";
2024 subprecedence = Shift;
2025 operandSign = IsSigned;
2028 operatorStr =
" / ";
2029 subprecedence = Multiply;
2030 operandSign = IsUnsigned;
2033 operatorStr =
" / ";
2034 subprecedence = Multiply;
2035 operandSign = IsSigned;
2038 operatorStr =
" % ";
2039 subprecedence = Multiply;
2040 operandSign = IsUnsigned;
2043 operatorStr =
" % ";
2044 subprecedence = Multiply;
2045 operandSign = IsSigned;
2048 openStr =
"$clog2(";
2050 operandSign = IsUnsigned;
2051 hasOpenClose =
true;
2054 case PEO::StrConcat:
2057 hasOpenClose =
true;
2060 subprecedence = LowestPrecedence;
2065 prec = subprecedence;
2068 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2070 assert(isUnary || hasOpenClose ||
2071 !llvm::hasSingleElement(expr.getOperands()));
2078 auto emitOperand = [&](Attribute operand) ->
bool {
2080 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2081 if (operandSign.has_value())
2082 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2085 if (operandSign.has_value()) {
2087 signedness = *operandSign;
2089 return signedness == IsSigned;
2093 if (prec > parenthesizeIfLooserThan)
2102 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2103 for (
auto op : expr.getOperands().drop_front()) {
2106 if (expr.getOpcode() == PEO::Add) {
2107 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2108 const APInt &value = integer.getValue();
2109 if (value.isNegative() && !value.isMinSignedValue()) {
2111 allOperandsSigned &=
2112 emitOperand(IntegerAttr::get(op.getType(), -value));
2119 allOperandsSigned &= emitOperand(op);
2123 if (prec > parenthesizeIfLooserThan) {
2127 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2142class ExprEmitter :
public EmitterBase,
2144 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2149 ExprEmitter(ModuleEmitter &emitter,
2150 SmallPtrSetImpl<Operation *> &emittedExprs)
2151 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2153 ExprEmitter(ModuleEmitter &emitter,
2154 SmallPtrSetImpl<Operation *> &emittedExprs,
2156 : EmitterBase(emitter.state), emitter(emitter),
2157 emittedExprs(emittedExprs), buffer(tokens),
2158 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2159 assert(state.pp.getListener() == &state.saver);
2166 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2167 bool isAssignmentLikeContext) {
2168 assert(localTokens.empty());
2170 ps.scopedBox(PP::ibox0, [&]() {
2173 emitSubExpr(exp, parenthesizeIfLooserThan,
2175 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2177 isAssignmentLikeContext);
2182 if (&buffer.tokens == &localTokens)
2183 buffer.flush(state.pp);
2188 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2189 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2191 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2199 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2200 SubExprSignRequirement signReq = NoRequirement,
2201 bool isSelfDeterminedUnsignedValue =
false,
2202 bool isAssignmentLikeContext =
false);
2206 void emitSVAttributes(Operation *op);
2208 SubExprInfo visitUnhandledExpr(Operation *op);
2209 SubExprInfo visitInvalidComb(Operation *op) {
2212 SubExprInfo visitUnhandledComb(Operation *op) {
2213 return visitUnhandledExpr(op);
2216 return dispatchSVVisitor(op);
2219 return visitUnhandledExpr(op);
2221 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2224 enum EmitBinaryFlags {
2225 EB_RequireSignedOperands = RequireSigned,
2226 EB_RequireUnsignedOperands = RequireUnsigned,
2227 EB_OperandSignRequirementMask = 0x3,
2232 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2236 EB_ForceResultSigned = 0x8,
2241 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2242 const char *syntax,
unsigned emitBinaryFlags = 0);
2244 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2245 bool resultAlwaysUnsigned =
false);
2248 void emitSubExprIBox2(
2249 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2250 ps.scopedBox(PP::ibox2,
2251 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2256 template <
typename Container,
typename EachFn>
2257 void interleaveComma(
const Container &c, EachFn eachFn) {
2258 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2263 void interleaveComma(ValueRange ops) {
2264 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2281 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2283 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2284 CloseFunc closeFn) {
2286 ps.scopedBox(PP::cbox0, [&]() {
2287 interleaveComma(c, eachFn);
2293 template <
typename OpenFunc,
typename CloseFunc>
2294 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2295 return emitBracedList(
2296 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2300 void emitBracedList(ValueRange ops) {
2301 return emitBracedList(
2302 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2306 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2309 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2310 bool printAsPattern, Operation *op);
2312 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2313 ArrayAttr fieldValues,
bool printAsPattern,
2316 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2318 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2319 SubExprInfo visitSV(GetModportOp op);
2320 SubExprInfo visitSV(SystemFunctionOp op);
2321 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2322 SubExprInfo visitSV(XMROp op);
2323 SubExprInfo visitSV(SFormatFOp op);
2324 SubExprInfo visitSV(XMRRefOp op);
2325 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2326 SubExprInfo visitSV(VerbatimExprOp op) {
2327 return visitVerbatimExprOp(op, op.getSymbols());
2329 SubExprInfo visitSV(VerbatimExprSEOp op) {
2330 return visitVerbatimExprOp(op, op.getSymbols());
2332 SubExprInfo visitSV(MacroRefExprOp op);
2333 SubExprInfo visitSV(MacroRefExprSEOp op);
2334 template <
typename MacroTy>
2335 SubExprInfo emitMacroCall(MacroTy op);
2337 SubExprInfo visitSV(ConstantXOp op);
2338 SubExprInfo visitSV(ConstantZOp op);
2339 SubExprInfo visitSV(ConstantStrOp op);
2341 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2342 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2344 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2349 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2350 emitSVAttributes(op);
2353 SubExprInfo visitSV(ArrayIndexInOutOp op);
2354 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2355 SubExprInfo visitSV(IndexedPartSelectOp op);
2356 SubExprInfo visitSV(StructFieldInOutOp op);
2359 SubExprInfo visitSV(SampledOp op);
2362 SubExprInfo visitSV(TimeOp op);
2363 SubExprInfo visitSV(STimeOp op);
2366 using TypeOpVisitor::visitTypeOp;
2368 SubExprInfo visitTypeOp(AggregateConstantOp op);
2370 SubExprInfo visitTypeOp(ParamValueOp op);
2377 SubExprInfo visitTypeOp(StructInjectOp op);
2378 SubExprInfo visitTypeOp(UnionCreateOp op);
2379 SubExprInfo visitTypeOp(UnionExtractOp op);
2380 SubExprInfo visitTypeOp(EnumCmpOp op);
2381 SubExprInfo visitTypeOp(EnumConstantOp op);
2384 using CombinationalVisitor::visitComb;
2385 SubExprInfo visitComb(
MuxOp op);
2386 SubExprInfo visitComb(ReverseOp op);
2387 SubExprInfo visitComb(
AddOp op) {
2388 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2389 return emitBinary(op, Addition,
"+");
2391 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2392 SubExprInfo visitComb(
MulOp op) {
2393 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2394 return emitBinary(op, Multiply,
"*");
2396 SubExprInfo visitComb(
DivUOp op) {
2397 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2399 SubExprInfo visitComb(
DivSOp op) {
2400 return emitBinary(op, Multiply,
"/",
2401 EB_RequireSignedOperands | EB_ForceResultSigned);
2403 SubExprInfo visitComb(
ModUOp op) {
2404 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2406 SubExprInfo visitComb(
ModSOp op) {
2407 return emitBinary(op, Multiply,
"%",
2408 EB_RequireSignedOperands | EB_ForceResultSigned);
2410 SubExprInfo visitComb(
ShlOp op) {
2411 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2413 SubExprInfo visitComb(
ShrUOp op) {
2415 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2417 SubExprInfo visitComb(
ShrSOp op) {
2420 return emitBinary(op, Shift,
">>>",
2421 EB_RequireSignedOperands | EB_ForceResultSigned |
2422 EB_RHS_UnsignedWithSelfDeterminedWidth);
2424 SubExprInfo visitComb(
AndOp op) {
2425 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2426 return emitBinary(op, And,
"&");
2428 SubExprInfo visitComb(
OrOp op) {
2429 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2430 return emitBinary(op, Or,
"|");
2432 SubExprInfo visitComb(
XorOp op) {
2433 if (op.isBinaryNot())
2434 return emitUnary(op,
"~");
2435 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2436 return emitBinary(op, Xor,
"^");
2441 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2443 SubExprInfo visitComb(ReplicateOp op);
2444 SubExprInfo visitComb(
ConcatOp op);
2446 SubExprInfo visitComb(ICmpOp op);
2448 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2449 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2450 "not printed within an assignment-like context");
2451 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2452 "supposed to spill such expressions";
2456 SubExprInfo printStructCreate(
2457 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2459 bool printAsPattern, Operation *op);
2462 ModuleEmitter &emitter;
2469 SubExprSignRequirement signPreference = NoRequirement;
2473 SmallPtrSetImpl<Operation *> &emittedExprs;
2476 SmallVector<Token> localTokens;
2490 bool isAssignmentLikeContext =
false;
2494SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2496 unsigned emitBinaryFlags) {
2498 emitError(op,
"SV attributes emission is unimplemented for the op");
2509 if (emitBinaryFlags & EB_ForceResultSigned)
2510 ps <<
"$signed(" << PP::ibox0;
2511 auto operandSignReq =
2512 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2513 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2515 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2517 ps << lhsSpace << syntax << PP::nbsp;
2524 auto rhsPrec = prec;
2525 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2526 rhsPrec = VerilogPrecedence(prec - 1);
2531 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2532 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2533 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2534 operandSignReq = NoRequirement;
2537 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2538 rhsIsUnsignedValueWithSelfDeterminedWidth);
2542 SubExprSignResult signedness = IsUnsigned;
2543 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2544 signedness = IsSigned;
2546 if (emitBinaryFlags & EB_ForceResultSigned) {
2547 ps << PP::end <<
")";
2548 signedness = IsSigned;
2552 return {prec, signedness};
2555SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2556 bool resultAlwaysUnsigned) {
2558 emitError(op,
"SV attributes emission is unimplemented for the op");
2561 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2565 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2566 resultAlwaysUnsigned ? IsUnsigned : signedness};
2571void ExprEmitter::emitSVAttributes(Operation *op) {
2590 if (constant && constant.getValue().isZero())
2591 return concat.getOperand(1);
2601SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2602 VerilogPrecedence parenthesizeIfLooserThan,
2603 SubExprSignRequirement signRequirement,
2604 bool isSelfDeterminedUnsignedValue,
2605 bool isAssignmentLikeContext) {
2607 if (
auto result = dyn_cast<OpResult>(exp))
2608 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2609 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2610 parenthesizeIfLooserThan, signRequirement,
2611 isSelfDeterminedUnsignedValue,
2612 isAssignmentLikeContext);
2616 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2621 auto *op = exp.getDefiningOp();
2625 if (!shouldEmitInlineExpr) {
2628 if (signRequirement == RequireSigned) {
2630 return {Symbol, IsSigned};
2634 return {Symbol, IsUnsigned};
2637 unsigned subExprStartIndex = buffer.tokens.size();
2639 ps.addCallback({op,
true});
2640 auto done = llvm::make_scope_exit([&]() {
2642 ps.addCallback({op, false});
2648 signPreference = signRequirement;
2650 bool bitCastAdded =
false;
2651 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2653 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2654 ps.addAsString(inType.getWidth());
2655 ps <<
"'(" << PP::ibox0;
2656 bitCastAdded =
true;
2660 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2661 isAssignmentLikeContext);
2662 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2668 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2670 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2672 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2673 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2676 expInfo.signedness = IsSigned;
2677 expInfo.precedence = Selection;
2678 }
else if (signRequirement == RequireUnsigned &&
2679 expInfo.signedness == IsSigned) {
2682 expInfo.signedness = IsUnsigned;
2683 expInfo.precedence = Selection;
2684 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2691 expInfo.precedence = Selection;
2698 emittedExprs.insert(exp.getDefiningOp());
2702SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2703 auto openFn = [&]() {
2705 ps.addAsString(op.getMultiple());
2708 auto closeFn = [&]() { ps <<
"}}"; };
2712 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2713 if (op.getOperand().hasOneUse()) {
2714 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2715 return {Symbol, IsUnsigned};
2718 emitBracedList(op.getOperand(), openFn, closeFn);
2719 return {Symbol, IsUnsigned};
2722SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2723 emitBracedList(op.getOperands());
2724 return {Symbol, IsUnsigned};
2727SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2731 Type toType = op.getType();
2734 ps.invokeWithStringOS(
2735 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2738 return emitSubExpr(op.getInput(), LowestPrecedence);
2741SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2742 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2743 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2744 SubExprSignRequirement signop[] = {
2746 NoRequirement, NoRequirement,
2748 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2750 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2752 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2754 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2755 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2758 if (op.isEqualAllOnes())
2759 return emitUnary(op,
"&",
true);
2762 if (op.isNotEqualZero())
2763 return emitUnary(op,
"|",
true);
2765 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2769 result.signedness = IsUnsigned;
2773SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2775 emitError(op,
"SV attributes emission is unimplemented for the op");
2777 unsigned loBit = op.getLowBit();
2778 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2780 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2781 assert((x.precedence == Symbol ||
2783 "should be handled by isExpressionUnableToInline");
2788 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2792 ps.addAsString(hiBit);
2793 if (hiBit != loBit) {
2795 ps.addAsString(loBit);
2798 return {Unary, IsUnsigned};
2801SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2803 emitError(op,
"SV attributes emission is unimplemented for the op");
2805 auto decl = op.getReferencedDecl(state.symbolCache);
2808 return {Selection, IsUnsigned};
2811SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2813 emitError(op,
"SV attributes emission is unimplemented for the op");
2816 ps.scopedBox(PP::ibox0, [&]() {
2818 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2819 [&]() { ps <<
"," << PP::space; });
2822 return {Symbol, IsUnsigned};
2825SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2827 emitError(op,
"SV attributes emission is unimplemented for the op");
2829 auto decl = op.getReferencedDecl(state.symbolCache);
2833 return {Selection, IsUnsigned};
2836SubExprInfo ExprEmitter::visitSV(XMROp op) {
2838 emitError(op,
"SV attributes emission is unimplemented for the op");
2840 if (op.getIsRooted())
2842 for (
auto s : op.getPath())
2843 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2845 return {Selection, IsUnsigned};
2850SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2852 emitError(op,
"SV attributes emission is unimplemented for the op");
2855 auto globalRef = op.getReferencedPath(&state.symbolCache);
2856 auto namepath = globalRef.getNamepathAttr().getValue();
2857 auto *
module = state.symbolCache.getDefinition(
2858 cast<InnerRefAttr>(namepath.front()).getModule());
2860 for (
auto sym : namepath) {
2862 auto innerRef = cast<InnerRefAttr>(sym);
2863 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2864 innerRef.getName());
2865 if (ref.hasPort()) {
2871 auto leaf = op.getVerbatimSuffixAttr();
2872 if (leaf && leaf.size())
2874 return {Selection, IsUnsigned};
2877SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2879 emitError(op,
"SV attributes emission is unimplemented for the op");
2881 emitTextWithSubstitutions(
2882 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2883 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2885 return {Unary, IsUnsigned};
2888template <
typename MacroTy>
2889SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2891 emitError(op,
"SV attributes emission is unimplemented for the op");
2894 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2895 assert(macroOp &&
"Invalid IR");
2897 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2899 if (!op.getInputs().empty()) {
2901 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2902 emitExpression(val, LowestPrecedence, false);
2906 return {LowestPrecedence, IsUnsigned};
2909SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2910 return emitMacroCall(op);
2913SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2914 return emitMacroCall(op);
2917SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2919 emitError(op,
"SV attributes emission is unimplemented for the op");
2921 ps.addAsString(op.getWidth());
2923 return {Unary, IsUnsigned};
2926SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2928 emitError(op,
"SV attributes emission is unimplemented for the op");
2930 ps.writeQuotedEscaped(op.getStr());
2931 return {Symbol, IsUnsigned};
2934SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2936 emitError(op,
"SV attributes emission is unimplemented for the op");
2938 ps.addAsString(op.getWidth());
2940 return {Unary, IsUnsigned};
2943SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2944 bool isNegated =
false;
2947 if (signPreference == RequireSigned && value.isNegative() &&
2948 !value.isMinSignedValue()) {
2953 ps.addAsString(type.getWidth());
2957 if (signPreference == RequireSigned)
2963 SmallString<32> valueStr;
2965 (-value).toStringUnsigned(valueStr, 16);
2967 value.toStringUnsigned(valueStr, 16);
2970 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2973SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2975 emitError(op,
"SV attributes emission is unimplemented for the op");
2977 auto value = op.getValue();
2981 if (value.getBitWidth() == 0) {
2982 emitOpError(op,
"will not emit zero width constants in the general case");
2983 ps <<
"<<unsupported zero width constant: "
2984 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2985 return {Unary, IsUnsigned};
2988 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2991void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2992 bool printAsPattern, Operation *op) {
2993 if (printAsPattern && !isAssignmentLikeContext)
2994 emitAssignmentPatternContextError(op);
2995 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2998 elementValues, [&]() { ps << openDelim; },
2999 [&](Attribute elementValue) {
3000 printConstantAggregate(elementValue,
elementType, op);
3002 [&]() { ps <<
"}"; });
3005void ExprEmitter::printConstantStruct(
3006 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3007 bool printAsPattern, Operation *op) {
3008 if (printAsPattern && !isAssignmentLikeContext)
3009 emitAssignmentPatternContextError(op);
3016 auto fieldRange = llvm::make_filter_range(
3017 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3022 if (printAsPattern) {
3024 fieldRange, [&]() { ps <<
"'{"; },
3025 [&](
const auto &fieldAndValue) {
3026 ps.scopedBox(PP::ibox2, [&]() {
3027 const auto &[field, value] = fieldAndValue;
3028 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3029 <<
":" << PP::space;
3030 printConstantAggregate(value, field.type, op);
3033 [&]() { ps <<
"}"; });
3036 fieldRange, [&]() { ps <<
"{"; },
3037 [&](
const auto &fieldAndValue) {
3038 ps.scopedBox(PP::ibox2, [&]() {
3039 const auto &[field, value] = fieldAndValue;
3040 printConstantAggregate(value, field.type, op);
3043 [&]() { ps <<
"}"; });
3047void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3050 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3051 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3052 isAssignmentLikeContext, op);
3055 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3056 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3060 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3061 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3062 isAssignmentLikeContext, op);
3064 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3065 auto value = cast<IntegerAttr>(attr).getValue();
3066 printConstantScalar(value, intType);
3070 emitOpError(op,
"contains constant of type ")
3071 << type <<
" which cannot be emitted as Verilog";
3074SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3076 emitError(op,
"SV attributes emission is unimplemented for the op");
3080 "zero-bit types not allowed at this point");
3082 printConstantAggregate(op.getFields(), op.getType(), op);
3083 return {Symbol, IsUnsigned};
3086SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3088 emitError(op,
"SV attributes emission is unimplemented for the op");
3090 return ps.invokeWithStringOS([&](
auto &os) {
3091 return emitter.printParamValue(op.getValue(), os, [&]() {
3092 return op->emitOpError(
"invalid parameter use");
3101 emitError(op,
"SV attributes emission is unimplemented for the op");
3103 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3105 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3107 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3109 ps.addAsString(dstWidth);
3111 return {Selection, arrayPrec.signedness};
3114SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3115 emitSubExpr(op.getInput(), Selection);
3120 emitSubExpr(op.getIndex(), LowestPrecedence);
3122 emitSVAttributes(op);
3123 return {Selection, IsUnsigned};
3129 emitError(op,
"SV attributes emission is unimplemented for the op");
3131 if (op.isUniform()) {
3133 ps.addAsString(op.getInputs().size());
3135 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3139 op.getInputs(), [&]() { ps <<
"{"; },
3142 emitSubExprIBox2(v);
3145 [&]() { ps <<
"}"; });
3147 return {Unary, IsUnsigned};
3150SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3152 emitError(op,
"SV attributes emission is unimplemented for the op");
3155 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3156 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3157 return {Unary, IsUnsigned};
3162 emitError(op,
"SV attributes emission is unimplemented for the op");
3164 emitBracedList(op.getOperands());
3165 return {Unary, IsUnsigned};
3168SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3170 emitError(op,
"SV attributes emission is unimplemented for the op");
3172 auto index = op.getIndex();
3173 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3178 emitSubExpr(index, LowestPrecedence);
3180 return {Selection, arrayPrec.signedness};
3183SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3185 emitError(op,
"SV attributes emission is unimplemented for the op");
3187 auto prec = emitSubExpr(op.getInput(), Selection);
3189 emitSubExpr(op.getBase(), LowestPrecedence);
3190 if (op.getDecrement())
3194 ps.addAsString(op.getWidth());
3196 return {Selection, prec.signedness};
3199SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3201 emitError(op,
"SV attributes emission is unimplemented for the op");
3203 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3205 emitSubExpr(op.getBase(), LowestPrecedence);
3206 if (op.getDecrement())
3210 ps.addAsString(op.getWidth());
3215SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3217 emitError(op,
"SV attributes emission is unimplemented for the op");
3219 auto prec = emitSubExpr(op.getInput(), Selection);
3221 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3222 return {Selection, prec.signedness};
3225SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3227 emitError(op,
"SV attributes emission is unimplemented for the op");
3230 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3235SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3237 emitError(op,
"SV attributes emission is unimplemented for the op");
3240 ps.scopedBox(PP::ibox0, [&]() {
3241 ps.writeQuotedEscaped(op.getFormatString());
3248 for (
auto operand : op.getSubstitutions()) {
3249 ps <<
"," << PP::space;
3250 emitSubExpr(operand, LowestPrecedence);
3254 return {Symbol, IsUnsigned};
3257SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3259 emitError(op,
"SV attributes emission is unimplemented for the op");
3262 return {Symbol, IsUnsigned};
3265SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3267 emitError(op,
"SV attributes emission is unimplemented for the op");
3270 return {Symbol, IsUnsigned};
3273SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3287 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3288 ps.scopedBox(PP::ibox0, [&]() {
3289 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3293 emitSVAttributes(op);
3295 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3296 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3300 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3301 return emitSubExpr(op.getFalseValue(), Conditional);
3304 SubExprSignResult signedness = IsUnsigned;
3305 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3306 signedness = IsSigned;
3308 return {Conditional, signedness};
3312SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3314 emitError(op,
"SV attributes emission is unimplemented for the op");
3317 emitSubExpr(op.getInput(), LowestPrecedence);
3320 return {Symbol, IsUnsigned};
3323SubExprInfo ExprEmitter::printStructCreate(
3324 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3326 bool printAsPattern, Operation *op) {
3327 if (printAsPattern && !isAssignmentLikeContext)
3328 emitAssignmentPatternContextError(op);
3331 auto filteredFields = llvm::make_filter_range(
3332 llvm::enumerate(fieldInfos),
3333 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3335 if (printAsPattern) {
3337 filteredFields, [&]() { ps <<
"'{"; },
3338 [&](
const auto &field) {
3339 ps.scopedBox(PP::ibox2, [&]() {
3341 emitter.getVerilogStructFieldName(field.value().name))
3342 <<
":" << PP::space;
3343 fieldFn(field.value(), field.index());
3346 [&]() { ps <<
"}"; });
3349 filteredFields, [&]() { ps <<
"{"; },
3350 [&](
const auto &field) {
3351 ps.scopedBox(PP::ibox2,
3352 [&]() { fieldFn(field.value(), field.index()); });
3354 [&]() { ps <<
"}"; });
3357 return {Selection, IsUnsigned};
3362 emitError(op,
"SV attributes emission is unimplemented for the op");
3366 bool printAsPattern = isAssignmentLikeContext;
3367 StructType structType = op.getType();
3368 return printStructCreate(
3369 structType.getElements(),
3370 [&](
const auto &field,
auto index) {
3371 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3373 isAssignmentLikeContext);
3375 printAsPattern, op);
3380 emitError(op,
"SV attributes emission is unimplemented for the op");
3382 emitSubExpr(op.getInput(), Selection);
3384 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3385 return {Selection, IsUnsigned};
3388SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3390 emitError(op,
"SV attributes emission is unimplemented for the op");
3394 bool printAsPattern = isAssignmentLikeContext;
3395 StructType structType = op.getType();
3396 return printStructCreate(
3397 structType.getElements(),
3398 [&](
const auto &field,
auto index) {
3399 if (field.name == op.getFieldNameAttr()) {
3400 emitSubExpr(op.getNewValue(), Selection);
3402 emitSubExpr(op.getInput(), Selection);
3404 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3407 printAsPattern, op);
3410SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3411 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3412 return {Selection, IsUnsigned};
3415SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3417 emitError(op,
"SV attributes emission is unimplemented for the op");
3418 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3421 result.signedness = IsUnsigned;
3425SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3427 emitError(op,
"SV attributes emission is unimplemented for the op");
3431 auto unionWidth = hw::getBitWidth(unionType);
3432 auto &element = unionType.getElements()[op.getFieldIndex()];
3433 auto elementWidth = hw::getBitWidth(element.type);
3436 if (!elementWidth) {
3437 ps.addAsString(unionWidth);
3439 return {Unary, IsUnsigned};
3443 if (elementWidth == unionWidth) {
3444 emitSubExpr(op.getInput(), LowestPrecedence);
3445 return {Unary, IsUnsigned};
3450 ps.scopedBox(PP::ibox0, [&]() {
3451 if (
auto prePadding = element.offset) {
3452 ps.addAsString(prePadding);
3453 ps <<
"'h0," << PP::space;
3455 emitSubExpr(op.getInput(), Selection);
3456 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3457 ps <<
"," << PP::space;
3458 ps.addAsString(postPadding);
3464 return {Unary, IsUnsigned};
3467SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3469 emitError(op,
"SV attributes emission is unimplemented for the op");
3470 emitSubExpr(op.getInput(), Selection);
3473 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3474 auto unionWidth = hw::getBitWidth(unionType);
3475 auto &element = unionType.getElements()[op.getFieldIndex()];
3476 auto elementWidth = hw::getBitWidth(element.type);
3477 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3478 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3487 return {Selection, IsUnsigned};
3490SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3491 emitOpError(op,
"cannot emit this expression to Verilog");
3492 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3494 return {Symbol, IsUnsigned};
3510enum class PropertyPrecedence {
3530struct EmittedProperty {
3532 PropertyPrecedence precedence;
3537class PropertyEmitter :
public EmitterBase,
3538 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3542 PropertyEmitter(ModuleEmitter &emitter,
3543 SmallPtrSetImpl<Operation *> &emittedOps)
3544 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3545 PropertyEmitter(ModuleEmitter &emitter,
3546 SmallPtrSetImpl<Operation *> &emittedOps,
3548 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3550 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3551 assert(state.pp.getListener() == &state.saver);
3554 void emitAssertPropertyDisable(
3555 Value property, Value disable,
3556 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3558 void emitAssertPropertyBody(
3559 Value property, Value disable,
3560 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3562 void emitAssertPropertyBody(
3563 Value property, sv::EventControl event, Value clock, Value disable,
3564 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3569 emitNestedProperty(Value property,
3570 PropertyPrecedence parenthesizeIfLooserThan);
3571 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3572 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3574 EmittedProperty visitUnhandledLTL(Operation *op);
3575 EmittedProperty visitLTL(ltl::AndOp op);
3576 EmittedProperty visitLTL(ltl::OrOp op);
3577 EmittedProperty visitLTL(ltl::IntersectOp op);
3578 EmittedProperty visitLTL(ltl::DelayOp op);
3579 EmittedProperty visitLTL(ltl::ConcatOp op);
3580 EmittedProperty visitLTL(ltl::RepeatOp op);
3581 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3582 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3583 EmittedProperty visitLTL(ltl::NotOp op);
3584 EmittedProperty visitLTL(ltl::ImplicationOp op);
3585 EmittedProperty visitLTL(ltl::UntilOp op);
3586 EmittedProperty visitLTL(ltl::EventuallyOp op);
3587 EmittedProperty visitLTL(ltl::ClockOp op);
3589 void emitLTLConcat(ValueRange inputs);
3592 ModuleEmitter &emitter;
3597 SmallPtrSetImpl<Operation *> &emittedOps;
3600 SmallVector<Token> localTokens;
3613void PropertyEmitter::emitAssertPropertyDisable(
3614 Value property, Value disable,
3615 PropertyPrecedence parenthesizeIfLooserThan) {
3618 ps <<
"disable iff" << PP::nbsp <<
"(";
3620 emitNestedProperty(disable, PropertyPrecedence::Unary);
3626 ps.scopedBox(PP::ibox0,
3627 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3633void PropertyEmitter::emitAssertPropertyBody(
3634 Value property, Value disable,
3635 PropertyPrecedence parenthesizeIfLooserThan) {
3636 assert(localTokens.empty());
3638 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3643 if (&buffer.tokens == &localTokens)
3644 buffer.flush(state.pp);
3647void PropertyEmitter::emitAssertPropertyBody(
3648 Value property, sv::EventControl event, Value clock, Value disable,
3649 PropertyPrecedence parenthesizeIfLooserThan) {
3650 assert(localTokens.empty());
3653 ps.scopedBox(PP::ibox2, [&] {
3654 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3655 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3661 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3666 if (&buffer.tokens == &localTokens)
3667 buffer.flush(state.pp);
3670EmittedProperty PropertyEmitter::emitNestedProperty(
3671 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3681 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3682 ExprEmitter(emitter, emittedOps, buffer.tokens)
3683 .emitExpression(property, LowestPrecedence,
3685 return {PropertyPrecedence::Symbol};
3688 unsigned startIndex = buffer.tokens.size();
3689 auto info = dispatchLTLVisitor(property.getDefiningOp());
3694 if (
info.precedence > parenthesizeIfLooserThan) {
3696 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3697 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3699 ps << PP::end <<
")";
3701 info.precedence = PropertyPrecedence::Symbol;
3705 emittedOps.insert(property.getDefiningOp());
3709EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3710 emitOpError(op,
"emission as Verilog property or sequence not supported");
3711 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3712 return {PropertyPrecedence::Symbol};
3715EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3718 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3719 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3720 return {PropertyPrecedence::And};
3723EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3726 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3727 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3728 return {PropertyPrecedence::Or};
3731EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3735 emitNestedProperty(input, PropertyPrecedence::Intersect);
3737 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3738 return {PropertyPrecedence::Intersect};
3741EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3743 if (
auto length = op.getLength()) {
3745 ps.addAsString(op.getDelay());
3748 ps.addAsString(op.getDelay());
3750 ps.addAsString(op.getDelay() + *length);
3754 if (op.getDelay() == 0) {
3756 }
else if (op.getDelay() == 1) {
3760 ps.addAsString(op.getDelay());
3765 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3766 return {PropertyPrecedence::Concat};
3769void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3770 bool addSeparator =
false;
3771 for (
auto input : inputs) {
3774 if (!input.getDefiningOp<ltl::DelayOp>())
3775 ps <<
"##0" << PP::space;
3777 addSeparator =
true;
3778 emitNestedProperty(input, PropertyPrecedence::Concat);
3782EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3783 emitLTLConcat(op.getInputs());
3784 return {PropertyPrecedence::Concat};
3787EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3788 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3789 if (
auto more = op.getMore()) {
3791 ps.addAsString(op.getBase());
3794 ps.addAsString(op.getBase() + *more);
3798 if (op.getBase() == 0) {
3800 }
else if (op.getBase() == 1) {
3804 ps.addAsString(op.getBase());
3808 return {PropertyPrecedence::Repeat};
3811EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3812 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3814 auto more = op.getMore();
3816 ps.addAsString(op.getBase());
3819 ps.addAsString(op.getBase() + more);
3823 return {PropertyPrecedence::Repeat};
3826EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3827 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3829 auto more = op.getMore();
3831 ps.addAsString(op.getBase());
3834 ps.addAsString(op.getBase() + more);
3838 return {PropertyPrecedence::Repeat};
3841EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3842 ps <<
"not" << PP::space;
3843 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3844 return {PropertyPrecedence::Unary};
3850 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3851 if (!concatOp || concatOp.getInputs().size() < 2)
3853 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3854 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3856 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3857 if (!constOp || !constOp.getValue().isOne())
3859 return concatOp.getInputs().drop_back();
3862EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3866 emitLTLConcat(range);
3867 ps << PP::space <<
"|=>" << PP::nbsp;
3869 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3870 ps << PP::space <<
"|->" << PP::nbsp;
3872 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3873 return {PropertyPrecedence::Implication};
3876EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3877 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3878 ps << PP::space <<
"until" << PP::space;
3879 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3880 return {PropertyPrecedence::Until};
3883EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3884 ps <<
"s_eventually" << PP::space;
3885 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3886 return {PropertyPrecedence::Qualifier};
3889EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3891 ps.scopedBox(PP::ibox2, [&] {
3892 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3893 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3897 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3898 return {PropertyPrecedence::Clocking};
3908class NameCollector {
3910 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3914 void collectNames(Block &block);
3916 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3917 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3920 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3921 ModuleEmitter &moduleEmitter;
3926 static constexpr size_t maxTypeWidthBound = 32;
3931void NameCollector::collectNames(Block &block) {
3934 for (
auto &op : block) {
3938 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3939 FuncCallProceduralOp, FuncCallOp>(op))
3941 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3945 for (
auto result : op.getResults()) {
3947 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3948 SmallString<16> typeString;
3952 llvm::raw_svector_ostream stringStream(typeString);
3954 stringStream, op.getLoc());
3956 if (typeString.size() <= maxTypeWidthBound)
3957 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3964 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3965 for (
auto ®ion : op.getRegions()) {
3966 if (!region.empty())
3967 collectNames(region.front());
3981class StmtEmitter :
public EmitterBase,
3989 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3991 void emitStatement(Operation *op);
3992 void emitStatementBlock(Block &body);
3995 LogicalResult emitDeclaration(Operation *op);
3998 void collectNamesAndCalculateDeclarationWidths(Block &block);
4001 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4002 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4003 bool isAssignmentLikeContext =
false);
4004 void emitSVAttributes(Operation *op);
4007 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4010 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4014 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4015 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4016 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4017 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4018 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4019 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4021 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4022 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4023 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4024 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4025 template <
typename Op>
4028 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4029 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4030 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4032 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4033 LogicalResult visitSV(
AssignOp op);
4034 LogicalResult visitSV(BPAssignOp op);
4035 LogicalResult visitSV(PAssignOp op);
4036 LogicalResult visitSV(ForceOp op);
4037 LogicalResult visitSV(ReleaseOp op);
4038 LogicalResult visitSV(AliasOp op);
4039 LogicalResult visitSV(InterfaceInstanceOp op);
4040 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4041 LogicalResult visitStmt(OutputOp op);
4043 LogicalResult visitStmt(InstanceOp op);
4044 LogicalResult visitStmt(InstanceChoiceOp op);
4045 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4046 ArrayRef<Value> instPortValues);
4051 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4052 LogicalResult visitSV(OrderedOutputOp op);
4053 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4054 LogicalResult visitSV(IfDefProceduralOp op) {
4055 return emitIfDef(op, op.getCond());
4057 LogicalResult visitSV(IfOp op);
4058 LogicalResult visitSV(AlwaysOp op);
4059 LogicalResult visitSV(AlwaysCombOp op);
4060 LogicalResult visitSV(AlwaysFFOp op);
4061 LogicalResult visitSV(InitialOp op);
4062 LogicalResult visitSV(CaseOp op);
4063 LogicalResult visitSV(FWriteOp op);
4064 LogicalResult visitSV(FFlushOp op);
4065 LogicalResult visitSV(VerbatimOp op);
4066 LogicalResult visitSV(MacroRefOp op);
4068 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4069 std::optional<unsigned> verbosity);
4070 LogicalResult visitSV(StopOp op);
4071 LogicalResult visitSV(FinishOp op);
4072 LogicalResult visitSV(ExitOp op);
4074 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4075 std::optional<unsigned> verbosity,
4077 ValueRange operands);
4078 LogicalResult visitSV(FatalOp op);
4079 LogicalResult visitSV(ErrorOp op);
4080 LogicalResult visitSV(WarningOp op);
4081 LogicalResult visitSV(InfoOp op);
4083 LogicalResult visitSV(ReadMemOp op);
4085 LogicalResult visitSV(GenerateOp op);
4086 LogicalResult visitSV(GenerateCaseOp op);
4088 LogicalResult visitSV(
ForOp op);
4090 void emitAssertionLabel(Operation *op);
4091 void emitAssertionMessage(StringAttr message, ValueRange args,
4092 SmallPtrSetImpl<Operation *> &ops,
4094 template <
typename Op>
4095 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4096 LogicalResult visitSV(AssertOp op);
4097 LogicalResult visitSV(AssumeOp op);
4098 LogicalResult visitSV(CoverOp op);
4099 template <
typename Op>
4100 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4101 LogicalResult visitSV(AssertConcurrentOp op);
4102 LogicalResult visitSV(AssumeConcurrentOp op);
4103 LogicalResult visitSV(CoverConcurrentOp op);
4104 template <
typename Op>
4105 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4106 LogicalResult visitSV(AssertPropertyOp op);
4107 LogicalResult visitSV(AssumePropertyOp op);
4108 LogicalResult visitSV(CoverPropertyOp op);
4110 LogicalResult visitSV(BindOp op);
4111 LogicalResult visitSV(InterfaceOp op);
4112 LogicalResult visitSV(InterfaceSignalOp op);
4113 LogicalResult visitSV(InterfaceModportOp op);
4114 LogicalResult visitSV(AssignInterfaceSignalOp op);
4115 LogicalResult visitSV(MacroErrorOp op);
4116 LogicalResult visitSV(MacroDefOp op);
4118 void emitBlockAsStatement(Block *block,
4119 const SmallPtrSetImpl<Operation *> &locationOps,
4120 StringRef multiLineComment = StringRef());
4122 LogicalResult visitSV(FuncDPIImportOp op);
4123 template <
typename CallOp>
4124 LogicalResult emitFunctionCall(CallOp callOp);
4125 LogicalResult visitSV(FuncCallProceduralOp op);
4126 LogicalResult visitSV(FuncCallOp op);
4127 LogicalResult visitSV(ReturnOp op);
4128 LogicalResult visitSV(IncludeOp op);
4131 ModuleEmitter &emitter;
4136 size_t maxDeclNameWidth = 0;
4137 size_t maxTypeWidth = 0;
4148void StmtEmitter::emitExpression(Value exp,
4149 SmallPtrSetImpl<Operation *> &emittedExprs,
4150 VerilogPrecedence parenthesizeIfLooserThan,
4151 bool isAssignmentLikeContext) {
4152 ExprEmitter(emitter, emittedExprs)
4153 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4158void StmtEmitter::emitSVAttributes(Operation *op) {
4166 setPendingNewline();
4169void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4170 llvm::function_ref<
void()> emitRHS,
4172 std::optional<PPExtString> wordBeforeLHS) {
4174 ps.scopedBox(PP::ibox2, [&]() {
4175 if (wordBeforeLHS) {
4176 ps << *wordBeforeLHS << PP::space;
4180 ps << PP::space << syntax << PP::space;
4182 ps.scopedBox(PP::ibox0, [&]() {
4189template <
typename Op>
4191StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4192 std::optional<PPExtString> wordBeforeLHS) {
4193 SmallPtrSet<Operation *, 8> ops;
4197 ps.addCallback({op,
true});
4198 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4200 emitExpression(op.getSrc(), ops, LowestPrecedence,
4205 ps.addCallback({op,
false});
4206 emitLocationInfoAndNewLine(ops);
4210LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4213 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4216 if (emitter.assignsInlined.count(op))
4220 emitSVAttributes(op);
4225LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4226 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4230 if (emitter.assignsInlined.count(op))
4234 emitSVAttributes(op);
4239LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4241 emitSVAttributes(op);
4246LogicalResult StmtEmitter::visitSV(ForceOp op) {
4248 emitError(op,
"SV attributes emission is unimplemented for the op");
4253LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4255 emitError(op,
"SV attributes emission is unimplemented for the op");
4258 SmallPtrSet<Operation *, 8> ops;
4260 ps.addCallback({op,
true});
4261 ps.scopedBox(PP::ibox2, [&]() {
4262 ps <<
"release" << PP::space;
4263 emitExpression(op.getDest(), ops);
4266 ps.addCallback({op,
false});
4267 emitLocationInfoAndNewLine(ops);
4271LogicalResult StmtEmitter::visitSV(AliasOp op) {
4273 emitError(op,
"SV attributes emission is unimplemented for the op");
4276 SmallPtrSet<Operation *, 8> ops;
4278 ps.addCallback({op,
true});
4279 ps.scopedBox(PP::ibox2, [&]() {
4280 ps <<
"alias" << PP::space;
4281 ps.scopedBox(PP::cbox0, [&]() {
4283 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4284 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4288 ps.addCallback({op,
false});
4289 emitLocationInfoAndNewLine(ops);
4293LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4294 auto doNotPrint = op.getDoNotPrint();
4295 if (doNotPrint && !state.options.emitBindComments)
4299 emitError(op,
"SV attributes emission is unimplemented for the op");
4302 StringRef prefix =
"";
4303 ps.addCallback({op,
true});
4306 ps <<
"// This interface is elsewhere emitted as a bind statement."
4310 SmallPtrSet<Operation *, 8> ops;
4313 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4314 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4315 "point to an interface");
4318 if (!prefix.empty())
4324 ps.addCallback({op,
false});
4325 emitLocationInfoAndNewLine(ops);
4333LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4335 SmallPtrSet<Operation *, 8> ops;
4336 size_t operandIndex = 0;
4337 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4338 for (
PortInfo port : ports.getOutputs()) {
4339 auto operand = op->getOperand(operandIndex);
4343 if (operand.hasOneUse() && operand.getDefiningOp() &&
4344 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4353 ps.addCallback({op,
true});
4355 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4357 ps <<
"// Zero width: ";
4360 ps <<
"assign" << PP::space;
4362 ps << PP::space <<
"=" << PP::space;
4363 ps.scopedBox(PP::ibox0, [&]() {
4367 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4368 ps <<
"/*Zero width*/";
4370 emitExpression(operand, ops, LowestPrecedence,
4375 ps.addCallback({op,
false});
4376 emitLocationInfoAndNewLine(ops);
4383LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4384 auto parent = op->getParentOfType<PortList>();
4386 return emitOutputLikeOp(op, ports);
4389LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4391 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4392 ps <<
"`ifndef " << typescopeDef << PP::newline;
4393 ps <<
"`define " << typescopeDef;
4394 setPendingNewline();
4395 emitStatementBlock(*op.getBodyBlock());
4397 ps <<
"`endif // " << typescopeDef;
4398 setPendingNewline();
4402LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4404 emitError(op,
"SV attributes emission is unimplemented for the op");
4409 ps << PP::neverbox <<
"// ";
4411 SmallPtrSet<Operation *, 8> ops;
4413 ps.scopedBox(PP::ibox2, [&]() {
4414 ps <<
"typedef" << PP::space;
4415 ps.invokeWithStringOS([&](
auto &os) {
4417 op.getAliasType(),
false);
4419 ps << PP::space <<
PPExtString(op.getPreferredName());
4420 ps.invokeWithStringOS(
4421 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4426 emitLocationInfoAndNewLine(ops);
4430template <
typename CallOpTy>
4431LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4435 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4437 SmallPtrSet<Operation *, 8> ops;
4441 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4442 if (explicitReturn) {
4443 assert(explicitReturn.hasOneUse());
4444 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4445 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4446 emitExpression(bpassignOp.getDest(), ops);
4448 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4449 ps <<
"assign" << PP::nbsp;
4450 emitExpression(assignOp.getDest(), ops);
4452 ps << PP::nbsp <<
"=" << PP::nbsp;
4455 auto arguments = callee.getPortList(
true);
4459 bool needsComma =
false;
4460 auto printArg = [&](Value value) {
4462 ps <<
"," << PP::space;
4463 emitExpression(value, ops);
4467 ps.scopedBox(PP::ibox0, [&] {
4468 unsigned inputIndex = 0, outputIndex = 0;
4469 for (
auto arg : arguments) {
4472 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4474 printArg(op.getInputs()[inputIndex++]);
4479 emitLocationInfoAndNewLine(ops);
4483LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4484 return emitFunctionCall(op);
4487LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4488 return emitFunctionCall(op);
4491template <
typename PPS>
4493 bool isAutomatic =
false,
4494 bool emitAsTwoStateType =
false) {
4495 ps <<
"function" << PP::nbsp;
4497 ps <<
"automatic" << PP::nbsp;
4498 auto retType = op.getExplicitlyReturnedType();
4500 ps.invokeWithStringOS([&](
auto &os) {
4501 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4502 emitAsTwoStateType);
4508 emitter.emitPortList(
4512LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4513 auto parent = op->getParentOfType<sv::FuncOp>();
4515 return emitOutputLikeOp(op, ports);
4518LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4520 ps <<
"`include" << PP::nbsp;
4522 if (op.getStyle() == IncludeStyle::System)
4523 ps <<
"<" << op.getTarget() <<
">";
4525 ps <<
"\"" << op.getTarget() <<
"\"";
4527 emitLocationInfo(op.getLoc());
4528 setPendingNewline();
4532LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4535 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4539 if (
auto linkageName = importOp.getLinkageName())
4540 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4542 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4543 assert(op.isDeclaration() &&
"function must be a declaration");
4546 assert(state.pendingNewline);
4552LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4554 emitError(op,
"SV attributes emission is unimplemented for the op");
4557 SmallPtrSet<Operation *, 8> ops;
4560 ps.addCallback({op,
true});
4562 if (
auto fd = op.getFd())
4563 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4566 ps.addCallback({op,
false});
4567 emitLocationInfoAndNewLine(ops);
4571LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4573 emitError(op,
"SV attributes emission is unimplemented for the op");
4576 SmallPtrSet<Operation *, 8> ops;
4579 ps.addCallback({op,
true});
4581 ps.scopedBox(PP::ibox0, [&]() {
4582 emitExpression(op.getFd(), ops);
4584 ps <<
"," << PP::space;
4585 ps.writeQuotedEscaped(op.getFormatString());
4593 for (
auto operand : op.getSubstitutions()) {
4594 ps <<
"," << PP::space;
4595 emitExpression(operand, ops);
4599 ps.addCallback({op,
false});
4600 emitLocationInfoAndNewLine(ops);
4604LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4606 emitError(op,
"SV attributes emission is unimplemented for the op");
4609 SmallPtrSet<Operation *, 8> ops;
4614 StringRef
string = op.getFormatString();
4615 if (
string.ends_with(
"\n"))
4616 string =
string.drop_back();
4621 bool isFirst =
true;
4624 while (!
string.
empty()) {
4625 auto lhsRhs =
string.split(
'\n');
4629 ps << PP::end << PP::newline << PP::neverbox;
4633 emitTextWithSubstitutions(
4634 ps, lhsRhs.first, op,
4635 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4636 string = lhsRhs.second;
4641 emitLocationInfoAndNewLine(ops);
4646LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4648 emitError(op,
"SV attributes emission is unimplemented for the op");
4652 SmallPtrSet<Operation *, 8> ops;
4657 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4658 assert(macroOp &&
"Invalid IR");
4660 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4662 if (!op.getInputs().empty()) {
4664 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4665 emitExpression(val, ops, LowestPrecedence,
4671 emitLocationInfoAndNewLine(ops);
4677StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4678 std::optional<unsigned> verbosity) {
4680 emitError(op,
"SV attributes emission is unimplemented for the op");
4683 SmallPtrSet<Operation *, 8> ops;
4685 ps.addCallback({op,
true});
4687 if (verbosity && *verbosity != 1) {
4689 ps.addAsString(*verbosity);
4693 ps.addCallback({op,
false});
4694 emitLocationInfoAndNewLine(ops);
4698LogicalResult StmtEmitter::visitSV(StopOp op) {
4699 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4702LogicalResult StmtEmitter::visitSV(FinishOp op) {
4703 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4707LogicalResult StmtEmitter::visitSV(ExitOp op) {
4708 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4714StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4715 std::optional<unsigned> verbosity,
4716 StringAttr message, ValueRange operands) {
4718 emitError(op,
"SV attributes emission is unimplemented for the op");
4721 SmallPtrSet<Operation *, 8> ops;
4723 ps.addCallback({op,
true});
4729 if ((verbosity && *verbosity != 1) || message) {
4731 ps.scopedBox(PP::ibox0, [&]() {
4735 ps.addAsString(*verbosity);
4740 ps <<
"," << PP::space;
4741 ps.writeQuotedEscaped(message.getValue());
4743 for (
auto operand : operands) {
4744 ps <<
"," << PP::space;
4745 emitExpression(operand, ops);
4754 ps.addCallback({op,
false});
4755 emitLocationInfoAndNewLine(ops);
4759LogicalResult StmtEmitter::visitSV(FatalOp op) {
4760 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4761 op.getMessageAttr(), op.getSubstitutions());
4764LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4765 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4766 op.getMessageAttr(), op.getSubstitutions());
4769LogicalResult StmtEmitter::visitSV(WarningOp op) {
4770 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4771 op.getMessageAttr(), op.getSubstitutions());
4774LogicalResult StmtEmitter::visitSV(InfoOp op) {
4775 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4776 op.getMessageAttr(), op.getSubstitutions());
4779LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4780 SmallPtrSet<Operation *, 8> ops({op});
4783 ps.addCallback({op,
true});
4785 switch (op.getBaseAttr().getValue()) {
4786 case MemBaseTypeAttr::MemBaseBin:
4789 case MemBaseTypeAttr::MemBaseHex:
4794 ps.scopedBox(PP::ibox0, [&]() {
4795 ps.writeQuotedEscaped(op.getFilename());
4796 ps <<
"," << PP::space;
4797 emitExpression(op.getDest(), ops);
4801 ps.addCallback({op,
false});
4802 emitLocationInfoAndNewLine(ops);
4806LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4807 emitSVAttributes(op);
4810 ps.addCallback({op,
true});
4811 ps <<
"generate" << PP::newline;
4813 setPendingNewline();
4814 emitStatementBlock(op.getBody().getBlocks().front());
4817 ps <<
"endgenerate";
4818 ps.addCallback({op,
false});
4819 setPendingNewline();
4823LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4824 emitSVAttributes(op);
4827 ps.addCallback({op,
true});
4829 ps.invokeWithStringOS([&](
auto &os) {
4830 emitter.printParamValue(
4831 op.getCond(), os, VerilogPrecedence::Selection,
4832 [&]() { return op->emitOpError(
"invalid case parameter"); });
4835 setPendingNewline();
4838 ArrayAttr
patterns = op.getCasePatterns();
4839 ArrayAttr caseNames = op.getCaseNames();
4840 MutableArrayRef<Region> regions = op.getCaseRegions();
4847 llvm::StringMap<size_t> nextGenIds;
4848 ps.scopedBox(PP::bbox2, [&]() {
4850 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4851 auto ®ion = regions[i];
4852 assert(region.hasOneBlock());
4853 Attribute patternAttr =
patterns[i];
4856 if (!isa<mlir::TypedAttr>(patternAttr))
4859 ps.invokeWithStringOS([&](
auto &os) {
4860 emitter.printParamValue(
4861 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4862 [&]() {
return op->emitOpError(
"invalid case value"); });
4865 StringRef legalName =
4866 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4869 setPendingNewline();
4870 emitStatementBlock(region.getBlocks().front());
4873 setPendingNewline();
4879 ps.addCallback({op,
false});
4880 setPendingNewline();
4884LogicalResult StmtEmitter::visitSV(
ForOp op) {
4885 emitSVAttributes(op);
4886 llvm::SmallPtrSet<Operation *, 8> ops;
4887 ps.addCallback({op,
true});
4889 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4892 ps.scopedBox(PP::cbox0, [&]() {
4896 ps <<
"logic" << PP::nbsp;
4897 ps.invokeWithStringOS([&](
auto &os) {
4898 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4903 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4908 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4909 [&]() { emitExpression(op.getUpperBound(), ops); },
4915 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4916 [&]() { emitExpression(op.getStep(), ops); },
4920 ps << PP::neverbreak;
4921 setPendingNewline();
4922 emitStatementBlock(op.getBody().getBlocks().front());
4925 ps.addCallback({op,
false});
4926 emitLocationInfoAndNewLine(ops);
4931void StmtEmitter::emitAssertionLabel(Operation *op) {
4932 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4938void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4939 SmallPtrSetImpl<Operation *> &ops,
4940 bool isConcurrent =
false) {
4943 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4944 ps.scopedBox(PP::ibox0, [&]() {
4945 ps.writeQuotedEscaped(message.getValue());
4947 for (
auto arg : args) {
4948 ps <<
"," << PP::space;
4949 emitExpression(arg, ops);
4955template <
typename Op>
4956LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4958 emitError(op,
"SV attributes emission is unimplemented for the op");
4961 SmallPtrSet<Operation *, 8> ops;
4963 ps.addCallback({op,
true});
4964 ps.scopedBox(PP::ibox2, [&]() {
4965 emitAssertionLabel(op);
4966 ps.scopedBox(PP::cbox0, [&]() {
4968 switch (op.getDefer()) {
4969 case DeferAssert::Immediate:
4971 case DeferAssert::Observed:
4974 case DeferAssert::Final:
4979 ps.scopedBox(PP::ibox0, [&]() {
4980 emitExpression(op.getExpression(), ops);
4983 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4987 ps.addCallback({op,
false});
4988 emitLocationInfoAndNewLine(ops);
4992LogicalResult StmtEmitter::visitSV(AssertOp op) {
4993 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4996LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4997 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5000LogicalResult StmtEmitter::visitSV(CoverOp op) {
5001 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5004template <
typename Op>
5005LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5007 emitError(op,
"SV attributes emission is unimplemented for the op");
5010 SmallPtrSet<Operation *, 8> ops;
5012 ps.addCallback({op,
true});
5013 ps.scopedBox(PP::ibox2, [&]() {
5014 emitAssertionLabel(op);
5015 ps.scopedBox(PP::cbox0, [&]() {
5016 ps << opName << PP::nbsp <<
"property (";
5017 ps.scopedBox(PP::ibox0, [&]() {
5018 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5020 emitExpression(op.getClock(), ops);
5021 ps <<
")" << PP::space;
5022 emitExpression(op.getProperty(), ops);
5025 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5030 ps.addCallback({op,
false});
5031 emitLocationInfoAndNewLine(ops);
5035LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5036 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5039LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5040 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5043LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5044 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5049template <
typename Op>
5050LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5052 emitError(op,
"SV attributes emission is unimplemented for the op");
5062 Operation *parent = op->getParentOp();
5063 Value
property = op.getProperty();
5064 bool isTemporal = !
property.getType().isSignlessInteger(1);
5065 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5066 bool emitAsImmediate = !isTemporal && isProcedural;
5069 SmallPtrSet<Operation *, 8> ops;
5071 ps.addCallback({op,
true});
5072 ps.scopedBox(PP::ibox2, [&]() {
5074 emitAssertionLabel(op);
5076 ps.scopedBox(PP::cbox0, [&]() {
5077 if (emitAsImmediate)
5078 ps << opName <<
"(";
5080 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5082 Value clock = op.getClock();
5083 auto event = op.getEvent();
5085 ps.scopedBox(PP::ibox2, [&]() {
5086 PropertyEmitter(emitter, ops)
5087 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5090 ps.scopedBox(PP::ibox2, [&]() {
5091 PropertyEmitter(emitter, ops)
5092 .emitAssertPropertyBody(property, op.getDisable());
5097 ps.addCallback({op,
false});
5098 emitLocationInfoAndNewLine(ops);
5102LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5103 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5106LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5107 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5110LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5111 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5114LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5116 emitError(op,
"SV attributes emission is unimplemented for the op");
5119 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5120 .getMacroIdentifier());
5123 bool hasEmptyThen = op->getRegion(0).front().empty();
5125 ps <<
"`ifndef " << ident;
5127 ps <<
"`ifdef " << ident;
5129 SmallPtrSet<Operation *, 8> ops;
5131 emitLocationInfoAndNewLine(ops);
5134 emitStatementBlock(op->getRegion(0).front());
5136 if (!op->getRegion(1).empty()) {
5137 if (!hasEmptyThen) {
5139 ps <<
"`else // " << ident;
5140 setPendingNewline();
5142 emitStatementBlock(op->getRegion(1).front());
5149 setPendingNewline();
5157void StmtEmitter::emitBlockAsStatement(
5158 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5159 StringRef multiLineComment) {
5166 emitLocationInfoAndNewLine(locationOps);
5169 emitStatementBlock(*block);
5171 if (needsBeginEnd) {
5175 if (!multiLineComment.empty())
5176 ps <<
" // " << multiLineComment;
5177 setPendingNewline();
5181LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5183 for (
auto &op : ooop.getBody().front())
5188LogicalResult StmtEmitter::visitSV(IfOp op) {
5189 SmallPtrSet<Operation *, 8> ops;
5191 auto ifcondBox = PP::ibox2;
5193 emitSVAttributes(op);
5195 ps.addCallback({op,
true});
5196 ps <<
"if (" << ifcondBox;
5206 emitExpression(ifOp.getCond(), ops);
5207 ps << PP::end <<
")";
5208 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5210 if (!ifOp.hasElse())
5214 Block *elseBlock = ifOp.getElseBlock();
5216 if (!nestedElseIfOp) {
5221 emitBlockAsStatement(elseBlock, ops);
5227 ifOp = nestedElseIfOp;
5228 ps <<
"else if (" << ifcondBox;
5230 ps.addCallback({op,
false});
5235LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5236 emitSVAttributes(op);
5237 SmallPtrSet<Operation *, 8> ops;
5241 auto printEvent = [&](AlwaysOp::Condition cond) {
5242 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5243 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5245 ps.addCallback({op,
true});
5247 switch (op.getNumConditions()) {
5253 printEvent(op.getCondition(0));
5258 ps.scopedBox(PP::cbox0, [&]() {
5259 printEvent(op.getCondition(0));
5260 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5261 ps << PP::space <<
"or" << PP::space;
5262 printEvent(op.getCondition(i));
5271 std::string comment;
5272 if (op.getNumConditions() == 0) {
5273 comment =
"always @*";
5275 comment =
"always @(";
5278 [&](Attribute eventAttr) {
5279 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5280 comment += stringifyEventControl(event);
5282 [&]() { comment +=
", "; });
5286 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5287 ps.addCallback({op,
false});
5291LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5292 emitSVAttributes(op);
5293 SmallPtrSet<Operation *, 8> ops;
5297 ps.addCallback({op,
true});
5298 StringRef opString =
"always_comb";
5299 if (state.options.noAlwaysComb)
5300 opString =
"always @(*)";
5303 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5304 ps.addCallback({op,
false});
5308LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5309 emitSVAttributes(op);
5311 SmallPtrSet<Operation *, 8> ops;
5315 ps.addCallback({op,
true});
5316 ps <<
"always_ff @(";
5317 ps.scopedBox(PP::cbox0, [&]() {
5318 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5319 emitExpression(op.getClock(), ops);
5320 if (op.getResetStyle() == ResetType::AsyncReset) {
5321 ps << PP::nbsp <<
"or" << PP::space
5322 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5323 emitExpression(op.getReset(), ops);
5330 std::string comment;
5331 comment +=
"always_ff @(";
5332 comment += stringifyEventControl(op.getClockEdge());
5333 if (op.getResetStyle() == ResetType::AsyncReset) {
5335 comment += stringifyEventControl(*op.getResetEdge());
5339 if (op.getResetStyle() == ResetType::NoReset)
5340 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5343 emitLocationInfoAndNewLine(ops);
5344 ps.scopedBox(PP::bbox2, [&]() {
5350 if (op.getResetStyle() == ResetType::AsyncReset &&
5351 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5353 emitExpression(op.getReset(), ops);
5355 emitBlockAsStatement(op.getResetBlock(), ops);
5358 emitBlockAsStatement(op.getBodyBlock(), ops);
5363 ps <<
" // " << comment;
5364 setPendingNewline();
5366 ps.addCallback({op,
false});
5370LogicalResult StmtEmitter::visitSV(InitialOp op) {
5371 emitSVAttributes(op);
5372 SmallPtrSet<Operation *, 8> ops;
5375 ps.addCallback({op,
true});
5377 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5378 ps.addCallback({op,
false});
5382LogicalResult StmtEmitter::visitSV(CaseOp op) {
5383 emitSVAttributes(op);
5384 SmallPtrSet<Operation *, 8> ops, emptyOps;
5387 ps.addCallback({op,
true});
5388 if (op.getValidationQualifier() !=
5389 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5390 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5391 op.getValidationQualifier()))
5393 const char *opname =
nullptr;
5394 switch (op.getCaseStyle()) {
5395 case CaseStmtType::CaseStmt:
5398 case CaseStmtType::CaseXStmt:
5401 case CaseStmtType::CaseZStmt:
5405 ps << opname <<
" (";
5406 ps.scopedBox(PP::ibox0, [&]() {
5407 emitExpression(op.getCond(), ops);
5410 emitLocationInfoAndNewLine(ops);
5412 size_t caseValueIndex = 0;
5413 ps.scopedBox(PP::bbox2, [&]() {
5414 for (
auto &caseInfo : op.getCases()) {
5416 auto &
pattern = caseInfo.pattern;
5418 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5419 .Case<CaseBitPattern>([&](
auto bitPattern) {
5422 ps.invokeWithStringOS([&](
auto &os) {
5423 os << bitPattern->getWidth() <<
"'b";
5424 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5425 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5428 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5429 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5430 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5432 .Case<CaseExprPattern>([&](
auto) {
5433 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5435 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5436 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5439 emitBlockAsStatement(caseInfo.block, emptyOps);
5445 ps.addCallback({op,
false});
5446 emitLocationInfoAndNewLine(ops);
5450LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5451 bool doNotPrint = op.getDoNotPrint();
5452 if (doNotPrint && !state.options.emitBindComments)
5457 emitSVAttributes(op);
5459 ps.addCallback({op,
true});
5462 <<
"/* This instance is elsewhere emitted as a bind statement."
5465 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5466 "attributes. The attributes will not be emitted.";
5469 SmallPtrSet<Operation *, 8> ops;
5474 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5475 assert(moduleOp &&
"Invalid IR");
5479 if (!op.getParameters().empty()) {
5482 bool printed =
false;
5484 llvm::zip(op.getParameters(),
5485 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5486 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5487 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5489 if (param.getValue() == modParam.getValue())
5494 ps <<
" #(" << PP::bbox2 << PP::newline;
5497 ps <<
"," << PP::newline;
5501 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5503 ps.invokeWithStringOS([&](
auto &os) {
5504 emitter.printParamValue(param.getValue(), os, [&]() {
5505 return op->emitOpError(
"invalid instance parameter '")
5506 << param.getName().getValue() <<
"' value";
5512 ps << PP::end << PP::newline <<
")";
5519 SmallVector<Value> instPortValues(modPortInfo.size());
5520 op.getValues(instPortValues, modPortInfo);
5521 emitInstancePortList(op, modPortInfo, instPortValues);
5523 ps.addCallback({op,
false});
5524 emitLocationInfoAndNewLine(ops);
5529 setPendingNewline();
5534LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5536 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5537 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5542 Operation *defaultModuleOp =
5543 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5545 SmallVector<Value> instPortValues(modPortInfo.size());
5546 op.getValues(instPortValues, modPortInfo);
5547 emitInstancePortList(op, modPortInfo, instPortValues);
5549 SmallPtrSet<Operation *, 8> ops;
5551 ps.addCallback({op,
false});
5552 emitLocationInfoAndNewLine(ops);
5557void StmtEmitter::emitInstancePortList(Operation *op,
5559 ArrayRef<Value> instPortValues) {
5560 SmallPtrSet<Operation *, 8> ops;
5563 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5564 ModulePortInfo containingPortList(containingModule.getPortList());
5569 size_t maxNameLength = 0;
5570 for (
auto &elt : modPortInfo) {
5571 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5574 auto getWireForValue = [&](Value result) {
5575 return result.getUsers().begin()->getOperand(0);
5579 bool isFirst =
true;
5580 bool isZeroWidth =
false;
5582 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5584 auto &modPort = modPortInfo.
at(portNum);
5586 Value portVal = instPortValues[portNum];
5591 bool shouldPrintComma =
true;
5593 shouldPrintComma =
false;
5594 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5596 shouldPrintComma =
true;
5601 if (shouldPrintComma)
5604 emitLocationInfoAndNewLine(ops);
5619 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5620 auto modPortName = modPort.getVerilogName();
5622 ps.spaces(maxNameLength - modPortName.size() + 1);
5624 ps.scopedBox(PP::ibox0, [&]() {
5631 if (!modPort.isOutput()) {
5633 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5634 ps <<
"/* Zero width */";
5636 emitExpression(portVal, ops, LowestPrecedence);
5637 }
else if (portVal.use_empty()) {
5638 ps <<
"/* unused */";
5639 }
else if (portVal.hasOneUse() &&
5640 (output = dyn_cast_or_null<OutputOp>(
5641 portVal.getUses().begin()->getOwner()))) {
5646 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5648 containingPortList.atOutput(outputPortNo).getVerilogName());
5650 portVal = getWireForValue(portVal);
5651 emitExpression(portVal, ops);
5657 if (!isFirst || isZeroWidth) {
5658 emitLocationInfoAndNewLine(ops);
5671LogicalResult StmtEmitter::visitSV(BindOp op) {
5672 emitter.emitBind(op);
5673 assert(state.pendingNewline);
5677LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5678 emitComment(op.getCommentAttr());
5680 emitSVAttributes(op);
5683 ps.addCallback({op,
true});
5685 setPendingNewline();
5687 emitStatementBlock(*op.getBodyBlock());
5689 ps <<
"endinterface" << PP::newline;
5690 ps.addCallback({op,
false});
5691 setPendingNewline();
5695LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5697 emitSVAttributes(op);
5699 ps.addCallback({op,
true});
5701 ps << PP::neverbox <<
"// ";
5702 ps.invokeWithStringOS([&](
auto &os) {
5707 ps.invokeWithStringOS(
5708 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5712 ps.addCallback({op,
false});
5713 setPendingNewline();
5717LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5719 ps.addCallback({op,
true});
5723 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5724 auto port = cast<ModportStructAttr>(portAttr);
5725 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5726 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5727 ps << PPExtString(getSymOpName(signalDecl));
5731 ps.addCallback({op,
false});
5732 setPendingNewline();
5736LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5738 ps.addCallback({op,
true});
5739 SmallPtrSet<Operation *, 8> emitted;
5742 emitExpression(op.getIface(), emitted);
5743 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5744 emitExpression(op.getRhs(), emitted);
5746 ps.addCallback({op,
false});
5747 setPendingNewline();
5751LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5753 ps <<
"`" << op.getMacroIdentifier();
5754 setPendingNewline();
5758LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5759 auto decl = op.getReferencedMacro(&state.symbolCache);
5762 ps.addCallback({op,
true});
5764 if (decl.getArgs()) {
5766 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5767 ps << cast<StringAttr>(name);
5771 if (!op.getFormatString().empty()) {
5773 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5776 ps.addCallback({op,
false});
5777 setPendingNewline();
5781void StmtEmitter::emitStatement(Operation *op) {
5788 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5792 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5793 succeeded(dispatchVerifVisitor(op)))
5796 emitOpError(op,
"emission to Verilog not supported");
5797 emitPendingNewlineIfNeeded();
5798 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5799 setPendingNewline();
5810 StmtEmitter &stmtEmitter) {
5817 if (isa<IfDefProceduralOp>(op->getParentOp()))
5825 SmallVector<Value, 8> exprsToScan(op->getOperands());
5830 while (!exprsToScan.empty()) {
5831 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5838 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5839 auto *defOp = readInout.getOperand().getDefiningOp();
5846 if (isa<sv::WireOp>(defOp))
5851 if (!isa<RegOp, LogicOp>(defOp))
5857 if (isa<LogicOp>(defOp) &&
5858 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5862 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5863 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5871 exprsToScan.append(expr->getOperands().begin(),
5872 expr->getOperands().end());
5878 if (expr->getBlock() != op->getBlock())
5883 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5890template <
class AssignTy>
5892 AssignTy singleAssign;
5893 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5894 if (hasSVAttributes(user))
5897 if (auto assign = dyn_cast<AssignTy>(user)) {
5900 singleAssign = assign;
5904 return isa<ReadInOutOp>(user);
5906 return singleAssign;
5912 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5916 if (op1->getBlock() != user->getBlock())
5922 return op1->isBeforeInBlock(user);
5926LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5927 emitSVAttributes(op);
5928 auto value = op->getResult(0);
5929 SmallPtrSet<Operation *, 8> opsForLocation;
5930 opsForLocation.insert(op);
5932 ps.addCallback({op,
true});
5935 auto type = value.getType();
5941 bool singleBitDefaultType = !isa<LocalParamOp>(op);
5943 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5944 unsigned targetColumn = 0;
5945 unsigned column = 0;
5948 if (maxDeclNameWidth > 0)
5949 targetColumn += maxDeclNameWidth + 1;
5952 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5953 }
else if (!word.empty()) {
5955 column += word.size();
5956 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5957 ps.spaces(numSpaces);
5958 column += numSpaces;
5961 SmallString<8> typeString;
5964 llvm::raw_svector_ostream stringStream(typeString);
5967 true, singleBitDefaultType);
5970 if (maxTypeWidth > 0)
5971 targetColumn += maxTypeWidth + 1;
5972 unsigned numSpaces = 0;
5973 if (!typeString.empty()) {
5975 column += typeString.size();
5978 if (targetColumn > column)
5979 numSpaces = targetColumn - column;
5980 ps.spaces(numSpaces);
5981 column += numSpaces;
5987 ps.invokeWithStringOS(
5988 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5991 if (state.options.printDebugInfo) {
5992 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5993 auto innerSym = innerSymOp.getInnerSymAttr();
5994 if (innerSym && !innerSym.empty()) {
5996 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6002 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6003 ps << PP::space <<
"=" << PP::space;
6004 ps.invokeWithStringOS([&](
auto &os) {
6005 emitter.printParamValue(localparam.getValue(), os, [&]() {
6006 return op->emitOpError(
"invalid localparam value");
6011 if (
auto regOp = dyn_cast<RegOp>(op)) {
6012 if (
auto initValue = regOp.getInit()) {
6013 ps << PP::space <<
"=" << PP::space;
6014 ps.scopedBox(PP::ibox0, [&]() {
6015 emitExpression(initValue, opsForLocation, LowestPrecedence,
6024 if (isa<sv::WireOp>(op) &&
6025 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6028 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6029 auto *source = singleAssign.getSrc().getDefiningOp();
6033 if (!source || isa<ConstantOp>(source) ||
6034 op->getNextNode() == singleAssign) {
6035 ps << PP::space <<
"=" << PP::space;
6036 ps.scopedBox(PP::ibox0, [&]() {
6037 emitExpression(singleAssign.getSrc(), opsForLocation,
6041 emitter.assignsInlined.insert(singleAssign);
6049 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
6052 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6055 auto *source = singleAssign.getSrc().getDefiningOp();
6059 if (!source || isa<ConstantOp>(source) ||
6062 ps << PP::space <<
"=" << PP::space;
6063 ps.scopedBox(PP::ibox0, [&]() {
6064 emitExpression(singleAssign.getSrc(), opsForLocation,
6069 emitter.assignsInlined.insert(singleAssign);
6070 emitter.expressionsEmittedIntoDecl.insert(op);
6077 ps.addCallback({op,
false});
6078 emitLocationInfoAndNewLine(opsForLocation);
6082void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6085 NameCollector collector(emitter);
6086 collector.collectNames(block);
6089 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6090 maxTypeWidth = collector.getMaxTypeWidth();
6093void StmtEmitter::emitStatementBlock(Block &body) {
6094 ps.scopedBox(PP::bbox2, [&]() {
6099 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6100 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6105 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6106 collectNamesAndCalculateDeclarationWidths(body);
6109 for (
auto &op : body) {
6116void ModuleEmitter::emitStatement(Operation *op) {
6117 StmtEmitter(*
this, state.options).emitStatement(op);
6122void ModuleEmitter::emitSVAttributes(Operation *op) {
6130 setPendingNewline();
6137void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6138 auto verilogName =
module.getVerilogModuleNameAttr();
6140 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6142 setPendingNewline();
6151void ModuleEmitter::emitBind(BindOp op) {
6153 emitError(op,
"SV attributes emission is unimplemented for the op");
6154 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6160 Operation *childMod =
6161 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6165 ps.addCallback({op,
true});
6166 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6167 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6169 bool isFirst =
true;
6170 ps.scopedBox(PP::bbox2, [&]() {
6171 auto parentPortInfo = parentMod.getPortList();
6175 size_t maxNameLength = 0;
6176 for (
auto &elt : childPortInfo) {
6177 auto portName = elt.getVerilogName();
6178 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6179 maxNameLength = std::max(maxNameLength, elt.getName().size());
6182 SmallVector<Value> instPortValues(childPortInfo.size());
6183 inst.getValues(instPortValues, childPortInfo);
6185 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6187 Value portVal = instPortValues[idx];
6193 bool shouldPrintComma =
true;
6195 shouldPrintComma =
false;
6196 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6198 shouldPrintComma =
true;
6203 if (shouldPrintComma)
6216 ps << PP::neverbox <<
"//";
6220 ps.nbsp(maxNameLength - elt.getName().size());
6222 llvm::SmallPtrSet<Operation *, 4> ops;
6223 if (elt.isOutput()) {
6224 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6225 "output port must have either single or no use");
6226 if (portVal.use_empty()) {
6227 ps <<
"/* unused */";
6228 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6229 portVal.getUses().begin()->getOwner())) {
6232 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6234 parentPortList.atOutput(outputPortNo).getVerilogName());
6236 portVal = portVal.getUsers().begin()->getOperand(0);
6237 ExprEmitter(*
this, ops)
6238 .emitExpression(portVal, LowestPrecedence,
6242 ExprEmitter(*
this, ops)
6243 .emitExpression(portVal, LowestPrecedence,
6256 ps.addCallback({op,
false});
6257 setPendingNewline();
6260void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6262 emitError(op,
"SV attributes emission is unimplemented for the op");
6264 auto instance = op.getReferencedInstance(&state.symbolCache);
6266 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6267 instance.getInterfaceType().getInterface());
6269 ps.addCallback({op,
true});
6270 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6271 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6273 ps.addCallback({op,
false});
6274 setPendingNewline();
6277void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6281 auto printParamType = [&](Type type, Attribute defaultValue,
6282 SmallString<8> &result) {
6284 llvm::raw_svector_ostream sstream(result);
6289 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6290 if (intAttr.getValue().getBitWidth() == 32)
6292 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6293 if (fpAttr.getType().isF64())
6296 if (isa<NoneType>(type))
6303 if (
auto intType = type_dyn_cast<IntegerType>(type))
6304 if (intType.getWidth() == 32) {
6305 sstream <<
"/*integer*/";
6309 printPackedType(type, sstream, module->getLoc(),
6317 size_t maxTypeWidth = 0;
6318 SmallString<8> scratch;
6319 for (
auto param : params) {
6320 auto paramAttr = cast<ParamDeclAttr>(param);
6322 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6323 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6326 if (maxTypeWidth > 0)
6329 ps.scopedBox(PP::bbox2, [&]() {
6330 ps << PP::newline <<
"#(";
6331 ps.scopedBox(PP::cbox0, [&]() {
6334 [&](Attribute param) {
6335 auto paramAttr = cast<ParamDeclAttr>(param);
6336 auto defaultValue = paramAttr.getValue();
6338 printParamType(paramAttr.getType(), defaultValue, scratch);
6339 if (!scratch.empty())
6341 if (scratch.size() < maxTypeWidth)
6342 ps.nbsp(maxTypeWidth - scratch.size());
6344 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6345 module, paramAttr.getName()));
6349 ps.invokeWithStringOS([&](
auto &os) {
6351 return module->emitError("parameter '")
6352 << paramAttr.getName().getValue()
6353 << "' has invalid value";
6358 [&]() { ps <<
"," << PP::newline; });
6364void ModuleEmitter::emitPortList(Operation *module,
6366 bool emitAsTwoStateType) {
6368 if (portInfo.
size())
6369 emitLocationInfo(module->getLoc());
6373 bool hasOutputs =
false, hasZeroWidth =
false;
6374 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6375 SmallVector<SmallString<8>, 16> portTypeStrings;
6377 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6378 auto port = portInfo.
at(i);
6382 lastNonZeroPort = i;
6385 portTypeStrings.push_back({});
6387 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6389 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6392 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6395 if (maxTypeWidth > 0)
6399 ps.scopedBox(PP::bbox2, [&]() {
6400 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6401 auto lastPort = e - 1;
6404 auto portType = portInfo.
at(portIdx).
type;
6408 bool isZeroWidth =
false;
6413 ps << (isZeroWidth ?
"// " :
" ");
6417 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6418 switch (thisPortDirection) {
6419 case ModulePort::Direction::Output:
6422 case ModulePort::Direction::Input:
6423 ps << (hasOutputs ?
"input " :
"input ");
6425 case ModulePort::Direction::InOut:
6426 ps << (hasOutputs ?
"inout " :
"inout ");
6429 bool emitWireInPorts = state.options.emitWireInPorts;
6430 if (emitWireInPorts)
6434 if (!portTypeStrings[portIdx].
empty())
6435 ps << portTypeStrings[portIdx];
6436 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6437 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6439 size_t startOfNamePos =
6440 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6446 ps.invokeWithStringOS(
6447 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6450 auto innerSym = portInfo.
at(portIdx).
getSym();
6451 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6453 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6458 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6462 if (
auto loc = portInfo.
at(portIdx).
loc)
6463 emitLocationInfo(loc);
6473 if (!state.options.disallowPortDeclSharing) {
6474 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6477 auto port = portInfo.
at(portIdx);
6481 bool isZeroWidth =
false;
6486 ps << (isZeroWidth ?
"// " :
" ");
6489 ps.nbsp(startOfNamePos);
6492 StringRef name = port.getVerilogName();
6496 ps.invokeWithStringOS(
6497 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6500 auto sym = port.getSym();
6501 if (state.options.printDebugInfo && sym && !sym.empty())
6502 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6506 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6510 if (
auto loc = port.loc)
6511 emitLocationInfo(loc);
6522 if (!portInfo.
size()) {
6524 SmallPtrSet<Operation *, 8> moduleOpSet;
6525 moduleOpSet.insert(module);
6526 emitLocationInfoAndNewLine(moduleOpSet);
6529 ps <<
");" << PP::newline;
6530 setPendingNewline();
6534void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6535 currentModuleOp =
module;
6537 emitComment(module.getCommentAttr());
6538 emitSVAttributes(module);
6540 ps.addCallback({module,
true});
6544 emitParameters(module, module.getParameters());
6548 assert(state.pendingNewline);
6551 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6554 ps.addCallback({module,
false});
6556 setPendingNewline();
6558 currentModuleOp =
nullptr;
6561void ModuleEmitter::emitFunc(FuncOp func) {
6563 if (func.isDeclaration())
6566 currentModuleOp = func;
6568 ps.addCallback({func,
true});
6572 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6574 ps <<
"endfunction";
6576 currentModuleOp =
nullptr;
6585 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6592 void emit(emit::FileListOp op);
6595 void emit(Block *block);
6597 void emitOp(emit::RefOp op);
6598 void emitOp(emit::VerbatimOp op);
6602 for (Operation &op : *block) {
6603 TypeSwitch<Operation *>(&op)
6604 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6605 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6606 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6607 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6608 .Case<BindInterfaceOp>(
6609 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6610 .Case<TypeScopeOp>([&](
auto typedecls) {
6611 ModuleEmitter(state).emitStatement(typedecls);
6614 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6620 for (
auto sym : op.getFiles()) {
6621 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6623 auto it = state.fileMapping.find(fileName);
6624 if (it == state.fileMapping.end()) {
6625 emitOpError(op,
" references an invalid file: ") << sym;
6629 auto file = cast<emit::FileOp>(it->second);
6630 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6637 StringAttr target = op.getTargetAttr().getAttr();
6638 auto *targetOp = state.symbolCache.getDefinition(target);
6639 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6641 TypeSwitch<Operation *>(targetOp)
6642 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6643 .Case<hw::HWModuleOp>(
6644 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6645 .Case<TypeScopeOp>([&](
auto typedecls) {
6646 ModuleEmitter(state).emitStatement(typedecls);
6649 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6655 SmallPtrSet<Operation *, 8> ops;
6660 StringRef text = op.getText();
6664 const auto &[lhs, rhs] = text.split(
'\n');
6668 ps << PP::end << PP::newline << PP::neverbox;
6670 }
while (!text.empty());
6673 emitLocationInfoAndNewLine(ops);
6691 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6692 moduleOp->walk([&](Operation *op) {
6694 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6697 SymbolTable::getSymbolAttrName()),
6698 name.getSymName(), op);
6699 if (isa<BindOp>(op))
6705 auto collectPorts = [&](
auto moduleOp) {
6706 auto portInfo = moduleOp.getPortList();
6707 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6708 if (!p.attrs || p.attrs.empty())
6710 for (NamedAttribute portAttr : p.attrs) {
6711 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6720 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6721 for (
auto file :
designOp.getOps<emit::FileOp>())
6722 for (
auto refs : file.getOps<emit::RefOp>())
6723 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6725 SmallString<32> outputPath;
6726 for (
auto &op : *
designOp.getBody()) {
6729 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6731 bool hasFileName =
false;
6732 bool emitReplicatedOps = !isFileOp;
6733 bool addToFilelist = !isFileOp;
6739 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6741 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6742 <<
" on " << op <<
"\n";);
6743 if (!attr.isDirectory())
6746 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6747 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6750 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6755 if (!defaultFileName.isTriviallyEmpty()) {
6756 llvm::sys::path::append(outputPath, defaultFileName);
6758 op->emitError(
"file name unspecified");
6760 llvm::sys::path::append(outputPath,
"error.out");
6764 auto destFile = StringAttr::get(op->getContext(), outputPath);
6765 auto &file =
files[destFile];
6766 file.ops.push_back(info);
6767 file.emitReplicatedOps = emitReplicatedOps;
6768 file.addToFilelist = addToFilelist;
6769 file.isVerilog = outputPath.ends_with(
".sv");
6774 if (!attr || attr.isDirectory()) {
6775 auto excludeFromFileListAttr =
6776 BoolAttr::get(op->getContext(), !addToFilelist);
6777 auto includeReplicatedOpsAttr =
6778 BoolAttr::get(op->getContext(), emitReplicatedOps);
6779 auto outputFileAttr = hw::OutputFileAttr::get(
6780 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6781 op->setAttr(
"output_file", outputFileAttr);
6787 TypeSwitch<Operation *>(&op)
6788 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6790 fileMapping.try_emplace(file.getSymNameAttr(), file);
6791 separateFile(file, file.getFileName());
6793 .Case<emit::FragmentOp>([&](
auto fragment) {
6796 .Case<HWModuleOp>([&](
auto mod) {
6798 auto sym = mod.getNameAttr();
6801 collectInstanceSymbolsAndBinds(mod);
6803 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6804 if (it->second.size() != 1 || attr) {
6807 op.emitError(
"modules can be emitted to a single file");
6815 if (attr || separateModules)
6821 .Case<InterfaceOp>([&](InterfaceOp intf) {
6826 for (
auto &op : *intf.getBodyBlock())
6827 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6828 if (
auto name = symOp.getNameAttr())
6832 if (attr || separateModules)
6833 separateFile(intf, intf.getSymName() +
".sv");
6843 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6844 [&](Operation *op) {
6850 separateFile(op,
"");
6852 .Case<FuncOp>([&](
auto op) {
6858 separateFile(op,
"");
6862 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6865 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6874 separateFile(op,
"");
6876 .Case<BindOp>([&](
auto op) {
6878 separateFile(op,
"bindfile.sv");
6883 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
6884 .Case<MacroDeclOp>([&](
auto op) {
6887 .Case<sv::ReserveNamesOp>([](
auto op) {
6890 .Case<om::ClassLike>([&](
auto op) {
6893 .Case<om::ConstantOp>([&](
auto op) {
6896 .Default([&](
auto *) {
6897 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6917 size_t lastReplicatedOp = 0;
6919 bool emitHeaderInclude =
6922 if (emitHeaderInclude)
6925 size_t numReplicatedOps =
6930 DenseSet<emit::FragmentOp> includedFragments;
6931 for (
const auto &opInfo : file.
ops) {
6932 Operation *op = opInfo.op;
6936 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6942 if (
auto fragments =
6944 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6948 op->emitError(
"cannot find referenced fragment ") << sym;
6951 emit::FragmentOp fragment = it->second;
6952 if (includedFragments.insert(fragment).second) {
6953 thingsToEmit.emplace_back(it->second);
6959 thingsToEmit.emplace_back(op);
6964 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6969 TypeSwitch<Operation *>(op)
6970 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6971 .Case<HWModuleExternOp>([&](
auto op) {
6974 .Case<HWModuleGeneratedOp>(
6975 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6976 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6977 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6978 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6979 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6980 .Case<TypeScopeOp>([&](
auto typedecls) {
6981 ModuleEmitter(state).emitStatement(typedecls);
6983 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6985 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
6986 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6987 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6988 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6989 .Default([&](
auto *op) {
6990 state.encounteredError =
true;
6991 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6998 llvm::formatted_raw_ostream &os,
6999 StringAttr fileName,
bool parallelize) {
7000 MLIRContext *context =
designOp->getContext();
7004 parallelize &= context->isMultithreadingEnabled();
7015 size_t lineOffset = 0;
7016 for (
auto &entry : thingsToEmit) {
7017 entry.verilogLocs.setStream(os);
7018 if (
auto *op = entry.getOperation()) {
7023 state.addVerilogLocToOps(lineOffset, fileName);
7025 os << entry.getStringData();
7030 if (state.encounteredError)
7048 SmallString<256> buffer;
7049 llvm::raw_svector_ostream tmpStream(buffer);
7050 llvm::formatted_raw_ostream rs(tmpStream);
7061 for (
auto &entry : thingsToEmit) {
7064 auto *op = entry.getOperation();
7066 auto lineOffset = os.getLine() + 1;
7067 os << entry.getStringData();
7071 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7074 entry.verilogLocs.setStream(os);
7081 state.addVerilogLocToOps(0, fileName);
7097 module.emitWarning()
7098 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7099 "created only at SplitExportVerilog";
7108 for (
const auto &it : emitter.
files) {
7109 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7110 "\" ----- 8< -----\n\n");
7116 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7117 "\" ----- 8< -----\n\n");
7118 for (
auto &name : it.second)
7119 contents += name.str() +
"\n";
7120 list.emplace_back(contents);
7123 llvm::formatted_raw_ostream rs(os);
7127 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7136 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7138 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7139 if (failed(failableParallelForEach(
7140 module->getContext(), modulesToPrepare,
7141 [&](
auto op) { return prepareHWModule(op, options); })))
7148struct ExportVerilogPass
7149 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7150 ExportVerilogPass(raw_ostream &os) : os(os) {}
7151 void runOnOperation()
override {
7153 mlir::OpPassManager preparePM(
"builtin.module");
7154 preparePM.addPass(createLegalizeAnonEnums());
7156 auto &modulePM = preparePM.nestAny();
7157 modulePM.addPass(createPrepareForEmission());
7158 if (failed(runPipeline(preparePM, getOperation())))
7159 return signalPassFailure();
7162 return signalPassFailure();
7169struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7170 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7171 : ExportVerilogPass{*os} {
7172 owned = std::move(os);
7176 std::unique_ptr<llvm::raw_ostream> owned;
7180std::unique_ptr<mlir::Pass>
7182 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7185std::unique_ptr<mlir::Pass>
7187 return std::make_unique<ExportVerilogPass>(os);
7198static std::unique_ptr<llvm::ToolOutputFile>
7202 SmallString<128> outputFilename(dirname);
7204 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7207 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7209 emitter.
designOp.emitError(
"cannot create output directory \"")
7210 << outputDir <<
"\": " << error.message();
7216 std::string errorMessage;
7217 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7219 emitter.
designOp.emitError(errorMessage);
7236 llvm::formatted_raw_ostream rs(output->os());
7242 StringAttr::get(fileName.getContext(), output->getFilename()),
7248 StringRef dirname) {
7259 bool insertSuccess =
7261 .insert({StringAttr::get(module.getContext(),
circtHeader),
7267 if (!insertSuccess) {
7268 module.emitError() << "tried to emit a heder to " << circtHeader
7269 << ", but the file is used as an output too.";
7275 parallelForEach(module->getContext(), emitter.
files.begin(),
7276 emitter.
files.end(), [&](
auto &it) {
7277 createSplitOutputFile(it.first, it.second, dirname,
7282 SmallString<128> filelistPath(dirname);
7283 llvm::sys::path::append(filelistPath,
"filelist.f");
7285 std::string errorMessage;
7286 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7288 module->emitError(errorMessage);
7292 for (
const auto &it : emitter.
files) {
7293 if (it.second.addToFilelist)
7294 output->os() << it.first.str() <<
"\n";
7303 for (
auto &name : it.second)
7304 output->os() << name.str() <<
"\n";
7315 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7317 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7318 if (failed(failableParallelForEach(
7319 module->getContext(), modulesToPrepare,
7320 [&](
auto op) { return prepareHWModule(op, options); })))
7328struct ExportSplitVerilogPass
7329 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7330 ExportSplitVerilogPass(StringRef directory) {
7331 directoryName = directory.str();
7333 void runOnOperation()
override {
7335 mlir::OpPassManager preparePM(
"builtin.module");
7339 modulePM.addPass(createPrepareForEmission());
7340 if (failed(runPipeline(preparePM, getOperation())))
7341 return signalPassFailure();
7344 return signalPassFailure();
7349std::unique_ptr<mlir::Pass>
7351 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static bool hasSVAttributes(Operation *op)
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc)
Push this type's dimension into a vector.
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static bool haveMatchingDims(Type a, Type b, Location loc)
True iff 'a' and 'b' have the same wire dims.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
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=~0ULL)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
This helps visit TypeOp nodes.
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module)
Generates the macros used by instance choices.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
PP
Send one of these to TokenStream to add the corresponding token.
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoices()
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.