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);
2536 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2538 ps << lhsSpace << syntax << PP::nbsp;
2545 auto rhsPrec = prec;
2546 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2547 rhsPrec = VerilogPrecedence(prec - 1);
2552 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2553 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2554 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2555 operandSignReq = NoRequirement;
2558 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2559 rhsIsUnsignedValueWithSelfDeterminedWidth);
2563 SubExprSignResult signedness = IsUnsigned;
2564 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2565 signedness = IsSigned;
2567 if (emitBinaryFlags & EB_ForceResultSigned) {
2568 ps << PP::end <<
")";
2569 signedness = IsSigned;
2573 return {prec, signedness};
2576SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2577 bool resultAlwaysUnsigned) {
2579 emitError(op,
"SV attributes emission is unimplemented for the op");
2582 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2586 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2587 resultAlwaysUnsigned ? IsUnsigned : signedness};
2592void ExprEmitter::emitSVAttributes(Operation *op) {
2606 auto concat = value.getDefiningOp<
ConcatOp>();
2607 if (!concat || concat.getNumOperands() != 2)
2610 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2611 if (constant && constant.getValue().isZero())
2612 return concat.getOperand(1);
2622SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2623 VerilogPrecedence parenthesizeIfLooserThan,
2624 SubExprSignRequirement signRequirement,
2625 bool isSelfDeterminedUnsignedValue,
2626 bool isAssignmentLikeContext) {
2628 if (
auto result = dyn_cast<OpResult>(exp))
2629 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2630 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2631 parenthesizeIfLooserThan, signRequirement,
2632 isSelfDeterminedUnsignedValue,
2633 isAssignmentLikeContext);
2637 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2642 auto *op = exp.getDefiningOp();
2646 if (!shouldEmitInlineExpr) {
2649 if (signRequirement == RequireSigned) {
2651 return {Symbol, IsSigned};
2655 return {Symbol, IsUnsigned};
2658 unsigned subExprStartIndex = buffer.tokens.size();
2660 ps.addCallback({op,
true});
2661 llvm::scope_exit done([&]() {
2663 ps.addCallback({op, false});
2669 signPreference = signRequirement;
2671 bool bitCastAdded =
false;
2672 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2674 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2675 ps.addAsString(inType.getWidth());
2676 ps <<
"'(" << PP::ibox0;
2677 bitCastAdded =
true;
2681 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2682 isAssignmentLikeContext);
2683 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2689 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2691 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2693 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2694 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2697 expInfo.signedness = IsSigned;
2698 expInfo.precedence = Selection;
2699 }
else if (signRequirement == RequireUnsigned &&
2700 expInfo.signedness == IsSigned) {
2703 expInfo.signedness = IsUnsigned;
2704 expInfo.precedence = Selection;
2705 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2712 expInfo.precedence = Selection;
2719 emittedExprs.insert(exp.getDefiningOp());
2723SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2724 auto openFn = [&]() {
2726 ps.addAsString(op.getMultiple());
2729 auto closeFn = [&]() { ps <<
"}}"; };
2733 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2734 if (op.getOperand().hasOneUse()) {
2735 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2736 return {Symbol, IsUnsigned};
2739 emitBracedList(op.getOperand(), openFn, closeFn);
2740 return {Symbol, IsUnsigned};
2743SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2744 emitBracedList(op.getOperands());
2745 return {Symbol, IsUnsigned};
2748SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2752 Type toType = op.getType();
2754 toType, op.getInput().getType(), op.getLoc(),
2755 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2757 ps.invokeWithStringOS(
2758 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2761 return emitSubExpr(op.getInput(), LowestPrecedence);
2764SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2765 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2766 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2767 SubExprSignRequirement signop[] = {
2769 NoRequirement, NoRequirement,
2771 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2773 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2775 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2777 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2778 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2781 if (op.isEqualAllOnes())
2782 return emitUnary(op,
"&",
true);
2785 if (op.isNotEqualZero())
2786 return emitUnary(op,
"|",
true);
2788 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2792 result.signedness = IsUnsigned;
2796SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2798 emitError(op,
"SV attributes emission is unimplemented for the op");
2800 unsigned loBit = op.getLowBit();
2801 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2803 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2804 assert((x.precedence == Symbol ||
2806 "should be handled by isExpressionUnableToInline");
2811 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2815 ps.addAsString(hiBit);
2816 if (hiBit != loBit) {
2818 ps.addAsString(loBit);
2821 return {Unary, IsUnsigned};
2824SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2826 emitError(op,
"SV attributes emission is unimplemented for the op");
2828 auto decl = op.getReferencedDecl(state.symbolCache);
2831 return {Selection, IsUnsigned};
2834SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2836 emitError(op,
"SV attributes emission is unimplemented for the op");
2839 ps.scopedBox(PP::ibox0, [&]() {
2841 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2842 [&]() { ps <<
"," << PP::space; });
2845 return {Symbol, IsUnsigned};
2848SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2850 emitError(op,
"SV attributes emission is unimplemented for the op");
2852 auto decl = op.getReferencedDecl(state.symbolCache);
2856 return {Selection, IsUnsigned};
2859SubExprInfo ExprEmitter::visitSV(XMROp op) {
2861 emitError(op,
"SV attributes emission is unimplemented for the op");
2863 if (op.getIsRooted())
2865 for (
auto s : op.getPath())
2866 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2868 return {Selection, IsUnsigned};
2873SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2875 emitError(op,
"SV attributes emission is unimplemented for the op");
2878 auto globalRef = op.getReferencedPath(&state.symbolCache);
2879 auto namepath = globalRef.getNamepathAttr().getValue();
2880 auto *
module = state.symbolCache.getDefinition(
2881 cast<InnerRefAttr>(namepath.front()).getModule());
2883 for (
auto sym : namepath) {
2885 auto innerRef = cast<InnerRefAttr>(sym);
2886 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2887 innerRef.getName());
2888 if (ref.hasPort()) {
2894 auto leaf = op.getVerbatimSuffixAttr();
2895 if (leaf && leaf.size())
2897 return {Selection, IsUnsigned};
2900SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2902 emitError(op,
"SV attributes emission is unimplemented for the op");
2904 emitTextWithSubstitutions(
2905 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2906 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2908 return {Unary, IsUnsigned};
2911template <
typename MacroTy>
2912SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2914 emitError(op,
"SV attributes emission is unimplemented for the op");
2917 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2918 assert(macroOp &&
"Invalid IR");
2920 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2922 if (!op.getInputs().empty()) {
2924 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2925 emitExpression(val, LowestPrecedence, false);
2929 return {LowestPrecedence, IsUnsigned};
2932SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2933 return emitMacroCall(op);
2936SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2937 return emitMacroCall(op);
2940SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2942 emitError(op,
"SV attributes emission is unimplemented for the op");
2944 ps.addAsString(op.getWidth());
2946 return {Unary, IsUnsigned};
2949SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2951 emitError(op,
"SV attributes emission is unimplemented for the op");
2953 ps.writeQuotedEscaped(op.getStr());
2954 return {Symbol, IsUnsigned};
2957SubExprInfo ExprEmitter::visitSV(ConcatStrOp op) {
2959 emitError(op,
"SV attributes emission is unimplemented for the op");
2963 emitBracedList(op.getInputs());
2964 return {Symbol, IsUnsigned};
2967SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2969 emitError(op,
"SV attributes emission is unimplemented for the op");
2971 ps.addAsString(op.getWidth());
2973 return {Unary, IsUnsigned};
2976SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2977 bool isNegated =
false;
2980 if (signPreference == RequireSigned && value.isNegative() &&
2981 !value.isMinSignedValue()) {
2986 ps.addAsString(type.getWidth());
2990 if (signPreference == RequireSigned)
2996 SmallString<32> valueStr;
2998 (-value).toStringUnsigned(valueStr, 16);
3000 value.toStringUnsigned(valueStr, 16);
3003 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
3006SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
3008 emitError(op,
"SV attributes emission is unimplemented for the op");
3010 auto value = op.getValue();
3014 if (value.getBitWidth() == 0) {
3015 emitOpError(op,
"will not emit zero width constants in the general case");
3016 ps <<
"<<unsupported zero width constant: "
3017 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3018 return {Unary, IsUnsigned};
3021 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3024void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3025 bool printAsPattern, Operation *op) {
3026 if (printAsPattern && !isAssignmentLikeContext)
3027 emitAssignmentPatternContextError(op);
3028 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3031 elementValues, [&]() { ps << openDelim; },
3032 [&](Attribute elementValue) {
3033 printConstantAggregate(elementValue,
elementType, op);
3035 [&]() { ps <<
"}"; });
3038void ExprEmitter::printConstantStruct(
3039 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3040 bool printAsPattern, Operation *op) {
3041 if (printAsPattern && !isAssignmentLikeContext)
3042 emitAssignmentPatternContextError(op);
3049 auto fieldRange = llvm::make_filter_range(
3050 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3055 if (printAsPattern) {
3057 fieldRange, [&]() { ps <<
"'{"; },
3058 [&](
const auto &fieldAndValue) {
3059 ps.scopedBox(PP::ibox2, [&]() {
3060 const auto &[field, value] = fieldAndValue;
3061 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3062 <<
":" << PP::space;
3063 printConstantAggregate(value, field.type, op);
3066 [&]() { ps <<
"}"; });
3069 fieldRange, [&]() { ps <<
"{"; },
3070 [&](
const auto &fieldAndValue) {
3071 ps.scopedBox(PP::ibox2, [&]() {
3072 const auto &[field, value] = fieldAndValue;
3073 printConstantAggregate(value, field.type, op);
3076 [&]() { ps <<
"}"; });
3080void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3083 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3084 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3085 isAssignmentLikeContext, op);
3088 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3089 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3093 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3094 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3095 isAssignmentLikeContext, op);
3097 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3098 auto value = cast<IntegerAttr>(attr).getValue();
3099 printConstantScalar(value, intType);
3103 emitOpError(op,
"contains constant of type ")
3104 << type <<
" which cannot be emitted as Verilog";
3107SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3109 emitError(op,
"SV attributes emission is unimplemented for the op");
3113 "zero-bit types not allowed at this point");
3115 printConstantAggregate(op.getFields(), op.getType(), op);
3116 return {Symbol, IsUnsigned};
3119SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3121 emitError(op,
"SV attributes emission is unimplemented for the op");
3123 return ps.invokeWithStringOS([&](
auto &os) {
3124 return emitter.printParamValue(op.getValue(), os, [&]() {
3125 return op->emitOpError(
"invalid parameter use");
3134 emitError(op,
"SV attributes emission is unimplemented for the op");
3136 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3138 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3140 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3142 ps.addAsString(dstWidth);
3144 return {Selection, arrayPrec.signedness};
3147SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3148 emitSubExpr(op.getInput(), Selection);
3153 emitSubExpr(op.getIndex(), LowestPrecedence);
3155 emitSVAttributes(op);
3156 return {Selection, IsUnsigned};
3162 emitError(op,
"SV attributes emission is unimplemented for the op");
3164 if (op.isUniform()) {
3166 ps.addAsString(op.getInputs().size());
3168 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3172 op.getInputs(), [&]() { ps <<
"{"; },
3175 emitSubExprIBox2(v);
3178 [&]() { ps <<
"}"; });
3180 return {Unary, IsUnsigned};
3183SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3185 emitError(op,
"SV attributes emission is unimplemented for the op");
3188 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3189 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3190 return {Unary, IsUnsigned};
3195 emitError(op,
"SV attributes emission is unimplemented for the op");
3197 emitBracedList(op.getOperands());
3198 return {Unary, IsUnsigned};
3201SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3203 emitError(op,
"SV attributes emission is unimplemented for the op");
3205 auto index = op.getIndex();
3206 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3211 emitSubExpr(index, LowestPrecedence);
3213 return {Selection, arrayPrec.signedness};
3216SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3218 emitError(op,
"SV attributes emission is unimplemented for the op");
3220 auto prec = emitSubExpr(op.getInput(), Selection);
3222 emitSubExpr(op.getBase(), LowestPrecedence);
3223 if (op.getDecrement())
3227 ps.addAsString(op.getWidth());
3229 return {Selection, prec.signedness};
3232SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3234 emitError(op,
"SV attributes emission is unimplemented for the op");
3236 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3238 emitSubExpr(op.getBase(), LowestPrecedence);
3239 if (op.getDecrement())
3243 ps.addAsString(op.getWidth());
3248SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3250 emitError(op,
"SV attributes emission is unimplemented for the op");
3252 auto prec = emitSubExpr(op.getInput(), Selection);
3254 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3255 return {Selection, prec.signedness};
3258SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3260 emitError(op,
"SV attributes emission is unimplemented for the op");
3263 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3268SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3270 emitError(op,
"SV attributes emission is unimplemented for the op");
3273 ps.scopedBox(PP::ibox0, [&]() {
3274 ps.writeQuotedEscaped(op.getFormatString());
3281 for (
auto operand : op.getSubstitutions()) {
3282 ps <<
"," << PP::space;
3283 emitSubExpr(operand, LowestPrecedence);
3287 return {Symbol, IsUnsigned};
3290SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3292 emitError(op,
"SV attributes emission is unimplemented for the op");
3295 return {Symbol, IsUnsigned};
3298SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3300 emitError(op,
"SV attributes emission is unimplemented for the op");
3303 return {Symbol, IsUnsigned};
3306SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3320 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3321 ps.scopedBox(PP::ibox0, [&]() {
3322 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3326 emitSVAttributes(op);
3328 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3329 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3333 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3334 return emitSubExpr(op.getFalseValue(), Conditional);
3337 SubExprSignResult signedness = IsUnsigned;
3338 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3339 signedness = IsSigned;
3341 return {Conditional, signedness};
3345SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3347 emitError(op,
"SV attributes emission is unimplemented for the op");
3350 emitSubExpr(op.getInput(), LowestPrecedence);
3353 return {Symbol, IsUnsigned};
3356SubExprInfo ExprEmitter::printStructCreate(
3357 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3359 bool printAsPattern, Operation *op) {
3360 if (printAsPattern && !isAssignmentLikeContext)
3361 emitAssignmentPatternContextError(op);
3364 auto filteredFields = llvm::make_filter_range(
3365 llvm::enumerate(fieldInfos),
3366 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3368 if (printAsPattern) {
3370 filteredFields, [&]() { ps <<
"'{"; },
3371 [&](
const auto &field) {
3372 ps.scopedBox(PP::ibox2, [&]() {
3374 emitter.getVerilogStructFieldName(field.value().name))
3375 <<
":" << PP::space;
3376 fieldFn(field.value(), field.index());
3379 [&]() { ps <<
"}"; });
3382 filteredFields, [&]() { ps <<
"{"; },
3383 [&](
const auto &field) {
3384 ps.scopedBox(PP::ibox2,
3385 [&]() { fieldFn(field.value(), field.index()); });
3387 [&]() { ps <<
"}"; });
3390 return {Selection, IsUnsigned};
3395 emitError(op,
"SV attributes emission is unimplemented for the op");
3399 bool printAsPattern = isAssignmentLikeContext;
3400 StructType structType = op.getType();
3401 return printStructCreate(
3402 structType.getElements(),
3403 [&](
const auto &field,
auto index) {
3404 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3406 isAssignmentLikeContext);
3408 printAsPattern, op);
3413 emitError(op,
"SV attributes emission is unimplemented for the op");
3415 emitSubExpr(op.getInput(), Selection);
3417 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3418 return {Selection, IsUnsigned};
3421SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3423 emitError(op,
"SV attributes emission is unimplemented for the op");
3427 bool printAsPattern = isAssignmentLikeContext;
3428 StructType structType = op.getType();
3429 return printStructCreate(
3430 structType.getElements(),
3431 [&](
const auto &field,
auto index) {
3432 if (field.name == op.getFieldNameAttr()) {
3433 emitSubExpr(op.getNewValue(), Selection);
3435 emitSubExpr(op.getInput(), Selection);
3437 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3440 printAsPattern, op);
3443SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3444 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3445 return {Selection, IsUnsigned};
3448SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3450 emitError(op,
"SV attributes emission is unimplemented for the op");
3451 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3454 result.signedness = IsUnsigned;
3458SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3460 emitError(op,
"SV attributes emission is unimplemented for the op");
3464 auto unionWidth = hw::getBitWidth(unionType);
3465 auto &element = unionType.getElements()[op.getFieldIndex()];
3466 auto elementWidth = hw::getBitWidth(element.type);
3469 if (!elementWidth) {
3470 ps.addAsString(unionWidth);
3472 return {Unary, IsUnsigned};
3476 if (elementWidth == unionWidth) {
3477 emitSubExpr(op.getInput(), LowestPrecedence);
3478 return {Unary, IsUnsigned};
3483 ps.scopedBox(PP::ibox0, [&]() {
3484 if (
auto prePadding = element.offset) {
3485 ps.addAsString(prePadding);
3486 ps <<
"'h0," << PP::space;
3488 emitSubExpr(op.getInput(), Selection);
3489 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3490 ps <<
"," << PP::space;
3491 ps.addAsString(postPadding);
3497 return {Unary, IsUnsigned};
3500SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3502 emitError(op,
"SV attributes emission is unimplemented for the op");
3503 emitSubExpr(op.getInput(), Selection);
3506 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3507 auto unionWidth = hw::getBitWidth(unionType);
3508 auto &element = unionType.getElements()[op.getFieldIndex()];
3509 auto elementWidth = hw::getBitWidth(element.type);
3510 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3511 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3520 return {Selection, IsUnsigned};
3523SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3524 emitOpError(op,
"cannot emit this expression to Verilog");
3525 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3527 return {Symbol, IsUnsigned};
3543enum class PropertyPrecedence {
3563struct EmittedProperty {
3565 PropertyPrecedence precedence;
3570class PropertyEmitter :
public EmitterBase,
3571 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3575 PropertyEmitter(ModuleEmitter &emitter,
3576 SmallPtrSetImpl<Operation *> &emittedOps)
3577 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3578 PropertyEmitter(ModuleEmitter &emitter,
3579 SmallPtrSetImpl<Operation *> &emittedOps,
3581 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3583 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3584 assert(state.pp.getListener() == &state.saver);
3587 void emitAssertPropertyDisable(
3588 Value property, Value disable,
3589 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3591 void emitAssertPropertyBody(
3592 Value property, Value disable,
3593 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3595 void emitAssertPropertyBody(
3596 Value property, sv::EventControl event, Value clock, Value disable,
3597 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3602 emitNestedProperty(Value property,
3603 PropertyPrecedence parenthesizeIfLooserThan);
3604 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3605 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3607 EmittedProperty visitUnhandledLTL(Operation *op);
3608 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3609 EmittedProperty visitLTL(ltl::AndOp op);
3610 EmittedProperty visitLTL(ltl::OrOp op);
3611 EmittedProperty visitLTL(ltl::IntersectOp op);
3612 EmittedProperty visitLTL(ltl::DelayOp op);
3613 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3614 EmittedProperty visitLTL(ltl::ConcatOp op);
3615 EmittedProperty visitLTL(ltl::RepeatOp op);
3616 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3617 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3618 EmittedProperty visitLTL(ltl::NotOp op);
3619 EmittedProperty visitLTL(ltl::ImplicationOp op);
3620 EmittedProperty visitLTL(ltl::UntilOp op);
3621 EmittedProperty visitLTL(ltl::EventuallyOp op);
3622 EmittedProperty visitLTL(ltl::ClockOp op);
3624 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3625 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3626 void emitLTLConcat(ValueRange inputs);
3629 ModuleEmitter &emitter;
3634 SmallPtrSetImpl<Operation *> &emittedOps;
3637 SmallVector<Token> localTokens;
3650void PropertyEmitter::emitAssertPropertyDisable(
3651 Value property, Value disable,
3652 PropertyPrecedence parenthesizeIfLooserThan) {
3655 ps <<
"disable iff" << PP::nbsp <<
"(";
3657 emitNestedProperty(disable, PropertyPrecedence::Unary);
3663 ps.scopedBox(PP::ibox0,
3664 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3670void PropertyEmitter::emitAssertPropertyBody(
3671 Value property, Value disable,
3672 PropertyPrecedence parenthesizeIfLooserThan) {
3673 assert(localTokens.empty());
3675 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3680 if (&buffer.tokens == &localTokens)
3681 buffer.flush(state.pp);
3684void PropertyEmitter::emitAssertPropertyBody(
3685 Value property, sv::EventControl event, Value clock, Value disable,
3686 PropertyPrecedence parenthesizeIfLooserThan) {
3687 assert(localTokens.empty());
3690 ps.scopedBox(PP::ibox2, [&] {
3691 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3692 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3698 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3703 if (&buffer.tokens == &localTokens)
3704 buffer.flush(state.pp);
3707EmittedProperty PropertyEmitter::emitNestedProperty(
3708 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3718 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3719 ExprEmitter(emitter, emittedOps, buffer.tokens)
3720 .emitExpression(property, LowestPrecedence,
3722 return {PropertyPrecedence::Symbol};
3725 unsigned startIndex = buffer.tokens.size();
3726 auto info = dispatchLTLVisitor(property.getDefiningOp());
3731 if (
info.precedence > parenthesizeIfLooserThan) {
3733 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3734 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3736 ps << PP::end <<
")";
3738 info.precedence = PropertyPrecedence::Symbol;
3742 emittedOps.insert(property.getDefiningOp());
3746EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3747 emitOpError(op,
"emission as Verilog property or sequence not supported");
3748 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3749 return {PropertyPrecedence::Symbol};
3752EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3754 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3755 return {PropertyPrecedence::Symbol};
3758EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3761 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3762 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3763 return {PropertyPrecedence::And};
3766EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3769 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3770 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3771 return {PropertyPrecedence::Or};
3774EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3778 emitNestedProperty(input, PropertyPrecedence::Intersect);
3780 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3781 return {PropertyPrecedence::Intersect};
3784void PropertyEmitter::emitLTLDelay(int64_t delay,
3785 std::optional<int64_t> length) {
3789 ps.addAsString(delay);
3792 ps.addAsString(delay);
3794 ps.addAsString(delay + *length);
3800 }
else if (delay == 1) {
3804 ps.addAsString(delay);
3810void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3812 ps.scopedBox(PP::ibox2, [&] {
3813 ps <<
PPExtString(stringifyClockEdge(edge)) << PP::space;
3814 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3819EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3820 emitLTLDelay(op.getDelay(), op.getLength());
3822 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3823 return {PropertyPrecedence::Concat};
3826EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3827 emitLTLClockingEvent(op.getEdge(), op.getClock());
3829 emitLTLDelay(op.getDelay(), op.getLength());
3831 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3832 return {PropertyPrecedence::Clocking};
3835void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3836 bool addSeparator =
false;
3837 for (
auto input : inputs) {
3840 if (!input.getDefiningOp<ltl::DelayOp>())
3841 ps <<
"##0" << PP::space;
3843 addSeparator =
true;
3844 emitNestedProperty(input, PropertyPrecedence::Concat);
3848EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3849 emitLTLConcat(op.getInputs());
3850 return {PropertyPrecedence::Concat};
3853EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3854 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3855 if (
auto more = op.getMore()) {
3857 ps.addAsString(op.getBase());
3860 ps.addAsString(op.getBase() + *more);
3864 if (op.getBase() == 0) {
3866 }
else if (op.getBase() == 1) {
3870 ps.addAsString(op.getBase());
3874 return {PropertyPrecedence::Repeat};
3877EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3878 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3880 auto more = op.getMore();
3882 ps.addAsString(op.getBase());
3885 ps.addAsString(op.getBase() + more);
3889 return {PropertyPrecedence::Repeat};
3892EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3893 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3895 auto more = op.getMore();
3897 ps.addAsString(op.getBase());
3900 ps.addAsString(op.getBase() + more);
3904 return {PropertyPrecedence::Repeat};
3907EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3908 ps <<
"not" << PP::space;
3909 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3910 return {PropertyPrecedence::Unary};
3916 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3917 if (!concatOp || concatOp.getInputs().size() < 2)
3919 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3920 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3922 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3923 if (!constOp || !constOp.getValue().isOne())
3925 return concatOp.getInputs().drop_back();
3928EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3932 emitLTLConcat(range);
3933 ps << PP::space <<
"|=>" << PP::nbsp;
3935 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3936 ps << PP::space <<
"|->" << PP::nbsp;
3938 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3939 return {PropertyPrecedence::Implication};
3942EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3943 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3944 ps << PP::space <<
"until" << PP::space;
3945 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3946 return {PropertyPrecedence::Until};
3949EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3950 ps <<
"s_eventually" << PP::space;
3951 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3952 return {PropertyPrecedence::Qualifier};
3955EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3956 emitLTLClockingEvent(op.getEdge(), op.getClock());
3958 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3959 return {PropertyPrecedence::Clocking};
3969class NameCollector {
3971 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3975 void collectNames(Block &block);
3977 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3978 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3981 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3982 ModuleEmitter &moduleEmitter;
3987 static constexpr size_t maxTypeWidthBound = 32;
3992void NameCollector::collectNames(Block &block) {
3995 for (
auto &op : block) {
3999 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
4002 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
4006 for (
auto result : op.getResults()) {
4008 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
4009 SmallString<16> typeString;
4013 llvm::raw_svector_ostream stringStream(typeString);
4015 stringStream, op.getLoc());
4017 if (typeString.size() <= maxTypeWidthBound)
4018 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4025 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4026 for (
auto ®ion : op.getRegions()) {
4027 if (!region.empty())
4028 collectNames(region.front());
4042class StmtEmitter :
public EmitterBase,
4050 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4052 void emitStatement(Operation *op);
4053 void emitStatementBlock(Block &body);
4056 LogicalResult emitDeclaration(Operation *op);
4059 void collectNamesAndCalculateDeclarationWidths(Block &block);
4062 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4063 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4064 bool isAssignmentLikeContext =
false);
4065 void emitSVAttributes(Operation *op);
4068 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4071 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4075 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4076 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4077 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4078 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4079 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4080 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4082 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4083 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4084 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4085 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4086 template <
typename Op>
4089 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4090 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4091 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4093 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4094 LogicalResult visitSV(
AssignOp op);
4095 LogicalResult visitSV(BPAssignOp op);
4096 LogicalResult visitSV(PAssignOp op);
4097 LogicalResult visitSV(ForceOp op);
4098 LogicalResult visitSV(ReleaseOp op);
4099 LogicalResult visitSV(AliasOp op);
4100 LogicalResult visitSV(InterfaceInstanceOp op);
4101 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4102 LogicalResult visitStmt(OutputOp op);
4104 LogicalResult visitStmt(InstanceOp op);
4105 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4106 ArrayRef<Value> instPortValues);
4111 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4112 LogicalResult visitSV(OrderedOutputOp op);
4113 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4114 LogicalResult visitSV(IfDefProceduralOp op) {
4115 return emitIfDef(op, op.getCond());
4117 LogicalResult visitSV(IfOp op);
4118 LogicalResult visitSV(AlwaysOp op);
4119 LogicalResult visitSV(AlwaysCombOp op);
4120 LogicalResult visitSV(AlwaysFFOp op);
4121 LogicalResult visitSV(InitialOp op);
4122 LogicalResult visitSV(CaseOp op);
4123 template <
typename OpTy,
typename EmitPrefixFn>
4125 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4126 ValueRange substitutions, EmitPrefixFn emitPrefix);
4127 LogicalResult visitSV(WriteOp op);
4128 LogicalResult visitSV(FWriteOp op);
4129 LogicalResult visitSV(FFlushOp op);
4130 LogicalResult visitSV(FCloseOp op);
4131 LogicalResult visitSV(VerbatimOp op);
4132 LogicalResult visitSV(MacroRefOp op);
4134 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4135 std::optional<unsigned> verbosity);
4136 LogicalResult visitSV(StopOp op);
4137 LogicalResult visitSV(FinishOp op);
4138 LogicalResult visitSV(ExitOp op);
4140 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4141 std::optional<unsigned> verbosity,
4143 ValueRange operands);
4146 template <
typename OpTy>
4147 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4148 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4149 op.getMessageAttr(), op.getSubstitutions());
4153 template <
typename OpTy>
4154 LogicalResult emitFatalMessageOp(OpTy op) {
4155 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4156 op.getMessageAttr(), op.getSubstitutions());
4159 LogicalResult visitSV(FatalProceduralOp op);
4160 LogicalResult visitSV(FatalOp op);
4161 LogicalResult visitSV(ErrorProceduralOp op);
4162 LogicalResult visitSV(WarningProceduralOp op);
4163 LogicalResult visitSV(InfoProceduralOp op);
4164 LogicalResult visitSV(ErrorOp op);
4165 LogicalResult visitSV(WarningOp op);
4166 LogicalResult visitSV(InfoOp op);
4168 LogicalResult visitSV(ReadMemOp op);
4170 LogicalResult visitSV(GenerateOp op);
4171 LogicalResult visitSV(GenerateCaseOp op);
4172 LogicalResult visitSV(GenerateForOp op);
4174 LogicalResult visitSV(
ForOp op);
4176 void emitAssertionLabel(Operation *op);
4177 void emitAssertionMessage(StringAttr message, ValueRange args,
4178 SmallPtrSetImpl<Operation *> &ops,
4180 template <
typename Op>
4181 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4182 LogicalResult visitSV(AssertOp op);
4183 LogicalResult visitSV(AssumeOp op);
4184 LogicalResult visitSV(CoverOp op);
4185 template <
typename Op>
4186 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4187 LogicalResult visitSV(AssertConcurrentOp op);
4188 LogicalResult visitSV(AssumeConcurrentOp op);
4189 LogicalResult visitSV(CoverConcurrentOp op);
4190 template <
typename Op>
4191 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4192 LogicalResult visitSV(AssertPropertyOp op);
4193 LogicalResult visitSV(AssumePropertyOp op);
4194 LogicalResult visitSV(CoverPropertyOp op);
4196 LogicalResult visitSV(BindOp op);
4197 LogicalResult visitSV(InterfaceOp op);
4199 LogicalResult visitSV(InterfaceSignalOp op);
4200 LogicalResult visitSV(InterfaceModportOp op);
4201 LogicalResult visitSV(AssignInterfaceSignalOp op);
4202 LogicalResult visitSV(MacroErrorOp op);
4203 LogicalResult visitSV(MacroDefOp op);
4205 void emitBlockAsStatement(Block *block,
4206 const SmallPtrSetImpl<Operation *> &locationOps,
4207 StringRef multiLineComment = StringRef());
4209 LogicalResult visitSV(FuncDPIImportOp op);
4210 template <
typename CallOp>
4211 LogicalResult emitFunctionCall(CallOp callOp);
4212 LogicalResult visitSV(FuncCallProceduralOp op);
4213 LogicalResult visitSV(FuncCallOp op);
4214 LogicalResult visitSV(ReturnOp op);
4215 LogicalResult visitSV(IncludeOp op);
4218 ModuleEmitter &emitter;
4223 size_t maxDeclNameWidth = 0;
4224 size_t maxTypeWidth = 0;
4235void StmtEmitter::emitExpression(Value exp,
4236 SmallPtrSetImpl<Operation *> &emittedExprs,
4237 VerilogPrecedence parenthesizeIfLooserThan,
4238 bool isAssignmentLikeContext) {
4239 ExprEmitter(emitter, emittedExprs)
4240 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4245void StmtEmitter::emitSVAttributes(Operation *op) {
4253 setPendingNewline();
4256void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4257 llvm::function_ref<
void()> emitRHS,
4259 std::optional<PPExtString> wordBeforeLHS) {
4261 ps.scopedBox(PP::ibox2, [&]() {
4262 if (wordBeforeLHS) {
4263 ps << *wordBeforeLHS << PP::space;
4267 ps << PP::space << syntax << PP::space;
4269 ps.scopedBox(PP::ibox0, [&]() {
4276template <
typename Op>
4278StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4279 std::optional<PPExtString> wordBeforeLHS) {
4280 SmallPtrSet<Operation *, 8> ops;
4284 ps.addCallback({op,
true});
4285 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4287 emitExpression(op.getSrc(), ops, LowestPrecedence,
4292 ps.addCallback({op,
false});
4293 emitLocationInfoAndNewLine(ops);
4297LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4300 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4303 if (emitter.assignsInlined.count(op))
4307 emitSVAttributes(op);
4312LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4313 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4317 if (emitter.assignsInlined.count(op))
4321 emitSVAttributes(op);
4326LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4328 emitSVAttributes(op);
4333LogicalResult StmtEmitter::visitSV(ForceOp op) {
4335 emitError(op,
"SV attributes emission is unimplemented for the op");
4340LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4342 emitError(op,
"SV attributes emission is unimplemented for the op");
4345 SmallPtrSet<Operation *, 8> ops;
4347 ps.addCallback({op,
true});
4348 ps.scopedBox(PP::ibox2, [&]() {
4349 ps <<
"release" << PP::space;
4350 emitExpression(op.getDest(), ops);
4353 ps.addCallback({op,
false});
4354 emitLocationInfoAndNewLine(ops);
4358LogicalResult StmtEmitter::visitSV(AliasOp op) {
4360 emitError(op,
"SV attributes emission is unimplemented for the op");
4363 SmallPtrSet<Operation *, 8> ops;
4365 ps.addCallback({op,
true});
4366 ps.scopedBox(PP::ibox2, [&]() {
4367 ps <<
"alias" << PP::space;
4368 ps.scopedBox(PP::cbox0, [&]() {
4370 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4371 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4375 ps.addCallback({op,
false});
4376 emitLocationInfoAndNewLine(ops);
4380LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4381 auto doNotPrint = op.getDoNotPrint();
4382 if (doNotPrint && !state.options.emitBindComments)
4386 emitError(op,
"SV attributes emission is unimplemented for the op");
4389 StringRef prefix =
"";
4390 ps.addCallback({op,
true});
4393 ps <<
"// This interface is elsewhere emitted as a bind statement."
4397 SmallPtrSet<Operation *, 8> ops;
4400 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4401 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4402 "point to an interface");
4405 if (!prefix.empty())
4411 ps.addCallback({op,
false});
4412 emitLocationInfoAndNewLine(ops);
4420LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4422 SmallPtrSet<Operation *, 8> ops;
4423 size_t operandIndex = 0;
4425 for (
PortInfo port : ports.getOutputs()) {
4426 auto operand = op->getOperand(operandIndex);
4430 if (operand.hasOneUse() && operand.getDefiningOp() &&
4431 isa<InstanceOp>(operand.getDefiningOp())) {
4440 ps.addCallback({op,
true});
4442 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4444 ps <<
"// Zero width: ";
4447 ps <<
"assign" << PP::space;
4449 ps << PP::space <<
"=" << PP::space;
4450 ps.scopedBox(PP::ibox0, [&]() {
4454 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4455 ps <<
"/*Zero width*/";
4457 emitExpression(operand, ops, LowestPrecedence,
4462 ps.addCallback({op,
false});
4463 emitLocationInfoAndNewLine(ops);
4470LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4471 auto parent = op->getParentOfType<PortList>();
4473 return emitOutputLikeOp(op, ports);
4476LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4478 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4479 ps <<
"`ifndef " << typescopeDef << PP::newline;
4480 ps <<
"`define " << typescopeDef;
4481 setPendingNewline();
4482 emitStatementBlock(*op.getBodyBlock());
4484 ps <<
"`endif // " << typescopeDef;
4485 setPendingNewline();
4489LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4491 emitError(op,
"SV attributes emission is unimplemented for the op");
4496 ps << PP::neverbox <<
"// ";
4498 SmallPtrSet<Operation *, 8> ops;
4500 ps.scopedBox(PP::ibox2, [&]() {
4501 ps <<
"typedef" << PP::space;
4502 ps.invokeWithStringOS([&](
auto &os) {
4504 op.getAliasType(),
false);
4506 ps << PP::space <<
PPExtString(op.getPreferredName());
4507 ps.invokeWithStringOS(
4508 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4513 emitLocationInfoAndNewLine(ops);
4517template <
typename CallOpTy>
4518LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4522 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4524 SmallPtrSet<Operation *, 8> ops;
4528 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4529 if (explicitReturn) {
4530 assert(explicitReturn.hasOneUse());
4531 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4532 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4533 emitExpression(bpassignOp.getDest(), ops);
4535 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4536 ps <<
"assign" << PP::nbsp;
4537 emitExpression(assignOp.getDest(), ops);
4539 ps << PP::nbsp <<
"=" << PP::nbsp;
4542 auto arguments = callee.getPortList(
true);
4546 bool needsComma =
false;
4547 auto printArg = [&](Value value) {
4549 ps <<
"," << PP::space;
4550 emitExpression(value, ops);
4554 ps.scopedBox(PP::ibox0, [&] {
4555 unsigned inputIndex = 0, outputIndex = 0;
4556 for (
auto arg : arguments) {
4559 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4561 printArg(op.getInputs()[inputIndex++]);
4566 emitLocationInfoAndNewLine(ops);
4570LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4571 return emitFunctionCall(op);
4574LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4575 return emitFunctionCall(op);
4578template <
typename PPS>
4580 bool isAutomatic =
false,
4581 bool emitAsTwoStateType =
false) {
4582 ps <<
"function" << PP::nbsp;
4584 ps <<
"automatic" << PP::nbsp;
4585 auto retType = op.getExplicitlyReturnedType();
4587 ps.invokeWithStringOS([&](
auto &os) {
4588 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4589 emitAsTwoStateType);
4595 emitter.emitPortList(
4599LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4600 auto parent = op->getParentOfType<sv::FuncOp>();
4602 return emitOutputLikeOp(op, ports);
4605LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4607 ps <<
"`include" << PP::nbsp;
4609 if (op.getStyle() == IncludeStyle::System)
4610 ps <<
"<" << op.getTarget() <<
">";
4612 ps <<
"\"" << op.getTarget() <<
"\"";
4614 emitLocationInfo(op.getLoc());
4615 setPendingNewline();
4619LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4622 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4626 if (
auto linkageName = importOp.getLinkageName())
4627 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4629 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4630 assert(op.isDeclaration() &&
"function must be a declaration");
4633 assert(state.pendingNewline);
4639LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4641 emitError(op,
"SV attributes emission is unimplemented for the op");
4644 SmallPtrSet<Operation *, 8> ops;
4647 ps.addCallback({op,
true});
4649 if (
auto fd = op.getFd())
4650 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4653 ps.addCallback({op,
false});
4654 emitLocationInfoAndNewLine(ops);
4658LogicalResult StmtEmitter::visitSV(FCloseOp op) {
4660 emitError(op,
"SV attributes emission is unimplemented for the op");
4663 SmallPtrSet<Operation *, 8> ops;
4666 ps.addCallback({op,
true});
4668 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4670 ps.addCallback({op,
false});
4671 emitLocationInfoAndNewLine(ops);
4675template <
typename OpTy,
typename EmitPrefixFn>
4676LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4677 StringRef formatString,
4678 ValueRange substitutions,
4679 EmitPrefixFn emitPrefix) {
4681 emitError(op,
"SV attributes emission is unimplemented for the op");
4684 SmallPtrSet<Operation *, 8> ops;
4687 ps.addCallback({op,
true});
4689 ps.scopedBox(PP::ibox0, [&]() {
4691 ps.writeQuotedEscaped(formatString);
4698 for (
auto operand : substitutions) {
4699 ps <<
"," << PP::space;
4700 emitExpression(operand, ops);
4704 ps.addCallback({op,
false});
4705 emitLocationInfoAndNewLine(ops);
4709LogicalResult StmtEmitter::visitSV(WriteOp op) {
4710 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4711 op.getSubstitutions(),
4712 [&](SmallPtrSetImpl<Operation *> &) {});
4715LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4716 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4717 op.getSubstitutions(),
4718 [&](SmallPtrSetImpl<Operation *> &ops) {
4719 emitExpression(op.getFd(), ops);
4720 ps <<
"," << PP::space;
4724LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4726 emitError(op,
"SV attributes emission is unimplemented for the op");
4729 SmallPtrSet<Operation *, 8> ops;
4734 StringRef
string = op.getFormatString();
4735 if (
string.ends_with(
"\n"))
4736 string =
string.drop_back();
4741 bool isFirst =
true;
4744 while (!
string.
empty()) {
4745 auto lhsRhs =
string.split(
'\n');
4749 ps << PP::end << PP::newline << PP::neverbox;
4753 emitTextWithSubstitutions(
4754 ps, lhsRhs.first, op,
4755 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4756 string = lhsRhs.second;
4761 emitLocationInfoAndNewLine(ops);
4766LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4768 emitError(op,
"SV attributes emission is unimplemented for the op");
4772 SmallPtrSet<Operation *, 8> ops;
4777 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4778 assert(macroOp &&
"Invalid IR");
4780 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4782 if (!op.getInputs().empty()) {
4784 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4785 emitExpression(val, ops, LowestPrecedence,
4791 emitLocationInfoAndNewLine(ops);
4797StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4798 std::optional<unsigned> verbosity) {
4800 emitError(op,
"SV attributes emission is unimplemented for the op");
4803 SmallPtrSet<Operation *, 8> ops;
4805 ps.addCallback({op,
true});
4807 if (verbosity && *verbosity != 1) {
4809 ps.addAsString(*verbosity);
4813 ps.addCallback({op,
false});
4814 emitLocationInfoAndNewLine(ops);
4818LogicalResult StmtEmitter::visitSV(StopOp op) {
4819 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4822LogicalResult StmtEmitter::visitSV(FinishOp op) {
4823 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4827LogicalResult StmtEmitter::visitSV(ExitOp op) {
4828 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4834StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4835 std::optional<unsigned> verbosity,
4836 StringAttr message, ValueRange operands) {
4838 emitError(op,
"SV attributes emission is unimplemented for the op");
4841 SmallPtrSet<Operation *, 8> ops;
4843 ps.addCallback({op,
true});
4849 if ((verbosity && *verbosity != 1) || message) {
4851 ps.scopedBox(PP::ibox0, [&]() {
4855 ps.addAsString(*verbosity);
4860 ps <<
"," << PP::space;
4861 ps.writeQuotedEscaped(message.getValue());
4863 for (
auto operand : operands) {
4864 ps <<
"," << PP::space;
4865 emitExpression(operand, ops);
4874 ps.addCallback({op,
false});
4875 emitLocationInfoAndNewLine(ops);
4879LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4880 return emitFatalMessageOp(op);
4883LogicalResult StmtEmitter::visitSV(FatalOp op) {
4884 return emitFatalMessageOp(op);
4887LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4888 return emitNonfatalMessageOp(op,
"$error");
4891LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4892 return emitNonfatalMessageOp(op,
"$warning");
4895LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4896 return emitNonfatalMessageOp(op,
"$info");
4899LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4900 return emitNonfatalMessageOp(op,
"$error");
4903LogicalResult StmtEmitter::visitSV(WarningOp op) {
4904 return emitNonfatalMessageOp(op,
"$warning");
4907LogicalResult StmtEmitter::visitSV(InfoOp op) {
4908 return emitNonfatalMessageOp(op,
"$info");
4911LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4912 SmallPtrSet<Operation *, 8> ops({op});
4915 ps.addCallback({op,
true});
4917 switch (op.getBaseAttr().getValue()) {
4918 case MemBaseTypeAttr::MemBaseBin:
4921 case MemBaseTypeAttr::MemBaseHex:
4926 ps.scopedBox(PP::ibox0, [&]() {
4927 ps.writeQuotedEscaped(op.getFilename());
4928 ps <<
"," << PP::space;
4929 emitExpression(op.getDest(), ops);
4933 ps.addCallback({op,
false});
4934 emitLocationInfoAndNewLine(ops);
4938LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4939 emitSVAttributes(op);
4942 ps.addCallback({op,
true});
4943 ps <<
"generate" << PP::newline;
4945 setPendingNewline();
4946 emitStatementBlock(op.getBody().getBlocks().front());
4949 ps <<
"endgenerate";
4950 ps.addCallback({op,
false});
4951 setPendingNewline();
4955LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4956 emitSVAttributes(op);
4959 ps.addCallback({op,
true});
4961 ps.invokeWithStringOS([&](
auto &os) {
4962 emitter.printParamValue(
4963 op.getCond(), os, VerilogPrecedence::Selection,
4964 [&]() { return op->emitOpError(
"invalid case parameter"); });
4967 setPendingNewline();
4970 ArrayAttr
patterns = op.getCasePatterns();
4971 ArrayAttr caseNames = op.getCaseNames();
4972 MutableArrayRef<Region> regions = op.getCaseRegions();
4979 llvm::StringMap<size_t> nextGenIds;
4980 ps.scopedBox(PP::bbox2, [&]() {
4982 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4983 auto ®ion = regions[i];
4984 assert(region.hasOneBlock());
4985 Attribute patternAttr =
patterns[i];
4988 if (!isa<mlir::TypedAttr>(patternAttr))
4991 ps.invokeWithStringOS([&](
auto &os) {
4992 emitter.printParamValue(
4993 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4994 [&]() {
return op->emitOpError(
"invalid case value"); });
4997 StringRef legalName =
4998 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
5001 setPendingNewline();
5002 emitStatementBlock(region.getBlocks().front());
5005 setPendingNewline();
5011 ps.addCallback({op,
false});
5012 setPendingNewline();
5016LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
5017 emitSVAttributes(op);
5018 llvm::SmallPtrSet<Operation *, 8> ops;
5019 ps.addCallback({op,
true});
5022 StringRef inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5025 ps.scopedBox(PP::cbox0, [&]() {
5027 [&]() { ps <<
"genvar" << PP::nbsp <<
PPExtString(inductionVarName); },
5029 ps.invokeWithStringOS([&](
auto &os) {
5030 emitter.printParamValue(
5031 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5032 [&]() { return op->emitOpError(
"invalid lower bound"); });
5041 ps.invokeWithStringOS([&](
auto &os) {
5042 emitter.printParamValue(
5043 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5044 [&]() { return op->emitOpError(
"invalid upper bound"); });
5050 ps <<
PPExtString(inductionVarName) << PP::nbsp <<
"+=" << PP::nbsp;
5051 ps.invokeWithStringOS([&](
auto &os) {
5052 emitter.printParamValue(
5053 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5054 [&]() { return op->emitOpError(
"invalid step"); });
5057 StringRef blockName = op.getGenBlockName();
5058 if (!blockName.empty())
5062 ps << PP::neverbreak;
5063 setPendingNewline();
5064 emitStatementBlock(op.getBody().getBlocks().front());
5067 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5069 ps.addCallback({op,
false});
5070 setPendingNewline();
5074LogicalResult StmtEmitter::visitSV(
ForOp op) {
5075 emitSVAttributes(op);
5076 llvm::SmallPtrSet<Operation *, 8> ops;
5077 ps.addCallback({op,
true});
5079 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5082 ps.scopedBox(PP::cbox0, [&]() {
5086 ps <<
"logic" << PP::nbsp;
5087 ps.invokeWithStringOS([&](
auto &os) {
5088 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5093 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
5098 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5099 [&]() { emitExpression(op.getUpperBound(), ops); },
5105 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5106 [&]() { emitExpression(op.getStep(), ops); },
5110 ps << PP::neverbreak;
5111 setPendingNewline();
5112 emitStatementBlock(op.getBody().getBlocks().front());
5115 ps.addCallback({op,
false});
5116 emitLocationInfoAndNewLine(ops);
5121void StmtEmitter::emitAssertionLabel(Operation *op) {
5122 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5128void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5129 SmallPtrSetImpl<Operation *> &ops,
5130 bool isConcurrent =
false) {
5133 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5134 ps.scopedBox(PP::ibox0, [&]() {
5135 ps.writeQuotedEscaped(message.getValue());
5137 for (
auto arg : args) {
5138 ps <<
"," << PP::space;
5139 emitExpression(arg, ops);
5145template <
typename Op>
5146LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5148 emitError(op,
"SV attributes emission is unimplemented for the op");
5151 SmallPtrSet<Operation *, 8> ops;
5153 ps.addCallback({op,
true});
5154 ps.scopedBox(PP::ibox2, [&]() {
5155 emitAssertionLabel(op);
5156 ps.scopedBox(PP::cbox0, [&]() {
5158 switch (op.getDefer()) {
5159 case DeferAssert::Immediate:
5161 case DeferAssert::Observed:
5164 case DeferAssert::Final:
5169 ps.scopedBox(PP::ibox0, [&]() {
5170 emitExpression(op.getExpression(), ops);
5173 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5177 ps.addCallback({op,
false});
5178 emitLocationInfoAndNewLine(ops);
5182LogicalResult StmtEmitter::visitSV(AssertOp op) {
5183 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5186LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5187 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5190LogicalResult StmtEmitter::visitSV(CoverOp op) {
5191 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5194template <
typename Op>
5195LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5197 emitError(op,
"SV attributes emission is unimplemented for the op");
5200 SmallPtrSet<Operation *, 8> ops;
5202 ps.addCallback({op,
true});
5203 ps.scopedBox(PP::ibox2, [&]() {
5204 emitAssertionLabel(op);
5205 ps.scopedBox(PP::cbox0, [&]() {
5206 ps << opName << PP::nbsp <<
"property (";
5207 ps.scopedBox(PP::ibox0, [&]() {
5208 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5210 emitExpression(op.getClock(), ops);
5211 ps <<
")" << PP::space;
5212 emitExpression(op.getProperty(), ops);
5215 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5220 ps.addCallback({op,
false});
5221 emitLocationInfoAndNewLine(ops);
5225LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5226 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5229LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5230 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5233LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5234 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5239template <
typename Op>
5240LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5242 emitError(op,
"SV attributes emission is unimplemented for the op");
5252 Operation *parent = op->getParentOp();
5253 Value
property = op.getProperty();
5254 bool isTemporal = !
property.getType().isSignlessInteger(1);
5256 bool emitAsImmediate = !isTemporal && isProcedural;
5259 SmallPtrSet<Operation *, 8> ops;
5261 ps.addCallback({op,
true});
5262 ps.scopedBox(PP::ibox2, [&]() {
5264 emitAssertionLabel(op);
5266 ps.scopedBox(PP::cbox0, [&]() {
5267 if (emitAsImmediate)
5268 ps << opName <<
"(";
5270 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5272 Value clock = op.getClock();
5273 auto event = op.getEvent();
5275 ps.scopedBox(PP::ibox2, [&]() {
5276 PropertyEmitter(emitter, ops)
5277 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5280 ps.scopedBox(PP::ibox2, [&]() {
5281 PropertyEmitter(emitter, ops)
5282 .emitAssertPropertyBody(property, op.getDisable());
5287 ps.addCallback({op,
false});
5288 emitLocationInfoAndNewLine(ops);
5292LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5293 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5296LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5297 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5300LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5301 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5304LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5306 emitError(op,
"SV attributes emission is unimplemented for the op");
5309 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5310 .getMacroIdentifier());
5313 bool hasEmptyThen = op->getRegion(0).front().empty();
5315 ps <<
"`ifndef " << ident;
5317 ps <<
"`ifdef " << ident;
5319 SmallPtrSet<Operation *, 8> ops;
5321 emitLocationInfoAndNewLine(ops);
5324 emitStatementBlock(op->getRegion(0).front());
5326 if (!op->getRegion(1).empty()) {
5327 if (!hasEmptyThen) {
5329 ps <<
"`else // " << ident;
5330 setPendingNewline();
5332 emitStatementBlock(op->getRegion(1).front());
5339 setPendingNewline();
5347void StmtEmitter::emitBlockAsStatement(
5348 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5349 StringRef multiLineComment) {
5353 auto needsBeginEnd =
5357 emitLocationInfoAndNewLine(locationOps);
5360 emitStatementBlock(*block);
5362 if (needsBeginEnd) {
5366 if (!multiLineComment.empty())
5367 ps <<
" // " << multiLineComment;
5368 setPendingNewline();
5372LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5374 for (
auto &op : ooop.getBody().front())
5379LogicalResult StmtEmitter::visitSV(IfOp op) {
5380 SmallPtrSet<Operation *, 8> ops;
5382 auto ifcondBox = PP::ibox2;
5384 emitSVAttributes(op);
5386 ps.addCallback({op,
true});
5387 ps <<
"if (" << ifcondBox;
5397 emitExpression(ifOp.getCond(), ops);
5398 ps << PP::end <<
")";
5399 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5401 if (!ifOp.hasElse())
5405 Block *elseBlock = ifOp.getElseBlock();
5407 if (!nestedElseIfOp) {
5412 emitBlockAsStatement(elseBlock, ops);
5418 ifOp = nestedElseIfOp;
5419 ps <<
"else if (" << ifcondBox;
5421 ps.addCallback({op,
false});
5426LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5427 emitSVAttributes(op);
5428 SmallPtrSet<Operation *, 8> ops;
5432 auto printEvent = [&](AlwaysOp::Condition cond) {
5433 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5434 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5436 ps.addCallback({op,
true});
5438 switch (op.getNumConditions()) {
5444 printEvent(op.getCondition(0));
5449 ps.scopedBox(PP::cbox0, [&]() {
5450 printEvent(op.getCondition(0));
5451 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5452 ps << PP::space <<
"or" << PP::space;
5453 printEvent(op.getCondition(i));
5462 std::string comment;
5463 if (op.getNumConditions() == 0) {
5464 comment =
"always @*";
5466 comment =
"always @(";
5469 [&](Attribute eventAttr) {
5470 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5471 comment += stringifyEventControl(event);
5473 [&]() { comment +=
", "; });
5477 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5478 ps.addCallback({op,
false});
5482LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5483 emitSVAttributes(op);
5484 SmallPtrSet<Operation *, 8> ops;
5488 ps.addCallback({op,
true});
5489 StringRef opString =
"always_comb";
5490 if (state.options.noAlwaysComb)
5491 opString =
"always @(*)";
5494 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5495 ps.addCallback({op,
false});
5499LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5500 emitSVAttributes(op);
5502 SmallPtrSet<Operation *, 8> ops;
5506 ps.addCallback({op,
true});
5507 ps <<
"always_ff @(";
5508 ps.scopedBox(PP::cbox0, [&]() {
5509 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5510 emitExpression(op.getClock(), ops);
5511 if (op.getResetStyle() == ResetType::AsyncReset) {
5512 ps << PP::nbsp <<
"or" << PP::space
5513 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5514 emitExpression(op.getReset(), ops);
5521 std::string comment;
5522 comment +=
"always_ff @(";
5523 comment += stringifyEventControl(op.getClockEdge());
5524 if (op.getResetStyle() == ResetType::AsyncReset) {
5526 comment += stringifyEventControl(*op.getResetEdge());
5530 if (op.getResetStyle() == ResetType::NoReset)
5531 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5534 emitLocationInfoAndNewLine(ops);
5535 ps.scopedBox(PP::bbox2, [&]() {
5541 if (op.getResetStyle() == ResetType::AsyncReset &&
5542 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5544 emitExpression(op.getReset(), ops);
5546 emitBlockAsStatement(op.getResetBlock(), ops);
5549 emitBlockAsStatement(op.getBodyBlock(), ops);
5554 ps <<
" // " << comment;
5555 setPendingNewline();
5557 ps.addCallback({op,
false});
5561LogicalResult StmtEmitter::visitSV(InitialOp op) {
5562 emitSVAttributes(op);
5563 SmallPtrSet<Operation *, 8> ops;
5566 ps.addCallback({op,
true});
5568 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5569 ps.addCallback({op,
false});
5573LogicalResult StmtEmitter::visitSV(CaseOp op) {
5574 emitSVAttributes(op);
5575 SmallPtrSet<Operation *, 8> ops, emptyOps;
5578 ps.addCallback({op,
true});
5579 if (op.getValidationQualifier() !=
5580 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5581 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5582 op.getValidationQualifier()))
5584 const char *opname =
nullptr;
5585 switch (op.getCaseStyle()) {
5586 case CaseStmtType::CaseStmt:
5589 case CaseStmtType::CaseXStmt:
5592 case CaseStmtType::CaseZStmt:
5596 ps << opname <<
" (";
5597 ps.scopedBox(PP::ibox0, [&]() {
5598 emitExpression(op.getCond(), ops);
5601 emitLocationInfoAndNewLine(ops);
5603 size_t caseValueIndex = 0;
5604 ps.scopedBox(PP::bbox2, [&]() {
5605 for (
auto &caseInfo : op.getCases()) {
5607 auto &
pattern = caseInfo.pattern;
5609 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5610 .Case<CaseBitPattern>([&](
auto bitPattern) {
5613 ps.invokeWithStringOS([&](
auto &os) {
5614 os << bitPattern->getWidth() <<
"'b";
5615 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5616 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5619 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5620 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5621 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5623 .Case<CaseExprPattern>([&](
auto) {
5624 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5626 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5627 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5630 emitBlockAsStatement(caseInfo.block, emptyOps);
5636 ps.addCallback({op,
false});
5637 emitLocationInfoAndNewLine(ops);
5641LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5642 bool doNotPrint = op.getDoNotPrint();
5643 if (doNotPrint && !state.options.emitBindComments)
5648 emitSVAttributes(op);
5650 ps.addCallback({op,
true});
5653 <<
"/* This instance is elsewhere emitted as a bind statement."
5656 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5657 "attributes. The attributes will not be emitted.";
5660 SmallPtrSet<Operation *, 8> ops;
5665 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5666 assert(moduleOp &&
"Invalid IR");
5670 if (!op.getParameters().empty()) {
5673 bool printed =
false;
5675 llvm::zip(op.getParameters(),
5676 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5677 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5678 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5680 if (param.getValue() == modParam.getValue())
5685 ps <<
" #(" << PP::bbox2 << PP::newline;
5688 ps <<
"," << PP::newline;
5692 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5694 ps.invokeWithStringOS([&](
auto &os) {
5695 emitter.printParamValue(param.getValue(), os, [&]() {
5696 return op->emitOpError(
"invalid instance parameter '")
5697 << param.getName().getValue() <<
"' value";
5703 ps << PP::end << PP::newline <<
")";
5710 SmallVector<Value> instPortValues(modPortInfo.size());
5711 op.getValues(instPortValues, modPortInfo);
5712 emitInstancePortList(op, modPortInfo, instPortValues);
5714 ps.addCallback({op,
false});
5715 emitLocationInfoAndNewLine(ops);
5720 setPendingNewline();
5725void StmtEmitter::emitInstancePortList(Operation *op,
5727 ArrayRef<Value> instPortValues) {
5728 SmallPtrSet<Operation *, 8> ops;
5731 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5732 ModulePortInfo containingPortList(containingModule.getPortList());
5737 size_t maxNameLength = 0;
5738 for (
auto &elt : modPortInfo) {
5739 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5742 auto getWireForValue = [&](Value result) {
5743 return result.getUsers().begin()->getOperand(0);
5747 bool isFirst =
true;
5748 bool isZeroWidth =
false;
5750 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5752 auto &modPort = modPortInfo.
at(portNum);
5754 Value portVal = instPortValues[portNum];
5759 bool shouldPrintComma =
true;
5761 shouldPrintComma =
false;
5762 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5764 shouldPrintComma =
true;
5769 if (shouldPrintComma)
5772 emitLocationInfoAndNewLine(ops);
5787 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5788 auto modPortName = modPort.getVerilogName();
5790 ps.spaces(maxNameLength - modPortName.size() + 1);
5792 ps.scopedBox(PP::ibox0, [&]() {
5799 if (!modPort.isOutput()) {
5801 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5802 ps <<
"/* Zero width */";
5804 emitExpression(portVal, ops, LowestPrecedence);
5805 }
else if (portVal.use_empty()) {
5806 ps <<
"/* unused */";
5807 }
else if (portVal.hasOneUse() &&
5808 (output = dyn_cast_or_null<OutputOp>(
5809 portVal.getUses().begin()->getOwner()))) {
5814 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5816 containingPortList.atOutput(outputPortNo).getVerilogName());
5818 portVal = getWireForValue(portVal);
5819 emitExpression(portVal, ops);
5825 if (!isFirst || isZeroWidth) {
5826 emitLocationInfoAndNewLine(ops);
5839LogicalResult StmtEmitter::visitSV(BindOp op) {
5840 emitter.emitBind(op);
5841 assert(state.pendingNewline);
5845LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5846 emitComment(op.getCommentAttr());
5848 emitSVAttributes(op);
5851 ps.addCallback({op,
true});
5853 setPendingNewline();
5855 emitStatementBlock(*op.getBodyBlock());
5857 ps <<
"endinterface" << PP::newline;
5858 ps.addCallback({op,
false});
5859 setPendingNewline();
5864 emitSVAttributes(op);
5866 ps.addCallback({op,
true});
5868 ps << op.getContent();
5870 ps.addCallback({op,
false});
5871 setPendingNewline();
5875LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5877 emitSVAttributes(op);
5879 ps.addCallback({op,
true});
5881 ps << PP::neverbox <<
"// ";
5882 ps.invokeWithStringOS([&](
auto &os) {
5887 ps.invokeWithStringOS(
5888 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5892 ps.addCallback({op,
false});
5893 setPendingNewline();
5897LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5899 ps.addCallback({op,
true});
5903 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5904 auto port = cast<ModportStructAttr>(portAttr);
5905 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5906 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5907 ps << PPExtString(getSymOpName(signalDecl));
5911 ps.addCallback({op,
false});
5912 setPendingNewline();
5916LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5918 ps.addCallback({op,
true});
5919 SmallPtrSet<Operation *, 8> emitted;
5922 emitExpression(op.getIface(), emitted);
5923 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5924 emitExpression(op.getRhs(), emitted);
5926 ps.addCallback({op,
false});
5927 setPendingNewline();
5931LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5933 ps <<
"`" << op.getMacroIdentifier();
5934 setPendingNewline();
5938LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5939 auto decl = op.getReferencedMacro(&state.symbolCache);
5942 ps.addCallback({op,
true});
5944 if (decl.getArgs()) {
5946 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5947 ps << cast<StringAttr>(name);
5951 if (!op.getFormatString().empty()) {
5953 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5956 ps.addCallback({op,
false});
5957 setPendingNewline();
5961void StmtEmitter::emitStatement(Operation *op) {
5968 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5972 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5973 succeeded(dispatchVerifVisitor(op)))
5976 emitOpError(op,
"emission to Verilog not supported");
5977 emitPendingNewlineIfNeeded();
5978 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5979 setPendingNewline();
5990 StmtEmitter &stmtEmitter) {
5997 if (isa<IfDefProceduralOp>(op->getParentOp()))
6005 SmallVector<Value, 8> exprsToScan(op->getOperands());
6010 while (!exprsToScan.empty()) {
6011 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
6018 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
6019 auto *defOp = readInout.getOperand().getDefiningOp();
6026 if (isa<sv::WireOp>(defOp))
6031 if (!isa<RegOp, LogicOp>(defOp))
6037 if (isa<LogicOp>(defOp) &&
6038 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6042 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6043 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6051 exprsToScan.append(expr->getOperands().begin(),
6052 expr->getOperands().end());
6058 if (expr->getBlock() != op->getBlock())
6063 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6070template <
class AssignTy>
6072 AssignTy singleAssign;
6073 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6074 if (hasSVAttributes(user))
6077 if (auto assign = dyn_cast<AssignTy>(user)) {
6080 singleAssign = assign;
6084 return isa<ReadInOutOp>(user);
6086 return singleAssign;
6092 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6096 if (op1->getBlock() != user->getBlock())
6102 return op1->isBeforeInBlock(user);
6106LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6107 emitSVAttributes(op);
6108 auto value = op->getResult(0);
6109 SmallPtrSet<Operation *, 8> opsForLocation;
6110 opsForLocation.insert(op);
6112 ps.addCallback({op,
true});
6115 auto type = value.getType();
6121 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6123 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6124 unsigned targetColumn = 0;
6125 unsigned column = 0;
6128 if (maxDeclNameWidth > 0)
6129 targetColumn += maxDeclNameWidth + 1;
6132 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6133 }
else if (!word.empty()) {
6135 column += word.size();
6136 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6137 ps.spaces(numSpaces);
6138 column += numSpaces;
6141 SmallString<8> typeString;
6144 llvm::raw_svector_ostream stringStream(typeString);
6147 true, singleBitDefaultType);
6150 if (maxTypeWidth > 0)
6151 targetColumn += maxTypeWidth + 1;
6152 unsigned numSpaces = 0;
6153 if (!typeString.empty()) {
6155 column += typeString.size();
6158 if (targetColumn > column)
6159 numSpaces = targetColumn - column;
6160 ps.spaces(numSpaces);
6161 column += numSpaces;
6167 ps.invokeWithStringOS(
6168 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6171 if (state.options.printDebugInfo) {
6172 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6173 auto innerSym = innerSymOp.getInnerSymAttr();
6174 if (innerSym && !innerSym.empty()) {
6176 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6182 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6183 ps << PP::space <<
"=" << PP::space;
6184 ps.invokeWithStringOS([&](
auto &os) {
6185 emitter.printParamValue(localparam.getValue(), os, [&]() {
6186 return op->emitOpError(
"invalid localparam value");
6191 if (
auto regOp = dyn_cast<RegOp>(op)) {
6192 if (
auto initValue = regOp.getInit()) {
6193 ps << PP::space <<
"=" << PP::space;
6194 ps.scopedBox(PP::ibox0, [&]() {
6195 emitExpression(initValue, opsForLocation, LowestPrecedence,
6204 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6208 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6209 auto *source = singleAssign.getSrc().getDefiningOp();
6213 if (!source || isa<ConstantOp>(source) ||
6214 op->getNextNode() == singleAssign) {
6215 ps << PP::space <<
"=" << PP::space;
6216 ps.scopedBox(PP::ibox0, [&]() {
6217 emitExpression(singleAssign.getSrc(), opsForLocation,
6221 emitter.assignsInlined.insert(singleAssign);
6229 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6233 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6236 auto *source = singleAssign.getSrc().getDefiningOp();
6240 if (!source || isa<ConstantOp>(source) ||
6243 ps << PP::space <<
"=" << PP::space;
6244 ps.scopedBox(PP::ibox0, [&]() {
6245 emitExpression(singleAssign.getSrc(), opsForLocation,
6250 emitter.assignsInlined.insert(singleAssign);
6251 emitter.expressionsEmittedIntoDecl.insert(op);
6258 ps.addCallback({op,
false});
6259 emitLocationInfoAndNewLine(opsForLocation);
6263void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6266 NameCollector collector(emitter);
6267 collector.collectNames(block);
6270 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6271 maxTypeWidth = collector.getMaxTypeWidth();
6274void StmtEmitter::emitStatementBlock(Block &body) {
6275 ps.scopedBox(PP::bbox2, [&]() {
6280 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6281 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6286 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6287 collectNamesAndCalculateDeclarationWidths(body);
6290 for (
auto &op : body) {
6297void ModuleEmitter::emitStatement(Operation *op) {
6298 StmtEmitter(*
this, state.options).emitStatement(op);
6303void ModuleEmitter::emitSVAttributes(Operation *op) {
6311 setPendingNewline();
6318void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6319 auto verilogName =
module.getVerilogModuleNameAttr();
6321 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6323 setPendingNewline();
6332void ModuleEmitter::emitBind(BindOp op) {
6334 emitError(op,
"SV attributes emission is unimplemented for the op");
6335 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6341 Operation *childMod =
6342 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6346 ps.addCallback({op,
true});
6347 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6348 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6350 bool isFirst =
true;
6351 ps.scopedBox(PP::bbox2, [&]() {
6352 auto parentPortInfo = parentMod.getPortList();
6356 size_t maxNameLength = 0;
6357 for (
auto &elt : childPortInfo) {
6358 auto portName = elt.getVerilogName();
6359 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6360 maxNameLength = std::max(maxNameLength, elt.getName().size());
6363 SmallVector<Value> instPortValues(childPortInfo.size());
6364 inst.getValues(instPortValues, childPortInfo);
6366 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6368 Value portVal = instPortValues[idx];
6374 bool shouldPrintComma =
true;
6376 shouldPrintComma =
false;
6377 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6379 shouldPrintComma =
true;
6384 if (shouldPrintComma)
6397 ps << PP::neverbox <<
"//";
6401 ps.nbsp(maxNameLength - elt.getName().size());
6403 llvm::SmallPtrSet<Operation *, 4> ops;
6404 if (elt.isOutput()) {
6405 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6406 "output port must have either single or no use");
6407 if (portVal.use_empty()) {
6408 ps <<
"/* unused */";
6409 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6410 portVal.getUses().begin()->getOwner())) {
6413 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6415 parentPortList.atOutput(outputPortNo).getVerilogName());
6417 portVal = portVal.getUsers().begin()->getOperand(0);
6418 ExprEmitter(*
this, ops)
6419 .emitExpression(portVal, LowestPrecedence,
6423 ExprEmitter(*
this, ops)
6424 .emitExpression(portVal, LowestPrecedence,
6437 ps.addCallback({op,
false});
6438 setPendingNewline();
6441void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6443 emitError(op,
"SV attributes emission is unimplemented for the op");
6445 auto instance = op.getReferencedInstance(&state.symbolCache);
6447 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6448 instance.getInterfaceType().getInterface());
6450 ps.addCallback({op,
true});
6451 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6452 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6454 ps.addCallback({op,
false});
6455 setPendingNewline();
6458void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6462 auto printParamType = [&](Type type, Attribute defaultValue,
6463 SmallString<8> &result) {
6465 llvm::raw_svector_ostream sstream(result);
6470 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6471 if (intAttr.getValue().getBitWidth() == 32)
6473 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6474 if (fpAttr.getType().isF64())
6477 if (isa<NoneType>(type))
6484 if (
auto intType = type_dyn_cast<IntegerType>(type))
6485 if (intType.getWidth() == 32) {
6486 sstream <<
"/*integer*/";
6490 printPackedType(type, sstream, module->getLoc(),
6498 size_t maxTypeWidth = 0;
6499 SmallString<8> scratch;
6500 for (
auto param : params) {
6501 auto paramAttr = cast<ParamDeclAttr>(param);
6503 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6504 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6507 if (maxTypeWidth > 0)
6510 ps.scopedBox(PP::bbox2, [&]() {
6511 ps << PP::newline <<
"#(";
6512 ps.scopedBox(PP::cbox0, [&]() {
6515 [&](Attribute param) {
6516 auto paramAttr = cast<ParamDeclAttr>(param);
6517 auto defaultValue = paramAttr.getValue();
6519 printParamType(paramAttr.getType(), defaultValue, scratch);
6520 if (!scratch.empty())
6522 if (scratch.size() < maxTypeWidth)
6523 ps.nbsp(maxTypeWidth - scratch.size());
6525 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6526 module, paramAttr.getName()));
6530 ps.invokeWithStringOS([&](
auto &os) {
6532 return module->emitError("parameter '")
6533 << paramAttr.getName().getValue()
6534 << "' has invalid value";
6539 [&]() { ps <<
"," << PP::newline; });
6545void ModuleEmitter::emitPortList(Operation *module,
6547 bool emitAsTwoStateType) {
6549 if (portInfo.
size())
6550 emitLocationInfo(module->getLoc());
6554 bool hasOutputs =
false, hasZeroWidth =
false;
6555 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6556 SmallVector<SmallString<8>, 16> portTypeStrings;
6558 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6559 auto port = portInfo.
at(i);
6563 lastNonZeroPort = i;
6566 portTypeStrings.push_back({});
6568 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6570 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6573 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6576 if (maxTypeWidth > 0)
6580 ps.scopedBox(PP::bbox2, [&]() {
6581 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6582 auto lastPort = e - 1;
6585 auto portType = portInfo.
at(portIdx).
type;
6589 bool isZeroWidth =
false;
6594 ps << (isZeroWidth ?
"// " :
" ");
6598 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6599 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6600 (state.options.emitWireInPorts ? 5 : 0) +
6605 if (!isa<ModportType>(portType)) {
6606 switch (thisPortDirection) {
6607 case ModulePort::Direction::Output:
6610 case ModulePort::Direction::Input:
6611 ps << (hasOutputs ?
"input " :
"input ");
6613 case ModulePort::Direction::InOut:
6614 ps << (hasOutputs ?
"inout " :
"inout ");
6617 if (state.options.emitWireInPorts)
6619 if (!portTypeStrings[portIdx].
empty())
6620 ps << portTypeStrings[portIdx];
6621 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6622 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6624 ps << portTypeStrings[portIdx];
6625 if (portTypeStrings[portIdx].size() < startOfNamePos)
6626 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6633 ps.invokeWithStringOS(
6634 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6637 auto innerSym = portInfo.
at(portIdx).
getSym();
6638 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6640 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6645 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6649 if (
auto loc = portInfo.
at(portIdx).
loc)
6650 emitLocationInfo(loc);
6660 if (!state.options.disallowPortDeclSharing) {
6661 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6664 auto port = portInfo.
at(portIdx);
6668 bool isZeroWidth =
false;
6673 ps << (isZeroWidth ?
"// " :
" ");
6676 ps.nbsp(startOfNamePos);
6679 StringRef name = port.getVerilogName();
6683 ps.invokeWithStringOS(
6684 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6687 auto sym = port.getSym();
6688 if (state.options.printDebugInfo && sym && !sym.empty())
6689 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6693 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6697 if (
auto loc = port.loc)
6698 emitLocationInfo(loc);
6709 if (!portInfo.
size()) {
6711 SmallPtrSet<Operation *, 8> moduleOpSet;
6712 moduleOpSet.insert(module);
6713 emitLocationInfoAndNewLine(moduleOpSet);
6716 ps <<
");" << PP::newline;
6717 setPendingNewline();
6721void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6722 currentModuleOp =
module;
6724 emitComment(module.getCommentAttr());
6725 emitSVAttributes(module);
6727 ps.addCallback({module,
true});
6731 emitParameters(module, module.getParameters());
6735 assert(state.pendingNewline);
6738 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6741 ps.addCallback({module,
false});
6743 setPendingNewline();
6745 currentModuleOp =
nullptr;
6748void ModuleEmitter::emitFunc(FuncOp func) {
6750 if (func.isDeclaration())
6753 currentModuleOp = func;
6755 ps.addCallback({func,
true});
6759 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6761 ps <<
"endfunction";
6763 currentModuleOp =
nullptr;
6772 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6779 void emit(emit::FileListOp op);
6782 void emit(Block *block);
6784 void emitOp(emit::RefOp op);
6785 void emitOp(emit::VerbatimOp op);
6789 for (Operation &op : *block) {
6790 TypeSwitch<Operation *>(&op)
6791 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6792 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6793 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6794 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6795 .Case<BindInterfaceOp>(
6796 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6797 .Case<TypeScopeOp>([&](
auto typedecls) {
6798 ModuleEmitter(state).emitStatement(typedecls);
6801 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6807 for (
auto sym : op.getFiles()) {
6808 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6810 auto it = state.fileMapping.find(fileName);
6811 if (it == state.fileMapping.end()) {
6812 emitOpError(op,
" references an invalid file: ") << sym;
6816 auto file = cast<emit::FileOp>(it->second);
6817 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6824 StringAttr target = op.getTargetAttr().getAttr();
6825 auto *targetOp = state.symbolCache.getDefinition(target);
6826 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6828 TypeSwitch<Operation *>(targetOp)
6829 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6830 .Case<hw::HWModuleOp>(
6831 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6832 .Case<TypeScopeOp>([&](
auto typedecls) {
6833 ModuleEmitter(state).emitStatement(typedecls);
6836 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6842 SmallPtrSet<Operation *, 8> ops;
6847 StringRef text = op.getText();
6851 const auto &[lhs, rhs] = text.split(
'\n');
6855 ps << PP::end << PP::newline << PP::neverbox;
6857 }
while (!text.empty());
6860 emitLocationInfoAndNewLine(ops);
6878 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6879 moduleOp->walk([&](Operation *op) {
6881 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6884 SymbolTable::getSymbolAttrName()),
6885 name.getSymName(), op);
6886 if (isa<BindOp>(op))
6892 auto collectPorts = [&](
auto moduleOp) {
6893 auto portInfo = moduleOp.getPortList();
6894 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6895 if (!p.attrs || p.attrs.empty())
6897 for (NamedAttribute portAttr : p.attrs) {
6898 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6907 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6908 for (
auto file :
designOp.getOps<emit::FileOp>())
6909 for (
auto refs : file.getOps<emit::RefOp>())
6910 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6912 SmallString<32> outputPath;
6913 for (
auto &op : *
designOp.getBody()) {
6916 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6918 bool hasFileName =
false;
6919 bool emitReplicatedOps = !isFileOp;
6920 bool addToFilelist = !isFileOp;
6926 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6928 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6929 <<
" on " << op <<
"\n";);
6930 if (!attr.isDirectory())
6933 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6934 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6937 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6942 if (!defaultFileName.isTriviallyEmpty()) {
6943 llvm::sys::path::append(outputPath, defaultFileName);
6945 op->emitError(
"file name unspecified");
6947 llvm::sys::path::append(outputPath,
"error.out");
6951 auto destFile = StringAttr::get(op->getContext(), outputPath);
6952 auto &file =
files[destFile];
6953 file.ops.push_back(info);
6954 file.emitReplicatedOps = emitReplicatedOps;
6955 file.addToFilelist = addToFilelist;
6956 file.isVerilog = outputPath.ends_with(
".sv");
6961 if (!attr || attr.isDirectory()) {
6962 auto excludeFromFileListAttr =
6963 BoolAttr::get(op->getContext(), !addToFilelist);
6964 auto includeReplicatedOpsAttr =
6965 BoolAttr::get(op->getContext(), emitReplicatedOps);
6966 auto outputFileAttr = hw::OutputFileAttr::get(
6967 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6968 op->setAttr(
"output_file", outputFileAttr);
6974 TypeSwitch<Operation *>(&op)
6975 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6977 fileMapping.try_emplace(file.getSymNameAttr(), file);
6978 separateFile(file, file.getFileName());
6980 .Case<emit::FragmentOp>([&](
auto fragment) {
6983 .Case<HWModuleOp>([&](
auto mod) {
6985 auto sym = mod.getNameAttr();
6988 collectInstanceSymbolsAndBinds(mod);
6990 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6991 if (it->second.size() != 1 || attr) {
6994 op.emitError(
"modules can be emitted to a single file");
7002 if (attr || separateModules)
7008 .Case<InterfaceOp>([&](InterfaceOp intf) {
7013 for (
auto &op : *intf.getBodyBlock())
7014 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
7015 if (
auto name = symOp.getNameAttr())
7019 if (attr || separateModules)
7020 separateFile(intf, intf.getSymName() +
".sv");
7026 separateFile(op, op.getOutputFile().getFilename().getValue());
7028 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7034 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7035 [&](Operation *op) {
7041 separateFile(op,
"");
7043 .Case<FuncOp>([&](
auto op) {
7049 separateFile(op,
"");
7053 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7056 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7065 separateFile(op,
"");
7067 .Case<BindOp>([&](
auto op) {
7069 separateFile(op,
"bindfile.sv");
7074 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
7075 .Case<MacroDeclOp>([&](
auto op) {
7078 .Case<sv::ReserveNamesOp>([](
auto op) {
7081 .Case<om::ClassLike>([&](
auto op) {
7084 .Case<om::ConstantOp>([&](
auto op) {
7087 .Default([&](
auto *) {
7088 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7108 size_t lastReplicatedOp = 0;
7110 bool emitHeaderInclude =
7113 if (emitHeaderInclude)
7116 size_t numReplicatedOps =
7121 DenseSet<emit::FragmentOp> includedFragments;
7122 for (
const auto &opInfo : file.
ops) {
7123 Operation *op = opInfo.op;
7127 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7133 if (
auto fragments =
7135 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7139 op->emitError(
"cannot find referenced fragment ") << sym;
7142 emit::FragmentOp fragment = it->second;
7143 if (includedFragments.insert(fragment).second) {
7144 thingsToEmit.emplace_back(it->second);
7150 thingsToEmit.emplace_back(op);
7155 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7160 TypeSwitch<Operation *>(op)
7161 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7162 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7165 .Case<HWModuleGeneratedOp>(
7166 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7167 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7168 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7169 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7170 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7171 .Case<TypeScopeOp>([&](
auto typedecls) {
7172 ModuleEmitter(state).emitStatement(typedecls);
7174 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7176 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7177 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7178 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7179 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7180 .Default([&](
auto *op) {
7181 state.encounteredError =
true;
7182 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7189 llvm::formatted_raw_ostream &os,
7190 StringAttr fileName,
bool parallelize) {
7195 parallelize &=
context->isMultithreadingEnabled();
7206 size_t lineOffset = 0;
7207 for (
auto &entry : thingsToEmit) {
7208 entry.verilogLocs.setStream(os);
7209 if (
auto *op = entry.getOperation()) {
7214 state.addVerilogLocToOps(lineOffset, fileName);
7216 os << entry.getStringData();
7221 if (state.encounteredError)
7239 SmallString<256> buffer;
7240 llvm::raw_svector_ostream tmpStream(buffer);
7241 llvm::formatted_raw_ostream rs(tmpStream);
7249 if (state.encounteredError)
7254 for (
auto &entry : thingsToEmit) {
7257 auto *op = entry.getOperation();
7259 auto lineOffset = os.getLine() + 1;
7260 os << entry.getStringData();
7264 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7267 entry.verilogLocs.setStream(os);
7274 state.addVerilogLocToOps(0, fileName);
7275 if (state.encounteredError) {
7294 module.emitWarning()
7295 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7296 "created only at SplitExportVerilog";
7305 for (
const auto &it : emitter.
files) {
7306 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7307 "\" ----- 8< -----\n\n");
7313 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7314 "\" ----- 8< -----\n\n");
7315 for (
auto &name : it.second)
7316 contents += name.str() +
"\n";
7317 list.emplace_back(contents);
7320 llvm::formatted_raw_ostream rs(os);
7324 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7331 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7333 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7334 if (failed(failableParallelForEach(
7335 module->getContext(), modulesToPrepare,
7336 [&](
auto op) { return prepareHWModule(op, options); })))
7343struct ExportVerilogPass
7344 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7345 ExportVerilogPass(raw_ostream &os) : os(os) {}
7346 void runOnOperation()
override {
7348 mlir::OpPassManager preparePM(
"builtin.module");
7349 preparePM.addPass(createLegalizeAnonEnums());
7350 auto &modulePM = preparePM.nestAny();
7351 modulePM.addPass(createPrepareForEmission());
7352 if (failed(runPipeline(preparePM, getOperation())))
7353 return signalPassFailure();
7356 return signalPassFailure();
7363struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7364 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7365 : ExportVerilogPass{*os} {
7366 owned = std::move(os);
7370 std::unique_ptr<llvm::raw_ostream> owned;
7374std::unique_ptr<mlir::Pass>
7376 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7379std::unique_ptr<mlir::Pass>
7381 return std::make_unique<ExportVerilogPass>(os);
7392static std::unique_ptr<llvm::ToolOutputFile>
7396 SmallString<128> outputFilename(dirname);
7398 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7401 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7403 emitter.
designOp.emitError(
"cannot create output directory \"")
7404 << outputDir <<
"\": " << error.message();
7410 std::string errorMessage;
7411 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7413 emitter.
designOp.emitError(errorMessage);
7430 llvm::formatted_raw_ostream rs(output->os());
7436 StringAttr::get(fileName.getContext(), output->getFilename()),
7442 StringRef dirname) {
7453 bool insertSuccess =
7455 .insert({StringAttr::get(module.getContext(),
circtHeader),
7461 if (!insertSuccess) {
7462 module.emitError() << "tried to emit a heder to " << circtHeader
7463 << ", but the file is used as an output too.";
7469 parallelForEach(module->getContext(), emitter.
files.begin(),
7470 emitter.
files.end(), [&](
auto &it) {
7471 createSplitOutputFile(it.first, it.second, dirname,
7476 SmallString<128> filelistPath(dirname);
7477 llvm::sys::path::append(filelistPath,
"filelist.f");
7479 std::string errorMessage;
7480 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7482 module->emitError(errorMessage);
7486 for (
const auto &it : emitter.
files) {
7487 if (it.second.addToFilelist)
7488 output->os() << it.first.str() <<
"\n";
7497 for (
auto &name : it.second)
7498 output->os() << name.str() <<
"\n";
7507 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7509 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7510 if (failed(failableParallelForEach(
7511 module->getContext(), modulesToPrepare,
7512 [&](
auto op) { return prepareHWModule(op, options); })))
7520struct ExportSplitVerilogPass
7521 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7522 ExportSplitVerilogPass(StringRef directory) {
7523 directoryName = directory.str();
7525 void runOnOperation()
override {
7527 mlir::OpPassManager preparePM(
"builtin.module");
7530 modulePM.addPass(createPrepareForEmission());
7531 if (failed(runPipeline(preparePM, getOperation())))
7532 return signalPassFailure();
7535 return signalPassFailure();
7540std::unique_ptr<mlir::Pass>
7542 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.