42#include "mlir/IR/BuiltinOps.h"
43#include "mlir/IR/ImplicitLocOpBuilder.h"
44#include "mlir/IR/Location.h"
45#include "mlir/IR/Threading.h"
46#include "mlir/Interfaces/FunctionImplementation.h"
47#include "mlir/Pass/PassManager.h"
48#include "mlir/Support/FileUtilities.h"
49#include "llvm/ADT/MapVector.h"
50#include "llvm/ADT/STLExtras.h"
51#include "llvm/ADT/StringSet.h"
52#include "llvm/ADT/TypeSwitch.h"
53#include "llvm/Support/FileSystem.h"
54#include "llvm/Support/FormattedStream.h"
55#include "llvm/Support/Path.h"
56#include "llvm/Support/SaveAndRestore.h"
57#include "llvm/Support/ToolOutputFile.h"
58#include "llvm/Support/raw_ostream.h"
61#define GEN_PASS_DEF_EXPORTSPLITVERILOG
62#define GEN_PASS_DEF_EXPORTVERILOG
63#include "circt/Conversion/Passes.h.inc"
70using namespace ExportVerilog;
72using namespace pretty;
74#define DEBUG_TYPE "export-verilog"
82enum VerilogPrecedence {
103enum SubExprSignResult { IsSigned, IsUnsigned };
109 VerilogPrecedence precedence;
112 SubExprSignResult signedness;
114 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
115 : precedence(precedence), signedness(signedness) {}
125 return Builder(ctx).getI32IntegerAttr(value);
128static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
129 return Builder(ctx).getIntegerAttr(t, value);
145 if (isa<VerbatimExprOp>(op)) {
146 if (op->getNumOperands() == 0 &&
147 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
152 if (isa<XMRRefOp>(op))
156 if (isa<MacroRefExprOp>(op))
166 if (op->getNumOperands() == 0)
170 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
174 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
175 auto *indexOp = array.getIndex().getDefiningOp();
176 if (!indexOp || isa<ConstantOp>(indexOp))
178 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
179 auto *readSrc = read.getInput().getDefiningOp();
181 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
196 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
197 return attr.getValue();
198 return TypeSwitch<Operation *, StringRef>(symOp)
203 return op.getVerilogNameAttr().getValue();
205 .Case<InterfaceOp>([&](InterfaceOp op) {
208 .Case<InterfaceSignalOp>(
209 [&](InterfaceSignalOp op) {
return op.getSymName(); })
210 .Case<InterfaceModportOp>(
211 [&](InterfaceModportOp op) {
return op.getSymName(); })
212 .Default([&](Operation *op) {
213 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
214 return attr.getValue();
215 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
216 return attr.getValue();
217 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
218 return attr.getValue();
220 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
221 return attr.getValue();
222 return StringRef(
"");
227template <
typename PPS>
229 os <<
"/*Zero width*/ 1\'b0";
234 auto hml = cast<HWModuleLike>(module);
235 return hml.getPort(portArgNum).getVerilogName();
240 auto hml = cast<HWModuleLike>(module);
241 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
242 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
243 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
244 return updatedName.getValue();
245 return hml.getHWModuleType().getPortName(pId);
254 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
255 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
256 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
257 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
258 UnpackedOpenArrayCastOp>(op))
262 if (isa<verif::ContractOp>(op))
273 SmallVectorImpl<Attribute> &dims, Type type, Location loc,
274 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
275 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
276 if (integer.getWidth() != 1)
277 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
280 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
281 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
282 getTypeDims(dims, array.getElementType(), loc, errorHandler);
286 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
287 dims.push_back(intType.getWidth());
291 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
292 return getTypeDims(dims, inout.getElementType(), loc, errorHandler);
293 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
294 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
295 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
296 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
297 if (hw::type_isa<InterfaceType, StructType, EnumType, UnionType>(type))
300 errorHandler(loc) <<
"value has an unsupported verilog type " << type;
306 Type a, Type b, Location loc,
307 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
308 SmallVector<Attribute, 4> aDims;
311 SmallVector<Attribute, 4> bDims;
314 return aDims == bDims;
320 if (
auto intType = dyn_cast<IntegerType>(type))
321 return intType.getWidth() == 0;
322 if (
auto inout = dyn_cast<hw::InOutType>(type))
324 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
325 return uarray.getNumElements() == 0 ||
327 if (
auto array = dyn_cast<hw::ArrayType>(type))
328 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
329 if (
auto structType = dyn_cast<hw::StructType>(type))
330 return llvm::all_of(structType.getElements(),
331 [](
auto elem) { return isZeroBitType(elem.type); });
332 if (
auto enumType = dyn_cast<hw::EnumType>(type))
333 return enumType.getFields().empty();
334 if (
auto unionType = dyn_cast<hw::UnionType>(type))
335 return hw::getBitWidth(unionType) == 0;
347 return TypeSwitch<Type, Type>(type)
348 .Case<InOutType>([](InOutType inoutType) {
351 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
354 .Default([](Type type) {
return type; });
359 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
360 auto elementType = cast<hw::InOutType>(type).getElementType();
366 return TypeSwitch<Type, bool>(type)
367 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
370 .Case<StructType>([](
auto) {
return true; })
371 .Default([](
auto) {
return false; });
385 if (
auto name = lhs.getName().compare(rhs.getName()))
387 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
392 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
394 if (lhs.getLine() != rhs.getLine())
395 return lhs.getLine() < rhs.getLine() ? -1 : 1;
396 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
401 Location lhsCallee = lhs.getCallee();
402 Location rhsCallee = rhs.getCallee();
406 Location lhsCaller = lhs.getCaller();
407 Location rhsCaller = rhs.getCaller();
411template <
typename TTargetLoc>
413 auto lhsT = dyn_cast<TTargetLoc>(lhs);
414 auto rhsT = dyn_cast<TTargetLoc>(rhs);
441 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
446 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
451 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
468 SmallPtrSetImpl<Attribute> &locationSet) {
469 llvm::TypeSwitch<Location, void>(loc)
470 .Case<FusedLoc>([&](
auto fusedLoc) {
471 for (
auto subLoc : fusedLoc.getLocations())
474 .Default([&](
auto loc) { locationSet.insert(loc); });
478template <
typename TVector>
480 llvm::array_pod_sort(
481 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
482 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
490 SmallPtrSet<Attribute, 8> locationSet;
491 locationSet.insert(loc);
492 llvm::raw_string_ostream os(
output);
498 const SmallPtrSetImpl<Operation *> &ops) {
502 SmallPtrSet<Attribute, 8> locationSet;
505 llvm::raw_string_ostream os(
output);
514 const SmallPtrSetImpl<Attribute> &locationSet) {
515 if (style == LoweringOptions::LocationInfoStyle::None)
518 llvm::raw_string_ostream sstr(resstr);
520 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
524 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
525 "other styles must be already handled");
526 os <<
"@[" << resstr <<
"]";
535 const SmallPtrSetImpl<Attribute> &locationSet)
551 bool withName = !loc.getName().empty();
553 os <<
"'" << loc.getName().strref() <<
"'(";
562 os << loc.getFilename().getValue();
563 if (
auto line = loc.getLine()) {
565 if (
auto col = loc.getColumn())
577 StringRef lastFileName;
578 for (
size_t i = 0, e = locVector.size(); i != e;) {
583 auto first = locVector[i];
584 if (first.getFilename() != lastFileName) {
585 lastFileName = first.getFilename();
592 first.getFilename() == locVector[
end].getFilename() &&
593 first.getLine() == locVector[
end].getLine())
598 if (
auto line = first.getLine()) {
600 if (
auto col = first.getColumn())
608 os <<
':' << first.getLine() <<
":{";
610 os << locVector[i++].getColumn();
622 llvm::TypeSwitch<Location, void>(loc)
623 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
625 .Case<mlir::FusedLoc>([&](
auto loc) {
626 SmallPtrSet<Attribute, 8> locationSet;
630 .Default([&](
auto loc) {
642 switch (locationSet.size()) {
653 SmallVector<FileLineColLoc, 8> flcLocs;
654 SmallVector<Attribute, 8> otherLocs;
655 flcLocs.reserve(locationSet.size());
656 otherLocs.reserve(locationSet.size());
657 for (Attribute loc : locationSet) {
658 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
659 flcLocs.push_back(flcLoc);
661 otherLocs.push_back(loc);
672 size_t sstrSize =
os.tell();
673 bool emittedAnything =
false;
674 auto recheckEmittedSomething = [&]() {
675 size_t currSize =
os.tell();
676 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
677 emittedAnything |= emittedSomethingSinceLastCheck;
679 return emittedSomethingSinceLastCheck;
688 if (recheckEmittedSomething()) {
690 recheckEmittedSomething();
696 if (emittedAnything && !flcLocs.empty())
701 llvm::raw_string_ostream &
os;
713 if (isa<BlockArgument>(v))
722 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
727 if (v.getDefiningOp<ReadInterfaceSignalOp>())
740 if (
auto cast = dyn_cast<BitcastOp>(op))
741 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
743 [&](Location loc) { return emitError(loc); })) {
746 if (op->hasOneUse() &&
747 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
755 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
761 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
765 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
766 if (verbatim.getFormatString().size() > 32)
771 for (
auto &use : op->getUses()) {
772 auto *user = use.getOwner();
782 StructInjectOp, StructExplodeOp, UnionExtractOp,
783 IndexedPartSelectOp>(user))
784 if (use.getOperandNumber() == 0 &&
795 auto usedInExprControl = [user, &use]() {
796 return TypeSwitch<Operation *, bool>(user)
797 .Case<ltl::ClockOp>([&](
auto clockOp) {
799 return clockOp.getClock() == use.get();
801 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
802 sv::CoverConcurrentOp>(
803 [&](
auto op) {
return op.getClock() == use.get(); })
804 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
805 sv::CoverPropertyOp>([&](
auto op) {
806 return op.getDisable() == use.get() || op.getClock() == use.get();
808 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
813 .Default([](
auto) {
return false; });
816 if (!usedInExprControl())
820 auto read = dyn_cast<ReadInOutOp>(op);
823 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
834 unsigned numStatements = 0;
835 block.walk([&](Operation *op) {
837 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
838 return WalkResult::advance();
840 TypeSwitch<Operation *, unsigned>(op)
841 .Case<VerbatimOp>([&](
auto) {
847 .Case<IfOp>([&](
auto) {
858 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
859 .Case<OutputOp>([&](OutputOp oop) {
862 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
863 Operation *op = operand.getDefiningOp();
864 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
867 .Default([](
auto) {
return 1; });
868 if (numStatements > 1)
869 return WalkResult::interrupt();
870 return WalkResult::advance();
872 if (numStatements == 0)
874 if (numStatements == 1)
884 if (op->getResult(0).use_empty())
889 if (op->hasOneUse() &&
890 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
891 *op->getUsers().begin()))
913 for (
auto &op : *elseBlock) {
914 if (
auto opIf = dyn_cast<IfOp>(op)) {
931template <
typename PPS>
933 enum Container { NoContainer, InComment, InAttr };
934 Container currentContainer = NoContainer;
936 auto closeContainer = [&] {
937 if (currentContainer == NoContainer)
939 if (currentContainer == InComment)
941 else if (currentContainer == InAttr)
943 ps << PP::end << PP::end;
945 currentContainer = NoContainer;
948 bool isFirstContainer =
true;
949 auto openContainer = [&](Container newContainer) {
950 assert(newContainer != NoContainer);
951 if (currentContainer == newContainer)
955 if (!isFirstContainer)
956 ps << (mayBreak ? PP::space : PP::nbsp);
957 isFirstContainer =
false;
960 if (newContainer == InComment)
962 else if (newContainer == InAttr)
964 currentContainer = newContainer;
972 ps.scopedBox(PP::cbox0, [&]() {
973 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
974 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
976 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
978 if (attr.getExpression())
979 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
988 if (
auto *op = val.getDefiningOp())
991 if (
auto port = dyn_cast<BlockArgument>(val)) {
993 auto parent = port.getParentBlock()->getParentOp();
994 if (isa<ForOp, GenerateForOp>(parent))
995 return parent->getAttrOfType<StringAttr>(
"hw.verilogName");
997 port.getArgNumber());
999 assert(
false &&
"unhandled value");
1011class VerilogEmitterState {
1013 explicit VerilogEmitterState(ModuleOp designOp,
1019 llvm::formatted_raw_ostream &os,
1020 StringAttr fileName,
OpLocMap &verilogLocMap)
1021 : designOp(designOp), shared(shared), options(options),
1022 symbolCache(symbolCache), globalNames(globalNames),
1023 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1024 pp(os, options.emittedLineLength), fileName(fileName) {
1025 pp.setListener(&saver);
1048 llvm::formatted_raw_ostream &os;
1050 bool encounteredError =
false;
1059 bool pendingNewline =
false;
1073 StringAttr fileName;
1079 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1082 verilogLocMap.
clear();
1086 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1087 void operator=(
const VerilogEmitterState &) =
delete;
1100using CallbackDataTy = std::pair<Operation *, bool>;
1104 VerilogEmitterState &state;
1109 explicit EmitterBase(VerilogEmitterState &state)
1111 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1113 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1114 state.encounteredError =
true;
1115 return op->emitError(message);
1118 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1119 state.encounteredError =
true;
1120 return op->emitOpError(message);
1123 InFlightDiagnostic emitError(Location loc,
const Twine &message =
"") {
1124 state.encounteredError =
true;
1125 return mlir::emitError(loc, message);
1128 void emitLocationImpl(llvm::StringRef location) {
1131 ps << PP::neverbreak;
1132 if (!location.empty())
1133 ps <<
"\t// " << location;
1136 void emitLocationInfo(Location loc) {
1144 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1147 setPendingNewline();
1150 template <
typename PPS>
1151 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1152 llvm::function_ref<
void(Value)> operandEmitter,
1153 ArrayAttr symAttrs);
1159 void emitComment(StringAttr comment);
1163 void emitPendingNewlineIfNeeded() {
1164 if (state.pendingNewline) {
1165 state.pendingNewline =
false;
1169 void setPendingNewline() {
1170 assert(!state.pendingNewline);
1171 state.pendingNewline =
true;
1174 void startStatement() { emitPendingNewlineIfNeeded(); }
1177 void operator=(
const EmitterBase &) =
delete;
1178 EmitterBase(
const EmitterBase &) =
delete;
1182template <
typename PPS>
1183void EmitterBase::emitTextWithSubstitutions(
1184 PPS &ps, StringRef
string, Operation *op,
1185 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1196 if (
auto *itemOp = item.getOp()) {
1197 if (item.hasPort()) {
1201 if (!symOpName.empty())
1203 emitError(itemOp,
"cannot get name for symbol ") << sym;
1205 emitError(op,
"cannot get name for symbol ") << sym;
1207 return StringRef(
"<INVALID>");
1213 unsigned numSymOps = symAttrs.size();
1214 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1217 next =
string.find(
"{{", next);
1218 if (next == StringRef::npos)
1225 while (next <
string.size() &&
isdigit(
string[next]))
1228 if (start == next) {
1232 size_t operandNoLength = next - start;
1235 StringRef fmtOptsStr;
1236 if (
string[next] ==
':') {
1237 size_t startFmtOpts = next + 1;
1238 while (next <
string.size() &&
string[next] !=
'}')
1240 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1244 if (!
string.substr(next).starts_with(
"}}"))
1248 unsigned operandNo = 0;
1249 if (
string.drop_front(start)
1250 .take_front(operandNoLength)
1251 .getAsInteger(10, operandNo)) {
1252 emitError(op,
"operand substitution too large");
1258 auto before =
string.take_front(start - 2);
1259 if (!before.empty())
1264 if (operandNo < op->getNumOperands())
1266 operandEmitter(op->getOperand(operandNo));
1267 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1268 unsigned symOpNum = operandNo - op->getNumOperands();
1269 auto sym = symAttrs[symOpNum];
1270 StringRef symVerilogName;
1271 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1272 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1273 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1274 auto namepath = globalRef.getNamepathAttr().getValue();
1275 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1278 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1280 auto innerRef = cast<InnerRefAttr>(sym);
1281 auto ref = state.symbolCache.getInnerDefinition(
1282 innerRef.getModule(), innerRef.getName());
1283 ps << namify(innerRef, ref);
1286 symVerilogName = namify(sym, symOp);
1289 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1290 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1292 symVerilogName = namify(sym, symOp);
1294 if (!symVerilogName.empty())
1297 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1301 string =
string.drop_front(next);
1307 while (emitUntilSubstitution())
1311 if (!
string.
empty())
1315void EmitterBase::emitComment(StringAttr comment) {
1322 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1326 auto ref = comment.getValue();
1328 while (!ref.empty()) {
1329 std::tie(line, ref) = ref.split(
"\n");
1336 if (line.size() <= lineLength) {
1338 setPendingNewline();
1349 auto breakPos = line.rfind(
' ', lineLength);
1351 if (breakPos == StringRef::npos) {
1352 breakPos = line.find(
' ', lineLength);
1355 if (breakPos == StringRef::npos)
1356 breakPos = line.size();
1363 setPendingNewline();
1364 breakPos = line.find_first_not_of(
' ', breakPos);
1366 if (breakPos == StringRef::npos)
1369 line = line.drop_front(breakPos);
1379 bool addPrefixUnderScore =
true;
1382 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1386 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1388 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1390 result = StringAttr::get(expr.getContext(), name);
1392 }
else if (
auto *op = expr.getDefiningOp()) {
1394 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1396 result = StringAttr::get(expr.getContext(), name);
1398 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1404 addPrefixUnderScore =
false;
1406 TypeSwitch<Operation *>(op)
1409 .Case([&result](VerbatimExprOp verbatim) {
1410 verbatim.getAsmResultNames([&](Value, StringRef name) {
1411 result = StringAttr::get(verbatim.getContext(), name);
1414 .Case([&result](VerbatimExprSEOp verbatim) {
1415 verbatim.getAsmResultNames([&](Value, StringRef name) {
1416 result = StringAttr::get(verbatim.getContext(), name);
1422 if (
auto operandName =
1425 cast<IntegerType>(extract.getType()).getWidth();
1427 result = StringAttr::get(extract.getContext(),
1428 operandName.strref() +
"_" +
1429 Twine(extract.getLowBit()));
1431 result = StringAttr::get(
1432 extract.getContext(),
1433 operandName.strref() +
"_" +
1434 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1435 Twine(extract.getLowBit()));
1443 if (!result || result.strref().empty())
1447 if (addPrefixUnderScore && result.strref().front() !=
'_')
1448 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1460class ModuleEmitter :
public EmitterBase {
1462 explicit ModuleEmitter(VerilogEmitterState &state)
1463 : EmitterBase(state), currentModuleOp(nullptr),
1467 emitPendingNewlineIfNeeded();
1471 void emitParameters(Operation *module, ArrayAttr params);
1472 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1473 bool emitAsTwoStateType =
false);
1476 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1477 void emitFunc(FuncOp);
1480 void emitStatement(Operation *op);
1481 void emitBind(BindOp op);
1482 void emitBindInterface(BindInterfaceOp op);
1484 void emitSVAttributes(Operation *op);
1487 StringRef getVerilogStructFieldName(StringAttr field) {
1488 return fieldNameResolver.getRenamedFieldName(field).getValue();
1495 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1507 bool printPackedType(Type type, raw_ostream &os, Location loc,
1508 Type optionalAliasType = {},
bool implicitIntType =
true,
1509 bool singleBitDefaultType =
true,
1510 bool emitAsTwoStateType =
false);
1514 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1522 function_ref<InFlightDiagnostic()> emitError);
1525 VerilogPrecedence parenthesizeIfLooserThan,
1526 function_ref<InFlightDiagnostic()> emitError);
1532 Operation *currentModuleOp;
1538 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1544 SmallPtrSet<Operation *, 16> assignsInlined;
1553 const ModuleEmitter &emitter) {
1554 if (isa<RegOp>(op)) {
1559 cast<InOutType>(op->getResult(0).getType()).getElementType();
1562 while (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(
elementType))
1564 while (
auto arrayType = hw::type_dyn_cast<ArrayType>(
elementType))
1567 if (isa<StructType, UnionType, EnumType, TypeAliasType>(
elementType))
1572 if (isa<sv::WireOp>(op))
1574 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1575 return "localparam";
1578 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1579 return interface.getInterfaceType().getInterface().getValue();
1587 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1589 if (isa<LogicOp>(op)) {
1595 if (isProcedural && !stripAutomatic)
1596 return hasStruct ?
"automatic" :
"automatic logic";
1597 return hasStruct ?
"" :
"logic";
1604 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1607 assert(!emitter.state.options.disallowLocalVariables &&
1608 "automatic variables not allowed");
1612 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1613 :
"automatic logic";
1620static void emitDim(Attribute width, raw_ostream &os, Location loc,
1621 ModuleEmitter &emitter,
bool downTo) {
1623 os <<
"<<invalid type>>";
1626 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1627 if (intAttr.getValue().isZero()) {
1628 os <<
"/*Zero Width*/";
1633 os << (intAttr.getValue().getZExtValue() - 1);
1643 auto typedAttr = dyn_cast<TypedAttr>(width);
1645 emitter.emitError(loc,
"untyped dimension attribute ") << width;
1649 getIntAttr(loc.getContext(), typedAttr.getType(),
1650 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1651 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1655 emitter.printParamValue(width, os, [loc, &emitter]() {
1656 return emitter.emitError(loc,
"invalid parameter in type");
1664static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1665 ModuleEmitter &emitter) {
1666 for (Attribute width : dims) {
1667 emitDim(width, os, loc, emitter,
true);
1672void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1673 SmallVector<Attribute, 4> dims;
1675 [&](Location loc) {
return this->emitError(loc); });
1706 SmallVectorImpl<Attribute> &dims,
1707 bool implicitIntType,
bool singleBitDefaultType,
1708 ModuleEmitter &emitter,
1709 Type optionalAliasType = {},
1710 bool emitAsTwoStateType =
false) {
1711 return TypeSwitch<Type, bool>(type)
1712 .Case<IntegerType>([&](IntegerType integerType) ->
bool {
1713 if (emitAsTwoStateType && dims.empty()) {
1715 if (!typeName.empty()) {
1720 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1722 getInt32Attr(type.getContext(), integerType.getWidth()));
1724 StringRef typeName =
1725 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1726 if (!typeName.empty()) {
1733 return !dims.empty() || !implicitIntType;
1735 .Case<IntType>([&](IntType intType) {
1736 if (!implicitIntType)
1738 dims.push_back(intType.getWidth());
1742 .Case<ArrayType>([&](ArrayType arrayType) {
1743 dims.push_back(arrayType.getSizeAttr());
1745 implicitIntType, singleBitDefaultType,
1747 emitAsTwoStateType);
1749 .Case<InOutType>([&](InOutType inoutType) {
1751 implicitIntType, singleBitDefaultType,
1753 emitAsTwoStateType);
1755 .Case<EnumType>([&](EnumType enumType) {
1756 assert(enumType.getBitWidth().has_value() &&
1757 "enum type must have bitwidth");
1759 if (enumType.getBitWidth() != 32)
1760 os <<
"bit [" << *enumType.getBitWidth() - 1 <<
":0] ";
1762 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1763 llvm::interleaveComma(
1764 enumType.getFields().getAsRange<StringAttr>(), os,
1765 [&](
auto enumerator) {
1766 os << emitter.fieldNameResolver.getEnumFieldName(
1767 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1772 .Case<StructType>([&](StructType structType) {
1773 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1774 os <<
"/*Zero Width*/";
1777 os <<
"struct packed {";
1778 for (
auto &element : structType.getElements()) {
1780 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1781 <<
": Zero Width;*/ ";
1784 SmallVector<Attribute, 8> structDims;
1789 {}, emitAsTwoStateType);
1790 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1791 emitter.printUnpackedTypePostfix(element.type, os);
1798 .Case<UnionType>([&](UnionType unionType) {
1799 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1800 os <<
"/*Zero Width*/";
1804 int64_t unionWidth = hw::getBitWidth(unionType);
1805 os <<
"union packed {";
1806 for (
auto &element : unionType.getElements()) {
1808 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1809 <<
": Zero Width;*/ ";
1812 int64_t elementWidth = hw::getBitWidth(element.type);
1813 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1815 os <<
" struct packed {";
1816 if (element.offset) {
1817 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1818 << element.offset - 1 <<
":0] "
1819 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1823 SmallVector<Attribute, 8> structDims;
1827 true, emitter, {}, emitAsTwoStateType);
1828 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1829 emitter.printUnpackedTypePostfix(element.type, os);
1833 if (elementWidth + (int64_t)element.offset < unionWidth) {
1834 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1835 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1836 <<
"__post_padding_" << element.name.getValue() <<
";";
1838 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1847 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1848 .Case<ModportType>([&](ModportType modportType) {
1849 auto modportAttr = modportType.getModport();
1850 os << modportAttr.getRootReference().getValue() <<
"."
1851 << modportAttr.getNestedReferences().front().getValue();
1854 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1855 os <<
"<<unexpected unpacked array>>";
1856 emitter.emitError(loc,
"Unexpected unpacked array in packed type ")
1860 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1861 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1863 emitter.emitError(loc,
"unresolvable type reference");
1866 if (typedecl.getType() != typeRef.getInnerType()) {
1867 emitter.emitError(loc,
"declared type did not match aliased type");
1871 os << typedecl.getPreferredName();
1872 emitDims(dims, os, typedecl->getLoc(), emitter);
1875 .Default([&](Type type) {
1876 os <<
"<<invalid type '" << type <<
"'>>";
1877 emitter.emitError(loc,
"value has an unsupported verilog type ")
1894bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1895 Type optionalAliasType,
1896 bool implicitIntType,
1897 bool singleBitDefaultType,
1898 bool emitAsTwoStateType) {
1899 SmallVector<Attribute, 8> packedDimensions;
1901 singleBitDefaultType, *
this, optionalAliasType,
1902 emitAsTwoStateType);
1908void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1909 TypeSwitch<Type, void>(type)
1911 printUnpackedTypePostfix(inoutType.getElementType(), os);
1913 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1914 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1915 : state.designOp->getLoc();
1916 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1918 printUnpackedTypePostfix(arrayType.getElementType(), os);
1920 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1922 printUnpackedTypePostfix(arrayType.getElementType(), os);
1924 .Case<InterfaceType>([&](
auto) {
1938ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1939 function_ref<InFlightDiagnostic()> emitError) {
1940 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1948ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1949 VerilogPrecedence parenthesizeIfLooserThan,
1950 function_ref<InFlightDiagnostic()> emitError) {
1951 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1952 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1953 APInt value = intAttr.getValue();
1957 if (intTy.getWidth() > 32) {
1959 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1963 if (intTy.isSigned())
1964 os << intTy.getWidth() <<
"'sd";
1966 os << intTy.getWidth() <<
"'d";
1968 value.print(os, intTy.isSigned());
1969 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1971 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1973 os.write_escaped(strAttr.getValue());
1975 return {Symbol, IsUnsigned};
1977 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1979 os << fpAttr.getValueAsDouble();
1980 return {Symbol, IsUnsigned};
1982 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1983 os << verbatimParam.getValue().getValue();
1984 return {Symbol, IsUnsigned};
1986 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1988 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1989 parameterRef.getName());
1992 return {Symbol, IsUnsigned};
1996 auto expr = dyn_cast<ParamExprAttr>(value);
1998 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1999 emitError() <<
" = " << value;
2000 return {LowestPrecedence, IsUnsigned};
2003 StringRef operatorStr;
2004 StringRef openStr, closeStr;
2005 VerilogPrecedence subprecedence = LowestPrecedence;
2006 VerilogPrecedence prec;
2007 std::optional<SubExprSignResult> operandSign;
2008 bool isUnary =
false;
2009 bool hasOpenClose =
false;
2011 switch (expr.getOpcode()) {
2013 operatorStr =
" + ";
2014 subprecedence = Addition;
2017 operatorStr =
" * ";
2018 subprecedence = Multiply;
2021 operatorStr =
" & ";
2022 subprecedence = And;
2025 operatorStr =
" | ";
2029 operatorStr =
" ^ ";
2030 subprecedence = Xor;
2033 operatorStr =
" << ";
2034 subprecedence = Shift;
2038 operatorStr =
" >> ";
2039 subprecedence = Shift;
2043 operatorStr =
" >>> ";
2044 subprecedence = Shift;
2045 operandSign = IsSigned;
2048 operatorStr =
" / ";
2049 subprecedence = Multiply;
2050 operandSign = IsUnsigned;
2053 operatorStr =
" / ";
2054 subprecedence = Multiply;
2055 operandSign = IsSigned;
2058 operatorStr =
" % ";
2059 subprecedence = Multiply;
2060 operandSign = IsUnsigned;
2063 operatorStr =
" % ";
2064 subprecedence = Multiply;
2065 operandSign = IsSigned;
2068 openStr =
"$clog2(";
2070 operandSign = IsUnsigned;
2071 hasOpenClose =
true;
2074 case PEO::StrConcat:
2077 hasOpenClose =
true;
2080 subprecedence = LowestPrecedence;
2085 prec = subprecedence;
2088 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2090 assert(isUnary || hasOpenClose ||
2091 !llvm::hasSingleElement(expr.getOperands()));
2098 auto emitOperand = [&](Attribute operand) ->
bool {
2100 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2101 if (operandSign.has_value())
2102 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2105 if (operandSign.has_value()) {
2107 signedness = *operandSign;
2109 return signedness == IsSigned;
2113 if (prec > parenthesizeIfLooserThan)
2122 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2123 for (
auto op : expr.getOperands().drop_front()) {
2126 if (expr.getOpcode() == PEO::Add) {
2127 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2128 const APInt &value = integer.getValue();
2129 if (value.isNegative() && !value.isMinSignedValue()) {
2131 allOperandsSigned &=
2132 emitOperand(IntegerAttr::get(op.getType(), -value));
2139 allOperandsSigned &= emitOperand(op);
2143 if (prec > parenthesizeIfLooserThan) {
2147 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2162class ExprEmitter :
public EmitterBase,
2164 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2169 ExprEmitter(ModuleEmitter &emitter,
2170 SmallPtrSetImpl<Operation *> &emittedExprs)
2171 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2173 ExprEmitter(ModuleEmitter &emitter,
2174 SmallPtrSetImpl<Operation *> &emittedExprs,
2176 : EmitterBase(emitter.state), emitter(emitter),
2177 emittedExprs(emittedExprs), buffer(tokens),
2178 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2179 assert(state.pp.getListener() == &state.saver);
2186 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2187 bool isAssignmentLikeContext) {
2188 assert(localTokens.empty());
2190 ps.scopedBox(PP::ibox0, [&]() {
2193 emitSubExpr(exp, parenthesizeIfLooserThan,
2195 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2197 isAssignmentLikeContext);
2202 if (&buffer.tokens == &localTokens)
2203 buffer.flush(state.pp);
2208 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2209 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2211 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2219 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2220 SubExprSignRequirement signReq = NoRequirement,
2221 bool isSelfDeterminedUnsignedValue =
false,
2222 bool isAssignmentLikeContext =
false);
2226 void emitSVAttributes(Operation *op);
2228 SubExprInfo visitUnhandledExpr(Operation *op);
2229 SubExprInfo visitInvalidComb(Operation *op) {
2232 SubExprInfo visitUnhandledComb(Operation *op) {
2233 return visitUnhandledExpr(op);
2236 return dispatchSVVisitor(op);
2239 return visitUnhandledExpr(op);
2241 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2244 enum EmitBinaryFlags {
2245 EB_RequireSignedOperands = RequireSigned,
2246 EB_RequireUnsignedOperands = RequireUnsigned,
2247 EB_OperandSignRequirementMask = 0x3,
2252 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2256 EB_ForceResultSigned = 0x8,
2261 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2262 const char *syntax,
unsigned emitBinaryFlags = 0);
2264 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2265 bool resultAlwaysUnsigned =
false);
2268 void emitSubExprIBox2(
2269 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2270 ps.scopedBox(PP::ibox2,
2271 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2276 template <
typename Container,
typename EachFn>
2277 void interleaveComma(
const Container &c, EachFn eachFn) {
2278 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2283 void interleaveComma(ValueRange ops) {
2284 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2301 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2303 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2304 CloseFunc closeFn) {
2306 ps.scopedBox(PP::cbox0, [&]() {
2307 interleaveComma(c, eachFn);
2313 template <
typename OpenFunc,
typename CloseFunc>
2314 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2315 return emitBracedList(
2316 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2320 void emitBracedList(ValueRange ops) {
2321 return emitBracedList(
2322 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2326 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2329 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2330 bool printAsPattern, Operation *op);
2332 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2333 ArrayAttr fieldValues,
bool printAsPattern,
2336 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2338 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2339 SubExprInfo visitSV(GetModportOp op);
2340 SubExprInfo visitSV(SystemFunctionOp op);
2341 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2342 SubExprInfo visitSV(XMROp op);
2343 SubExprInfo visitSV(SFormatFOp op);
2344 SubExprInfo visitSV(XMRRefOp op);
2345 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2346 SubExprInfo visitSV(VerbatimExprOp op) {
2347 return visitVerbatimExprOp(op, op.getSymbols());
2349 SubExprInfo visitSV(VerbatimExprSEOp op) {
2350 return visitVerbatimExprOp(op, op.getSymbols());
2352 SubExprInfo visitSV(MacroRefExprOp op);
2353 SubExprInfo visitSV(MacroRefExprSEOp op);
2354 template <
typename MacroTy>
2355 SubExprInfo emitMacroCall(MacroTy op);
2357 SubExprInfo visitSV(ConstantXOp op);
2358 SubExprInfo visitSV(ConstantZOp op);
2359 SubExprInfo visitSV(ConstantStrOp op);
2361 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2362 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2364 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2369 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2370 emitSVAttributes(op);
2373 SubExprInfo visitSV(ArrayIndexInOutOp op);
2374 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2375 SubExprInfo visitSV(IndexedPartSelectOp op);
2376 SubExprInfo visitSV(StructFieldInOutOp op);
2379 SubExprInfo visitSV(SampledOp op);
2382 SubExprInfo visitSV(TimeOp op);
2383 SubExprInfo visitSV(STimeOp op);
2386 using TypeOpVisitor::visitTypeOp;
2388 SubExprInfo visitTypeOp(AggregateConstantOp op);
2390 SubExprInfo visitTypeOp(ParamValueOp op);
2397 SubExprInfo visitTypeOp(StructInjectOp op);
2398 SubExprInfo visitTypeOp(UnionCreateOp op);
2399 SubExprInfo visitTypeOp(UnionExtractOp op);
2400 SubExprInfo visitTypeOp(EnumCmpOp op);
2401 SubExprInfo visitTypeOp(EnumConstantOp op);
2404 using CombinationalVisitor::visitComb;
2405 SubExprInfo visitComb(
MuxOp op);
2406 SubExprInfo visitComb(ReverseOp op);
2407 SubExprInfo visitComb(
AddOp op) {
2408 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2409 return emitBinary(op, Addition,
"+");
2411 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2412 SubExprInfo visitComb(
MulOp op) {
2413 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2414 return emitBinary(op, Multiply,
"*");
2416 SubExprInfo visitComb(
DivUOp op) {
2417 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2419 SubExprInfo visitComb(
DivSOp op) {
2420 return emitBinary(op, Multiply,
"/",
2421 EB_RequireSignedOperands | EB_ForceResultSigned);
2423 SubExprInfo visitComb(
ModUOp op) {
2424 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2426 SubExprInfo visitComb(
ModSOp op) {
2427 return emitBinary(op, Multiply,
"%",
2428 EB_RequireSignedOperands | EB_ForceResultSigned);
2430 SubExprInfo visitComb(
ShlOp op) {
2431 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2433 SubExprInfo visitComb(
ShrUOp op) {
2435 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2437 SubExprInfo visitComb(
ShrSOp op) {
2440 return emitBinary(op, Shift,
">>>",
2441 EB_RequireSignedOperands | EB_ForceResultSigned |
2442 EB_RHS_UnsignedWithSelfDeterminedWidth);
2444 SubExprInfo visitComb(
AndOp op) {
2445 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2446 return emitBinary(op, And,
"&");
2448 SubExprInfo visitComb(
OrOp op) {
2449 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2450 return emitBinary(op, Or,
"|");
2452 SubExprInfo visitComb(
XorOp op) {
2453 if (op.isBinaryNot())
2454 return emitUnary(op,
"~");
2455 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2456 return emitBinary(op, Xor,
"^");
2461 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2463 SubExprInfo visitComb(ReplicateOp op);
2464 SubExprInfo visitComb(
ConcatOp op);
2466 SubExprInfo visitComb(ICmpOp op);
2468 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2469 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2470 "not printed within an assignment-like context");
2471 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2472 "supposed to spill such expressions";
2476 SubExprInfo printStructCreate(
2477 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2479 bool printAsPattern, Operation *op);
2482 ModuleEmitter &emitter;
2489 SubExprSignRequirement signPreference = NoRequirement;
2493 SmallPtrSetImpl<Operation *> &emittedExprs;
2496 SmallVector<Token> localTokens;
2510 bool isAssignmentLikeContext =
false;
2514SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2516 unsigned emitBinaryFlags) {
2518 emitError(op,
"SV attributes emission is unimplemented for the op");
2529 if (emitBinaryFlags & EB_ForceResultSigned)
2530 ps <<
"$signed(" << PP::ibox0;
2531 auto operandSignReq =
2532 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2533 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2535 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2537 ps << lhsSpace << syntax << PP::nbsp;
2544 auto rhsPrec = prec;
2545 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2546 rhsPrec = VerilogPrecedence(prec - 1);
2551 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2552 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2553 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2554 operandSignReq = NoRequirement;
2557 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2558 rhsIsUnsignedValueWithSelfDeterminedWidth);
2562 SubExprSignResult signedness = IsUnsigned;
2563 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2564 signedness = IsSigned;
2566 if (emitBinaryFlags & EB_ForceResultSigned) {
2567 ps << PP::end <<
")";
2568 signedness = IsSigned;
2572 return {prec, signedness};
2575SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2576 bool resultAlwaysUnsigned) {
2578 emitError(op,
"SV attributes emission is unimplemented for the op");
2581 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2585 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2586 resultAlwaysUnsigned ? IsUnsigned : signedness};
2591void ExprEmitter::emitSVAttributes(Operation *op) {
2605 auto concat = value.getDefiningOp<
ConcatOp>();
2606 if (!concat || concat.getNumOperands() != 2)
2609 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2610 if (constant && constant.getValue().isZero())
2611 return concat.getOperand(1);
2621SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2622 VerilogPrecedence parenthesizeIfLooserThan,
2623 SubExprSignRequirement signRequirement,
2624 bool isSelfDeterminedUnsignedValue,
2625 bool isAssignmentLikeContext) {
2627 if (
auto result = dyn_cast<OpResult>(exp))
2628 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2629 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2630 parenthesizeIfLooserThan, signRequirement,
2631 isSelfDeterminedUnsignedValue,
2632 isAssignmentLikeContext);
2636 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2641 auto *op = exp.getDefiningOp();
2645 if (!shouldEmitInlineExpr) {
2648 if (signRequirement == RequireSigned) {
2650 return {Symbol, IsSigned};
2654 return {Symbol, IsUnsigned};
2657 unsigned subExprStartIndex = buffer.tokens.size();
2659 ps.addCallback({op,
true});
2660 llvm::scope_exit done([&]() {
2662 ps.addCallback({op, false});
2668 signPreference = signRequirement;
2670 bool bitCastAdded =
false;
2671 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2673 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2674 ps.addAsString(inType.getWidth());
2675 ps <<
"'(" << PP::ibox0;
2676 bitCastAdded =
true;
2680 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2681 isAssignmentLikeContext);
2682 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2688 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2690 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2692 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2693 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2696 expInfo.signedness = IsSigned;
2697 expInfo.precedence = Selection;
2698 }
else if (signRequirement == RequireUnsigned &&
2699 expInfo.signedness == IsSigned) {
2702 expInfo.signedness = IsUnsigned;
2703 expInfo.precedence = Selection;
2704 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2711 expInfo.precedence = Selection;
2718 emittedExprs.insert(exp.getDefiningOp());
2722SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2723 auto openFn = [&]() {
2725 ps.addAsString(op.getMultiple());
2728 auto closeFn = [&]() { ps <<
"}}"; };
2732 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2733 if (op.getOperand().hasOneUse()) {
2734 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2735 return {Symbol, IsUnsigned};
2738 emitBracedList(op.getOperand(), openFn, closeFn);
2739 return {Symbol, IsUnsigned};
2742SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2743 emitBracedList(op.getOperands());
2744 return {Symbol, IsUnsigned};
2747SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2751 Type toType = op.getType();
2753 toType, op.getInput().getType(), op.getLoc(),
2754 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2756 ps.invokeWithStringOS(
2757 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2760 return emitSubExpr(op.getInput(), LowestPrecedence);
2763SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2764 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2765 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2766 SubExprSignRequirement signop[] = {
2768 NoRequirement, NoRequirement,
2770 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2772 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2774 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2776 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2777 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2780 if (op.isEqualAllOnes())
2781 return emitUnary(op,
"&",
true);
2784 if (op.isNotEqualZero())
2785 return emitUnary(op,
"|",
true);
2787 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2791 result.signedness = IsUnsigned;
2795SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2797 emitError(op,
"SV attributes emission is unimplemented for the op");
2799 unsigned loBit = op.getLowBit();
2800 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2802 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2803 assert((x.precedence == Symbol ||
2805 "should be handled by isExpressionUnableToInline");
2810 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2814 ps.addAsString(hiBit);
2815 if (hiBit != loBit) {
2817 ps.addAsString(loBit);
2820 return {Unary, IsUnsigned};
2823SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2825 emitError(op,
"SV attributes emission is unimplemented for the op");
2827 auto decl = op.getReferencedDecl(state.symbolCache);
2830 return {Selection, IsUnsigned};
2833SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2835 emitError(op,
"SV attributes emission is unimplemented for the op");
2838 ps.scopedBox(PP::ibox0, [&]() {
2840 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2841 [&]() { ps <<
"," << PP::space; });
2844 return {Symbol, IsUnsigned};
2847SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2849 emitError(op,
"SV attributes emission is unimplemented for the op");
2851 auto decl = op.getReferencedDecl(state.symbolCache);
2855 return {Selection, IsUnsigned};
2858SubExprInfo ExprEmitter::visitSV(XMROp op) {
2860 emitError(op,
"SV attributes emission is unimplemented for the op");
2862 if (op.getIsRooted())
2864 for (
auto s : op.getPath())
2865 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2867 return {Selection, IsUnsigned};
2872SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2874 emitError(op,
"SV attributes emission is unimplemented for the op");
2877 auto globalRef = op.getReferencedPath(&state.symbolCache);
2878 auto namepath = globalRef.getNamepathAttr().getValue();
2879 auto *
module = state.symbolCache.getDefinition(
2880 cast<InnerRefAttr>(namepath.front()).getModule());
2882 for (
auto sym : namepath) {
2884 auto innerRef = cast<InnerRefAttr>(sym);
2885 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2886 innerRef.getName());
2887 if (ref.hasPort()) {
2893 auto leaf = op.getVerbatimSuffixAttr();
2894 if (leaf && leaf.size())
2896 return {Selection, IsUnsigned};
2899SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2901 emitError(op,
"SV attributes emission is unimplemented for the op");
2903 emitTextWithSubstitutions(
2904 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2905 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2907 return {Unary, IsUnsigned};
2910template <
typename MacroTy>
2911SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2913 emitError(op,
"SV attributes emission is unimplemented for the op");
2916 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2917 assert(macroOp &&
"Invalid IR");
2919 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2921 if (!op.getInputs().empty()) {
2923 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2924 emitExpression(val, LowestPrecedence, false);
2928 return {LowestPrecedence, IsUnsigned};
2931SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2932 return emitMacroCall(op);
2935SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2936 return emitMacroCall(op);
2939SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2941 emitError(op,
"SV attributes emission is unimplemented for the op");
2943 ps.addAsString(op.getWidth());
2945 return {Unary, IsUnsigned};
2948SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2950 emitError(op,
"SV attributes emission is unimplemented for the op");
2952 ps.writeQuotedEscaped(op.getStr());
2953 return {Symbol, IsUnsigned};
2956SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2958 emitError(op,
"SV attributes emission is unimplemented for the op");
2960 ps.addAsString(op.getWidth());
2962 return {Unary, IsUnsigned};
2965SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2966 bool isNegated =
false;
2969 if (signPreference == RequireSigned && value.isNegative() &&
2970 !value.isMinSignedValue()) {
2975 ps.addAsString(type.getWidth());
2979 if (signPreference == RequireSigned)
2985 SmallString<32> valueStr;
2987 (-value).toStringUnsigned(valueStr, 16);
2989 value.toStringUnsigned(valueStr, 16);
2992 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2995SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2997 emitError(op,
"SV attributes emission is unimplemented for the op");
2999 auto value = op.getValue();
3003 if (value.getBitWidth() == 0) {
3004 emitOpError(op,
"will not emit zero width constants in the general case");
3005 ps <<
"<<unsupported zero width constant: "
3006 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3007 return {Unary, IsUnsigned};
3010 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3013void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3014 bool printAsPattern, Operation *op) {
3015 if (printAsPattern && !isAssignmentLikeContext)
3016 emitAssignmentPatternContextError(op);
3017 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3020 elementValues, [&]() { ps << openDelim; },
3021 [&](Attribute elementValue) {
3022 printConstantAggregate(elementValue,
elementType, op);
3024 [&]() { ps <<
"}"; });
3027void ExprEmitter::printConstantStruct(
3028 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3029 bool printAsPattern, Operation *op) {
3030 if (printAsPattern && !isAssignmentLikeContext)
3031 emitAssignmentPatternContextError(op);
3038 auto fieldRange = llvm::make_filter_range(
3039 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3044 if (printAsPattern) {
3046 fieldRange, [&]() { ps <<
"'{"; },
3047 [&](
const auto &fieldAndValue) {
3048 ps.scopedBox(PP::ibox2, [&]() {
3049 const auto &[field, value] = fieldAndValue;
3050 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3051 <<
":" << PP::space;
3052 printConstantAggregate(value, field.type, op);
3055 [&]() { ps <<
"}"; });
3058 fieldRange, [&]() { ps <<
"{"; },
3059 [&](
const auto &fieldAndValue) {
3060 ps.scopedBox(PP::ibox2, [&]() {
3061 const auto &[field, value] = fieldAndValue;
3062 printConstantAggregate(value, field.type, op);
3065 [&]() { ps <<
"}"; });
3069void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3072 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3073 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3074 isAssignmentLikeContext, op);
3077 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3078 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3082 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3083 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3084 isAssignmentLikeContext, op);
3086 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3087 auto value = cast<IntegerAttr>(attr).getValue();
3088 printConstantScalar(value, intType);
3092 emitOpError(op,
"contains constant of type ")
3093 << type <<
" which cannot be emitted as Verilog";
3096SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3098 emitError(op,
"SV attributes emission is unimplemented for the op");
3102 "zero-bit types not allowed at this point");
3104 printConstantAggregate(op.getFields(), op.getType(), op);
3105 return {Symbol, IsUnsigned};
3108SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3110 emitError(op,
"SV attributes emission is unimplemented for the op");
3112 return ps.invokeWithStringOS([&](
auto &os) {
3113 return emitter.printParamValue(op.getValue(), os, [&]() {
3114 return op->emitOpError(
"invalid parameter use");
3123 emitError(op,
"SV attributes emission is unimplemented for the op");
3125 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3127 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3129 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3131 ps.addAsString(dstWidth);
3133 return {Selection, arrayPrec.signedness};
3136SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3137 emitSubExpr(op.getInput(), Selection);
3142 emitSubExpr(op.getIndex(), LowestPrecedence);
3144 emitSVAttributes(op);
3145 return {Selection, IsUnsigned};
3151 emitError(op,
"SV attributes emission is unimplemented for the op");
3153 if (op.isUniform()) {
3155 ps.addAsString(op.getInputs().size());
3157 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3161 op.getInputs(), [&]() { ps <<
"{"; },
3164 emitSubExprIBox2(v);
3167 [&]() { ps <<
"}"; });
3169 return {Unary, IsUnsigned};
3172SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3174 emitError(op,
"SV attributes emission is unimplemented for the op");
3177 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3178 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3179 return {Unary, IsUnsigned};
3184 emitError(op,
"SV attributes emission is unimplemented for the op");
3186 emitBracedList(op.getOperands());
3187 return {Unary, IsUnsigned};
3190SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3192 emitError(op,
"SV attributes emission is unimplemented for the op");
3194 auto index = op.getIndex();
3195 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3200 emitSubExpr(index, LowestPrecedence);
3202 return {Selection, arrayPrec.signedness};
3205SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3207 emitError(op,
"SV attributes emission is unimplemented for the op");
3209 auto prec = emitSubExpr(op.getInput(), Selection);
3211 emitSubExpr(op.getBase(), LowestPrecedence);
3212 if (op.getDecrement())
3216 ps.addAsString(op.getWidth());
3218 return {Selection, prec.signedness};
3221SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3223 emitError(op,
"SV attributes emission is unimplemented for the op");
3225 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3227 emitSubExpr(op.getBase(), LowestPrecedence);
3228 if (op.getDecrement())
3232 ps.addAsString(op.getWidth());
3237SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3239 emitError(op,
"SV attributes emission is unimplemented for the op");
3241 auto prec = emitSubExpr(op.getInput(), Selection);
3243 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3244 return {Selection, prec.signedness};
3247SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3249 emitError(op,
"SV attributes emission is unimplemented for the op");
3252 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3257SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3259 emitError(op,
"SV attributes emission is unimplemented for the op");
3262 ps.scopedBox(PP::ibox0, [&]() {
3263 ps.writeQuotedEscaped(op.getFormatString());
3270 for (
auto operand : op.getSubstitutions()) {
3271 ps <<
"," << PP::space;
3272 emitSubExpr(operand, LowestPrecedence);
3276 return {Symbol, IsUnsigned};
3279SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3281 emitError(op,
"SV attributes emission is unimplemented for the op");
3284 return {Symbol, IsUnsigned};
3287SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3289 emitError(op,
"SV attributes emission is unimplemented for the op");
3292 return {Symbol, IsUnsigned};
3295SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3309 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3310 ps.scopedBox(PP::ibox0, [&]() {
3311 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3315 emitSVAttributes(op);
3317 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3318 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3322 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3323 return emitSubExpr(op.getFalseValue(), Conditional);
3326 SubExprSignResult signedness = IsUnsigned;
3327 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3328 signedness = IsSigned;
3330 return {Conditional, signedness};
3334SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3336 emitError(op,
"SV attributes emission is unimplemented for the op");
3339 emitSubExpr(op.getInput(), LowestPrecedence);
3342 return {Symbol, IsUnsigned};
3345SubExprInfo ExprEmitter::printStructCreate(
3346 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3348 bool printAsPattern, Operation *op) {
3349 if (printAsPattern && !isAssignmentLikeContext)
3350 emitAssignmentPatternContextError(op);
3353 auto filteredFields = llvm::make_filter_range(
3354 llvm::enumerate(fieldInfos),
3355 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3357 if (printAsPattern) {
3359 filteredFields, [&]() { ps <<
"'{"; },
3360 [&](
const auto &field) {
3361 ps.scopedBox(PP::ibox2, [&]() {
3363 emitter.getVerilogStructFieldName(field.value().name))
3364 <<
":" << PP::space;
3365 fieldFn(field.value(), field.index());
3368 [&]() { ps <<
"}"; });
3371 filteredFields, [&]() { ps <<
"{"; },
3372 [&](
const auto &field) {
3373 ps.scopedBox(PP::ibox2,
3374 [&]() { fieldFn(field.value(), field.index()); });
3376 [&]() { ps <<
"}"; });
3379 return {Selection, IsUnsigned};
3384 emitError(op,
"SV attributes emission is unimplemented for the op");
3388 bool printAsPattern = isAssignmentLikeContext;
3389 StructType structType = op.getType();
3390 return printStructCreate(
3391 structType.getElements(),
3392 [&](
const auto &field,
auto index) {
3393 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3395 isAssignmentLikeContext);
3397 printAsPattern, op);
3402 emitError(op,
"SV attributes emission is unimplemented for the op");
3404 emitSubExpr(op.getInput(), Selection);
3406 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3407 return {Selection, IsUnsigned};
3410SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3412 emitError(op,
"SV attributes emission is unimplemented for the op");
3416 bool printAsPattern = isAssignmentLikeContext;
3417 StructType structType = op.getType();
3418 return printStructCreate(
3419 structType.getElements(),
3420 [&](
const auto &field,
auto index) {
3421 if (field.name == op.getFieldNameAttr()) {
3422 emitSubExpr(op.getNewValue(), Selection);
3424 emitSubExpr(op.getInput(), Selection);
3426 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3429 printAsPattern, op);
3432SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3433 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3434 return {Selection, IsUnsigned};
3437SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3439 emitError(op,
"SV attributes emission is unimplemented for the op");
3440 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3443 result.signedness = IsUnsigned;
3447SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3449 emitError(op,
"SV attributes emission is unimplemented for the op");
3453 auto unionWidth = hw::getBitWidth(unionType);
3454 auto &element = unionType.getElements()[op.getFieldIndex()];
3455 auto elementWidth = hw::getBitWidth(element.type);
3458 if (!elementWidth) {
3459 ps.addAsString(unionWidth);
3461 return {Unary, IsUnsigned};
3465 if (elementWidth == unionWidth) {
3466 emitSubExpr(op.getInput(), LowestPrecedence);
3467 return {Unary, IsUnsigned};
3472 ps.scopedBox(PP::ibox0, [&]() {
3473 if (
auto prePadding = element.offset) {
3474 ps.addAsString(prePadding);
3475 ps <<
"'h0," << PP::space;
3477 emitSubExpr(op.getInput(), Selection);
3478 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3479 ps <<
"," << PP::space;
3480 ps.addAsString(postPadding);
3486 return {Unary, IsUnsigned};
3489SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3491 emitError(op,
"SV attributes emission is unimplemented for the op");
3492 emitSubExpr(op.getInput(), Selection);
3495 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3496 auto unionWidth = hw::getBitWidth(unionType);
3497 auto &element = unionType.getElements()[op.getFieldIndex()];
3498 auto elementWidth = hw::getBitWidth(element.type);
3499 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3500 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3509 return {Selection, IsUnsigned};
3512SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3513 emitOpError(op,
"cannot emit this expression to Verilog");
3514 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3516 return {Symbol, IsUnsigned};
3532enum class PropertyPrecedence {
3552struct EmittedProperty {
3554 PropertyPrecedence precedence;
3559class PropertyEmitter :
public EmitterBase,
3560 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3564 PropertyEmitter(ModuleEmitter &emitter,
3565 SmallPtrSetImpl<Operation *> &emittedOps)
3566 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3567 PropertyEmitter(ModuleEmitter &emitter,
3568 SmallPtrSetImpl<Operation *> &emittedOps,
3570 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3572 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3573 assert(state.pp.getListener() == &state.saver);
3576 void emitAssertPropertyDisable(
3577 Value property, Value disable,
3578 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3580 void emitAssertPropertyBody(
3581 Value property, Value disable,
3582 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3584 void emitAssertPropertyBody(
3585 Value property, sv::EventControl event, Value clock, Value disable,
3586 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3591 emitNestedProperty(Value property,
3592 PropertyPrecedence parenthesizeIfLooserThan);
3593 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3594 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3596 EmittedProperty visitUnhandledLTL(Operation *op);
3597 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3598 EmittedProperty visitLTL(ltl::AndOp op);
3599 EmittedProperty visitLTL(ltl::OrOp op);
3600 EmittedProperty visitLTL(ltl::IntersectOp op);
3601 EmittedProperty visitLTL(ltl::DelayOp op);
3602 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3603 EmittedProperty visitLTL(ltl::ConcatOp op);
3604 EmittedProperty visitLTL(ltl::RepeatOp op);
3605 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3606 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3607 EmittedProperty visitLTL(ltl::NotOp op);
3608 EmittedProperty visitLTL(ltl::ImplicationOp op);
3609 EmittedProperty visitLTL(ltl::UntilOp op);
3610 EmittedProperty visitLTL(ltl::EventuallyOp op);
3611 EmittedProperty visitLTL(ltl::ClockOp op);
3613 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3614 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3615 void emitLTLConcat(ValueRange inputs);
3618 ModuleEmitter &emitter;
3623 SmallPtrSetImpl<Operation *> &emittedOps;
3626 SmallVector<Token> localTokens;
3639void PropertyEmitter::emitAssertPropertyDisable(
3640 Value property, Value disable,
3641 PropertyPrecedence parenthesizeIfLooserThan) {
3644 ps <<
"disable iff" << PP::nbsp <<
"(";
3646 emitNestedProperty(disable, PropertyPrecedence::Unary);
3652 ps.scopedBox(PP::ibox0,
3653 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3659void PropertyEmitter::emitAssertPropertyBody(
3660 Value property, Value disable,
3661 PropertyPrecedence parenthesizeIfLooserThan) {
3662 assert(localTokens.empty());
3664 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3669 if (&buffer.tokens == &localTokens)
3670 buffer.flush(state.pp);
3673void PropertyEmitter::emitAssertPropertyBody(
3674 Value property, sv::EventControl event, Value clock, Value disable,
3675 PropertyPrecedence parenthesizeIfLooserThan) {
3676 assert(localTokens.empty());
3679 ps.scopedBox(PP::ibox2, [&] {
3680 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3681 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3687 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3692 if (&buffer.tokens == &localTokens)
3693 buffer.flush(state.pp);
3696EmittedProperty PropertyEmitter::emitNestedProperty(
3697 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3707 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3708 ExprEmitter(emitter, emittedOps, buffer.tokens)
3709 .emitExpression(property, LowestPrecedence,
3711 return {PropertyPrecedence::Symbol};
3714 unsigned startIndex = buffer.tokens.size();
3715 auto info = dispatchLTLVisitor(property.getDefiningOp());
3720 if (
info.precedence > parenthesizeIfLooserThan) {
3722 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3723 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3725 ps << PP::end <<
")";
3727 info.precedence = PropertyPrecedence::Symbol;
3731 emittedOps.insert(property.getDefiningOp());
3735EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3736 emitOpError(op,
"emission as Verilog property or sequence not supported");
3737 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3738 return {PropertyPrecedence::Symbol};
3741EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3743 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3744 return {PropertyPrecedence::Symbol};
3747EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3750 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3751 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3752 return {PropertyPrecedence::And};
3755EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3758 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3759 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3760 return {PropertyPrecedence::Or};
3763EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3767 emitNestedProperty(input, PropertyPrecedence::Intersect);
3769 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3770 return {PropertyPrecedence::Intersect};
3773void PropertyEmitter::emitLTLDelay(int64_t delay,
3774 std::optional<int64_t> length) {
3778 ps.addAsString(delay);
3781 ps.addAsString(delay);
3783 ps.addAsString(delay + *length);
3789 }
else if (delay == 1) {
3793 ps.addAsString(delay);
3799void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3801 ps.scopedBox(PP::ibox2, [&] {
3802 ps <<
PPExtString(stringifyClockEdge(edge)) << PP::space;
3803 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3808EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3809 emitLTLDelay(op.getDelay(), op.getLength());
3811 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3812 return {PropertyPrecedence::Concat};
3815EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3816 emitLTLClockingEvent(op.getEdge(), op.getClock());
3818 emitLTLDelay(op.getDelay(), op.getLength());
3820 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3821 return {PropertyPrecedence::Clocking};
3824void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3825 bool addSeparator =
false;
3826 for (
auto input : inputs) {
3829 if (!input.getDefiningOp<ltl::DelayOp>())
3830 ps <<
"##0" << PP::space;
3832 addSeparator =
true;
3833 emitNestedProperty(input, PropertyPrecedence::Concat);
3837EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3838 emitLTLConcat(op.getInputs());
3839 return {PropertyPrecedence::Concat};
3842EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3843 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3844 if (
auto more = op.getMore()) {
3846 ps.addAsString(op.getBase());
3849 ps.addAsString(op.getBase() + *more);
3853 if (op.getBase() == 0) {
3855 }
else if (op.getBase() == 1) {
3859 ps.addAsString(op.getBase());
3863 return {PropertyPrecedence::Repeat};
3866EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3867 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3869 auto more = op.getMore();
3871 ps.addAsString(op.getBase());
3874 ps.addAsString(op.getBase() + more);
3878 return {PropertyPrecedence::Repeat};
3881EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3882 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3884 auto more = op.getMore();
3886 ps.addAsString(op.getBase());
3889 ps.addAsString(op.getBase() + more);
3893 return {PropertyPrecedence::Repeat};
3896EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3897 ps <<
"not" << PP::space;
3898 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3899 return {PropertyPrecedence::Unary};
3905 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3906 if (!concatOp || concatOp.getInputs().size() < 2)
3908 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3909 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3911 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3912 if (!constOp || !constOp.getValue().isOne())
3914 return concatOp.getInputs().drop_back();
3917EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3921 emitLTLConcat(range);
3922 ps << PP::space <<
"|=>" << PP::nbsp;
3924 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3925 ps << PP::space <<
"|->" << PP::nbsp;
3927 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3928 return {PropertyPrecedence::Implication};
3931EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3932 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3933 ps << PP::space <<
"until" << PP::space;
3934 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3935 return {PropertyPrecedence::Until};
3938EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3939 ps <<
"s_eventually" << PP::space;
3940 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3941 return {PropertyPrecedence::Qualifier};
3944EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3945 emitLTLClockingEvent(op.getEdge(), op.getClock());
3947 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3948 return {PropertyPrecedence::Clocking};
3958class NameCollector {
3960 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3964 void collectNames(Block &block);
3966 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3967 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3970 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3971 ModuleEmitter &moduleEmitter;
3976 static constexpr size_t maxTypeWidthBound = 32;
3981void NameCollector::collectNames(Block &block) {
3984 for (
auto &op : block) {
3988 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
3991 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3995 for (
auto result : op.getResults()) {
3997 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3998 SmallString<16> typeString;
4002 llvm::raw_svector_ostream stringStream(typeString);
4004 stringStream, op.getLoc());
4006 if (typeString.size() <= maxTypeWidthBound)
4007 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4014 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4015 for (
auto ®ion : op.getRegions()) {
4016 if (!region.empty())
4017 collectNames(region.front());
4031class StmtEmitter :
public EmitterBase,
4039 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4041 void emitStatement(Operation *op);
4042 void emitStatementBlock(Block &body);
4045 LogicalResult emitDeclaration(Operation *op);
4048 void collectNamesAndCalculateDeclarationWidths(Block &block);
4051 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4052 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4053 bool isAssignmentLikeContext =
false);
4054 void emitSVAttributes(Operation *op);
4057 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4060 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4064 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4065 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4066 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4067 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4068 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4069 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4071 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4072 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4073 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4074 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4075 template <
typename Op>
4078 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4079 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4080 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4082 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4083 LogicalResult visitSV(
AssignOp op);
4084 LogicalResult visitSV(BPAssignOp op);
4085 LogicalResult visitSV(PAssignOp op);
4086 LogicalResult visitSV(ForceOp op);
4087 LogicalResult visitSV(ReleaseOp op);
4088 LogicalResult visitSV(AliasOp op);
4089 LogicalResult visitSV(InterfaceInstanceOp op);
4090 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4091 LogicalResult visitStmt(OutputOp op);
4093 LogicalResult visitStmt(InstanceOp op);
4094 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4095 ArrayRef<Value> instPortValues);
4100 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4101 LogicalResult visitSV(OrderedOutputOp op);
4102 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4103 LogicalResult visitSV(IfDefProceduralOp op) {
4104 return emitIfDef(op, op.getCond());
4106 LogicalResult visitSV(IfOp op);
4107 LogicalResult visitSV(AlwaysOp op);
4108 LogicalResult visitSV(AlwaysCombOp op);
4109 LogicalResult visitSV(AlwaysFFOp op);
4110 LogicalResult visitSV(InitialOp op);
4111 LogicalResult visitSV(CaseOp op);
4112 template <
typename OpTy,
typename EmitPrefixFn>
4114 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4115 ValueRange substitutions, EmitPrefixFn emitPrefix);
4116 LogicalResult visitSV(WriteOp op);
4117 LogicalResult visitSV(FWriteOp op);
4118 LogicalResult visitSV(FFlushOp op);
4119 LogicalResult visitSV(VerbatimOp op);
4120 LogicalResult visitSV(MacroRefOp op);
4122 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4123 std::optional<unsigned> verbosity);
4124 LogicalResult visitSV(StopOp op);
4125 LogicalResult visitSV(FinishOp op);
4126 LogicalResult visitSV(ExitOp op);
4128 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4129 std::optional<unsigned> verbosity,
4131 ValueRange operands);
4134 template <
typename OpTy>
4135 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4136 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4137 op.getMessageAttr(), op.getSubstitutions());
4141 template <
typename OpTy>
4142 LogicalResult emitFatalMessageOp(OpTy op) {
4143 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4144 op.getMessageAttr(), op.getSubstitutions());
4147 LogicalResult visitSV(FatalProceduralOp op);
4148 LogicalResult visitSV(FatalOp op);
4149 LogicalResult visitSV(ErrorProceduralOp op);
4150 LogicalResult visitSV(WarningProceduralOp op);
4151 LogicalResult visitSV(InfoProceduralOp op);
4152 LogicalResult visitSV(ErrorOp op);
4153 LogicalResult visitSV(WarningOp op);
4154 LogicalResult visitSV(InfoOp op);
4156 LogicalResult visitSV(ReadMemOp op);
4158 LogicalResult visitSV(GenerateOp op);
4159 LogicalResult visitSV(GenerateCaseOp op);
4160 LogicalResult visitSV(GenerateForOp op);
4162 LogicalResult visitSV(
ForOp op);
4164 void emitAssertionLabel(Operation *op);
4165 void emitAssertionMessage(StringAttr message, ValueRange args,
4166 SmallPtrSetImpl<Operation *> &ops,
4168 template <
typename Op>
4169 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4170 LogicalResult visitSV(AssertOp op);
4171 LogicalResult visitSV(AssumeOp op);
4172 LogicalResult visitSV(CoverOp op);
4173 template <
typename Op>
4174 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4175 LogicalResult visitSV(AssertConcurrentOp op);
4176 LogicalResult visitSV(AssumeConcurrentOp op);
4177 LogicalResult visitSV(CoverConcurrentOp op);
4178 template <
typename Op>
4179 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4180 LogicalResult visitSV(AssertPropertyOp op);
4181 LogicalResult visitSV(AssumePropertyOp op);
4182 LogicalResult visitSV(CoverPropertyOp op);
4184 LogicalResult visitSV(BindOp op);
4185 LogicalResult visitSV(InterfaceOp op);
4187 LogicalResult visitSV(InterfaceSignalOp op);
4188 LogicalResult visitSV(InterfaceModportOp op);
4189 LogicalResult visitSV(AssignInterfaceSignalOp op);
4190 LogicalResult visitSV(MacroErrorOp op);
4191 LogicalResult visitSV(MacroDefOp op);
4193 void emitBlockAsStatement(Block *block,
4194 const SmallPtrSetImpl<Operation *> &locationOps,
4195 StringRef multiLineComment = StringRef());
4197 LogicalResult visitSV(FuncDPIImportOp op);
4198 template <
typename CallOp>
4199 LogicalResult emitFunctionCall(CallOp callOp);
4200 LogicalResult visitSV(FuncCallProceduralOp op);
4201 LogicalResult visitSV(FuncCallOp op);
4202 LogicalResult visitSV(ReturnOp op);
4203 LogicalResult visitSV(IncludeOp op);
4206 ModuleEmitter &emitter;
4211 size_t maxDeclNameWidth = 0;
4212 size_t maxTypeWidth = 0;
4223void StmtEmitter::emitExpression(Value exp,
4224 SmallPtrSetImpl<Operation *> &emittedExprs,
4225 VerilogPrecedence parenthesizeIfLooserThan,
4226 bool isAssignmentLikeContext) {
4227 ExprEmitter(emitter, emittedExprs)
4228 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4233void StmtEmitter::emitSVAttributes(Operation *op) {
4241 setPendingNewline();
4244void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4245 llvm::function_ref<
void()> emitRHS,
4247 std::optional<PPExtString> wordBeforeLHS) {
4249 ps.scopedBox(PP::ibox2, [&]() {
4250 if (wordBeforeLHS) {
4251 ps << *wordBeforeLHS << PP::space;
4255 ps << PP::space << syntax << PP::space;
4257 ps.scopedBox(PP::ibox0, [&]() {
4264template <
typename Op>
4266StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4267 std::optional<PPExtString> wordBeforeLHS) {
4268 SmallPtrSet<Operation *, 8> ops;
4272 ps.addCallback({op,
true});
4273 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4275 emitExpression(op.getSrc(), ops, LowestPrecedence,
4280 ps.addCallback({op,
false});
4281 emitLocationInfoAndNewLine(ops);
4285LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4288 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4291 if (emitter.assignsInlined.count(op))
4295 emitSVAttributes(op);
4300LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4301 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4305 if (emitter.assignsInlined.count(op))
4309 emitSVAttributes(op);
4314LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4316 emitSVAttributes(op);
4321LogicalResult StmtEmitter::visitSV(ForceOp op) {
4323 emitError(op,
"SV attributes emission is unimplemented for the op");
4328LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4330 emitError(op,
"SV attributes emission is unimplemented for the op");
4333 SmallPtrSet<Operation *, 8> ops;
4335 ps.addCallback({op,
true});
4336 ps.scopedBox(PP::ibox2, [&]() {
4337 ps <<
"release" << PP::space;
4338 emitExpression(op.getDest(), ops);
4341 ps.addCallback({op,
false});
4342 emitLocationInfoAndNewLine(ops);
4346LogicalResult StmtEmitter::visitSV(AliasOp op) {
4348 emitError(op,
"SV attributes emission is unimplemented for the op");
4351 SmallPtrSet<Operation *, 8> ops;
4353 ps.addCallback({op,
true});
4354 ps.scopedBox(PP::ibox2, [&]() {
4355 ps <<
"alias" << PP::space;
4356 ps.scopedBox(PP::cbox0, [&]() {
4358 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4359 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4363 ps.addCallback({op,
false});
4364 emitLocationInfoAndNewLine(ops);
4368LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4369 auto doNotPrint = op.getDoNotPrint();
4370 if (doNotPrint && !state.options.emitBindComments)
4374 emitError(op,
"SV attributes emission is unimplemented for the op");
4377 StringRef prefix =
"";
4378 ps.addCallback({op,
true});
4381 ps <<
"// This interface is elsewhere emitted as a bind statement."
4385 SmallPtrSet<Operation *, 8> ops;
4388 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4389 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4390 "point to an interface");
4393 if (!prefix.empty())
4399 ps.addCallback({op,
false});
4400 emitLocationInfoAndNewLine(ops);
4408LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4410 SmallPtrSet<Operation *, 8> ops;
4411 size_t operandIndex = 0;
4413 for (
PortInfo port : ports.getOutputs()) {
4414 auto operand = op->getOperand(operandIndex);
4418 if (operand.hasOneUse() && operand.getDefiningOp() &&
4419 isa<InstanceOp>(operand.getDefiningOp())) {
4428 ps.addCallback({op,
true});
4430 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4432 ps <<
"// Zero width: ";
4435 ps <<
"assign" << PP::space;
4437 ps << PP::space <<
"=" << PP::space;
4438 ps.scopedBox(PP::ibox0, [&]() {
4442 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4443 ps <<
"/*Zero width*/";
4445 emitExpression(operand, ops, LowestPrecedence,
4450 ps.addCallback({op,
false});
4451 emitLocationInfoAndNewLine(ops);
4458LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4459 auto parent = op->getParentOfType<PortList>();
4461 return emitOutputLikeOp(op, ports);
4464LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4466 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4467 ps <<
"`ifndef " << typescopeDef << PP::newline;
4468 ps <<
"`define " << typescopeDef;
4469 setPendingNewline();
4470 emitStatementBlock(*op.getBodyBlock());
4472 ps <<
"`endif // " << typescopeDef;
4473 setPendingNewline();
4477LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4479 emitError(op,
"SV attributes emission is unimplemented for the op");
4484 ps << PP::neverbox <<
"// ";
4486 SmallPtrSet<Operation *, 8> ops;
4488 ps.scopedBox(PP::ibox2, [&]() {
4489 ps <<
"typedef" << PP::space;
4490 ps.invokeWithStringOS([&](
auto &os) {
4492 op.getAliasType(),
false);
4494 ps << PP::space <<
PPExtString(op.getPreferredName());
4495 ps.invokeWithStringOS(
4496 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4501 emitLocationInfoAndNewLine(ops);
4505template <
typename CallOpTy>
4506LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4510 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4512 SmallPtrSet<Operation *, 8> ops;
4516 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4517 if (explicitReturn) {
4518 assert(explicitReturn.hasOneUse());
4519 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4520 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4521 emitExpression(bpassignOp.getDest(), ops);
4523 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4524 ps <<
"assign" << PP::nbsp;
4525 emitExpression(assignOp.getDest(), ops);
4527 ps << PP::nbsp <<
"=" << PP::nbsp;
4530 auto arguments = callee.getPortList(
true);
4534 bool needsComma =
false;
4535 auto printArg = [&](Value value) {
4537 ps <<
"," << PP::space;
4538 emitExpression(value, ops);
4542 ps.scopedBox(PP::ibox0, [&] {
4543 unsigned inputIndex = 0, outputIndex = 0;
4544 for (
auto arg : arguments) {
4547 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4549 printArg(op.getInputs()[inputIndex++]);
4554 emitLocationInfoAndNewLine(ops);
4558LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4559 return emitFunctionCall(op);
4562LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4563 return emitFunctionCall(op);
4566template <
typename PPS>
4568 bool isAutomatic =
false,
4569 bool emitAsTwoStateType =
false) {
4570 ps <<
"function" << PP::nbsp;
4572 ps <<
"automatic" << PP::nbsp;
4573 auto retType = op.getExplicitlyReturnedType();
4575 ps.invokeWithStringOS([&](
auto &os) {
4576 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4577 emitAsTwoStateType);
4583 emitter.emitPortList(
4587LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4588 auto parent = op->getParentOfType<sv::FuncOp>();
4590 return emitOutputLikeOp(op, ports);
4593LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4595 ps <<
"`include" << PP::nbsp;
4597 if (op.getStyle() == IncludeStyle::System)
4598 ps <<
"<" << op.getTarget() <<
">";
4600 ps <<
"\"" << op.getTarget() <<
"\"";
4602 emitLocationInfo(op.getLoc());
4603 setPendingNewline();
4607LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4610 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4614 if (
auto linkageName = importOp.getLinkageName())
4615 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4617 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4618 assert(op.isDeclaration() &&
"function must be a declaration");
4621 assert(state.pendingNewline);
4627LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4629 emitError(op,
"SV attributes emission is unimplemented for the op");
4632 SmallPtrSet<Operation *, 8> ops;
4635 ps.addCallback({op,
true});
4637 if (
auto fd = op.getFd())
4638 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4641 ps.addCallback({op,
false});
4642 emitLocationInfoAndNewLine(ops);
4646template <
typename OpTy,
typename EmitPrefixFn>
4647LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4648 StringRef formatString,
4649 ValueRange substitutions,
4650 EmitPrefixFn emitPrefix) {
4652 emitError(op,
"SV attributes emission is unimplemented for the op");
4655 SmallPtrSet<Operation *, 8> ops;
4658 ps.addCallback({op,
true});
4660 ps.scopedBox(PP::ibox0, [&]() {
4662 ps.writeQuotedEscaped(formatString);
4669 for (
auto operand : substitutions) {
4670 ps <<
"," << PP::space;
4671 emitExpression(operand, ops);
4675 ps.addCallback({op,
false});
4676 emitLocationInfoAndNewLine(ops);
4680LogicalResult StmtEmitter::visitSV(WriteOp op) {
4681 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4682 op.getSubstitutions(),
4683 [&](SmallPtrSetImpl<Operation *> &) {});
4686LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4687 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4688 op.getSubstitutions(),
4689 [&](SmallPtrSetImpl<Operation *> &ops) {
4690 emitExpression(op.getFd(), ops);
4691 ps <<
"," << PP::space;
4695LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4697 emitError(op,
"SV attributes emission is unimplemented for the op");
4700 SmallPtrSet<Operation *, 8> ops;
4705 StringRef
string = op.getFormatString();
4706 if (
string.ends_with(
"\n"))
4707 string =
string.drop_back();
4712 bool isFirst =
true;
4715 while (!
string.
empty()) {
4716 auto lhsRhs =
string.split(
'\n');
4720 ps << PP::end << PP::newline << PP::neverbox;
4724 emitTextWithSubstitutions(
4725 ps, lhsRhs.first, op,
4726 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4727 string = lhsRhs.second;
4732 emitLocationInfoAndNewLine(ops);
4737LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4739 emitError(op,
"SV attributes emission is unimplemented for the op");
4743 SmallPtrSet<Operation *, 8> ops;
4748 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4749 assert(macroOp &&
"Invalid IR");
4751 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4753 if (!op.getInputs().empty()) {
4755 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4756 emitExpression(val, ops, LowestPrecedence,
4762 emitLocationInfoAndNewLine(ops);
4768StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4769 std::optional<unsigned> verbosity) {
4771 emitError(op,
"SV attributes emission is unimplemented for the op");
4774 SmallPtrSet<Operation *, 8> ops;
4776 ps.addCallback({op,
true});
4778 if (verbosity && *verbosity != 1) {
4780 ps.addAsString(*verbosity);
4784 ps.addCallback({op,
false});
4785 emitLocationInfoAndNewLine(ops);
4789LogicalResult StmtEmitter::visitSV(StopOp op) {
4790 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4793LogicalResult StmtEmitter::visitSV(FinishOp op) {
4794 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4798LogicalResult StmtEmitter::visitSV(ExitOp op) {
4799 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4805StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4806 std::optional<unsigned> verbosity,
4807 StringAttr message, ValueRange operands) {
4809 emitError(op,
"SV attributes emission is unimplemented for the op");
4812 SmallPtrSet<Operation *, 8> ops;
4814 ps.addCallback({op,
true});
4820 if ((verbosity && *verbosity != 1) || message) {
4822 ps.scopedBox(PP::ibox0, [&]() {
4826 ps.addAsString(*verbosity);
4831 ps <<
"," << PP::space;
4832 ps.writeQuotedEscaped(message.getValue());
4834 for (
auto operand : operands) {
4835 ps <<
"," << PP::space;
4836 emitExpression(operand, ops);
4845 ps.addCallback({op,
false});
4846 emitLocationInfoAndNewLine(ops);
4850LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4851 return emitFatalMessageOp(op);
4854LogicalResult StmtEmitter::visitSV(FatalOp op) {
4855 return emitFatalMessageOp(op);
4858LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4859 return emitNonfatalMessageOp(op,
"$error");
4862LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4863 return emitNonfatalMessageOp(op,
"$warning");
4866LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4867 return emitNonfatalMessageOp(op,
"$info");
4870LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4871 return emitNonfatalMessageOp(op,
"$error");
4874LogicalResult StmtEmitter::visitSV(WarningOp op) {
4875 return emitNonfatalMessageOp(op,
"$warning");
4878LogicalResult StmtEmitter::visitSV(InfoOp op) {
4879 return emitNonfatalMessageOp(op,
"$info");
4882LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4883 SmallPtrSet<Operation *, 8> ops({op});
4886 ps.addCallback({op,
true});
4888 switch (op.getBaseAttr().getValue()) {
4889 case MemBaseTypeAttr::MemBaseBin:
4892 case MemBaseTypeAttr::MemBaseHex:
4897 ps.scopedBox(PP::ibox0, [&]() {
4898 ps.writeQuotedEscaped(op.getFilename());
4899 ps <<
"," << PP::space;
4900 emitExpression(op.getDest(), ops);
4904 ps.addCallback({op,
false});
4905 emitLocationInfoAndNewLine(ops);
4909LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4910 emitSVAttributes(op);
4913 ps.addCallback({op,
true});
4914 ps <<
"generate" << PP::newline;
4916 setPendingNewline();
4917 emitStatementBlock(op.getBody().getBlocks().front());
4920 ps <<
"endgenerate";
4921 ps.addCallback({op,
false});
4922 setPendingNewline();
4926LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4927 emitSVAttributes(op);
4930 ps.addCallback({op,
true});
4932 ps.invokeWithStringOS([&](
auto &os) {
4933 emitter.printParamValue(
4934 op.getCond(), os, VerilogPrecedence::Selection,
4935 [&]() { return op->emitOpError(
"invalid case parameter"); });
4938 setPendingNewline();
4941 ArrayAttr
patterns = op.getCasePatterns();
4942 ArrayAttr caseNames = op.getCaseNames();
4943 MutableArrayRef<Region> regions = op.getCaseRegions();
4950 llvm::StringMap<size_t> nextGenIds;
4951 ps.scopedBox(PP::bbox2, [&]() {
4953 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4954 auto ®ion = regions[i];
4955 assert(region.hasOneBlock());
4956 Attribute patternAttr =
patterns[i];
4959 if (!isa<mlir::TypedAttr>(patternAttr))
4962 ps.invokeWithStringOS([&](
auto &os) {
4963 emitter.printParamValue(
4964 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4965 [&]() {
return op->emitOpError(
"invalid case value"); });
4968 StringRef legalName =
4969 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4972 setPendingNewline();
4973 emitStatementBlock(region.getBlocks().front());
4976 setPendingNewline();
4982 ps.addCallback({op,
false});
4983 setPendingNewline();
4987LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
4988 emitSVAttributes(op);
4989 llvm::SmallPtrSet<Operation *, 8> ops;
4990 ps.addCallback({op,
true});
4993 StringRef inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4996 ps.scopedBox(PP::cbox0, [&]() {
4998 [&]() { ps <<
"genvar" << PP::nbsp <<
PPExtString(inductionVarName); },
5000 ps.invokeWithStringOS([&](
auto &os) {
5001 emitter.printParamValue(
5002 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5003 [&]() { return op->emitOpError(
"invalid lower bound"); });
5012 ps.invokeWithStringOS([&](
auto &os) {
5013 emitter.printParamValue(
5014 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5015 [&]() { return op->emitOpError(
"invalid upper bound"); });
5021 ps <<
PPExtString(inductionVarName) << PP::nbsp <<
"+=" << PP::nbsp;
5022 ps.invokeWithStringOS([&](
auto &os) {
5023 emitter.printParamValue(
5024 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5025 [&]() { return op->emitOpError(
"invalid step"); });
5028 StringRef blockName = op.getGenBlockName();
5029 if (!blockName.empty())
5033 ps << PP::neverbreak;
5034 setPendingNewline();
5035 emitStatementBlock(op.getBody().getBlocks().front());
5038 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5040 ps.addCallback({op,
false});
5041 setPendingNewline();
5045LogicalResult StmtEmitter::visitSV(
ForOp op) {
5046 emitSVAttributes(op);
5047 llvm::SmallPtrSet<Operation *, 8> ops;
5048 ps.addCallback({op,
true});
5050 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5053 ps.scopedBox(PP::cbox0, [&]() {
5057 ps <<
"logic" << PP::nbsp;
5058 ps.invokeWithStringOS([&](
auto &os) {
5059 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5064 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
5069 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5070 [&]() { emitExpression(op.getUpperBound(), ops); },
5076 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5077 [&]() { emitExpression(op.getStep(), ops); },
5081 ps << PP::neverbreak;
5082 setPendingNewline();
5083 emitStatementBlock(op.getBody().getBlocks().front());
5086 ps.addCallback({op,
false});
5087 emitLocationInfoAndNewLine(ops);
5092void StmtEmitter::emitAssertionLabel(Operation *op) {
5093 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5099void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5100 SmallPtrSetImpl<Operation *> &ops,
5101 bool isConcurrent =
false) {
5104 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5105 ps.scopedBox(PP::ibox0, [&]() {
5106 ps.writeQuotedEscaped(message.getValue());
5108 for (
auto arg : args) {
5109 ps <<
"," << PP::space;
5110 emitExpression(arg, ops);
5116template <
typename Op>
5117LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5119 emitError(op,
"SV attributes emission is unimplemented for the op");
5122 SmallPtrSet<Operation *, 8> ops;
5124 ps.addCallback({op,
true});
5125 ps.scopedBox(PP::ibox2, [&]() {
5126 emitAssertionLabel(op);
5127 ps.scopedBox(PP::cbox0, [&]() {
5129 switch (op.getDefer()) {
5130 case DeferAssert::Immediate:
5132 case DeferAssert::Observed:
5135 case DeferAssert::Final:
5140 ps.scopedBox(PP::ibox0, [&]() {
5141 emitExpression(op.getExpression(), ops);
5144 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5148 ps.addCallback({op,
false});
5149 emitLocationInfoAndNewLine(ops);
5153LogicalResult StmtEmitter::visitSV(AssertOp op) {
5154 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5157LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5158 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5161LogicalResult StmtEmitter::visitSV(CoverOp op) {
5162 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5165template <
typename Op>
5166LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5168 emitError(op,
"SV attributes emission is unimplemented for the op");
5171 SmallPtrSet<Operation *, 8> ops;
5173 ps.addCallback({op,
true});
5174 ps.scopedBox(PP::ibox2, [&]() {
5175 emitAssertionLabel(op);
5176 ps.scopedBox(PP::cbox0, [&]() {
5177 ps << opName << PP::nbsp <<
"property (";
5178 ps.scopedBox(PP::ibox0, [&]() {
5179 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5181 emitExpression(op.getClock(), ops);
5182 ps <<
")" << PP::space;
5183 emitExpression(op.getProperty(), ops);
5186 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5191 ps.addCallback({op,
false});
5192 emitLocationInfoAndNewLine(ops);
5196LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5197 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5200LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5201 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5204LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5205 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5210template <
typename Op>
5211LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5213 emitError(op,
"SV attributes emission is unimplemented for the op");
5223 Operation *parent = op->getParentOp();
5224 Value
property = op.getProperty();
5225 bool isTemporal = !
property.getType().isSignlessInteger(1);
5227 bool emitAsImmediate = !isTemporal && isProcedural;
5230 SmallPtrSet<Operation *, 8> ops;
5232 ps.addCallback({op,
true});
5233 ps.scopedBox(PP::ibox2, [&]() {
5235 emitAssertionLabel(op);
5237 ps.scopedBox(PP::cbox0, [&]() {
5238 if (emitAsImmediate)
5239 ps << opName <<
"(";
5241 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5243 Value clock = op.getClock();
5244 auto event = op.getEvent();
5246 ps.scopedBox(PP::ibox2, [&]() {
5247 PropertyEmitter(emitter, ops)
5248 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5251 ps.scopedBox(PP::ibox2, [&]() {
5252 PropertyEmitter(emitter, ops)
5253 .emitAssertPropertyBody(property, op.getDisable());
5258 ps.addCallback({op,
false});
5259 emitLocationInfoAndNewLine(ops);
5263LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5264 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5267LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5268 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5271LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5272 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5275LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5277 emitError(op,
"SV attributes emission is unimplemented for the op");
5280 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5281 .getMacroIdentifier());
5284 bool hasEmptyThen = op->getRegion(0).front().empty();
5286 ps <<
"`ifndef " << ident;
5288 ps <<
"`ifdef " << ident;
5290 SmallPtrSet<Operation *, 8> ops;
5292 emitLocationInfoAndNewLine(ops);
5295 emitStatementBlock(op->getRegion(0).front());
5297 if (!op->getRegion(1).empty()) {
5298 if (!hasEmptyThen) {
5300 ps <<
"`else // " << ident;
5301 setPendingNewline();
5303 emitStatementBlock(op->getRegion(1).front());
5310 setPendingNewline();
5318void StmtEmitter::emitBlockAsStatement(
5319 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5320 StringRef multiLineComment) {
5324 auto needsBeginEnd =
5328 emitLocationInfoAndNewLine(locationOps);
5331 emitStatementBlock(*block);
5333 if (needsBeginEnd) {
5337 if (!multiLineComment.empty())
5338 ps <<
" // " << multiLineComment;
5339 setPendingNewline();
5343LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5345 for (
auto &op : ooop.getBody().front())
5350LogicalResult StmtEmitter::visitSV(IfOp op) {
5351 SmallPtrSet<Operation *, 8> ops;
5353 auto ifcondBox = PP::ibox2;
5355 emitSVAttributes(op);
5357 ps.addCallback({op,
true});
5358 ps <<
"if (" << ifcondBox;
5368 emitExpression(ifOp.getCond(), ops);
5369 ps << PP::end <<
")";
5370 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5372 if (!ifOp.hasElse())
5376 Block *elseBlock = ifOp.getElseBlock();
5378 if (!nestedElseIfOp) {
5383 emitBlockAsStatement(elseBlock, ops);
5389 ifOp = nestedElseIfOp;
5390 ps <<
"else if (" << ifcondBox;
5392 ps.addCallback({op,
false});
5397LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5398 emitSVAttributes(op);
5399 SmallPtrSet<Operation *, 8> ops;
5403 auto printEvent = [&](AlwaysOp::Condition cond) {
5404 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5405 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5407 ps.addCallback({op,
true});
5409 switch (op.getNumConditions()) {
5415 printEvent(op.getCondition(0));
5420 ps.scopedBox(PP::cbox0, [&]() {
5421 printEvent(op.getCondition(0));
5422 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5423 ps << PP::space <<
"or" << PP::space;
5424 printEvent(op.getCondition(i));
5433 std::string comment;
5434 if (op.getNumConditions() == 0) {
5435 comment =
"always @*";
5437 comment =
"always @(";
5440 [&](Attribute eventAttr) {
5441 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5442 comment += stringifyEventControl(event);
5444 [&]() { comment +=
", "; });
5448 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5449 ps.addCallback({op,
false});
5453LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5454 emitSVAttributes(op);
5455 SmallPtrSet<Operation *, 8> ops;
5459 ps.addCallback({op,
true});
5460 StringRef opString =
"always_comb";
5461 if (state.options.noAlwaysComb)
5462 opString =
"always @(*)";
5465 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5466 ps.addCallback({op,
false});
5470LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5471 emitSVAttributes(op);
5473 SmallPtrSet<Operation *, 8> ops;
5477 ps.addCallback({op,
true});
5478 ps <<
"always_ff @(";
5479 ps.scopedBox(PP::cbox0, [&]() {
5480 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5481 emitExpression(op.getClock(), ops);
5482 if (op.getResetStyle() == ResetType::AsyncReset) {
5483 ps << PP::nbsp <<
"or" << PP::space
5484 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5485 emitExpression(op.getReset(), ops);
5492 std::string comment;
5493 comment +=
"always_ff @(";
5494 comment += stringifyEventControl(op.getClockEdge());
5495 if (op.getResetStyle() == ResetType::AsyncReset) {
5497 comment += stringifyEventControl(*op.getResetEdge());
5501 if (op.getResetStyle() == ResetType::NoReset)
5502 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5505 emitLocationInfoAndNewLine(ops);
5506 ps.scopedBox(PP::bbox2, [&]() {
5512 if (op.getResetStyle() == ResetType::AsyncReset &&
5513 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5515 emitExpression(op.getReset(), ops);
5517 emitBlockAsStatement(op.getResetBlock(), ops);
5520 emitBlockAsStatement(op.getBodyBlock(), ops);
5525 ps <<
" // " << comment;
5526 setPendingNewline();
5528 ps.addCallback({op,
false});
5532LogicalResult StmtEmitter::visitSV(InitialOp op) {
5533 emitSVAttributes(op);
5534 SmallPtrSet<Operation *, 8> ops;
5537 ps.addCallback({op,
true});
5539 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5540 ps.addCallback({op,
false});
5544LogicalResult StmtEmitter::visitSV(CaseOp op) {
5545 emitSVAttributes(op);
5546 SmallPtrSet<Operation *, 8> ops, emptyOps;
5549 ps.addCallback({op,
true});
5550 if (op.getValidationQualifier() !=
5551 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5552 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5553 op.getValidationQualifier()))
5555 const char *opname =
nullptr;
5556 switch (op.getCaseStyle()) {
5557 case CaseStmtType::CaseStmt:
5560 case CaseStmtType::CaseXStmt:
5563 case CaseStmtType::CaseZStmt:
5567 ps << opname <<
" (";
5568 ps.scopedBox(PP::ibox0, [&]() {
5569 emitExpression(op.getCond(), ops);
5572 emitLocationInfoAndNewLine(ops);
5574 size_t caseValueIndex = 0;
5575 ps.scopedBox(PP::bbox2, [&]() {
5576 for (
auto &caseInfo : op.getCases()) {
5578 auto &
pattern = caseInfo.pattern;
5580 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5581 .Case<CaseBitPattern>([&](
auto bitPattern) {
5584 ps.invokeWithStringOS([&](
auto &os) {
5585 os << bitPattern->getWidth() <<
"'b";
5586 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5587 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5590 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5591 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5592 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5594 .Case<CaseExprPattern>([&](
auto) {
5595 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5597 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5598 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5601 emitBlockAsStatement(caseInfo.block, emptyOps);
5607 ps.addCallback({op,
false});
5608 emitLocationInfoAndNewLine(ops);
5612LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5613 bool doNotPrint = op.getDoNotPrint();
5614 if (doNotPrint && !state.options.emitBindComments)
5619 emitSVAttributes(op);
5621 ps.addCallback({op,
true});
5624 <<
"/* This instance is elsewhere emitted as a bind statement."
5627 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5628 "attributes. The attributes will not be emitted.";
5631 SmallPtrSet<Operation *, 8> ops;
5636 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5637 assert(moduleOp &&
"Invalid IR");
5641 if (!op.getParameters().empty()) {
5644 bool printed =
false;
5646 llvm::zip(op.getParameters(),
5647 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5648 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5649 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5651 if (param.getValue() == modParam.getValue())
5656 ps <<
" #(" << PP::bbox2 << PP::newline;
5659 ps <<
"," << PP::newline;
5663 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5665 ps.invokeWithStringOS([&](
auto &os) {
5666 emitter.printParamValue(param.getValue(), os, [&]() {
5667 return op->emitOpError(
"invalid instance parameter '")
5668 << param.getName().getValue() <<
"' value";
5674 ps << PP::end << PP::newline <<
")";
5681 SmallVector<Value> instPortValues(modPortInfo.size());
5682 op.getValues(instPortValues, modPortInfo);
5683 emitInstancePortList(op, modPortInfo, instPortValues);
5685 ps.addCallback({op,
false});
5686 emitLocationInfoAndNewLine(ops);
5691 setPendingNewline();
5696void StmtEmitter::emitInstancePortList(Operation *op,
5698 ArrayRef<Value> instPortValues) {
5699 SmallPtrSet<Operation *, 8> ops;
5702 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5703 ModulePortInfo containingPortList(containingModule.getPortList());
5708 size_t maxNameLength = 0;
5709 for (
auto &elt : modPortInfo) {
5710 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5713 auto getWireForValue = [&](Value result) {
5714 return result.getUsers().begin()->getOperand(0);
5718 bool isFirst =
true;
5719 bool isZeroWidth =
false;
5721 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5723 auto &modPort = modPortInfo.
at(portNum);
5725 Value portVal = instPortValues[portNum];
5730 bool shouldPrintComma =
true;
5732 shouldPrintComma =
false;
5733 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5735 shouldPrintComma =
true;
5740 if (shouldPrintComma)
5743 emitLocationInfoAndNewLine(ops);
5758 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5759 auto modPortName = modPort.getVerilogName();
5761 ps.spaces(maxNameLength - modPortName.size() + 1);
5763 ps.scopedBox(PP::ibox0, [&]() {
5770 if (!modPort.isOutput()) {
5772 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5773 ps <<
"/* Zero width */";
5775 emitExpression(portVal, ops, LowestPrecedence);
5776 }
else if (portVal.use_empty()) {
5777 ps <<
"/* unused */";
5778 }
else if (portVal.hasOneUse() &&
5779 (output = dyn_cast_or_null<OutputOp>(
5780 portVal.getUses().begin()->getOwner()))) {
5785 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5787 containingPortList.atOutput(outputPortNo).getVerilogName());
5789 portVal = getWireForValue(portVal);
5790 emitExpression(portVal, ops);
5796 if (!isFirst || isZeroWidth) {
5797 emitLocationInfoAndNewLine(ops);
5810LogicalResult StmtEmitter::visitSV(BindOp op) {
5811 emitter.emitBind(op);
5812 assert(state.pendingNewline);
5816LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5817 emitComment(op.getCommentAttr());
5819 emitSVAttributes(op);
5822 ps.addCallback({op,
true});
5824 setPendingNewline();
5826 emitStatementBlock(*op.getBodyBlock());
5828 ps <<
"endinterface" << PP::newline;
5829 ps.addCallback({op,
false});
5830 setPendingNewline();
5835 emitSVAttributes(op);
5837 ps.addCallback({op,
true});
5839 ps << op.getContent();
5841 ps.addCallback({op,
false});
5842 setPendingNewline();
5846LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5848 emitSVAttributes(op);
5850 ps.addCallback({op,
true});
5852 ps << PP::neverbox <<
"// ";
5853 ps.invokeWithStringOS([&](
auto &os) {
5858 ps.invokeWithStringOS(
5859 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5863 ps.addCallback({op,
false});
5864 setPendingNewline();
5868LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5870 ps.addCallback({op,
true});
5874 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5875 auto port = cast<ModportStructAttr>(portAttr);
5876 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5877 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5878 ps << PPExtString(getSymOpName(signalDecl));
5882 ps.addCallback({op,
false});
5883 setPendingNewline();
5887LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5889 ps.addCallback({op,
true});
5890 SmallPtrSet<Operation *, 8> emitted;
5893 emitExpression(op.getIface(), emitted);
5894 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5895 emitExpression(op.getRhs(), emitted);
5897 ps.addCallback({op,
false});
5898 setPendingNewline();
5902LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5904 ps <<
"`" << op.getMacroIdentifier();
5905 setPendingNewline();
5909LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5910 auto decl = op.getReferencedMacro(&state.symbolCache);
5913 ps.addCallback({op,
true});
5915 if (decl.getArgs()) {
5917 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5918 ps << cast<StringAttr>(name);
5922 if (!op.getFormatString().empty()) {
5924 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5927 ps.addCallback({op,
false});
5928 setPendingNewline();
5932void StmtEmitter::emitStatement(Operation *op) {
5939 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5943 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5944 succeeded(dispatchVerifVisitor(op)))
5947 emitOpError(op,
"emission to Verilog not supported");
5948 emitPendingNewlineIfNeeded();
5949 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5950 setPendingNewline();
5961 StmtEmitter &stmtEmitter) {
5968 if (isa<IfDefProceduralOp>(op->getParentOp()))
5976 SmallVector<Value, 8> exprsToScan(op->getOperands());
5981 while (!exprsToScan.empty()) {
5982 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5989 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5990 auto *defOp = readInout.getOperand().getDefiningOp();
5997 if (isa<sv::WireOp>(defOp))
6002 if (!isa<RegOp, LogicOp>(defOp))
6008 if (isa<LogicOp>(defOp) &&
6009 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6013 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6014 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6022 exprsToScan.append(expr->getOperands().begin(),
6023 expr->getOperands().end());
6029 if (expr->getBlock() != op->getBlock())
6034 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6041template <
class AssignTy>
6043 AssignTy singleAssign;
6044 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6045 if (hasSVAttributes(user))
6048 if (auto assign = dyn_cast<AssignTy>(user)) {
6051 singleAssign = assign;
6055 return isa<ReadInOutOp>(user);
6057 return singleAssign;
6063 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6067 if (op1->getBlock() != user->getBlock())
6073 return op1->isBeforeInBlock(user);
6077LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6078 emitSVAttributes(op);
6079 auto value = op->getResult(0);
6080 SmallPtrSet<Operation *, 8> opsForLocation;
6081 opsForLocation.insert(op);
6083 ps.addCallback({op,
true});
6086 auto type = value.getType();
6092 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6094 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6095 unsigned targetColumn = 0;
6096 unsigned column = 0;
6099 if (maxDeclNameWidth > 0)
6100 targetColumn += maxDeclNameWidth + 1;
6103 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6104 }
else if (!word.empty()) {
6106 column += word.size();
6107 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6108 ps.spaces(numSpaces);
6109 column += numSpaces;
6112 SmallString<8> typeString;
6115 llvm::raw_svector_ostream stringStream(typeString);
6118 true, singleBitDefaultType);
6121 if (maxTypeWidth > 0)
6122 targetColumn += maxTypeWidth + 1;
6123 unsigned numSpaces = 0;
6124 if (!typeString.empty()) {
6126 column += typeString.size();
6129 if (targetColumn > column)
6130 numSpaces = targetColumn - column;
6131 ps.spaces(numSpaces);
6132 column += numSpaces;
6138 ps.invokeWithStringOS(
6139 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6142 if (state.options.printDebugInfo) {
6143 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6144 auto innerSym = innerSymOp.getInnerSymAttr();
6145 if (innerSym && !innerSym.empty()) {
6147 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6153 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6154 ps << PP::space <<
"=" << PP::space;
6155 ps.invokeWithStringOS([&](
auto &os) {
6156 emitter.printParamValue(localparam.getValue(), os, [&]() {
6157 return op->emitOpError(
"invalid localparam value");
6162 if (
auto regOp = dyn_cast<RegOp>(op)) {
6163 if (
auto initValue = regOp.getInit()) {
6164 ps << PP::space <<
"=" << PP::space;
6165 ps.scopedBox(PP::ibox0, [&]() {
6166 emitExpression(initValue, opsForLocation, LowestPrecedence,
6175 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6179 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6180 auto *source = singleAssign.getSrc().getDefiningOp();
6184 if (!source || isa<ConstantOp>(source) ||
6185 op->getNextNode() == singleAssign) {
6186 ps << PP::space <<
"=" << PP::space;
6187 ps.scopedBox(PP::ibox0, [&]() {
6188 emitExpression(singleAssign.getSrc(), opsForLocation,
6192 emitter.assignsInlined.insert(singleAssign);
6200 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6204 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6207 auto *source = singleAssign.getSrc().getDefiningOp();
6211 if (!source || isa<ConstantOp>(source) ||
6214 ps << PP::space <<
"=" << PP::space;
6215 ps.scopedBox(PP::ibox0, [&]() {
6216 emitExpression(singleAssign.getSrc(), opsForLocation,
6221 emitter.assignsInlined.insert(singleAssign);
6222 emitter.expressionsEmittedIntoDecl.insert(op);
6229 ps.addCallback({op,
false});
6230 emitLocationInfoAndNewLine(opsForLocation);
6234void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6237 NameCollector collector(emitter);
6238 collector.collectNames(block);
6241 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6242 maxTypeWidth = collector.getMaxTypeWidth();
6245void StmtEmitter::emitStatementBlock(Block &body) {
6246 ps.scopedBox(PP::bbox2, [&]() {
6251 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6252 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6257 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6258 collectNamesAndCalculateDeclarationWidths(body);
6261 for (
auto &op : body) {
6268void ModuleEmitter::emitStatement(Operation *op) {
6269 StmtEmitter(*
this, state.options).emitStatement(op);
6274void ModuleEmitter::emitSVAttributes(Operation *op) {
6282 setPendingNewline();
6289void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6290 auto verilogName =
module.getVerilogModuleNameAttr();
6292 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6294 setPendingNewline();
6303void ModuleEmitter::emitBind(BindOp op) {
6305 emitError(op,
"SV attributes emission is unimplemented for the op");
6306 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6312 Operation *childMod =
6313 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6317 ps.addCallback({op,
true});
6318 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6319 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6321 bool isFirst =
true;
6322 ps.scopedBox(PP::bbox2, [&]() {
6323 auto parentPortInfo = parentMod.getPortList();
6327 size_t maxNameLength = 0;
6328 for (
auto &elt : childPortInfo) {
6329 auto portName = elt.getVerilogName();
6330 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6331 maxNameLength = std::max(maxNameLength, elt.getName().size());
6334 SmallVector<Value> instPortValues(childPortInfo.size());
6335 inst.getValues(instPortValues, childPortInfo);
6337 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6339 Value portVal = instPortValues[idx];
6345 bool shouldPrintComma =
true;
6347 shouldPrintComma =
false;
6348 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6350 shouldPrintComma =
true;
6355 if (shouldPrintComma)
6368 ps << PP::neverbox <<
"//";
6372 ps.nbsp(maxNameLength - elt.getName().size());
6374 llvm::SmallPtrSet<Operation *, 4> ops;
6375 if (elt.isOutput()) {
6376 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6377 "output port must have either single or no use");
6378 if (portVal.use_empty()) {
6379 ps <<
"/* unused */";
6380 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6381 portVal.getUses().begin()->getOwner())) {
6384 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6386 parentPortList.atOutput(outputPortNo).getVerilogName());
6388 portVal = portVal.getUsers().begin()->getOperand(0);
6389 ExprEmitter(*
this, ops)
6390 .emitExpression(portVal, LowestPrecedence,
6394 ExprEmitter(*
this, ops)
6395 .emitExpression(portVal, LowestPrecedence,
6408 ps.addCallback({op,
false});
6409 setPendingNewline();
6412void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6414 emitError(op,
"SV attributes emission is unimplemented for the op");
6416 auto instance = op.getReferencedInstance(&state.symbolCache);
6418 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6419 instance.getInterfaceType().getInterface());
6421 ps.addCallback({op,
true});
6422 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6423 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6425 ps.addCallback({op,
false});
6426 setPendingNewline();
6429void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6433 auto printParamType = [&](Type type, Attribute defaultValue,
6434 SmallString<8> &result) {
6436 llvm::raw_svector_ostream sstream(result);
6441 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6442 if (intAttr.getValue().getBitWidth() == 32)
6444 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6445 if (fpAttr.getType().isF64())
6448 if (isa<NoneType>(type))
6455 if (
auto intType = type_dyn_cast<IntegerType>(type))
6456 if (intType.getWidth() == 32) {
6457 sstream <<
"/*integer*/";
6461 printPackedType(type, sstream, module->getLoc(),
6469 size_t maxTypeWidth = 0;
6470 SmallString<8> scratch;
6471 for (
auto param : params) {
6472 auto paramAttr = cast<ParamDeclAttr>(param);
6474 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6475 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6478 if (maxTypeWidth > 0)
6481 ps.scopedBox(PP::bbox2, [&]() {
6482 ps << PP::newline <<
"#(";
6483 ps.scopedBox(PP::cbox0, [&]() {
6486 [&](Attribute param) {
6487 auto paramAttr = cast<ParamDeclAttr>(param);
6488 auto defaultValue = paramAttr.getValue();
6490 printParamType(paramAttr.getType(), defaultValue, scratch);
6491 if (!scratch.empty())
6493 if (scratch.size() < maxTypeWidth)
6494 ps.nbsp(maxTypeWidth - scratch.size());
6496 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6497 module, paramAttr.getName()));
6501 ps.invokeWithStringOS([&](
auto &os) {
6503 return module->emitError("parameter '")
6504 << paramAttr.getName().getValue()
6505 << "' has invalid value";
6510 [&]() { ps <<
"," << PP::newline; });
6516void ModuleEmitter::emitPortList(Operation *module,
6518 bool emitAsTwoStateType) {
6520 if (portInfo.
size())
6521 emitLocationInfo(module->getLoc());
6525 bool hasOutputs =
false, hasZeroWidth =
false;
6526 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6527 SmallVector<SmallString<8>, 16> portTypeStrings;
6529 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6530 auto port = portInfo.
at(i);
6534 lastNonZeroPort = i;
6537 portTypeStrings.push_back({});
6539 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6541 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6544 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6547 if (maxTypeWidth > 0)
6551 ps.scopedBox(PP::bbox2, [&]() {
6552 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6553 auto lastPort = e - 1;
6556 auto portType = portInfo.
at(portIdx).
type;
6560 bool isZeroWidth =
false;
6565 ps << (isZeroWidth ?
"// " :
" ");
6569 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6570 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6571 (state.options.emitWireInPorts ? 5 : 0) +
6576 if (!isa<ModportType>(portType)) {
6577 switch (thisPortDirection) {
6578 case ModulePort::Direction::Output:
6581 case ModulePort::Direction::Input:
6582 ps << (hasOutputs ?
"input " :
"input ");
6584 case ModulePort::Direction::InOut:
6585 ps << (hasOutputs ?
"inout " :
"inout ");
6588 if (state.options.emitWireInPorts)
6590 if (!portTypeStrings[portIdx].
empty())
6591 ps << portTypeStrings[portIdx];
6592 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6593 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6595 ps << portTypeStrings[portIdx];
6596 if (portTypeStrings[portIdx].size() < startOfNamePos)
6597 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6604 ps.invokeWithStringOS(
6605 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6608 auto innerSym = portInfo.
at(portIdx).
getSym();
6609 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6611 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6616 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6620 if (
auto loc = portInfo.
at(portIdx).
loc)
6621 emitLocationInfo(loc);
6631 if (!state.options.disallowPortDeclSharing) {
6632 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6635 auto port = portInfo.
at(portIdx);
6639 bool isZeroWidth =
false;
6644 ps << (isZeroWidth ?
"// " :
" ");
6647 ps.nbsp(startOfNamePos);
6650 StringRef name = port.getVerilogName();
6654 ps.invokeWithStringOS(
6655 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6658 auto sym = port.getSym();
6659 if (state.options.printDebugInfo && sym && !sym.empty())
6660 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6664 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6668 if (
auto loc = port.loc)
6669 emitLocationInfo(loc);
6680 if (!portInfo.
size()) {
6682 SmallPtrSet<Operation *, 8> moduleOpSet;
6683 moduleOpSet.insert(module);
6684 emitLocationInfoAndNewLine(moduleOpSet);
6687 ps <<
");" << PP::newline;
6688 setPendingNewline();
6692void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6693 currentModuleOp =
module;
6695 emitComment(module.getCommentAttr());
6696 emitSVAttributes(module);
6698 ps.addCallback({module,
true});
6702 emitParameters(module, module.getParameters());
6706 assert(state.pendingNewline);
6709 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6712 ps.addCallback({module,
false});
6714 setPendingNewline();
6716 currentModuleOp =
nullptr;
6719void ModuleEmitter::emitFunc(FuncOp func) {
6721 if (func.isDeclaration())
6724 currentModuleOp = func;
6726 ps.addCallback({func,
true});
6730 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6732 ps <<
"endfunction";
6734 currentModuleOp =
nullptr;
6743 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6750 void emit(emit::FileListOp op);
6753 void emit(Block *block);
6755 void emitOp(emit::RefOp op);
6756 void emitOp(emit::VerbatimOp op);
6760 for (Operation &op : *block) {
6761 TypeSwitch<Operation *>(&op)
6762 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6763 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6764 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6765 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6766 .Case<BindInterfaceOp>(
6767 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6768 .Case<TypeScopeOp>([&](
auto typedecls) {
6769 ModuleEmitter(state).emitStatement(typedecls);
6772 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6778 for (
auto sym : op.getFiles()) {
6779 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6781 auto it = state.fileMapping.find(fileName);
6782 if (it == state.fileMapping.end()) {
6783 emitOpError(op,
" references an invalid file: ") << sym;
6787 auto file = cast<emit::FileOp>(it->second);
6788 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6795 StringAttr target = op.getTargetAttr().getAttr();
6796 auto *targetOp = state.symbolCache.getDefinition(target);
6797 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6799 TypeSwitch<Operation *>(targetOp)
6800 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6801 .Case<hw::HWModuleOp>(
6802 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6803 .Case<TypeScopeOp>([&](
auto typedecls) {
6804 ModuleEmitter(state).emitStatement(typedecls);
6807 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6813 SmallPtrSet<Operation *, 8> ops;
6818 StringRef text = op.getText();
6822 const auto &[lhs, rhs] = text.split(
'\n');
6826 ps << PP::end << PP::newline << PP::neverbox;
6828 }
while (!text.empty());
6831 emitLocationInfoAndNewLine(ops);
6849 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6850 moduleOp->walk([&](Operation *op) {
6852 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6855 SymbolTable::getSymbolAttrName()),
6856 name.getSymName(), op);
6857 if (isa<BindOp>(op))
6863 auto collectPorts = [&](
auto moduleOp) {
6864 auto portInfo = moduleOp.getPortList();
6865 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6866 if (!p.attrs || p.attrs.empty())
6868 for (NamedAttribute portAttr : p.attrs) {
6869 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6878 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6879 for (
auto file :
designOp.getOps<emit::FileOp>())
6880 for (
auto refs : file.getOps<emit::RefOp>())
6881 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6883 SmallString<32> outputPath;
6884 for (
auto &op : *
designOp.getBody()) {
6887 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6889 bool hasFileName =
false;
6890 bool emitReplicatedOps = !isFileOp;
6891 bool addToFilelist = !isFileOp;
6897 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6899 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6900 <<
" on " << op <<
"\n";);
6901 if (!attr.isDirectory())
6904 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6905 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6908 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6913 if (!defaultFileName.isTriviallyEmpty()) {
6914 llvm::sys::path::append(outputPath, defaultFileName);
6916 op->emitError(
"file name unspecified");
6918 llvm::sys::path::append(outputPath,
"error.out");
6922 auto destFile = StringAttr::get(op->getContext(), outputPath);
6923 auto &file =
files[destFile];
6924 file.ops.push_back(info);
6925 file.emitReplicatedOps = emitReplicatedOps;
6926 file.addToFilelist = addToFilelist;
6927 file.isVerilog = outputPath.ends_with(
".sv");
6932 if (!attr || attr.isDirectory()) {
6933 auto excludeFromFileListAttr =
6934 BoolAttr::get(op->getContext(), !addToFilelist);
6935 auto includeReplicatedOpsAttr =
6936 BoolAttr::get(op->getContext(), emitReplicatedOps);
6937 auto outputFileAttr = hw::OutputFileAttr::get(
6938 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6939 op->setAttr(
"output_file", outputFileAttr);
6945 TypeSwitch<Operation *>(&op)
6946 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6948 fileMapping.try_emplace(file.getSymNameAttr(), file);
6949 separateFile(file, file.getFileName());
6951 .Case<emit::FragmentOp>([&](
auto fragment) {
6954 .Case<HWModuleOp>([&](
auto mod) {
6956 auto sym = mod.getNameAttr();
6959 collectInstanceSymbolsAndBinds(mod);
6961 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6962 if (it->second.size() != 1 || attr) {
6965 op.emitError(
"modules can be emitted to a single file");
6973 if (attr || separateModules)
6979 .Case<InterfaceOp>([&](InterfaceOp intf) {
6984 for (
auto &op : *intf.getBodyBlock())
6985 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6986 if (
auto name = symOp.getNameAttr())
6990 if (attr || separateModules)
6991 separateFile(intf, intf.getSymName() +
".sv");
6997 separateFile(op, op.getOutputFile().getFilename().getValue());
6999 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7005 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7006 [&](Operation *op) {
7012 separateFile(op,
"");
7014 .Case<FuncOp>([&](
auto op) {
7020 separateFile(op,
"");
7024 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7027 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7036 separateFile(op,
"");
7038 .Case<BindOp>([&](
auto op) {
7040 separateFile(op,
"bindfile.sv");
7045 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
7046 .Case<MacroDeclOp>([&](
auto op) {
7049 .Case<sv::ReserveNamesOp>([](
auto op) {
7052 .Case<om::ClassLike>([&](
auto op) {
7055 .Case<om::ConstantOp>([&](
auto op) {
7058 .Default([&](
auto *) {
7059 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7079 size_t lastReplicatedOp = 0;
7081 bool emitHeaderInclude =
7084 if (emitHeaderInclude)
7087 size_t numReplicatedOps =
7092 DenseSet<emit::FragmentOp> includedFragments;
7093 for (
const auto &opInfo : file.
ops) {
7094 Operation *op = opInfo.op;
7098 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7104 if (
auto fragments =
7106 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7110 op->emitError(
"cannot find referenced fragment ") << sym;
7113 emit::FragmentOp fragment = it->second;
7114 if (includedFragments.insert(fragment).second) {
7115 thingsToEmit.emplace_back(it->second);
7121 thingsToEmit.emplace_back(op);
7126 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7131 TypeSwitch<Operation *>(op)
7132 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7133 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7136 .Case<HWModuleGeneratedOp>(
7137 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7138 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7139 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7140 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7141 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7142 .Case<TypeScopeOp>([&](
auto typedecls) {
7143 ModuleEmitter(state).emitStatement(typedecls);
7145 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7147 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7148 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7149 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7150 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7151 .Default([&](
auto *op) {
7152 state.encounteredError =
true;
7153 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7160 llvm::formatted_raw_ostream &os,
7161 StringAttr fileName,
bool parallelize) {
7166 parallelize &=
context->isMultithreadingEnabled();
7177 size_t lineOffset = 0;
7178 for (
auto &entry : thingsToEmit) {
7179 entry.verilogLocs.setStream(os);
7180 if (
auto *op = entry.getOperation()) {
7185 state.addVerilogLocToOps(lineOffset, fileName);
7187 os << entry.getStringData();
7192 if (state.encounteredError)
7210 SmallString<256> buffer;
7211 llvm::raw_svector_ostream tmpStream(buffer);
7212 llvm::formatted_raw_ostream rs(tmpStream);
7220 if (state.encounteredError)
7225 for (
auto &entry : thingsToEmit) {
7228 auto *op = entry.getOperation();
7230 auto lineOffset = os.getLine() + 1;
7231 os << entry.getStringData();
7235 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7238 entry.verilogLocs.setStream(os);
7245 state.addVerilogLocToOps(0, fileName);
7246 if (state.encounteredError) {
7265 module.emitWarning()
7266 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7267 "created only at SplitExportVerilog";
7276 for (
const auto &it : emitter.
files) {
7277 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7278 "\" ----- 8< -----\n\n");
7284 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7285 "\" ----- 8< -----\n\n");
7286 for (
auto &name : it.second)
7287 contents += name.str() +
"\n";
7288 list.emplace_back(contents);
7291 llvm::formatted_raw_ostream rs(os);
7295 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7302 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7304 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7305 if (failed(failableParallelForEach(
7306 module->getContext(), modulesToPrepare,
7307 [&](
auto op) { return prepareHWModule(op, options); })))
7314struct ExportVerilogPass
7315 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7316 ExportVerilogPass(raw_ostream &os) : os(os) {}
7317 void runOnOperation()
override {
7319 mlir::OpPassManager preparePM(
"builtin.module");
7320 preparePM.addPass(createLegalizeAnonEnums());
7321 auto &modulePM = preparePM.nestAny();
7322 modulePM.addPass(createPrepareForEmission());
7323 if (failed(runPipeline(preparePM, getOperation())))
7324 return signalPassFailure();
7327 return signalPassFailure();
7334struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7335 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7336 : ExportVerilogPass{*os} {
7337 owned = std::move(os);
7341 std::unique_ptr<llvm::raw_ostream> owned;
7345std::unique_ptr<mlir::Pass>
7347 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7350std::unique_ptr<mlir::Pass>
7352 return std::make_unique<ExportVerilogPass>(os);
7363static std::unique_ptr<llvm::ToolOutputFile>
7367 SmallString<128> outputFilename(dirname);
7369 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7372 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7374 emitter.
designOp.emitError(
"cannot create output directory \"")
7375 << outputDir <<
"\": " << error.message();
7381 std::string errorMessage;
7382 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7384 emitter.
designOp.emitError(errorMessage);
7401 llvm::formatted_raw_ostream rs(output->os());
7407 StringAttr::get(fileName.getContext(), output->getFilename()),
7413 StringRef dirname) {
7424 bool insertSuccess =
7426 .insert({StringAttr::get(module.getContext(),
circtHeader),
7432 if (!insertSuccess) {
7433 module.emitError() << "tried to emit a heder to " << circtHeader
7434 << ", but the file is used as an output too.";
7440 parallelForEach(module->getContext(), emitter.
files.begin(),
7441 emitter.
files.end(), [&](
auto &it) {
7442 createSplitOutputFile(it.first, it.second, dirname,
7447 SmallString<128> filelistPath(dirname);
7448 llvm::sys::path::append(filelistPath,
"filelist.f");
7450 std::string errorMessage;
7451 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7453 module->emitError(errorMessage);
7457 for (
const auto &it : emitter.
files) {
7458 if (it.second.addToFilelist)
7459 output->os() << it.first.str() <<
"\n";
7468 for (
auto &name : it.second)
7469 output->os() << name.str() <<
"\n";
7478 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7480 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7481 if (failed(failableParallelForEach(
7482 module->getContext(), modulesToPrepare,
7483 [&](
auto op) { return prepareHWModule(op, options); })))
7491struct ExportSplitVerilogPass
7492 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7493 ExportSplitVerilogPass(StringRef directory) {
7494 directoryName = directory.str();
7496 void runOnOperation()
override {
7498 mlir::OpPassManager preparePM(
"builtin.module");
7501 modulePM.addPass(createPrepareForEmission());
7502 if (failed(runPipeline(preparePM, getOperation())))
7503 return signalPassFailure();
7506 return signalPassFailure();
7511std::unique_ptr<mlir::Pass>
7513 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static bool hasSVAttributes(Operation *op)
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool haveMatchingDims(Type a, Type b, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
True iff 'a' and 'b' have the same wire dims.
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc, llvm::function_ref< mlir::InFlightDiagnostic(Location)> errorHandler)
Push this type's dimension into a vector.
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static std::unique_ptr< Context > context
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
Signals that an operation's regions are procedural.
This stores lookup tables to make manipulating and working with the IR more efficient.
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=invalidPort)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
This helps visit TypeOp nodes.
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
PP
Send one of these to TokenStream to add the corresponding token.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
Information to control the emission of a single operation into a file.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
const LoweringOptions & options
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.