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);
126static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &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;
266static void getTypeDims(SmallVectorImpl<Attribute> &dims, 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 return WalkResult::advance();
829 TypeSwitch<Operation *, unsigned>(op)
830 .Case<VerbatimOp>([&](
auto) {
836 .Case<IfOp>([&](
auto) {
847 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
848 .Case<OutputOp>([&](OutputOp oop) {
851 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
852 Operation *op = operand.getDefiningOp();
853 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
856 .Default([](
auto) {
return 1; });
857 if (numStatements > 1)
858 return WalkResult::interrupt();
859 return WalkResult::advance();
861 if (numStatements == 0)
863 if (numStatements == 1)
873 if (op->getResult(0).use_empty())
878 if (op->hasOneUse() &&
879 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
880 *op->getUsers().begin()))
902 for (
auto &op : *elseBlock) {
903 if (
auto opIf = dyn_cast<IfOp>(op)) {
920template <
typename PPS>
922 enum Container { NoContainer, InComment, InAttr };
923 Container currentContainer = NoContainer;
925 auto closeContainer = [&] {
926 if (currentContainer == NoContainer)
928 if (currentContainer == InComment)
930 else if (currentContainer == InAttr)
932 ps << PP::end << PP::end;
934 currentContainer = NoContainer;
937 bool isFirstContainer =
true;
938 auto openContainer = [&](Container newContainer) {
939 assert(newContainer != NoContainer);
940 if (currentContainer == newContainer)
944 if (!isFirstContainer)
945 ps << (mayBreak ? PP::space : PP::nbsp);
946 isFirstContainer =
false;
949 if (newContainer == InComment)
951 else if (newContainer == InAttr)
953 currentContainer = newContainer;
961 ps.scopedBox(PP::cbox0, [&]() {
962 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
963 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
965 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
967 if (attr.getExpression())
968 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
977 if (
auto *op = val.getDefiningOp())
980 if (
auto port = dyn_cast<BlockArgument>(val)) {
982 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
983 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
985 port.getArgNumber());
987 assert(
false &&
"unhandled value");
999class VerilogEmitterState {
1001 explicit VerilogEmitterState(ModuleOp designOp,
1007 llvm::formatted_raw_ostream &os,
1008 StringAttr fileName,
OpLocMap &verilogLocMap)
1009 : designOp(designOp), shared(shared), options(options),
1010 symbolCache(symbolCache), globalNames(globalNames),
1011 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1012 pp(os, options.emittedLineLength), fileName(fileName) {
1013 pp.setListener(&saver);
1036 llvm::formatted_raw_ostream &os;
1038 bool encounteredError =
false;
1047 bool pendingNewline =
false;
1061 StringAttr fileName;
1067 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1070 verilogLocMap.
clear();
1074 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1075 void operator=(
const VerilogEmitterState &) =
delete;
1088using CallbackDataTy = std::pair<Operation *, bool>;
1092 VerilogEmitterState &state;
1097 explicit EmitterBase(VerilogEmitterState &state)
1099 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1101 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1102 state.encounteredError =
true;
1103 return op->emitError(message);
1106 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1107 state.encounteredError =
true;
1108 return op->emitOpError(message);
1111 void emitLocationImpl(llvm::StringRef location) {
1114 ps << PP::neverbreak;
1115 if (!location.empty())
1116 ps <<
"\t// " << location;
1119 void emitLocationInfo(Location loc) {
1127 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1130 setPendingNewline();
1133 template <
typename PPS>
1134 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1135 llvm::function_ref<
void(Value)> operandEmitter,
1136 ArrayAttr symAttrs);
1142 void emitComment(StringAttr comment);
1146 void emitPendingNewlineIfNeeded() {
1147 if (state.pendingNewline) {
1148 state.pendingNewline =
false;
1152 void setPendingNewline() {
1153 assert(!state.pendingNewline);
1154 state.pendingNewline =
true;
1157 void startStatement() { emitPendingNewlineIfNeeded(); }
1160 void operator=(
const EmitterBase &) =
delete;
1161 EmitterBase(
const EmitterBase &) =
delete;
1165template <
typename PPS>
1166void EmitterBase::emitTextWithSubstitutions(
1167 PPS &ps, StringRef
string, Operation *op,
1168 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1179 if (
auto *itemOp = item.getOp()) {
1180 if (item.hasPort()) {
1184 if (!symOpName.empty())
1186 emitError(itemOp,
"cannot get name for symbol ") << sym;
1188 emitError(op,
"cannot get name for symbol ") << sym;
1190 return StringRef(
"<INVALID>");
1196 unsigned numSymOps = symAttrs.size();
1197 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1200 next =
string.find(
"{{", next);
1201 if (next == StringRef::npos)
1208 while (next <
string.size() &&
isdigit(
string[next]))
1211 if (start == next) {
1215 size_t operandNoLength = next - start;
1218 StringRef fmtOptsStr;
1219 if (
string[next] ==
':') {
1220 size_t startFmtOpts = next + 1;
1221 while (next <
string.size() &&
string[next] !=
'}')
1223 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1227 if (!
string.substr(next).starts_with(
"}}"))
1231 unsigned operandNo = 0;
1232 if (
string.drop_front(start)
1233 .take_front(operandNoLength)
1234 .getAsInteger(10, operandNo)) {
1235 emitError(op,
"operand substitution too large");
1241 auto before =
string.take_front(start - 2);
1242 if (!before.empty())
1247 if (operandNo < op->getNumOperands())
1249 operandEmitter(op->getOperand(operandNo));
1250 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1251 unsigned symOpNum = operandNo - op->getNumOperands();
1252 auto sym = symAttrs[symOpNum];
1253 StringRef symVerilogName;
1254 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1255 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1256 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1257 auto namepath = globalRef.getNamepathAttr().getValue();
1258 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1261 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1263 auto innerRef = cast<InnerRefAttr>(sym);
1264 auto ref = state.symbolCache.getInnerDefinition(
1265 innerRef.getModule(), innerRef.getName());
1266 ps << namify(innerRef, ref);
1269 symVerilogName = namify(sym, symOp);
1272 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1273 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1275 symVerilogName = namify(sym, symOp);
1277 if (!symVerilogName.empty())
1280 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1284 string =
string.drop_front(next);
1290 while (emitUntilSubstitution())
1294 if (!
string.
empty())
1298void EmitterBase::emitComment(StringAttr comment) {
1305 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1309 auto ref = comment.getValue();
1311 while (!ref.empty()) {
1312 std::tie(line, ref) = ref.split(
"\n");
1319 if (line.size() <= lineLength) {
1321 setPendingNewline();
1332 auto breakPos = line.rfind(
' ', lineLength);
1334 if (breakPos == StringRef::npos) {
1335 breakPos = line.find(
' ', lineLength);
1338 if (breakPos == StringRef::npos)
1339 breakPos = line.size();
1346 setPendingNewline();
1347 breakPos = line.find_first_not_of(
' ', breakPos);
1349 if (breakPos == StringRef::npos)
1352 line = line.drop_front(breakPos);
1362 bool addPrefixUnderScore =
true;
1365 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1369 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1371 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1373 result = StringAttr::get(expr.getContext(), name);
1375 }
else if (
auto *op = expr.getDefiningOp()) {
1377 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1379 result = StringAttr::get(expr.getContext(), name);
1381 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1387 addPrefixUnderScore =
false;
1389 TypeSwitch<Operation *>(op)
1392 .Case([&result](VerbatimExprOp verbatim) {
1393 verbatim.getAsmResultNames([&](Value, StringRef name) {
1394 result = StringAttr::get(verbatim.getContext(), name);
1397 .Case([&result](VerbatimExprSEOp verbatim) {
1398 verbatim.getAsmResultNames([&](Value, StringRef name) {
1399 result = StringAttr::get(verbatim.getContext(), name);
1405 if (
auto operandName =
1408 cast<IntegerType>(extract.getType()).getWidth();
1410 result = StringAttr::get(extract.getContext(),
1411 operandName.strref() +
"_" +
1412 Twine(extract.getLowBit()));
1414 result = StringAttr::get(
1415 extract.getContext(),
1416 operandName.strref() +
"_" +
1417 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1418 Twine(extract.getLowBit()));
1426 if (!result || result.strref().empty())
1430 if (addPrefixUnderScore && result.strref().front() !=
'_')
1431 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1443class ModuleEmitter :
public EmitterBase {
1445 explicit ModuleEmitter(VerilogEmitterState &state)
1446 : EmitterBase(state), currentModuleOp(nullptr),
1450 emitPendingNewlineIfNeeded();
1454 void emitParameters(Operation *module, ArrayAttr params);
1455 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1456 bool emitAsTwoStateType =
false);
1459 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1460 void emitFunc(FuncOp);
1463 void emitStatement(Operation *op);
1464 void emitBind(BindOp op);
1465 void emitBindInterface(BindInterfaceOp op);
1467 void emitSVAttributes(Operation *op);
1470 StringRef getVerilogStructFieldName(StringAttr field) {
1471 return fieldNameResolver.getRenamedFieldName(field).getValue();
1478 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1490 bool printPackedType(Type type, raw_ostream &os, Location loc,
1491 Type optionalAliasType = {},
bool implicitIntType =
true,
1492 bool singleBitDefaultType =
true,
1493 bool emitAsTwoStateType =
false);
1497 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1505 function_ref<InFlightDiagnostic()> emitError);
1508 VerilogPrecedence parenthesizeIfLooserThan,
1509 function_ref<InFlightDiagnostic()> emitError);
1515 Operation *currentModuleOp;
1521 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1527 SmallPtrSet<Operation *, 16> assignsInlined;
1536 const ModuleEmitter &emitter) {
1537 if (isa<RegOp>(op)) {
1542 cast<InOutType>(op->getResult(0).getType()).getElementType();
1549 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1550 while (isa<ArrayType>(innerType.getElementType()))
1551 innerType = cast<ArrayType>(innerType.getElementType());
1552 if (isa<StructType>(innerType.getElementType()) ||
1553 isa<TypeAliasType>(innerType.getElementType()))
1561 if (isa<sv::WireOp>(op))
1563 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1564 return "localparam";
1567 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1568 return interface.getInterfaceType().getInterface().getValue();
1572 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1576 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1578 if (isa<LogicOp>(op)) {
1584 if (isProcedural && !stripAutomatic)
1585 return hasStruct ?
"automatic" :
"automatic logic";
1586 return hasStruct ?
"" :
"logic";
1593 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1596 assert(!emitter.state.options.disallowLocalVariables &&
1597 "automatic variables not allowed");
1601 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1602 :
"automatic logic";
1609static void emitDim(Attribute width, raw_ostream &os, Location loc,
1610 ModuleEmitter &emitter,
bool downTo) {
1612 os <<
"<<invalid type>>";
1615 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1616 if (intAttr.getValue().isZero()) {
1617 os <<
"/*Zero Width*/";
1622 os << (intAttr.getValue().getZExtValue() - 1);
1632 auto typedAttr = dyn_cast<TypedAttr>(width);
1634 mlir::emitError(loc,
"untyped dimension attribute ") << width;
1638 getIntAttr(loc.getContext(), typedAttr.getType(),
1639 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1640 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1644 emitter.printParamValue(width, os, [loc]() {
1645 return mlir::emitError(loc,
"invalid parameter in type");
1609static void emitDim(Attribute width, raw_ostream &os, Location loc, {
…}
1653static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1654 ModuleEmitter &emitter) {
1655 for (Attribute width : dims) {
1656 emitDim(width, os, loc, emitter,
true);
1653static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc, {
…}
1661void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1662 SmallVector<Attribute, 4> dims;
1694 SmallVectorImpl<Attribute> &dims,
1695 bool implicitIntType,
bool singleBitDefaultType,
1696 ModuleEmitter &emitter,
1697 Type optionalAliasType = {},
1698 bool emitAsTwoStateType =
false) {
1699 return TypeSwitch<Type, bool>(type)
1700 .Case<IntegerType>([&](IntegerType integerType) {
1701 if (emitAsTwoStateType && dims.empty()) {
1703 if (!typeName.empty()) {
1708 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1710 getInt32Attr(type.getContext(), integerType.getWidth()));
1712 StringRef typeName =
1713 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1714 if (!typeName.empty()) {
1721 return !dims.empty() || !implicitIntType;
1723 .Case<IntType>([&](IntType intType) {
1724 if (!implicitIntType)
1726 dims.push_back(intType.getWidth());
1730 .Case<ArrayType>([&](ArrayType arrayType) {
1731 dims.push_back(arrayType.getSizeAttr());
1733 implicitIntType, singleBitDefaultType,
1735 emitAsTwoStateType);
1737 .Case<InOutType>([&](InOutType inoutType) {
1739 implicitIntType, singleBitDefaultType,
1741 emitAsTwoStateType);
1743 .Case<EnumType>([&](EnumType enumType) {
1745 if (enumType.getBitWidth() != 32)
1746 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1748 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1749 llvm::interleaveComma(
1750 enumType.getFields().getAsRange<StringAttr>(), os,
1751 [&](
auto enumerator) {
1752 os << emitter.fieldNameResolver.getEnumFieldName(
1753 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1758 .Case<StructType>([&](StructType structType) {
1759 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1760 os <<
"/*Zero Width*/";
1763 os <<
"struct packed {";
1764 for (
auto &element : structType.getElements()) {
1766 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1767 <<
": Zero Width;*/ ";
1770 SmallVector<Attribute, 8> structDims;
1775 {}, emitAsTwoStateType);
1776 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1777 emitter.printUnpackedTypePostfix(element.type, os);
1784 .Case<UnionType>([&](UnionType unionType) {
1785 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1786 os <<
"/*Zero Width*/";
1790 int64_t unionWidth = hw::getBitWidth(unionType);
1791 os <<
"union packed {";
1792 for (
auto &element : unionType.getElements()) {
1794 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1795 <<
": Zero Width;*/ ";
1798 int64_t elementWidth = hw::getBitWidth(element.type);
1799 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1801 os <<
" struct packed {";
1802 if (element.offset) {
1803 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1804 << element.offset - 1 <<
":0] "
1805 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1809 SmallVector<Attribute, 8> structDims;
1813 true, emitter, {}, emitAsTwoStateType);
1814 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1815 emitter.printUnpackedTypePostfix(element.type, os);
1819 if (elementWidth + (int64_t)element.offset < unionWidth) {
1820 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1821 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1822 <<
"__post_padding_" << element.name.getValue() <<
";";
1824 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1833 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1834 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1835 os <<
"<<unexpected unpacked array>>";
1836 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1840 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1841 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1843 mlir::emitError(loc,
"unresolvable type reference");
1846 if (typedecl.getType() != typeRef.getInnerType()) {
1847 mlir::emitError(loc,
"declared type did not match aliased type");
1851 os << typedecl.getPreferredName();
1852 emitDims(dims, os, typedecl->getLoc(), emitter);
1855 .Default([&](Type type) {
1856 os <<
"<<invalid type '" << type <<
"'>>";
1857 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1873bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1874 Type optionalAliasType,
1875 bool implicitIntType,
1876 bool singleBitDefaultType,
1877 bool emitAsTwoStateType) {
1878 SmallVector<Attribute, 8> packedDimensions;
1880 singleBitDefaultType, *
this, optionalAliasType,
1881 emitAsTwoStateType);
1887void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1888 TypeSwitch<Type, void>(type)
1890 printUnpackedTypePostfix(inoutType.getElementType(), os);
1892 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1893 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1894 : state.designOp->getLoc();
1895 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1897 printUnpackedTypePostfix(arrayType.getElementType(), os);
1899 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1901 printUnpackedTypePostfix(arrayType.getElementType(), os);
1903 .Case<InterfaceType>([&](
auto) {
1917ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1918 function_ref<InFlightDiagnostic()> emitError) {
1919 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1927ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1928 VerilogPrecedence parenthesizeIfLooserThan,
1929 function_ref<InFlightDiagnostic()> emitError) {
1930 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1931 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1932 APInt value = intAttr.getValue();
1936 if (intTy.getWidth() > 32) {
1938 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1942 if (intTy.isSigned())
1943 os << intTy.getWidth() <<
"'sd";
1945 os << intTy.getWidth() <<
"'d";
1947 value.print(os, intTy.isSigned());
1948 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1950 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1952 os.write_escaped(strAttr.getValue());
1954 return {Symbol, IsUnsigned};
1956 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1958 os << fpAttr.getValueAsDouble();
1959 return {Symbol, IsUnsigned};
1961 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1962 os << verbatimParam.getValue().getValue();
1963 return {Symbol, IsUnsigned};
1965 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1967 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1968 parameterRef.getName());
1971 return {Symbol, IsUnsigned};
1975 auto expr = dyn_cast<ParamExprAttr>(value);
1977 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1978 emitError() <<
" = " << value;
1979 return {LowestPrecedence, IsUnsigned};
1982 StringRef operatorStr;
1983 StringRef openStr, closeStr;
1984 VerilogPrecedence subprecedence = LowestPrecedence;
1985 VerilogPrecedence prec;
1986 std::optional<SubExprSignResult> operandSign;
1987 bool isUnary =
false;
1988 bool hasOpenClose =
false;
1990 switch (expr.getOpcode()) {
1992 operatorStr =
" + ";
1993 subprecedence = Addition;
1996 operatorStr =
" * ";
1997 subprecedence = Multiply;
2000 operatorStr =
" & ";
2001 subprecedence = And;
2004 operatorStr =
" | ";
2008 operatorStr =
" ^ ";
2009 subprecedence = Xor;
2012 operatorStr =
" << ";
2013 subprecedence = Shift;
2017 operatorStr =
" >> ";
2018 subprecedence = Shift;
2022 operatorStr =
" >>> ";
2023 subprecedence = Shift;
2024 operandSign = IsSigned;
2027 operatorStr =
" / ";
2028 subprecedence = Multiply;
2029 operandSign = IsUnsigned;
2032 operatorStr =
" / ";
2033 subprecedence = Multiply;
2034 operandSign = IsSigned;
2037 operatorStr =
" % ";
2038 subprecedence = Multiply;
2039 operandSign = IsUnsigned;
2042 operatorStr =
" % ";
2043 subprecedence = Multiply;
2044 operandSign = IsSigned;
2047 openStr =
"$clog2(";
2049 operandSign = IsUnsigned;
2050 hasOpenClose =
true;
2053 case PEO::StrConcat:
2056 hasOpenClose =
true;
2059 subprecedence = LowestPrecedence;
2064 prec = subprecedence;
2067 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2069 assert(isUnary || hasOpenClose ||
2070 !llvm::hasSingleElement(expr.getOperands()));
2077 auto emitOperand = [&](Attribute operand) ->
bool {
2079 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2080 if (operandSign.has_value())
2081 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2084 if (operandSign.has_value()) {
2086 signedness = *operandSign;
2088 return signedness == IsSigned;
2092 if (prec > parenthesizeIfLooserThan)
2101 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2102 for (
auto op : expr.getOperands().drop_front()) {
2105 if (expr.getOpcode() == PEO::Add) {
2106 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2107 const APInt &value = integer.getValue();
2108 if (value.isNegative() && !value.isMinSignedValue()) {
2110 allOperandsSigned &=
2111 emitOperand(IntegerAttr::get(op.getType(), -value));
2118 allOperandsSigned &= emitOperand(op);
2122 if (prec > parenthesizeIfLooserThan) {
2126 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2141class ExprEmitter :
public EmitterBase,
2143 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2148 ExprEmitter(ModuleEmitter &emitter,
2149 SmallPtrSetImpl<Operation *> &emittedExprs)
2150 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2152 ExprEmitter(ModuleEmitter &emitter,
2153 SmallPtrSetImpl<Operation *> &emittedExprs,
2155 : EmitterBase(emitter.state), emitter(emitter),
2156 emittedExprs(emittedExprs), buffer(tokens),
2157 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2158 assert(state.pp.getListener() == &state.saver);
2165 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2166 bool isAssignmentLikeContext) {
2167 assert(localTokens.empty());
2169 ps.scopedBox(PP::ibox0, [&]() {
2170 emitSubExpr(exp, parenthesizeIfLooserThan,
2173 isAssignmentLikeContext);
2178 if (&buffer.tokens == &localTokens)
2179 buffer.flush(state.pp);
2184 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2185 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2187 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2195 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2196 SubExprSignRequirement signReq = NoRequirement,
2197 bool isSelfDeterminedUnsignedValue =
false,
2198 bool isAssignmentLikeContext =
false);
2202 void emitSVAttributes(Operation *op);
2204 SubExprInfo visitUnhandledExpr(Operation *op);
2205 SubExprInfo visitInvalidComb(Operation *op) {
2208 SubExprInfo visitUnhandledComb(Operation *op) {
2209 return visitUnhandledExpr(op);
2212 return dispatchSVVisitor(op);
2215 return visitUnhandledExpr(op);
2217 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2220 enum EmitBinaryFlags {
2221 EB_RequireSignedOperands = RequireSigned,
2222 EB_RequireUnsignedOperands = RequireUnsigned,
2223 EB_OperandSignRequirementMask = 0x3,
2228 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2232 EB_ForceResultSigned = 0x8,
2237 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2238 const char *syntax,
unsigned emitBinaryFlags = 0);
2240 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2241 bool resultAlwaysUnsigned =
false);
2244 void emitSubExprIBox2(
2245 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2246 ps.scopedBox(PP::ibox2,
2247 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2252 template <
typename Container,
typename EachFn>
2253 void interleaveComma(
const Container &c, EachFn eachFn) {
2254 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2259 void interleaveComma(ValueRange ops) {
2260 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2277 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2279 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2280 CloseFunc closeFn) {
2282 ps.scopedBox(PP::cbox0, [&]() {
2283 interleaveComma(c, eachFn);
2289 template <
typename OpenFunc,
typename CloseFunc>
2290 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2291 return emitBracedList(
2292 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2296 void emitBracedList(ValueRange ops) {
2297 return emitBracedList(
2298 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2302 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2305 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2306 bool printAsPattern, Operation *op);
2308 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2309 ArrayAttr fieldValues,
bool printAsPattern,
2312 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2314 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2315 SubExprInfo visitSV(GetModportOp op);
2316 SubExprInfo visitSV(SystemFunctionOp op);
2317 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2318 SubExprInfo visitSV(XMROp op);
2319 SubExprInfo visitSV(SFormatFOp op);
2320 SubExprInfo visitSV(XMRRefOp op);
2321 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2322 SubExprInfo visitSV(VerbatimExprOp op) {
2323 return visitVerbatimExprOp(op, op.getSymbols());
2325 SubExprInfo visitSV(VerbatimExprSEOp op) {
2326 return visitVerbatimExprOp(op, op.getSymbols());
2328 SubExprInfo visitSV(MacroRefExprOp op);
2329 SubExprInfo visitSV(MacroRefExprSEOp op);
2330 template <
typename MacroTy>
2331 SubExprInfo emitMacroCall(MacroTy op);
2333 SubExprInfo visitSV(ConstantXOp op);
2334 SubExprInfo visitSV(ConstantZOp op);
2335 SubExprInfo visitSV(ConstantStrOp op);
2337 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2338 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2340 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2345 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2346 emitSVAttributes(op);
2349 SubExprInfo visitSV(ArrayIndexInOutOp op);
2350 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2351 SubExprInfo visitSV(IndexedPartSelectOp op);
2352 SubExprInfo visitSV(StructFieldInOutOp op);
2355 SubExprInfo visitSV(SampledOp op);
2358 SubExprInfo visitSV(TimeOp op);
2359 SubExprInfo visitSV(STimeOp op);
2362 using TypeOpVisitor::visitTypeOp;
2364 SubExprInfo visitTypeOp(AggregateConstantOp op);
2366 SubExprInfo visitTypeOp(ParamValueOp op);
2373 SubExprInfo visitTypeOp(StructInjectOp op);
2374 SubExprInfo visitTypeOp(UnionCreateOp op);
2375 SubExprInfo visitTypeOp(UnionExtractOp op);
2376 SubExprInfo visitTypeOp(EnumCmpOp op);
2377 SubExprInfo visitTypeOp(EnumConstantOp op);
2380 using CombinationalVisitor::visitComb;
2381 SubExprInfo visitComb(
MuxOp op);
2382 SubExprInfo visitComb(
AddOp op) {
2383 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2384 return emitBinary(op, Addition,
"+");
2386 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2387 SubExprInfo visitComb(
MulOp op) {
2388 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2389 return emitBinary(op, Multiply,
"*");
2391 SubExprInfo visitComb(
DivUOp op) {
2392 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2394 SubExprInfo visitComb(
DivSOp op) {
2395 return emitBinary(op, Multiply,
"/",
2396 EB_RequireSignedOperands | EB_ForceResultSigned);
2398 SubExprInfo visitComb(
ModUOp op) {
2399 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2401 SubExprInfo visitComb(
ModSOp op) {
2402 return emitBinary(op, Multiply,
"%",
2403 EB_RequireSignedOperands | EB_ForceResultSigned);
2405 SubExprInfo visitComb(
ShlOp op) {
2406 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2408 SubExprInfo visitComb(
ShrUOp op) {
2410 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2412 SubExprInfo visitComb(
ShrSOp op) {
2415 return emitBinary(op, Shift,
">>>",
2416 EB_RequireSignedOperands | EB_ForceResultSigned |
2417 EB_RHS_UnsignedWithSelfDeterminedWidth);
2419 SubExprInfo visitComb(
AndOp op) {
2420 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2421 return emitBinary(op, And,
"&");
2423 SubExprInfo visitComb(
OrOp op) {
2424 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2425 return emitBinary(op, Or,
"|");
2427 SubExprInfo visitComb(
XorOp op) {
2428 if (op.isBinaryNot())
2429 return emitUnary(op,
"~");
2430 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2431 return emitBinary(op, Xor,
"^");
2436 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2438 SubExprInfo visitComb(ReplicateOp op);
2439 SubExprInfo visitComb(
ConcatOp op);
2441 SubExprInfo visitComb(ICmpOp op);
2443 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2444 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2445 "not printed within an assignment-like context");
2446 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2447 "supposed to spill such expressions";
2451 SubExprInfo printStructCreate(
2452 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2454 bool printAsPattern, Operation *op);
2457 ModuleEmitter &emitter;
2464 SubExprSignRequirement signPreference = NoRequirement;
2468 SmallPtrSetImpl<Operation *> &emittedExprs;
2471 SmallVector<Token> localTokens;
2485 bool isAssignmentLikeContext =
false;
2489SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2491 unsigned emitBinaryFlags) {
2493 emitError(op,
"SV attributes emission is unimplemented for the op");
2504 if (emitBinaryFlags & EB_ForceResultSigned)
2505 ps <<
"$signed(" << PP::ibox0;
2506 auto operandSignReq =
2507 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2508 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2510 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2512 ps << lhsSpace << syntax << PP::nbsp;
2519 auto rhsPrec = prec;
2520 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2521 rhsPrec = VerilogPrecedence(prec - 1);
2526 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2527 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2528 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2529 operandSignReq = NoRequirement;
2532 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2533 rhsIsUnsignedValueWithSelfDeterminedWidth);
2537 SubExprSignResult signedness = IsUnsigned;
2538 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2539 signedness = IsSigned;
2541 if (emitBinaryFlags & EB_ForceResultSigned) {
2542 ps << PP::end <<
")";
2543 signedness = IsSigned;
2547 return {prec, signedness};
2550SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2551 bool resultAlwaysUnsigned) {
2553 emitError(op,
"SV attributes emission is unimplemented for the op");
2556 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2560 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2561 resultAlwaysUnsigned ? IsUnsigned : signedness};
2566void ExprEmitter::emitSVAttributes(Operation *op) {
2585 if (constant && constant.getValue().isZero())
2586 return concat.getOperand(1);
2596SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2597 VerilogPrecedence parenthesizeIfLooserThan,
2598 SubExprSignRequirement signRequirement,
2599 bool isSelfDeterminedUnsignedValue,
2600 bool isAssignmentLikeContext) {
2602 if (
auto result = dyn_cast<OpResult>(exp))
2603 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2604 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2605 parenthesizeIfLooserThan, signRequirement,
2606 isSelfDeterminedUnsignedValue,
2607 isAssignmentLikeContext);
2611 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2616 auto *op = exp.getDefiningOp();
2620 if (!shouldEmitInlineExpr) {
2623 if (signRequirement == RequireSigned) {
2625 return {Symbol, IsSigned};
2629 return {Symbol, IsUnsigned};
2632 unsigned subExprStartIndex = buffer.tokens.size();
2634 ps.addCallback({op,
true});
2635 auto done = llvm::make_scope_exit([&]() {
2637 ps.addCallback({op, false});
2643 signPreference = signRequirement;
2645 bool bitCastAdded =
false;
2646 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2648 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2649 ps.addAsString(inType.getWidth());
2650 ps <<
"'(" << PP::ibox0;
2651 bitCastAdded =
true;
2655 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2656 isAssignmentLikeContext);
2657 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2663 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2665 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2667 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2668 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2671 expInfo.signedness = IsSigned;
2672 expInfo.precedence = Selection;
2673 }
else if (signRequirement == RequireUnsigned &&
2674 expInfo.signedness == IsSigned) {
2677 expInfo.signedness = IsUnsigned;
2678 expInfo.precedence = Selection;
2679 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2686 expInfo.precedence = Selection;
2693 emittedExprs.insert(exp.getDefiningOp());
2697SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2698 auto openFn = [&]() {
2700 ps.addAsString(op.getMultiple());
2703 auto closeFn = [&]() { ps <<
"}}"; };
2707 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2708 if (op.getOperand().hasOneUse()) {
2709 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2710 return {Symbol, IsUnsigned};
2713 emitBracedList(op.getOperand(), openFn, closeFn);
2714 return {Symbol, IsUnsigned};
2717SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2718 emitBracedList(op.getOperands());
2719 return {Symbol, IsUnsigned};
2722SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2726 Type toType = op.getType();
2729 ps.invokeWithStringOS(
2730 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2733 return emitSubExpr(op.getInput(), LowestPrecedence);
2736SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2737 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2738 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2739 SubExprSignRequirement signop[] = {
2741 NoRequirement, NoRequirement,
2743 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2745 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2747 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2749 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2750 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2753 if (op.isEqualAllOnes())
2754 return emitUnary(op,
"&",
true);
2757 if (op.isNotEqualZero())
2758 return emitUnary(op,
"|",
true);
2760 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2764 result.signedness = IsUnsigned;
2768SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2770 emitError(op,
"SV attributes emission is unimplemented for the op");
2772 unsigned loBit = op.getLowBit();
2773 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2775 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2776 assert((x.precedence == Symbol ||
2778 "should be handled by isExpressionUnableToInline");
2783 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2787 ps.addAsString(hiBit);
2788 if (hiBit != loBit) {
2790 ps.addAsString(loBit);
2793 return {Unary, IsUnsigned};
2796SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2798 emitError(op,
"SV attributes emission is unimplemented for the op");
2800 auto decl = op.getReferencedDecl(state.symbolCache);
2803 return {Selection, IsUnsigned};
2806SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2808 emitError(op,
"SV attributes emission is unimplemented for the op");
2811 ps.scopedBox(PP::ibox0, [&]() {
2813 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2814 [&]() { ps <<
"," << PP::space; });
2817 return {Symbol, IsUnsigned};
2820SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2822 emitError(op,
"SV attributes emission is unimplemented for the op");
2824 auto decl = op.getReferencedDecl(state.symbolCache);
2828 return {Selection, IsUnsigned};
2831SubExprInfo ExprEmitter::visitSV(XMROp op) {
2833 emitError(op,
"SV attributes emission is unimplemented for the op");
2835 if (op.getIsRooted())
2837 for (
auto s : op.getPath())
2838 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2840 return {Selection, IsUnsigned};
2845SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2847 emitError(op,
"SV attributes emission is unimplemented for the op");
2850 auto globalRef = op.getReferencedPath(&state.symbolCache);
2851 auto namepath = globalRef.getNamepathAttr().getValue();
2852 auto *
module = state.symbolCache.getDefinition(
2853 cast<InnerRefAttr>(namepath.front()).getModule());
2855 for (
auto sym : namepath) {
2857 auto innerRef = cast<InnerRefAttr>(sym);
2858 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2859 innerRef.getName());
2860 if (ref.hasPort()) {
2866 auto leaf = op.getVerbatimSuffixAttr();
2867 if (leaf && leaf.size())
2869 return {Selection, IsUnsigned};
2872SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2874 emitError(op,
"SV attributes emission is unimplemented for the op");
2876 emitTextWithSubstitutions(
2877 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2878 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2880 return {Unary, IsUnsigned};
2883template <
typename MacroTy>
2884SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2886 emitError(op,
"SV attributes emission is unimplemented for the op");
2889 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2890 assert(macroOp &&
"Invalid IR");
2892 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2894 if (!op.getInputs().empty()) {
2896 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2897 emitExpression(val, LowestPrecedence, false);
2901 return {LowestPrecedence, IsUnsigned};
2904SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2905 return emitMacroCall(op);
2908SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2909 return emitMacroCall(op);
2912SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2914 emitError(op,
"SV attributes emission is unimplemented for the op");
2916 ps.addAsString(op.getWidth());
2918 return {Unary, IsUnsigned};
2921SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2923 emitError(op,
"SV attributes emission is unimplemented for the op");
2925 ps.writeQuotedEscaped(op.getStr());
2926 return {Symbol, IsUnsigned};
2929SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2931 emitError(op,
"SV attributes emission is unimplemented for the op");
2933 ps.addAsString(op.getWidth());
2935 return {Unary, IsUnsigned};
2938SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2939 bool isNegated =
false;
2942 if (signPreference == RequireSigned && value.isNegative() &&
2943 !value.isMinSignedValue()) {
2948 ps.addAsString(type.getWidth());
2952 if (signPreference == RequireSigned)
2958 SmallString<32> valueStr;
2960 (-value).toStringUnsigned(valueStr, 16);
2962 value.toStringUnsigned(valueStr, 16);
2965 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2968SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2970 emitError(op,
"SV attributes emission is unimplemented for the op");
2972 auto value = op.getValue();
2976 if (value.getBitWidth() == 0) {
2977 emitOpError(op,
"will not emit zero width constants in the general case");
2978 ps <<
"<<unsupported zero width constant: "
2979 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2980 return {Unary, IsUnsigned};
2983 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2986void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2987 bool printAsPattern, Operation *op) {
2988 if (printAsPattern && !isAssignmentLikeContext)
2989 emitAssignmentPatternContextError(op);
2990 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2993 elementValues, [&]() { ps << openDelim; },
2994 [&](Attribute elementValue) {
2995 printConstantAggregate(elementValue,
elementType, op);
2997 [&]() { ps <<
"}"; });
3000void ExprEmitter::printConstantStruct(
3001 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3002 bool printAsPattern, Operation *op) {
3003 if (printAsPattern && !isAssignmentLikeContext)
3004 emitAssignmentPatternContextError(op);
3011 auto fieldRange = llvm::make_filter_range(
3012 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3017 if (printAsPattern) {
3019 fieldRange, [&]() { ps <<
"'{"; },
3020 [&](
const auto &fieldAndValue) {
3021 ps.scopedBox(PP::ibox2, [&]() {
3022 const auto &[field, value] = fieldAndValue;
3023 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3024 <<
":" << PP::space;
3025 printConstantAggregate(value, field.type, op);
3028 [&]() { ps <<
"}"; });
3031 fieldRange, [&]() { ps <<
"{"; },
3032 [&](
const auto &fieldAndValue) {
3033 ps.scopedBox(PP::ibox2, [&]() {
3034 const auto &[field, value] = fieldAndValue;
3035 printConstantAggregate(value, field.type, op);
3038 [&]() { ps <<
"}"; });
3042void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3045 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3046 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3047 isAssignmentLikeContext, op);
3050 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3051 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3055 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3056 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3057 isAssignmentLikeContext, op);
3059 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3060 auto value = cast<IntegerAttr>(attr).getValue();
3061 printConstantScalar(value, intType);
3065 emitOpError(op,
"contains constant of type ")
3066 << type <<
" which cannot be emitted as Verilog";
3069SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3071 emitError(op,
"SV attributes emission is unimplemented for the op");
3075 "zero-bit types not allowed at this point");
3077 printConstantAggregate(op.getFields(), op.getType(), op);
3078 return {Symbol, IsUnsigned};
3081SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3083 emitError(op,
"SV attributes emission is unimplemented for the op");
3085 return ps.invokeWithStringOS([&](
auto &os) {
3086 return emitter.printParamValue(op.getValue(), os, [&]() {
3087 return op->emitOpError(
"invalid parameter use");
3096 emitError(op,
"SV attributes emission is unimplemented for the op");
3098 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3100 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3102 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3104 ps.addAsString(dstWidth);
3106 return {Selection, arrayPrec.signedness};
3109SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3110 emitSubExpr(op.getInput(), Selection);
3115 emitSubExpr(op.getIndex(), LowestPrecedence);
3117 emitSVAttributes(op);
3118 return {Selection, IsUnsigned};
3124 emitError(op,
"SV attributes emission is unimplemented for the op");
3126 if (op.isUniform()) {
3128 ps.addAsString(op.getInputs().size());
3130 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3134 op.getInputs(), [&]() { ps <<
"{"; },
3137 emitSubExprIBox2(v);
3140 [&]() { ps <<
"}"; });
3142 return {Unary, IsUnsigned};
3145SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3147 emitError(op,
"SV attributes emission is unimplemented for the op");
3150 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3151 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3152 return {Unary, IsUnsigned};
3157 emitError(op,
"SV attributes emission is unimplemented for the op");
3159 emitBracedList(op.getOperands());
3160 return {Unary, IsUnsigned};
3163SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3165 emitError(op,
"SV attributes emission is unimplemented for the op");
3167 auto index = op.getIndex();
3168 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3173 emitSubExpr(index, LowestPrecedence);
3175 return {Selection, arrayPrec.signedness};
3178SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3180 emitError(op,
"SV attributes emission is unimplemented for the op");
3182 auto prec = emitSubExpr(op.getInput(), Selection);
3184 emitSubExpr(op.getBase(), LowestPrecedence);
3185 if (op.getDecrement())
3189 ps.addAsString(op.getWidth());
3191 return {Selection, prec.signedness};
3194SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3196 emitError(op,
"SV attributes emission is unimplemented for the op");
3198 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3200 emitSubExpr(op.getBase(), LowestPrecedence);
3201 if (op.getDecrement())
3205 ps.addAsString(op.getWidth());
3210SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3212 emitError(op,
"SV attributes emission is unimplemented for the op");
3214 auto prec = emitSubExpr(op.getInput(), Selection);
3216 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3217 return {Selection, prec.signedness};
3220SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3222 emitError(op,
"SV attributes emission is unimplemented for the op");
3225 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3230SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3232 emitError(op,
"SV attributes emission is unimplemented for the op");
3235 ps.scopedBox(PP::ibox0, [&]() {
3236 ps.writeQuotedEscaped(op.getFormatString());
3243 for (
auto operand : op.getSubstitutions()) {
3244 ps <<
"," << PP::space;
3245 emitSubExpr(operand, LowestPrecedence);
3249 return {Symbol, IsUnsigned};
3252SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3254 emitError(op,
"SV attributes emission is unimplemented for the op");
3257 return {Symbol, IsUnsigned};
3260SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3262 emitError(op,
"SV attributes emission is unimplemented for the op");
3265 return {Symbol, IsUnsigned};
3268SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3282 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3283 ps.scopedBox(PP::ibox0, [&]() {
3284 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3288 emitSVAttributes(op);
3290 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3291 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3295 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3296 return emitSubExpr(op.getFalseValue(), Conditional);
3299 SubExprSignResult signedness = IsUnsigned;
3300 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3301 signedness = IsSigned;
3303 return {Conditional, signedness};
3307SubExprInfo ExprEmitter::printStructCreate(
3308 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3310 bool printAsPattern, Operation *op) {
3311 if (printAsPattern && !isAssignmentLikeContext)
3312 emitAssignmentPatternContextError(op);
3315 auto filteredFields = llvm::make_filter_range(
3316 llvm::enumerate(fieldInfos),
3317 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3319 if (printAsPattern) {
3321 filteredFields, [&]() { ps <<
"'{"; },
3322 [&](
const auto &field) {
3323 ps.scopedBox(PP::ibox2, [&]() {
3325 emitter.getVerilogStructFieldName(field.value().name))
3326 <<
":" << PP::space;
3327 fieldFn(field.value(), field.index());
3330 [&]() { ps <<
"}"; });
3333 filteredFields, [&]() { ps <<
"{"; },
3334 [&](
const auto &field) {
3335 ps.scopedBox(PP::ibox2,
3336 [&]() { fieldFn(field.value(), field.index()); });
3338 [&]() { ps <<
"}"; });
3341 return {Selection, IsUnsigned};
3346 emitError(op,
"SV attributes emission is unimplemented for the op");
3350 bool printAsPattern = isAssignmentLikeContext;
3351 StructType structType = op.getType();
3352 return printStructCreate(
3353 structType.getElements(),
3354 [&](
const auto &field,
auto index) {
3355 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3357 isAssignmentLikeContext);
3359 printAsPattern, op);
3364 emitError(op,
"SV attributes emission is unimplemented for the op");
3366 emitSubExpr(op.getInput(), Selection);
3368 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3369 return {Selection, IsUnsigned};
3372SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3374 emitError(op,
"SV attributes emission is unimplemented for the op");
3378 bool printAsPattern = isAssignmentLikeContext;
3379 StructType structType = op.getType();
3380 return printStructCreate(
3381 structType.getElements(),
3382 [&](
const auto &field,
auto index) {
3383 if (field.name == op.getFieldNameAttr()) {
3384 emitSubExpr(op.getNewValue(), Selection);
3386 emitSubExpr(op.getInput(), Selection);
3388 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3391 printAsPattern, op);
3394SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3395 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3396 return {Selection, IsUnsigned};
3399SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3401 emitError(op,
"SV attributes emission is unimplemented for the op");
3402 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3405 result.signedness = IsUnsigned;
3409SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3411 emitError(op,
"SV attributes emission is unimplemented for the op");
3415 auto unionWidth = hw::getBitWidth(unionType);
3416 auto &element = unionType.getElements()[op.getFieldIndex()];
3417 auto elementWidth = hw::getBitWidth(element.type);
3420 if (!elementWidth) {
3421 ps.addAsString(unionWidth);
3423 return {Unary, IsUnsigned};
3427 if (elementWidth == unionWidth) {
3428 emitSubExpr(op.getInput(), LowestPrecedence);
3429 return {Unary, IsUnsigned};
3434 ps.scopedBox(PP::ibox0, [&]() {
3435 if (
auto prePadding = element.offset) {
3436 ps.addAsString(prePadding);
3437 ps <<
"'h0," << PP::space;
3439 emitSubExpr(op.getInput(), Selection);
3440 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3441 ps <<
"," << PP::space;
3442 ps.addAsString(postPadding);
3448 return {Unary, IsUnsigned};
3451SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3453 emitError(op,
"SV attributes emission is unimplemented for the op");
3454 emitSubExpr(op.getInput(), Selection);
3457 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3458 auto unionWidth = hw::getBitWidth(unionType);
3459 auto &element = unionType.getElements()[op.getFieldIndex()];
3460 auto elementWidth = hw::getBitWidth(element.type);
3461 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3462 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3471 return {Selection, IsUnsigned};
3474SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3475 emitOpError(op,
"cannot emit this expression to Verilog");
3476 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3478 return {Symbol, IsUnsigned};
3494enum class PropertyPrecedence {
3514struct EmittedProperty {
3516 PropertyPrecedence precedence;
3521class PropertyEmitter :
public EmitterBase,
3522 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3526 PropertyEmitter(ModuleEmitter &emitter,
3527 SmallPtrSetImpl<Operation *> &emittedOps)
3528 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3529 PropertyEmitter(ModuleEmitter &emitter,
3530 SmallPtrSetImpl<Operation *> &emittedOps,
3532 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3534 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3535 assert(state.pp.getListener() == &state.saver);
3538 void emitAssertPropertyDisable(
3539 Value property, Value disable,
3540 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3542 void emitAssertPropertyBody(
3543 Value property, Value disable,
3544 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3546 void emitAssertPropertyBody(
3547 Value property, sv::EventControl event, Value clock, Value disable,
3548 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3553 emitNestedProperty(Value property,
3554 PropertyPrecedence parenthesizeIfLooserThan);
3555 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3556 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3558 EmittedProperty visitUnhandledLTL(Operation *op);
3559 EmittedProperty visitLTL(ltl::AndOp op);
3560 EmittedProperty visitLTL(ltl::OrOp op);
3561 EmittedProperty visitLTL(ltl::IntersectOp op);
3562 EmittedProperty visitLTL(ltl::DelayOp op);
3563 EmittedProperty visitLTL(ltl::ConcatOp op);
3564 EmittedProperty visitLTL(ltl::RepeatOp op);
3565 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3566 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3567 EmittedProperty visitLTL(ltl::NotOp op);
3568 EmittedProperty visitLTL(ltl::ImplicationOp op);
3569 EmittedProperty visitLTL(ltl::UntilOp op);
3570 EmittedProperty visitLTL(ltl::EventuallyOp op);
3571 EmittedProperty visitLTL(ltl::ClockOp op);
3573 void emitLTLConcat(ValueRange inputs);
3576 ModuleEmitter &emitter;
3581 SmallPtrSetImpl<Operation *> &emittedOps;
3584 SmallVector<Token> localTokens;
3597void PropertyEmitter::emitAssertPropertyDisable(
3598 Value property, Value disable,
3599 PropertyPrecedence parenthesizeIfLooserThan) {
3602 ps <<
"disable iff" << PP::nbsp <<
"(";
3604 emitNestedProperty(disable, PropertyPrecedence::Unary);
3610 ps.scopedBox(PP::ibox0,
3611 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3617void PropertyEmitter::emitAssertPropertyBody(
3618 Value property, Value disable,
3619 PropertyPrecedence parenthesizeIfLooserThan) {
3620 assert(localTokens.empty());
3622 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3627 if (&buffer.tokens == &localTokens)
3628 buffer.flush(state.pp);
3631void PropertyEmitter::emitAssertPropertyBody(
3632 Value property, sv::EventControl event, Value clock, Value disable,
3633 PropertyPrecedence parenthesizeIfLooserThan) {
3634 assert(localTokens.empty());
3637 ps.scopedBox(PP::ibox2, [&] {
3638 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3639 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3645 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3650 if (&buffer.tokens == &localTokens)
3651 buffer.flush(state.pp);
3654EmittedProperty PropertyEmitter::emitNestedProperty(
3655 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3665 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3666 ExprEmitter(emitter, emittedOps, buffer.tokens)
3667 .emitExpression(property, LowestPrecedence,
3669 return {PropertyPrecedence::Symbol};
3672 unsigned startIndex = buffer.tokens.size();
3673 auto info = dispatchLTLVisitor(property.getDefiningOp());
3678 if (
info.precedence > parenthesizeIfLooserThan) {
3680 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3681 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3683 ps << PP::end <<
")";
3685 info.precedence = PropertyPrecedence::Symbol;
3689 emittedOps.insert(property.getDefiningOp());
3693EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3694 emitOpError(op,
"emission as Verilog property or sequence not supported");
3695 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3696 return {PropertyPrecedence::Symbol};
3699EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3702 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3703 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3704 return {PropertyPrecedence::And};
3707EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3710 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3711 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3712 return {PropertyPrecedence::Or};
3715EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3719 emitNestedProperty(input, PropertyPrecedence::Intersect);
3721 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3722 return {PropertyPrecedence::Intersect};
3725EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3727 if (
auto length = op.getLength()) {
3729 ps.addAsString(op.getDelay());
3732 ps.addAsString(op.getDelay());
3734 ps.addAsString(op.getDelay() + *length);
3738 if (op.getDelay() == 0) {
3740 }
else if (op.getDelay() == 1) {
3744 ps.addAsString(op.getDelay());
3749 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3750 return {PropertyPrecedence::Concat};
3753void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3754 bool addSeparator =
false;
3755 for (
auto input : inputs) {
3758 if (!input.getDefiningOp<ltl::DelayOp>())
3759 ps <<
"##0" << PP::space;
3761 addSeparator =
true;
3762 emitNestedProperty(input, PropertyPrecedence::Concat);
3766EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3767 emitLTLConcat(op.getInputs());
3768 return {PropertyPrecedence::Concat};
3771EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3772 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3773 if (
auto more = op.getMore()) {
3775 ps.addAsString(op.getBase());
3778 ps.addAsString(op.getBase() + *more);
3782 if (op.getBase() == 0) {
3784 }
else if (op.getBase() == 1) {
3788 ps.addAsString(op.getBase());
3792 return {PropertyPrecedence::Repeat};
3795EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3796 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3798 auto more = op.getMore();
3800 ps.addAsString(op.getBase());
3803 ps.addAsString(op.getBase() + more);
3807 return {PropertyPrecedence::Repeat};
3810EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3811 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3813 auto more = op.getMore();
3815 ps.addAsString(op.getBase());
3818 ps.addAsString(op.getBase() + more);
3822 return {PropertyPrecedence::Repeat};
3825EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3826 ps <<
"not" << PP::space;
3827 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3828 return {PropertyPrecedence::Unary};
3834 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3835 if (!concatOp || concatOp.getInputs().size() < 2)
3837 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3838 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3840 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3841 if (!constOp || !constOp.getValue().isOne())
3843 return concatOp.getInputs().drop_back();
3846EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3850 emitLTLConcat(range);
3851 ps << PP::space <<
"|=>" << PP::nbsp;
3853 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3854 ps << PP::space <<
"|->" << PP::nbsp;
3856 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3857 return {PropertyPrecedence::Implication};
3860EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3861 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3862 ps << PP::space <<
"until" << PP::space;
3863 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3864 return {PropertyPrecedence::Until};
3867EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3868 ps <<
"s_eventually" << PP::space;
3869 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3870 return {PropertyPrecedence::Qualifier};
3873EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3875 ps.scopedBox(PP::ibox2, [&] {
3876 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3877 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3881 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3882 return {PropertyPrecedence::Clocking};
3892class NameCollector {
3894 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3898 void collectNames(Block &block);
3900 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3901 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3904 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3905 ModuleEmitter &moduleEmitter;
3910 static constexpr size_t maxTypeWidthBound = 32;
3915void NameCollector::collectNames(Block &block) {
3918 for (
auto &op : block) {
3922 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3923 FuncCallProceduralOp, FuncCallOp>(op))
3925 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3929 for (
auto result : op.getResults()) {
3931 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3932 SmallString<16> typeString;
3936 llvm::raw_svector_ostream stringStream(typeString);
3938 stringStream, op.getLoc());
3940 if (typeString.size() <= maxTypeWidthBound)
3941 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3948 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3949 for (
auto ®ion : op.getRegions()) {
3950 if (!region.empty())
3951 collectNames(region.front());
3965class StmtEmitter :
public EmitterBase,
3973 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3975 void emitStatement(Operation *op);
3976 void emitStatementBlock(Block &body);
3979 LogicalResult emitDeclaration(Operation *op);
3982 void collectNamesAndCalculateDeclarationWidths(Block &block);
3985 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
3986 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
3987 bool isAssignmentLikeContext =
false);
3988 void emitSVAttributes(Operation *op);
3991 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
3994 friend class sv::Visitor<StmtEmitter, LogicalResult>;
3998 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
3999 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4000 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4001 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4002 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4003 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4005 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4006 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4007 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4008 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4009 template <
typename Op>
4012 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4013 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4014 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4016 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4017 LogicalResult visitSV(
AssignOp op);
4018 LogicalResult visitSV(BPAssignOp op);
4019 LogicalResult visitSV(PAssignOp op);
4020 LogicalResult visitSV(ForceOp op);
4021 LogicalResult visitSV(ReleaseOp op);
4022 LogicalResult visitSV(AliasOp op);
4023 LogicalResult visitSV(InterfaceInstanceOp op);
4024 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4025 LogicalResult visitStmt(OutputOp op);
4027 LogicalResult visitStmt(InstanceOp op);
4028 LogicalResult visitStmt(InstanceChoiceOp op);
4029 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4030 ArrayRef<Value> instPortValues);
4035 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4036 LogicalResult visitSV(OrderedOutputOp op);
4037 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4038 LogicalResult visitSV(IfDefProceduralOp op) {
4039 return emitIfDef(op, op.getCond());
4041 LogicalResult visitSV(IfOp op);
4042 LogicalResult visitSV(AlwaysOp op);
4043 LogicalResult visitSV(AlwaysCombOp op);
4044 LogicalResult visitSV(AlwaysFFOp op);
4045 LogicalResult visitSV(InitialOp op);
4046 LogicalResult visitSV(CaseOp op);
4047 LogicalResult visitSV(FWriteOp op);
4048 LogicalResult visitSV(FFlushOp op);
4049 LogicalResult visitSV(VerbatimOp op);
4050 LogicalResult visitSV(MacroRefOp op);
4052 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4053 std::optional<unsigned> verbosity);
4054 LogicalResult visitSV(StopOp op);
4055 LogicalResult visitSV(FinishOp op);
4056 LogicalResult visitSV(ExitOp op);
4058 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4059 std::optional<unsigned> verbosity,
4061 ValueRange operands);
4062 LogicalResult visitSV(FatalOp op);
4063 LogicalResult visitSV(ErrorOp op);
4064 LogicalResult visitSV(WarningOp op);
4065 LogicalResult visitSV(InfoOp op);
4067 LogicalResult visitSV(ReadMemOp op);
4069 LogicalResult visitSV(GenerateOp op);
4070 LogicalResult visitSV(GenerateCaseOp op);
4072 LogicalResult visitSV(
ForOp op);
4074 void emitAssertionLabel(Operation *op);
4075 void emitAssertionMessage(StringAttr message, ValueRange args,
4076 SmallPtrSetImpl<Operation *> &ops,
4078 template <
typename Op>
4079 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4080 LogicalResult visitSV(AssertOp op);
4081 LogicalResult visitSV(AssumeOp op);
4082 LogicalResult visitSV(CoverOp op);
4083 template <
typename Op>
4084 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4085 LogicalResult visitSV(AssertConcurrentOp op);
4086 LogicalResult visitSV(AssumeConcurrentOp op);
4087 LogicalResult visitSV(CoverConcurrentOp op);
4088 template <
typename Op>
4089 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4090 LogicalResult visitSV(AssertPropertyOp op);
4091 LogicalResult visitSV(AssumePropertyOp op);
4092 LogicalResult visitSV(CoverPropertyOp op);
4094 LogicalResult visitSV(BindOp op);
4095 LogicalResult visitSV(InterfaceOp op);
4096 LogicalResult visitSV(InterfaceSignalOp op);
4097 LogicalResult visitSV(InterfaceModportOp op);
4098 LogicalResult visitSV(AssignInterfaceSignalOp op);
4099 LogicalResult visitSV(MacroDefOp op);
4101 void emitBlockAsStatement(Block *block,
4102 const SmallPtrSetImpl<Operation *> &locationOps,
4103 StringRef multiLineComment = StringRef());
4105 LogicalResult visitSV(FuncDPIImportOp op);
4106 template <
typename CallOp>
4107 LogicalResult emitFunctionCall(CallOp callOp);
4108 LogicalResult visitSV(FuncCallProceduralOp op);
4109 LogicalResult visitSV(FuncCallOp op);
4110 LogicalResult visitSV(ReturnOp op);
4111 LogicalResult visitSV(IncludeOp op);
4114 ModuleEmitter &emitter;
4119 size_t maxDeclNameWidth = 0;
4120 size_t maxTypeWidth = 0;
4131void StmtEmitter::emitExpression(Value exp,
4132 SmallPtrSetImpl<Operation *> &emittedExprs,
4133 VerilogPrecedence parenthesizeIfLooserThan,
4134 bool isAssignmentLikeContext) {
4135 ExprEmitter(emitter, emittedExprs)
4136 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4141void StmtEmitter::emitSVAttributes(Operation *op) {
4149 setPendingNewline();
4152void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4153 llvm::function_ref<
void()> emitRHS,
4155 std::optional<PPExtString> wordBeforeLHS) {
4157 ps.scopedBox(PP::ibox2, [&]() {
4158 if (wordBeforeLHS) {
4159 ps << *wordBeforeLHS << PP::space;
4163 ps << PP::space << syntax << PP::space;
4165 ps.scopedBox(PP::ibox0, [&]() {
4172template <
typename Op>
4174StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4175 std::optional<PPExtString> wordBeforeLHS) {
4176 SmallPtrSet<Operation *, 8> ops;
4180 ps.addCallback({op,
true});
4181 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4183 emitExpression(op.getSrc(), ops, LowestPrecedence,
4188 ps.addCallback({op,
false});
4189 emitLocationInfoAndNewLine(ops);
4193LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4196 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4199 if (emitter.assignsInlined.count(op))
4203 emitSVAttributes(op);
4208LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4209 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4213 if (emitter.assignsInlined.count(op))
4217 emitSVAttributes(op);
4222LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4224 emitSVAttributes(op);
4229LogicalResult StmtEmitter::visitSV(ForceOp op) {
4231 emitError(op,
"SV attributes emission is unimplemented for the op");
4236LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4238 emitError(op,
"SV attributes emission is unimplemented for the op");
4241 SmallPtrSet<Operation *, 8> ops;
4243 ps.addCallback({op,
true});
4244 ps.scopedBox(PP::ibox2, [&]() {
4245 ps <<
"release" << PP::space;
4246 emitExpression(op.getDest(), ops);
4249 ps.addCallback({op,
false});
4250 emitLocationInfoAndNewLine(ops);
4254LogicalResult StmtEmitter::visitSV(AliasOp op) {
4256 emitError(op,
"SV attributes emission is unimplemented for the op");
4259 SmallPtrSet<Operation *, 8> ops;
4261 ps.addCallback({op,
true});
4262 ps.scopedBox(PP::ibox2, [&]() {
4263 ps <<
"alias" << PP::space;
4264 ps.scopedBox(PP::cbox0, [&]() {
4266 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4267 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4271 ps.addCallback({op,
false});
4272 emitLocationInfoAndNewLine(ops);
4276LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4277 auto doNotPrint = op.getDoNotPrint();
4278 if (doNotPrint && !state.options.emitBindComments)
4282 emitError(op,
"SV attributes emission is unimplemented for the op");
4285 StringRef prefix =
"";
4286 ps.addCallback({op,
true});
4289 ps <<
"// This interface is elsewhere emitted as a bind statement."
4293 SmallPtrSet<Operation *, 8> ops;
4296 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4297 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4298 "point to an interface");
4301 if (!prefix.empty())
4307 ps.addCallback({op,
false});
4308 emitLocationInfoAndNewLine(ops);
4316LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4318 SmallPtrSet<Operation *, 8> ops;
4319 size_t operandIndex = 0;
4320 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4321 for (
PortInfo port : ports.getOutputs()) {
4322 auto operand = op->getOperand(operandIndex);
4326 if (operand.hasOneUse() && operand.getDefiningOp() &&
4327 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4336 ps.addCallback({op,
true});
4338 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4340 ps <<
"// Zero width: ";
4343 ps <<
"assign" << PP::space;
4345 ps << PP::space <<
"=" << PP::space;
4346 ps.scopedBox(PP::ibox0, [&]() {
4350 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4351 ps <<
"/*Zero width*/";
4353 emitExpression(operand, ops, LowestPrecedence,
4358 ps.addCallback({op,
false});
4359 emitLocationInfoAndNewLine(ops);
4366LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4367 auto parent = op->getParentOfType<PortList>();
4369 return emitOutputLikeOp(op, ports);
4372LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4374 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4375 ps <<
"`ifndef " << typescopeDef << PP::newline;
4376 ps <<
"`define " << typescopeDef;
4377 setPendingNewline();
4378 emitStatementBlock(*op.getBodyBlock());
4380 ps <<
"`endif // " << typescopeDef;
4381 setPendingNewline();
4385LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4387 emitError(op,
"SV attributes emission is unimplemented for the op");
4392 ps << PP::neverbox <<
"// ";
4394 SmallPtrSet<Operation *, 8> ops;
4396 ps.scopedBox(PP::ibox2, [&]() {
4397 ps <<
"typedef" << PP::space;
4398 ps.invokeWithStringOS([&](
auto &os) {
4400 op.getAliasType(),
false);
4402 ps << PP::space <<
PPExtString(op.getPreferredName());
4403 ps.invokeWithStringOS(
4404 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4409 emitLocationInfoAndNewLine(ops);
4413template <
typename CallOpTy>
4414LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4418 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4420 SmallPtrSet<Operation *, 8> ops;
4424 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4425 if (explicitReturn) {
4426 assert(explicitReturn.hasOneUse());
4427 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4428 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4429 emitExpression(bpassignOp.getDest(), ops);
4431 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4432 ps <<
"assign" << PP::nbsp;
4433 emitExpression(assignOp.getDest(), ops);
4435 ps << PP::nbsp <<
"=" << PP::nbsp;
4438 auto arguments = callee.getPortList(
true);
4442 bool needsComma =
false;
4443 auto printArg = [&](Value value) {
4445 ps <<
"," << PP::space;
4446 emitExpression(value, ops);
4450 ps.scopedBox(PP::ibox0, [&] {
4451 unsigned inputIndex = 0, outputIndex = 0;
4452 for (
auto arg : arguments) {
4455 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4457 printArg(op.getInputs()[inputIndex++]);
4462 emitLocationInfoAndNewLine(ops);
4466LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4467 return emitFunctionCall(op);
4470LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4471 return emitFunctionCall(op);
4474template <
typename PPS>
4476 bool isAutomatic =
false,
4477 bool emitAsTwoStateType =
false) {
4478 ps <<
"function" << PP::nbsp;
4480 ps <<
"automatic" << PP::nbsp;
4481 auto retType = op.getExplicitlyReturnedType();
4483 ps.invokeWithStringOS([&](
auto &os) {
4484 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4485 emitAsTwoStateType);
4491 emitter.emitPortList(
4495LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4496 auto parent = op->getParentOfType<sv::FuncOp>();
4498 return emitOutputLikeOp(op, ports);
4501LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4503 ps <<
"`include" << PP::nbsp;
4505 if (op.getStyle() == IncludeStyle::System)
4506 ps <<
"<" << op.getTarget() <<
">";
4508 ps <<
"\"" << op.getTarget() <<
"\"";
4510 emitLocationInfo(op.getLoc());
4511 setPendingNewline();
4515LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4518 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4522 if (
auto linkageName = importOp.getLinkageName())
4523 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4525 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4526 assert(op.isDeclaration() &&
"function must be a declaration");
4529 assert(state.pendingNewline);
4535LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4537 emitError(op,
"SV attributes emission is unimplemented for the op");
4540 SmallPtrSet<Operation *, 8> ops;
4543 ps.addCallback({op,
true});
4545 if (
auto fd = op.getFd())
4546 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4549 ps.addCallback({op,
false});
4550 emitLocationInfoAndNewLine(ops);
4554LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4556 emitError(op,
"SV attributes emission is unimplemented for the op");
4559 SmallPtrSet<Operation *, 8> ops;
4562 ps.addCallback({op,
true});
4564 ps.scopedBox(PP::ibox0, [&]() {
4565 emitExpression(op.getFd(), ops);
4567 ps <<
"," << PP::space;
4568 ps.writeQuotedEscaped(op.getFormatString());
4576 for (
auto operand : op.getSubstitutions()) {
4577 ps <<
"," << PP::space;
4578 emitExpression(operand, ops);
4582 ps.addCallback({op,
false});
4583 emitLocationInfoAndNewLine(ops);
4587LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4589 emitError(op,
"SV attributes emission is unimplemented for the op");
4592 SmallPtrSet<Operation *, 8> ops;
4597 StringRef
string = op.getFormatString();
4598 if (
string.ends_with(
"\n"))
4599 string =
string.drop_back();
4604 bool isFirst =
true;
4607 while (!
string.
empty()) {
4608 auto lhsRhs =
string.split(
'\n');
4612 ps << PP::end << PP::newline << PP::neverbox;
4616 emitTextWithSubstitutions(
4617 ps, lhsRhs.first, op,
4618 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4619 string = lhsRhs.second;
4624 emitLocationInfoAndNewLine(ops);
4629LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4631 emitError(op,
"SV attributes emission is unimplemented for the op");
4635 SmallPtrSet<Operation *, 8> ops;
4640 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4641 assert(macroOp &&
"Invalid IR");
4643 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4645 if (!op.getInputs().empty()) {
4647 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4648 emitExpression(val, ops, LowestPrecedence,
4654 emitLocationInfoAndNewLine(ops);
4660StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4661 std::optional<unsigned> verbosity) {
4663 emitError(op,
"SV attributes emission is unimplemented for the op");
4666 SmallPtrSet<Operation *, 8> ops;
4668 ps.addCallback({op,
true});
4670 if (verbosity && *verbosity != 1) {
4672 ps.addAsString(*verbosity);
4676 ps.addCallback({op,
false});
4677 emitLocationInfoAndNewLine(ops);
4681LogicalResult StmtEmitter::visitSV(StopOp op) {
4682 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4685LogicalResult StmtEmitter::visitSV(FinishOp op) {
4686 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4690LogicalResult StmtEmitter::visitSV(ExitOp op) {
4691 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4697StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4698 std::optional<unsigned> verbosity,
4699 StringAttr message, ValueRange operands) {
4701 emitError(op,
"SV attributes emission is unimplemented for the op");
4704 SmallPtrSet<Operation *, 8> ops;
4706 ps.addCallback({op,
true});
4712 if ((verbosity && *verbosity != 1) || message) {
4714 ps.scopedBox(PP::ibox0, [&]() {
4718 ps.addAsString(*verbosity);
4723 ps <<
"," << PP::space;
4724 ps.writeQuotedEscaped(message.getValue());
4726 for (
auto operand : operands) {
4727 ps <<
"," << PP::space;
4728 emitExpression(operand, ops);
4737 ps.addCallback({op,
false});
4738 emitLocationInfoAndNewLine(ops);
4742LogicalResult StmtEmitter::visitSV(FatalOp op) {
4743 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4744 op.getMessageAttr(), op.getSubstitutions());
4747LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4748 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4749 op.getMessageAttr(), op.getSubstitutions());
4752LogicalResult StmtEmitter::visitSV(WarningOp op) {
4753 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4754 op.getMessageAttr(), op.getSubstitutions());
4757LogicalResult StmtEmitter::visitSV(InfoOp op) {
4758 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4759 op.getMessageAttr(), op.getSubstitutions());
4762LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4763 SmallPtrSet<Operation *, 8> ops({op});
4766 ps.addCallback({op,
true});
4768 switch (op.getBaseAttr().getValue()) {
4769 case MemBaseTypeAttr::MemBaseBin:
4772 case MemBaseTypeAttr::MemBaseHex:
4777 ps.scopedBox(PP::ibox0, [&]() {
4778 ps.writeQuotedEscaped(op.getFilename());
4779 ps <<
"," << PP::space;
4780 emitExpression(op.getDest(), ops);
4784 ps.addCallback({op,
false});
4785 emitLocationInfoAndNewLine(ops);
4789LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4790 emitSVAttributes(op);
4793 ps.addCallback({op,
true});
4794 ps <<
"generate" << PP::newline;
4796 setPendingNewline();
4797 emitStatementBlock(op.getBody().getBlocks().front());
4800 ps <<
"endgenerate";
4801 ps.addCallback({op,
false});
4802 setPendingNewline();
4806LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4807 emitSVAttributes(op);
4810 ps.addCallback({op,
true});
4812 ps.invokeWithStringOS([&](
auto &os) {
4813 emitter.printParamValue(
4814 op.getCond(), os, VerilogPrecedence::Selection,
4815 [&]() { return op->emitOpError(
"invalid case parameter"); });
4818 setPendingNewline();
4821 ArrayAttr
patterns = op.getCasePatterns();
4822 ArrayAttr caseNames = op.getCaseNames();
4823 MutableArrayRef<Region> regions = op.getCaseRegions();
4830 llvm::StringMap<size_t> nextGenIds;
4831 ps.scopedBox(PP::bbox2, [&]() {
4833 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4834 auto ®ion = regions[i];
4835 assert(region.hasOneBlock());
4836 Attribute patternAttr =
patterns[i];
4839 if (!isa<mlir::TypedAttr>(patternAttr))
4842 ps.invokeWithStringOS([&](
auto &os) {
4843 emitter.printParamValue(
4844 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4845 [&]() {
return op->emitOpError(
"invalid case value"); });
4848 StringRef legalName =
4849 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4852 setPendingNewline();
4853 emitStatementBlock(region.getBlocks().front());
4856 setPendingNewline();
4862 ps.addCallback({op,
false});
4863 setPendingNewline();
4867LogicalResult StmtEmitter::visitSV(
ForOp op) {
4868 emitSVAttributes(op);
4869 llvm::SmallPtrSet<Operation *, 8> ops;
4870 ps.addCallback({op,
true});
4872 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4875 ps.scopedBox(PP::cbox0, [&]() {
4879 ps <<
"logic" << PP::nbsp;
4880 ps.invokeWithStringOS([&](
auto &os) {
4881 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4886 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4891 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4892 [&]() { emitExpression(op.getUpperBound(), ops); },
4898 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4899 [&]() { emitExpression(op.getStep(), ops); },
4903 ps << PP::neverbreak;
4904 setPendingNewline();
4905 emitStatementBlock(op.getBody().getBlocks().front());
4908 ps.addCallback({op,
false});
4909 emitLocationInfoAndNewLine(ops);
4914void StmtEmitter::emitAssertionLabel(Operation *op) {
4915 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4921void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4922 SmallPtrSetImpl<Operation *> &ops,
4923 bool isConcurrent =
false) {
4926 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4927 ps.scopedBox(PP::ibox0, [&]() {
4928 ps.writeQuotedEscaped(message.getValue());
4930 for (
auto arg : args) {
4931 ps <<
"," << PP::space;
4932 emitExpression(arg, ops);
4938template <
typename Op>
4939LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4941 emitError(op,
"SV attributes emission is unimplemented for the op");
4944 SmallPtrSet<Operation *, 8> ops;
4946 ps.addCallback({op,
true});
4947 ps.scopedBox(PP::ibox2, [&]() {
4948 emitAssertionLabel(op);
4949 ps.scopedBox(PP::cbox0, [&]() {
4951 switch (op.getDefer()) {
4952 case DeferAssert::Immediate:
4954 case DeferAssert::Observed:
4957 case DeferAssert::Final:
4962 ps.scopedBox(PP::ibox0, [&]() {
4963 emitExpression(op.getExpression(), ops);
4966 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4970 ps.addCallback({op,
false});
4971 emitLocationInfoAndNewLine(ops);
4975LogicalResult StmtEmitter::visitSV(AssertOp op) {
4976 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4979LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4980 return emitImmediateAssertion(op,
PPExtString(
"assume"));
4983LogicalResult StmtEmitter::visitSV(CoverOp op) {
4984 return emitImmediateAssertion(op,
PPExtString(
"cover"));
4987template <
typename Op>
4988LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
4990 emitError(op,
"SV attributes emission is unimplemented for the op");
4993 SmallPtrSet<Operation *, 8> ops;
4995 ps.addCallback({op,
true});
4996 ps.scopedBox(PP::ibox2, [&]() {
4997 emitAssertionLabel(op);
4998 ps.scopedBox(PP::cbox0, [&]() {
4999 ps << opName << PP::nbsp <<
"property (";
5000 ps.scopedBox(PP::ibox0, [&]() {
5001 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5003 emitExpression(op.getClock(), ops);
5004 ps <<
")" << PP::space;
5005 emitExpression(op.getProperty(), ops);
5008 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5013 ps.addCallback({op,
false});
5014 emitLocationInfoAndNewLine(ops);
5018LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5019 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5022LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5023 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5026LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5027 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5032template <
typename Op>
5033LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5035 emitError(op,
"SV attributes emission is unimplemented for the op");
5045 Operation *parent = op->getParentOp();
5046 Value
property = op.getProperty();
5047 bool isTemporal = !
property.getType().isSignlessInteger(1);
5048 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5049 bool emitAsImmediate = !isTemporal && isProcedural;
5052 SmallPtrSet<Operation *, 8> ops;
5054 ps.addCallback({op,
true});
5055 ps.scopedBox(PP::ibox2, [&]() {
5057 emitAssertionLabel(op);
5059 ps.scopedBox(PP::cbox0, [&]() {
5060 if (emitAsImmediate)
5061 ps << opName <<
"(";
5063 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5065 Value clock = op.getClock();
5066 auto event = op.getEvent();
5068 ps.scopedBox(PP::ibox2, [&]() {
5069 PropertyEmitter(emitter, ops)
5070 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5073 ps.scopedBox(PP::ibox2, [&]() {
5074 PropertyEmitter(emitter, ops)
5075 .emitAssertPropertyBody(property, op.getDisable());
5080 ps.addCallback({op,
false});
5081 emitLocationInfoAndNewLine(ops);
5085LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5086 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5089LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5090 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5093LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5094 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5097LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5099 emitError(op,
"SV attributes emission is unimplemented for the op");
5102 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5103 .getMacroIdentifier());
5106 bool hasEmptyThen = op->getRegion(0).front().empty();
5108 ps <<
"`ifndef " << ident;
5110 ps <<
"`ifdef " << ident;
5112 SmallPtrSet<Operation *, 8> ops;
5114 emitLocationInfoAndNewLine(ops);
5117 emitStatementBlock(op->getRegion(0).front());
5119 if (!op->getRegion(1).empty()) {
5120 if (!hasEmptyThen) {
5122 ps <<
"`else // " << ident;
5123 setPendingNewline();
5125 emitStatementBlock(op->getRegion(1).front());
5132 setPendingNewline();
5140void StmtEmitter::emitBlockAsStatement(
5141 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5142 StringRef multiLineComment) {
5149 emitLocationInfoAndNewLine(locationOps);
5152 emitStatementBlock(*block);
5154 if (needsBeginEnd) {
5158 if (!multiLineComment.empty())
5159 ps <<
" // " << multiLineComment;
5160 setPendingNewline();
5164LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5166 for (
auto &op : ooop.getBody().front())
5171LogicalResult StmtEmitter::visitSV(IfOp op) {
5172 SmallPtrSet<Operation *, 8> ops;
5174 auto ifcondBox = PP::ibox2;
5176 emitSVAttributes(op);
5178 ps.addCallback({op,
true});
5179 ps <<
"if (" << ifcondBox;
5189 emitExpression(ifOp.getCond(), ops);
5190 ps << PP::end <<
")";
5191 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5193 if (!ifOp.hasElse())
5197 Block *elseBlock = ifOp.getElseBlock();
5199 if (!nestedElseIfOp) {
5204 emitBlockAsStatement(elseBlock, ops);
5210 ifOp = nestedElseIfOp;
5211 ps <<
"else if (" << ifcondBox;
5213 ps.addCallback({op,
false});
5218LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5219 emitSVAttributes(op);
5220 SmallPtrSet<Operation *, 8> ops;
5224 auto printEvent = [&](AlwaysOp::Condition cond) {
5225 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5226 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5228 ps.addCallback({op,
true});
5230 switch (op.getNumConditions()) {
5236 printEvent(op.getCondition(0));
5241 ps.scopedBox(PP::cbox0, [&]() {
5242 printEvent(op.getCondition(0));
5243 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5244 ps << PP::space <<
"or" << PP::space;
5245 printEvent(op.getCondition(i));
5254 std::string comment;
5255 if (op.getNumConditions() == 0) {
5256 comment =
"always @*";
5258 comment =
"always @(";
5261 [&](Attribute eventAttr) {
5262 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5263 comment += stringifyEventControl(event);
5265 [&]() { comment +=
", "; });
5269 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5270 ps.addCallback({op,
false});
5274LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5275 emitSVAttributes(op);
5276 SmallPtrSet<Operation *, 8> ops;
5280 ps.addCallback({op,
true});
5281 StringRef opString =
"always_comb";
5282 if (state.options.noAlwaysComb)
5283 opString =
"always @(*)";
5286 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5287 ps.addCallback({op,
false});
5291LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5292 emitSVAttributes(op);
5294 SmallPtrSet<Operation *, 8> ops;
5298 ps.addCallback({op,
true});
5299 ps <<
"always_ff @(";
5300 ps.scopedBox(PP::cbox0, [&]() {
5301 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5302 emitExpression(op.getClock(), ops);
5303 if (op.getResetStyle() == ResetType::AsyncReset) {
5304 ps << PP::nbsp <<
"or" << PP::space
5305 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5306 emitExpression(op.getReset(), ops);
5313 std::string comment;
5314 comment +=
"always_ff @(";
5315 comment += stringifyEventControl(op.getClockEdge());
5316 if (op.getResetStyle() == ResetType::AsyncReset) {
5318 comment += stringifyEventControl(*op.getResetEdge());
5322 if (op.getResetStyle() == ResetType::NoReset)
5323 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5326 emitLocationInfoAndNewLine(ops);
5327 ps.scopedBox(PP::bbox2, [&]() {
5333 if (op.getResetStyle() == ResetType::AsyncReset &&
5334 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5336 emitExpression(op.getReset(), ops);
5338 emitBlockAsStatement(op.getResetBlock(), ops);
5341 emitBlockAsStatement(op.getBodyBlock(), ops);
5346 ps <<
" // " << comment;
5347 setPendingNewline();
5349 ps.addCallback({op,
false});
5353LogicalResult StmtEmitter::visitSV(InitialOp op) {
5354 emitSVAttributes(op);
5355 SmallPtrSet<Operation *, 8> ops;
5358 ps.addCallback({op,
true});
5360 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5361 ps.addCallback({op,
false});
5365LogicalResult StmtEmitter::visitSV(CaseOp op) {
5366 emitSVAttributes(op);
5367 SmallPtrSet<Operation *, 8> ops, emptyOps;
5370 ps.addCallback({op,
true});
5371 if (op.getValidationQualifier() !=
5372 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5373 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5374 op.getValidationQualifier()))
5376 const char *opname =
nullptr;
5377 switch (op.getCaseStyle()) {
5378 case CaseStmtType::CaseStmt:
5381 case CaseStmtType::CaseXStmt:
5384 case CaseStmtType::CaseZStmt:
5388 ps << opname <<
" (";
5389 ps.scopedBox(PP::ibox0, [&]() {
5390 emitExpression(op.getCond(), ops);
5393 emitLocationInfoAndNewLine(ops);
5395 ps.scopedBox(PP::bbox2, [&]() {
5396 for (
auto &caseInfo : op.getCases()) {
5398 auto &
pattern = caseInfo.pattern;
5400 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5401 .Case<CaseBitPattern>([&](
auto bitPattern) {
5404 ps.invokeWithStringOS([&](
auto &os) {
5405 os << bitPattern->getWidth() <<
"'b";
5406 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5407 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5410 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5411 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5412 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5414 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5415 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5418 emitBlockAsStatement(caseInfo.block, emptyOps);
5424 ps.addCallback({op,
false});
5425 emitLocationInfoAndNewLine(ops);
5429LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5430 bool doNotPrint = op.getDoNotPrint();
5431 if (doNotPrint && !state.options.emitBindComments)
5436 emitSVAttributes(op);
5438 ps.addCallback({op,
true});
5441 <<
"/* This instance is elsewhere emitted as a bind statement."
5444 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5445 "attributes. The attributes will not be emitted.";
5448 SmallPtrSet<Operation *, 8> ops;
5453 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5454 assert(moduleOp &&
"Invalid IR");
5458 if (!op.getParameters().empty()) {
5461 bool printed =
false;
5463 llvm::zip(op.getParameters(),
5464 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5465 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5466 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5468 if (param.getValue() == modParam.getValue())
5473 ps <<
" #(" << PP::bbox2 << PP::newline;
5476 ps <<
"," << PP::newline;
5480 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5482 ps.invokeWithStringOS([&](
auto &os) {
5483 emitter.printParamValue(param.getValue(), os, [&]() {
5484 return op->emitOpError(
"invalid instance parameter '")
5485 << param.getName().getValue() <<
"' value";
5491 ps << PP::end << PP::newline <<
")";
5498 SmallVector<Value> instPortValues(modPortInfo.size());
5499 op.getValues(instPortValues, modPortInfo);
5500 emitInstancePortList(op, modPortInfo, instPortValues);
5502 ps.addCallback({op,
false});
5503 emitLocationInfoAndNewLine(ops);
5508 setPendingNewline();
5513LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5515 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5516 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5521 Operation *defaultModuleOp =
5522 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5524 SmallVector<Value> instPortValues(modPortInfo.size());
5525 op.getValues(instPortValues, modPortInfo);
5526 emitInstancePortList(op, modPortInfo, instPortValues);
5528 SmallPtrSet<Operation *, 8> ops;
5530 ps.addCallback({op,
false});
5531 emitLocationInfoAndNewLine(ops);
5536void StmtEmitter::emitInstancePortList(Operation *op,
5538 ArrayRef<Value> instPortValues) {
5539 SmallPtrSet<Operation *, 8> ops;
5542 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5543 ModulePortInfo containingPortList(containingModule.getPortList());
5548 size_t maxNameLength = 0;
5549 for (
auto &elt : modPortInfo) {
5550 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5553 auto getWireForValue = [&](Value result) {
5554 return result.getUsers().begin()->getOperand(0);
5558 bool isFirst =
true;
5559 bool isZeroWidth =
false;
5561 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5563 auto &modPort = modPortInfo.
at(portNum);
5565 Value portVal = instPortValues[portNum];
5570 bool shouldPrintComma =
true;
5572 shouldPrintComma =
false;
5573 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5575 shouldPrintComma =
true;
5580 if (shouldPrintComma)
5583 emitLocationInfoAndNewLine(ops);
5598 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5599 auto modPortName = modPort.getVerilogName();
5601 ps.spaces(maxNameLength - modPortName.size() + 1);
5603 ps.scopedBox(PP::ibox0, [&]() {
5610 if (!modPort.isOutput()) {
5612 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5613 ps <<
"/* Zero width */";
5615 emitExpression(portVal, ops, LowestPrecedence);
5616 }
else if (portVal.use_empty()) {
5617 ps <<
"/* unused */";
5618 }
else if (portVal.hasOneUse() &&
5619 (output = dyn_cast_or_null<OutputOp>(
5620 portVal.getUses().begin()->getOwner()))) {
5625 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5627 containingPortList.atOutput(outputPortNo).getVerilogName());
5629 portVal = getWireForValue(portVal);
5630 emitExpression(portVal, ops);
5636 if (!isFirst || isZeroWidth) {
5637 emitLocationInfoAndNewLine(ops);
5650LogicalResult StmtEmitter::visitSV(BindOp op) {
5651 emitter.emitBind(op);
5652 assert(state.pendingNewline);
5656LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5657 emitComment(op.getCommentAttr());
5659 emitSVAttributes(op);
5662 ps.addCallback({op,
true});
5664 setPendingNewline();
5666 emitStatementBlock(*op.getBodyBlock());
5668 ps <<
"endinterface" << PP::newline;
5669 ps.addCallback({op,
false});
5670 setPendingNewline();
5674LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5676 emitSVAttributes(op);
5678 ps.addCallback({op,
true});
5680 ps << PP::neverbox <<
"// ";
5681 ps.invokeWithStringOS([&](
auto &os) {
5686 ps.invokeWithStringOS(
5687 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5691 ps.addCallback({op,
false});
5692 setPendingNewline();
5696LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5698 ps.addCallback({op,
true});
5702 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5703 auto port = cast<ModportStructAttr>(portAttr);
5704 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5705 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5706 ps << PPExtString(getSymOpName(signalDecl));
5710 ps.addCallback({op,
false});
5711 setPendingNewline();
5715LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5717 ps.addCallback({op,
true});
5718 SmallPtrSet<Operation *, 8> emitted;
5721 emitExpression(op.getIface(), emitted);
5722 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5723 emitExpression(op.getRhs(), emitted);
5725 ps.addCallback({op,
false});
5726 setPendingNewline();
5730LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5731 auto decl = op.getReferencedMacro(&state.symbolCache);
5734 ps.addCallback({op,
true});
5736 if (decl.getArgs()) {
5738 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5739 ps << cast<StringAttr>(name);
5743 if (!op.getFormatString().empty()) {
5745 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5748 ps.addCallback({op,
false});
5749 setPendingNewline();
5753void StmtEmitter::emitStatement(Operation *op) {
5760 if (isa<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5764 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5765 succeeded(dispatchVerifVisitor(op)))
5768 emitOpError(op,
"emission to Verilog not supported");
5769 emitPendingNewlineIfNeeded();
5770 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5771 setPendingNewline();
5782 StmtEmitter &stmtEmitter) {
5789 if (isa<IfDefProceduralOp>(op->getParentOp()))
5797 SmallVector<Value, 8> exprsToScan(op->getOperands());
5802 while (!exprsToScan.empty()) {
5803 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5810 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5811 auto *defOp = readInout.getOperand().getDefiningOp();
5818 if (isa<sv::WireOp>(defOp))
5823 if (!isa<RegOp, LogicOp>(defOp))
5829 if (isa<LogicOp>(defOp) &&
5830 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5834 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5835 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5843 exprsToScan.append(expr->getOperands().begin(),
5844 expr->getOperands().end());
5850 if (expr->getBlock() != op->getBlock())
5855 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5862template <
class AssignTy>
5864 AssignTy singleAssign;
5865 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5866 if (hasSVAttributes(user))
5869 if (auto assign = dyn_cast<AssignTy>(user)) {
5872 singleAssign = assign;
5876 return isa<ReadInOutOp>(user);
5878 return singleAssign;
5884 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5888 if (op1->getBlock() != user->getBlock())
5894 return op1->isBeforeInBlock(user);
5898LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5899 emitSVAttributes(op);
5900 auto value = op->getResult(0);
5901 SmallPtrSet<Operation *, 8> opsForLocation;
5902 opsForLocation.insert(op);
5904 ps.addCallback({op,
true});
5907 auto type = value.getType();
5910 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5911 unsigned targetColumn = 0;
5912 unsigned column = 0;
5915 if (maxDeclNameWidth > 0)
5916 targetColumn += maxDeclNameWidth + 1;
5919 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5920 }
else if (!word.empty()) {
5922 column += word.size();
5923 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5924 ps.spaces(numSpaces);
5925 column += numSpaces;
5928 SmallString<8> typeString;
5931 llvm::raw_svector_ostream stringStream(typeString);
5936 if (maxTypeWidth > 0)
5937 targetColumn += maxTypeWidth + 1;
5938 unsigned numSpaces = 0;
5939 if (!typeString.empty()) {
5941 column += typeString.size();
5944 if (targetColumn > column)
5945 numSpaces = targetColumn - column;
5946 ps.spaces(numSpaces);
5947 column += numSpaces;
5953 ps.invokeWithStringOS(
5954 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5957 if (state.options.printDebugInfo) {
5958 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5959 auto innerSym = innerSymOp.getInnerSymAttr();
5960 if (innerSym && !innerSym.empty()) {
5962 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5968 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5969 ps << PP::space <<
"=" << PP::space;
5970 ps.invokeWithStringOS([&](
auto &os) {
5971 emitter.printParamValue(localparam.getValue(), os, [&]() {
5972 return op->emitOpError(
"invalid localparam value");
5977 if (
auto regOp = dyn_cast<RegOp>(op)) {
5978 if (
auto initValue = regOp.getInit()) {
5979 ps << PP::space <<
"=" << PP::space;
5980 ps.scopedBox(PP::ibox0, [&]() {
5981 emitExpression(initValue, opsForLocation, LowestPrecedence,
5990 if (isa<sv::WireOp>(op) &&
5991 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
5994 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
5995 auto *source = singleAssign.getSrc().getDefiningOp();
5999 if (!source || isa<ConstantOp>(source) ||
6000 op->getNextNode() == singleAssign) {
6001 ps << PP::space <<
"=" << PP::space;
6002 ps.scopedBox(PP::ibox0, [&]() {
6003 emitExpression(singleAssign.getSrc(), opsForLocation,
6007 emitter.assignsInlined.insert(singleAssign);
6015 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
6018 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6021 auto *source = singleAssign.getSrc().getDefiningOp();
6025 if (!source || isa<ConstantOp>(source) ||
6028 ps << PP::space <<
"=" << PP::space;
6029 ps.scopedBox(PP::ibox0, [&]() {
6030 emitExpression(singleAssign.getSrc(), opsForLocation,
6035 emitter.assignsInlined.insert(singleAssign);
6036 emitter.expressionsEmittedIntoDecl.insert(op);
6043 ps.addCallback({op,
false});
6044 emitLocationInfoAndNewLine(opsForLocation);
6048void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6051 NameCollector collector(emitter);
6052 collector.collectNames(block);
6055 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6056 maxTypeWidth = collector.getMaxTypeWidth();
6059void StmtEmitter::emitStatementBlock(Block &body) {
6060 ps.scopedBox(PP::bbox2, [&]() {
6065 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6066 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6071 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6072 collectNamesAndCalculateDeclarationWidths(body);
6075 for (
auto &op : body) {
6082void ModuleEmitter::emitStatement(Operation *op) {
6083 StmtEmitter(*
this, state.options).emitStatement(op);
6088void ModuleEmitter::emitSVAttributes(Operation *op) {
6096 setPendingNewline();
6103void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6104 auto verilogName =
module.getVerilogModuleNameAttr();
6106 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6108 setPendingNewline();
6117void ModuleEmitter::emitBind(BindOp op) {
6119 emitError(op,
"SV attributes emission is unimplemented for the op");
6120 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6126 Operation *childMod =
6127 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6131 ps.addCallback({op,
true});
6132 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6133 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6135 bool isFirst =
true;
6136 ps.scopedBox(PP::bbox2, [&]() {
6137 auto parentPortInfo = parentMod.getPortList();
6141 size_t maxNameLength = 0;
6142 for (
auto &elt : childPortInfo) {
6143 auto portName = elt.getVerilogName();
6144 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6145 maxNameLength = std::max(maxNameLength, elt.getName().size());
6148 SmallVector<Value> instPortValues(childPortInfo.size());
6149 inst.getValues(instPortValues, childPortInfo);
6151 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6153 Value portVal = instPortValues[idx];
6159 bool shouldPrintComma =
true;
6161 shouldPrintComma =
false;
6162 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6164 shouldPrintComma =
true;
6169 if (shouldPrintComma)
6182 ps << PP::neverbox <<
"//";
6186 ps.nbsp(maxNameLength - elt.getName().size());
6188 llvm::SmallPtrSet<Operation *, 4> ops;
6189 if (elt.isOutput()) {
6190 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6191 "output port must have either single or no use");
6192 if (portVal.use_empty()) {
6193 ps <<
"/* unused */";
6194 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6195 portVal.getUses().begin()->getOwner())) {
6198 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6200 parentPortList.atOutput(outputPortNo).getVerilogName());
6202 portVal = portVal.getUsers().begin()->getOperand(0);
6203 ExprEmitter(*
this, ops)
6204 .emitExpression(portVal, LowestPrecedence,
6208 ExprEmitter(*
this, ops)
6209 .emitExpression(portVal, LowestPrecedence,
6222 ps.addCallback({op,
false});
6223 setPendingNewline();
6226void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6228 emitError(op,
"SV attributes emission is unimplemented for the op");
6230 auto instance = op.getReferencedInstance(&state.symbolCache);
6232 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6233 instance.getInterfaceType().getInterface());
6235 ps.addCallback({op,
true});
6236 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6237 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6239 ps.addCallback({op,
false});
6240 setPendingNewline();
6243void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6247 auto printParamType = [&](Type type, Attribute defaultValue,
6248 SmallString<8> &result) {
6250 llvm::raw_svector_ostream sstream(result);
6255 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6256 if (intAttr.getValue().getBitWidth() == 32)
6258 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6259 if (fpAttr.getType().isF64())
6262 if (isa<NoneType>(type))
6269 if (
auto intType = type_dyn_cast<IntegerType>(type))
6270 if (intType.getWidth() == 32) {
6271 sstream <<
"/*integer*/";
6275 printPackedType(type, sstream, module->getLoc(),
6283 size_t maxTypeWidth = 0;
6284 SmallString<8> scratch;
6285 for (
auto param : params) {
6286 auto paramAttr = cast<ParamDeclAttr>(param);
6288 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6289 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6292 if (maxTypeWidth > 0)
6295 ps.scopedBox(PP::bbox2, [&]() {
6296 ps << PP::newline <<
"#(";
6297 ps.scopedBox(PP::cbox0, [&]() {
6300 [&](Attribute param) {
6301 auto paramAttr = cast<ParamDeclAttr>(param);
6302 auto defaultValue = paramAttr.getValue();
6304 printParamType(paramAttr.getType(), defaultValue, scratch);
6305 if (!scratch.empty())
6307 if (scratch.size() < maxTypeWidth)
6308 ps.nbsp(maxTypeWidth - scratch.size());
6310 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6311 module, paramAttr.getName()));
6315 ps.invokeWithStringOS([&](
auto &os) {
6317 return module->emitError("parameter '")
6318 << paramAttr.getName().getValue()
6319 << "' has invalid value";
6324 [&]() { ps <<
"," << PP::newline; });
6330void ModuleEmitter::emitPortList(Operation *module,
6332 bool emitAsTwoStateType) {
6334 if (portInfo.
size())
6335 emitLocationInfo(module->getLoc());
6339 bool hasOutputs =
false, hasZeroWidth =
false;
6340 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6341 SmallVector<SmallString<8>, 16> portTypeStrings;
6343 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6344 auto port = portInfo.
at(i);
6348 lastNonZeroPort = i;
6351 portTypeStrings.push_back({});
6353 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6355 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6358 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6361 if (maxTypeWidth > 0)
6365 ps.scopedBox(PP::bbox2, [&]() {
6366 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6367 auto lastPort = e - 1;
6370 auto portType = portInfo.
at(portIdx).
type;
6374 bool isZeroWidth =
false;
6379 ps << (isZeroWidth ?
"// " :
" ");
6383 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6384 switch (thisPortDirection) {
6385 case ModulePort::Direction::Output:
6388 case ModulePort::Direction::Input:
6389 ps << (hasOutputs ?
"input " :
"input ");
6391 case ModulePort::Direction::InOut:
6392 ps << (hasOutputs ?
"inout " :
"inout ");
6395 bool emitWireInPorts = state.options.emitWireInPorts;
6396 if (emitWireInPorts)
6400 if (!portTypeStrings[portIdx].
empty())
6401 ps << portTypeStrings[portIdx];
6402 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6403 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6405 size_t startOfNamePos =
6406 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6412 ps.invokeWithStringOS(
6413 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6416 auto innerSym = portInfo.
at(portIdx).
getSym();
6417 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6419 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6424 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6428 if (
auto loc = portInfo.
at(portIdx).
loc)
6429 emitLocationInfo(loc);
6439 if (!state.options.disallowPortDeclSharing) {
6440 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6443 auto port = portInfo.
at(portIdx);
6447 bool isZeroWidth =
false;
6452 ps << (isZeroWidth ?
"// " :
" ");
6455 ps.nbsp(startOfNamePos);
6458 StringRef name = port.getVerilogName();
6462 ps.invokeWithStringOS(
6463 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6466 auto sym = port.getSym();
6467 if (state.options.printDebugInfo && sym && !sym.empty())
6468 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6472 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6476 if (
auto loc = port.loc)
6477 emitLocationInfo(loc);
6488 if (!portInfo.
size()) {
6490 SmallPtrSet<Operation *, 8> moduleOpSet;
6491 moduleOpSet.insert(module);
6492 emitLocationInfoAndNewLine(moduleOpSet);
6495 ps <<
");" << PP::newline;
6496 setPendingNewline();
6500void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6501 currentModuleOp =
module;
6503 emitComment(module.getCommentAttr());
6504 emitSVAttributes(module);
6506 ps.addCallback({module,
true});
6510 emitParameters(module, module.getParameters());
6514 assert(state.pendingNewline);
6517 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6520 ps.addCallback({module,
false});
6522 setPendingNewline();
6524 currentModuleOp =
nullptr;
6527void ModuleEmitter::emitFunc(FuncOp func) {
6529 if (func.isDeclaration())
6532 currentModuleOp = func;
6534 ps.addCallback({func,
true});
6538 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6540 ps <<
"endfunction";
6542 currentModuleOp =
nullptr;
6551 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6558 void emit(emit::FileListOp op);
6561 void emit(Block *block);
6563 void emitOp(emit::RefOp op);
6564 void emitOp(emit::VerbatimOp op);
6568 for (Operation &op : *block) {
6569 TypeSwitch<Operation *>(&op)
6570 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6571 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6572 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6573 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6574 .Case<BindInterfaceOp>(
6575 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6576 .Case<TypeScopeOp>([&](
auto typedecls) {
6577 ModuleEmitter(state).emitStatement(typedecls);
6580 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6586 for (
auto sym : op.getFiles()) {
6587 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6589 auto it = state.fileMapping.find(fileName);
6590 if (it == state.fileMapping.end()) {
6591 emitOpError(op,
" references an invalid file: ") << sym;
6595 auto file = cast<emit::FileOp>(it->second);
6596 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6603 StringAttr target = op.getTargetAttr().getAttr();
6604 auto *targetOp = state.symbolCache.getDefinition(target);
6605 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6607 TypeSwitch<Operation *>(targetOp)
6608 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6609 .Case<hw::HWModuleOp>(
6610 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6611 .Case<TypeScopeOp>([&](
auto typedecls) {
6612 ModuleEmitter(state).emitStatement(typedecls);
6615 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6621 SmallPtrSet<Operation *, 8> ops;
6626 StringRef text = op.getText();
6630 const auto &[lhs, rhs] = text.split(
'\n');
6634 ps << PP::end << PP::newline << PP::neverbox;
6636 }
while (!text.empty());
6639 emitLocationInfoAndNewLine(ops);
6657 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6658 moduleOp->walk([&](Operation *op) {
6660 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6663 SymbolTable::getSymbolAttrName()),
6664 name.getSymName(), op);
6665 if (isa<BindOp>(op))
6671 auto collectPorts = [&](
auto moduleOp) {
6672 auto portInfo = moduleOp.getPortList();
6673 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6674 if (!p.attrs || p.attrs.empty())
6676 for (NamedAttribute portAttr : p.attrs) {
6677 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6686 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6687 for (
auto file :
designOp.getOps<emit::FileOp>())
6688 for (
auto refs : file.getOps<emit::RefOp>())
6689 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6691 SmallString<32> outputPath;
6692 for (
auto &op : *
designOp.getBody()) {
6695 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6697 bool hasFileName =
false;
6698 bool emitReplicatedOps = !isFileOp;
6699 bool addToFilelist = !isFileOp;
6705 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6707 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6708 <<
" on " << op <<
"\n";);
6709 if (!attr.isDirectory())
6712 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6713 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6716 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6721 if (!defaultFileName.isTriviallyEmpty()) {
6722 llvm::sys::path::append(outputPath, defaultFileName);
6724 op->emitError(
"file name unspecified");
6726 llvm::sys::path::append(outputPath,
"error.out");
6730 auto destFile = StringAttr::get(op->getContext(), outputPath);
6731 auto &file =
files[destFile];
6732 file.ops.push_back(info);
6733 file.emitReplicatedOps = emitReplicatedOps;
6734 file.addToFilelist = addToFilelist;
6735 file.isVerilog = outputPath.ends_with(
".sv");
6740 if (!attr || attr.isDirectory()) {
6741 auto excludeFromFileListAttr =
6742 BoolAttr::get(op->getContext(), !addToFilelist);
6743 auto includeReplicatedOpsAttr =
6744 BoolAttr::get(op->getContext(), emitReplicatedOps);
6745 auto outputFileAttr = hw::OutputFileAttr::get(
6746 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6747 op->setAttr(
"output_file", outputFileAttr);
6753 TypeSwitch<Operation *>(&op)
6754 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6756 fileMapping.try_emplace(file.getSymNameAttr(), file);
6757 separateFile(file, file.getFileName());
6759 .Case<emit::FragmentOp>([&](
auto fragment) {
6762 .Case<HWModuleOp>([&](
auto mod) {
6764 auto sym = mod.getNameAttr();
6767 collectInstanceSymbolsAndBinds(mod);
6769 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6770 if (it->second.size() != 1 || attr) {
6773 op.emitError(
"modules can be emitted to a single file");
6781 if (attr || separateModules)
6787 .Case<InterfaceOp>([&](InterfaceOp intf) {
6792 for (
auto &op : *intf.getBodyBlock())
6793 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6794 if (
auto name = symOp.getNameAttr())
6798 if (attr || separateModules)
6799 separateFile(intf, intf.getSymName() +
".sv");
6809 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6810 [&](Operation *op) {
6816 separateFile(op,
"");
6818 .Case<FuncOp>([&](
auto op) {
6824 separateFile(op,
"");
6828 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6831 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6840 separateFile(op,
"");
6842 .Case<BindOp>([&](
auto op) {
6844 separateFile(op,
"bindfile.sv");
6849 .Case<MacroDeclOp>([&](
auto op) {
6852 .Case<sv::ReserveNamesOp>([](
auto op) {
6855 .Case<om::ClassLike>([&](
auto op) {
6858 .Case<om::ConstantOp>([&](
auto op) {
6861 .Default([&](
auto *) {
6862 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6882 size_t lastReplicatedOp = 0;
6884 bool emitHeaderInclude =
6887 if (emitHeaderInclude)
6890 size_t numReplicatedOps =
6895 DenseSet<emit::FragmentOp> includedFragments;
6896 for (
const auto &opInfo : file.
ops) {
6897 Operation *op = opInfo.op;
6901 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6907 if (
auto fragments =
6909 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6913 op->emitError(
"cannot find referenced fragment ") << sym;
6916 emit::FragmentOp fragment = it->second;
6917 if (includedFragments.insert(fragment).second) {
6918 thingsToEmit.emplace_back(it->second);
6924 thingsToEmit.emplace_back(op);
6929 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6934 TypeSwitch<Operation *>(op)
6935 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6936 .Case<HWModuleExternOp>([&](
auto op) {
6939 .Case<HWModuleGeneratedOp>(
6940 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6941 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6942 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6943 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6944 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6945 .Case<TypeScopeOp>([&](
auto typedecls) {
6946 ModuleEmitter(state).emitStatement(typedecls);
6948 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6950 .Case<MacroDefOp, FuncDPIImportOp>(
6951 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6952 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6953 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6954 .Default([&](
auto *op) {
6955 state.encounteredError =
true;
6956 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6963 llvm::formatted_raw_ostream &os,
6964 StringAttr fileName,
bool parallelize) {
6965 MLIRContext *context =
designOp->getContext();
6969 parallelize &= context->isMultithreadingEnabled();
6980 size_t lineOffset = 0;
6981 for (
auto &entry : thingsToEmit) {
6982 entry.verilogLocs.setStream(os);
6983 if (
auto *op = entry.getOperation()) {
6988 state.addVerilogLocToOps(lineOffset, fileName);
6990 os << entry.getStringData();
6995 if (state.encounteredError)
7013 SmallString<256> buffer;
7014 llvm::raw_svector_ostream tmpStream(buffer);
7015 llvm::formatted_raw_ostream rs(tmpStream);
7026 for (
auto &entry : thingsToEmit) {
7029 auto *op = entry.getOperation();
7031 auto lineOffset = os.getLine() + 1;
7032 os << entry.getStringData();
7036 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7039 entry.verilogLocs.setStream(os);
7046 state.addVerilogLocToOps(0, fileName);
7062 module.emitWarning()
7063 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7064 "created only at SplitExportVerilog";
7073 for (
const auto &it : emitter.
files) {
7074 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7075 "\" ----- 8< -----\n\n");
7081 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7082 "\" ----- 8< -----\n\n");
7083 for (
auto &name : it.second)
7084 contents += name.str() +
"\n";
7085 list.emplace_back(contents);
7088 llvm::formatted_raw_ostream rs(os);
7092 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7101 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7103 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7104 if (failed(failableParallelForEach(
7105 module->getContext(), modulesToPrepare,
7106 [&](
auto op) { return prepareHWModule(op, options); })))
7113struct ExportVerilogPass
7114 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7115 ExportVerilogPass(raw_ostream &os) : os(os) {}
7116 void runOnOperation()
override {
7118 mlir::OpPassManager preparePM(
"builtin.module");
7121 auto &modulePM = preparePM.nestAny();
7123 if (failed(runPipeline(preparePM, getOperation())))
7124 return signalPassFailure();
7127 return signalPassFailure();
7134struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7135 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7136 : ExportVerilogPass{*os} {
7137 owned = std::move(os);
7141 std::unique_ptr<llvm::raw_ostream> owned;
7145std::unique_ptr<mlir::Pass>
7147 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7150std::unique_ptr<mlir::Pass>
7152 return std::make_unique<ExportVerilogPass>(os);
7163static std::unique_ptr<llvm::ToolOutputFile>
7167 SmallString<128> outputFilename(dirname);
7169 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7172 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7174 emitter.
designOp.emitError(
"cannot create output directory \"")
7175 << outputDir <<
"\": " << error.message();
7181 std::string errorMessage;
7182 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7184 emitter.
designOp.emitError(errorMessage);
7201 llvm::formatted_raw_ostream rs(output->os());
7207 StringAttr::get(fileName.getContext(), output->getFilename()),
7213 StringRef dirname) {
7224 bool insertSuccess =
7226 .insert({StringAttr::get(module.getContext(),
circtHeader),
7232 if (!insertSuccess) {
7233 module.emitError() << "tried to emit a heder to " << circtHeader
7234 << ", but the file is used as an output too.";
7240 parallelForEach(module->getContext(), emitter.
files.begin(),
7241 emitter.
files.end(), [&](
auto &it) {
7242 createSplitOutputFile(it.first, it.second, dirname,
7247 SmallString<128> filelistPath(dirname);
7248 llvm::sys::path::append(filelistPath,
"filelist.f");
7250 std::string errorMessage;
7251 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7253 module->emitError(errorMessage);
7257 for (
const auto &it : emitter.
files) {
7258 if (it.second.addToFilelist)
7259 output->os() << it.first.str() <<
"\n";
7268 for (
auto &name : it.second)
7269 output->os() << name.str() <<
"\n";
7280 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7282 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7283 if (failed(failableParallelForEach(
7284 module->getContext(), modulesToPrepare,
7285 [&](
auto op) { return prepareHWModule(op, options); })))
7293struct ExportSplitVerilogPass
7294 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7295 ExportSplitVerilogPass(StringRef directory) {
7296 directoryName = directory.str();
7298 void runOnOperation()
override {
7300 mlir::OpPassManager preparePM(
"builtin.module");
7305 if (failed(runPipeline(preparePM, getOperation())))
7306 return signalPassFailure();
7309 return signalPassFailure();
7314std::unique_ptr<mlir::Pass>
7316 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 std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef fileName, StringRef dirname, SharedEmitterState &emitter)
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)
static InstancePath empty
RewritePatternSet pattern
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.
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="./")
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoicesPass()
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< mlir::Pass > createLegalizeAnonEnumsPass()
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.
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
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.