40#include "mlir/IR/BuiltinOps.h"
41#include "mlir/IR/ImplicitLocOpBuilder.h"
42#include "mlir/IR/Location.h"
43#include "mlir/IR/Threading.h"
44#include "mlir/Interfaces/FunctionImplementation.h"
45#include "mlir/Pass/PassManager.h"
46#include "mlir/Support/FileUtilities.h"
47#include "llvm/ADT/MapVector.h"
48#include "llvm/ADT/STLExtras.h"
49#include "llvm/ADT/StringSet.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/Support/FileSystem.h"
52#include "llvm/Support/FormattedStream.h"
53#include "llvm/Support/Path.h"
54#include "llvm/Support/SaveAndRestore.h"
55#include "llvm/Support/ToolOutputFile.h"
56#include "llvm/Support/raw_ostream.h"
59#define GEN_PASS_DEF_EXPORTSPLITVERILOG
60#define GEN_PASS_DEF_EXPORTVERILOG
61#include "circt/Conversion/Passes.h.inc"
68using namespace ExportVerilog;
70using namespace pretty;
72#define DEBUG_TYPE "export-verilog"
80enum VerilogPrecedence {
101enum SubExprSignResult { IsSigned, IsUnsigned };
107 VerilogPrecedence precedence;
110 SubExprSignResult signedness;
112 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
113 : precedence(precedence), signedness(signedness) {}
123 return Builder(ctx).getI32IntegerAttr(value);
126static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
127 return Builder(ctx).getIntegerAttr(t, value);
143 if (isa<VerbatimExprOp>(op)) {
144 if (op->getNumOperands() == 0 &&
145 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
150 if (isa<XMRRefOp>(op))
154 if (isa<MacroRefExprOp>(op))
164 if (op->getNumOperands() == 0)
168 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
172 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
173 auto *indexOp = array.getIndex().getDefiningOp();
174 if (!indexOp || isa<ConstantOp>(indexOp))
176 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
177 auto *readSrc = read.getInput().getDefiningOp();
179 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
194 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
195 return attr.getValue();
196 return TypeSwitch<Operation *, StringRef>(symOp)
199 .Case<InterfaceOp>([&](InterfaceOp op) {
202 .Case<InterfaceSignalOp>(
203 [&](InterfaceSignalOp op) {
return op.getSymName(); })
204 .Case<InterfaceModportOp>(
205 [&](InterfaceModportOp op) {
return op.getSymName(); })
206 .Default([&](Operation *op) {
207 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
208 return attr.getValue();
209 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
210 return attr.getValue();
211 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
212 return attr.getValue();
214 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
215 return attr.getValue();
216 return StringRef(
"");
221template <
typename PPS>
223 os <<
"/*Zero width*/ 1\'b0";
228 auto hml = cast<HWModuleLike>(module);
229 return hml.getPort(portArgNum).getVerilogName();
234 auto hml = cast<HWModuleLike>(module);
235 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
236 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
237 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
238 return updatedName.getValue();
239 return hml.getHWModuleType().getPortName(pId);
248 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
249 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
250 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
251 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
252 UnpackedOpenArrayCastOp>(op))
256 if (isa<verif::ContractOp>(op))
266static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
268 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
269 if (integer.getWidth() != 1)
270 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
273 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
274 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
279 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
280 dims.push_back(intType.getWidth());
284 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
285 return getTypeDims(dims, inout.getElementType(), loc);
286 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
287 return getTypeDims(dims, uarray.getElementType(), loc);
288 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
289 return getTypeDims(dims, uarray.getElementType(), loc);
291 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
294 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
300 SmallVector<Attribute, 4> aDims;
303 SmallVector<Attribute, 4> bDims;
306 return aDims == bDims;
312 if (
auto intType = dyn_cast<IntegerType>(type))
313 return intType.getWidth() == 0;
314 if (
auto inout = dyn_cast<hw::InOutType>(type))
316 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
317 return uarray.getNumElements() == 0 ||
319 if (
auto array = dyn_cast<hw::ArrayType>(type))
320 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
321 if (
auto structType = dyn_cast<hw::StructType>(type))
322 return llvm::all_of(structType.getElements(),
323 [](
auto elem) { return isZeroBitType(elem.type); });
324 if (
auto enumType = dyn_cast<hw::EnumType>(type))
325 return enumType.getFields().empty();
326 if (
auto unionType = dyn_cast<hw::UnionType>(type))
327 return hw::getBitWidth(unionType) == 0;
339 return TypeSwitch<Type, Type>(type)
340 .Case<InOutType>([](InOutType inoutType) {
343 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
346 .Default([](Type type) {
return type; });
351 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
352 auto elementType = cast<hw::InOutType>(type).getElementType();
358 return TypeSwitch<Type, bool>(type)
359 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
362 .Case<StructType>([](
auto) {
return true; })
363 .Default([](
auto) {
return false; });
377 if (
auto name = lhs.getName().compare(rhs.getName()))
379 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
384 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
386 if (lhs.getLine() != rhs.getLine())
387 return lhs.getLine() < rhs.getLine() ? -1 : 1;
388 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
393 Location lhsCallee = lhs.getCallee();
394 Location rhsCallee = rhs.getCallee();
398 Location lhsCaller = lhs.getCaller();
399 Location rhsCaller = rhs.getCaller();
403template <
typename TTargetLoc>
405 auto lhsT = dyn_cast<TTargetLoc>(lhs);
406 auto rhsT = dyn_cast<TTargetLoc>(rhs);
433 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
438 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
443 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
460 SmallPtrSetImpl<Attribute> &locationSet) {
461 llvm::TypeSwitch<Location, void>(loc)
462 .Case<FusedLoc>([&](
auto fusedLoc) {
463 for (
auto subLoc : fusedLoc.getLocations())
466 .Default([&](
auto loc) { locationSet.insert(loc); });
470template <
typename TVector>
472 llvm::array_pod_sort(
473 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
474 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
482 SmallPtrSet<Attribute, 8> locationSet;
483 locationSet.insert(loc);
484 llvm::raw_string_ostream os(
output);
490 const SmallPtrSetImpl<Operation *> &ops) {
494 SmallPtrSet<Attribute, 8> locationSet;
497 llvm::raw_string_ostream os(
output);
506 const SmallPtrSetImpl<Attribute> &locationSet) {
507 if (style == LoweringOptions::LocationInfoStyle::None)
510 llvm::raw_string_ostream sstr(resstr);
512 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
516 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
517 "other styles must be already handled");
518 os <<
"@[" << resstr <<
"]";
527 const SmallPtrSetImpl<Attribute> &locationSet)
543 bool withName = !loc.getName().empty();
545 os <<
"'" << loc.getName().strref() <<
"'(";
554 os << loc.getFilename().getValue();
555 if (
auto line = loc.getLine()) {
557 if (
auto col = loc.getColumn())
569 StringRef lastFileName;
570 for (
size_t i = 0, e = locVector.size(); i != e;) {
575 auto first = locVector[i];
576 if (first.getFilename() != lastFileName) {
577 lastFileName = first.getFilename();
584 first.getFilename() == locVector[
end].getFilename() &&
585 first.getLine() == locVector[
end].getLine())
590 if (
auto line = first.getLine()) {
592 if (
auto col = first.getColumn())
600 os <<
':' << first.getLine() <<
":{";
602 os << locVector[i++].getColumn();
614 llvm::TypeSwitch<Location, void>(loc)
615 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
617 .Case<mlir::FusedLoc>([&](
auto loc) {
618 SmallPtrSet<Attribute, 8> locationSet;
622 .Default([&](
auto loc) {
634 switch (locationSet.size()) {
645 SmallVector<FileLineColLoc, 8> flcLocs;
646 SmallVector<Attribute, 8> otherLocs;
647 flcLocs.reserve(locationSet.size());
648 otherLocs.reserve(locationSet.size());
649 for (Attribute loc : locationSet) {
650 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
651 flcLocs.push_back(flcLoc);
653 otherLocs.push_back(loc);
664 size_t sstrSize =
os.tell();
665 bool emittedAnything =
false;
666 auto recheckEmittedSomething = [&]() {
667 size_t currSize =
os.tell();
668 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
669 emittedAnything |= emittedSomethingSinceLastCheck;
671 return emittedSomethingSinceLastCheck;
680 if (recheckEmittedSomething()) {
682 recheckEmittedSomething();
688 if (emittedAnything && !flcLocs.empty())
693 llvm::raw_string_ostream &
os;
705 if (isa<BlockArgument>(v))
714 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
719 if (v.getDefiningOp<ReadInterfaceSignalOp>())
732 if (
auto cast = dyn_cast<BitcastOp>(op))
733 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
737 if (op->hasOneUse() &&
738 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
746 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
752 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
756 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
757 if (verbatim.getFormatString().size() > 32)
762 for (
auto &use : op->getUses()) {
763 auto *user = use.getOwner();
773 UnionExtractOp, IndexedPartSelectOp>(user))
774 if (use.getOperandNumber() == 0 &&
785 auto usedInExprControl = [user, &use]() {
786 return TypeSwitch<Operation *, bool>(user)
787 .Case<ltl::ClockOp>([&](
auto clockOp) {
789 return clockOp.getClock() == use.get();
791 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
792 sv::CoverConcurrentOp>(
793 [&](
auto op) {
return op.getClock() == use.get(); })
794 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
795 sv::CoverPropertyOp>([&](
auto op) {
796 return op.getDisable() == use.get() || op.getClock() == use.get();
798 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
803 .Default([](
auto) {
return false; });
806 if (!usedInExprControl())
810 auto read = dyn_cast<ReadInOutOp>(op);
813 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
824 unsigned numStatements = 0;
825 block.walk([&](Operation *op) {
827 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
828 return WalkResult::advance();
830 TypeSwitch<Operation *, unsigned>(op)
831 .Case<VerbatimOp>([&](
auto) {
837 .Case<IfOp>([&](
auto) {
848 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
849 .Case<OutputOp>([&](OutputOp oop) {
852 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
853 Operation *op = operand.getDefiningOp();
854 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
857 .Default([](
auto) {
return 1; });
858 if (numStatements > 1)
859 return WalkResult::interrupt();
860 return WalkResult::advance();
862 if (numStatements == 0)
864 if (numStatements == 1)
874 if (op->getResult(0).use_empty())
879 if (op->hasOneUse() &&
880 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
881 *op->getUsers().begin()))
903 for (
auto &op : *elseBlock) {
904 if (
auto opIf = dyn_cast<IfOp>(op)) {
921template <
typename PPS>
923 enum Container { NoContainer, InComment, InAttr };
924 Container currentContainer = NoContainer;
926 auto closeContainer = [&] {
927 if (currentContainer == NoContainer)
929 if (currentContainer == InComment)
931 else if (currentContainer == InAttr)
933 ps << PP::end << PP::end;
935 currentContainer = NoContainer;
938 bool isFirstContainer =
true;
939 auto openContainer = [&](Container newContainer) {
940 assert(newContainer != NoContainer);
941 if (currentContainer == newContainer)
945 if (!isFirstContainer)
946 ps << (mayBreak ? PP::space : PP::nbsp);
947 isFirstContainer =
false;
950 if (newContainer == InComment)
952 else if (newContainer == InAttr)
954 currentContainer = newContainer;
962 ps.scopedBox(PP::cbox0, [&]() {
963 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
964 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
966 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
968 if (attr.getExpression())
969 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
978 if (
auto *op = val.getDefiningOp())
981 if (
auto port = dyn_cast<BlockArgument>(val)) {
983 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
984 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
986 port.getArgNumber());
988 assert(
false &&
"unhandled value");
1000class VerilogEmitterState {
1002 explicit VerilogEmitterState(ModuleOp designOp,
1008 llvm::formatted_raw_ostream &os,
1009 StringAttr fileName,
OpLocMap &verilogLocMap)
1010 : designOp(designOp), shared(shared), options(options),
1011 symbolCache(symbolCache), globalNames(globalNames),
1012 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1013 pp(os, options.emittedLineLength), fileName(fileName) {
1014 pp.setListener(&saver);
1037 llvm::formatted_raw_ostream &os;
1039 bool encounteredError =
false;
1048 bool pendingNewline =
false;
1062 StringAttr fileName;
1068 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1071 verilogLocMap.
clear();
1075 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1076 void operator=(
const VerilogEmitterState &) =
delete;
1089using CallbackDataTy = std::pair<Operation *, bool>;
1093 VerilogEmitterState &state;
1098 explicit EmitterBase(VerilogEmitterState &state)
1100 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1102 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1103 state.encounteredError =
true;
1104 return op->emitError(message);
1107 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1108 state.encounteredError =
true;
1109 return op->emitOpError(message);
1112 void emitLocationImpl(llvm::StringRef location) {
1115 ps << PP::neverbreak;
1116 if (!location.empty())
1117 ps <<
"\t// " << location;
1120 void emitLocationInfo(Location loc) {
1128 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1131 setPendingNewline();
1134 template <
typename PPS>
1135 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1136 llvm::function_ref<
void(Value)> operandEmitter,
1137 ArrayAttr symAttrs);
1143 void emitComment(StringAttr comment);
1147 void emitPendingNewlineIfNeeded() {
1148 if (state.pendingNewline) {
1149 state.pendingNewline =
false;
1153 void setPendingNewline() {
1154 assert(!state.pendingNewline);
1155 state.pendingNewline =
true;
1158 void startStatement() { emitPendingNewlineIfNeeded(); }
1161 void operator=(
const EmitterBase &) =
delete;
1162 EmitterBase(
const EmitterBase &) =
delete;
1166template <
typename PPS>
1167void EmitterBase::emitTextWithSubstitutions(
1168 PPS &ps, StringRef
string, Operation *op,
1169 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1180 if (
auto *itemOp = item.getOp()) {
1181 if (item.hasPort()) {
1185 if (!symOpName.empty())
1187 emitError(itemOp,
"cannot get name for symbol ") << sym;
1189 emitError(op,
"cannot get name for symbol ") << sym;
1191 return StringRef(
"<INVALID>");
1197 unsigned numSymOps = symAttrs.size();
1198 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1201 next =
string.find(
"{{", next);
1202 if (next == StringRef::npos)
1209 while (next <
string.size() &&
isdigit(
string[next]))
1212 if (start == next) {
1216 size_t operandNoLength = next - start;
1219 StringRef fmtOptsStr;
1220 if (
string[next] ==
':') {
1221 size_t startFmtOpts = next + 1;
1222 while (next <
string.size() &&
string[next] !=
'}')
1224 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1228 if (!
string.substr(next).starts_with(
"}}"))
1232 unsigned operandNo = 0;
1233 if (
string.drop_front(start)
1234 .take_front(operandNoLength)
1235 .getAsInteger(10, operandNo)) {
1236 emitError(op,
"operand substitution too large");
1242 auto before =
string.take_front(start - 2);
1243 if (!before.empty())
1248 if (operandNo < op->getNumOperands())
1250 operandEmitter(op->getOperand(operandNo));
1251 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1252 unsigned symOpNum = operandNo - op->getNumOperands();
1253 auto sym = symAttrs[symOpNum];
1254 StringRef symVerilogName;
1255 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1256 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1257 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1258 auto namepath = globalRef.getNamepathAttr().getValue();
1259 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1262 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1264 auto innerRef = cast<InnerRefAttr>(sym);
1265 auto ref = state.symbolCache.getInnerDefinition(
1266 innerRef.getModule(), innerRef.getName());
1267 ps << namify(innerRef, ref);
1270 symVerilogName = namify(sym, symOp);
1273 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1274 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1276 symVerilogName = namify(sym, symOp);
1278 if (!symVerilogName.empty())
1281 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1285 string =
string.drop_front(next);
1291 while (emitUntilSubstitution())
1295 if (!
string.
empty())
1299void EmitterBase::emitComment(StringAttr comment) {
1306 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1310 auto ref = comment.getValue();
1312 while (!ref.empty()) {
1313 std::tie(line, ref) = ref.split(
"\n");
1320 if (line.size() <= lineLength) {
1322 setPendingNewline();
1333 auto breakPos = line.rfind(
' ', lineLength);
1335 if (breakPos == StringRef::npos) {
1336 breakPos = line.find(
' ', lineLength);
1339 if (breakPos == StringRef::npos)
1340 breakPos = line.size();
1347 setPendingNewline();
1348 breakPos = line.find_first_not_of(
' ', breakPos);
1350 if (breakPos == StringRef::npos)
1353 line = line.drop_front(breakPos);
1363 bool addPrefixUnderScore =
true;
1366 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1370 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1372 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1374 result = StringAttr::get(expr.getContext(), name);
1376 }
else if (
auto *op = expr.getDefiningOp()) {
1378 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1380 result = StringAttr::get(expr.getContext(), name);
1382 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1388 addPrefixUnderScore =
false;
1390 TypeSwitch<Operation *>(op)
1393 .Case([&result](VerbatimExprOp verbatim) {
1394 verbatim.getAsmResultNames([&](Value, StringRef name) {
1395 result = StringAttr::get(verbatim.getContext(), name);
1398 .Case([&result](VerbatimExprSEOp verbatim) {
1399 verbatim.getAsmResultNames([&](Value, StringRef name) {
1400 result = StringAttr::get(verbatim.getContext(), name);
1406 if (
auto operandName =
1409 cast<IntegerType>(extract.getType()).getWidth();
1411 result = StringAttr::get(extract.getContext(),
1412 operandName.strref() +
"_" +
1413 Twine(extract.getLowBit()));
1415 result = StringAttr::get(
1416 extract.getContext(),
1417 operandName.strref() +
"_" +
1418 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1419 Twine(extract.getLowBit()));
1427 if (!result || result.strref().empty())
1431 if (addPrefixUnderScore && result.strref().front() !=
'_')
1432 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1444class ModuleEmitter :
public EmitterBase {
1446 explicit ModuleEmitter(VerilogEmitterState &state)
1447 : EmitterBase(state), currentModuleOp(nullptr),
1451 emitPendingNewlineIfNeeded();
1455 void emitParameters(Operation *module, ArrayAttr params);
1456 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1457 bool emitAsTwoStateType =
false);
1460 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1461 void emitFunc(FuncOp);
1464 void emitStatement(Operation *op);
1465 void emitBind(BindOp op);
1466 void emitBindInterface(BindInterfaceOp op);
1468 void emitSVAttributes(Operation *op);
1471 StringRef getVerilogStructFieldName(StringAttr field) {
1472 return fieldNameResolver.getRenamedFieldName(field).getValue();
1479 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1491 bool printPackedType(Type type, raw_ostream &os, Location loc,
1492 Type optionalAliasType = {},
bool implicitIntType =
true,
1493 bool singleBitDefaultType =
true,
1494 bool emitAsTwoStateType =
false);
1498 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1506 function_ref<InFlightDiagnostic()> emitError);
1509 VerilogPrecedence parenthesizeIfLooserThan,
1510 function_ref<InFlightDiagnostic()> emitError);
1516 Operation *currentModuleOp;
1522 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1528 SmallPtrSet<Operation *, 16> assignsInlined;
1537 const ModuleEmitter &emitter) {
1538 if (isa<RegOp>(op)) {
1543 cast<InOutType>(op->getResult(0).getType()).getElementType();
1550 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1551 while (isa<ArrayType>(innerType.getElementType()))
1552 innerType = cast<ArrayType>(innerType.getElementType());
1553 if (isa<StructType>(innerType.getElementType()) ||
1554 isa<TypeAliasType>(innerType.getElementType()))
1562 if (isa<sv::WireOp>(op))
1564 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1565 return "localparam";
1568 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1569 return interface.getInterfaceType().getInterface().getValue();
1573 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1577 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1579 if (isa<LogicOp>(op)) {
1585 if (isProcedural && !stripAutomatic)
1586 return hasStruct ?
"automatic" :
"automatic logic";
1587 return hasStruct ?
"" :
"logic";
1594 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1597 assert(!emitter.state.options.disallowLocalVariables &&
1598 "automatic variables not allowed");
1602 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1603 :
"automatic logic";
1610static void emitDim(Attribute width, raw_ostream &os, Location loc,
1611 ModuleEmitter &emitter,
bool downTo) {
1613 os <<
"<<invalid type>>";
1616 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1617 if (intAttr.getValue().isZero()) {
1618 os <<
"/*Zero Width*/";
1623 os << (intAttr.getValue().getZExtValue() - 1);
1633 auto typedAttr = dyn_cast<TypedAttr>(width);
1635 mlir::emitError(loc,
"untyped dimension attribute ") << width;
1639 getIntAttr(loc.getContext(), typedAttr.getType(),
1640 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1641 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1645 emitter.printParamValue(width, os, [loc]() {
1646 return mlir::emitError(loc,
"invalid parameter in type");
1654static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1655 ModuleEmitter &emitter) {
1656 for (Attribute width : dims) {
1657 emitDim(width, os, loc, emitter,
true);
1662void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1663 SmallVector<Attribute, 4> dims;
1695 SmallVectorImpl<Attribute> &dims,
1696 bool implicitIntType,
bool singleBitDefaultType,
1697 ModuleEmitter &emitter,
1698 Type optionalAliasType = {},
1699 bool emitAsTwoStateType =
false) {
1700 return TypeSwitch<Type, bool>(type)
1701 .Case<IntegerType>([&](IntegerType integerType) {
1702 if (emitAsTwoStateType && dims.empty()) {
1704 if (!typeName.empty()) {
1709 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1711 getInt32Attr(type.getContext(), integerType.getWidth()));
1713 StringRef typeName =
1714 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1715 if (!typeName.empty()) {
1722 return !dims.empty() || !implicitIntType;
1724 .Case<IntType>([&](IntType intType) {
1725 if (!implicitIntType)
1727 dims.push_back(intType.getWidth());
1731 .Case<ArrayType>([&](ArrayType arrayType) {
1732 dims.push_back(arrayType.getSizeAttr());
1734 implicitIntType, singleBitDefaultType,
1736 emitAsTwoStateType);
1738 .Case<InOutType>([&](InOutType inoutType) {
1740 implicitIntType, singleBitDefaultType,
1742 emitAsTwoStateType);
1744 .Case<EnumType>([&](EnumType enumType) {
1746 if (enumType.getBitWidth() != 32)
1747 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1749 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1750 llvm::interleaveComma(
1751 enumType.getFields().getAsRange<StringAttr>(), os,
1752 [&](
auto enumerator) {
1753 os << emitter.fieldNameResolver.getEnumFieldName(
1754 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1759 .Case<StructType>([&](StructType structType) {
1760 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1761 os <<
"/*Zero Width*/";
1764 os <<
"struct packed {";
1765 for (
auto &element : structType.getElements()) {
1767 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1768 <<
": Zero Width;*/ ";
1771 SmallVector<Attribute, 8> structDims;
1776 {}, emitAsTwoStateType);
1777 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1778 emitter.printUnpackedTypePostfix(element.type, os);
1785 .Case<UnionType>([&](UnionType unionType) {
1786 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1787 os <<
"/*Zero Width*/";
1791 int64_t unionWidth = hw::getBitWidth(unionType);
1792 os <<
"union packed {";
1793 for (
auto &element : unionType.getElements()) {
1795 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1796 <<
": Zero Width;*/ ";
1799 int64_t elementWidth = hw::getBitWidth(element.type);
1800 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1802 os <<
" struct packed {";
1803 if (element.offset) {
1804 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1805 << element.offset - 1 <<
":0] "
1806 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1810 SmallVector<Attribute, 8> structDims;
1814 true, emitter, {}, emitAsTwoStateType);
1815 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1816 emitter.printUnpackedTypePostfix(element.type, os);
1820 if (elementWidth + (int64_t)element.offset < unionWidth) {
1821 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1822 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1823 <<
"__post_padding_" << element.name.getValue() <<
";";
1825 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1834 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1835 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1836 os <<
"<<unexpected unpacked array>>";
1837 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1841 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1842 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1844 mlir::emitError(loc,
"unresolvable type reference");
1847 if (typedecl.getType() != typeRef.getInnerType()) {
1848 mlir::emitError(loc,
"declared type did not match aliased type");
1852 os << typedecl.getPreferredName();
1853 emitDims(dims, os, typedecl->getLoc(), emitter);
1856 .Default([&](Type type) {
1857 os <<
"<<invalid type '" << type <<
"'>>";
1858 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1874bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1875 Type optionalAliasType,
1876 bool implicitIntType,
1877 bool singleBitDefaultType,
1878 bool emitAsTwoStateType) {
1879 SmallVector<Attribute, 8> packedDimensions;
1881 singleBitDefaultType, *
this, optionalAliasType,
1882 emitAsTwoStateType);
1888void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1889 TypeSwitch<Type, void>(type)
1891 printUnpackedTypePostfix(inoutType.getElementType(), os);
1893 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1894 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1895 : state.designOp->getLoc();
1896 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1898 printUnpackedTypePostfix(arrayType.getElementType(), os);
1900 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1902 printUnpackedTypePostfix(arrayType.getElementType(), os);
1904 .Case<InterfaceType>([&](
auto) {
1918ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1919 function_ref<InFlightDiagnostic()> emitError) {
1920 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1928ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1929 VerilogPrecedence parenthesizeIfLooserThan,
1930 function_ref<InFlightDiagnostic()> emitError) {
1931 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1932 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1933 APInt value = intAttr.getValue();
1937 if (intTy.getWidth() > 32) {
1939 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1943 if (intTy.isSigned())
1944 os << intTy.getWidth() <<
"'sd";
1946 os << intTy.getWidth() <<
"'d";
1948 value.print(os, intTy.isSigned());
1949 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1951 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1953 os.write_escaped(strAttr.getValue());
1955 return {Symbol, IsUnsigned};
1957 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1959 os << fpAttr.getValueAsDouble();
1960 return {Symbol, IsUnsigned};
1962 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1963 os << verbatimParam.getValue().getValue();
1964 return {Symbol, IsUnsigned};
1966 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1968 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1969 parameterRef.getName());
1972 return {Symbol, IsUnsigned};
1976 auto expr = dyn_cast<ParamExprAttr>(value);
1978 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1979 emitError() <<
" = " << value;
1980 return {LowestPrecedence, IsUnsigned};
1983 StringRef operatorStr;
1984 StringRef openStr, closeStr;
1985 VerilogPrecedence subprecedence = LowestPrecedence;
1986 VerilogPrecedence prec;
1987 std::optional<SubExprSignResult> operandSign;
1988 bool isUnary =
false;
1989 bool hasOpenClose =
false;
1991 switch (expr.getOpcode()) {
1993 operatorStr =
" + ";
1994 subprecedence = Addition;
1997 operatorStr =
" * ";
1998 subprecedence = Multiply;
2001 operatorStr =
" & ";
2002 subprecedence = And;
2005 operatorStr =
" | ";
2009 operatorStr =
" ^ ";
2010 subprecedence = Xor;
2013 operatorStr =
" << ";
2014 subprecedence = Shift;
2018 operatorStr =
" >> ";
2019 subprecedence = Shift;
2023 operatorStr =
" >>> ";
2024 subprecedence = Shift;
2025 operandSign = IsSigned;
2028 operatorStr =
" / ";
2029 subprecedence = Multiply;
2030 operandSign = IsUnsigned;
2033 operatorStr =
" / ";
2034 subprecedence = Multiply;
2035 operandSign = IsSigned;
2038 operatorStr =
" % ";
2039 subprecedence = Multiply;
2040 operandSign = IsUnsigned;
2043 operatorStr =
" % ";
2044 subprecedence = Multiply;
2045 operandSign = IsSigned;
2048 openStr =
"$clog2(";
2050 operandSign = IsUnsigned;
2051 hasOpenClose =
true;
2054 case PEO::StrConcat:
2057 hasOpenClose =
true;
2060 subprecedence = LowestPrecedence;
2065 prec = subprecedence;
2068 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2070 assert(isUnary || hasOpenClose ||
2071 !llvm::hasSingleElement(expr.getOperands()));
2078 auto emitOperand = [&](Attribute operand) ->
bool {
2080 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2081 if (operandSign.has_value())
2082 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2085 if (operandSign.has_value()) {
2087 signedness = *operandSign;
2089 return signedness == IsSigned;
2093 if (prec > parenthesizeIfLooserThan)
2102 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2103 for (
auto op : expr.getOperands().drop_front()) {
2106 if (expr.getOpcode() == PEO::Add) {
2107 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2108 const APInt &value = integer.getValue();
2109 if (value.isNegative() && !value.isMinSignedValue()) {
2111 allOperandsSigned &=
2112 emitOperand(IntegerAttr::get(op.getType(), -value));
2119 allOperandsSigned &= emitOperand(op);
2123 if (prec > parenthesizeIfLooserThan) {
2127 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2142class ExprEmitter :
public EmitterBase,
2144 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2149 ExprEmitter(ModuleEmitter &emitter,
2150 SmallPtrSetImpl<Operation *> &emittedExprs)
2151 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2153 ExprEmitter(ModuleEmitter &emitter,
2154 SmallPtrSetImpl<Operation *> &emittedExprs,
2156 : EmitterBase(emitter.state), emitter(emitter),
2157 emittedExprs(emittedExprs), buffer(tokens),
2158 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2159 assert(state.pp.getListener() == &state.saver);
2166 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2167 bool isAssignmentLikeContext) {
2168 assert(localTokens.empty());
2170 ps.scopedBox(PP::ibox0, [&]() {
2173 emitSubExpr(exp, parenthesizeIfLooserThan,
2175 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2177 isAssignmentLikeContext);
2182 if (&buffer.tokens == &localTokens)
2183 buffer.flush(state.pp);
2188 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2189 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2191 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2199 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2200 SubExprSignRequirement signReq = NoRequirement,
2201 bool isSelfDeterminedUnsignedValue =
false,
2202 bool isAssignmentLikeContext =
false);
2206 void emitSVAttributes(Operation *op);
2208 SubExprInfo visitUnhandledExpr(Operation *op);
2209 SubExprInfo visitInvalidComb(Operation *op) {
2212 SubExprInfo visitUnhandledComb(Operation *op) {
2213 return visitUnhandledExpr(op);
2216 return dispatchSVVisitor(op);
2219 return visitUnhandledExpr(op);
2221 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2224 enum EmitBinaryFlags {
2225 EB_RequireSignedOperands = RequireSigned,
2226 EB_RequireUnsignedOperands = RequireUnsigned,
2227 EB_OperandSignRequirementMask = 0x3,
2232 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2236 EB_ForceResultSigned = 0x8,
2241 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2242 const char *syntax,
unsigned emitBinaryFlags = 0);
2244 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2245 bool resultAlwaysUnsigned =
false);
2248 void emitSubExprIBox2(
2249 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2250 ps.scopedBox(PP::ibox2,
2251 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2256 template <
typename Container,
typename EachFn>
2257 void interleaveComma(
const Container &c, EachFn eachFn) {
2258 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2263 void interleaveComma(ValueRange ops) {
2264 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2281 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2283 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2284 CloseFunc closeFn) {
2286 ps.scopedBox(PP::cbox0, [&]() {
2287 interleaveComma(c, eachFn);
2293 template <
typename OpenFunc,
typename CloseFunc>
2294 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2295 return emitBracedList(
2296 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2300 void emitBracedList(ValueRange ops) {
2301 return emitBracedList(
2302 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2306 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2309 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2310 bool printAsPattern, Operation *op);
2312 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2313 ArrayAttr fieldValues,
bool printAsPattern,
2316 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2318 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2319 SubExprInfo visitSV(GetModportOp op);
2320 SubExprInfo visitSV(SystemFunctionOp op);
2321 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2322 SubExprInfo visitSV(XMROp op);
2323 SubExprInfo visitSV(SFormatFOp op);
2324 SubExprInfo visitSV(XMRRefOp op);
2325 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2326 SubExprInfo visitSV(VerbatimExprOp op) {
2327 return visitVerbatimExprOp(op, op.getSymbols());
2329 SubExprInfo visitSV(VerbatimExprSEOp op) {
2330 return visitVerbatimExprOp(op, op.getSymbols());
2332 SubExprInfo visitSV(MacroRefExprOp op);
2333 SubExprInfo visitSV(MacroRefExprSEOp op);
2334 template <
typename MacroTy>
2335 SubExprInfo emitMacroCall(MacroTy op);
2337 SubExprInfo visitSV(ConstantXOp op);
2338 SubExprInfo visitSV(ConstantZOp op);
2339 SubExprInfo visitSV(ConstantStrOp op);
2341 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2342 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2344 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2349 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2350 emitSVAttributes(op);
2353 SubExprInfo visitSV(ArrayIndexInOutOp op);
2354 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2355 SubExprInfo visitSV(IndexedPartSelectOp op);
2356 SubExprInfo visitSV(StructFieldInOutOp op);
2359 SubExprInfo visitSV(SampledOp op);
2362 SubExprInfo visitSV(TimeOp op);
2363 SubExprInfo visitSV(STimeOp op);
2366 using TypeOpVisitor::visitTypeOp;
2368 SubExprInfo visitTypeOp(AggregateConstantOp op);
2370 SubExprInfo visitTypeOp(ParamValueOp op);
2377 SubExprInfo visitTypeOp(StructInjectOp op);
2378 SubExprInfo visitTypeOp(UnionCreateOp op);
2379 SubExprInfo visitTypeOp(UnionExtractOp op);
2380 SubExprInfo visitTypeOp(EnumCmpOp op);
2381 SubExprInfo visitTypeOp(EnumConstantOp op);
2384 using CombinationalVisitor::visitComb;
2385 SubExprInfo visitComb(
MuxOp op);
2386 SubExprInfo visitComb(ReverseOp op);
2387 SubExprInfo visitComb(
AddOp op) {
2388 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2389 return emitBinary(op, Addition,
"+");
2391 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2392 SubExprInfo visitComb(
MulOp op) {
2393 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2394 return emitBinary(op, Multiply,
"*");
2396 SubExprInfo visitComb(
DivUOp op) {
2397 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2399 SubExprInfo visitComb(
DivSOp op) {
2400 return emitBinary(op, Multiply,
"/",
2401 EB_RequireSignedOperands | EB_ForceResultSigned);
2403 SubExprInfo visitComb(
ModUOp op) {
2404 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2406 SubExprInfo visitComb(
ModSOp op) {
2407 return emitBinary(op, Multiply,
"%",
2408 EB_RequireSignedOperands | EB_ForceResultSigned);
2410 SubExprInfo visitComb(
ShlOp op) {
2411 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2413 SubExprInfo visitComb(
ShrUOp op) {
2415 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2417 SubExprInfo visitComb(
ShrSOp op) {
2420 return emitBinary(op, Shift,
">>>",
2421 EB_RequireSignedOperands | EB_ForceResultSigned |
2422 EB_RHS_UnsignedWithSelfDeterminedWidth);
2424 SubExprInfo visitComb(
AndOp op) {
2425 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2426 return emitBinary(op, And,
"&");
2428 SubExprInfo visitComb(
OrOp op) {
2429 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2430 return emitBinary(op, Or,
"|");
2432 SubExprInfo visitComb(
XorOp op) {
2433 if (op.isBinaryNot())
2434 return emitUnary(op,
"~");
2435 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2436 return emitBinary(op, Xor,
"^");
2441 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2443 SubExprInfo visitComb(ReplicateOp op);
2444 SubExprInfo visitComb(
ConcatOp op);
2446 SubExprInfo visitComb(ICmpOp op);
2448 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2449 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2450 "not printed within an assignment-like context");
2451 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2452 "supposed to spill such expressions";
2456 SubExprInfo printStructCreate(
2457 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2459 bool printAsPattern, Operation *op);
2462 ModuleEmitter &emitter;
2469 SubExprSignRequirement signPreference = NoRequirement;
2473 SmallPtrSetImpl<Operation *> &emittedExprs;
2476 SmallVector<Token> localTokens;
2490 bool isAssignmentLikeContext =
false;
2494SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2496 unsigned emitBinaryFlags) {
2498 emitError(op,
"SV attributes emission is unimplemented for the op");
2509 if (emitBinaryFlags & EB_ForceResultSigned)
2510 ps <<
"$signed(" << PP::ibox0;
2511 auto operandSignReq =
2512 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2513 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2515 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2517 ps << lhsSpace << syntax << PP::nbsp;
2524 auto rhsPrec = prec;
2525 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2526 rhsPrec = VerilogPrecedence(prec - 1);
2531 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2532 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2533 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2534 operandSignReq = NoRequirement;
2537 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2538 rhsIsUnsignedValueWithSelfDeterminedWidth);
2542 SubExprSignResult signedness = IsUnsigned;
2543 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2544 signedness = IsSigned;
2546 if (emitBinaryFlags & EB_ForceResultSigned) {
2547 ps << PP::end <<
")";
2548 signedness = IsSigned;
2552 return {prec, signedness};
2555SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2556 bool resultAlwaysUnsigned) {
2558 emitError(op,
"SV attributes emission is unimplemented for the op");
2561 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2565 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2566 resultAlwaysUnsigned ? IsUnsigned : signedness};
2571void ExprEmitter::emitSVAttributes(Operation *op) {
2590 if (constant && constant.getValue().isZero())
2591 return concat.getOperand(1);
2601SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2602 VerilogPrecedence parenthesizeIfLooserThan,
2603 SubExprSignRequirement signRequirement,
2604 bool isSelfDeterminedUnsignedValue,
2605 bool isAssignmentLikeContext) {
2607 if (
auto result = dyn_cast<OpResult>(exp))
2608 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2609 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2610 parenthesizeIfLooserThan, signRequirement,
2611 isSelfDeterminedUnsignedValue,
2612 isAssignmentLikeContext);
2616 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2621 auto *op = exp.getDefiningOp();
2625 if (!shouldEmitInlineExpr) {
2628 if (signRequirement == RequireSigned) {
2630 return {Symbol, IsSigned};
2634 return {Symbol, IsUnsigned};
2637 unsigned subExprStartIndex = buffer.tokens.size();
2639 ps.addCallback({op,
true});
2640 auto done = llvm::make_scope_exit([&]() {
2642 ps.addCallback({op, false});
2648 signPreference = signRequirement;
2650 bool bitCastAdded =
false;
2651 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2653 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2654 ps.addAsString(inType.getWidth());
2655 ps <<
"'(" << PP::ibox0;
2656 bitCastAdded =
true;
2660 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2661 isAssignmentLikeContext);
2662 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2668 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2670 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2672 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2673 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2676 expInfo.signedness = IsSigned;
2677 expInfo.precedence = Selection;
2678 }
else if (signRequirement == RequireUnsigned &&
2679 expInfo.signedness == IsSigned) {
2682 expInfo.signedness = IsUnsigned;
2683 expInfo.precedence = Selection;
2684 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2691 expInfo.precedence = Selection;
2698 emittedExprs.insert(exp.getDefiningOp());
2702SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2703 auto openFn = [&]() {
2705 ps.addAsString(op.getMultiple());
2708 auto closeFn = [&]() { ps <<
"}}"; };
2712 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2713 if (op.getOperand().hasOneUse()) {
2714 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2715 return {Symbol, IsUnsigned};
2718 emitBracedList(op.getOperand(), openFn, closeFn);
2719 return {Symbol, IsUnsigned};
2722SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2723 emitBracedList(op.getOperands());
2724 return {Symbol, IsUnsigned};
2727SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2731 Type toType = op.getType();
2734 ps.invokeWithStringOS(
2735 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2738 return emitSubExpr(op.getInput(), LowestPrecedence);
2741SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2742 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2743 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2744 SubExprSignRequirement signop[] = {
2746 NoRequirement, NoRequirement,
2748 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2750 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2752 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2754 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2755 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2758 if (op.isEqualAllOnes())
2759 return emitUnary(op,
"&",
true);
2762 if (op.isNotEqualZero())
2763 return emitUnary(op,
"|",
true);
2765 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2769 result.signedness = IsUnsigned;
2773SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2775 emitError(op,
"SV attributes emission is unimplemented for the op");
2777 unsigned loBit = op.getLowBit();
2778 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2780 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2781 assert((x.precedence == Symbol ||
2783 "should be handled by isExpressionUnableToInline");
2788 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2792 ps.addAsString(hiBit);
2793 if (hiBit != loBit) {
2795 ps.addAsString(loBit);
2798 return {Unary, IsUnsigned};
2801SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2803 emitError(op,
"SV attributes emission is unimplemented for the op");
2805 auto decl = op.getReferencedDecl(state.symbolCache);
2808 return {Selection, IsUnsigned};
2811SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2813 emitError(op,
"SV attributes emission is unimplemented for the op");
2816 ps.scopedBox(PP::ibox0, [&]() {
2818 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2819 [&]() { ps <<
"," << PP::space; });
2822 return {Symbol, IsUnsigned};
2825SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2827 emitError(op,
"SV attributes emission is unimplemented for the op");
2829 auto decl = op.getReferencedDecl(state.symbolCache);
2833 return {Selection, IsUnsigned};
2836SubExprInfo ExprEmitter::visitSV(XMROp op) {
2838 emitError(op,
"SV attributes emission is unimplemented for the op");
2840 if (op.getIsRooted())
2842 for (
auto s : op.getPath())
2843 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2845 return {Selection, IsUnsigned};
2850SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2852 emitError(op,
"SV attributes emission is unimplemented for the op");
2855 auto globalRef = op.getReferencedPath(&state.symbolCache);
2856 auto namepath = globalRef.getNamepathAttr().getValue();
2857 auto *
module = state.symbolCache.getDefinition(
2858 cast<InnerRefAttr>(namepath.front()).getModule());
2860 for (
auto sym : namepath) {
2862 auto innerRef = cast<InnerRefAttr>(sym);
2863 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2864 innerRef.getName());
2865 if (ref.hasPort()) {
2871 auto leaf = op.getVerbatimSuffixAttr();
2872 if (leaf && leaf.size())
2874 return {Selection, IsUnsigned};
2877SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2879 emitError(op,
"SV attributes emission is unimplemented for the op");
2881 emitTextWithSubstitutions(
2882 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2883 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2885 return {Unary, IsUnsigned};
2888template <
typename MacroTy>
2889SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2891 emitError(op,
"SV attributes emission is unimplemented for the op");
2894 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2895 assert(macroOp &&
"Invalid IR");
2897 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2899 if (!op.getInputs().empty()) {
2901 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2902 emitExpression(val, LowestPrecedence, false);
2906 return {LowestPrecedence, IsUnsigned};
2909SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2910 return emitMacroCall(op);
2913SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2914 return emitMacroCall(op);
2917SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2919 emitError(op,
"SV attributes emission is unimplemented for the op");
2921 ps.addAsString(op.getWidth());
2923 return {Unary, IsUnsigned};
2926SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2928 emitError(op,
"SV attributes emission is unimplemented for the op");
2930 ps.writeQuotedEscaped(op.getStr());
2931 return {Symbol, IsUnsigned};
2934SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2936 emitError(op,
"SV attributes emission is unimplemented for the op");
2938 ps.addAsString(op.getWidth());
2940 return {Unary, IsUnsigned};
2943SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2944 bool isNegated =
false;
2947 if (signPreference == RequireSigned && value.isNegative() &&
2948 !value.isMinSignedValue()) {
2953 ps.addAsString(type.getWidth());
2957 if (signPreference == RequireSigned)
2963 SmallString<32> valueStr;
2965 (-value).toStringUnsigned(valueStr, 16);
2967 value.toStringUnsigned(valueStr, 16);
2970 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2973SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2975 emitError(op,
"SV attributes emission is unimplemented for the op");
2977 auto value = op.getValue();
2981 if (value.getBitWidth() == 0) {
2982 emitOpError(op,
"will not emit zero width constants in the general case");
2983 ps <<
"<<unsupported zero width constant: "
2984 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2985 return {Unary, IsUnsigned};
2988 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2991void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2992 bool printAsPattern, Operation *op) {
2993 if (printAsPattern && !isAssignmentLikeContext)
2994 emitAssignmentPatternContextError(op);
2995 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2998 elementValues, [&]() { ps << openDelim; },
2999 [&](Attribute elementValue) {
3000 printConstantAggregate(elementValue,
elementType, op);
3002 [&]() { ps <<
"}"; });
3005void ExprEmitter::printConstantStruct(
3006 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3007 bool printAsPattern, Operation *op) {
3008 if (printAsPattern && !isAssignmentLikeContext)
3009 emitAssignmentPatternContextError(op);
3016 auto fieldRange = llvm::make_filter_range(
3017 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3022 if (printAsPattern) {
3024 fieldRange, [&]() { ps <<
"'{"; },
3025 [&](
const auto &fieldAndValue) {
3026 ps.scopedBox(PP::ibox2, [&]() {
3027 const auto &[field, value] = fieldAndValue;
3028 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3029 <<
":" << PP::space;
3030 printConstantAggregate(value, field.type, op);
3033 [&]() { ps <<
"}"; });
3036 fieldRange, [&]() { ps <<
"{"; },
3037 [&](
const auto &fieldAndValue) {
3038 ps.scopedBox(PP::ibox2, [&]() {
3039 const auto &[field, value] = fieldAndValue;
3040 printConstantAggregate(value, field.type, op);
3043 [&]() { ps <<
"}"; });
3047void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3050 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3051 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3052 isAssignmentLikeContext, op);
3055 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3056 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3060 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3061 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3062 isAssignmentLikeContext, op);
3064 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3065 auto value = cast<IntegerAttr>(attr).getValue();
3066 printConstantScalar(value, intType);
3070 emitOpError(op,
"contains constant of type ")
3071 << type <<
" which cannot be emitted as Verilog";
3074SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3076 emitError(op,
"SV attributes emission is unimplemented for the op");
3080 "zero-bit types not allowed at this point");
3082 printConstantAggregate(op.getFields(), op.getType(), op);
3083 return {Symbol, IsUnsigned};
3086SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3088 emitError(op,
"SV attributes emission is unimplemented for the op");
3090 return ps.invokeWithStringOS([&](
auto &os) {
3091 return emitter.printParamValue(op.getValue(), os, [&]() {
3092 return op->emitOpError(
"invalid parameter use");
3101 emitError(op,
"SV attributes emission is unimplemented for the op");
3103 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3105 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3107 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3109 ps.addAsString(dstWidth);
3111 return {Selection, arrayPrec.signedness};
3114SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3115 emitSubExpr(op.getInput(), Selection);
3120 emitSubExpr(op.getIndex(), LowestPrecedence);
3122 emitSVAttributes(op);
3123 return {Selection, IsUnsigned};
3129 emitError(op,
"SV attributes emission is unimplemented for the op");
3131 if (op.isUniform()) {
3133 ps.addAsString(op.getInputs().size());
3135 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3139 op.getInputs(), [&]() { ps <<
"{"; },
3142 emitSubExprIBox2(v);
3145 [&]() { ps <<
"}"; });
3147 return {Unary, IsUnsigned};
3150SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3152 emitError(op,
"SV attributes emission is unimplemented for the op");
3155 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3156 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3157 return {Unary, IsUnsigned};
3162 emitError(op,
"SV attributes emission is unimplemented for the op");
3164 emitBracedList(op.getOperands());
3165 return {Unary, IsUnsigned};
3168SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3170 emitError(op,
"SV attributes emission is unimplemented for the op");
3172 auto index = op.getIndex();
3173 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3178 emitSubExpr(index, LowestPrecedence);
3180 return {Selection, arrayPrec.signedness};
3183SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3185 emitError(op,
"SV attributes emission is unimplemented for the op");
3187 auto prec = emitSubExpr(op.getInput(), Selection);
3189 emitSubExpr(op.getBase(), LowestPrecedence);
3190 if (op.getDecrement())
3194 ps.addAsString(op.getWidth());
3196 return {Selection, prec.signedness};
3199SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3201 emitError(op,
"SV attributes emission is unimplemented for the op");
3203 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3205 emitSubExpr(op.getBase(), LowestPrecedence);
3206 if (op.getDecrement())
3210 ps.addAsString(op.getWidth());
3215SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3217 emitError(op,
"SV attributes emission is unimplemented for the op");
3219 auto prec = emitSubExpr(op.getInput(), Selection);
3221 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3222 return {Selection, prec.signedness};
3225SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3227 emitError(op,
"SV attributes emission is unimplemented for the op");
3230 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3235SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3237 emitError(op,
"SV attributes emission is unimplemented for the op");
3240 ps.scopedBox(PP::ibox0, [&]() {
3241 ps.writeQuotedEscaped(op.getFormatString());
3248 for (
auto operand : op.getSubstitutions()) {
3249 ps <<
"," << PP::space;
3250 emitSubExpr(operand, LowestPrecedence);
3254 return {Symbol, IsUnsigned};
3257SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3259 emitError(op,
"SV attributes emission is unimplemented for the op");
3262 return {Symbol, IsUnsigned};
3265SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3267 emitError(op,
"SV attributes emission is unimplemented for the op");
3270 return {Symbol, IsUnsigned};
3273SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3287 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3288 ps.scopedBox(PP::ibox0, [&]() {
3289 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3293 emitSVAttributes(op);
3295 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3296 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3300 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3301 return emitSubExpr(op.getFalseValue(), Conditional);
3304 SubExprSignResult signedness = IsUnsigned;
3305 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3306 signedness = IsSigned;
3308 return {Conditional, signedness};
3312SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3314 emitError(op,
"SV attributes emission is unimplemented for the op");
3317 emitSubExpr(op.getInput(), LowestPrecedence);
3320 return {Symbol, IsUnsigned};
3323SubExprInfo ExprEmitter::printStructCreate(
3324 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3326 bool printAsPattern, Operation *op) {
3327 if (printAsPattern && !isAssignmentLikeContext)
3328 emitAssignmentPatternContextError(op);
3331 auto filteredFields = llvm::make_filter_range(
3332 llvm::enumerate(fieldInfos),
3333 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3335 if (printAsPattern) {
3337 filteredFields, [&]() { ps <<
"'{"; },
3338 [&](
const auto &field) {
3339 ps.scopedBox(PP::ibox2, [&]() {
3341 emitter.getVerilogStructFieldName(field.value().name))
3342 <<
":" << PP::space;
3343 fieldFn(field.value(), field.index());
3346 [&]() { ps <<
"}"; });
3349 filteredFields, [&]() { ps <<
"{"; },
3350 [&](
const auto &field) {
3351 ps.scopedBox(PP::ibox2,
3352 [&]() { fieldFn(field.value(), field.index()); });
3354 [&]() { ps <<
"}"; });
3357 return {Selection, IsUnsigned};
3362 emitError(op,
"SV attributes emission is unimplemented for the op");
3366 bool printAsPattern = isAssignmentLikeContext;
3367 StructType structType = op.getType();
3368 return printStructCreate(
3369 structType.getElements(),
3370 [&](
const auto &field,
auto index) {
3371 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3373 isAssignmentLikeContext);
3375 printAsPattern, op);
3380 emitError(op,
"SV attributes emission is unimplemented for the op");
3382 emitSubExpr(op.getInput(), Selection);
3384 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3385 return {Selection, IsUnsigned};
3388SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3390 emitError(op,
"SV attributes emission is unimplemented for the op");
3394 bool printAsPattern = isAssignmentLikeContext;
3395 StructType structType = op.getType();
3396 return printStructCreate(
3397 structType.getElements(),
3398 [&](
const auto &field,
auto index) {
3399 if (field.name == op.getFieldNameAttr()) {
3400 emitSubExpr(op.getNewValue(), Selection);
3402 emitSubExpr(op.getInput(), Selection);
3404 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3407 printAsPattern, op);
3410SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3411 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3412 return {Selection, IsUnsigned};
3415SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3417 emitError(op,
"SV attributes emission is unimplemented for the op");
3418 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3421 result.signedness = IsUnsigned;
3425SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3427 emitError(op,
"SV attributes emission is unimplemented for the op");
3431 auto unionWidth = hw::getBitWidth(unionType);
3432 auto &element = unionType.getElements()[op.getFieldIndex()];
3433 auto elementWidth = hw::getBitWidth(element.type);
3436 if (!elementWidth) {
3437 ps.addAsString(unionWidth);
3439 return {Unary, IsUnsigned};
3443 if (elementWidth == unionWidth) {
3444 emitSubExpr(op.getInput(), LowestPrecedence);
3445 return {Unary, IsUnsigned};
3450 ps.scopedBox(PP::ibox0, [&]() {
3451 if (
auto prePadding = element.offset) {
3452 ps.addAsString(prePadding);
3453 ps <<
"'h0," << PP::space;
3455 emitSubExpr(op.getInput(), Selection);
3456 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3457 ps <<
"," << PP::space;
3458 ps.addAsString(postPadding);
3464 return {Unary, IsUnsigned};
3467SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3469 emitError(op,
"SV attributes emission is unimplemented for the op");
3470 emitSubExpr(op.getInput(), Selection);
3473 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3474 auto unionWidth = hw::getBitWidth(unionType);
3475 auto &element = unionType.getElements()[op.getFieldIndex()];
3476 auto elementWidth = hw::getBitWidth(element.type);
3477 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3478 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3487 return {Selection, IsUnsigned};
3490SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3491 emitOpError(op,
"cannot emit this expression to Verilog");
3492 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3494 return {Symbol, IsUnsigned};
3510enum class PropertyPrecedence {
3530struct EmittedProperty {
3532 PropertyPrecedence precedence;
3537class PropertyEmitter :
public EmitterBase,
3538 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3542 PropertyEmitter(ModuleEmitter &emitter,
3543 SmallPtrSetImpl<Operation *> &emittedOps)
3544 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3545 PropertyEmitter(ModuleEmitter &emitter,
3546 SmallPtrSetImpl<Operation *> &emittedOps,
3548 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3550 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3551 assert(state.pp.getListener() == &state.saver);
3554 void emitAssertPropertyDisable(
3555 Value property, Value disable,
3556 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3558 void emitAssertPropertyBody(
3559 Value property, Value disable,
3560 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3562 void emitAssertPropertyBody(
3563 Value property, sv::EventControl event, Value clock, Value disable,
3564 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3569 emitNestedProperty(Value property,
3570 PropertyPrecedence parenthesizeIfLooserThan);
3571 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3572 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3574 EmittedProperty visitUnhandledLTL(Operation *op);
3575 EmittedProperty visitLTL(ltl::AndOp op);
3576 EmittedProperty visitLTL(ltl::OrOp op);
3577 EmittedProperty visitLTL(ltl::IntersectOp op);
3578 EmittedProperty visitLTL(ltl::DelayOp op);
3579 EmittedProperty visitLTL(ltl::ConcatOp op);
3580 EmittedProperty visitLTL(ltl::RepeatOp op);
3581 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3582 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3583 EmittedProperty visitLTL(ltl::NotOp op);
3584 EmittedProperty visitLTL(ltl::ImplicationOp op);
3585 EmittedProperty visitLTL(ltl::UntilOp op);
3586 EmittedProperty visitLTL(ltl::EventuallyOp op);
3587 EmittedProperty visitLTL(ltl::ClockOp op);
3589 void emitLTLConcat(ValueRange inputs);
3592 ModuleEmitter &emitter;
3597 SmallPtrSetImpl<Operation *> &emittedOps;
3600 SmallVector<Token> localTokens;
3613void PropertyEmitter::emitAssertPropertyDisable(
3614 Value property, Value disable,
3615 PropertyPrecedence parenthesizeIfLooserThan) {
3618 ps <<
"disable iff" << PP::nbsp <<
"(";
3620 emitNestedProperty(disable, PropertyPrecedence::Unary);
3626 ps.scopedBox(PP::ibox0,
3627 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3633void PropertyEmitter::emitAssertPropertyBody(
3634 Value property, Value disable,
3635 PropertyPrecedence parenthesizeIfLooserThan) {
3636 assert(localTokens.empty());
3638 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3643 if (&buffer.tokens == &localTokens)
3644 buffer.flush(state.pp);
3647void PropertyEmitter::emitAssertPropertyBody(
3648 Value property, sv::EventControl event, Value clock, Value disable,
3649 PropertyPrecedence parenthesizeIfLooserThan) {
3650 assert(localTokens.empty());
3653 ps.scopedBox(PP::ibox2, [&] {
3654 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3655 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3661 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3666 if (&buffer.tokens == &localTokens)
3667 buffer.flush(state.pp);
3670EmittedProperty PropertyEmitter::emitNestedProperty(
3671 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3681 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3682 ExprEmitter(emitter, emittedOps, buffer.tokens)
3683 .emitExpression(property, LowestPrecedence,
3685 return {PropertyPrecedence::Symbol};
3688 unsigned startIndex = buffer.tokens.size();
3689 auto info = dispatchLTLVisitor(property.getDefiningOp());
3694 if (
info.precedence > parenthesizeIfLooserThan) {
3696 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3697 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3699 ps << PP::end <<
")";
3701 info.precedence = PropertyPrecedence::Symbol;
3705 emittedOps.insert(property.getDefiningOp());
3709EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3710 emitOpError(op,
"emission as Verilog property or sequence not supported");
3711 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3712 return {PropertyPrecedence::Symbol};
3715EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3718 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3719 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3720 return {PropertyPrecedence::And};
3723EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3726 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3727 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3728 return {PropertyPrecedence::Or};
3731EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3735 emitNestedProperty(input, PropertyPrecedence::Intersect);
3737 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3738 return {PropertyPrecedence::Intersect};
3741EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3743 if (
auto length = op.getLength()) {
3745 ps.addAsString(op.getDelay());
3748 ps.addAsString(op.getDelay());
3750 ps.addAsString(op.getDelay() + *length);
3754 if (op.getDelay() == 0) {
3756 }
else if (op.getDelay() == 1) {
3760 ps.addAsString(op.getDelay());
3765 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3766 return {PropertyPrecedence::Concat};
3769void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3770 bool addSeparator =
false;
3771 for (
auto input : inputs) {
3774 if (!input.getDefiningOp<ltl::DelayOp>())
3775 ps <<
"##0" << PP::space;
3777 addSeparator =
true;
3778 emitNestedProperty(input, PropertyPrecedence::Concat);
3782EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3783 emitLTLConcat(op.getInputs());
3784 return {PropertyPrecedence::Concat};
3787EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3788 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3789 if (
auto more = op.getMore()) {
3791 ps.addAsString(op.getBase());
3794 ps.addAsString(op.getBase() + *more);
3798 if (op.getBase() == 0) {
3800 }
else if (op.getBase() == 1) {
3804 ps.addAsString(op.getBase());
3808 return {PropertyPrecedence::Repeat};
3811EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3812 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3814 auto more = op.getMore();
3816 ps.addAsString(op.getBase());
3819 ps.addAsString(op.getBase() + more);
3823 return {PropertyPrecedence::Repeat};
3826EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3827 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3829 auto more = op.getMore();
3831 ps.addAsString(op.getBase());
3834 ps.addAsString(op.getBase() + more);
3838 return {PropertyPrecedence::Repeat};
3841EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3842 ps <<
"not" << PP::space;
3843 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3844 return {PropertyPrecedence::Unary};
3850 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3851 if (!concatOp || concatOp.getInputs().size() < 2)
3853 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3854 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3856 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3857 if (!constOp || !constOp.getValue().isOne())
3859 return concatOp.getInputs().drop_back();
3862EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3866 emitLTLConcat(range);
3867 ps << PP::space <<
"|=>" << PP::nbsp;
3869 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3870 ps << PP::space <<
"|->" << PP::nbsp;
3872 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3873 return {PropertyPrecedence::Implication};
3876EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3877 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3878 ps << PP::space <<
"until" << PP::space;
3879 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3880 return {PropertyPrecedence::Until};
3883EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3884 ps <<
"s_eventually" << PP::space;
3885 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3886 return {PropertyPrecedence::Qualifier};
3889EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3891 ps.scopedBox(PP::ibox2, [&] {
3892 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3893 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3897 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3898 return {PropertyPrecedence::Clocking};
3908class NameCollector {
3910 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3914 void collectNames(Block &block);
3916 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3917 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3920 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3921 ModuleEmitter &moduleEmitter;
3926 static constexpr size_t maxTypeWidthBound = 32;
3931void NameCollector::collectNames(Block &block) {
3934 for (
auto &op : block) {
3938 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3939 FuncCallProceduralOp, FuncCallOp>(op))
3941 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3945 for (
auto result : op.getResults()) {
3947 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3948 SmallString<16> typeString;
3952 llvm::raw_svector_ostream stringStream(typeString);
3954 stringStream, op.getLoc());
3956 if (typeString.size() <= maxTypeWidthBound)
3957 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3964 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3965 for (
auto ®ion : op.getRegions()) {
3966 if (!region.empty())
3967 collectNames(region.front());
3981class StmtEmitter :
public EmitterBase,
3989 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3991 void emitStatement(Operation *op);
3992 void emitStatementBlock(Block &body);
3995 LogicalResult emitDeclaration(Operation *op);
3998 void collectNamesAndCalculateDeclarationWidths(Block &block);
4001 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4002 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4003 bool isAssignmentLikeContext =
false);
4004 void emitSVAttributes(Operation *op);
4007 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4010 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4014 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4015 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4016 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4017 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4018 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4019 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4021 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4022 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4023 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4024 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4025 template <
typename Op>
4028 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4029 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4030 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4032 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4033 LogicalResult visitSV(
AssignOp op);
4034 LogicalResult visitSV(BPAssignOp op);
4035 LogicalResult visitSV(PAssignOp op);
4036 LogicalResult visitSV(ForceOp op);
4037 LogicalResult visitSV(ReleaseOp op);
4038 LogicalResult visitSV(AliasOp op);
4039 LogicalResult visitSV(InterfaceInstanceOp op);
4040 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4041 LogicalResult visitStmt(OutputOp op);
4043 LogicalResult visitStmt(InstanceOp op);
4044 LogicalResult visitStmt(InstanceChoiceOp op);
4045 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4046 ArrayRef<Value> instPortValues);
4051 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4052 LogicalResult visitSV(OrderedOutputOp op);
4053 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4054 LogicalResult visitSV(IfDefProceduralOp op) {
4055 return emitIfDef(op, op.getCond());
4057 LogicalResult visitSV(IfOp op);
4058 LogicalResult visitSV(AlwaysOp op);
4059 LogicalResult visitSV(AlwaysCombOp op);
4060 LogicalResult visitSV(AlwaysFFOp op);
4061 LogicalResult visitSV(InitialOp op);
4062 LogicalResult visitSV(CaseOp op);
4063 LogicalResult visitSV(FWriteOp op);
4064 LogicalResult visitSV(FFlushOp op);
4065 LogicalResult visitSV(VerbatimOp op);
4066 LogicalResult visitSV(MacroRefOp op);
4068 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4069 std::optional<unsigned> verbosity);
4070 LogicalResult visitSV(StopOp op);
4071 LogicalResult visitSV(FinishOp op);
4072 LogicalResult visitSV(ExitOp op);
4074 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4075 std::optional<unsigned> verbosity,
4077 ValueRange operands);
4078 LogicalResult visitSV(FatalOp op);
4079 LogicalResult visitSV(ErrorOp op);
4080 LogicalResult visitSV(WarningOp op);
4081 LogicalResult visitSV(InfoOp op);
4083 LogicalResult visitSV(ReadMemOp op);
4085 LogicalResult visitSV(GenerateOp op);
4086 LogicalResult visitSV(GenerateCaseOp op);
4088 LogicalResult visitSV(
ForOp op);
4090 void emitAssertionLabel(Operation *op);
4091 void emitAssertionMessage(StringAttr message, ValueRange args,
4092 SmallPtrSetImpl<Operation *> &ops,
4094 template <
typename Op>
4095 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4096 LogicalResult visitSV(AssertOp op);
4097 LogicalResult visitSV(AssumeOp op);
4098 LogicalResult visitSV(CoverOp op);
4099 template <
typename Op>
4100 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4101 LogicalResult visitSV(AssertConcurrentOp op);
4102 LogicalResult visitSV(AssumeConcurrentOp op);
4103 LogicalResult visitSV(CoverConcurrentOp op);
4104 template <
typename Op>
4105 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4106 LogicalResult visitSV(AssertPropertyOp op);
4107 LogicalResult visitSV(AssumePropertyOp op);
4108 LogicalResult visitSV(CoverPropertyOp op);
4110 LogicalResult visitSV(BindOp op);
4111 LogicalResult visitSV(InterfaceOp op);
4112 LogicalResult visitSV(InterfaceSignalOp op);
4113 LogicalResult visitSV(InterfaceModportOp op);
4114 LogicalResult visitSV(AssignInterfaceSignalOp op);
4115 LogicalResult visitSV(MacroErrorOp op);
4116 LogicalResult visitSV(MacroDefOp op);
4118 void emitBlockAsStatement(Block *block,
4119 const SmallPtrSetImpl<Operation *> &locationOps,
4120 StringRef multiLineComment = StringRef());
4122 LogicalResult visitSV(FuncDPIImportOp op);
4123 template <
typename CallOp>
4124 LogicalResult emitFunctionCall(CallOp callOp);
4125 LogicalResult visitSV(FuncCallProceduralOp op);
4126 LogicalResult visitSV(FuncCallOp op);
4127 LogicalResult visitSV(ReturnOp op);
4128 LogicalResult visitSV(IncludeOp op);
4131 ModuleEmitter &emitter;
4136 size_t maxDeclNameWidth = 0;
4137 size_t maxTypeWidth = 0;
4148void StmtEmitter::emitExpression(Value exp,
4149 SmallPtrSetImpl<Operation *> &emittedExprs,
4150 VerilogPrecedence parenthesizeIfLooserThan,
4151 bool isAssignmentLikeContext) {
4152 ExprEmitter(emitter, emittedExprs)
4153 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4158void StmtEmitter::emitSVAttributes(Operation *op) {
4166 setPendingNewline();
4169void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4170 llvm::function_ref<
void()> emitRHS,
4172 std::optional<PPExtString> wordBeforeLHS) {
4174 ps.scopedBox(PP::ibox2, [&]() {
4175 if (wordBeforeLHS) {
4176 ps << *wordBeforeLHS << PP::space;
4180 ps << PP::space << syntax << PP::space;
4182 ps.scopedBox(PP::ibox0, [&]() {
4189template <
typename Op>
4191StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4192 std::optional<PPExtString> wordBeforeLHS) {
4193 SmallPtrSet<Operation *, 8> ops;
4197 ps.addCallback({op,
true});
4198 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4200 emitExpression(op.getSrc(), ops, LowestPrecedence,
4205 ps.addCallback({op,
false});
4206 emitLocationInfoAndNewLine(ops);
4210LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4213 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4216 if (emitter.assignsInlined.count(op))
4220 emitSVAttributes(op);
4225LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4226 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4230 if (emitter.assignsInlined.count(op))
4234 emitSVAttributes(op);
4239LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4241 emitSVAttributes(op);
4246LogicalResult StmtEmitter::visitSV(ForceOp op) {
4248 emitError(op,
"SV attributes emission is unimplemented for the op");
4253LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4255 emitError(op,
"SV attributes emission is unimplemented for the op");
4258 SmallPtrSet<Operation *, 8> ops;
4260 ps.addCallback({op,
true});
4261 ps.scopedBox(PP::ibox2, [&]() {
4262 ps <<
"release" << PP::space;
4263 emitExpression(op.getDest(), ops);
4266 ps.addCallback({op,
false});
4267 emitLocationInfoAndNewLine(ops);
4271LogicalResult StmtEmitter::visitSV(AliasOp op) {
4273 emitError(op,
"SV attributes emission is unimplemented for the op");
4276 SmallPtrSet<Operation *, 8> ops;
4278 ps.addCallback({op,
true});
4279 ps.scopedBox(PP::ibox2, [&]() {
4280 ps <<
"alias" << PP::space;
4281 ps.scopedBox(PP::cbox0, [&]() {
4283 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4284 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4288 ps.addCallback({op,
false});
4289 emitLocationInfoAndNewLine(ops);
4293LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4294 auto doNotPrint = op.getDoNotPrint();
4295 if (doNotPrint && !state.options.emitBindComments)
4299 emitError(op,
"SV attributes emission is unimplemented for the op");
4302 StringRef prefix =
"";
4303 ps.addCallback({op,
true});
4306 ps <<
"// This interface is elsewhere emitted as a bind statement."
4310 SmallPtrSet<Operation *, 8> ops;
4313 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4314 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4315 "point to an interface");
4318 if (!prefix.empty())
4324 ps.addCallback({op,
false});
4325 emitLocationInfoAndNewLine(ops);
4333LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4335 SmallPtrSet<Operation *, 8> ops;
4336 size_t operandIndex = 0;
4337 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4338 for (
PortInfo port : ports.getOutputs()) {
4339 auto operand = op->getOperand(operandIndex);
4343 if (operand.hasOneUse() && operand.getDefiningOp() &&
4344 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4353 ps.addCallback({op,
true});
4355 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4357 ps <<
"// Zero width: ";
4360 ps <<
"assign" << PP::space;
4362 ps << PP::space <<
"=" << PP::space;
4363 ps.scopedBox(PP::ibox0, [&]() {
4367 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4368 ps <<
"/*Zero width*/";
4370 emitExpression(operand, ops, LowestPrecedence,
4375 ps.addCallback({op,
false});
4376 emitLocationInfoAndNewLine(ops);
4383LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4384 auto parent = op->getParentOfType<PortList>();
4386 return emitOutputLikeOp(op, ports);
4389LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4391 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4392 ps <<
"`ifndef " << typescopeDef << PP::newline;
4393 ps <<
"`define " << typescopeDef;
4394 setPendingNewline();
4395 emitStatementBlock(*op.getBodyBlock());
4397 ps <<
"`endif // " << typescopeDef;
4398 setPendingNewline();
4402LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4404 emitError(op,
"SV attributes emission is unimplemented for the op");
4409 ps << PP::neverbox <<
"// ";
4411 SmallPtrSet<Operation *, 8> ops;
4413 ps.scopedBox(PP::ibox2, [&]() {
4414 ps <<
"typedef" << PP::space;
4415 ps.invokeWithStringOS([&](
auto &os) {
4417 op.getAliasType(),
false);
4419 ps << PP::space <<
PPExtString(op.getPreferredName());
4420 ps.invokeWithStringOS(
4421 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4426 emitLocationInfoAndNewLine(ops);
4430template <
typename CallOpTy>
4431LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4435 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4437 SmallPtrSet<Operation *, 8> ops;
4441 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4442 if (explicitReturn) {
4443 assert(explicitReturn.hasOneUse());
4444 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4445 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4446 emitExpression(bpassignOp.getDest(), ops);
4448 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4449 ps <<
"assign" << PP::nbsp;
4450 emitExpression(assignOp.getDest(), ops);
4452 ps << PP::nbsp <<
"=" << PP::nbsp;
4455 auto arguments = callee.getPortList(
true);
4459 bool needsComma =
false;
4460 auto printArg = [&](Value value) {
4462 ps <<
"," << PP::space;
4463 emitExpression(value, ops);
4467 ps.scopedBox(PP::ibox0, [&] {
4468 unsigned inputIndex = 0, outputIndex = 0;
4469 for (
auto arg : arguments) {
4472 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4474 printArg(op.getInputs()[inputIndex++]);
4479 emitLocationInfoAndNewLine(ops);
4483LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4484 return emitFunctionCall(op);
4487LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4488 return emitFunctionCall(op);
4491template <
typename PPS>
4493 bool isAutomatic =
false,
4494 bool emitAsTwoStateType =
false) {
4495 ps <<
"function" << PP::nbsp;
4497 ps <<
"automatic" << PP::nbsp;
4498 auto retType = op.getExplicitlyReturnedType();
4500 ps.invokeWithStringOS([&](
auto &os) {
4501 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4502 emitAsTwoStateType);
4508 emitter.emitPortList(
4512LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4513 auto parent = op->getParentOfType<sv::FuncOp>();
4515 return emitOutputLikeOp(op, ports);
4518LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4520 ps <<
"`include" << PP::nbsp;
4522 if (op.getStyle() == IncludeStyle::System)
4523 ps <<
"<" << op.getTarget() <<
">";
4525 ps <<
"\"" << op.getTarget() <<
"\"";
4527 emitLocationInfo(op.getLoc());
4528 setPendingNewline();
4532LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4535 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4539 if (
auto linkageName = importOp.getLinkageName())
4540 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4542 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4543 assert(op.isDeclaration() &&
"function must be a declaration");
4546 assert(state.pendingNewline);
4552LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4554 emitError(op,
"SV attributes emission is unimplemented for the op");
4557 SmallPtrSet<Operation *, 8> ops;
4560 ps.addCallback({op,
true});
4562 if (
auto fd = op.getFd())
4563 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4566 ps.addCallback({op,
false});
4567 emitLocationInfoAndNewLine(ops);
4571LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4573 emitError(op,
"SV attributes emission is unimplemented for the op");
4576 SmallPtrSet<Operation *, 8> ops;
4579 ps.addCallback({op,
true});
4581 ps.scopedBox(PP::ibox0, [&]() {
4582 emitExpression(op.getFd(), ops);
4584 ps <<
"," << PP::space;
4585 ps.writeQuotedEscaped(op.getFormatString());
4593 for (
auto operand : op.getSubstitutions()) {
4594 ps <<
"," << PP::space;
4595 emitExpression(operand, ops);
4599 ps.addCallback({op,
false});
4600 emitLocationInfoAndNewLine(ops);
4604LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4606 emitError(op,
"SV attributes emission is unimplemented for the op");
4609 SmallPtrSet<Operation *, 8> ops;
4614 StringRef
string = op.getFormatString();
4615 if (
string.ends_with(
"\n"))
4616 string =
string.drop_back();
4621 bool isFirst =
true;
4624 while (!
string.
empty()) {
4625 auto lhsRhs =
string.split(
'\n');
4629 ps << PP::end << PP::newline << PP::neverbox;
4633 emitTextWithSubstitutions(
4634 ps, lhsRhs.first, op,
4635 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4636 string = lhsRhs.second;
4641 emitLocationInfoAndNewLine(ops);
4646LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4648 emitError(op,
"SV attributes emission is unimplemented for the op");
4652 SmallPtrSet<Operation *, 8> ops;
4657 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4658 assert(macroOp &&
"Invalid IR");
4660 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4662 if (!op.getInputs().empty()) {
4664 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4665 emitExpression(val, ops, LowestPrecedence,
4671 emitLocationInfoAndNewLine(ops);
4677StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4678 std::optional<unsigned> verbosity) {
4680 emitError(op,
"SV attributes emission is unimplemented for the op");
4683 SmallPtrSet<Operation *, 8> ops;
4685 ps.addCallback({op,
true});
4687 if (verbosity && *verbosity != 1) {
4689 ps.addAsString(*verbosity);
4693 ps.addCallback({op,
false});
4694 emitLocationInfoAndNewLine(ops);
4698LogicalResult StmtEmitter::visitSV(StopOp op) {
4699 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4702LogicalResult StmtEmitter::visitSV(FinishOp op) {
4703 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4707LogicalResult StmtEmitter::visitSV(ExitOp op) {
4708 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4714StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4715 std::optional<unsigned> verbosity,
4716 StringAttr message, ValueRange operands) {
4718 emitError(op,
"SV attributes emission is unimplemented for the op");
4721 SmallPtrSet<Operation *, 8> ops;
4723 ps.addCallback({op,
true});
4729 if ((verbosity && *verbosity != 1) || message) {
4731 ps.scopedBox(PP::ibox0, [&]() {
4735 ps.addAsString(*verbosity);
4740 ps <<
"," << PP::space;
4741 ps.writeQuotedEscaped(message.getValue());
4743 for (
auto operand : operands) {
4744 ps <<
"," << PP::space;
4745 emitExpression(operand, ops);
4754 ps.addCallback({op,
false});
4755 emitLocationInfoAndNewLine(ops);
4759LogicalResult StmtEmitter::visitSV(FatalOp op) {
4760 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4761 op.getMessageAttr(), op.getSubstitutions());
4764LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4765 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4766 op.getMessageAttr(), op.getSubstitutions());
4769LogicalResult StmtEmitter::visitSV(WarningOp op) {
4770 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4771 op.getMessageAttr(), op.getSubstitutions());
4774LogicalResult StmtEmitter::visitSV(InfoOp op) {
4775 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4776 op.getMessageAttr(), op.getSubstitutions());
4779LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4780 SmallPtrSet<Operation *, 8> ops({op});
4783 ps.addCallback({op,
true});
4785 switch (op.getBaseAttr().getValue()) {
4786 case MemBaseTypeAttr::MemBaseBin:
4789 case MemBaseTypeAttr::MemBaseHex:
4794 ps.scopedBox(PP::ibox0, [&]() {
4795 ps.writeQuotedEscaped(op.getFilename());
4796 ps <<
"," << PP::space;
4797 emitExpression(op.getDest(), ops);
4801 ps.addCallback({op,
false});
4802 emitLocationInfoAndNewLine(ops);
4806LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4807 emitSVAttributes(op);
4810 ps.addCallback({op,
true});
4811 ps <<
"generate" << PP::newline;
4813 setPendingNewline();
4814 emitStatementBlock(op.getBody().getBlocks().front());
4817 ps <<
"endgenerate";
4818 ps.addCallback({op,
false});
4819 setPendingNewline();
4823LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4824 emitSVAttributes(op);
4827 ps.addCallback({op,
true});
4829 ps.invokeWithStringOS([&](
auto &os) {
4830 emitter.printParamValue(
4831 op.getCond(), os, VerilogPrecedence::Selection,
4832 [&]() { return op->emitOpError(
"invalid case parameter"); });
4835 setPendingNewline();
4838 ArrayAttr
patterns = op.getCasePatterns();
4839 ArrayAttr caseNames = op.getCaseNames();
4840 MutableArrayRef<Region> regions = op.getCaseRegions();
4847 llvm::StringMap<size_t> nextGenIds;
4848 ps.scopedBox(PP::bbox2, [&]() {
4850 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4851 auto ®ion = regions[i];
4852 assert(region.hasOneBlock());
4853 Attribute patternAttr =
patterns[i];
4856 if (!isa<mlir::TypedAttr>(patternAttr))
4859 ps.invokeWithStringOS([&](
auto &os) {
4860 emitter.printParamValue(
4861 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4862 [&]() {
return op->emitOpError(
"invalid case value"); });
4865 StringRef legalName =
4866 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4869 setPendingNewline();
4870 emitStatementBlock(region.getBlocks().front());
4873 setPendingNewline();
4879 ps.addCallback({op,
false});
4880 setPendingNewline();
4884LogicalResult StmtEmitter::visitSV(
ForOp op) {
4885 emitSVAttributes(op);
4886 llvm::SmallPtrSet<Operation *, 8> ops;
4887 ps.addCallback({op,
true});
4889 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4892 ps.scopedBox(PP::cbox0, [&]() {
4896 ps <<
"logic" << PP::nbsp;
4897 ps.invokeWithStringOS([&](
auto &os) {
4898 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4903 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4908 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4909 [&]() { emitExpression(op.getUpperBound(), ops); },
4915 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4916 [&]() { emitExpression(op.getStep(), ops); },
4920 ps << PP::neverbreak;
4921 setPendingNewline();
4922 emitStatementBlock(op.getBody().getBlocks().front());
4925 ps.addCallback({op,
false});
4926 emitLocationInfoAndNewLine(ops);
4931void StmtEmitter::emitAssertionLabel(Operation *op) {
4932 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4938void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4939 SmallPtrSetImpl<Operation *> &ops,
4940 bool isConcurrent =
false) {
4943 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4944 ps.scopedBox(PP::ibox0, [&]() {
4945 ps.writeQuotedEscaped(message.getValue());
4947 for (
auto arg : args) {
4948 ps <<
"," << PP::space;
4949 emitExpression(arg, ops);
4955template <
typename Op>
4956LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4958 emitError(op,
"SV attributes emission is unimplemented for the op");
4961 SmallPtrSet<Operation *, 8> ops;
4963 ps.addCallback({op,
true});
4964 ps.scopedBox(PP::ibox2, [&]() {
4965 emitAssertionLabel(op);
4966 ps.scopedBox(PP::cbox0, [&]() {
4968 switch (op.getDefer()) {
4969 case DeferAssert::Immediate:
4971 case DeferAssert::Observed:
4974 case DeferAssert::Final:
4979 ps.scopedBox(PP::ibox0, [&]() {
4980 emitExpression(op.getExpression(), ops);
4983 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4987 ps.addCallback({op,
false});
4988 emitLocationInfoAndNewLine(ops);
4992LogicalResult StmtEmitter::visitSV(AssertOp op) {
4993 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4996LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4997 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5000LogicalResult StmtEmitter::visitSV(CoverOp op) {
5001 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5004template <
typename Op>
5005LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5007 emitError(op,
"SV attributes emission is unimplemented for the op");
5010 SmallPtrSet<Operation *, 8> ops;
5012 ps.addCallback({op,
true});
5013 ps.scopedBox(PP::ibox2, [&]() {
5014 emitAssertionLabel(op);
5015 ps.scopedBox(PP::cbox0, [&]() {
5016 ps << opName << PP::nbsp <<
"property (";
5017 ps.scopedBox(PP::ibox0, [&]() {
5018 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5020 emitExpression(op.getClock(), ops);
5021 ps <<
")" << PP::space;
5022 emitExpression(op.getProperty(), ops);
5025 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5030 ps.addCallback({op,
false});
5031 emitLocationInfoAndNewLine(ops);
5035LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5036 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5039LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5040 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5043LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5044 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5049template <
typename Op>
5050LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5052 emitError(op,
"SV attributes emission is unimplemented for the op");
5062 Operation *parent = op->getParentOp();
5063 Value
property = op.getProperty();
5064 bool isTemporal = !
property.getType().isSignlessInteger(1);
5065 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5066 bool emitAsImmediate = !isTemporal && isProcedural;
5069 SmallPtrSet<Operation *, 8> ops;
5071 ps.addCallback({op,
true});
5072 ps.scopedBox(PP::ibox2, [&]() {
5074 emitAssertionLabel(op);
5076 ps.scopedBox(PP::cbox0, [&]() {
5077 if (emitAsImmediate)
5078 ps << opName <<
"(";
5080 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5082 Value clock = op.getClock();
5083 auto event = op.getEvent();
5085 ps.scopedBox(PP::ibox2, [&]() {
5086 PropertyEmitter(emitter, ops)
5087 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5090 ps.scopedBox(PP::ibox2, [&]() {
5091 PropertyEmitter(emitter, ops)
5092 .emitAssertPropertyBody(property, op.getDisable());
5097 ps.addCallback({op,
false});
5098 emitLocationInfoAndNewLine(ops);
5102LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5103 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5106LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5107 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5110LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5111 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5114LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5116 emitError(op,
"SV attributes emission is unimplemented for the op");
5119 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5120 .getMacroIdentifier());
5123 bool hasEmptyThen = op->getRegion(0).front().empty();
5125 ps <<
"`ifndef " << ident;
5127 ps <<
"`ifdef " << ident;
5129 SmallPtrSet<Operation *, 8> ops;
5131 emitLocationInfoAndNewLine(ops);
5134 emitStatementBlock(op->getRegion(0).front());
5136 if (!op->getRegion(1).empty()) {
5137 if (!hasEmptyThen) {
5139 ps <<
"`else // " << ident;
5140 setPendingNewline();
5142 emitStatementBlock(op->getRegion(1).front());
5149 setPendingNewline();
5157void StmtEmitter::emitBlockAsStatement(
5158 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5159 StringRef multiLineComment) {
5166 emitLocationInfoAndNewLine(locationOps);
5169 emitStatementBlock(*block);
5171 if (needsBeginEnd) {
5175 if (!multiLineComment.empty())
5176 ps <<
" // " << multiLineComment;
5177 setPendingNewline();
5181LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5183 for (
auto &op : ooop.getBody().front())
5188LogicalResult StmtEmitter::visitSV(IfOp op) {
5189 SmallPtrSet<Operation *, 8> ops;
5191 auto ifcondBox = PP::ibox2;
5193 emitSVAttributes(op);
5195 ps.addCallback({op,
true});
5196 ps <<
"if (" << ifcondBox;
5206 emitExpression(ifOp.getCond(), ops);
5207 ps << PP::end <<
")";
5208 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5210 if (!ifOp.hasElse())
5214 Block *elseBlock = ifOp.getElseBlock();
5216 if (!nestedElseIfOp) {
5221 emitBlockAsStatement(elseBlock, ops);
5227 ifOp = nestedElseIfOp;
5228 ps <<
"else if (" << ifcondBox;
5230 ps.addCallback({op,
false});
5235LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5236 emitSVAttributes(op);
5237 SmallPtrSet<Operation *, 8> ops;
5241 auto printEvent = [&](AlwaysOp::Condition cond) {
5242 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5243 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5245 ps.addCallback({op,
true});
5247 switch (op.getNumConditions()) {
5253 printEvent(op.getCondition(0));
5258 ps.scopedBox(PP::cbox0, [&]() {
5259 printEvent(op.getCondition(0));
5260 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5261 ps << PP::space <<
"or" << PP::space;
5262 printEvent(op.getCondition(i));
5271 std::string comment;
5272 if (op.getNumConditions() == 0) {
5273 comment =
"always @*";
5275 comment =
"always @(";
5278 [&](Attribute eventAttr) {
5279 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5280 comment += stringifyEventControl(event);
5282 [&]() { comment +=
", "; });
5286 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5287 ps.addCallback({op,
false});
5291LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5292 emitSVAttributes(op);
5293 SmallPtrSet<Operation *, 8> ops;
5297 ps.addCallback({op,
true});
5298 StringRef opString =
"always_comb";
5299 if (state.options.noAlwaysComb)
5300 opString =
"always @(*)";
5303 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5304 ps.addCallback({op,
false});
5308LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5309 emitSVAttributes(op);
5311 SmallPtrSet<Operation *, 8> ops;
5315 ps.addCallback({op,
true});
5316 ps <<
"always_ff @(";
5317 ps.scopedBox(PP::cbox0, [&]() {
5318 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5319 emitExpression(op.getClock(), ops);
5320 if (op.getResetStyle() == ResetType::AsyncReset) {
5321 ps << PP::nbsp <<
"or" << PP::space
5322 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5323 emitExpression(op.getReset(), ops);
5330 std::string comment;
5331 comment +=
"always_ff @(";
5332 comment += stringifyEventControl(op.getClockEdge());
5333 if (op.getResetStyle() == ResetType::AsyncReset) {
5335 comment += stringifyEventControl(*op.getResetEdge());
5339 if (op.getResetStyle() == ResetType::NoReset)
5340 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5343 emitLocationInfoAndNewLine(ops);
5344 ps.scopedBox(PP::bbox2, [&]() {
5350 if (op.getResetStyle() == ResetType::AsyncReset &&
5351 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5353 emitExpression(op.getReset(), ops);
5355 emitBlockAsStatement(op.getResetBlock(), ops);
5358 emitBlockAsStatement(op.getBodyBlock(), ops);
5363 ps <<
" // " << comment;
5364 setPendingNewline();
5366 ps.addCallback({op,
false});
5370LogicalResult StmtEmitter::visitSV(InitialOp op) {
5371 emitSVAttributes(op);
5372 SmallPtrSet<Operation *, 8> ops;
5375 ps.addCallback({op,
true});
5377 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5378 ps.addCallback({op,
false});
5382LogicalResult StmtEmitter::visitSV(CaseOp op) {
5383 emitSVAttributes(op);
5384 SmallPtrSet<Operation *, 8> ops, emptyOps;
5387 ps.addCallback({op,
true});
5388 if (op.getValidationQualifier() !=
5389 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5390 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5391 op.getValidationQualifier()))
5393 const char *opname =
nullptr;
5394 switch (op.getCaseStyle()) {
5395 case CaseStmtType::CaseStmt:
5398 case CaseStmtType::CaseXStmt:
5401 case CaseStmtType::CaseZStmt:
5405 ps << opname <<
" (";
5406 ps.scopedBox(PP::ibox0, [&]() {
5407 emitExpression(op.getCond(), ops);
5410 emitLocationInfoAndNewLine(ops);
5412 ps.scopedBox(PP::bbox2, [&]() {
5413 for (
auto &caseInfo : op.getCases()) {
5415 auto &
pattern = caseInfo.pattern;
5417 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5418 .Case<CaseBitPattern>([&](
auto bitPattern) {
5421 ps.invokeWithStringOS([&](
auto &os) {
5422 os << bitPattern->getWidth() <<
"'b";
5423 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5424 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5427 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5428 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5429 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5431 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5432 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5435 emitBlockAsStatement(caseInfo.block, emptyOps);
5441 ps.addCallback({op,
false});
5442 emitLocationInfoAndNewLine(ops);
5446LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5447 bool doNotPrint = op.getDoNotPrint();
5448 if (doNotPrint && !state.options.emitBindComments)
5453 emitSVAttributes(op);
5455 ps.addCallback({op,
true});
5458 <<
"/* This instance is elsewhere emitted as a bind statement."
5461 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5462 "attributes. The attributes will not be emitted.";
5465 SmallPtrSet<Operation *, 8> ops;
5470 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5471 assert(moduleOp &&
"Invalid IR");
5475 if (!op.getParameters().empty()) {
5478 bool printed =
false;
5480 llvm::zip(op.getParameters(),
5481 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5482 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5483 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5485 if (param.getValue() == modParam.getValue())
5490 ps <<
" #(" << PP::bbox2 << PP::newline;
5493 ps <<
"," << PP::newline;
5497 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5499 ps.invokeWithStringOS([&](
auto &os) {
5500 emitter.printParamValue(param.getValue(), os, [&]() {
5501 return op->emitOpError(
"invalid instance parameter '")
5502 << param.getName().getValue() <<
"' value";
5508 ps << PP::end << PP::newline <<
")";
5515 SmallVector<Value> instPortValues(modPortInfo.size());
5516 op.getValues(instPortValues, modPortInfo);
5517 emitInstancePortList(op, modPortInfo, instPortValues);
5519 ps.addCallback({op,
false});
5520 emitLocationInfoAndNewLine(ops);
5525 setPendingNewline();
5530LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5532 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5533 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5538 Operation *defaultModuleOp =
5539 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5541 SmallVector<Value> instPortValues(modPortInfo.size());
5542 op.getValues(instPortValues, modPortInfo);
5543 emitInstancePortList(op, modPortInfo, instPortValues);
5545 SmallPtrSet<Operation *, 8> ops;
5547 ps.addCallback({op,
false});
5548 emitLocationInfoAndNewLine(ops);
5553void StmtEmitter::emitInstancePortList(Operation *op,
5555 ArrayRef<Value> instPortValues) {
5556 SmallPtrSet<Operation *, 8> ops;
5559 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5560 ModulePortInfo containingPortList(containingModule.getPortList());
5565 size_t maxNameLength = 0;
5566 for (
auto &elt : modPortInfo) {
5567 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5570 auto getWireForValue = [&](Value result) {
5571 return result.getUsers().begin()->getOperand(0);
5575 bool isFirst =
true;
5576 bool isZeroWidth =
false;
5578 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5580 auto &modPort = modPortInfo.
at(portNum);
5582 Value portVal = instPortValues[portNum];
5587 bool shouldPrintComma =
true;
5589 shouldPrintComma =
false;
5590 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5592 shouldPrintComma =
true;
5597 if (shouldPrintComma)
5600 emitLocationInfoAndNewLine(ops);
5615 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5616 auto modPortName = modPort.getVerilogName();
5618 ps.spaces(maxNameLength - modPortName.size() + 1);
5620 ps.scopedBox(PP::ibox0, [&]() {
5627 if (!modPort.isOutput()) {
5629 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5630 ps <<
"/* Zero width */";
5632 emitExpression(portVal, ops, LowestPrecedence);
5633 }
else if (portVal.use_empty()) {
5634 ps <<
"/* unused */";
5635 }
else if (portVal.hasOneUse() &&
5636 (output = dyn_cast_or_null<OutputOp>(
5637 portVal.getUses().begin()->getOwner()))) {
5642 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5644 containingPortList.atOutput(outputPortNo).getVerilogName());
5646 portVal = getWireForValue(portVal);
5647 emitExpression(portVal, ops);
5653 if (!isFirst || isZeroWidth) {
5654 emitLocationInfoAndNewLine(ops);
5667LogicalResult StmtEmitter::visitSV(BindOp op) {
5668 emitter.emitBind(op);
5669 assert(state.pendingNewline);
5673LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5674 emitComment(op.getCommentAttr());
5676 emitSVAttributes(op);
5679 ps.addCallback({op,
true});
5681 setPendingNewline();
5683 emitStatementBlock(*op.getBodyBlock());
5685 ps <<
"endinterface" << PP::newline;
5686 ps.addCallback({op,
false});
5687 setPendingNewline();
5691LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5693 emitSVAttributes(op);
5695 ps.addCallback({op,
true});
5697 ps << PP::neverbox <<
"// ";
5698 ps.invokeWithStringOS([&](
auto &os) {
5703 ps.invokeWithStringOS(
5704 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5708 ps.addCallback({op,
false});
5709 setPendingNewline();
5713LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5715 ps.addCallback({op,
true});
5719 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5720 auto port = cast<ModportStructAttr>(portAttr);
5721 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5722 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5723 ps << PPExtString(getSymOpName(signalDecl));
5727 ps.addCallback({op,
false});
5728 setPendingNewline();
5732LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5734 ps.addCallback({op,
true});
5735 SmallPtrSet<Operation *, 8> emitted;
5738 emitExpression(op.getIface(), emitted);
5739 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5740 emitExpression(op.getRhs(), emitted);
5742 ps.addCallback({op,
false});
5743 setPendingNewline();
5747LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5749 ps <<
"`" << op.getMacroIdentifier();
5750 setPendingNewline();
5754LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5755 auto decl = op.getReferencedMacro(&state.symbolCache);
5758 ps.addCallback({op,
true});
5760 if (decl.getArgs()) {
5762 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5763 ps << cast<StringAttr>(name);
5767 if (!op.getFormatString().empty()) {
5769 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5772 ps.addCallback({op,
false});
5773 setPendingNewline();
5777void StmtEmitter::emitStatement(Operation *op) {
5784 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5788 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5789 succeeded(dispatchVerifVisitor(op)))
5792 emitOpError(op,
"emission to Verilog not supported");
5793 emitPendingNewlineIfNeeded();
5794 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5795 setPendingNewline();
5806 StmtEmitter &stmtEmitter) {
5813 if (isa<IfDefProceduralOp>(op->getParentOp()))
5821 SmallVector<Value, 8> exprsToScan(op->getOperands());
5826 while (!exprsToScan.empty()) {
5827 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5834 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5835 auto *defOp = readInout.getOperand().getDefiningOp();
5842 if (isa<sv::WireOp>(defOp))
5847 if (!isa<RegOp, LogicOp>(defOp))
5853 if (isa<LogicOp>(defOp) &&
5854 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5858 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5859 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5867 exprsToScan.append(expr->getOperands().begin(),
5868 expr->getOperands().end());
5874 if (expr->getBlock() != op->getBlock())
5879 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5886template <
class AssignTy>
5888 AssignTy singleAssign;
5889 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5890 if (hasSVAttributes(user))
5893 if (auto assign = dyn_cast<AssignTy>(user)) {
5896 singleAssign = assign;
5900 return isa<ReadInOutOp>(user);
5902 return singleAssign;
5908 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5912 if (op1->getBlock() != user->getBlock())
5918 return op1->isBeforeInBlock(user);
5922LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5923 emitSVAttributes(op);
5924 auto value = op->getResult(0);
5925 SmallPtrSet<Operation *, 8> opsForLocation;
5926 opsForLocation.insert(op);
5928 ps.addCallback({op,
true});
5931 auto type = value.getType();
5937 bool singleBitDefaultType = !isa<LocalParamOp>(op);
5939 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5940 unsigned targetColumn = 0;
5941 unsigned column = 0;
5944 if (maxDeclNameWidth > 0)
5945 targetColumn += maxDeclNameWidth + 1;
5948 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5949 }
else if (!word.empty()) {
5951 column += word.size();
5952 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5953 ps.spaces(numSpaces);
5954 column += numSpaces;
5957 SmallString<8> typeString;
5960 llvm::raw_svector_ostream stringStream(typeString);
5963 true, singleBitDefaultType);
5966 if (maxTypeWidth > 0)
5967 targetColumn += maxTypeWidth + 1;
5968 unsigned numSpaces = 0;
5969 if (!typeString.empty()) {
5971 column += typeString.size();
5974 if (targetColumn > column)
5975 numSpaces = targetColumn - column;
5976 ps.spaces(numSpaces);
5977 column += numSpaces;
5983 ps.invokeWithStringOS(
5984 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5987 if (state.options.printDebugInfo) {
5988 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5989 auto innerSym = innerSymOp.getInnerSymAttr();
5990 if (innerSym && !innerSym.empty()) {
5992 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5998 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5999 ps << PP::space <<
"=" << PP::space;
6000 ps.invokeWithStringOS([&](
auto &os) {
6001 emitter.printParamValue(localparam.getValue(), os, [&]() {
6002 return op->emitOpError(
"invalid localparam value");
6007 if (
auto regOp = dyn_cast<RegOp>(op)) {
6008 if (
auto initValue = regOp.getInit()) {
6009 ps << PP::space <<
"=" << PP::space;
6010 ps.scopedBox(PP::ibox0, [&]() {
6011 emitExpression(initValue, opsForLocation, LowestPrecedence,
6020 if (isa<sv::WireOp>(op) &&
6021 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6024 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6025 auto *source = singleAssign.getSrc().getDefiningOp();
6029 if (!source || isa<ConstantOp>(source) ||
6030 op->getNextNode() == singleAssign) {
6031 ps << PP::space <<
"=" << PP::space;
6032 ps.scopedBox(PP::ibox0, [&]() {
6033 emitExpression(singleAssign.getSrc(), opsForLocation,
6037 emitter.assignsInlined.insert(singleAssign);
6045 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
6048 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6051 auto *source = singleAssign.getSrc().getDefiningOp();
6055 if (!source || isa<ConstantOp>(source) ||
6058 ps << PP::space <<
"=" << PP::space;
6059 ps.scopedBox(PP::ibox0, [&]() {
6060 emitExpression(singleAssign.getSrc(), opsForLocation,
6065 emitter.assignsInlined.insert(singleAssign);
6066 emitter.expressionsEmittedIntoDecl.insert(op);
6073 ps.addCallback({op,
false});
6074 emitLocationInfoAndNewLine(opsForLocation);
6078void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6081 NameCollector collector(emitter);
6082 collector.collectNames(block);
6085 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6086 maxTypeWidth = collector.getMaxTypeWidth();
6089void StmtEmitter::emitStatementBlock(Block &body) {
6090 ps.scopedBox(PP::bbox2, [&]() {
6095 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6096 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6101 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6102 collectNamesAndCalculateDeclarationWidths(body);
6105 for (
auto &op : body) {
6112void ModuleEmitter::emitStatement(Operation *op) {
6113 StmtEmitter(*
this, state.options).emitStatement(op);
6118void ModuleEmitter::emitSVAttributes(Operation *op) {
6126 setPendingNewline();
6133void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6134 auto verilogName =
module.getVerilogModuleNameAttr();
6136 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6138 setPendingNewline();
6147void ModuleEmitter::emitBind(BindOp op) {
6149 emitError(op,
"SV attributes emission is unimplemented for the op");
6150 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6156 Operation *childMod =
6157 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6161 ps.addCallback({op,
true});
6162 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6163 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6165 bool isFirst =
true;
6166 ps.scopedBox(PP::bbox2, [&]() {
6167 auto parentPortInfo = parentMod.getPortList();
6171 size_t maxNameLength = 0;
6172 for (
auto &elt : childPortInfo) {
6173 auto portName = elt.getVerilogName();
6174 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6175 maxNameLength = std::max(maxNameLength, elt.getName().size());
6178 SmallVector<Value> instPortValues(childPortInfo.size());
6179 inst.getValues(instPortValues, childPortInfo);
6181 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6183 Value portVal = instPortValues[idx];
6189 bool shouldPrintComma =
true;
6191 shouldPrintComma =
false;
6192 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6194 shouldPrintComma =
true;
6199 if (shouldPrintComma)
6212 ps << PP::neverbox <<
"//";
6216 ps.nbsp(maxNameLength - elt.getName().size());
6218 llvm::SmallPtrSet<Operation *, 4> ops;
6219 if (elt.isOutput()) {
6220 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6221 "output port must have either single or no use");
6222 if (portVal.use_empty()) {
6223 ps <<
"/* unused */";
6224 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6225 portVal.getUses().begin()->getOwner())) {
6228 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6230 parentPortList.atOutput(outputPortNo).getVerilogName());
6232 portVal = portVal.getUsers().begin()->getOperand(0);
6233 ExprEmitter(*
this, ops)
6234 .emitExpression(portVal, LowestPrecedence,
6238 ExprEmitter(*
this, ops)
6239 .emitExpression(portVal, LowestPrecedence,
6252 ps.addCallback({op,
false});
6253 setPendingNewline();
6256void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6258 emitError(op,
"SV attributes emission is unimplemented for the op");
6260 auto instance = op.getReferencedInstance(&state.symbolCache);
6262 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6263 instance.getInterfaceType().getInterface());
6265 ps.addCallback({op,
true});
6266 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6267 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6269 ps.addCallback({op,
false});
6270 setPendingNewline();
6273void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6277 auto printParamType = [&](Type type, Attribute defaultValue,
6278 SmallString<8> &result) {
6280 llvm::raw_svector_ostream sstream(result);
6285 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6286 if (intAttr.getValue().getBitWidth() == 32)
6288 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6289 if (fpAttr.getType().isF64())
6292 if (isa<NoneType>(type))
6299 if (
auto intType = type_dyn_cast<IntegerType>(type))
6300 if (intType.getWidth() == 32) {
6301 sstream <<
"/*integer*/";
6305 printPackedType(type, sstream, module->getLoc(),
6313 size_t maxTypeWidth = 0;
6314 SmallString<8> scratch;
6315 for (
auto param : params) {
6316 auto paramAttr = cast<ParamDeclAttr>(param);
6318 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6319 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6322 if (maxTypeWidth > 0)
6325 ps.scopedBox(PP::bbox2, [&]() {
6326 ps << PP::newline <<
"#(";
6327 ps.scopedBox(PP::cbox0, [&]() {
6330 [&](Attribute param) {
6331 auto paramAttr = cast<ParamDeclAttr>(param);
6332 auto defaultValue = paramAttr.getValue();
6334 printParamType(paramAttr.getType(), defaultValue, scratch);
6335 if (!scratch.empty())
6337 if (scratch.size() < maxTypeWidth)
6338 ps.nbsp(maxTypeWidth - scratch.size());
6340 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6341 module, paramAttr.getName()));
6345 ps.invokeWithStringOS([&](
auto &os) {
6347 return module->emitError("parameter '")
6348 << paramAttr.getName().getValue()
6349 << "' has invalid value";
6354 [&]() { ps <<
"," << PP::newline; });
6360void ModuleEmitter::emitPortList(Operation *module,
6362 bool emitAsTwoStateType) {
6364 if (portInfo.
size())
6365 emitLocationInfo(module->getLoc());
6369 bool hasOutputs =
false, hasZeroWidth =
false;
6370 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6371 SmallVector<SmallString<8>, 16> portTypeStrings;
6373 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6374 auto port = portInfo.
at(i);
6378 lastNonZeroPort = i;
6381 portTypeStrings.push_back({});
6383 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6385 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6388 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6391 if (maxTypeWidth > 0)
6395 ps.scopedBox(PP::bbox2, [&]() {
6396 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6397 auto lastPort = e - 1;
6400 auto portType = portInfo.
at(portIdx).
type;
6404 bool isZeroWidth =
false;
6409 ps << (isZeroWidth ?
"// " :
" ");
6413 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6414 switch (thisPortDirection) {
6415 case ModulePort::Direction::Output:
6418 case ModulePort::Direction::Input:
6419 ps << (hasOutputs ?
"input " :
"input ");
6421 case ModulePort::Direction::InOut:
6422 ps << (hasOutputs ?
"inout " :
"inout ");
6425 bool emitWireInPorts = state.options.emitWireInPorts;
6426 if (emitWireInPorts)
6430 if (!portTypeStrings[portIdx].
empty())
6431 ps << portTypeStrings[portIdx];
6432 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6433 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6435 size_t startOfNamePos =
6436 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6442 ps.invokeWithStringOS(
6443 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6446 auto innerSym = portInfo.
at(portIdx).
getSym();
6447 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6449 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6454 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6458 if (
auto loc = portInfo.
at(portIdx).
loc)
6459 emitLocationInfo(loc);
6469 if (!state.options.disallowPortDeclSharing) {
6470 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6473 auto port = portInfo.
at(portIdx);
6477 bool isZeroWidth =
false;
6482 ps << (isZeroWidth ?
"// " :
" ");
6485 ps.nbsp(startOfNamePos);
6488 StringRef name = port.getVerilogName();
6492 ps.invokeWithStringOS(
6493 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6496 auto sym = port.getSym();
6497 if (state.options.printDebugInfo && sym && !sym.empty())
6498 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6502 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6506 if (
auto loc = port.loc)
6507 emitLocationInfo(loc);
6518 if (!portInfo.
size()) {
6520 SmallPtrSet<Operation *, 8> moduleOpSet;
6521 moduleOpSet.insert(module);
6522 emitLocationInfoAndNewLine(moduleOpSet);
6525 ps <<
");" << PP::newline;
6526 setPendingNewline();
6530void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6531 currentModuleOp =
module;
6533 emitComment(module.getCommentAttr());
6534 emitSVAttributes(module);
6536 ps.addCallback({module,
true});
6540 emitParameters(module, module.getParameters());
6544 assert(state.pendingNewline);
6547 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6550 ps.addCallback({module,
false});
6552 setPendingNewline();
6554 currentModuleOp =
nullptr;
6557void ModuleEmitter::emitFunc(FuncOp func) {
6559 if (func.isDeclaration())
6562 currentModuleOp = func;
6564 ps.addCallback({func,
true});
6568 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6570 ps <<
"endfunction";
6572 currentModuleOp =
nullptr;
6581 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6588 void emit(emit::FileListOp op);
6591 void emit(Block *block);
6593 void emitOp(emit::RefOp op);
6594 void emitOp(emit::VerbatimOp op);
6598 for (Operation &op : *block) {
6599 TypeSwitch<Operation *>(&op)
6600 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6601 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6602 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6603 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6604 .Case<BindInterfaceOp>(
6605 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6606 .Case<TypeScopeOp>([&](
auto typedecls) {
6607 ModuleEmitter(state).emitStatement(typedecls);
6610 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6616 for (
auto sym : op.getFiles()) {
6617 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6619 auto it = state.fileMapping.find(fileName);
6620 if (it == state.fileMapping.end()) {
6621 emitOpError(op,
" references an invalid file: ") << sym;
6625 auto file = cast<emit::FileOp>(it->second);
6626 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6633 StringAttr target = op.getTargetAttr().getAttr();
6634 auto *targetOp = state.symbolCache.getDefinition(target);
6635 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6637 TypeSwitch<Operation *>(targetOp)
6638 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6639 .Case<hw::HWModuleOp>(
6640 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6641 .Case<TypeScopeOp>([&](
auto typedecls) {
6642 ModuleEmitter(state).emitStatement(typedecls);
6645 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6651 SmallPtrSet<Operation *, 8> ops;
6656 StringRef text = op.getText();
6660 const auto &[lhs, rhs] = text.split(
'\n');
6664 ps << PP::end << PP::newline << PP::neverbox;
6666 }
while (!text.empty());
6669 emitLocationInfoAndNewLine(ops);
6687 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6688 moduleOp->walk([&](Operation *op) {
6690 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6693 SymbolTable::getSymbolAttrName()),
6694 name.getSymName(), op);
6695 if (isa<BindOp>(op))
6701 auto collectPorts = [&](
auto moduleOp) {
6702 auto portInfo = moduleOp.getPortList();
6703 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6704 if (!p.attrs || p.attrs.empty())
6706 for (NamedAttribute portAttr : p.attrs) {
6707 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6716 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6717 for (
auto file :
designOp.getOps<emit::FileOp>())
6718 for (
auto refs : file.getOps<emit::RefOp>())
6719 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6721 SmallString<32> outputPath;
6722 for (
auto &op : *
designOp.getBody()) {
6725 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6727 bool hasFileName =
false;
6728 bool emitReplicatedOps = !isFileOp;
6729 bool addToFilelist = !isFileOp;
6735 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6737 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6738 <<
" on " << op <<
"\n";);
6739 if (!attr.isDirectory())
6742 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6743 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6746 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6751 if (!defaultFileName.isTriviallyEmpty()) {
6752 llvm::sys::path::append(outputPath, defaultFileName);
6754 op->emitError(
"file name unspecified");
6756 llvm::sys::path::append(outputPath,
"error.out");
6760 auto destFile = StringAttr::get(op->getContext(), outputPath);
6761 auto &file =
files[destFile];
6762 file.ops.push_back(info);
6763 file.emitReplicatedOps = emitReplicatedOps;
6764 file.addToFilelist = addToFilelist;
6765 file.isVerilog = outputPath.ends_with(
".sv");
6770 if (!attr || attr.isDirectory()) {
6771 auto excludeFromFileListAttr =
6772 BoolAttr::get(op->getContext(), !addToFilelist);
6773 auto includeReplicatedOpsAttr =
6774 BoolAttr::get(op->getContext(), emitReplicatedOps);
6775 auto outputFileAttr = hw::OutputFileAttr::get(
6776 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6777 op->setAttr(
"output_file", outputFileAttr);
6783 TypeSwitch<Operation *>(&op)
6784 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6786 fileMapping.try_emplace(file.getSymNameAttr(), file);
6787 separateFile(file, file.getFileName());
6789 .Case<emit::FragmentOp>([&](
auto fragment) {
6792 .Case<HWModuleOp>([&](
auto mod) {
6794 auto sym = mod.getNameAttr();
6797 collectInstanceSymbolsAndBinds(mod);
6799 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6800 if (it->second.size() != 1 || attr) {
6803 op.emitError(
"modules can be emitted to a single file");
6811 if (attr || separateModules)
6817 .Case<InterfaceOp>([&](InterfaceOp intf) {
6822 for (
auto &op : *intf.getBodyBlock())
6823 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6824 if (
auto name = symOp.getNameAttr())
6828 if (attr || separateModules)
6829 separateFile(intf, intf.getSymName() +
".sv");
6839 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6840 [&](Operation *op) {
6846 separateFile(op,
"");
6848 .Case<FuncOp>([&](
auto op) {
6854 separateFile(op,
"");
6858 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6861 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6870 separateFile(op,
"");
6872 .Case<BindOp>([&](
auto op) {
6874 separateFile(op,
"bindfile.sv");
6879 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
6880 .Case<MacroDeclOp>([&](
auto op) {
6883 .Case<sv::ReserveNamesOp>([](
auto op) {
6886 .Case<om::ClassLike>([&](
auto op) {
6889 .Case<om::ConstantOp>([&](
auto op) {
6892 .Default([&](
auto *) {
6893 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6913 size_t lastReplicatedOp = 0;
6915 bool emitHeaderInclude =
6918 if (emitHeaderInclude)
6921 size_t numReplicatedOps =
6926 DenseSet<emit::FragmentOp> includedFragments;
6927 for (
const auto &opInfo : file.
ops) {
6928 Operation *op = opInfo.op;
6932 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6938 if (
auto fragments =
6940 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6944 op->emitError(
"cannot find referenced fragment ") << sym;
6947 emit::FragmentOp fragment = it->second;
6948 if (includedFragments.insert(fragment).second) {
6949 thingsToEmit.emplace_back(it->second);
6955 thingsToEmit.emplace_back(op);
6960 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6965 TypeSwitch<Operation *>(op)
6966 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6967 .Case<HWModuleExternOp>([&](
auto op) {
6970 .Case<HWModuleGeneratedOp>(
6971 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6972 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6973 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6974 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6975 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6976 .Case<TypeScopeOp>([&](
auto typedecls) {
6977 ModuleEmitter(state).emitStatement(typedecls);
6979 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6981 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
6982 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6983 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6984 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6985 .Default([&](
auto *op) {
6986 state.encounteredError =
true;
6987 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6994 llvm::formatted_raw_ostream &os,
6995 StringAttr fileName,
bool parallelize) {
6996 MLIRContext *context =
designOp->getContext();
7000 parallelize &= context->isMultithreadingEnabled();
7011 size_t lineOffset = 0;
7012 for (
auto &entry : thingsToEmit) {
7013 entry.verilogLocs.setStream(os);
7014 if (
auto *op = entry.getOperation()) {
7019 state.addVerilogLocToOps(lineOffset, fileName);
7021 os << entry.getStringData();
7026 if (state.encounteredError)
7044 SmallString<256> buffer;
7045 llvm::raw_svector_ostream tmpStream(buffer);
7046 llvm::formatted_raw_ostream rs(tmpStream);
7057 for (
auto &entry : thingsToEmit) {
7060 auto *op = entry.getOperation();
7062 auto lineOffset = os.getLine() + 1;
7063 os << entry.getStringData();
7067 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7070 entry.verilogLocs.setStream(os);
7077 state.addVerilogLocToOps(0, fileName);
7093 module.emitWarning()
7094 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7095 "created only at SplitExportVerilog";
7104 for (
const auto &it : emitter.
files) {
7105 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7106 "\" ----- 8< -----\n\n");
7112 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7113 "\" ----- 8< -----\n\n");
7114 for (
auto &name : it.second)
7115 contents += name.str() +
"\n";
7116 list.emplace_back(contents);
7119 llvm::formatted_raw_ostream rs(os);
7123 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7132 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7134 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7135 if (failed(failableParallelForEach(
7136 module->getContext(), modulesToPrepare,
7137 [&](
auto op) { return prepareHWModule(op, options); })))
7144struct ExportVerilogPass
7145 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7146 ExportVerilogPass(raw_ostream &os) : os(os) {}
7147 void runOnOperation()
override {
7149 mlir::OpPassManager preparePM(
"builtin.module");
7150 preparePM.addPass(createLegalizeAnonEnums());
7152 auto &modulePM = preparePM.nestAny();
7153 modulePM.addPass(createPrepareForEmission());
7154 if (failed(runPipeline(preparePM, getOperation())))
7155 return signalPassFailure();
7158 return signalPassFailure();
7165struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7166 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7167 : ExportVerilogPass{*os} {
7168 owned = std::move(os);
7172 std::unique_ptr<llvm::raw_ostream> owned;
7176std::unique_ptr<mlir::Pass>
7178 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7181std::unique_ptr<mlir::Pass>
7183 return std::make_unique<ExportVerilogPass>(os);
7194static std::unique_ptr<llvm::ToolOutputFile>
7198 SmallString<128> outputFilename(dirname);
7200 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7203 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7205 emitter.
designOp.emitError(
"cannot create output directory \"")
7206 << outputDir <<
"\": " << error.message();
7212 std::string errorMessage;
7213 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7215 emitter.
designOp.emitError(errorMessage);
7232 llvm::formatted_raw_ostream rs(output->os());
7238 StringAttr::get(fileName.getContext(), output->getFilename()),
7244 StringRef dirname) {
7255 bool insertSuccess =
7257 .insert({StringAttr::get(module.getContext(),
circtHeader),
7263 if (!insertSuccess) {
7264 module.emitError() << "tried to emit a heder to " << circtHeader
7265 << ", but the file is used as an output too.";
7271 parallelForEach(module->getContext(), emitter.
files.begin(),
7272 emitter.
files.end(), [&](
auto &it) {
7273 createSplitOutputFile(it.first, it.second, dirname,
7278 SmallString<128> filelistPath(dirname);
7279 llvm::sys::path::append(filelistPath,
"filelist.f");
7281 std::string errorMessage;
7282 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7284 module->emitError(errorMessage);
7288 for (
const auto &it : emitter.
files) {
7289 if (it.second.addToFilelist)
7290 output->os() << it.first.str() <<
"\n";
7299 for (
auto &name : it.second)
7300 output->os() << name.str() <<
"\n";
7311 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7313 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7314 if (failed(failableParallelForEach(
7315 module->getContext(), modulesToPrepare,
7316 [&](
auto op) { return prepareHWModule(op, options); })))
7324struct ExportSplitVerilogPass
7325 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7326 ExportSplitVerilogPass(StringRef directory) {
7327 directoryName = directory.str();
7329 void runOnOperation()
override {
7331 mlir::OpPassManager preparePM(
"builtin.module");
7335 modulePM.addPass(createPrepareForEmission());
7336 if (failed(runPipeline(preparePM, getOperation())))
7337 return signalPassFailure();
7340 return signalPassFailure();
7345std::unique_ptr<mlir::Pass>
7347 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static bool hasSVAttributes(Operation *op)
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc)
Push this type's dimension into a vector.
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static bool haveMatchingDims(Type a, Type b, Location loc)
True iff 'a' and 'b' have the same wire dims.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
This stores lookup tables to make manipulating and working with the IR more efficient.
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=~0ULL)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
This helps visit TypeOp nodes.
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module)
Generates the macros used by instance choices.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
PP
Send one of these to TokenStream to add the corresponding token.
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoices()
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
Information to control the emission of a single operation into a file.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
const LoweringOptions & options
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.