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, [&]() {
2171 emitSubExpr(exp, parenthesizeIfLooserThan,
2174 isAssignmentLikeContext);
2179 if (&buffer.tokens == &localTokens)
2180 buffer.flush(state.pp);
2185 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2186 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2188 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2196 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2197 SubExprSignRequirement signReq = NoRequirement,
2198 bool isSelfDeterminedUnsignedValue =
false,
2199 bool isAssignmentLikeContext =
false);
2203 void emitSVAttributes(Operation *op);
2205 SubExprInfo visitUnhandledExpr(Operation *op);
2206 SubExprInfo visitInvalidComb(Operation *op) {
2209 SubExprInfo visitUnhandledComb(Operation *op) {
2210 return visitUnhandledExpr(op);
2213 return dispatchSVVisitor(op);
2216 return visitUnhandledExpr(op);
2218 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2221 enum EmitBinaryFlags {
2222 EB_RequireSignedOperands = RequireSigned,
2223 EB_RequireUnsignedOperands = RequireUnsigned,
2224 EB_OperandSignRequirementMask = 0x3,
2229 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2233 EB_ForceResultSigned = 0x8,
2238 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2239 const char *syntax,
unsigned emitBinaryFlags = 0);
2241 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2242 bool resultAlwaysUnsigned =
false);
2245 void emitSubExprIBox2(
2246 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2247 ps.scopedBox(PP::ibox2,
2248 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2253 template <
typename Container,
typename EachFn>
2254 void interleaveComma(
const Container &c, EachFn eachFn) {
2255 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2260 void interleaveComma(ValueRange ops) {
2261 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2278 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2280 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2281 CloseFunc closeFn) {
2283 ps.scopedBox(PP::cbox0, [&]() {
2284 interleaveComma(c, eachFn);
2290 template <
typename OpenFunc,
typename CloseFunc>
2291 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2292 return emitBracedList(
2293 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2297 void emitBracedList(ValueRange ops) {
2298 return emitBracedList(
2299 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2303 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2306 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2307 bool printAsPattern, Operation *op);
2309 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2310 ArrayAttr fieldValues,
bool printAsPattern,
2313 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2315 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2316 SubExprInfo visitSV(GetModportOp op);
2317 SubExprInfo visitSV(SystemFunctionOp op);
2318 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2319 SubExprInfo visitSV(XMROp op);
2320 SubExprInfo visitSV(SFormatFOp op);
2321 SubExprInfo visitSV(XMRRefOp op);
2322 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2323 SubExprInfo visitSV(VerbatimExprOp op) {
2324 return visitVerbatimExprOp(op, op.getSymbols());
2326 SubExprInfo visitSV(VerbatimExprSEOp op) {
2327 return visitVerbatimExprOp(op, op.getSymbols());
2329 SubExprInfo visitSV(MacroRefExprOp op);
2330 SubExprInfo visitSV(MacroRefExprSEOp op);
2331 template <
typename MacroTy>
2332 SubExprInfo emitMacroCall(MacroTy op);
2334 SubExprInfo visitSV(ConstantXOp op);
2335 SubExprInfo visitSV(ConstantZOp op);
2336 SubExprInfo visitSV(ConstantStrOp op);
2338 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2339 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2341 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2346 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2347 emitSVAttributes(op);
2350 SubExprInfo visitSV(ArrayIndexInOutOp op);
2351 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2352 SubExprInfo visitSV(IndexedPartSelectOp op);
2353 SubExprInfo visitSV(StructFieldInOutOp op);
2356 SubExprInfo visitSV(SampledOp op);
2359 SubExprInfo visitSV(TimeOp op);
2360 SubExprInfo visitSV(STimeOp op);
2363 using TypeOpVisitor::visitTypeOp;
2365 SubExprInfo visitTypeOp(AggregateConstantOp op);
2367 SubExprInfo visitTypeOp(ParamValueOp op);
2374 SubExprInfo visitTypeOp(StructInjectOp op);
2375 SubExprInfo visitTypeOp(UnionCreateOp op);
2376 SubExprInfo visitTypeOp(UnionExtractOp op);
2377 SubExprInfo visitTypeOp(EnumCmpOp op);
2378 SubExprInfo visitTypeOp(EnumConstantOp op);
2381 using CombinationalVisitor::visitComb;
2382 SubExprInfo visitComb(
MuxOp op);
2383 SubExprInfo visitComb(ReverseOp op);
2384 SubExprInfo visitComb(
AddOp op) {
2385 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2386 return emitBinary(op, Addition,
"+");
2388 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2389 SubExprInfo visitComb(
MulOp op) {
2390 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2391 return emitBinary(op, Multiply,
"*");
2393 SubExprInfo visitComb(
DivUOp op) {
2394 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2396 SubExprInfo visitComb(
DivSOp op) {
2397 return emitBinary(op, Multiply,
"/",
2398 EB_RequireSignedOperands | EB_ForceResultSigned);
2400 SubExprInfo visitComb(
ModUOp op) {
2401 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2403 SubExprInfo visitComb(
ModSOp op) {
2404 return emitBinary(op, Multiply,
"%",
2405 EB_RequireSignedOperands | EB_ForceResultSigned);
2407 SubExprInfo visitComb(
ShlOp op) {
2408 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2410 SubExprInfo visitComb(
ShrUOp op) {
2412 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2414 SubExprInfo visitComb(
ShrSOp op) {
2417 return emitBinary(op, Shift,
">>>",
2418 EB_RequireSignedOperands | EB_ForceResultSigned |
2419 EB_RHS_UnsignedWithSelfDeterminedWidth);
2421 SubExprInfo visitComb(
AndOp op) {
2422 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2423 return emitBinary(op, And,
"&");
2425 SubExprInfo visitComb(
OrOp op) {
2426 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2427 return emitBinary(op, Or,
"|");
2429 SubExprInfo visitComb(
XorOp op) {
2430 if (op.isBinaryNot())
2431 return emitUnary(op,
"~");
2432 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2433 return emitBinary(op, Xor,
"^");
2438 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2440 SubExprInfo visitComb(ReplicateOp op);
2441 SubExprInfo visitComb(
ConcatOp op);
2443 SubExprInfo visitComb(ICmpOp op);
2445 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2446 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2447 "not printed within an assignment-like context");
2448 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2449 "supposed to spill such expressions";
2453 SubExprInfo printStructCreate(
2454 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2456 bool printAsPattern, Operation *op);
2459 ModuleEmitter &emitter;
2466 SubExprSignRequirement signPreference = NoRequirement;
2470 SmallPtrSetImpl<Operation *> &emittedExprs;
2473 SmallVector<Token> localTokens;
2487 bool isAssignmentLikeContext =
false;
2491SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2493 unsigned emitBinaryFlags) {
2495 emitError(op,
"SV attributes emission is unimplemented for the op");
2506 if (emitBinaryFlags & EB_ForceResultSigned)
2507 ps <<
"$signed(" << PP::ibox0;
2508 auto operandSignReq =
2509 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2510 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2512 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2514 ps << lhsSpace << syntax << PP::nbsp;
2521 auto rhsPrec = prec;
2522 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2523 rhsPrec = VerilogPrecedence(prec - 1);
2528 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2529 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2530 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2531 operandSignReq = NoRequirement;
2534 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2535 rhsIsUnsignedValueWithSelfDeterminedWidth);
2539 SubExprSignResult signedness = IsUnsigned;
2540 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2541 signedness = IsSigned;
2543 if (emitBinaryFlags & EB_ForceResultSigned) {
2544 ps << PP::end <<
")";
2545 signedness = IsSigned;
2549 return {prec, signedness};
2552SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2553 bool resultAlwaysUnsigned) {
2555 emitError(op,
"SV attributes emission is unimplemented for the op");
2558 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2562 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2563 resultAlwaysUnsigned ? IsUnsigned : signedness};
2568void ExprEmitter::emitSVAttributes(Operation *op) {
2587 if (constant && constant.getValue().isZero())
2588 return concat.getOperand(1);
2598SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2599 VerilogPrecedence parenthesizeIfLooserThan,
2600 SubExprSignRequirement signRequirement,
2601 bool isSelfDeterminedUnsignedValue,
2602 bool isAssignmentLikeContext) {
2604 if (
auto result = dyn_cast<OpResult>(exp))
2605 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2606 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2607 parenthesizeIfLooserThan, signRequirement,
2608 isSelfDeterminedUnsignedValue,
2609 isAssignmentLikeContext);
2613 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2618 auto *op = exp.getDefiningOp();
2622 if (!shouldEmitInlineExpr) {
2625 if (signRequirement == RequireSigned) {
2627 return {Symbol, IsSigned};
2631 return {Symbol, IsUnsigned};
2634 unsigned subExprStartIndex = buffer.tokens.size();
2636 ps.addCallback({op,
true});
2637 auto done = llvm::make_scope_exit([&]() {
2639 ps.addCallback({op, false});
2645 signPreference = signRequirement;
2647 bool bitCastAdded =
false;
2648 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2650 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2651 ps.addAsString(inType.getWidth());
2652 ps <<
"'(" << PP::ibox0;
2653 bitCastAdded =
true;
2657 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2658 isAssignmentLikeContext);
2659 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2665 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2667 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2669 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2670 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2673 expInfo.signedness = IsSigned;
2674 expInfo.precedence = Selection;
2675 }
else if (signRequirement == RequireUnsigned &&
2676 expInfo.signedness == IsSigned) {
2679 expInfo.signedness = IsUnsigned;
2680 expInfo.precedence = Selection;
2681 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2688 expInfo.precedence = Selection;
2695 emittedExprs.insert(exp.getDefiningOp());
2699SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2700 auto openFn = [&]() {
2702 ps.addAsString(op.getMultiple());
2705 auto closeFn = [&]() { ps <<
"}}"; };
2709 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2710 if (op.getOperand().hasOneUse()) {
2711 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2712 return {Symbol, IsUnsigned};
2715 emitBracedList(op.getOperand(), openFn, closeFn);
2716 return {Symbol, IsUnsigned};
2719SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2720 emitBracedList(op.getOperands());
2721 return {Symbol, IsUnsigned};
2724SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2728 Type toType = op.getType();
2731 ps.invokeWithStringOS(
2732 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2735 return emitSubExpr(op.getInput(), LowestPrecedence);
2738SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2739 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2740 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2741 SubExprSignRequirement signop[] = {
2743 NoRequirement, NoRequirement,
2745 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2747 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2749 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2751 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2752 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2755 if (op.isEqualAllOnes())
2756 return emitUnary(op,
"&",
true);
2759 if (op.isNotEqualZero())
2760 return emitUnary(op,
"|",
true);
2762 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2766 result.signedness = IsUnsigned;
2770SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2772 emitError(op,
"SV attributes emission is unimplemented for the op");
2774 unsigned loBit = op.getLowBit();
2775 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2777 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2778 assert((x.precedence == Symbol ||
2780 "should be handled by isExpressionUnableToInline");
2785 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2789 ps.addAsString(hiBit);
2790 if (hiBit != loBit) {
2792 ps.addAsString(loBit);
2795 return {Unary, IsUnsigned};
2798SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2800 emitError(op,
"SV attributes emission is unimplemented for the op");
2802 auto decl = op.getReferencedDecl(state.symbolCache);
2805 return {Selection, IsUnsigned};
2808SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2810 emitError(op,
"SV attributes emission is unimplemented for the op");
2813 ps.scopedBox(PP::ibox0, [&]() {
2815 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2816 [&]() { ps <<
"," << PP::space; });
2819 return {Symbol, IsUnsigned};
2822SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2824 emitError(op,
"SV attributes emission is unimplemented for the op");
2826 auto decl = op.getReferencedDecl(state.symbolCache);
2830 return {Selection, IsUnsigned};
2833SubExprInfo ExprEmitter::visitSV(XMROp op) {
2835 emitError(op,
"SV attributes emission is unimplemented for the op");
2837 if (op.getIsRooted())
2839 for (
auto s : op.getPath())
2840 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2842 return {Selection, IsUnsigned};
2847SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2849 emitError(op,
"SV attributes emission is unimplemented for the op");
2852 auto globalRef = op.getReferencedPath(&state.symbolCache);
2853 auto namepath = globalRef.getNamepathAttr().getValue();
2854 auto *
module = state.symbolCache.getDefinition(
2855 cast<InnerRefAttr>(namepath.front()).getModule());
2857 for (
auto sym : namepath) {
2859 auto innerRef = cast<InnerRefAttr>(sym);
2860 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2861 innerRef.getName());
2862 if (ref.hasPort()) {
2868 auto leaf = op.getVerbatimSuffixAttr();
2869 if (leaf && leaf.size())
2871 return {Selection, IsUnsigned};
2874SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2876 emitError(op,
"SV attributes emission is unimplemented for the op");
2878 emitTextWithSubstitutions(
2879 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2880 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2882 return {Unary, IsUnsigned};
2885template <
typename MacroTy>
2886SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2888 emitError(op,
"SV attributes emission is unimplemented for the op");
2891 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2892 assert(macroOp &&
"Invalid IR");
2894 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2896 if (!op.getInputs().empty()) {
2898 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2899 emitExpression(val, LowestPrecedence, false);
2903 return {LowestPrecedence, IsUnsigned};
2906SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2907 return emitMacroCall(op);
2910SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2911 return emitMacroCall(op);
2914SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2916 emitError(op,
"SV attributes emission is unimplemented for the op");
2918 ps.addAsString(op.getWidth());
2920 return {Unary, IsUnsigned};
2923SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2925 emitError(op,
"SV attributes emission is unimplemented for the op");
2927 ps.writeQuotedEscaped(op.getStr());
2928 return {Symbol, IsUnsigned};
2931SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2933 emitError(op,
"SV attributes emission is unimplemented for the op");
2935 ps.addAsString(op.getWidth());
2937 return {Unary, IsUnsigned};
2940SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2941 bool isNegated =
false;
2944 if (signPreference == RequireSigned && value.isNegative() &&
2945 !value.isMinSignedValue()) {
2950 ps.addAsString(type.getWidth());
2954 if (signPreference == RequireSigned)
2960 SmallString<32> valueStr;
2962 (-value).toStringUnsigned(valueStr, 16);
2964 value.toStringUnsigned(valueStr, 16);
2967 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2970SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2972 emitError(op,
"SV attributes emission is unimplemented for the op");
2974 auto value = op.getValue();
2978 if (value.getBitWidth() == 0) {
2979 emitOpError(op,
"will not emit zero width constants in the general case");
2980 ps <<
"<<unsupported zero width constant: "
2981 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2982 return {Unary, IsUnsigned};
2985 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2988void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2989 bool printAsPattern, Operation *op) {
2990 if (printAsPattern && !isAssignmentLikeContext)
2991 emitAssignmentPatternContextError(op);
2992 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2995 elementValues, [&]() { ps << openDelim; },
2996 [&](Attribute elementValue) {
2997 printConstantAggregate(elementValue,
elementType, op);
2999 [&]() { ps <<
"}"; });
3002void ExprEmitter::printConstantStruct(
3003 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3004 bool printAsPattern, Operation *op) {
3005 if (printAsPattern && !isAssignmentLikeContext)
3006 emitAssignmentPatternContextError(op);
3013 auto fieldRange = llvm::make_filter_range(
3014 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3019 if (printAsPattern) {
3021 fieldRange, [&]() { ps <<
"'{"; },
3022 [&](
const auto &fieldAndValue) {
3023 ps.scopedBox(PP::ibox2, [&]() {
3024 const auto &[field, value] = fieldAndValue;
3025 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3026 <<
":" << PP::space;
3027 printConstantAggregate(value, field.type, op);
3030 [&]() { ps <<
"}"; });
3033 fieldRange, [&]() { ps <<
"{"; },
3034 [&](
const auto &fieldAndValue) {
3035 ps.scopedBox(PP::ibox2, [&]() {
3036 const auto &[field, value] = fieldAndValue;
3037 printConstantAggregate(value, field.type, op);
3040 [&]() { ps <<
"}"; });
3044void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3047 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3048 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3049 isAssignmentLikeContext, op);
3052 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3053 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3057 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3058 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3059 isAssignmentLikeContext, op);
3061 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3062 auto value = cast<IntegerAttr>(attr).getValue();
3063 printConstantScalar(value, intType);
3067 emitOpError(op,
"contains constant of type ")
3068 << type <<
" which cannot be emitted as Verilog";
3071SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3073 emitError(op,
"SV attributes emission is unimplemented for the op");
3077 "zero-bit types not allowed at this point");
3079 printConstantAggregate(op.getFields(), op.getType(), op);
3080 return {Symbol, IsUnsigned};
3083SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3085 emitError(op,
"SV attributes emission is unimplemented for the op");
3087 return ps.invokeWithStringOS([&](
auto &os) {
3088 return emitter.printParamValue(op.getValue(), os, [&]() {
3089 return op->emitOpError(
"invalid parameter use");
3098 emitError(op,
"SV attributes emission is unimplemented for the op");
3100 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3102 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3104 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3106 ps.addAsString(dstWidth);
3108 return {Selection, arrayPrec.signedness};
3111SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3112 emitSubExpr(op.getInput(), Selection);
3117 emitSubExpr(op.getIndex(), LowestPrecedence);
3119 emitSVAttributes(op);
3120 return {Selection, IsUnsigned};
3126 emitError(op,
"SV attributes emission is unimplemented for the op");
3128 if (op.isUniform()) {
3130 ps.addAsString(op.getInputs().size());
3132 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3136 op.getInputs(), [&]() { ps <<
"{"; },
3139 emitSubExprIBox2(v);
3142 [&]() { ps <<
"}"; });
3144 return {Unary, IsUnsigned};
3147SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3149 emitError(op,
"SV attributes emission is unimplemented for the op");
3152 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3153 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3154 return {Unary, IsUnsigned};
3159 emitError(op,
"SV attributes emission is unimplemented for the op");
3161 emitBracedList(op.getOperands());
3162 return {Unary, IsUnsigned};
3165SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3167 emitError(op,
"SV attributes emission is unimplemented for the op");
3169 auto index = op.getIndex();
3170 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3175 emitSubExpr(index, LowestPrecedence);
3177 return {Selection, arrayPrec.signedness};
3180SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3182 emitError(op,
"SV attributes emission is unimplemented for the op");
3184 auto prec = emitSubExpr(op.getInput(), Selection);
3186 emitSubExpr(op.getBase(), LowestPrecedence);
3187 if (op.getDecrement())
3191 ps.addAsString(op.getWidth());
3193 return {Selection, prec.signedness};
3196SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3198 emitError(op,
"SV attributes emission is unimplemented for the op");
3200 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3202 emitSubExpr(op.getBase(), LowestPrecedence);
3203 if (op.getDecrement())
3207 ps.addAsString(op.getWidth());
3212SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3214 emitError(op,
"SV attributes emission is unimplemented for the op");
3216 auto prec = emitSubExpr(op.getInput(), Selection);
3218 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3219 return {Selection, prec.signedness};
3222SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3224 emitError(op,
"SV attributes emission is unimplemented for the op");
3227 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3232SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3234 emitError(op,
"SV attributes emission is unimplemented for the op");
3237 ps.scopedBox(PP::ibox0, [&]() {
3238 ps.writeQuotedEscaped(op.getFormatString());
3245 for (
auto operand : op.getSubstitutions()) {
3246 ps <<
"," << PP::space;
3247 emitSubExpr(operand, LowestPrecedence);
3251 return {Symbol, IsUnsigned};
3254SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3256 emitError(op,
"SV attributes emission is unimplemented for the op");
3259 return {Symbol, IsUnsigned};
3262SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3264 emitError(op,
"SV attributes emission is unimplemented for the op");
3267 return {Symbol, IsUnsigned};
3270SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3284 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3285 ps.scopedBox(PP::ibox0, [&]() {
3286 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3290 emitSVAttributes(op);
3292 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3293 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3297 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3298 return emitSubExpr(op.getFalseValue(), Conditional);
3301 SubExprSignResult signedness = IsUnsigned;
3302 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3303 signedness = IsSigned;
3305 return {Conditional, signedness};
3309SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3311 emitError(op,
"SV attributes emission is unimplemented for the op");
3314 emitSubExpr(op.getInput(), LowestPrecedence);
3317 return {Symbol, IsUnsigned};
3320SubExprInfo ExprEmitter::printStructCreate(
3321 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3323 bool printAsPattern, Operation *op) {
3324 if (printAsPattern && !isAssignmentLikeContext)
3325 emitAssignmentPatternContextError(op);
3328 auto filteredFields = llvm::make_filter_range(
3329 llvm::enumerate(fieldInfos),
3330 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3332 if (printAsPattern) {
3334 filteredFields, [&]() { ps <<
"'{"; },
3335 [&](
const auto &field) {
3336 ps.scopedBox(PP::ibox2, [&]() {
3338 emitter.getVerilogStructFieldName(field.value().name))
3339 <<
":" << PP::space;
3340 fieldFn(field.value(), field.index());
3343 [&]() { ps <<
"}"; });
3346 filteredFields, [&]() { ps <<
"{"; },
3347 [&](
const auto &field) {
3348 ps.scopedBox(PP::ibox2,
3349 [&]() { fieldFn(field.value(), field.index()); });
3351 [&]() { ps <<
"}"; });
3354 return {Selection, IsUnsigned};
3359 emitError(op,
"SV attributes emission is unimplemented for the op");
3363 bool printAsPattern = isAssignmentLikeContext;
3364 StructType structType = op.getType();
3365 return printStructCreate(
3366 structType.getElements(),
3367 [&](
const auto &field,
auto index) {
3368 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3370 isAssignmentLikeContext);
3372 printAsPattern, op);
3377 emitError(op,
"SV attributes emission is unimplemented for the op");
3379 emitSubExpr(op.getInput(), Selection);
3381 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3382 return {Selection, IsUnsigned};
3385SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3387 emitError(op,
"SV attributes emission is unimplemented for the op");
3391 bool printAsPattern = isAssignmentLikeContext;
3392 StructType structType = op.getType();
3393 return printStructCreate(
3394 structType.getElements(),
3395 [&](
const auto &field,
auto index) {
3396 if (field.name == op.getFieldNameAttr()) {
3397 emitSubExpr(op.getNewValue(), Selection);
3399 emitSubExpr(op.getInput(), Selection);
3401 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3404 printAsPattern, op);
3407SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3408 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3409 return {Selection, IsUnsigned};
3412SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3414 emitError(op,
"SV attributes emission is unimplemented for the op");
3415 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3418 result.signedness = IsUnsigned;
3422SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3424 emitError(op,
"SV attributes emission is unimplemented for the op");
3428 auto unionWidth = hw::getBitWidth(unionType);
3429 auto &element = unionType.getElements()[op.getFieldIndex()];
3430 auto elementWidth = hw::getBitWidth(element.type);
3433 if (!elementWidth) {
3434 ps.addAsString(unionWidth);
3436 return {Unary, IsUnsigned};
3440 if (elementWidth == unionWidth) {
3441 emitSubExpr(op.getInput(), LowestPrecedence);
3442 return {Unary, IsUnsigned};
3447 ps.scopedBox(PP::ibox0, [&]() {
3448 if (
auto prePadding = element.offset) {
3449 ps.addAsString(prePadding);
3450 ps <<
"'h0," << PP::space;
3452 emitSubExpr(op.getInput(), Selection);
3453 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3454 ps <<
"," << PP::space;
3455 ps.addAsString(postPadding);
3461 return {Unary, IsUnsigned};
3464SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3466 emitError(op,
"SV attributes emission is unimplemented for the op");
3467 emitSubExpr(op.getInput(), Selection);
3470 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3471 auto unionWidth = hw::getBitWidth(unionType);
3472 auto &element = unionType.getElements()[op.getFieldIndex()];
3473 auto elementWidth = hw::getBitWidth(element.type);
3474 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3475 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3484 return {Selection, IsUnsigned};
3487SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3488 emitOpError(op,
"cannot emit this expression to Verilog");
3489 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3491 return {Symbol, IsUnsigned};
3507enum class PropertyPrecedence {
3527struct EmittedProperty {
3529 PropertyPrecedence precedence;
3534class PropertyEmitter :
public EmitterBase,
3535 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3539 PropertyEmitter(ModuleEmitter &emitter,
3540 SmallPtrSetImpl<Operation *> &emittedOps)
3541 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3542 PropertyEmitter(ModuleEmitter &emitter,
3543 SmallPtrSetImpl<Operation *> &emittedOps,
3545 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3547 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3548 assert(state.pp.getListener() == &state.saver);
3551 void emitAssertPropertyDisable(
3552 Value property, Value disable,
3553 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3555 void emitAssertPropertyBody(
3556 Value property, Value disable,
3557 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3559 void emitAssertPropertyBody(
3560 Value property, sv::EventControl event, Value clock, Value disable,
3561 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3566 emitNestedProperty(Value property,
3567 PropertyPrecedence parenthesizeIfLooserThan);
3568 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3569 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3571 EmittedProperty visitUnhandledLTL(Operation *op);
3572 EmittedProperty visitLTL(ltl::AndOp op);
3573 EmittedProperty visitLTL(ltl::OrOp op);
3574 EmittedProperty visitLTL(ltl::IntersectOp op);
3575 EmittedProperty visitLTL(ltl::DelayOp op);
3576 EmittedProperty visitLTL(ltl::ConcatOp op);
3577 EmittedProperty visitLTL(ltl::RepeatOp op);
3578 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3579 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3580 EmittedProperty visitLTL(ltl::NotOp op);
3581 EmittedProperty visitLTL(ltl::ImplicationOp op);
3582 EmittedProperty visitLTL(ltl::UntilOp op);
3583 EmittedProperty visitLTL(ltl::EventuallyOp op);
3584 EmittedProperty visitLTL(ltl::ClockOp op);
3586 void emitLTLConcat(ValueRange inputs);
3589 ModuleEmitter &emitter;
3594 SmallPtrSetImpl<Operation *> &emittedOps;
3597 SmallVector<Token> localTokens;
3610void PropertyEmitter::emitAssertPropertyDisable(
3611 Value property, Value disable,
3612 PropertyPrecedence parenthesizeIfLooserThan) {
3615 ps <<
"disable iff" << PP::nbsp <<
"(";
3617 emitNestedProperty(disable, PropertyPrecedence::Unary);
3623 ps.scopedBox(PP::ibox0,
3624 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3630void PropertyEmitter::emitAssertPropertyBody(
3631 Value property, Value disable,
3632 PropertyPrecedence parenthesizeIfLooserThan) {
3633 assert(localTokens.empty());
3635 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3640 if (&buffer.tokens == &localTokens)
3641 buffer.flush(state.pp);
3644void PropertyEmitter::emitAssertPropertyBody(
3645 Value property, sv::EventControl event, Value clock, Value disable,
3646 PropertyPrecedence parenthesizeIfLooserThan) {
3647 assert(localTokens.empty());
3650 ps.scopedBox(PP::ibox2, [&] {
3651 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3652 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3658 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3663 if (&buffer.tokens == &localTokens)
3664 buffer.flush(state.pp);
3667EmittedProperty PropertyEmitter::emitNestedProperty(
3668 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3678 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3679 ExprEmitter(emitter, emittedOps, buffer.tokens)
3680 .emitExpression(property, LowestPrecedence,
3682 return {PropertyPrecedence::Symbol};
3685 unsigned startIndex = buffer.tokens.size();
3686 auto info = dispatchLTLVisitor(property.getDefiningOp());
3691 if (
info.precedence > parenthesizeIfLooserThan) {
3693 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3694 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3696 ps << PP::end <<
")";
3698 info.precedence = PropertyPrecedence::Symbol;
3702 emittedOps.insert(property.getDefiningOp());
3706EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3707 emitOpError(op,
"emission as Verilog property or sequence not supported");
3708 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3709 return {PropertyPrecedence::Symbol};
3712EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3715 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3716 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3717 return {PropertyPrecedence::And};
3720EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3723 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3724 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3725 return {PropertyPrecedence::Or};
3728EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3732 emitNestedProperty(input, PropertyPrecedence::Intersect);
3734 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3735 return {PropertyPrecedence::Intersect};
3738EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3740 if (
auto length = op.getLength()) {
3742 ps.addAsString(op.getDelay());
3745 ps.addAsString(op.getDelay());
3747 ps.addAsString(op.getDelay() + *length);
3751 if (op.getDelay() == 0) {
3753 }
else if (op.getDelay() == 1) {
3757 ps.addAsString(op.getDelay());
3762 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3763 return {PropertyPrecedence::Concat};
3766void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3767 bool addSeparator =
false;
3768 for (
auto input : inputs) {
3771 if (!input.getDefiningOp<ltl::DelayOp>())
3772 ps <<
"##0" << PP::space;
3774 addSeparator =
true;
3775 emitNestedProperty(input, PropertyPrecedence::Concat);
3779EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3780 emitLTLConcat(op.getInputs());
3781 return {PropertyPrecedence::Concat};
3784EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3785 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3786 if (
auto more = op.getMore()) {
3788 ps.addAsString(op.getBase());
3791 ps.addAsString(op.getBase() + *more);
3795 if (op.getBase() == 0) {
3797 }
else if (op.getBase() == 1) {
3801 ps.addAsString(op.getBase());
3805 return {PropertyPrecedence::Repeat};
3808EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3809 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3811 auto more = op.getMore();
3813 ps.addAsString(op.getBase());
3816 ps.addAsString(op.getBase() + more);
3820 return {PropertyPrecedence::Repeat};
3823EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3824 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3826 auto more = op.getMore();
3828 ps.addAsString(op.getBase());
3831 ps.addAsString(op.getBase() + more);
3835 return {PropertyPrecedence::Repeat};
3838EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3839 ps <<
"not" << PP::space;
3840 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3841 return {PropertyPrecedence::Unary};
3847 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3848 if (!concatOp || concatOp.getInputs().size() < 2)
3850 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3851 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3853 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3854 if (!constOp || !constOp.getValue().isOne())
3856 return concatOp.getInputs().drop_back();
3859EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3863 emitLTLConcat(range);
3864 ps << PP::space <<
"|=>" << PP::nbsp;
3866 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3867 ps << PP::space <<
"|->" << PP::nbsp;
3869 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3870 return {PropertyPrecedence::Implication};
3873EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3874 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3875 ps << PP::space <<
"until" << PP::space;
3876 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3877 return {PropertyPrecedence::Until};
3880EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3881 ps <<
"s_eventually" << PP::space;
3882 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3883 return {PropertyPrecedence::Qualifier};
3886EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3888 ps.scopedBox(PP::ibox2, [&] {
3889 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3890 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3894 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3895 return {PropertyPrecedence::Clocking};
3905class NameCollector {
3907 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3911 void collectNames(Block &block);
3913 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3914 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3917 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3918 ModuleEmitter &moduleEmitter;
3923 static constexpr size_t maxTypeWidthBound = 32;
3928void NameCollector::collectNames(Block &block) {
3931 for (
auto &op : block) {
3935 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3936 FuncCallProceduralOp, FuncCallOp>(op))
3938 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3942 for (
auto result : op.getResults()) {
3944 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3945 SmallString<16> typeString;
3949 llvm::raw_svector_ostream stringStream(typeString);
3951 stringStream, op.getLoc());
3953 if (typeString.size() <= maxTypeWidthBound)
3954 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3961 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3962 for (
auto ®ion : op.getRegions()) {
3963 if (!region.empty())
3964 collectNames(region.front());
3978class StmtEmitter :
public EmitterBase,
3986 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3988 void emitStatement(Operation *op);
3989 void emitStatementBlock(Block &body);
3992 LogicalResult emitDeclaration(Operation *op);
3995 void collectNamesAndCalculateDeclarationWidths(Block &block);
3998 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
3999 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4000 bool isAssignmentLikeContext =
false);
4001 void emitSVAttributes(Operation *op);
4004 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4007 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4011 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4012 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4013 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4014 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4015 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4016 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4018 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4019 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4020 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4021 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4022 template <
typename Op>
4025 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4026 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4027 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4029 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4030 LogicalResult visitSV(
AssignOp op);
4031 LogicalResult visitSV(BPAssignOp op);
4032 LogicalResult visitSV(PAssignOp op);
4033 LogicalResult visitSV(ForceOp op);
4034 LogicalResult visitSV(ReleaseOp op);
4035 LogicalResult visitSV(AliasOp op);
4036 LogicalResult visitSV(InterfaceInstanceOp op);
4037 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4038 LogicalResult visitStmt(OutputOp op);
4040 LogicalResult visitStmt(InstanceOp op);
4041 LogicalResult visitStmt(InstanceChoiceOp op);
4042 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4043 ArrayRef<Value> instPortValues);
4048 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4049 LogicalResult visitSV(OrderedOutputOp op);
4050 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4051 LogicalResult visitSV(IfDefProceduralOp op) {
4052 return emitIfDef(op, op.getCond());
4054 LogicalResult visitSV(IfOp op);
4055 LogicalResult visitSV(AlwaysOp op);
4056 LogicalResult visitSV(AlwaysCombOp op);
4057 LogicalResult visitSV(AlwaysFFOp op);
4058 LogicalResult visitSV(InitialOp op);
4059 LogicalResult visitSV(CaseOp op);
4060 LogicalResult visitSV(FWriteOp op);
4061 LogicalResult visitSV(FFlushOp op);
4062 LogicalResult visitSV(VerbatimOp op);
4063 LogicalResult visitSV(MacroRefOp op);
4065 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4066 std::optional<unsigned> verbosity);
4067 LogicalResult visitSV(StopOp op);
4068 LogicalResult visitSV(FinishOp op);
4069 LogicalResult visitSV(ExitOp op);
4071 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4072 std::optional<unsigned> verbosity,
4074 ValueRange operands);
4075 LogicalResult visitSV(FatalOp op);
4076 LogicalResult visitSV(ErrorOp op);
4077 LogicalResult visitSV(WarningOp op);
4078 LogicalResult visitSV(InfoOp op);
4080 LogicalResult visitSV(ReadMemOp op);
4082 LogicalResult visitSV(GenerateOp op);
4083 LogicalResult visitSV(GenerateCaseOp op);
4085 LogicalResult visitSV(
ForOp op);
4087 void emitAssertionLabel(Operation *op);
4088 void emitAssertionMessage(StringAttr message, ValueRange args,
4089 SmallPtrSetImpl<Operation *> &ops,
4091 template <
typename Op>
4092 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4093 LogicalResult visitSV(AssertOp op);
4094 LogicalResult visitSV(AssumeOp op);
4095 LogicalResult visitSV(CoverOp op);
4096 template <
typename Op>
4097 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4098 LogicalResult visitSV(AssertConcurrentOp op);
4099 LogicalResult visitSV(AssumeConcurrentOp op);
4100 LogicalResult visitSV(CoverConcurrentOp op);
4101 template <
typename Op>
4102 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4103 LogicalResult visitSV(AssertPropertyOp op);
4104 LogicalResult visitSV(AssumePropertyOp op);
4105 LogicalResult visitSV(CoverPropertyOp op);
4107 LogicalResult visitSV(BindOp op);
4108 LogicalResult visitSV(InterfaceOp op);
4109 LogicalResult visitSV(InterfaceSignalOp op);
4110 LogicalResult visitSV(InterfaceModportOp op);
4111 LogicalResult visitSV(AssignInterfaceSignalOp op);
4112 LogicalResult visitSV(MacroErrorOp op);
4113 LogicalResult visitSV(MacroDefOp op);
4115 void emitBlockAsStatement(Block *block,
4116 const SmallPtrSetImpl<Operation *> &locationOps,
4117 StringRef multiLineComment = StringRef());
4119 LogicalResult visitSV(FuncDPIImportOp op);
4120 template <
typename CallOp>
4121 LogicalResult emitFunctionCall(CallOp callOp);
4122 LogicalResult visitSV(FuncCallProceduralOp op);
4123 LogicalResult visitSV(FuncCallOp op);
4124 LogicalResult visitSV(ReturnOp op);
4125 LogicalResult visitSV(IncludeOp op);
4128 ModuleEmitter &emitter;
4133 size_t maxDeclNameWidth = 0;
4134 size_t maxTypeWidth = 0;
4145void StmtEmitter::emitExpression(Value exp,
4146 SmallPtrSetImpl<Operation *> &emittedExprs,
4147 VerilogPrecedence parenthesizeIfLooserThan,
4148 bool isAssignmentLikeContext) {
4149 ExprEmitter(emitter, emittedExprs)
4150 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4155void StmtEmitter::emitSVAttributes(Operation *op) {
4163 setPendingNewline();
4166void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4167 llvm::function_ref<
void()> emitRHS,
4169 std::optional<PPExtString> wordBeforeLHS) {
4171 ps.scopedBox(PP::ibox2, [&]() {
4172 if (wordBeforeLHS) {
4173 ps << *wordBeforeLHS << PP::space;
4177 ps << PP::space << syntax << PP::space;
4179 ps.scopedBox(PP::ibox0, [&]() {
4186template <
typename Op>
4188StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4189 std::optional<PPExtString> wordBeforeLHS) {
4190 SmallPtrSet<Operation *, 8> ops;
4194 ps.addCallback({op,
true});
4195 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4197 emitExpression(op.getSrc(), ops, LowestPrecedence,
4202 ps.addCallback({op,
false});
4203 emitLocationInfoAndNewLine(ops);
4207LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4210 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4213 if (emitter.assignsInlined.count(op))
4217 emitSVAttributes(op);
4222LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4223 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4227 if (emitter.assignsInlined.count(op))
4231 emitSVAttributes(op);
4236LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4238 emitSVAttributes(op);
4243LogicalResult StmtEmitter::visitSV(ForceOp op) {
4245 emitError(op,
"SV attributes emission is unimplemented for the op");
4250LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4252 emitError(op,
"SV attributes emission is unimplemented for the op");
4255 SmallPtrSet<Operation *, 8> ops;
4257 ps.addCallback({op,
true});
4258 ps.scopedBox(PP::ibox2, [&]() {
4259 ps <<
"release" << PP::space;
4260 emitExpression(op.getDest(), ops);
4263 ps.addCallback({op,
false});
4264 emitLocationInfoAndNewLine(ops);
4268LogicalResult StmtEmitter::visitSV(AliasOp op) {
4270 emitError(op,
"SV attributes emission is unimplemented for the op");
4273 SmallPtrSet<Operation *, 8> ops;
4275 ps.addCallback({op,
true});
4276 ps.scopedBox(PP::ibox2, [&]() {
4277 ps <<
"alias" << PP::space;
4278 ps.scopedBox(PP::cbox0, [&]() {
4280 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4281 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4285 ps.addCallback({op,
false});
4286 emitLocationInfoAndNewLine(ops);
4290LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4291 auto doNotPrint = op.getDoNotPrint();
4292 if (doNotPrint && !state.options.emitBindComments)
4296 emitError(op,
"SV attributes emission is unimplemented for the op");
4299 StringRef prefix =
"";
4300 ps.addCallback({op,
true});
4303 ps <<
"// This interface is elsewhere emitted as a bind statement."
4307 SmallPtrSet<Operation *, 8> ops;
4310 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4311 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4312 "point to an interface");
4315 if (!prefix.empty())
4321 ps.addCallback({op,
false});
4322 emitLocationInfoAndNewLine(ops);
4330LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4332 SmallPtrSet<Operation *, 8> ops;
4333 size_t operandIndex = 0;
4334 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4335 for (
PortInfo port : ports.getOutputs()) {
4336 auto operand = op->getOperand(operandIndex);
4340 if (operand.hasOneUse() && operand.getDefiningOp() &&
4341 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4350 ps.addCallback({op,
true});
4352 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4354 ps <<
"// Zero width: ";
4357 ps <<
"assign" << PP::space;
4359 ps << PP::space <<
"=" << PP::space;
4360 ps.scopedBox(PP::ibox0, [&]() {
4364 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4365 ps <<
"/*Zero width*/";
4367 emitExpression(operand, ops, LowestPrecedence,
4372 ps.addCallback({op,
false});
4373 emitLocationInfoAndNewLine(ops);
4380LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4381 auto parent = op->getParentOfType<PortList>();
4383 return emitOutputLikeOp(op, ports);
4386LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4388 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4389 ps <<
"`ifndef " << typescopeDef << PP::newline;
4390 ps <<
"`define " << typescopeDef;
4391 setPendingNewline();
4392 emitStatementBlock(*op.getBodyBlock());
4394 ps <<
"`endif // " << typescopeDef;
4395 setPendingNewline();
4399LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4401 emitError(op,
"SV attributes emission is unimplemented for the op");
4406 ps << PP::neverbox <<
"// ";
4408 SmallPtrSet<Operation *, 8> ops;
4410 ps.scopedBox(PP::ibox2, [&]() {
4411 ps <<
"typedef" << PP::space;
4412 ps.invokeWithStringOS([&](
auto &os) {
4414 op.getAliasType(),
false);
4416 ps << PP::space <<
PPExtString(op.getPreferredName());
4417 ps.invokeWithStringOS(
4418 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4423 emitLocationInfoAndNewLine(ops);
4427template <
typename CallOpTy>
4428LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4432 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4434 SmallPtrSet<Operation *, 8> ops;
4438 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4439 if (explicitReturn) {
4440 assert(explicitReturn.hasOneUse());
4441 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4442 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4443 emitExpression(bpassignOp.getDest(), ops);
4445 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4446 ps <<
"assign" << PP::nbsp;
4447 emitExpression(assignOp.getDest(), ops);
4449 ps << PP::nbsp <<
"=" << PP::nbsp;
4452 auto arguments = callee.getPortList(
true);
4456 bool needsComma =
false;
4457 auto printArg = [&](Value value) {
4459 ps <<
"," << PP::space;
4460 emitExpression(value, ops);
4464 ps.scopedBox(PP::ibox0, [&] {
4465 unsigned inputIndex = 0, outputIndex = 0;
4466 for (
auto arg : arguments) {
4469 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4471 printArg(op.getInputs()[inputIndex++]);
4476 emitLocationInfoAndNewLine(ops);
4480LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4481 return emitFunctionCall(op);
4484LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4485 return emitFunctionCall(op);
4488template <
typename PPS>
4490 bool isAutomatic =
false,
4491 bool emitAsTwoStateType =
false) {
4492 ps <<
"function" << PP::nbsp;
4494 ps <<
"automatic" << PP::nbsp;
4495 auto retType = op.getExplicitlyReturnedType();
4497 ps.invokeWithStringOS([&](
auto &os) {
4498 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4499 emitAsTwoStateType);
4505 emitter.emitPortList(
4509LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4510 auto parent = op->getParentOfType<sv::FuncOp>();
4512 return emitOutputLikeOp(op, ports);
4515LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4517 ps <<
"`include" << PP::nbsp;
4519 if (op.getStyle() == IncludeStyle::System)
4520 ps <<
"<" << op.getTarget() <<
">";
4522 ps <<
"\"" << op.getTarget() <<
"\"";
4524 emitLocationInfo(op.getLoc());
4525 setPendingNewline();
4529LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4532 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4536 if (
auto linkageName = importOp.getLinkageName())
4537 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4539 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4540 assert(op.isDeclaration() &&
"function must be a declaration");
4543 assert(state.pendingNewline);
4549LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4551 emitError(op,
"SV attributes emission is unimplemented for the op");
4554 SmallPtrSet<Operation *, 8> ops;
4557 ps.addCallback({op,
true});
4559 if (
auto fd = op.getFd())
4560 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4563 ps.addCallback({op,
false});
4564 emitLocationInfoAndNewLine(ops);
4568LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4570 emitError(op,
"SV attributes emission is unimplemented for the op");
4573 SmallPtrSet<Operation *, 8> ops;
4576 ps.addCallback({op,
true});
4578 ps.scopedBox(PP::ibox0, [&]() {
4579 emitExpression(op.getFd(), ops);
4581 ps <<
"," << PP::space;
4582 ps.writeQuotedEscaped(op.getFormatString());
4590 for (
auto operand : op.getSubstitutions()) {
4591 ps <<
"," << PP::space;
4592 emitExpression(operand, ops);
4596 ps.addCallback({op,
false});
4597 emitLocationInfoAndNewLine(ops);
4601LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4603 emitError(op,
"SV attributes emission is unimplemented for the op");
4606 SmallPtrSet<Operation *, 8> ops;
4611 StringRef
string = op.getFormatString();
4612 if (
string.ends_with(
"\n"))
4613 string =
string.drop_back();
4618 bool isFirst =
true;
4621 while (!
string.
empty()) {
4622 auto lhsRhs =
string.split(
'\n');
4626 ps << PP::end << PP::newline << PP::neverbox;
4630 emitTextWithSubstitutions(
4631 ps, lhsRhs.first, op,
4632 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4633 string = lhsRhs.second;
4638 emitLocationInfoAndNewLine(ops);
4643LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4645 emitError(op,
"SV attributes emission is unimplemented for the op");
4649 SmallPtrSet<Operation *, 8> ops;
4654 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4655 assert(macroOp &&
"Invalid IR");
4657 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4659 if (!op.getInputs().empty()) {
4661 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4662 emitExpression(val, ops, LowestPrecedence,
4668 emitLocationInfoAndNewLine(ops);
4674StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4675 std::optional<unsigned> verbosity) {
4677 emitError(op,
"SV attributes emission is unimplemented for the op");
4680 SmallPtrSet<Operation *, 8> ops;
4682 ps.addCallback({op,
true});
4684 if (verbosity && *verbosity != 1) {
4686 ps.addAsString(*verbosity);
4690 ps.addCallback({op,
false});
4691 emitLocationInfoAndNewLine(ops);
4695LogicalResult StmtEmitter::visitSV(StopOp op) {
4696 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4699LogicalResult StmtEmitter::visitSV(FinishOp op) {
4700 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4704LogicalResult StmtEmitter::visitSV(ExitOp op) {
4705 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4711StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4712 std::optional<unsigned> verbosity,
4713 StringAttr message, ValueRange operands) {
4715 emitError(op,
"SV attributes emission is unimplemented for the op");
4718 SmallPtrSet<Operation *, 8> ops;
4720 ps.addCallback({op,
true});
4726 if ((verbosity && *verbosity != 1) || message) {
4728 ps.scopedBox(PP::ibox0, [&]() {
4732 ps.addAsString(*verbosity);
4737 ps <<
"," << PP::space;
4738 ps.writeQuotedEscaped(message.getValue());
4740 for (
auto operand : operands) {
4741 ps <<
"," << PP::space;
4742 emitExpression(operand, ops);
4751 ps.addCallback({op,
false});
4752 emitLocationInfoAndNewLine(ops);
4756LogicalResult StmtEmitter::visitSV(FatalOp op) {
4757 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4758 op.getMessageAttr(), op.getSubstitutions());
4761LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4762 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4763 op.getMessageAttr(), op.getSubstitutions());
4766LogicalResult StmtEmitter::visitSV(WarningOp op) {
4767 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4768 op.getMessageAttr(), op.getSubstitutions());
4771LogicalResult StmtEmitter::visitSV(InfoOp op) {
4772 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4773 op.getMessageAttr(), op.getSubstitutions());
4776LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4777 SmallPtrSet<Operation *, 8> ops({op});
4780 ps.addCallback({op,
true});
4782 switch (op.getBaseAttr().getValue()) {
4783 case MemBaseTypeAttr::MemBaseBin:
4786 case MemBaseTypeAttr::MemBaseHex:
4791 ps.scopedBox(PP::ibox0, [&]() {
4792 ps.writeQuotedEscaped(op.getFilename());
4793 ps <<
"," << PP::space;
4794 emitExpression(op.getDest(), ops);
4798 ps.addCallback({op,
false});
4799 emitLocationInfoAndNewLine(ops);
4803LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4804 emitSVAttributes(op);
4807 ps.addCallback({op,
true});
4808 ps <<
"generate" << PP::newline;
4810 setPendingNewline();
4811 emitStatementBlock(op.getBody().getBlocks().front());
4814 ps <<
"endgenerate";
4815 ps.addCallback({op,
false});
4816 setPendingNewline();
4820LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4821 emitSVAttributes(op);
4824 ps.addCallback({op,
true});
4826 ps.invokeWithStringOS([&](
auto &os) {
4827 emitter.printParamValue(
4828 op.getCond(), os, VerilogPrecedence::Selection,
4829 [&]() { return op->emitOpError(
"invalid case parameter"); });
4832 setPendingNewline();
4835 ArrayAttr
patterns = op.getCasePatterns();
4836 ArrayAttr caseNames = op.getCaseNames();
4837 MutableArrayRef<Region> regions = op.getCaseRegions();
4844 llvm::StringMap<size_t> nextGenIds;
4845 ps.scopedBox(PP::bbox2, [&]() {
4847 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4848 auto ®ion = regions[i];
4849 assert(region.hasOneBlock());
4850 Attribute patternAttr =
patterns[i];
4853 if (!isa<mlir::TypedAttr>(patternAttr))
4856 ps.invokeWithStringOS([&](
auto &os) {
4857 emitter.printParamValue(
4858 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4859 [&]() {
return op->emitOpError(
"invalid case value"); });
4862 StringRef legalName =
4863 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4866 setPendingNewline();
4867 emitStatementBlock(region.getBlocks().front());
4870 setPendingNewline();
4876 ps.addCallback({op,
false});
4877 setPendingNewline();
4881LogicalResult StmtEmitter::visitSV(
ForOp op) {
4882 emitSVAttributes(op);
4883 llvm::SmallPtrSet<Operation *, 8> ops;
4884 ps.addCallback({op,
true});
4886 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4889 ps.scopedBox(PP::cbox0, [&]() {
4893 ps <<
"logic" << PP::nbsp;
4894 ps.invokeWithStringOS([&](
auto &os) {
4895 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4900 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4905 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4906 [&]() { emitExpression(op.getUpperBound(), ops); },
4912 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4913 [&]() { emitExpression(op.getStep(), ops); },
4917 ps << PP::neverbreak;
4918 setPendingNewline();
4919 emitStatementBlock(op.getBody().getBlocks().front());
4922 ps.addCallback({op,
false});
4923 emitLocationInfoAndNewLine(ops);
4928void StmtEmitter::emitAssertionLabel(Operation *op) {
4929 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4935void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4936 SmallPtrSetImpl<Operation *> &ops,
4937 bool isConcurrent =
false) {
4940 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4941 ps.scopedBox(PP::ibox0, [&]() {
4942 ps.writeQuotedEscaped(message.getValue());
4944 for (
auto arg : args) {
4945 ps <<
"," << PP::space;
4946 emitExpression(arg, ops);
4952template <
typename Op>
4953LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4955 emitError(op,
"SV attributes emission is unimplemented for the op");
4958 SmallPtrSet<Operation *, 8> ops;
4960 ps.addCallback({op,
true});
4961 ps.scopedBox(PP::ibox2, [&]() {
4962 emitAssertionLabel(op);
4963 ps.scopedBox(PP::cbox0, [&]() {
4965 switch (op.getDefer()) {
4966 case DeferAssert::Immediate:
4968 case DeferAssert::Observed:
4971 case DeferAssert::Final:
4976 ps.scopedBox(PP::ibox0, [&]() {
4977 emitExpression(op.getExpression(), ops);
4980 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4984 ps.addCallback({op,
false});
4985 emitLocationInfoAndNewLine(ops);
4989LogicalResult StmtEmitter::visitSV(AssertOp op) {
4990 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4993LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4994 return emitImmediateAssertion(op,
PPExtString(
"assume"));
4997LogicalResult StmtEmitter::visitSV(CoverOp op) {
4998 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5001template <
typename Op>
5002LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5004 emitError(op,
"SV attributes emission is unimplemented for the op");
5007 SmallPtrSet<Operation *, 8> ops;
5009 ps.addCallback({op,
true});
5010 ps.scopedBox(PP::ibox2, [&]() {
5011 emitAssertionLabel(op);
5012 ps.scopedBox(PP::cbox0, [&]() {
5013 ps << opName << PP::nbsp <<
"property (";
5014 ps.scopedBox(PP::ibox0, [&]() {
5015 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5017 emitExpression(op.getClock(), ops);
5018 ps <<
")" << PP::space;
5019 emitExpression(op.getProperty(), ops);
5022 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5027 ps.addCallback({op,
false});
5028 emitLocationInfoAndNewLine(ops);
5032LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5033 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5036LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5037 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5040LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5041 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5046template <
typename Op>
5047LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5049 emitError(op,
"SV attributes emission is unimplemented for the op");
5059 Operation *parent = op->getParentOp();
5060 Value
property = op.getProperty();
5061 bool isTemporal = !
property.getType().isSignlessInteger(1);
5062 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5063 bool emitAsImmediate = !isTemporal && isProcedural;
5066 SmallPtrSet<Operation *, 8> ops;
5068 ps.addCallback({op,
true});
5069 ps.scopedBox(PP::ibox2, [&]() {
5071 emitAssertionLabel(op);
5073 ps.scopedBox(PP::cbox0, [&]() {
5074 if (emitAsImmediate)
5075 ps << opName <<
"(";
5077 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5079 Value clock = op.getClock();
5080 auto event = op.getEvent();
5082 ps.scopedBox(PP::ibox2, [&]() {
5083 PropertyEmitter(emitter, ops)
5084 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5087 ps.scopedBox(PP::ibox2, [&]() {
5088 PropertyEmitter(emitter, ops)
5089 .emitAssertPropertyBody(property, op.getDisable());
5094 ps.addCallback({op,
false});
5095 emitLocationInfoAndNewLine(ops);
5099LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5100 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5103LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5104 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5107LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5108 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5111LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5113 emitError(op,
"SV attributes emission is unimplemented for the op");
5116 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5117 .getMacroIdentifier());
5120 bool hasEmptyThen = op->getRegion(0).front().empty();
5122 ps <<
"`ifndef " << ident;
5124 ps <<
"`ifdef " << ident;
5126 SmallPtrSet<Operation *, 8> ops;
5128 emitLocationInfoAndNewLine(ops);
5131 emitStatementBlock(op->getRegion(0).front());
5133 if (!op->getRegion(1).empty()) {
5134 if (!hasEmptyThen) {
5136 ps <<
"`else // " << ident;
5137 setPendingNewline();
5139 emitStatementBlock(op->getRegion(1).front());
5146 setPendingNewline();
5154void StmtEmitter::emitBlockAsStatement(
5155 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5156 StringRef multiLineComment) {
5163 emitLocationInfoAndNewLine(locationOps);
5166 emitStatementBlock(*block);
5168 if (needsBeginEnd) {
5172 if (!multiLineComment.empty())
5173 ps <<
" // " << multiLineComment;
5174 setPendingNewline();
5178LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5180 for (
auto &op : ooop.getBody().front())
5185LogicalResult StmtEmitter::visitSV(IfOp op) {
5186 SmallPtrSet<Operation *, 8> ops;
5188 auto ifcondBox = PP::ibox2;
5190 emitSVAttributes(op);
5192 ps.addCallback({op,
true});
5193 ps <<
"if (" << ifcondBox;
5203 emitExpression(ifOp.getCond(), ops);
5204 ps << PP::end <<
")";
5205 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5207 if (!ifOp.hasElse())
5211 Block *elseBlock = ifOp.getElseBlock();
5213 if (!nestedElseIfOp) {
5218 emitBlockAsStatement(elseBlock, ops);
5224 ifOp = nestedElseIfOp;
5225 ps <<
"else if (" << ifcondBox;
5227 ps.addCallback({op,
false});
5232LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5233 emitSVAttributes(op);
5234 SmallPtrSet<Operation *, 8> ops;
5238 auto printEvent = [&](AlwaysOp::Condition cond) {
5239 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5240 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5242 ps.addCallback({op,
true});
5244 switch (op.getNumConditions()) {
5250 printEvent(op.getCondition(0));
5255 ps.scopedBox(PP::cbox0, [&]() {
5256 printEvent(op.getCondition(0));
5257 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5258 ps << PP::space <<
"or" << PP::space;
5259 printEvent(op.getCondition(i));
5268 std::string comment;
5269 if (op.getNumConditions() == 0) {
5270 comment =
"always @*";
5272 comment =
"always @(";
5275 [&](Attribute eventAttr) {
5276 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5277 comment += stringifyEventControl(event);
5279 [&]() { comment +=
", "; });
5283 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5284 ps.addCallback({op,
false});
5288LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5289 emitSVAttributes(op);
5290 SmallPtrSet<Operation *, 8> ops;
5294 ps.addCallback({op,
true});
5295 StringRef opString =
"always_comb";
5296 if (state.options.noAlwaysComb)
5297 opString =
"always @(*)";
5300 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5301 ps.addCallback({op,
false});
5305LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5306 emitSVAttributes(op);
5308 SmallPtrSet<Operation *, 8> ops;
5312 ps.addCallback({op,
true});
5313 ps <<
"always_ff @(";
5314 ps.scopedBox(PP::cbox0, [&]() {
5315 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5316 emitExpression(op.getClock(), ops);
5317 if (op.getResetStyle() == ResetType::AsyncReset) {
5318 ps << PP::nbsp <<
"or" << PP::space
5319 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5320 emitExpression(op.getReset(), ops);
5327 std::string comment;
5328 comment +=
"always_ff @(";
5329 comment += stringifyEventControl(op.getClockEdge());
5330 if (op.getResetStyle() == ResetType::AsyncReset) {
5332 comment += stringifyEventControl(*op.getResetEdge());
5336 if (op.getResetStyle() == ResetType::NoReset)
5337 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5340 emitLocationInfoAndNewLine(ops);
5341 ps.scopedBox(PP::bbox2, [&]() {
5347 if (op.getResetStyle() == ResetType::AsyncReset &&
5348 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5350 emitExpression(op.getReset(), ops);
5352 emitBlockAsStatement(op.getResetBlock(), ops);
5355 emitBlockAsStatement(op.getBodyBlock(), ops);
5360 ps <<
" // " << comment;
5361 setPendingNewline();
5363 ps.addCallback({op,
false});
5367LogicalResult StmtEmitter::visitSV(InitialOp op) {
5368 emitSVAttributes(op);
5369 SmallPtrSet<Operation *, 8> ops;
5372 ps.addCallback({op,
true});
5374 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5375 ps.addCallback({op,
false});
5379LogicalResult StmtEmitter::visitSV(CaseOp op) {
5380 emitSVAttributes(op);
5381 SmallPtrSet<Operation *, 8> ops, emptyOps;
5384 ps.addCallback({op,
true});
5385 if (op.getValidationQualifier() !=
5386 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5387 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5388 op.getValidationQualifier()))
5390 const char *opname =
nullptr;
5391 switch (op.getCaseStyle()) {
5392 case CaseStmtType::CaseStmt:
5395 case CaseStmtType::CaseXStmt:
5398 case CaseStmtType::CaseZStmt:
5402 ps << opname <<
" (";
5403 ps.scopedBox(PP::ibox0, [&]() {
5404 emitExpression(op.getCond(), ops);
5407 emitLocationInfoAndNewLine(ops);
5409 ps.scopedBox(PP::bbox2, [&]() {
5410 for (
auto &caseInfo : op.getCases()) {
5412 auto &
pattern = caseInfo.pattern;
5414 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5415 .Case<CaseBitPattern>([&](
auto bitPattern) {
5418 ps.invokeWithStringOS([&](
auto &os) {
5419 os << bitPattern->getWidth() <<
"'b";
5420 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5421 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5424 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5425 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5426 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5428 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5429 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5432 emitBlockAsStatement(caseInfo.block, emptyOps);
5438 ps.addCallback({op,
false});
5439 emitLocationInfoAndNewLine(ops);
5443LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5444 bool doNotPrint = op.getDoNotPrint();
5445 if (doNotPrint && !state.options.emitBindComments)
5450 emitSVAttributes(op);
5452 ps.addCallback({op,
true});
5455 <<
"/* This instance is elsewhere emitted as a bind statement."
5458 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5459 "attributes. The attributes will not be emitted.";
5462 SmallPtrSet<Operation *, 8> ops;
5467 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5468 assert(moduleOp &&
"Invalid IR");
5472 if (!op.getParameters().empty()) {
5475 bool printed =
false;
5477 llvm::zip(op.getParameters(),
5478 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5479 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5480 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5482 if (param.getValue() == modParam.getValue())
5487 ps <<
" #(" << PP::bbox2 << PP::newline;
5490 ps <<
"," << PP::newline;
5494 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5496 ps.invokeWithStringOS([&](
auto &os) {
5497 emitter.printParamValue(param.getValue(), os, [&]() {
5498 return op->emitOpError(
"invalid instance parameter '")
5499 << param.getName().getValue() <<
"' value";
5505 ps << PP::end << PP::newline <<
")";
5512 SmallVector<Value> instPortValues(modPortInfo.size());
5513 op.getValues(instPortValues, modPortInfo);
5514 emitInstancePortList(op, modPortInfo, instPortValues);
5516 ps.addCallback({op,
false});
5517 emitLocationInfoAndNewLine(ops);
5522 setPendingNewline();
5527LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5529 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5530 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5535 Operation *defaultModuleOp =
5536 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5538 SmallVector<Value> instPortValues(modPortInfo.size());
5539 op.getValues(instPortValues, modPortInfo);
5540 emitInstancePortList(op, modPortInfo, instPortValues);
5542 SmallPtrSet<Operation *, 8> ops;
5544 ps.addCallback({op,
false});
5545 emitLocationInfoAndNewLine(ops);
5550void StmtEmitter::emitInstancePortList(Operation *op,
5552 ArrayRef<Value> instPortValues) {
5553 SmallPtrSet<Operation *, 8> ops;
5556 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5557 ModulePortInfo containingPortList(containingModule.getPortList());
5562 size_t maxNameLength = 0;
5563 for (
auto &elt : modPortInfo) {
5564 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5567 auto getWireForValue = [&](Value result) {
5568 return result.getUsers().begin()->getOperand(0);
5572 bool isFirst =
true;
5573 bool isZeroWidth =
false;
5575 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5577 auto &modPort = modPortInfo.
at(portNum);
5579 Value portVal = instPortValues[portNum];
5584 bool shouldPrintComma =
true;
5586 shouldPrintComma =
false;
5587 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5589 shouldPrintComma =
true;
5594 if (shouldPrintComma)
5597 emitLocationInfoAndNewLine(ops);
5612 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5613 auto modPortName = modPort.getVerilogName();
5615 ps.spaces(maxNameLength - modPortName.size() + 1);
5617 ps.scopedBox(PP::ibox0, [&]() {
5624 if (!modPort.isOutput()) {
5626 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5627 ps <<
"/* Zero width */";
5629 emitExpression(portVal, ops, LowestPrecedence);
5630 }
else if (portVal.use_empty()) {
5631 ps <<
"/* unused */";
5632 }
else if (portVal.hasOneUse() &&
5633 (output = dyn_cast_or_null<OutputOp>(
5634 portVal.getUses().begin()->getOwner()))) {
5639 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5641 containingPortList.atOutput(outputPortNo).getVerilogName());
5643 portVal = getWireForValue(portVal);
5644 emitExpression(portVal, ops);
5650 if (!isFirst || isZeroWidth) {
5651 emitLocationInfoAndNewLine(ops);
5664LogicalResult StmtEmitter::visitSV(BindOp op) {
5665 emitter.emitBind(op);
5666 assert(state.pendingNewline);
5670LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5671 emitComment(op.getCommentAttr());
5673 emitSVAttributes(op);
5676 ps.addCallback({op,
true});
5678 setPendingNewline();
5680 emitStatementBlock(*op.getBodyBlock());
5682 ps <<
"endinterface" << PP::newline;
5683 ps.addCallback({op,
false});
5684 setPendingNewline();
5688LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5690 emitSVAttributes(op);
5692 ps.addCallback({op,
true});
5694 ps << PP::neverbox <<
"// ";
5695 ps.invokeWithStringOS([&](
auto &os) {
5700 ps.invokeWithStringOS(
5701 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5705 ps.addCallback({op,
false});
5706 setPendingNewline();
5710LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5712 ps.addCallback({op,
true});
5716 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5717 auto port = cast<ModportStructAttr>(portAttr);
5718 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5719 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5720 ps << PPExtString(getSymOpName(signalDecl));
5724 ps.addCallback({op,
false});
5725 setPendingNewline();
5729LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5731 ps.addCallback({op,
true});
5732 SmallPtrSet<Operation *, 8> emitted;
5735 emitExpression(op.getIface(), emitted);
5736 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5737 emitExpression(op.getRhs(), emitted);
5739 ps.addCallback({op,
false});
5740 setPendingNewline();
5744LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5746 ps <<
"`" << op.getMacroIdentifier();
5747 setPendingNewline();
5751LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5752 auto decl = op.getReferencedMacro(&state.symbolCache);
5755 ps.addCallback({op,
true});
5757 if (decl.getArgs()) {
5759 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5760 ps << cast<StringAttr>(name);
5764 if (!op.getFormatString().empty()) {
5766 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5769 ps.addCallback({op,
false});
5770 setPendingNewline();
5774void StmtEmitter::emitStatement(Operation *op) {
5781 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5785 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5786 succeeded(dispatchVerifVisitor(op)))
5789 emitOpError(op,
"emission to Verilog not supported");
5790 emitPendingNewlineIfNeeded();
5791 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5792 setPendingNewline();
5803 StmtEmitter &stmtEmitter) {
5810 if (isa<IfDefProceduralOp>(op->getParentOp()))
5818 SmallVector<Value, 8> exprsToScan(op->getOperands());
5823 while (!exprsToScan.empty()) {
5824 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5831 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5832 auto *defOp = readInout.getOperand().getDefiningOp();
5839 if (isa<sv::WireOp>(defOp))
5844 if (!isa<RegOp, LogicOp>(defOp))
5850 if (isa<LogicOp>(defOp) &&
5851 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5855 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5856 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5864 exprsToScan.append(expr->getOperands().begin(),
5865 expr->getOperands().end());
5871 if (expr->getBlock() != op->getBlock())
5876 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5883template <
class AssignTy>
5885 AssignTy singleAssign;
5886 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5887 if (hasSVAttributes(user))
5890 if (auto assign = dyn_cast<AssignTy>(user)) {
5893 singleAssign = assign;
5897 return isa<ReadInOutOp>(user);
5899 return singleAssign;
5905 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5909 if (op1->getBlock() != user->getBlock())
5915 return op1->isBeforeInBlock(user);
5919LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5920 emitSVAttributes(op);
5921 auto value = op->getResult(0);
5922 SmallPtrSet<Operation *, 8> opsForLocation;
5923 opsForLocation.insert(op);
5925 ps.addCallback({op,
true});
5928 auto type = value.getType();
5934 bool singleBitDefaultType = !isa<LocalParamOp>(op);
5936 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5937 unsigned targetColumn = 0;
5938 unsigned column = 0;
5941 if (maxDeclNameWidth > 0)
5942 targetColumn += maxDeclNameWidth + 1;
5945 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5946 }
else if (!word.empty()) {
5948 column += word.size();
5949 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5950 ps.spaces(numSpaces);
5951 column += numSpaces;
5954 SmallString<8> typeString;
5957 llvm::raw_svector_ostream stringStream(typeString);
5960 true, singleBitDefaultType);
5963 if (maxTypeWidth > 0)
5964 targetColumn += maxTypeWidth + 1;
5965 unsigned numSpaces = 0;
5966 if (!typeString.empty()) {
5968 column += typeString.size();
5971 if (targetColumn > column)
5972 numSpaces = targetColumn - column;
5973 ps.spaces(numSpaces);
5974 column += numSpaces;
5980 ps.invokeWithStringOS(
5981 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5984 if (state.options.printDebugInfo) {
5985 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5986 auto innerSym = innerSymOp.getInnerSymAttr();
5987 if (innerSym && !innerSym.empty()) {
5989 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5995 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5996 ps << PP::space <<
"=" << PP::space;
5997 ps.invokeWithStringOS([&](
auto &os) {
5998 emitter.printParamValue(localparam.getValue(), os, [&]() {
5999 return op->emitOpError(
"invalid localparam value");
6004 if (
auto regOp = dyn_cast<RegOp>(op)) {
6005 if (
auto initValue = regOp.getInit()) {
6006 ps << PP::space <<
"=" << PP::space;
6007 ps.scopedBox(PP::ibox0, [&]() {
6008 emitExpression(initValue, opsForLocation, LowestPrecedence,
6017 if (isa<sv::WireOp>(op) &&
6018 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6021 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6022 auto *source = singleAssign.getSrc().getDefiningOp();
6026 if (!source || isa<ConstantOp>(source) ||
6027 op->getNextNode() == singleAssign) {
6028 ps << PP::space <<
"=" << PP::space;
6029 ps.scopedBox(PP::ibox0, [&]() {
6030 emitExpression(singleAssign.getSrc(), opsForLocation,
6034 emitter.assignsInlined.insert(singleAssign);
6042 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
6045 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6048 auto *source = singleAssign.getSrc().getDefiningOp();
6052 if (!source || isa<ConstantOp>(source) ||
6055 ps << PP::space <<
"=" << PP::space;
6056 ps.scopedBox(PP::ibox0, [&]() {
6057 emitExpression(singleAssign.getSrc(), opsForLocation,
6062 emitter.assignsInlined.insert(singleAssign);
6063 emitter.expressionsEmittedIntoDecl.insert(op);
6070 ps.addCallback({op,
false});
6071 emitLocationInfoAndNewLine(opsForLocation);
6075void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6078 NameCollector collector(emitter);
6079 collector.collectNames(block);
6082 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6083 maxTypeWidth = collector.getMaxTypeWidth();
6086void StmtEmitter::emitStatementBlock(Block &body) {
6087 ps.scopedBox(PP::bbox2, [&]() {
6092 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6093 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6098 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6099 collectNamesAndCalculateDeclarationWidths(body);
6102 for (
auto &op : body) {
6109void ModuleEmitter::emitStatement(Operation *op) {
6110 StmtEmitter(*
this, state.options).emitStatement(op);
6115void ModuleEmitter::emitSVAttributes(Operation *op) {
6123 setPendingNewline();
6130void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6131 auto verilogName =
module.getVerilogModuleNameAttr();
6133 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6135 setPendingNewline();
6144void ModuleEmitter::emitBind(BindOp op) {
6146 emitError(op,
"SV attributes emission is unimplemented for the op");
6147 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6153 Operation *childMod =
6154 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6158 ps.addCallback({op,
true});
6159 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6160 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6162 bool isFirst =
true;
6163 ps.scopedBox(PP::bbox2, [&]() {
6164 auto parentPortInfo = parentMod.getPortList();
6168 size_t maxNameLength = 0;
6169 for (
auto &elt : childPortInfo) {
6170 auto portName = elt.getVerilogName();
6171 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6172 maxNameLength = std::max(maxNameLength, elt.getName().size());
6175 SmallVector<Value> instPortValues(childPortInfo.size());
6176 inst.getValues(instPortValues, childPortInfo);
6178 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6180 Value portVal = instPortValues[idx];
6186 bool shouldPrintComma =
true;
6188 shouldPrintComma =
false;
6189 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6191 shouldPrintComma =
true;
6196 if (shouldPrintComma)
6209 ps << PP::neverbox <<
"//";
6213 ps.nbsp(maxNameLength - elt.getName().size());
6215 llvm::SmallPtrSet<Operation *, 4> ops;
6216 if (elt.isOutput()) {
6217 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6218 "output port must have either single or no use");
6219 if (portVal.use_empty()) {
6220 ps <<
"/* unused */";
6221 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6222 portVal.getUses().begin()->getOwner())) {
6225 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6227 parentPortList.atOutput(outputPortNo).getVerilogName());
6229 portVal = portVal.getUsers().begin()->getOperand(0);
6230 ExprEmitter(*
this, ops)
6231 .emitExpression(portVal, LowestPrecedence,
6235 ExprEmitter(*
this, ops)
6236 .emitExpression(portVal, LowestPrecedence,
6249 ps.addCallback({op,
false});
6250 setPendingNewline();
6253void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6255 emitError(op,
"SV attributes emission is unimplemented for the op");
6257 auto instance = op.getReferencedInstance(&state.symbolCache);
6259 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6260 instance.getInterfaceType().getInterface());
6262 ps.addCallback({op,
true});
6263 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6264 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6266 ps.addCallback({op,
false});
6267 setPendingNewline();
6270void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6274 auto printParamType = [&](Type type, Attribute defaultValue,
6275 SmallString<8> &result) {
6277 llvm::raw_svector_ostream sstream(result);
6282 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6283 if (intAttr.getValue().getBitWidth() == 32)
6285 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6286 if (fpAttr.getType().isF64())
6289 if (isa<NoneType>(type))
6296 if (
auto intType = type_dyn_cast<IntegerType>(type))
6297 if (intType.getWidth() == 32) {
6298 sstream <<
"/*integer*/";
6302 printPackedType(type, sstream, module->getLoc(),
6310 size_t maxTypeWidth = 0;
6311 SmallString<8> scratch;
6312 for (
auto param : params) {
6313 auto paramAttr = cast<ParamDeclAttr>(param);
6315 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6316 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6319 if (maxTypeWidth > 0)
6322 ps.scopedBox(PP::bbox2, [&]() {
6323 ps << PP::newline <<
"#(";
6324 ps.scopedBox(PP::cbox0, [&]() {
6327 [&](Attribute param) {
6328 auto paramAttr = cast<ParamDeclAttr>(param);
6329 auto defaultValue = paramAttr.getValue();
6331 printParamType(paramAttr.getType(), defaultValue, scratch);
6332 if (!scratch.empty())
6334 if (scratch.size() < maxTypeWidth)
6335 ps.nbsp(maxTypeWidth - scratch.size());
6337 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6338 module, paramAttr.getName()));
6342 ps.invokeWithStringOS([&](
auto &os) {
6344 return module->emitError("parameter '")
6345 << paramAttr.getName().getValue()
6346 << "' has invalid value";
6351 [&]() { ps <<
"," << PP::newline; });
6357void ModuleEmitter::emitPortList(Operation *module,
6359 bool emitAsTwoStateType) {
6361 if (portInfo.
size())
6362 emitLocationInfo(module->getLoc());
6366 bool hasOutputs =
false, hasZeroWidth =
false;
6367 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6368 SmallVector<SmallString<8>, 16> portTypeStrings;
6370 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6371 auto port = portInfo.
at(i);
6375 lastNonZeroPort = i;
6378 portTypeStrings.push_back({});
6380 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6382 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6385 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6388 if (maxTypeWidth > 0)
6392 ps.scopedBox(PP::bbox2, [&]() {
6393 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6394 auto lastPort = e - 1;
6397 auto portType = portInfo.
at(portIdx).
type;
6401 bool isZeroWidth =
false;
6406 ps << (isZeroWidth ?
"// " :
" ");
6410 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6411 switch (thisPortDirection) {
6412 case ModulePort::Direction::Output:
6415 case ModulePort::Direction::Input:
6416 ps << (hasOutputs ?
"input " :
"input ");
6418 case ModulePort::Direction::InOut:
6419 ps << (hasOutputs ?
"inout " :
"inout ");
6422 bool emitWireInPorts = state.options.emitWireInPorts;
6423 if (emitWireInPorts)
6427 if (!portTypeStrings[portIdx].
empty())
6428 ps << portTypeStrings[portIdx];
6429 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6430 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6432 size_t startOfNamePos =
6433 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6439 ps.invokeWithStringOS(
6440 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6443 auto innerSym = portInfo.
at(portIdx).
getSym();
6444 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6446 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6451 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6455 if (
auto loc = portInfo.
at(portIdx).
loc)
6456 emitLocationInfo(loc);
6466 if (!state.options.disallowPortDeclSharing) {
6467 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6470 auto port = portInfo.
at(portIdx);
6474 bool isZeroWidth =
false;
6479 ps << (isZeroWidth ?
"// " :
" ");
6482 ps.nbsp(startOfNamePos);
6485 StringRef name = port.getVerilogName();
6489 ps.invokeWithStringOS(
6490 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6493 auto sym = port.getSym();
6494 if (state.options.printDebugInfo && sym && !sym.empty())
6495 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6499 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6503 if (
auto loc = port.loc)
6504 emitLocationInfo(loc);
6515 if (!portInfo.
size()) {
6517 SmallPtrSet<Operation *, 8> moduleOpSet;
6518 moduleOpSet.insert(module);
6519 emitLocationInfoAndNewLine(moduleOpSet);
6522 ps <<
");" << PP::newline;
6523 setPendingNewline();
6527void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6528 currentModuleOp =
module;
6530 emitComment(module.getCommentAttr());
6531 emitSVAttributes(module);
6533 ps.addCallback({module,
true});
6537 emitParameters(module, module.getParameters());
6541 assert(state.pendingNewline);
6544 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6547 ps.addCallback({module,
false});
6549 setPendingNewline();
6551 currentModuleOp =
nullptr;
6554void ModuleEmitter::emitFunc(FuncOp func) {
6556 if (func.isDeclaration())
6559 currentModuleOp = func;
6561 ps.addCallback({func,
true});
6565 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6567 ps <<
"endfunction";
6569 currentModuleOp =
nullptr;
6578 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6585 void emit(emit::FileListOp op);
6588 void emit(Block *block);
6590 void emitOp(emit::RefOp op);
6591 void emitOp(emit::VerbatimOp op);
6595 for (Operation &op : *block) {
6596 TypeSwitch<Operation *>(&op)
6597 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6598 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6599 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6600 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6601 .Case<BindInterfaceOp>(
6602 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6603 .Case<TypeScopeOp>([&](
auto typedecls) {
6604 ModuleEmitter(state).emitStatement(typedecls);
6607 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6613 for (
auto sym : op.getFiles()) {
6614 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6616 auto it = state.fileMapping.find(fileName);
6617 if (it == state.fileMapping.end()) {
6618 emitOpError(op,
" references an invalid file: ") << sym;
6622 auto file = cast<emit::FileOp>(it->second);
6623 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6630 StringAttr target = op.getTargetAttr().getAttr();
6631 auto *targetOp = state.symbolCache.getDefinition(target);
6632 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6634 TypeSwitch<Operation *>(targetOp)
6635 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6636 .Case<hw::HWModuleOp>(
6637 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6638 .Case<TypeScopeOp>([&](
auto typedecls) {
6639 ModuleEmitter(state).emitStatement(typedecls);
6642 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6648 SmallPtrSet<Operation *, 8> ops;
6653 StringRef text = op.getText();
6657 const auto &[lhs, rhs] = text.split(
'\n');
6661 ps << PP::end << PP::newline << PP::neverbox;
6663 }
while (!text.empty());
6666 emitLocationInfoAndNewLine(ops);
6684 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6685 moduleOp->walk([&](Operation *op) {
6687 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6690 SymbolTable::getSymbolAttrName()),
6691 name.getSymName(), op);
6692 if (isa<BindOp>(op))
6698 auto collectPorts = [&](
auto moduleOp) {
6699 auto portInfo = moduleOp.getPortList();
6700 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6701 if (!p.attrs || p.attrs.empty())
6703 for (NamedAttribute portAttr : p.attrs) {
6704 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6713 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6714 for (
auto file :
designOp.getOps<emit::FileOp>())
6715 for (
auto refs : file.getOps<emit::RefOp>())
6716 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6718 SmallString<32> outputPath;
6719 for (
auto &op : *
designOp.getBody()) {
6722 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6724 bool hasFileName =
false;
6725 bool emitReplicatedOps = !isFileOp;
6726 bool addToFilelist = !isFileOp;
6732 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6734 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6735 <<
" on " << op <<
"\n";);
6736 if (!attr.isDirectory())
6739 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6740 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6743 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6748 if (!defaultFileName.isTriviallyEmpty()) {
6749 llvm::sys::path::append(outputPath, defaultFileName);
6751 op->emitError(
"file name unspecified");
6753 llvm::sys::path::append(outputPath,
"error.out");
6757 auto destFile = StringAttr::get(op->getContext(), outputPath);
6758 auto &file =
files[destFile];
6759 file.ops.push_back(info);
6760 file.emitReplicatedOps = emitReplicatedOps;
6761 file.addToFilelist = addToFilelist;
6762 file.isVerilog = outputPath.ends_with(
".sv");
6767 if (!attr || attr.isDirectory()) {
6768 auto excludeFromFileListAttr =
6769 BoolAttr::get(op->getContext(), !addToFilelist);
6770 auto includeReplicatedOpsAttr =
6771 BoolAttr::get(op->getContext(), emitReplicatedOps);
6772 auto outputFileAttr = hw::OutputFileAttr::get(
6773 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6774 op->setAttr(
"output_file", outputFileAttr);
6780 TypeSwitch<Operation *>(&op)
6781 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6783 fileMapping.try_emplace(file.getSymNameAttr(), file);
6784 separateFile(file, file.getFileName());
6786 .Case<emit::FragmentOp>([&](
auto fragment) {
6789 .Case<HWModuleOp>([&](
auto mod) {
6791 auto sym = mod.getNameAttr();
6794 collectInstanceSymbolsAndBinds(mod);
6796 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6797 if (it->second.size() != 1 || attr) {
6800 op.emitError(
"modules can be emitted to a single file");
6808 if (attr || separateModules)
6814 .Case<InterfaceOp>([&](InterfaceOp intf) {
6819 for (
auto &op : *intf.getBodyBlock())
6820 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6821 if (
auto name = symOp.getNameAttr())
6825 if (attr || separateModules)
6826 separateFile(intf, intf.getSymName() +
".sv");
6836 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6837 [&](Operation *op) {
6843 separateFile(op,
"");
6845 .Case<FuncOp>([&](
auto op) {
6851 separateFile(op,
"");
6855 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6858 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6867 separateFile(op,
"");
6869 .Case<BindOp>([&](
auto op) {
6871 separateFile(op,
"bindfile.sv");
6876 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
6877 .Case<MacroDeclOp>([&](
auto op) {
6880 .Case<sv::ReserveNamesOp>([](
auto op) {
6883 .Case<om::ClassLike>([&](
auto op) {
6886 .Case<om::ConstantOp>([&](
auto op) {
6889 .Default([&](
auto *) {
6890 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6910 size_t lastReplicatedOp = 0;
6912 bool emitHeaderInclude =
6915 if (emitHeaderInclude)
6918 size_t numReplicatedOps =
6923 DenseSet<emit::FragmentOp> includedFragments;
6924 for (
const auto &opInfo : file.
ops) {
6925 Operation *op = opInfo.op;
6929 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6935 if (
auto fragments =
6937 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6941 op->emitError(
"cannot find referenced fragment ") << sym;
6944 emit::FragmentOp fragment = it->second;
6945 if (includedFragments.insert(fragment).second) {
6946 thingsToEmit.emplace_back(it->second);
6952 thingsToEmit.emplace_back(op);
6957 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6962 TypeSwitch<Operation *>(op)
6963 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6964 .Case<HWModuleExternOp>([&](
auto op) {
6967 .Case<HWModuleGeneratedOp>(
6968 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6969 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6970 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6971 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6972 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6973 .Case<TypeScopeOp>([&](
auto typedecls) {
6974 ModuleEmitter(state).emitStatement(typedecls);
6976 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6978 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
6979 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6980 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6981 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6982 .Default([&](
auto *op) {
6983 state.encounteredError =
true;
6984 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6991 llvm::formatted_raw_ostream &os,
6992 StringAttr fileName,
bool parallelize) {
6993 MLIRContext *context =
designOp->getContext();
6997 parallelize &= context->isMultithreadingEnabled();
7008 size_t lineOffset = 0;
7009 for (
auto &entry : thingsToEmit) {
7010 entry.verilogLocs.setStream(os);
7011 if (
auto *op = entry.getOperation()) {
7016 state.addVerilogLocToOps(lineOffset, fileName);
7018 os << entry.getStringData();
7023 if (state.encounteredError)
7041 SmallString<256> buffer;
7042 llvm::raw_svector_ostream tmpStream(buffer);
7043 llvm::formatted_raw_ostream rs(tmpStream);
7054 for (
auto &entry : thingsToEmit) {
7057 auto *op = entry.getOperation();
7059 auto lineOffset = os.getLine() + 1;
7060 os << entry.getStringData();
7064 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7067 entry.verilogLocs.setStream(os);
7074 state.addVerilogLocToOps(0, fileName);
7090 module.emitWarning()
7091 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7092 "created only at SplitExportVerilog";
7101 for (
const auto &it : emitter.
files) {
7102 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7103 "\" ----- 8< -----\n\n");
7109 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7110 "\" ----- 8< -----\n\n");
7111 for (
auto &name : it.second)
7112 contents += name.str() +
"\n";
7113 list.emplace_back(contents);
7116 llvm::formatted_raw_ostream rs(os);
7120 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7129 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7131 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7132 if (failed(failableParallelForEach(
7133 module->getContext(), modulesToPrepare,
7134 [&](
auto op) { return prepareHWModule(op, options); })))
7141struct ExportVerilogPass
7142 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7143 ExportVerilogPass(raw_ostream &os) : os(os) {}
7144 void runOnOperation()
override {
7146 mlir::OpPassManager preparePM(
"builtin.module");
7147 preparePM.addPass(createLegalizeAnonEnums());
7149 auto &modulePM = preparePM.nestAny();
7150 modulePM.addPass(createPrepareForEmission());
7151 if (failed(runPipeline(preparePM, getOperation())))
7152 return signalPassFailure();
7155 return signalPassFailure();
7162struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7163 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7164 : ExportVerilogPass{*os} {
7165 owned = std::move(os);
7169 std::unique_ptr<llvm::raw_ostream> owned;
7173std::unique_ptr<mlir::Pass>
7175 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7178std::unique_ptr<mlir::Pass>
7180 return std::make_unique<ExportVerilogPass>(os);
7191static std::unique_ptr<llvm::ToolOutputFile>
7195 SmallString<128> outputFilename(dirname);
7197 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7200 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7202 emitter.
designOp.emitError(
"cannot create output directory \"")
7203 << outputDir <<
"\": " << error.message();
7209 std::string errorMessage;
7210 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7212 emitter.
designOp.emitError(errorMessage);
7229 llvm::formatted_raw_ostream rs(output->os());
7235 StringAttr::get(fileName.getContext(), output->getFilename()),
7241 StringRef dirname) {
7252 bool insertSuccess =
7254 .insert({StringAttr::get(module.getContext(),
circtHeader),
7260 if (!insertSuccess) {
7261 module.emitError() << "tried to emit a heder to " << circtHeader
7262 << ", but the file is used as an output too.";
7268 parallelForEach(module->getContext(), emitter.
files.begin(),
7269 emitter.
files.end(), [&](
auto &it) {
7270 createSplitOutputFile(it.first, it.second, dirname,
7275 SmallString<128> filelistPath(dirname);
7276 llvm::sys::path::append(filelistPath,
"filelist.f");
7278 std::string errorMessage;
7279 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7281 module->emitError(errorMessage);
7285 for (
const auto &it : emitter.
files) {
7286 if (it.second.addToFilelist)
7287 output->os() << it.first.str() <<
"\n";
7296 for (
auto &name : it.second)
7297 output->os() << name.str() <<
"\n";
7308 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7310 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7311 if (failed(failableParallelForEach(
7312 module->getContext(), modulesToPrepare,
7313 [&](
auto op) { return prepareHWModule(op, options); })))
7321struct ExportSplitVerilogPass
7322 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7323 ExportSplitVerilogPass(StringRef directory) {
7324 directoryName = directory.str();
7326 void runOnOperation()
override {
7328 mlir::OpPassManager preparePM(
"builtin.module");
7332 modulePM.addPass(createPrepareForEmission());
7333 if (failed(runPipeline(preparePM, getOperation())))
7334 return signalPassFailure();
7337 return signalPassFailure();
7342std::unique_ptr<mlir::Pass>
7344 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.