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, ConcatStrOp>(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);
2360 SubExprInfo visitSV(ConcatStrOp op);
2362 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2363 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2365 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2370 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2371 emitSVAttributes(op);
2374 SubExprInfo visitSV(ArrayIndexInOutOp op);
2375 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2376 SubExprInfo visitSV(IndexedPartSelectOp op);
2377 SubExprInfo visitSV(StructFieldInOutOp op);
2380 SubExprInfo visitSV(SampledOp op);
2383 SubExprInfo visitSV(TimeOp op);
2384 SubExprInfo visitSV(STimeOp op);
2387 using TypeOpVisitor::visitTypeOp;
2389 SubExprInfo visitTypeOp(AggregateConstantOp op);
2391 SubExprInfo visitTypeOp(ParamValueOp op);
2398 SubExprInfo visitTypeOp(StructInjectOp op);
2399 SubExprInfo visitTypeOp(UnionCreateOp op);
2400 SubExprInfo visitTypeOp(UnionExtractOp op);
2401 SubExprInfo visitTypeOp(EnumCmpOp op);
2402 SubExprInfo visitTypeOp(EnumConstantOp op);
2405 using CombinationalVisitor::visitComb;
2406 SubExprInfo visitComb(
MuxOp op);
2407 SubExprInfo visitComb(ReverseOp op);
2408 SubExprInfo visitComb(
AddOp op) {
2409 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2410 return emitBinary(op, Addition,
"+");
2412 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2413 SubExprInfo visitComb(
MulOp op) {
2414 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2415 return emitBinary(op, Multiply,
"*");
2417 SubExprInfo visitComb(
DivUOp op) {
2418 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2420 SubExprInfo visitComb(
DivSOp op) {
2421 return emitBinary(op, Multiply,
"/",
2422 EB_RequireSignedOperands | EB_ForceResultSigned);
2424 SubExprInfo visitComb(
ModUOp op) {
2425 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2427 SubExprInfo visitComb(
ModSOp op) {
2428 return emitBinary(op, Multiply,
"%",
2429 EB_RequireSignedOperands | EB_ForceResultSigned);
2431 SubExprInfo visitComb(
ShlOp op) {
2432 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2434 SubExprInfo visitComb(
ShrUOp op) {
2436 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2438 SubExprInfo visitComb(
ShrSOp op) {
2441 return emitBinary(op, Shift,
">>>",
2442 EB_RequireSignedOperands | EB_ForceResultSigned |
2443 EB_RHS_UnsignedWithSelfDeterminedWidth);
2445 SubExprInfo visitComb(
AndOp op) {
2446 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2447 return emitBinary(op, And,
"&");
2449 SubExprInfo visitComb(
OrOp op) {
2450 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2451 return emitBinary(op, Or,
"|");
2453 SubExprInfo visitComb(
XorOp op) {
2454 if (op.isBinaryNot())
2455 return emitUnary(op,
"~");
2456 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2457 return emitBinary(op, Xor,
"^");
2462 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2464 SubExprInfo visitComb(ReplicateOp op);
2465 SubExprInfo visitComb(
ConcatOp op);
2467 SubExprInfo visitComb(ICmpOp op);
2469 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2470 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2471 "not printed within an assignment-like context");
2472 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2473 "supposed to spill such expressions";
2477 SubExprInfo printStructCreate(
2478 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2480 bool printAsPattern, Operation *op);
2483 ModuleEmitter &emitter;
2490 SubExprSignRequirement signPreference = NoRequirement;
2494 SmallPtrSetImpl<Operation *> &emittedExprs;
2497 SmallVector<Token> localTokens;
2511 bool isAssignmentLikeContext =
false;
2515SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2517 unsigned emitBinaryFlags) {
2519 emitError(op,
"SV attributes emission is unimplemented for the op");
2530 if (emitBinaryFlags & EB_ForceResultSigned)
2531 ps <<
"$signed(" << PP::ibox0;
2532 auto operandSignReq =
2533 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2534 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2537 auto lhsSpace = (prec == VerilogPrecedence::Comparison ||
2538 prec == VerilogPrecedence::Equality)
2542 ps << lhsSpace << syntax << PP::nbsp;
2549 auto rhsPrec = prec;
2550 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2551 rhsPrec = VerilogPrecedence(prec - 1);
2556 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2557 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2558 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2559 operandSignReq = NoRequirement;
2562 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2563 rhsIsUnsignedValueWithSelfDeterminedWidth);
2567 SubExprSignResult signedness = IsUnsigned;
2568 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2569 signedness = IsSigned;
2571 if (emitBinaryFlags & EB_ForceResultSigned) {
2572 ps << PP::end <<
")";
2573 signedness = IsSigned;
2577 return {prec, signedness};
2580SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2581 bool resultAlwaysUnsigned) {
2583 emitError(op,
"SV attributes emission is unimplemented for the op");
2586 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2590 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2591 resultAlwaysUnsigned ? IsUnsigned : signedness};
2596void ExprEmitter::emitSVAttributes(Operation *op) {
2610 auto concat = value.getDefiningOp<
ConcatOp>();
2611 if (!concat || concat.getNumOperands() != 2)
2614 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2615 if (constant && constant.getValue().isZero())
2616 return concat.getOperand(1);
2626SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2627 VerilogPrecedence parenthesizeIfLooserThan,
2628 SubExprSignRequirement signRequirement,
2629 bool isSelfDeterminedUnsignedValue,
2630 bool isAssignmentLikeContext) {
2632 if (
auto result = dyn_cast<OpResult>(exp))
2633 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2634 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2635 parenthesizeIfLooserThan, signRequirement,
2636 isSelfDeterminedUnsignedValue,
2637 isAssignmentLikeContext);
2641 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2646 auto *op = exp.getDefiningOp();
2650 if (!shouldEmitInlineExpr) {
2653 if (signRequirement == RequireSigned) {
2655 return {Symbol, IsSigned};
2659 return {Symbol, IsUnsigned};
2662 unsigned subExprStartIndex = buffer.tokens.size();
2664 ps.addCallback({op,
true});
2665 llvm::scope_exit done([&]() {
2667 ps.addCallback({op, false});
2673 signPreference = signRequirement;
2675 bool bitCastAdded =
false;
2676 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2678 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2679 ps.addAsString(inType.getWidth());
2680 ps <<
"'(" << PP::ibox0;
2681 bitCastAdded =
true;
2685 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2686 isAssignmentLikeContext);
2687 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2693 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2695 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2697 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2698 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2701 expInfo.signedness = IsSigned;
2702 expInfo.precedence = Selection;
2703 }
else if (signRequirement == RequireUnsigned &&
2704 expInfo.signedness == IsSigned) {
2707 expInfo.signedness = IsUnsigned;
2708 expInfo.precedence = Selection;
2709 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2716 expInfo.precedence = Selection;
2723 emittedExprs.insert(exp.getDefiningOp());
2727SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2728 auto openFn = [&]() {
2730 ps.addAsString(op.getMultiple());
2733 auto closeFn = [&]() { ps <<
"}}"; };
2737 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2738 if (op.getOperand().hasOneUse()) {
2739 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2740 return {Symbol, IsUnsigned};
2743 emitBracedList(op.getOperand(), openFn, closeFn);
2744 return {Symbol, IsUnsigned};
2747SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2748 emitBracedList(op.getOperands());
2749 return {Symbol, IsUnsigned};
2752SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2756 Type toType = op.getType();
2758 toType, op.getInput().getType(), op.getLoc(),
2759 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2761 ps.invokeWithStringOS(
2762 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2765 return emitSubExpr(op.getInput(), LowestPrecedence);
2768SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2769 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2770 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2771 SubExprSignRequirement signop[] = {
2773 NoRequirement, NoRequirement,
2775 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2777 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2779 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2781 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2782 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2785 if (op.isEqualAllOnes())
2786 return emitUnary(op,
"&",
true);
2789 if (op.isNotEqualZero())
2790 return emitUnary(op,
"|",
true);
2792 VerilogPrecedence precedence = Comparison;
2793 switch (op.getPredicate()) {
2794 case ICmpPredicate::eq:
2795 case ICmpPredicate::ne:
2796 case ICmpPredicate::ceq:
2797 case ICmpPredicate::cne:
2798 case ICmpPredicate::weq:
2799 case ICmpPredicate::wne:
2800 precedence = Equality;
2803 precedence = Comparison;
2806 auto result = emitBinary(op, precedence, symop[pred], signop[pred]);
2810 result.signedness = IsUnsigned;
2814SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2816 emitError(op,
"SV attributes emission is unimplemented for the op");
2818 unsigned loBit = op.getLowBit();
2819 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2821 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2822 assert((x.precedence == Symbol ||
2824 "should be handled by isExpressionUnableToInline");
2829 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2833 ps.addAsString(hiBit);
2834 if (hiBit != loBit) {
2836 ps.addAsString(loBit);
2839 return {Unary, IsUnsigned};
2842SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2844 emitError(op,
"SV attributes emission is unimplemented for the op");
2846 auto decl = op.getReferencedDecl(state.symbolCache);
2849 return {Selection, IsUnsigned};
2852SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2854 emitError(op,
"SV attributes emission is unimplemented for the op");
2857 ps.scopedBox(PP::ibox0, [&]() {
2859 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2860 [&]() { ps <<
"," << PP::space; });
2863 return {Symbol, IsUnsigned};
2866SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2868 emitError(op,
"SV attributes emission is unimplemented for the op");
2870 auto decl = op.getReferencedDecl(state.symbolCache);
2874 return {Selection, IsUnsigned};
2877SubExprInfo ExprEmitter::visitSV(XMROp op) {
2879 emitError(op,
"SV attributes emission is unimplemented for the op");
2881 if (op.getIsRooted())
2883 for (
auto s : op.getPath())
2884 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2886 return {Selection, IsUnsigned};
2891SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2893 emitError(op,
"SV attributes emission is unimplemented for the op");
2896 auto globalRef = op.getReferencedPath(&state.symbolCache);
2897 auto namepath = globalRef.getNamepathAttr().getValue();
2898 auto *
module = state.symbolCache.getDefinition(
2899 cast<InnerRefAttr>(namepath.front()).getModule());
2901 for (
auto sym : namepath) {
2903 auto innerRef = cast<InnerRefAttr>(sym);
2904 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2905 innerRef.getName());
2906 if (ref.hasPort()) {
2912 auto leaf = op.getVerbatimSuffixAttr();
2913 if (leaf && leaf.size())
2915 return {Selection, IsUnsigned};
2918SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2920 emitError(op,
"SV attributes emission is unimplemented for the op");
2922 emitTextWithSubstitutions(
2923 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2924 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2926 return {Unary, IsUnsigned};
2929template <
typename MacroTy>
2930SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2932 emitError(op,
"SV attributes emission is unimplemented for the op");
2935 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2936 assert(macroOp &&
"Invalid IR");
2938 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2940 if (!op.getInputs().empty()) {
2942 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2943 emitExpression(val, LowestPrecedence, false);
2947 return {LowestPrecedence, IsUnsigned};
2950SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2951 return emitMacroCall(op);
2954SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2955 return emitMacroCall(op);
2958SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2960 emitError(op,
"SV attributes emission is unimplemented for the op");
2962 ps.addAsString(op.getWidth());
2964 return {Unary, IsUnsigned};
2967SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2969 emitError(op,
"SV attributes emission is unimplemented for the op");
2971 ps.writeQuotedEscaped(op.getStr());
2972 return {Symbol, IsUnsigned};
2975SubExprInfo ExprEmitter::visitSV(ConcatStrOp op) {
2977 emitError(op,
"SV attributes emission is unimplemented for the op");
2981 emitBracedList(op.getInputs());
2982 return {Symbol, IsUnsigned};
2985SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2987 emitError(op,
"SV attributes emission is unimplemented for the op");
2989 ps.addAsString(op.getWidth());
2991 return {Unary, IsUnsigned};
2994SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2995 bool isNegated =
false;
2998 if (signPreference == RequireSigned && value.isNegative() &&
2999 !value.isMinSignedValue()) {
3004 ps.addAsString(type.getWidth());
3008 if (signPreference == RequireSigned)
3014 SmallString<32> valueStr;
3016 (-value).toStringUnsigned(valueStr, 16);
3018 value.toStringUnsigned(valueStr, 16);
3021 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
3024SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
3026 emitError(op,
"SV attributes emission is unimplemented for the op");
3028 auto value = op.getValue();
3032 if (value.getBitWidth() == 0) {
3033 emitOpError(op,
"will not emit zero width constants in the general case");
3034 ps <<
"<<unsupported zero width constant: "
3035 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3036 return {Unary, IsUnsigned};
3039 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3042void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3043 bool printAsPattern, Operation *op) {
3044 if (printAsPattern && !isAssignmentLikeContext)
3045 emitAssignmentPatternContextError(op);
3046 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3049 elementValues, [&]() { ps << openDelim; },
3050 [&](Attribute elementValue) {
3051 printConstantAggregate(elementValue,
elementType, op);
3053 [&]() { ps <<
"}"; });
3056void ExprEmitter::printConstantStruct(
3057 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3058 bool printAsPattern, Operation *op) {
3059 if (printAsPattern && !isAssignmentLikeContext)
3060 emitAssignmentPatternContextError(op);
3067 auto fieldRange = llvm::make_filter_range(
3068 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3073 if (printAsPattern) {
3075 fieldRange, [&]() { ps <<
"'{"; },
3076 [&](
const auto &fieldAndValue) {
3077 ps.scopedBox(PP::ibox2, [&]() {
3078 const auto &[field, value] = fieldAndValue;
3079 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3080 <<
":" << PP::space;
3081 printConstantAggregate(value, field.type, op);
3084 [&]() { ps <<
"}"; });
3087 fieldRange, [&]() { ps <<
"{"; },
3088 [&](
const auto &fieldAndValue) {
3089 ps.scopedBox(PP::ibox2, [&]() {
3090 const auto &[field, value] = fieldAndValue;
3091 printConstantAggregate(value, field.type, op);
3094 [&]() { ps <<
"}"; });
3098void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3101 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3102 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3103 isAssignmentLikeContext, op);
3106 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3107 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3111 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3112 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3113 isAssignmentLikeContext, op);
3115 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3116 auto value = cast<IntegerAttr>(attr).getValue();
3117 printConstantScalar(value, intType);
3121 emitOpError(op,
"contains constant of type ")
3122 << type <<
" which cannot be emitted as Verilog";
3125SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3127 emitError(op,
"SV attributes emission is unimplemented for the op");
3131 "zero-bit types not allowed at this point");
3133 printConstantAggregate(op.getFields(), op.getType(), op);
3134 return {Symbol, IsUnsigned};
3137SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3139 emitError(op,
"SV attributes emission is unimplemented for the op");
3141 return ps.invokeWithStringOS([&](
auto &os) {
3142 return emitter.printParamValue(op.getValue(), os, [&]() {
3143 return op->emitOpError(
"invalid parameter use");
3152 emitError(op,
"SV attributes emission is unimplemented for the op");
3154 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3156 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3158 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3160 ps.addAsString(dstWidth);
3162 return {Selection, arrayPrec.signedness};
3165SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3166 emitSubExpr(op.getInput(), Selection);
3171 emitSubExpr(op.getIndex(), LowestPrecedence);
3173 emitSVAttributes(op);
3174 return {Selection, IsUnsigned};
3180 emitError(op,
"SV attributes emission is unimplemented for the op");
3182 if (op.isUniform()) {
3184 ps.addAsString(op.getInputs().size());
3186 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3190 op.getInputs(), [&]() { ps <<
"{"; },
3193 emitSubExprIBox2(v);
3196 [&]() { ps <<
"}"; });
3198 return {Unary, IsUnsigned};
3201SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3203 emitError(op,
"SV attributes emission is unimplemented for the op");
3206 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3207 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3208 return {Unary, IsUnsigned};
3213 emitError(op,
"SV attributes emission is unimplemented for the op");
3215 emitBracedList(op.getOperands());
3216 return {Unary, IsUnsigned};
3219SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3221 emitError(op,
"SV attributes emission is unimplemented for the op");
3223 auto index = op.getIndex();
3224 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3229 emitSubExpr(index, LowestPrecedence);
3231 return {Selection, arrayPrec.signedness};
3234SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3236 emitError(op,
"SV attributes emission is unimplemented for the op");
3238 auto prec = emitSubExpr(op.getInput(), Selection);
3240 emitSubExpr(op.getBase(), LowestPrecedence);
3241 if (op.getDecrement())
3245 ps.addAsString(op.getWidth());
3247 return {Selection, prec.signedness};
3250SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3252 emitError(op,
"SV attributes emission is unimplemented for the op");
3254 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3256 emitSubExpr(op.getBase(), LowestPrecedence);
3257 if (op.getDecrement())
3261 ps.addAsString(op.getWidth());
3266SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3268 emitError(op,
"SV attributes emission is unimplemented for the op");
3270 auto prec = emitSubExpr(op.getInput(), Selection);
3272 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3273 return {Selection, prec.signedness};
3276SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3278 emitError(op,
"SV attributes emission is unimplemented for the op");
3281 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3286SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3288 emitError(op,
"SV attributes emission is unimplemented for the op");
3291 ps.scopedBox(PP::ibox0, [&]() {
3292 ps.writeQuotedEscaped(op.getFormatString());
3299 for (
auto operand : op.getSubstitutions()) {
3300 ps <<
"," << PP::space;
3301 emitSubExpr(operand, LowestPrecedence);
3305 return {Symbol, IsUnsigned};
3308SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3310 emitError(op,
"SV attributes emission is unimplemented for the op");
3313 return {Symbol, IsUnsigned};
3316SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3318 emitError(op,
"SV attributes emission is unimplemented for the op");
3321 return {Symbol, IsUnsigned};
3324SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3338 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3339 ps.scopedBox(PP::ibox0, [&]() {
3340 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3344 emitSVAttributes(op);
3346 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3347 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3351 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3352 return emitSubExpr(op.getFalseValue(), Conditional);
3355 SubExprSignResult signedness = IsUnsigned;
3356 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3357 signedness = IsSigned;
3359 return {Conditional, signedness};
3363SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3365 emitError(op,
"SV attributes emission is unimplemented for the op");
3368 emitSubExpr(op.getInput(), LowestPrecedence);
3371 return {Symbol, IsUnsigned};
3374SubExprInfo ExprEmitter::printStructCreate(
3375 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3377 bool printAsPattern, Operation *op) {
3378 if (printAsPattern && !isAssignmentLikeContext)
3379 emitAssignmentPatternContextError(op);
3382 auto filteredFields = llvm::make_filter_range(
3383 llvm::enumerate(fieldInfos),
3384 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3386 if (printAsPattern) {
3388 filteredFields, [&]() { ps <<
"'{"; },
3389 [&](
const auto &field) {
3390 ps.scopedBox(PP::ibox2, [&]() {
3392 emitter.getVerilogStructFieldName(field.value().name))
3393 <<
":" << PP::space;
3394 fieldFn(field.value(), field.index());
3397 [&]() { ps <<
"}"; });
3400 filteredFields, [&]() { ps <<
"{"; },
3401 [&](
const auto &field) {
3402 ps.scopedBox(PP::ibox2,
3403 [&]() { fieldFn(field.value(), field.index()); });
3405 [&]() { ps <<
"}"; });
3408 return {Selection, IsUnsigned};
3413 emitError(op,
"SV attributes emission is unimplemented for the op");
3417 bool printAsPattern = isAssignmentLikeContext;
3418 StructType structType = op.getType();
3419 return printStructCreate(
3420 structType.getElements(),
3421 [&](
const auto &field,
auto index) {
3422 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3424 isAssignmentLikeContext);
3426 printAsPattern, op);
3431 emitError(op,
"SV attributes emission is unimplemented for the op");
3433 emitSubExpr(op.getInput(), Selection);
3435 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3436 return {Selection, IsUnsigned};
3439SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3441 emitError(op,
"SV attributes emission is unimplemented for the op");
3445 bool printAsPattern = isAssignmentLikeContext;
3446 StructType structType = op.getType();
3447 return printStructCreate(
3448 structType.getElements(),
3449 [&](
const auto &field,
auto index) {
3450 if (field.name == op.getFieldNameAttr()) {
3451 emitSubExpr(op.getNewValue(), Selection);
3453 emitSubExpr(op.getInput(), Selection);
3455 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3458 printAsPattern, op);
3461SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3462 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3463 return {Selection, IsUnsigned};
3466SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3468 emitError(op,
"SV attributes emission is unimplemented for the op");
3469 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3472 result.signedness = IsUnsigned;
3476SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3478 emitError(op,
"SV attributes emission is unimplemented for the op");
3482 auto unionWidth = hw::getBitWidth(unionType);
3483 auto &element = unionType.getElements()[op.getFieldIndex()];
3484 auto elementWidth = hw::getBitWidth(element.type);
3487 if (!elementWidth) {
3488 ps.addAsString(unionWidth);
3490 return {Unary, IsUnsigned};
3494 if (elementWidth == unionWidth) {
3495 emitSubExpr(op.getInput(), LowestPrecedence);
3496 return {Unary, IsUnsigned};
3501 ps.scopedBox(PP::ibox0, [&]() {
3502 if (
auto prePadding = element.offset) {
3503 ps.addAsString(prePadding);
3504 ps <<
"'h0," << PP::space;
3506 emitSubExpr(op.getInput(), Selection);
3507 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3508 ps <<
"," << PP::space;
3509 ps.addAsString(postPadding);
3515 return {Unary, IsUnsigned};
3518SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3520 emitError(op,
"SV attributes emission is unimplemented for the op");
3521 emitSubExpr(op.getInput(), Selection);
3524 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3525 auto unionWidth = hw::getBitWidth(unionType);
3526 auto &element = unionType.getElements()[op.getFieldIndex()];
3527 auto elementWidth = hw::getBitWidth(element.type);
3528 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3529 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3538 return {Selection, IsUnsigned};
3541SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3542 emitOpError(op,
"cannot emit this expression to Verilog");
3543 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3545 return {Symbol, IsUnsigned};
3561enum class PropertyPrecedence {
3581struct EmittedProperty {
3583 PropertyPrecedence precedence;
3588class PropertyEmitter :
public EmitterBase,
3589 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3593 PropertyEmitter(ModuleEmitter &emitter,
3594 SmallPtrSetImpl<Operation *> &emittedOps)
3595 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3596 PropertyEmitter(ModuleEmitter &emitter,
3597 SmallPtrSetImpl<Operation *> &emittedOps,
3599 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3601 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3602 assert(state.pp.getListener() == &state.saver);
3605 void emitAssertPropertyDisable(
3606 Value property, Value disable,
3607 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3609 void emitAssertPropertyBody(
3610 Value property, Value disable,
3611 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3613 void emitAssertPropertyBody(
3614 Value property, sv::EventControl event, Value clock, Value disable,
3615 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3620 emitNestedProperty(Value property,
3621 PropertyPrecedence parenthesizeIfLooserThan);
3622 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3623 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3625 EmittedProperty visitUnhandledLTL(Operation *op);
3626 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3627 EmittedProperty visitLTL(ltl::AndOp op);
3628 EmittedProperty visitLTL(ltl::OrOp op);
3629 EmittedProperty visitLTL(ltl::IntersectOp op);
3630 EmittedProperty visitLTL(ltl::DelayOp op);
3631 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3632 EmittedProperty visitLTL(ltl::ConcatOp op);
3633 EmittedProperty visitLTL(ltl::RepeatOp op);
3634 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3635 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3636 EmittedProperty visitLTL(ltl::NotOp op);
3637 EmittedProperty visitLTL(ltl::ImplicationOp op);
3638 EmittedProperty visitLTL(ltl::UntilOp op);
3639 EmittedProperty visitLTL(ltl::EventuallyOp op);
3640 EmittedProperty visitLTL(ltl::ClockOp op);
3642 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3643 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3644 void emitLTLConcat(ValueRange inputs);
3647 ModuleEmitter &emitter;
3652 SmallPtrSetImpl<Operation *> &emittedOps;
3655 SmallVector<Token> localTokens;
3668void PropertyEmitter::emitAssertPropertyDisable(
3669 Value property, Value disable,
3670 PropertyPrecedence parenthesizeIfLooserThan) {
3673 ps <<
"disable iff" << PP::nbsp <<
"(";
3675 emitNestedProperty(disable, PropertyPrecedence::Unary);
3681 ps.scopedBox(PP::ibox0,
3682 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3688void PropertyEmitter::emitAssertPropertyBody(
3689 Value property, Value disable,
3690 PropertyPrecedence parenthesizeIfLooserThan) {
3691 assert(localTokens.empty());
3693 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3698 if (&buffer.tokens == &localTokens)
3699 buffer.flush(state.pp);
3702void PropertyEmitter::emitAssertPropertyBody(
3703 Value property, sv::EventControl event, Value clock, Value disable,
3704 PropertyPrecedence parenthesizeIfLooserThan) {
3705 assert(localTokens.empty());
3708 ps.scopedBox(PP::ibox2, [&] {
3709 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3710 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3716 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3721 if (&buffer.tokens == &localTokens)
3722 buffer.flush(state.pp);
3725EmittedProperty PropertyEmitter::emitNestedProperty(
3726 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3736 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3737 ExprEmitter(emitter, emittedOps, buffer.tokens)
3738 .emitExpression(property, LowestPrecedence,
3740 return {PropertyPrecedence::Symbol};
3743 unsigned startIndex = buffer.tokens.size();
3744 auto info = dispatchLTLVisitor(property.getDefiningOp());
3749 if (
info.precedence > parenthesizeIfLooserThan) {
3751 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3752 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3754 ps << PP::end <<
")";
3756 info.precedence = PropertyPrecedence::Symbol;
3760 emittedOps.insert(property.getDefiningOp());
3764EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3765 emitOpError(op,
"emission as Verilog property or sequence not supported");
3766 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3767 return {PropertyPrecedence::Symbol};
3770EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3772 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3773 return {PropertyPrecedence::Symbol};
3776EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3779 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3780 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3781 return {PropertyPrecedence::And};
3784EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3787 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3788 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3789 return {PropertyPrecedence::Or};
3792EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3796 emitNestedProperty(input, PropertyPrecedence::Intersect);
3798 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3799 return {PropertyPrecedence::Intersect};
3802void PropertyEmitter::emitLTLDelay(int64_t delay,
3803 std::optional<int64_t> length) {
3807 ps.addAsString(delay);
3810 ps.addAsString(delay);
3812 ps.addAsString(delay + *length);
3818 }
else if (delay == 1) {
3822 ps.addAsString(delay);
3828void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3830 ps.scopedBox(PP::ibox2, [&] {
3831 ps <<
PPExtString(stringifyClockEdge(edge)) << PP::space;
3832 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3837EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3838 emitLTLDelay(op.getDelay(), op.getLength());
3840 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3841 return {PropertyPrecedence::Concat};
3844EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3845 emitLTLClockingEvent(op.getEdge(), op.getClock());
3847 emitLTLDelay(op.getDelay(), op.getLength());
3849 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3850 return {PropertyPrecedence::Clocking};
3853void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3854 bool addSeparator =
false;
3855 for (
auto input : inputs) {
3858 if (!input.getDefiningOp<ltl::DelayOp>())
3859 ps <<
"##0" << PP::space;
3861 addSeparator =
true;
3862 emitNestedProperty(input, PropertyPrecedence::Concat);
3866EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3867 emitLTLConcat(op.getInputs());
3868 return {PropertyPrecedence::Concat};
3871EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3872 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3873 if (
auto more = op.getMore()) {
3875 ps.addAsString(op.getBase());
3878 ps.addAsString(op.getBase() + *more);
3882 if (op.getBase() == 0) {
3884 }
else if (op.getBase() == 1) {
3888 ps.addAsString(op.getBase());
3892 return {PropertyPrecedence::Repeat};
3895EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3896 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3898 auto more = op.getMore();
3900 ps.addAsString(op.getBase());
3903 ps.addAsString(op.getBase() + more);
3907 return {PropertyPrecedence::Repeat};
3910EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3911 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3913 auto more = op.getMore();
3915 ps.addAsString(op.getBase());
3918 ps.addAsString(op.getBase() + more);
3922 return {PropertyPrecedence::Repeat};
3925EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3928 if (
auto ev = op.getInput().getDefiningOp<ltl::EventuallyOp>()) {
3929 ps <<
"always" << PP::space;
3930 if (
auto innerNot = ev.getInput().getDefiningOp<ltl::NotOp>()) {
3932 emitNestedProperty(innerNot.getInput(), PropertyPrecedence::Qualifier);
3935 ps <<
"not" << PP::space;
3936 emitNestedProperty(ev.getInput(), PropertyPrecedence::Unary);
3938 return {PropertyPrecedence::Qualifier};
3940 ps <<
"not" << PP::space;
3941 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3942 return {PropertyPrecedence::Unary};
3948 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3949 if (!concatOp || concatOp.getInputs().size() < 2)
3951 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3952 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3954 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3955 if (!constOp || !constOp.getValue().isOne())
3957 return concatOp.getInputs().drop_back();
3960EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3964 emitLTLConcat(range);
3965 ps << PP::space <<
"|=>" << PP::nbsp;
3967 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3968 ps << PP::space <<
"|->" << PP::nbsp;
3970 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3971 return {PropertyPrecedence::Implication};
3974EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3975 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3976 ps << PP::space <<
"until" << PP::space;
3977 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3978 return {PropertyPrecedence::Until};
3981EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3982 ps <<
"s_eventually" << PP::space;
3983 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3984 return {PropertyPrecedence::Qualifier};
3987EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3988 emitLTLClockingEvent(op.getEdge(), op.getClock());
3990 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3991 return {PropertyPrecedence::Clocking};
4001class NameCollector {
4003 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
4007 void collectNames(Block &block);
4009 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
4010 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
4013 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
4014 ModuleEmitter &moduleEmitter;
4019 static constexpr size_t maxTypeWidthBound = 32;
4024void NameCollector::collectNames(Block &block) {
4027 for (
auto &op : block) {
4031 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
4034 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
4038 for (
auto result : op.getResults()) {
4040 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
4041 SmallString<16> typeString;
4045 llvm::raw_svector_ostream stringStream(typeString);
4047 stringStream, op.getLoc());
4049 if (typeString.size() <= maxTypeWidthBound)
4050 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4057 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4058 for (
auto ®ion : op.getRegions()) {
4059 if (!region.empty())
4060 collectNames(region.front());
4074class StmtEmitter :
public EmitterBase,
4082 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4084 void emitStatement(Operation *op);
4085 void emitStatementBlock(Block &body);
4088 LogicalResult emitDeclaration(Operation *op);
4091 void collectNamesAndCalculateDeclarationWidths(Block &block);
4094 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4095 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4096 bool isAssignmentLikeContext =
false);
4097 void emitSVAttributes(Operation *op);
4100 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4103 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4107 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4108 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4109 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4110 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4111 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4112 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4114 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4115 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4116 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4117 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4118 template <
typename Op>
4121 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4122 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4123 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4125 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4126 LogicalResult visitSV(
AssignOp op);
4127 LogicalResult visitSV(BPAssignOp op);
4128 LogicalResult visitSV(PAssignOp op);
4129 LogicalResult visitSV(ForceOp op);
4130 LogicalResult visitSV(ReleaseOp op);
4131 LogicalResult visitSV(AliasOp op);
4132 LogicalResult visitSV(InterfaceInstanceOp op);
4133 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4134 LogicalResult visitStmt(OutputOp op);
4136 LogicalResult visitStmt(InstanceOp op);
4137 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4138 ArrayRef<Value> instPortValues);
4143 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4144 LogicalResult visitSV(OrderedOutputOp op);
4145 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4146 LogicalResult visitSV(IfDefProceduralOp op) {
4147 return emitIfDef(op, op.getCond());
4149 LogicalResult visitSV(IfOp op);
4150 LogicalResult visitSV(AlwaysOp op);
4151 LogicalResult visitSV(AlwaysCombOp op);
4152 LogicalResult visitSV(AlwaysFFOp op);
4153 LogicalResult visitSV(InitialOp op);
4154 LogicalResult visitSV(CaseOp op);
4155 template <
typename OpTy,
typename EmitPrefixFn>
4157 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4158 ValueRange substitutions, EmitPrefixFn emitPrefix);
4159 LogicalResult visitSV(WriteOp op);
4160 LogicalResult visitSV(FWriteOp op);
4161 LogicalResult visitSV(FFlushOp op);
4162 LogicalResult visitSV(FCloseOp op);
4163 LogicalResult visitSV(VerbatimOp op);
4164 LogicalResult visitSV(MacroRefOp op);
4166 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4167 std::optional<unsigned> verbosity);
4168 LogicalResult visitSV(StopOp op);
4169 LogicalResult visitSV(FinishOp op);
4170 LogicalResult visitSV(ExitOp op);
4172 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4173 std::optional<unsigned> verbosity,
4175 ValueRange operands);
4178 template <
typename OpTy>
4179 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4180 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4181 op.getMessageAttr(), op.getSubstitutions());
4185 template <
typename OpTy>
4186 LogicalResult emitFatalMessageOp(OpTy op) {
4187 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4188 op.getMessageAttr(), op.getSubstitutions());
4191 LogicalResult visitSV(FatalProceduralOp op);
4192 LogicalResult visitSV(FatalOp op);
4193 LogicalResult visitSV(ErrorProceduralOp op);
4194 LogicalResult visitSV(WarningProceduralOp op);
4195 LogicalResult visitSV(InfoProceduralOp op);
4196 LogicalResult visitSV(ErrorOp op);
4197 LogicalResult visitSV(WarningOp op);
4198 LogicalResult visitSV(InfoOp op);
4200 LogicalResult visitSV(ReadMemOp op);
4202 LogicalResult visitSV(GenerateOp op);
4203 LogicalResult visitSV(GenerateCaseOp op);
4204 LogicalResult visitSV(GenerateForOp op);
4206 LogicalResult visitSV(
ForOp op);
4208 void emitAssertionLabel(Operation *op);
4209 void emitAssertionMessage(StringAttr message, ValueRange args,
4210 SmallPtrSetImpl<Operation *> &ops,
4212 template <
typename Op>
4213 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4214 LogicalResult visitSV(AssertOp op);
4215 LogicalResult visitSV(AssumeOp op);
4216 LogicalResult visitSV(CoverOp op);
4217 template <
typename Op>
4218 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4219 LogicalResult visitSV(AssertConcurrentOp op);
4220 LogicalResult visitSV(AssumeConcurrentOp op);
4221 LogicalResult visitSV(CoverConcurrentOp op);
4222 template <
typename Op>
4223 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4224 LogicalResult visitSV(AssertPropertyOp op);
4225 LogicalResult visitSV(AssumePropertyOp op);
4226 LogicalResult visitSV(CoverPropertyOp op);
4228 LogicalResult visitSV(BindOp op);
4229 LogicalResult visitSV(InterfaceOp op);
4231 LogicalResult visitSV(InterfaceSignalOp op);
4232 LogicalResult visitSV(InterfaceModportOp op);
4233 LogicalResult visitSV(AssignInterfaceSignalOp op);
4234 LogicalResult visitSV(MacroErrorOp op);
4235 LogicalResult visitSV(MacroDefOp op);
4237 void emitBlockAsStatement(Block *block,
4238 const SmallPtrSetImpl<Operation *> &locationOps,
4239 StringRef multiLineComment = StringRef());
4241 LogicalResult visitSV(FuncDPIImportOp op);
4242 template <
typename CallOp>
4243 LogicalResult emitFunctionCall(CallOp callOp);
4244 LogicalResult visitSV(FuncCallProceduralOp op);
4245 LogicalResult visitSV(FuncCallOp op);
4246 LogicalResult visitSV(ReturnOp op);
4247 LogicalResult visitSV(IncludeOp op);
4250 ModuleEmitter &emitter;
4255 size_t maxDeclNameWidth = 0;
4256 size_t maxTypeWidth = 0;
4267void StmtEmitter::emitExpression(Value exp,
4268 SmallPtrSetImpl<Operation *> &emittedExprs,
4269 VerilogPrecedence parenthesizeIfLooserThan,
4270 bool isAssignmentLikeContext) {
4271 ExprEmitter(emitter, emittedExprs)
4272 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4277void StmtEmitter::emitSVAttributes(Operation *op) {
4285 setPendingNewline();
4288void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4289 llvm::function_ref<
void()> emitRHS,
4291 std::optional<PPExtString> wordBeforeLHS) {
4293 ps.scopedBox(PP::ibox2, [&]() {
4294 if (wordBeforeLHS) {
4295 ps << *wordBeforeLHS << PP::space;
4299 ps << PP::space << syntax << PP::space;
4301 ps.scopedBox(PP::ibox0, [&]() {
4308template <
typename Op>
4310StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4311 std::optional<PPExtString> wordBeforeLHS) {
4312 SmallPtrSet<Operation *, 8> ops;
4316 ps.addCallback({op,
true});
4317 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4319 emitExpression(op.getSrc(), ops, LowestPrecedence,
4324 ps.addCallback({op,
false});
4325 emitLocationInfoAndNewLine(ops);
4329LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4332 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4335 if (emitter.assignsInlined.count(op))
4339 emitSVAttributes(op);
4344LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4345 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4349 if (emitter.assignsInlined.count(op))
4353 emitSVAttributes(op);
4358LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4360 emitSVAttributes(op);
4365LogicalResult StmtEmitter::visitSV(ForceOp op) {
4367 emitError(op,
"SV attributes emission is unimplemented for the op");
4372LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4374 emitError(op,
"SV attributes emission is unimplemented for the op");
4377 SmallPtrSet<Operation *, 8> ops;
4379 ps.addCallback({op,
true});
4380 ps.scopedBox(PP::ibox2, [&]() {
4381 ps <<
"release" << PP::space;
4382 emitExpression(op.getDest(), ops);
4385 ps.addCallback({op,
false});
4386 emitLocationInfoAndNewLine(ops);
4390LogicalResult StmtEmitter::visitSV(AliasOp op) {
4392 emitError(op,
"SV attributes emission is unimplemented for the op");
4395 SmallPtrSet<Operation *, 8> ops;
4397 ps.addCallback({op,
true});
4398 ps.scopedBox(PP::ibox2, [&]() {
4399 ps <<
"alias" << PP::space;
4400 ps.scopedBox(PP::cbox0, [&]() {
4402 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4403 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4407 ps.addCallback({op,
false});
4408 emitLocationInfoAndNewLine(ops);
4412LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4413 auto doNotPrint = op.getDoNotPrint();
4414 if (doNotPrint && !state.options.emitBindComments)
4418 emitError(op,
"SV attributes emission is unimplemented for the op");
4421 StringRef prefix =
"";
4422 ps.addCallback({op,
true});
4425 ps <<
"// This interface is elsewhere emitted as a bind statement."
4429 SmallPtrSet<Operation *, 8> ops;
4432 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4433 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4434 "point to an interface");
4437 if (!prefix.empty())
4443 ps.addCallback({op,
false});
4444 emitLocationInfoAndNewLine(ops);
4452LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4454 SmallPtrSet<Operation *, 8> ops;
4455 size_t operandIndex = 0;
4457 for (
PortInfo port : ports.getOutputs()) {
4458 auto operand = op->getOperand(operandIndex);
4462 if (operand.hasOneUse() && operand.getDefiningOp() &&
4463 isa<InstanceOp>(operand.getDefiningOp())) {
4472 ps.addCallback({op,
true});
4474 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4476 ps <<
"// Zero width: ";
4479 ps <<
"assign" << PP::space;
4481 ps << PP::space <<
"=" << PP::space;
4482 ps.scopedBox(PP::ibox0, [&]() {
4486 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4487 ps <<
"/*Zero width*/";
4489 emitExpression(operand, ops, LowestPrecedence,
4494 ps.addCallback({op,
false});
4495 emitLocationInfoAndNewLine(ops);
4502LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4503 auto parent = op->getParentOfType<PortList>();
4505 return emitOutputLikeOp(op, ports);
4508LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4510 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4511 ps <<
"`ifndef " << typescopeDef << PP::newline;
4512 ps <<
"`define " << typescopeDef;
4513 setPendingNewline();
4514 emitStatementBlock(*op.getBodyBlock());
4516 ps <<
"`endif // " << typescopeDef;
4517 setPendingNewline();
4521LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4523 emitError(op,
"SV attributes emission is unimplemented for the op");
4528 ps << PP::neverbox <<
"// ";
4530 SmallPtrSet<Operation *, 8> ops;
4532 ps.scopedBox(PP::ibox2, [&]() {
4533 ps <<
"typedef" << PP::space;
4534 ps.invokeWithStringOS([&](
auto &os) {
4536 op.getAliasType(),
false);
4538 ps << PP::space <<
PPExtString(op.getPreferredName());
4539 ps.invokeWithStringOS(
4540 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4545 emitLocationInfoAndNewLine(ops);
4549template <
typename CallOpTy>
4550LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4554 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4556 SmallPtrSet<Operation *, 8> ops;
4560 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4561 if (explicitReturn) {
4562 assert(explicitReturn.hasOneUse());
4563 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4564 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4565 emitExpression(bpassignOp.getDest(), ops);
4567 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4568 ps <<
"assign" << PP::nbsp;
4569 emitExpression(assignOp.getDest(), ops);
4571 ps << PP::nbsp <<
"=" << PP::nbsp;
4574 auto arguments = callee.getPortList(
true);
4578 bool needsComma =
false;
4579 auto printArg = [&](Value value) {
4581 ps <<
"," << PP::space;
4582 emitExpression(value, ops);
4586 ps.scopedBox(PP::ibox0, [&] {
4587 unsigned inputIndex = 0, outputIndex = 0;
4588 for (
auto arg : arguments) {
4591 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4593 printArg(op.getInputs()[inputIndex++]);
4598 emitLocationInfoAndNewLine(ops);
4602LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4603 return emitFunctionCall(op);
4606LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4607 return emitFunctionCall(op);
4610template <
typename PPS>
4612 bool isAutomatic =
false,
4613 bool emitAsTwoStateType =
false) {
4614 ps <<
"function" << PP::nbsp;
4616 ps <<
"automatic" << PP::nbsp;
4617 auto retType = op.getExplicitlyReturnedType();
4619 ps.invokeWithStringOS([&](
auto &os) {
4620 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4621 emitAsTwoStateType);
4627 emitter.emitPortList(
4631LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4632 auto parent = op->getParentOfType<sv::FuncOp>();
4634 return emitOutputLikeOp(op, ports);
4637LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4639 ps <<
"`include" << PP::nbsp;
4641 if (op.getStyle() == IncludeStyle::System)
4642 ps <<
"<" << op.getTarget() <<
">";
4644 ps <<
"\"" << op.getTarget() <<
"\"";
4646 emitLocationInfo(op.getLoc());
4647 setPendingNewline();
4651LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4654 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4658 if (
auto linkageName = importOp.getLinkageName())
4659 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4661 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4662 assert(op.isDeclaration() &&
"function must be a declaration");
4665 assert(state.pendingNewline);
4671LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4673 emitError(op,
"SV attributes emission is unimplemented for the op");
4676 SmallPtrSet<Operation *, 8> ops;
4679 ps.addCallback({op,
true});
4681 if (
auto fd = op.getFd())
4682 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4685 ps.addCallback({op,
false});
4686 emitLocationInfoAndNewLine(ops);
4690LogicalResult StmtEmitter::visitSV(FCloseOp op) {
4692 emitError(op,
"SV attributes emission is unimplemented for the op");
4695 SmallPtrSet<Operation *, 8> ops;
4698 ps.addCallback({op,
true});
4700 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4702 ps.addCallback({op,
false});
4703 emitLocationInfoAndNewLine(ops);
4707template <
typename OpTy,
typename EmitPrefixFn>
4708LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4709 StringRef formatString,
4710 ValueRange substitutions,
4711 EmitPrefixFn emitPrefix) {
4713 emitError(op,
"SV attributes emission is unimplemented for the op");
4716 SmallPtrSet<Operation *, 8> ops;
4719 ps.addCallback({op,
true});
4721 ps.scopedBox(PP::ibox0, [&]() {
4723 ps.writeQuotedEscaped(formatString);
4730 for (
auto operand : substitutions) {
4731 ps <<
"," << PP::space;
4732 emitExpression(operand, ops);
4736 ps.addCallback({op,
false});
4737 emitLocationInfoAndNewLine(ops);
4741LogicalResult StmtEmitter::visitSV(WriteOp op) {
4742 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4743 op.getSubstitutions(),
4744 [&](SmallPtrSetImpl<Operation *> &) {});
4747LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4748 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4749 op.getSubstitutions(),
4750 [&](SmallPtrSetImpl<Operation *> &ops) {
4751 emitExpression(op.getFd(), ops);
4752 ps <<
"," << PP::space;
4756LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4758 emitError(op,
"SV attributes emission is unimplemented for the op");
4761 SmallPtrSet<Operation *, 8> ops;
4766 StringRef
string = op.getFormatString();
4767 if (
string.ends_with(
"\n"))
4768 string =
string.drop_back();
4773 bool isFirst =
true;
4776 while (!
string.
empty()) {
4777 auto lhsRhs =
string.split(
'\n');
4781 ps << PP::end << PP::newline << PP::neverbox;
4785 emitTextWithSubstitutions(
4786 ps, lhsRhs.first, op,
4787 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4788 string = lhsRhs.second;
4793 emitLocationInfoAndNewLine(ops);
4798LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4800 emitError(op,
"SV attributes emission is unimplemented for the op");
4804 SmallPtrSet<Operation *, 8> ops;
4809 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4810 assert(macroOp &&
"Invalid IR");
4812 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4814 if (!op.getInputs().empty()) {
4816 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4817 emitExpression(val, ops, LowestPrecedence,
4823 emitLocationInfoAndNewLine(ops);
4829StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4830 std::optional<unsigned> verbosity) {
4832 emitError(op,
"SV attributes emission is unimplemented for the op");
4835 SmallPtrSet<Operation *, 8> ops;
4837 ps.addCallback({op,
true});
4839 if (verbosity && *verbosity != 1) {
4841 ps.addAsString(*verbosity);
4845 ps.addCallback({op,
false});
4846 emitLocationInfoAndNewLine(ops);
4850LogicalResult StmtEmitter::visitSV(StopOp op) {
4851 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4854LogicalResult StmtEmitter::visitSV(FinishOp op) {
4855 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4859LogicalResult StmtEmitter::visitSV(ExitOp op) {
4860 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4866StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4867 std::optional<unsigned> verbosity,
4868 StringAttr message, ValueRange operands) {
4870 emitError(op,
"SV attributes emission is unimplemented for the op");
4873 SmallPtrSet<Operation *, 8> ops;
4875 ps.addCallback({op,
true});
4881 if ((verbosity && *verbosity != 1) || message) {
4883 ps.scopedBox(PP::ibox0, [&]() {
4887 ps.addAsString(*verbosity);
4892 ps <<
"," << PP::space;
4893 ps.writeQuotedEscaped(message.getValue());
4895 for (
auto operand : operands) {
4896 ps <<
"," << PP::space;
4897 emitExpression(operand, ops);
4906 ps.addCallback({op,
false});
4907 emitLocationInfoAndNewLine(ops);
4911LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4912 return emitFatalMessageOp(op);
4915LogicalResult StmtEmitter::visitSV(FatalOp op) {
4916 return emitFatalMessageOp(op);
4919LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4920 return emitNonfatalMessageOp(op,
"$error");
4923LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4924 return emitNonfatalMessageOp(op,
"$warning");
4927LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4928 return emitNonfatalMessageOp(op,
"$info");
4931LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4932 return emitNonfatalMessageOp(op,
"$error");
4935LogicalResult StmtEmitter::visitSV(WarningOp op) {
4936 return emitNonfatalMessageOp(op,
"$warning");
4939LogicalResult StmtEmitter::visitSV(InfoOp op) {
4940 return emitNonfatalMessageOp(op,
"$info");
4943LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4944 SmallPtrSet<Operation *, 8> ops({op});
4947 ps.addCallback({op,
true});
4949 switch (op.getBaseAttr().getValue()) {
4950 case MemBaseTypeAttr::MemBaseBin:
4953 case MemBaseTypeAttr::MemBaseHex:
4958 ps.scopedBox(PP::ibox0, [&]() {
4959 ps.writeQuotedEscaped(op.getFilename());
4960 ps <<
"," << PP::space;
4961 emitExpression(op.getDest(), ops);
4965 ps.addCallback({op,
false});
4966 emitLocationInfoAndNewLine(ops);
4970LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4971 emitSVAttributes(op);
4974 ps.addCallback({op,
true});
4975 ps <<
"generate" << PP::newline;
4977 setPendingNewline();
4978 emitStatementBlock(op.getBody().getBlocks().front());
4981 ps <<
"endgenerate";
4982 ps.addCallback({op,
false});
4983 setPendingNewline();
4987LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4988 emitSVAttributes(op);
4991 ps.addCallback({op,
true});
4993 ps.invokeWithStringOS([&](
auto &os) {
4994 emitter.printParamValue(
4995 op.getCond(), os, VerilogPrecedence::Selection,
4996 [&]() { return op->emitOpError(
"invalid case parameter"); });
4999 setPendingNewline();
5002 ArrayAttr
patterns = op.getCasePatterns();
5003 ArrayAttr caseNames = op.getCaseNames();
5004 MutableArrayRef<Region> regions = op.getCaseRegions();
5011 llvm::StringMap<size_t> nextGenIds;
5012 ps.scopedBox(PP::bbox2, [&]() {
5014 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
5015 auto ®ion = regions[i];
5016 assert(region.hasOneBlock());
5017 Attribute patternAttr =
patterns[i];
5020 if (!isa<mlir::TypedAttr>(patternAttr))
5023 ps.invokeWithStringOS([&](
auto &os) {
5024 emitter.printParamValue(
5025 patternAttr, os, VerilogPrecedence::LowestPrecedence,
5026 [&]() {
return op->emitOpError(
"invalid case value"); });
5029 StringRef legalName =
5030 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
5033 setPendingNewline();
5034 emitStatementBlock(region.getBlocks().front());
5037 setPendingNewline();
5043 ps.addCallback({op,
false});
5044 setPendingNewline();
5048LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
5049 emitSVAttributes(op);
5050 llvm::SmallPtrSet<Operation *, 8> ops;
5051 ps.addCallback({op,
true});
5054 StringRef inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5057 ps.scopedBox(PP::cbox0, [&]() {
5059 [&]() { ps <<
"genvar" << PP::nbsp <<
PPExtString(inductionVarName); },
5061 ps.invokeWithStringOS([&](
auto &os) {
5062 emitter.printParamValue(
5063 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5064 [&]() { return op->emitOpError(
"invalid lower bound"); });
5073 ps.invokeWithStringOS([&](
auto &os) {
5074 emitter.printParamValue(
5075 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5076 [&]() { return op->emitOpError(
"invalid upper bound"); });
5082 ps <<
PPExtString(inductionVarName) << PP::nbsp <<
"+=" << PP::nbsp;
5083 ps.invokeWithStringOS([&](
auto &os) {
5084 emitter.printParamValue(
5085 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5086 [&]() { return op->emitOpError(
"invalid step"); });
5089 StringRef blockName = op.getGenBlockName();
5090 if (!blockName.empty())
5094 ps << PP::neverbreak;
5095 setPendingNewline();
5096 emitStatementBlock(op.getBody().getBlocks().front());
5099 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5101 ps.addCallback({op,
false});
5102 setPendingNewline();
5106LogicalResult StmtEmitter::visitSV(
ForOp op) {
5107 emitSVAttributes(op);
5108 llvm::SmallPtrSet<Operation *, 8> ops;
5109 ps.addCallback({op,
true});
5111 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5114 ps.scopedBox(PP::cbox0, [&]() {
5118 ps <<
"logic" << PP::nbsp;
5119 ps.invokeWithStringOS([&](
auto &os) {
5120 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5125 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
5130 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5131 [&]() { emitExpression(op.getUpperBound(), ops); },
5137 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5138 [&]() { emitExpression(op.getStep(), ops); },
5142 ps << PP::neverbreak;
5143 setPendingNewline();
5144 emitStatementBlock(op.getBody().getBlocks().front());
5147 ps.addCallback({op,
false});
5148 emitLocationInfoAndNewLine(ops);
5153void StmtEmitter::emitAssertionLabel(Operation *op) {
5154 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5160void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5161 SmallPtrSetImpl<Operation *> &ops,
5162 bool isConcurrent =
false) {
5165 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5166 ps.scopedBox(PP::ibox0, [&]() {
5167 ps.writeQuotedEscaped(message.getValue());
5169 for (
auto arg : args) {
5170 ps <<
"," << PP::space;
5171 emitExpression(arg, ops);
5177template <
typename Op>
5178LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5180 emitError(op,
"SV attributes emission is unimplemented for the op");
5183 SmallPtrSet<Operation *, 8> ops;
5185 ps.addCallback({op,
true});
5186 ps.scopedBox(PP::ibox2, [&]() {
5187 emitAssertionLabel(op);
5188 ps.scopedBox(PP::cbox0, [&]() {
5190 switch (op.getDefer()) {
5191 case DeferAssert::Immediate:
5193 case DeferAssert::Observed:
5196 case DeferAssert::Final:
5201 ps.scopedBox(PP::ibox0, [&]() {
5202 emitExpression(op.getExpression(), ops);
5205 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5209 ps.addCallback({op,
false});
5210 emitLocationInfoAndNewLine(ops);
5214LogicalResult StmtEmitter::visitSV(AssertOp op) {
5215 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5218LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5219 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5222LogicalResult StmtEmitter::visitSV(CoverOp op) {
5223 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5226template <
typename Op>
5227LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5229 emitError(op,
"SV attributes emission is unimplemented for the op");
5232 SmallPtrSet<Operation *, 8> ops;
5234 ps.addCallback({op,
true});
5235 ps.scopedBox(PP::ibox2, [&]() {
5236 emitAssertionLabel(op);
5237 ps.scopedBox(PP::cbox0, [&]() {
5238 ps << opName << PP::nbsp <<
"property (";
5239 ps.scopedBox(PP::ibox0, [&]() {
5240 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5242 emitExpression(op.getClock(), ops);
5243 ps <<
")" << PP::space;
5244 emitExpression(op.getProperty(), ops);
5247 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5252 ps.addCallback({op,
false});
5253 emitLocationInfoAndNewLine(ops);
5257LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5258 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5261LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5262 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5265LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5266 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5271template <
typename Op>
5272LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5274 emitError(op,
"SV attributes emission is unimplemented for the op");
5284 Operation *parent = op->getParentOp();
5285 Value
property = op.getProperty();
5286 bool isTemporal = !
property.getType().isSignlessInteger(1);
5288 bool emitAsImmediate = !isTemporal && isProcedural;
5291 SmallPtrSet<Operation *, 8> ops;
5293 ps.addCallback({op,
true});
5294 ps.scopedBox(PP::ibox2, [&]() {
5296 emitAssertionLabel(op);
5298 ps.scopedBox(PP::cbox0, [&]() {
5299 if (emitAsImmediate)
5300 ps << opName <<
"(";
5302 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5304 Value clock = op.getClock();
5305 auto event = op.getEvent();
5307 ps.scopedBox(PP::ibox2, [&]() {
5308 PropertyEmitter(emitter, ops)
5309 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5312 ps.scopedBox(PP::ibox2, [&]() {
5313 PropertyEmitter(emitter, ops)
5314 .emitAssertPropertyBody(property, op.getDisable());
5319 ps.addCallback({op,
false});
5320 emitLocationInfoAndNewLine(ops);
5324LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5325 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5328LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5329 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5332LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5333 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5336LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5338 emitError(op,
"SV attributes emission is unimplemented for the op");
5341 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5342 .getMacroIdentifier());
5345 bool hasEmptyThen = op->getRegion(0).front().empty();
5347 ps <<
"`ifndef " << ident;
5349 ps <<
"`ifdef " << ident;
5351 SmallPtrSet<Operation *, 8> ops;
5353 emitLocationInfoAndNewLine(ops);
5356 emitStatementBlock(op->getRegion(0).front());
5358 if (!op->getRegion(1).empty()) {
5359 if (!hasEmptyThen) {
5361 ps <<
"`else // " << ident;
5362 setPendingNewline();
5364 emitStatementBlock(op->getRegion(1).front());
5371 setPendingNewline();
5379void StmtEmitter::emitBlockAsStatement(
5380 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5381 StringRef multiLineComment) {
5385 auto needsBeginEnd =
5389 emitLocationInfoAndNewLine(locationOps);
5392 emitStatementBlock(*block);
5394 if (needsBeginEnd) {
5398 if (!multiLineComment.empty())
5399 ps <<
" // " << multiLineComment;
5400 setPendingNewline();
5404LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5406 for (
auto &op : ooop.getBody().front())
5411LogicalResult StmtEmitter::visitSV(IfOp op) {
5412 SmallPtrSet<Operation *, 8> ops;
5414 auto ifcondBox = PP::ibox2;
5416 emitSVAttributes(op);
5418 ps.addCallback({op,
true});
5419 ps <<
"if (" << ifcondBox;
5429 emitExpression(ifOp.getCond(), ops);
5430 ps << PP::end <<
")";
5431 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5433 if (!ifOp.hasElse())
5437 Block *elseBlock = ifOp.getElseBlock();
5439 if (!nestedElseIfOp) {
5444 emitBlockAsStatement(elseBlock, ops);
5450 ifOp = nestedElseIfOp;
5451 ps <<
"else if (" << ifcondBox;
5453 ps.addCallback({op,
false});
5458LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5459 emitSVAttributes(op);
5460 SmallPtrSet<Operation *, 8> ops;
5464 auto printEvent = [&](AlwaysOp::Condition cond) {
5465 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5466 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5468 ps.addCallback({op,
true});
5470 switch (op.getNumConditions()) {
5476 printEvent(op.getCondition(0));
5481 ps.scopedBox(PP::cbox0, [&]() {
5482 printEvent(op.getCondition(0));
5483 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5484 ps << PP::space <<
"or" << PP::space;
5485 printEvent(op.getCondition(i));
5494 std::string comment;
5495 if (op.getNumConditions() == 0) {
5496 comment =
"always @*";
5498 comment =
"always @(";
5501 [&](Attribute eventAttr) {
5502 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5503 comment += stringifyEventControl(event);
5505 [&]() { comment +=
", "; });
5509 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5510 ps.addCallback({op,
false});
5514LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5515 emitSVAttributes(op);
5516 SmallPtrSet<Operation *, 8> ops;
5520 ps.addCallback({op,
true});
5521 StringRef opString =
"always_comb";
5522 if (state.options.noAlwaysComb)
5523 opString =
"always @(*)";
5526 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5527 ps.addCallback({op,
false});
5531LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5532 emitSVAttributes(op);
5534 SmallPtrSet<Operation *, 8> ops;
5538 ps.addCallback({op,
true});
5539 ps <<
"always_ff @(";
5540 ps.scopedBox(PP::cbox0, [&]() {
5541 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5542 emitExpression(op.getClock(), ops);
5543 if (op.getResetStyle() == ResetType::AsyncReset) {
5544 ps << PP::nbsp <<
"or" << PP::space
5545 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5546 emitExpression(op.getReset(), ops);
5553 std::string comment;
5554 comment +=
"always_ff @(";
5555 comment += stringifyEventControl(op.getClockEdge());
5556 if (op.getResetStyle() == ResetType::AsyncReset) {
5558 comment += stringifyEventControl(*op.getResetEdge());
5562 if (op.getResetStyle() == ResetType::NoReset)
5563 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5566 emitLocationInfoAndNewLine(ops);
5567 ps.scopedBox(PP::bbox2, [&]() {
5573 if (op.getResetStyle() == ResetType::AsyncReset &&
5574 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5576 emitExpression(op.getReset(), ops);
5578 emitBlockAsStatement(op.getResetBlock(), ops);
5581 emitBlockAsStatement(op.getBodyBlock(), ops);
5586 ps <<
" // " << comment;
5587 setPendingNewline();
5589 ps.addCallback({op,
false});
5593LogicalResult StmtEmitter::visitSV(InitialOp op) {
5594 emitSVAttributes(op);
5595 SmallPtrSet<Operation *, 8> ops;
5598 ps.addCallback({op,
true});
5600 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5601 ps.addCallback({op,
false});
5605LogicalResult StmtEmitter::visitSV(CaseOp op) {
5606 emitSVAttributes(op);
5607 SmallPtrSet<Operation *, 8> ops, emptyOps;
5610 ps.addCallback({op,
true});
5611 if (op.getValidationQualifier() !=
5612 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5613 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5614 op.getValidationQualifier()))
5616 const char *opname =
nullptr;
5617 switch (op.getCaseStyle()) {
5618 case CaseStmtType::CaseStmt:
5621 case CaseStmtType::CaseXStmt:
5624 case CaseStmtType::CaseZStmt:
5628 ps << opname <<
" (";
5629 ps.scopedBox(PP::ibox0, [&]() {
5630 emitExpression(op.getCond(), ops);
5633 emitLocationInfoAndNewLine(ops);
5635 size_t caseValueIndex = 0;
5636 ps.scopedBox(PP::bbox2, [&]() {
5637 for (
auto &caseInfo : op.getCases()) {
5639 auto &
pattern = caseInfo.pattern;
5641 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5642 .Case<CaseBitPattern>([&](
auto bitPattern) {
5645 ps.invokeWithStringOS([&](
auto &os) {
5646 os << bitPattern->getWidth() <<
"'b";
5647 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5648 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5651 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5652 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5653 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5655 .Case<CaseExprPattern>([&](
auto) {
5656 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5658 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5659 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5662 emitBlockAsStatement(caseInfo.block, emptyOps);
5668 ps.addCallback({op,
false});
5669 emitLocationInfoAndNewLine(ops);
5673LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5674 bool doNotPrint = op.getDoNotPrint();
5675 if (doNotPrint && !state.options.emitBindComments)
5680 emitSVAttributes(op);
5682 ps.addCallback({op,
true});
5685 <<
"/* This instance is elsewhere emitted as a bind statement."
5688 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5689 "attributes. The attributes will not be emitted.";
5692 SmallPtrSet<Operation *, 8> ops;
5697 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5698 assert(moduleOp &&
"Invalid IR");
5702 if (!op.getParameters().empty()) {
5705 bool printed =
false;
5707 llvm::zip(op.getParameters(),
5708 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5709 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5710 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5712 if (param.getValue() == modParam.getValue())
5717 ps <<
" #(" << PP::bbox2 << PP::newline;
5720 ps <<
"," << PP::newline;
5724 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5726 ps.invokeWithStringOS([&](
auto &os) {
5727 emitter.printParamValue(param.getValue(), os, [&]() {
5728 return op->emitOpError(
"invalid instance parameter '")
5729 << param.getName().getValue() <<
"' value";
5735 ps << PP::end << PP::newline <<
")";
5742 SmallVector<Value> instPortValues(modPortInfo.size());
5743 op.getValues(instPortValues, modPortInfo);
5744 emitInstancePortList(op, modPortInfo, instPortValues);
5746 ps.addCallback({op,
false});
5747 emitLocationInfoAndNewLine(ops);
5752 setPendingNewline();
5757void StmtEmitter::emitInstancePortList(Operation *op,
5759 ArrayRef<Value> instPortValues) {
5760 SmallPtrSet<Operation *, 8> ops;
5763 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5764 ModulePortInfo containingPortList(containingModule.getPortList());
5770 size_t maxNameLength = 0;
5771 for (
auto &elt : modPortInfo) {
5772 size_t nameLength = elt.getVerilogName().size();
5773 if (nameLength <= state.options.emittedLineLength / 3)
5774 maxNameLength = std::max(maxNameLength, nameLength);
5777 auto getWireForValue = [&](Value result) {
5778 return result.getUsers().begin()->getOperand(0);
5782 bool isFirst =
true;
5783 bool isZeroWidth =
false;
5785 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5787 auto &modPort = modPortInfo.
at(portNum);
5789 Value portVal = instPortValues[portNum];
5794 bool shouldPrintComma =
true;
5796 shouldPrintComma =
false;
5797 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5799 shouldPrintComma =
true;
5804 if (shouldPrintComma)
5807 emitLocationInfoAndNewLine(ops);
5822 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5823 auto modPortName = modPort.getVerilogName();
5826 if (modPortName.size() <= maxNameLength)
5827 ps.spaces(maxNameLength - modPortName.size() + 1);
5831 ps.scopedBox(PP::ibox0, [&]() {
5838 if (!modPort.isOutput()) {
5840 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5841 ps <<
"/* Zero width */";
5843 emitExpression(portVal, ops, LowestPrecedence);
5844 }
else if (portVal.use_empty()) {
5845 ps <<
"/* unused */";
5846 }
else if (portVal.hasOneUse() &&
5847 (output = dyn_cast_or_null<OutputOp>(
5848 portVal.getUses().begin()->getOwner()))) {
5853 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5855 containingPortList.atOutput(outputPortNo).getVerilogName());
5857 portVal = getWireForValue(portVal);
5858 emitExpression(portVal, ops);
5864 if (!isFirst || isZeroWidth) {
5865 emitLocationInfoAndNewLine(ops);
5878LogicalResult StmtEmitter::visitSV(BindOp op) {
5879 emitter.emitBind(op);
5880 assert(state.pendingNewline);
5884LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5885 emitComment(op.getCommentAttr());
5887 emitSVAttributes(op);
5890 ps.addCallback({op,
true});
5892 setPendingNewline();
5894 emitStatementBlock(*op.getBodyBlock());
5896 ps <<
"endinterface" << PP::newline;
5897 ps.addCallback({op,
false});
5898 setPendingNewline();
5903 emitSVAttributes(op);
5905 ps.addCallback({op,
true});
5907 ps << op.getContent();
5909 ps.addCallback({op,
false});
5910 setPendingNewline();
5914LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5916 emitSVAttributes(op);
5918 ps.addCallback({op,
true});
5920 ps << PP::neverbox <<
"// ";
5921 ps.invokeWithStringOS([&](
auto &os) {
5926 ps.invokeWithStringOS(
5927 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5931 ps.addCallback({op,
false});
5932 setPendingNewline();
5936LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5938 ps.addCallback({op,
true});
5942 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5943 auto port = cast<ModportStructAttr>(portAttr);
5944 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5945 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5946 ps << PPExtString(getSymOpName(signalDecl));
5950 ps.addCallback({op,
false});
5951 setPendingNewline();
5955LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5957 ps.addCallback({op,
true});
5958 SmallPtrSet<Operation *, 8> emitted;
5961 emitExpression(op.getIface(), emitted);
5962 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5963 emitExpression(op.getRhs(), emitted);
5965 ps.addCallback({op,
false});
5966 setPendingNewline();
5970LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5972 ps <<
"`" << op.getMacroIdentifier();
5973 setPendingNewline();
5977LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5978 auto decl = op.getReferencedMacro(&state.symbolCache);
5981 ps.addCallback({op,
true});
5983 if (decl.getArgs()) {
5985 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5986 ps << cast<StringAttr>(name);
5990 if (!op.getFormatString().empty()) {
5992 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5995 ps.addCallback({op,
false});
5996 setPendingNewline();
6000void StmtEmitter::emitStatement(Operation *op) {
6007 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
6011 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
6012 succeeded(dispatchVerifVisitor(op)))
6015 emitOpError(op,
"emission to Verilog not supported");
6016 emitPendingNewlineIfNeeded();
6017 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
6018 setPendingNewline();
6029 StmtEmitter &stmtEmitter) {
6036 if (isa<IfDefProceduralOp>(op->getParentOp()))
6044 SmallVector<Value, 8> exprsToScan(op->getOperands());
6049 while (!exprsToScan.empty()) {
6050 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
6057 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
6058 auto *defOp = readInout.getOperand().getDefiningOp();
6065 if (isa<sv::WireOp>(defOp))
6070 if (!isa<RegOp, LogicOp>(defOp))
6076 if (isa<LogicOp>(defOp) &&
6077 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6081 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6082 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6090 exprsToScan.append(expr->getOperands().begin(),
6091 expr->getOperands().end());
6097 if (expr->getBlock() != op->getBlock())
6102 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6109template <
class AssignTy>
6111 AssignTy singleAssign;
6112 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6113 if (hasSVAttributes(user))
6116 if (auto assign = dyn_cast<AssignTy>(user)) {
6119 singleAssign = assign;
6123 return isa<ReadInOutOp>(user);
6125 return singleAssign;
6131 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6135 if (op1->getBlock() != user->getBlock())
6141 return op1->isBeforeInBlock(user);
6145LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6146 emitSVAttributes(op);
6147 auto value = op->getResult(0);
6148 SmallPtrSet<Operation *, 8> opsForLocation;
6149 opsForLocation.insert(op);
6151 ps.addCallback({op,
true});
6154 auto type = value.getType();
6160 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6162 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6163 unsigned targetColumn = 0;
6164 unsigned column = 0;
6167 if (maxDeclNameWidth > 0)
6168 targetColumn += maxDeclNameWidth + 1;
6171 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6172 }
else if (!word.empty()) {
6174 column += word.size();
6175 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6176 ps.spaces(numSpaces);
6177 column += numSpaces;
6180 SmallString<8> typeString;
6183 llvm::raw_svector_ostream stringStream(typeString);
6186 true, singleBitDefaultType);
6189 if (maxTypeWidth > 0)
6190 targetColumn += maxTypeWidth + 1;
6191 unsigned numSpaces = 0;
6192 if (!typeString.empty()) {
6194 column += typeString.size();
6197 if (targetColumn > column)
6198 numSpaces = targetColumn - column;
6199 ps.spaces(numSpaces);
6200 column += numSpaces;
6206 ps.invokeWithStringOS(
6207 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6210 if (state.options.printDebugInfo) {
6211 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6212 auto innerSym = innerSymOp.getInnerSymAttr();
6213 if (innerSym && !innerSym.empty()) {
6215 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6221 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6222 ps << PP::space <<
"=" << PP::space;
6223 ps.invokeWithStringOS([&](
auto &os) {
6224 emitter.printParamValue(localparam.getValue(), os, [&]() {
6225 return op->emitOpError(
"invalid localparam value");
6230 if (
auto regOp = dyn_cast<RegOp>(op)) {
6231 if (
auto initValue = regOp.getInit()) {
6232 ps << PP::space <<
"=" << PP::space;
6233 ps.scopedBox(PP::ibox0, [&]() {
6234 emitExpression(initValue, opsForLocation, LowestPrecedence,
6243 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6247 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6248 auto *source = singleAssign.getSrc().getDefiningOp();
6252 if (!source || isa<ConstantOp>(source) ||
6253 op->getNextNode() == singleAssign) {
6254 ps << PP::space <<
"=" << PP::space;
6255 ps.scopedBox(PP::ibox0, [&]() {
6256 emitExpression(singleAssign.getSrc(), opsForLocation,
6260 emitter.assignsInlined.insert(singleAssign);
6268 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6272 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6275 auto *source = singleAssign.getSrc().getDefiningOp();
6279 if (!source || isa<ConstantOp>(source) ||
6282 ps << PP::space <<
"=" << PP::space;
6283 ps.scopedBox(PP::ibox0, [&]() {
6284 emitExpression(singleAssign.getSrc(), opsForLocation,
6289 emitter.assignsInlined.insert(singleAssign);
6290 emitter.expressionsEmittedIntoDecl.insert(op);
6297 ps.addCallback({op,
false});
6298 emitLocationInfoAndNewLine(opsForLocation);
6302void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6305 NameCollector collector(emitter);
6306 collector.collectNames(block);
6309 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6310 maxTypeWidth = collector.getMaxTypeWidth();
6313void StmtEmitter::emitStatementBlock(Block &body) {
6314 ps.scopedBox(PP::bbox2, [&]() {
6319 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6320 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6325 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6326 collectNamesAndCalculateDeclarationWidths(body);
6329 for (
auto &op : body) {
6336void ModuleEmitter::emitStatement(Operation *op) {
6337 StmtEmitter(*
this, state.options).emitStatement(op);
6342void ModuleEmitter::emitSVAttributes(Operation *op) {
6350 setPendingNewline();
6357void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6358 auto verilogName =
module.getVerilogModuleNameAttr();
6360 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6362 setPendingNewline();
6371void ModuleEmitter::emitBind(BindOp op) {
6373 emitError(op,
"SV attributes emission is unimplemented for the op");
6374 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6380 Operation *childMod =
6381 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6385 ps.addCallback({op,
true});
6386 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6387 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6389 bool isFirst =
true;
6390 ps.scopedBox(PP::bbox2, [&]() {
6391 auto parentPortInfo = parentMod.getPortList();
6396 size_t maxNameLength = 0;
6397 for (
auto &elt : childPortInfo) {
6398 auto portName = elt.getVerilogName();
6399 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6400 size_t nameLength = elt.getName().size();
6401 if (nameLength <= state.options.emittedLineLength / 3)
6402 maxNameLength = std::max(maxNameLength, nameLength);
6405 SmallVector<Value> instPortValues(childPortInfo.size());
6406 inst.getValues(instPortValues, childPortInfo);
6408 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6410 Value portVal = instPortValues[idx];
6416 bool shouldPrintComma =
true;
6418 shouldPrintComma =
false;
6419 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6421 shouldPrintComma =
true;
6426 if (shouldPrintComma)
6439 ps << PP::neverbox <<
"//";
6444 if (elt.getName().size() <= maxNameLength)
6445 ps.nbsp(maxNameLength - elt.getName().size());
6447 llvm::SmallPtrSet<Operation *, 4> ops;
6448 if (elt.isOutput()) {
6449 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6450 "output port must have either single or no use");
6451 if (portVal.use_empty()) {
6452 ps <<
"/* unused */";
6453 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6454 portVal.getUses().begin()->getOwner())) {
6457 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6459 parentPortList.atOutput(outputPortNo).getVerilogName());
6461 portVal = portVal.getUsers().begin()->getOperand(0);
6462 ExprEmitter(*
this, ops)
6463 .emitExpression(portVal, LowestPrecedence,
6467 ExprEmitter(*
this, ops)
6468 .emitExpression(portVal, LowestPrecedence,
6481 ps.addCallback({op,
false});
6482 setPendingNewline();
6485void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6487 emitError(op,
"SV attributes emission is unimplemented for the op");
6489 auto instance = op.getReferencedInstance(&state.symbolCache);
6491 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6492 instance.getInterfaceType().getInterface());
6494 ps.addCallback({op,
true});
6495 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6496 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6498 ps.addCallback({op,
false});
6499 setPendingNewline();
6502void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6506 auto printParamType = [&](Type type, Attribute defaultValue,
6507 SmallString<8> &result) {
6509 llvm::raw_svector_ostream sstream(result);
6514 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6515 if (intAttr.getValue().getBitWidth() == 32)
6517 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6518 if (fpAttr.getType().isF64())
6521 if (isa<NoneType>(type))
6528 if (
auto intType = type_dyn_cast<IntegerType>(type))
6529 if (intType.getWidth() == 32) {
6530 sstream <<
"/*integer*/";
6534 printPackedType(type, sstream, module->getLoc(),
6542 size_t maxTypeWidth = 0;
6543 SmallString<8> scratch;
6544 for (
auto param : params) {
6545 auto paramAttr = cast<ParamDeclAttr>(param);
6547 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6548 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6551 if (maxTypeWidth > 0)
6554 ps.scopedBox(PP::bbox2, [&]() {
6555 ps << PP::newline <<
"#(";
6556 ps.scopedBox(PP::cbox0, [&]() {
6559 [&](Attribute param) {
6560 auto paramAttr = cast<ParamDeclAttr>(param);
6561 auto defaultValue = paramAttr.getValue();
6563 printParamType(paramAttr.getType(), defaultValue, scratch);
6564 if (!scratch.empty())
6566 if (scratch.size() < maxTypeWidth)
6567 ps.nbsp(maxTypeWidth - scratch.size());
6569 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6570 module, paramAttr.getName()));
6574 ps.invokeWithStringOS([&](
auto &os) {
6576 return module->emitError("parameter '")
6577 << paramAttr.getName().getValue()
6578 << "' has invalid value";
6583 [&]() { ps <<
"," << PP::newline; });
6589void ModuleEmitter::emitPortList(Operation *module,
6591 bool emitAsTwoStateType) {
6593 if (portInfo.
size())
6594 emitLocationInfo(module->getLoc());
6598 bool hasOutputs =
false, hasZeroWidth =
false;
6599 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6600 SmallVector<SmallString<8>, 16> portTypeStrings;
6602 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6603 auto port = portInfo.
at(i);
6607 lastNonZeroPort = i;
6610 portTypeStrings.push_back({});
6612 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6614 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6617 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6620 if (maxTypeWidth > 0)
6624 ps.scopedBox(PP::bbox2, [&]() {
6625 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6626 auto lastPort = e - 1;
6629 auto portType = portInfo.
at(portIdx).
type;
6633 bool isZeroWidth =
false;
6638 ps << (isZeroWidth ?
"// " :
" ");
6642 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6643 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6644 (state.options.emitWireInPorts ? 5 : 0) +
6649 if (!isa<ModportType>(portType)) {
6650 switch (thisPortDirection) {
6651 case ModulePort::Direction::Output:
6654 case ModulePort::Direction::Input:
6655 ps << (hasOutputs ?
"input " :
"input ");
6657 case ModulePort::Direction::InOut:
6658 ps << (hasOutputs ?
"inout " :
"inout ");
6661 if (state.options.emitWireInPorts)
6663 if (!portTypeStrings[portIdx].
empty())
6664 ps << portTypeStrings[portIdx];
6665 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6666 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6668 ps << portTypeStrings[portIdx];
6669 if (portTypeStrings[portIdx].size() < startOfNamePos)
6670 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6677 ps.invokeWithStringOS(
6678 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6681 auto innerSym = portInfo.
at(portIdx).
getSym();
6682 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6684 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6689 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6693 if (
auto loc = portInfo.
at(portIdx).
loc)
6694 emitLocationInfo(loc);
6704 if (!state.options.disallowPortDeclSharing) {
6705 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6708 auto port = portInfo.
at(portIdx);
6712 bool isZeroWidth =
false;
6717 ps << (isZeroWidth ?
"// " :
" ");
6720 ps.nbsp(startOfNamePos);
6723 StringRef name = port.getVerilogName();
6727 ps.invokeWithStringOS(
6728 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6731 auto sym = port.getSym();
6732 if (state.options.printDebugInfo && sym && !sym.empty())
6733 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6737 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6741 if (
auto loc = port.loc)
6742 emitLocationInfo(loc);
6753 if (!portInfo.
size()) {
6755 SmallPtrSet<Operation *, 8> moduleOpSet;
6756 moduleOpSet.insert(module);
6757 emitLocationInfoAndNewLine(moduleOpSet);
6760 ps <<
");" << PP::newline;
6761 setPendingNewline();
6765void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6766 currentModuleOp =
module;
6768 emitComment(module.getCommentAttr());
6769 emitSVAttributes(module);
6771 ps.addCallback({module,
true});
6775 emitParameters(module, module.getParameters());
6779 assert(state.pendingNewline);
6782 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6785 ps.addCallback({module,
false});
6787 setPendingNewline();
6789 currentModuleOp =
nullptr;
6792void ModuleEmitter::emitFunc(FuncOp func) {
6794 if (func.isDeclaration())
6797 currentModuleOp = func;
6799 ps.addCallback({func,
true});
6803 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6805 ps <<
"endfunction";
6807 currentModuleOp =
nullptr;
6816 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6823 void emit(emit::FileListOp op);
6826 void emit(Block *block);
6828 void emitOp(emit::RefOp op);
6829 void emitOp(emit::VerbatimOp op);
6833 for (Operation &op : *block) {
6834 TypeSwitch<Operation *>(&op)
6835 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6836 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6837 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6838 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6839 .Case<BindInterfaceOp>(
6840 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6841 .Case<TypeScopeOp>([&](
auto typedecls) {
6842 ModuleEmitter(state).emitStatement(typedecls);
6845 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6851 for (
auto sym : op.getFiles()) {
6852 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6854 auto it = state.fileMapping.find(fileName);
6855 if (it == state.fileMapping.end()) {
6856 emitOpError(op,
" references an invalid file: ") << sym;
6860 auto file = cast<emit::FileOp>(it->second);
6861 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6868 StringAttr target = op.getTargetAttr().getAttr();
6869 auto *targetOp = state.symbolCache.getDefinition(target);
6870 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6872 TypeSwitch<Operation *>(targetOp)
6873 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6874 .Case<hw::HWModuleOp>(
6875 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6876 .Case<TypeScopeOp>([&](
auto typedecls) {
6877 ModuleEmitter(state).emitStatement(typedecls);
6880 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6886 SmallPtrSet<Operation *, 8> ops;
6891 StringRef text = op.getText();
6895 const auto &[lhs, rhs] = text.split(
'\n');
6899 ps << PP::end << PP::newline << PP::neverbox;
6901 }
while (!text.empty());
6904 emitLocationInfoAndNewLine(ops);
6922 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6923 moduleOp->walk([&](Operation *op) {
6925 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6928 SymbolTable::getSymbolAttrName()),
6929 name.getSymName(), op);
6930 if (isa<BindOp>(op))
6936 auto collectPorts = [&](
auto moduleOp) {
6937 auto portInfo = moduleOp.getPortList();
6938 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6939 if (!p.attrs || p.attrs.empty())
6941 for (NamedAttribute portAttr : p.attrs) {
6942 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6951 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6952 for (
auto file :
designOp.getOps<emit::FileOp>())
6953 for (
auto refs : file.getOps<emit::RefOp>())
6954 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6956 SmallString<32> outputPath;
6957 for (
auto &op : *
designOp.getBody()) {
6960 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6962 bool hasFileName =
false;
6963 bool emitReplicatedOps = !isFileOp;
6964 bool addToFilelist = !isFileOp;
6970 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6972 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6973 <<
" on " << op <<
"\n";);
6974 if (!attr.isDirectory())
6977 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6978 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6981 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6986 if (!defaultFileName.isTriviallyEmpty()) {
6987 llvm::sys::path::append(outputPath, defaultFileName);
6989 op->emitError(
"file name unspecified");
6991 llvm::sys::path::append(outputPath,
"error.out");
6995 auto destFile = StringAttr::get(op->getContext(), outputPath);
6996 auto &file =
files[destFile];
6997 file.ops.push_back(info);
6998 file.emitReplicatedOps = emitReplicatedOps;
6999 file.addToFilelist = addToFilelist;
7000 file.isVerilog = outputPath.ends_with(
".sv");
7005 if (!attr || attr.isDirectory()) {
7006 auto excludeFromFileListAttr =
7007 BoolAttr::get(op->getContext(), !addToFilelist);
7008 auto includeReplicatedOpsAttr =
7009 BoolAttr::get(op->getContext(), emitReplicatedOps);
7010 auto outputFileAttr = hw::OutputFileAttr::get(
7011 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
7012 op->setAttr(
"output_file", outputFileAttr);
7018 TypeSwitch<Operation *>(&op)
7019 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
7021 fileMapping.try_emplace(file.getSymNameAttr(), file);
7022 separateFile(file, file.getFileName());
7024 .Case<emit::FragmentOp>([&](
auto fragment) {
7027 .Case<HWModuleOp>([&](
auto mod) {
7029 auto sym = mod.getNameAttr();
7032 collectInstanceSymbolsAndBinds(mod);
7034 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
7035 if (it->second.size() != 1 || attr) {
7038 op.emitError(
"modules can be emitted to a single file");
7046 if (attr || separateModules)
7052 .Case<InterfaceOp>([&](InterfaceOp intf) {
7057 for (
auto &op : *intf.getBodyBlock())
7058 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
7059 if (
auto name = symOp.getNameAttr())
7063 if (attr || separateModules)
7064 separateFile(intf, intf.getSymName() +
".sv");
7070 separateFile(op, op.getOutputFile().getFilename().getValue());
7072 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7078 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7079 [&](Operation *op) {
7085 separateFile(op,
"");
7087 .Case<FuncOp>([&](
auto op) {
7093 separateFile(op,
"");
7097 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7100 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7109 separateFile(op,
"");
7111 .Case<BindOp>([&](
auto op) {
7113 separateFile(op,
"bindfile.sv");
7118 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
7119 .Case<MacroDeclOp>([&](
auto op) {
7122 .Case<sv::ReserveNamesOp>([](
auto op) {
7125 .Case<om::ClassLike>([&](
auto op) {
7128 .Case<om::ConstantOp>([&](
auto op) {
7131 .Default([&](
auto *) {
7132 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7152 size_t lastReplicatedOp = 0;
7154 bool emitHeaderInclude =
7157 if (emitHeaderInclude)
7160 size_t numReplicatedOps =
7165 DenseSet<emit::FragmentOp> includedFragments;
7166 for (
const auto &opInfo : file.
ops) {
7167 Operation *op = opInfo.op;
7171 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7177 if (
auto fragments =
7179 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7183 op->emitError(
"cannot find referenced fragment ") << sym;
7186 emit::FragmentOp fragment = it->second;
7187 if (includedFragments.insert(fragment).second) {
7188 thingsToEmit.emplace_back(it->second);
7194 thingsToEmit.emplace_back(op);
7199 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7204 TypeSwitch<Operation *>(op)
7205 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7206 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7209 .Case<HWModuleGeneratedOp>(
7210 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7211 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7212 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7213 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7214 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7215 .Case<TypeScopeOp>([&](
auto typedecls) {
7216 ModuleEmitter(state).emitStatement(typedecls);
7218 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7220 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7221 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7222 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7223 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7224 .Default([&](
auto *op) {
7225 state.encounteredError =
true;
7226 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7233 llvm::formatted_raw_ostream &os,
7234 StringAttr fileName,
bool parallelize) {
7239 parallelize &=
context->isMultithreadingEnabled();
7250 size_t lineOffset = 0;
7251 for (
auto &entry : thingsToEmit) {
7252 entry.verilogLocs.setStream(os);
7253 if (
auto *op = entry.getOperation()) {
7258 state.addVerilogLocToOps(lineOffset, fileName);
7260 os << entry.getStringData();
7265 if (state.encounteredError)
7283 SmallString<256> buffer;
7284 llvm::raw_svector_ostream tmpStream(buffer);
7285 llvm::formatted_raw_ostream rs(tmpStream);
7293 if (state.encounteredError)
7298 for (
auto &entry : thingsToEmit) {
7301 auto *op = entry.getOperation();
7303 auto lineOffset = os.getLine() + 1;
7304 os << entry.getStringData();
7308 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7311 entry.verilogLocs.setStream(os);
7318 state.addVerilogLocToOps(0, fileName);
7319 if (state.encounteredError) {
7338 module.emitWarning()
7339 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7340 "created only at SplitExportVerilog";
7349 for (
const auto &it : emitter.
files) {
7350 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7351 "\" ----- 8< -----\n\n");
7357 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7358 "\" ----- 8< -----\n\n");
7359 for (
auto &name : it.second)
7360 contents += name.str() +
"\n";
7361 list.emplace_back(contents);
7364 llvm::formatted_raw_ostream rs(os);
7368 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7375 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7377 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7378 if (failed(failableParallelForEach(
7379 module->getContext(), modulesToPrepare,
7380 [&](
auto op) { return prepareHWModule(op, options); })))
7387struct ExportVerilogPass
7388 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7389 ExportVerilogPass(raw_ostream &os) : os(os) {}
7390 void runOnOperation()
override {
7392 mlir::OpPassManager preparePM(
"builtin.module");
7393 preparePM.addPass(createLegalizeAnonEnums());
7394 auto &modulePM = preparePM.nestAny();
7395 modulePM.addPass(createPrepareForEmission());
7396 if (failed(runPipeline(preparePM, getOperation())))
7397 return signalPassFailure();
7400 return signalPassFailure();
7407struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7408 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7409 : ExportVerilogPass{*os} {
7410 owned = std::move(os);
7414 std::unique_ptr<llvm::raw_ostream> owned;
7418std::unique_ptr<mlir::Pass>
7420 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7423std::unique_ptr<mlir::Pass>
7425 return std::make_unique<ExportVerilogPass>(os);
7436static std::unique_ptr<llvm::ToolOutputFile>
7440 SmallString<128> outputFilename(dirname);
7442 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7445 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7447 emitter.
designOp.emitError(
"cannot create output directory \"")
7448 << outputDir <<
"\": " << error.message();
7454 std::string errorMessage;
7455 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7457 emitter.
designOp.emitError(errorMessage);
7474 llvm::formatted_raw_ostream rs(output->os());
7480 StringAttr::get(fileName.getContext(), output->getFilename()),
7486 StringRef dirname) {
7497 bool insertSuccess =
7499 .insert({StringAttr::get(module.getContext(),
circtHeader),
7505 if (!insertSuccess) {
7506 module.emitError() << "tried to emit a heder to " << circtHeader
7507 << ", but the file is used as an output too.";
7513 parallelForEach(module->getContext(), emitter.
files.begin(),
7514 emitter.
files.end(), [&](
auto &it) {
7515 createSplitOutputFile(it.first, it.second, dirname,
7520 SmallString<128> filelistPath(dirname);
7521 llvm::sys::path::append(filelistPath,
"filelist.f");
7523 std::string errorMessage;
7524 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7526 module->emitError(errorMessage);
7530 for (
const auto &it : emitter.
files) {
7531 if (it.second.addToFilelist)
7532 output->os() << it.first.str() <<
"\n";
7541 for (
auto &name : it.second)
7542 output->os() << name.str() <<
"\n";
7551 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7553 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7554 if (failed(failableParallelForEach(
7555 module->getContext(), modulesToPrepare,
7556 [&](
auto op) { return prepareHWModule(op, options); })))
7564struct ExportSplitVerilogPass
7565 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7566 ExportSplitVerilogPass(StringRef directory) {
7567 directoryName = directory.str();
7569 void runOnOperation()
override {
7571 mlir::OpPassManager preparePM(
"builtin.module");
7574 modulePM.addPass(createPrepareForEmission());
7575 if (failed(runPipeline(preparePM, getOperation())))
7576 return signalPassFailure();
7579 return signalPassFailure();
7584std::unique_ptr<mlir::Pass>
7586 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.