42#include "mlir/IR/BuiltinOps.h"
43#include "mlir/IR/ImplicitLocOpBuilder.h"
44#include "mlir/IR/Location.h"
45#include "mlir/IR/Threading.h"
46#include "mlir/Interfaces/FunctionImplementation.h"
47#include "mlir/Pass/PassManager.h"
48#include "mlir/Support/FileUtilities.h"
49#include "llvm/ADT/MapVector.h"
50#include "llvm/ADT/STLExtras.h"
51#include "llvm/ADT/StringSet.h"
52#include "llvm/ADT/TypeSwitch.h"
53#include "llvm/Support/FileSystem.h"
54#include "llvm/Support/FormattedStream.h"
55#include "llvm/Support/Path.h"
56#include "llvm/Support/SaveAndRestore.h"
57#include "llvm/Support/ToolOutputFile.h"
58#include "llvm/Support/raw_ostream.h"
61#define GEN_PASS_DEF_EXPORTSPLITVERILOG
62#define GEN_PASS_DEF_EXPORTVERILOG
63#include "circt/Conversion/Passes.h.inc"
70using namespace ExportVerilog;
72using namespace pretty;
74#define DEBUG_TYPE "export-verilog"
82enum VerilogPrecedence {
103enum SubExprSignResult { IsSigned, IsUnsigned };
109 VerilogPrecedence precedence;
112 SubExprSignResult signedness;
114 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
115 : precedence(precedence), signedness(signedness) {}
125 return Builder(ctx).getI32IntegerAttr(value);
128static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
129 return Builder(ctx).getIntegerAttr(t, value);
145 if (isa<VerbatimExprOp>(op)) {
146 if (op->getNumOperands() == 0 &&
147 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
152 if (isa<XMRRefOp>(op))
156 if (isa<MacroRefExprOp>(op))
166 if (op->getNumOperands() == 0)
170 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
174 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
175 auto *indexOp = array.getIndex().getDefiningOp();
176 if (!indexOp || isa<ConstantOp>(indexOp))
178 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
179 auto *readSrc = read.getInput().getDefiningOp();
181 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
196 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
197 return attr.getValue();
198 return TypeSwitch<Operation *, StringRef>(symOp)
203 return op.getVerilogNameAttr().getValue();
205 .Case<InterfaceOp>([&](InterfaceOp op) {
208 .Case<InterfaceSignalOp>(
209 [&](InterfaceSignalOp op) {
return op.getSymName(); })
210 .Case<InterfaceModportOp>(
211 [&](InterfaceModportOp op) {
return op.getSymName(); })
212 .Default([&](Operation *op) {
213 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
214 return attr.getValue();
215 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
216 return attr.getValue();
217 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
218 return attr.getValue();
220 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
221 return attr.getValue();
222 return StringRef(
"");
227template <
typename PPS>
229 os <<
"/*Zero width*/ 1\'b0";
234 auto hml = cast<HWModuleLike>(module);
235 return hml.getPort(portArgNum).getVerilogName();
240 auto hml = cast<HWModuleLike>(module);
241 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
242 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
243 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
244 return updatedName.getValue();
245 return hml.getHWModuleType().getPortName(pId);
254 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
255 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
256 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
257 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
258 UnpackedOpenArrayCastOp>(op))
262 if (isa<verif::ContractOp>(op))
273 SmallVectorImpl<Attribute> &dims, Type type, Location loc,
274 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
275 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
276 if (integer.getWidth() != 1)
277 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
280 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
281 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
282 getTypeDims(dims, array.getElementType(), loc, errorHandler);
286 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
287 dims.push_back(intType.getWidth());
291 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
292 return getTypeDims(dims, inout.getElementType(), loc, errorHandler);
293 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
294 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
295 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
296 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
297 if (hw::type_isa<InterfaceType, StructType, EnumType, UnionType>(type))
300 errorHandler(loc) <<
"value has an unsupported verilog type " << type;
306 Type a, Type b, Location loc,
307 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
308 SmallVector<Attribute, 4> aDims;
311 SmallVector<Attribute, 4> bDims;
314 return aDims == bDims;
320 if (
auto intType = dyn_cast<IntegerType>(type))
321 return intType.getWidth() == 0;
322 if (
auto inout = dyn_cast<hw::InOutType>(type))
324 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
325 return uarray.getNumElements() == 0 ||
327 if (
auto array = dyn_cast<hw::ArrayType>(type))
328 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
329 if (
auto structType = dyn_cast<hw::StructType>(type))
330 return llvm::all_of(structType.getElements(),
331 [](
auto elem) { return isZeroBitType(elem.type); });
332 if (
auto enumType = dyn_cast<hw::EnumType>(type))
333 return enumType.getFields().empty();
334 if (
auto unionType = dyn_cast<hw::UnionType>(type))
335 return hw::getBitWidth(unionType) == 0;
347 return TypeSwitch<Type, Type>(type)
348 .Case<InOutType>([](InOutType inoutType) {
351 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
354 .Default([](Type type) {
return type; });
359 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
360 auto elementType = cast<hw::InOutType>(type).getElementType();
366 return TypeSwitch<Type, bool>(type)
367 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
370 .Case<StructType>([](
auto) {
return true; })
371 .Default([](
auto) {
return false; });
385 if (
auto name = lhs.getName().compare(rhs.getName()))
387 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
392 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
394 if (lhs.getLine() != rhs.getLine())
395 return lhs.getLine() < rhs.getLine() ? -1 : 1;
396 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
401 Location lhsCallee = lhs.getCallee();
402 Location rhsCallee = rhs.getCallee();
406 Location lhsCaller = lhs.getCaller();
407 Location rhsCaller = rhs.getCaller();
411template <
typename TTargetLoc>
413 auto lhsT = dyn_cast<TTargetLoc>(lhs);
414 auto rhsT = dyn_cast<TTargetLoc>(rhs);
441 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
446 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
451 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
468 SmallPtrSetImpl<Attribute> &locationSet) {
469 llvm::TypeSwitch<Location, void>(loc)
470 .Case<FusedLoc>([&](
auto fusedLoc) {
471 for (
auto subLoc : fusedLoc.getLocations())
474 .Default([&](
auto loc) { locationSet.insert(loc); });
478template <
typename TVector>
480 llvm::array_pod_sort(
481 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
482 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
490 SmallPtrSet<Attribute, 8> locationSet;
491 locationSet.insert(loc);
492 llvm::raw_string_ostream os(
output);
498 const SmallPtrSetImpl<Operation *> &ops) {
502 SmallPtrSet<Attribute, 8> locationSet;
505 llvm::raw_string_ostream os(
output);
514 const SmallPtrSetImpl<Attribute> &locationSet) {
515 if (style == LoweringOptions::LocationInfoStyle::None)
518 llvm::raw_string_ostream sstr(resstr);
520 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
524 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
525 "other styles must be already handled");
526 os <<
"@[" << resstr <<
"]";
535 const SmallPtrSetImpl<Attribute> &locationSet)
551 bool withName = !loc.getName().empty();
553 os <<
"'" << loc.getName().strref() <<
"'(";
562 os << loc.getFilename().getValue();
563 if (
auto line = loc.getLine()) {
565 if (
auto col = loc.getColumn())
577 StringRef lastFileName;
578 for (
size_t i = 0, e = locVector.size(); i != e;) {
583 auto first = locVector[i];
584 if (first.getFilename() != lastFileName) {
585 lastFileName = first.getFilename();
592 first.getFilename() == locVector[
end].getFilename() &&
593 first.getLine() == locVector[
end].getLine())
598 if (
auto line = first.getLine()) {
600 if (
auto col = first.getColumn())
608 os <<
':' << first.getLine() <<
":{";
610 os << locVector[i++].getColumn();
622 llvm::TypeSwitch<Location, void>(loc)
623 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
625 .Case<mlir::FusedLoc>([&](
auto loc) {
626 SmallPtrSet<Attribute, 8> locationSet;
630 .Default([&](
auto loc) {
642 switch (locationSet.size()) {
653 SmallVector<FileLineColLoc, 8> flcLocs;
654 SmallVector<Attribute, 8> otherLocs;
655 flcLocs.reserve(locationSet.size());
656 otherLocs.reserve(locationSet.size());
657 for (Attribute loc : locationSet) {
658 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
659 flcLocs.push_back(flcLoc);
661 otherLocs.push_back(loc);
672 size_t sstrSize =
os.tell();
673 bool emittedAnything =
false;
674 auto recheckEmittedSomething = [&]() {
675 size_t currSize =
os.tell();
676 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
677 emittedAnything |= emittedSomethingSinceLastCheck;
679 return emittedSomethingSinceLastCheck;
688 if (recheckEmittedSomething()) {
690 recheckEmittedSomething();
696 if (emittedAnything && !flcLocs.empty())
701 llvm::raw_string_ostream &
os;
713 if (isa<BlockArgument>(v))
722 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
727 if (v.getDefiningOp<ReadInterfaceSignalOp>())
740 if (
auto cast = dyn_cast<BitcastOp>(op))
741 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
743 [&](Location loc) { return emitError(loc); })) {
746 if (op->hasOneUse() &&
747 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
755 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
761 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
765 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
766 if (verbatim.getFormatString().size() > 32)
771 for (
auto &use : op->getUses()) {
772 auto *user = use.getOwner();
782 UnionExtractOp, IndexedPartSelectOp>(user))
783 if (use.getOperandNumber() == 0 &&
794 auto usedInExprControl = [user, &use]() {
795 return TypeSwitch<Operation *, bool>(user)
796 .Case<ltl::ClockOp>([&](
auto clockOp) {
798 return clockOp.getClock() == use.get();
800 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
801 sv::CoverConcurrentOp>(
802 [&](
auto op) {
return op.getClock() == use.get(); })
803 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
804 sv::CoverPropertyOp>([&](
auto op) {
805 return op.getDisable() == use.get() || op.getClock() == use.get();
807 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
812 .Default([](
auto) {
return false; });
815 if (!usedInExprControl())
819 auto read = dyn_cast<ReadInOutOp>(op);
822 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
833 unsigned numStatements = 0;
834 block.walk([&](Operation *op) {
836 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
837 return WalkResult::advance();
839 TypeSwitch<Operation *, unsigned>(op)
840 .Case<VerbatimOp>([&](
auto) {
846 .Case<IfOp>([&](
auto) {
857 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
858 .Case<OutputOp>([&](OutputOp oop) {
861 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
862 Operation *op = operand.getDefiningOp();
863 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
866 .Default([](
auto) {
return 1; });
867 if (numStatements > 1)
868 return WalkResult::interrupt();
869 return WalkResult::advance();
871 if (numStatements == 0)
873 if (numStatements == 1)
883 if (op->getResult(0).use_empty())
888 if (op->hasOneUse() &&
889 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
890 *op->getUsers().begin()))
912 for (
auto &op : *elseBlock) {
913 if (
auto opIf = dyn_cast<IfOp>(op)) {
930template <
typename PPS>
932 enum Container { NoContainer, InComment, InAttr };
933 Container currentContainer = NoContainer;
935 auto closeContainer = [&] {
936 if (currentContainer == NoContainer)
938 if (currentContainer == InComment)
940 else if (currentContainer == InAttr)
942 ps << PP::end << PP::end;
944 currentContainer = NoContainer;
947 bool isFirstContainer =
true;
948 auto openContainer = [&](Container newContainer) {
949 assert(newContainer != NoContainer);
950 if (currentContainer == newContainer)
954 if (!isFirstContainer)
955 ps << (mayBreak ? PP::space : PP::nbsp);
956 isFirstContainer =
false;
959 if (newContainer == InComment)
961 else if (newContainer == InAttr)
963 currentContainer = newContainer;
971 ps.scopedBox(PP::cbox0, [&]() {
972 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
973 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
975 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
977 if (attr.getExpression())
978 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
987 if (
auto *op = val.getDefiningOp())
990 if (
auto port = dyn_cast<BlockArgument>(val)) {
992 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
993 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
995 port.getArgNumber());
997 assert(
false &&
"unhandled value");
1009class VerilogEmitterState {
1011 explicit VerilogEmitterState(ModuleOp designOp,
1017 llvm::formatted_raw_ostream &os,
1018 StringAttr fileName,
OpLocMap &verilogLocMap)
1019 : designOp(designOp), shared(shared), options(options),
1020 symbolCache(symbolCache), globalNames(globalNames),
1021 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1022 pp(os, options.emittedLineLength), fileName(fileName) {
1023 pp.setListener(&saver);
1046 llvm::formatted_raw_ostream &os;
1048 bool encounteredError =
false;
1057 bool pendingNewline =
false;
1071 StringAttr fileName;
1077 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1080 verilogLocMap.
clear();
1084 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1085 void operator=(
const VerilogEmitterState &) =
delete;
1098using CallbackDataTy = std::pair<Operation *, bool>;
1102 VerilogEmitterState &state;
1107 explicit EmitterBase(VerilogEmitterState &state)
1109 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1111 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1112 state.encounteredError =
true;
1113 return op->emitError(message);
1116 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1117 state.encounteredError =
true;
1118 return op->emitOpError(message);
1121 InFlightDiagnostic emitError(Location loc,
const Twine &message =
"") {
1122 state.encounteredError =
true;
1123 return mlir::emitError(loc, message);
1126 void emitLocationImpl(llvm::StringRef location) {
1129 ps << PP::neverbreak;
1130 if (!location.empty())
1131 ps <<
"\t// " << location;
1134 void emitLocationInfo(Location loc) {
1142 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1145 setPendingNewline();
1148 template <
typename PPS>
1149 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1150 llvm::function_ref<
void(Value)> operandEmitter,
1151 ArrayAttr symAttrs);
1157 void emitComment(StringAttr comment);
1161 void emitPendingNewlineIfNeeded() {
1162 if (state.pendingNewline) {
1163 state.pendingNewline =
false;
1167 void setPendingNewline() {
1168 assert(!state.pendingNewline);
1169 state.pendingNewline =
true;
1172 void startStatement() { emitPendingNewlineIfNeeded(); }
1175 void operator=(
const EmitterBase &) =
delete;
1176 EmitterBase(
const EmitterBase &) =
delete;
1180template <
typename PPS>
1181void EmitterBase::emitTextWithSubstitutions(
1182 PPS &ps, StringRef
string, Operation *op,
1183 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1194 if (
auto *itemOp = item.getOp()) {
1195 if (item.hasPort()) {
1199 if (!symOpName.empty())
1201 emitError(itemOp,
"cannot get name for symbol ") << sym;
1203 emitError(op,
"cannot get name for symbol ") << sym;
1205 return StringRef(
"<INVALID>");
1211 unsigned numSymOps = symAttrs.size();
1212 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1215 next =
string.find(
"{{", next);
1216 if (next == StringRef::npos)
1223 while (next <
string.size() &&
isdigit(
string[next]))
1226 if (start == next) {
1230 size_t operandNoLength = next - start;
1233 StringRef fmtOptsStr;
1234 if (
string[next] ==
':') {
1235 size_t startFmtOpts = next + 1;
1236 while (next <
string.size() &&
string[next] !=
'}')
1238 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1242 if (!
string.substr(next).starts_with(
"}}"))
1246 unsigned operandNo = 0;
1247 if (
string.drop_front(start)
1248 .take_front(operandNoLength)
1249 .getAsInteger(10, operandNo)) {
1250 emitError(op,
"operand substitution too large");
1256 auto before =
string.take_front(start - 2);
1257 if (!before.empty())
1262 if (operandNo < op->getNumOperands())
1264 operandEmitter(op->getOperand(operandNo));
1265 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1266 unsigned symOpNum = operandNo - op->getNumOperands();
1267 auto sym = symAttrs[symOpNum];
1268 StringRef symVerilogName;
1269 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1270 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1271 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1272 auto namepath = globalRef.getNamepathAttr().getValue();
1273 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1276 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1278 auto innerRef = cast<InnerRefAttr>(sym);
1279 auto ref = state.symbolCache.getInnerDefinition(
1280 innerRef.getModule(), innerRef.getName());
1281 ps << namify(innerRef, ref);
1284 symVerilogName = namify(sym, symOp);
1287 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1288 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1290 symVerilogName = namify(sym, symOp);
1292 if (!symVerilogName.empty())
1295 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1299 string =
string.drop_front(next);
1305 while (emitUntilSubstitution())
1309 if (!
string.
empty())
1313void EmitterBase::emitComment(StringAttr comment) {
1320 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1324 auto ref = comment.getValue();
1326 while (!ref.empty()) {
1327 std::tie(line, ref) = ref.split(
"\n");
1334 if (line.size() <= lineLength) {
1336 setPendingNewline();
1347 auto breakPos = line.rfind(
' ', lineLength);
1349 if (breakPos == StringRef::npos) {
1350 breakPos = line.find(
' ', lineLength);
1353 if (breakPos == StringRef::npos)
1354 breakPos = line.size();
1361 setPendingNewline();
1362 breakPos = line.find_first_not_of(
' ', breakPos);
1364 if (breakPos == StringRef::npos)
1367 line = line.drop_front(breakPos);
1377 bool addPrefixUnderScore =
true;
1380 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1384 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1386 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1388 result = StringAttr::get(expr.getContext(), name);
1390 }
else if (
auto *op = expr.getDefiningOp()) {
1392 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1394 result = StringAttr::get(expr.getContext(), name);
1396 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1402 addPrefixUnderScore =
false;
1404 TypeSwitch<Operation *>(op)
1407 .Case([&result](VerbatimExprOp verbatim) {
1408 verbatim.getAsmResultNames([&](Value, StringRef name) {
1409 result = StringAttr::get(verbatim.getContext(), name);
1412 .Case([&result](VerbatimExprSEOp verbatim) {
1413 verbatim.getAsmResultNames([&](Value, StringRef name) {
1414 result = StringAttr::get(verbatim.getContext(), name);
1420 if (
auto operandName =
1423 cast<IntegerType>(extract.getType()).getWidth();
1425 result = StringAttr::get(extract.getContext(),
1426 operandName.strref() +
"_" +
1427 Twine(extract.getLowBit()));
1429 result = StringAttr::get(
1430 extract.getContext(),
1431 operandName.strref() +
"_" +
1432 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1433 Twine(extract.getLowBit()));
1441 if (!result || result.strref().empty())
1445 if (addPrefixUnderScore && result.strref().front() !=
'_')
1446 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1458class ModuleEmitter :
public EmitterBase {
1460 explicit ModuleEmitter(VerilogEmitterState &state)
1461 : EmitterBase(state), currentModuleOp(nullptr),
1465 emitPendingNewlineIfNeeded();
1469 void emitParameters(Operation *module, ArrayAttr params);
1470 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1471 bool emitAsTwoStateType =
false);
1474 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1475 void emitFunc(FuncOp);
1478 void emitStatement(Operation *op);
1479 void emitBind(BindOp op);
1480 void emitBindInterface(BindInterfaceOp op);
1482 void emitSVAttributes(Operation *op);
1485 StringRef getVerilogStructFieldName(StringAttr field) {
1486 return fieldNameResolver.getRenamedFieldName(field).getValue();
1493 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1505 bool printPackedType(Type type, raw_ostream &os, Location loc,
1506 Type optionalAliasType = {},
bool implicitIntType =
true,
1507 bool singleBitDefaultType =
true,
1508 bool emitAsTwoStateType =
false);
1512 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1520 function_ref<InFlightDiagnostic()> emitError);
1523 VerilogPrecedence parenthesizeIfLooserThan,
1524 function_ref<InFlightDiagnostic()> emitError);
1530 Operation *currentModuleOp;
1536 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1542 SmallPtrSet<Operation *, 16> assignsInlined;
1551 const ModuleEmitter &emitter) {
1552 if (isa<RegOp>(op)) {
1557 cast<InOutType>(op->getResult(0).getType()).getElementType();
1564 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1565 while (isa<ArrayType>(innerType.getElementType()))
1566 innerType = cast<ArrayType>(innerType.getElementType());
1567 if (isa<StructType>(innerType.getElementType()) ||
1568 isa<TypeAliasType>(innerType.getElementType()))
1576 if (isa<sv::WireOp>(op))
1578 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1579 return "localparam";
1582 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1583 return interface.getInterfaceType().getInterface().getValue();
1591 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1593 if (isa<LogicOp>(op)) {
1599 if (isProcedural && !stripAutomatic)
1600 return hasStruct ?
"automatic" :
"automatic logic";
1601 return hasStruct ?
"" :
"logic";
1608 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1611 assert(!emitter.state.options.disallowLocalVariables &&
1612 "automatic variables not allowed");
1616 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1617 :
"automatic logic";
1624static void emitDim(Attribute width, raw_ostream &os, Location loc,
1625 ModuleEmitter &emitter,
bool downTo) {
1627 os <<
"<<invalid type>>";
1630 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1631 if (intAttr.getValue().isZero()) {
1632 os <<
"/*Zero Width*/";
1637 os << (intAttr.getValue().getZExtValue() - 1);
1647 auto typedAttr = dyn_cast<TypedAttr>(width);
1649 emitter.emitError(loc,
"untyped dimension attribute ") << width;
1653 getIntAttr(loc.getContext(), typedAttr.getType(),
1654 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1655 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1659 emitter.printParamValue(width, os, [loc, &emitter]() {
1660 return emitter.emitError(loc,
"invalid parameter in type");
1668static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1669 ModuleEmitter &emitter) {
1670 for (Attribute width : dims) {
1671 emitDim(width, os, loc, emitter,
true);
1676void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1677 SmallVector<Attribute, 4> dims;
1679 [&](Location loc) {
return this->emitError(loc); });
1710 SmallVectorImpl<Attribute> &dims,
1711 bool implicitIntType,
bool singleBitDefaultType,
1712 ModuleEmitter &emitter,
1713 Type optionalAliasType = {},
1714 bool emitAsTwoStateType =
false) {
1715 return TypeSwitch<Type, bool>(type)
1716 .Case<IntegerType>([&](IntegerType integerType) ->
bool {
1717 if (emitAsTwoStateType && dims.empty()) {
1719 if (!typeName.empty()) {
1724 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1726 getInt32Attr(type.getContext(), integerType.getWidth()));
1728 StringRef typeName =
1729 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1730 if (!typeName.empty()) {
1737 return !dims.empty() || !implicitIntType;
1739 .Case<IntType>([&](IntType intType) {
1740 if (!implicitIntType)
1742 dims.push_back(intType.getWidth());
1746 .Case<ArrayType>([&](ArrayType arrayType) {
1747 dims.push_back(arrayType.getSizeAttr());
1749 implicitIntType, singleBitDefaultType,
1751 emitAsTwoStateType);
1753 .Case<InOutType>([&](InOutType inoutType) {
1755 implicitIntType, singleBitDefaultType,
1757 emitAsTwoStateType);
1759 .Case<EnumType>([&](EnumType enumType) {
1760 assert(enumType.getBitWidth().has_value() &&
1761 "enum type must have bitwidth");
1763 if (enumType.getBitWidth() != 32)
1764 os <<
"bit [" << *enumType.getBitWidth() - 1 <<
":0] ";
1766 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1767 llvm::interleaveComma(
1768 enumType.getFields().getAsRange<StringAttr>(), os,
1769 [&](
auto enumerator) {
1770 os << emitter.fieldNameResolver.getEnumFieldName(
1771 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1776 .Case<StructType>([&](StructType structType) {
1777 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1778 os <<
"/*Zero Width*/";
1781 os <<
"struct packed {";
1782 for (
auto &element : structType.getElements()) {
1784 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1785 <<
": Zero Width;*/ ";
1788 SmallVector<Attribute, 8> structDims;
1793 {}, emitAsTwoStateType);
1794 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1795 emitter.printUnpackedTypePostfix(element.type, os);
1802 .Case<UnionType>([&](UnionType unionType) {
1803 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1804 os <<
"/*Zero Width*/";
1808 int64_t unionWidth = hw::getBitWidth(unionType);
1809 os <<
"union packed {";
1810 for (
auto &element : unionType.getElements()) {
1812 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1813 <<
": Zero Width;*/ ";
1816 int64_t elementWidth = hw::getBitWidth(element.type);
1817 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1819 os <<
" struct packed {";
1820 if (element.offset) {
1821 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1822 << element.offset - 1 <<
":0] "
1823 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1827 SmallVector<Attribute, 8> structDims;
1831 true, emitter, {}, emitAsTwoStateType);
1832 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1833 emitter.printUnpackedTypePostfix(element.type, os);
1837 if (elementWidth + (int64_t)element.offset < unionWidth) {
1838 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1839 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1840 <<
"__post_padding_" << element.name.getValue() <<
";";
1842 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1851 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1852 .Case<ModportType>([&](ModportType modportType) {
1853 auto modportAttr = modportType.getModport();
1854 os << modportAttr.getRootReference().getValue() <<
"."
1855 << modportAttr.getNestedReferences().front().getValue();
1858 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1859 os <<
"<<unexpected unpacked array>>";
1860 emitter.emitError(loc,
"Unexpected unpacked array in packed type ")
1864 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1865 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1867 emitter.emitError(loc,
"unresolvable type reference");
1870 if (typedecl.getType() != typeRef.getInnerType()) {
1871 emitter.emitError(loc,
"declared type did not match aliased type");
1875 os << typedecl.getPreferredName();
1876 emitDims(dims, os, typedecl->getLoc(), emitter);
1879 .Default([&](Type type) {
1880 os <<
"<<invalid type '" << type <<
"'>>";
1881 emitter.emitError(loc,
"value has an unsupported verilog type ")
1898bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1899 Type optionalAliasType,
1900 bool implicitIntType,
1901 bool singleBitDefaultType,
1902 bool emitAsTwoStateType) {
1903 SmallVector<Attribute, 8> packedDimensions;
1905 singleBitDefaultType, *
this, optionalAliasType,
1906 emitAsTwoStateType);
1912void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1913 TypeSwitch<Type, void>(type)
1915 printUnpackedTypePostfix(inoutType.getElementType(), os);
1917 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1918 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1919 : state.designOp->getLoc();
1920 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1922 printUnpackedTypePostfix(arrayType.getElementType(), os);
1924 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1926 printUnpackedTypePostfix(arrayType.getElementType(), os);
1928 .Case<InterfaceType>([&](
auto) {
1942ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1943 function_ref<InFlightDiagnostic()> emitError) {
1944 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1952ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1953 VerilogPrecedence parenthesizeIfLooserThan,
1954 function_ref<InFlightDiagnostic()> emitError) {
1955 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1956 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1957 APInt value = intAttr.getValue();
1961 if (intTy.getWidth() > 32) {
1963 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1967 if (intTy.isSigned())
1968 os << intTy.getWidth() <<
"'sd";
1970 os << intTy.getWidth() <<
"'d";
1972 value.print(os, intTy.isSigned());
1973 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1975 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1977 os.write_escaped(strAttr.getValue());
1979 return {Symbol, IsUnsigned};
1981 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1983 os << fpAttr.getValueAsDouble();
1984 return {Symbol, IsUnsigned};
1986 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1987 os << verbatimParam.getValue().getValue();
1988 return {Symbol, IsUnsigned};
1990 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1992 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1993 parameterRef.getName());
1996 return {Symbol, IsUnsigned};
2000 auto expr = dyn_cast<ParamExprAttr>(value);
2002 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
2003 emitError() <<
" = " << value;
2004 return {LowestPrecedence, IsUnsigned};
2007 StringRef operatorStr;
2008 StringRef openStr, closeStr;
2009 VerilogPrecedence subprecedence = LowestPrecedence;
2010 VerilogPrecedence prec;
2011 std::optional<SubExprSignResult> operandSign;
2012 bool isUnary =
false;
2013 bool hasOpenClose =
false;
2015 switch (expr.getOpcode()) {
2017 operatorStr =
" + ";
2018 subprecedence = Addition;
2021 operatorStr =
" * ";
2022 subprecedence = Multiply;
2025 operatorStr =
" & ";
2026 subprecedence = And;
2029 operatorStr =
" | ";
2033 operatorStr =
" ^ ";
2034 subprecedence = Xor;
2037 operatorStr =
" << ";
2038 subprecedence = Shift;
2042 operatorStr =
" >> ";
2043 subprecedence = Shift;
2047 operatorStr =
" >>> ";
2048 subprecedence = Shift;
2049 operandSign = IsSigned;
2052 operatorStr =
" / ";
2053 subprecedence = Multiply;
2054 operandSign = IsUnsigned;
2057 operatorStr =
" / ";
2058 subprecedence = Multiply;
2059 operandSign = IsSigned;
2062 operatorStr =
" % ";
2063 subprecedence = Multiply;
2064 operandSign = IsUnsigned;
2067 operatorStr =
" % ";
2068 subprecedence = Multiply;
2069 operandSign = IsSigned;
2072 openStr =
"$clog2(";
2074 operandSign = IsUnsigned;
2075 hasOpenClose =
true;
2078 case PEO::StrConcat:
2081 hasOpenClose =
true;
2084 subprecedence = LowestPrecedence;
2089 prec = subprecedence;
2092 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2094 assert(isUnary || hasOpenClose ||
2095 !llvm::hasSingleElement(expr.getOperands()));
2102 auto emitOperand = [&](Attribute operand) ->
bool {
2104 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2105 if (operandSign.has_value())
2106 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2109 if (operandSign.has_value()) {
2111 signedness = *operandSign;
2113 return signedness == IsSigned;
2117 if (prec > parenthesizeIfLooserThan)
2126 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2127 for (
auto op : expr.getOperands().drop_front()) {
2130 if (expr.getOpcode() == PEO::Add) {
2131 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2132 const APInt &value = integer.getValue();
2133 if (value.isNegative() && !value.isMinSignedValue()) {
2135 allOperandsSigned &=
2136 emitOperand(IntegerAttr::get(op.getType(), -value));
2143 allOperandsSigned &= emitOperand(op);
2147 if (prec > parenthesizeIfLooserThan) {
2151 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2166class ExprEmitter :
public EmitterBase,
2168 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2173 ExprEmitter(ModuleEmitter &emitter,
2174 SmallPtrSetImpl<Operation *> &emittedExprs)
2175 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2177 ExprEmitter(ModuleEmitter &emitter,
2178 SmallPtrSetImpl<Operation *> &emittedExprs,
2180 : EmitterBase(emitter.state), emitter(emitter),
2181 emittedExprs(emittedExprs), buffer(tokens),
2182 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2183 assert(state.pp.getListener() == &state.saver);
2190 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2191 bool isAssignmentLikeContext) {
2192 assert(localTokens.empty());
2194 ps.scopedBox(PP::ibox0, [&]() {
2197 emitSubExpr(exp, parenthesizeIfLooserThan,
2199 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2201 isAssignmentLikeContext);
2206 if (&buffer.tokens == &localTokens)
2207 buffer.flush(state.pp);
2212 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2213 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2215 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2223 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2224 SubExprSignRequirement signReq = NoRequirement,
2225 bool isSelfDeterminedUnsignedValue =
false,
2226 bool isAssignmentLikeContext =
false);
2230 void emitSVAttributes(Operation *op);
2232 SubExprInfo visitUnhandledExpr(Operation *op);
2233 SubExprInfo visitInvalidComb(Operation *op) {
2236 SubExprInfo visitUnhandledComb(Operation *op) {
2237 return visitUnhandledExpr(op);
2240 return dispatchSVVisitor(op);
2243 return visitUnhandledExpr(op);
2245 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2248 enum EmitBinaryFlags {
2249 EB_RequireSignedOperands = RequireSigned,
2250 EB_RequireUnsignedOperands = RequireUnsigned,
2251 EB_OperandSignRequirementMask = 0x3,
2256 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2260 EB_ForceResultSigned = 0x8,
2265 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2266 const char *syntax,
unsigned emitBinaryFlags = 0);
2268 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2269 bool resultAlwaysUnsigned =
false);
2272 void emitSubExprIBox2(
2273 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2274 ps.scopedBox(PP::ibox2,
2275 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2280 template <
typename Container,
typename EachFn>
2281 void interleaveComma(
const Container &c, EachFn eachFn) {
2282 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2287 void interleaveComma(ValueRange ops) {
2288 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2305 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2307 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2308 CloseFunc closeFn) {
2310 ps.scopedBox(PP::cbox0, [&]() {
2311 interleaveComma(c, eachFn);
2317 template <
typename OpenFunc,
typename CloseFunc>
2318 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2319 return emitBracedList(
2320 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2324 void emitBracedList(ValueRange ops) {
2325 return emitBracedList(
2326 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2330 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2333 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2334 bool printAsPattern, Operation *op);
2336 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2337 ArrayAttr fieldValues,
bool printAsPattern,
2340 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2342 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2343 SubExprInfo visitSV(GetModportOp op);
2344 SubExprInfo visitSV(SystemFunctionOp op);
2345 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2346 SubExprInfo visitSV(XMROp op);
2347 SubExprInfo visitSV(SFormatFOp op);
2348 SubExprInfo visitSV(XMRRefOp op);
2349 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2350 SubExprInfo visitSV(VerbatimExprOp op) {
2351 return visitVerbatimExprOp(op, op.getSymbols());
2353 SubExprInfo visitSV(VerbatimExprSEOp op) {
2354 return visitVerbatimExprOp(op, op.getSymbols());
2356 SubExprInfo visitSV(MacroRefExprOp op);
2357 SubExprInfo visitSV(MacroRefExprSEOp op);
2358 template <
typename MacroTy>
2359 SubExprInfo emitMacroCall(MacroTy op);
2361 SubExprInfo visitSV(ConstantXOp op);
2362 SubExprInfo visitSV(ConstantZOp op);
2363 SubExprInfo visitSV(ConstantStrOp op);
2365 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2366 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2368 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2373 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2374 emitSVAttributes(op);
2377 SubExprInfo visitSV(ArrayIndexInOutOp op);
2378 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2379 SubExprInfo visitSV(IndexedPartSelectOp op);
2380 SubExprInfo visitSV(StructFieldInOutOp op);
2383 SubExprInfo visitSV(SampledOp op);
2386 SubExprInfo visitSV(TimeOp op);
2387 SubExprInfo visitSV(STimeOp op);
2390 using TypeOpVisitor::visitTypeOp;
2392 SubExprInfo visitTypeOp(AggregateConstantOp op);
2394 SubExprInfo visitTypeOp(ParamValueOp op);
2401 SubExprInfo visitTypeOp(StructInjectOp op);
2402 SubExprInfo visitTypeOp(UnionCreateOp op);
2403 SubExprInfo visitTypeOp(UnionExtractOp op);
2404 SubExprInfo visitTypeOp(EnumCmpOp op);
2405 SubExprInfo visitTypeOp(EnumConstantOp op);
2408 using CombinationalVisitor::visitComb;
2409 SubExprInfo visitComb(
MuxOp op);
2410 SubExprInfo visitComb(ReverseOp op);
2411 SubExprInfo visitComb(
AddOp op) {
2412 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2413 return emitBinary(op, Addition,
"+");
2415 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2416 SubExprInfo visitComb(
MulOp op) {
2417 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2418 return emitBinary(op, Multiply,
"*");
2420 SubExprInfo visitComb(
DivUOp op) {
2421 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2423 SubExprInfo visitComb(
DivSOp op) {
2424 return emitBinary(op, Multiply,
"/",
2425 EB_RequireSignedOperands | EB_ForceResultSigned);
2427 SubExprInfo visitComb(
ModUOp op) {
2428 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2430 SubExprInfo visitComb(
ModSOp op) {
2431 return emitBinary(op, Multiply,
"%",
2432 EB_RequireSignedOperands | EB_ForceResultSigned);
2434 SubExprInfo visitComb(
ShlOp op) {
2435 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2437 SubExprInfo visitComb(
ShrUOp op) {
2439 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2441 SubExprInfo visitComb(
ShrSOp op) {
2444 return emitBinary(op, Shift,
">>>",
2445 EB_RequireSignedOperands | EB_ForceResultSigned |
2446 EB_RHS_UnsignedWithSelfDeterminedWidth);
2448 SubExprInfo visitComb(
AndOp op) {
2449 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2450 return emitBinary(op, And,
"&");
2452 SubExprInfo visitComb(
OrOp op) {
2453 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2454 return emitBinary(op, Or,
"|");
2456 SubExprInfo visitComb(
XorOp op) {
2457 if (op.isBinaryNot())
2458 return emitUnary(op,
"~");
2459 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2460 return emitBinary(op, Xor,
"^");
2465 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2467 SubExprInfo visitComb(ReplicateOp op);
2468 SubExprInfo visitComb(
ConcatOp op);
2470 SubExprInfo visitComb(ICmpOp op);
2472 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2473 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2474 "not printed within an assignment-like context");
2475 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2476 "supposed to spill such expressions";
2480 SubExprInfo printStructCreate(
2481 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2483 bool printAsPattern, Operation *op);
2486 ModuleEmitter &emitter;
2493 SubExprSignRequirement signPreference = NoRequirement;
2497 SmallPtrSetImpl<Operation *> &emittedExprs;
2500 SmallVector<Token> localTokens;
2514 bool isAssignmentLikeContext =
false;
2518SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2520 unsigned emitBinaryFlags) {
2522 emitError(op,
"SV attributes emission is unimplemented for the op");
2533 if (emitBinaryFlags & EB_ForceResultSigned)
2534 ps <<
"$signed(" << PP::ibox0;
2535 auto operandSignReq =
2536 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2537 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2539 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2541 ps << lhsSpace << syntax << PP::nbsp;
2548 auto rhsPrec = prec;
2549 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2550 rhsPrec = VerilogPrecedence(prec - 1);
2555 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2556 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2557 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2558 operandSignReq = NoRequirement;
2561 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2562 rhsIsUnsignedValueWithSelfDeterminedWidth);
2566 SubExprSignResult signedness = IsUnsigned;
2567 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2568 signedness = IsSigned;
2570 if (emitBinaryFlags & EB_ForceResultSigned) {
2571 ps << PP::end <<
")";
2572 signedness = IsSigned;
2576 return {prec, signedness};
2579SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2580 bool resultAlwaysUnsigned) {
2582 emitError(op,
"SV attributes emission is unimplemented for the op");
2585 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2589 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2590 resultAlwaysUnsigned ? IsUnsigned : signedness};
2595void ExprEmitter::emitSVAttributes(Operation *op) {
2609 auto concat = value.getDefiningOp<
ConcatOp>();
2610 if (!concat || concat.getNumOperands() != 2)
2613 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2614 if (constant && constant.getValue().isZero())
2615 return concat.getOperand(1);
2625SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2626 VerilogPrecedence parenthesizeIfLooserThan,
2627 SubExprSignRequirement signRequirement,
2628 bool isSelfDeterminedUnsignedValue,
2629 bool isAssignmentLikeContext) {
2631 if (
auto result = dyn_cast<OpResult>(exp))
2632 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2633 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2634 parenthesizeIfLooserThan, signRequirement,
2635 isSelfDeterminedUnsignedValue,
2636 isAssignmentLikeContext);
2640 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2645 auto *op = exp.getDefiningOp();
2649 if (!shouldEmitInlineExpr) {
2652 if (signRequirement == RequireSigned) {
2654 return {Symbol, IsSigned};
2658 return {Symbol, IsUnsigned};
2661 unsigned subExprStartIndex = buffer.tokens.size();
2663 ps.addCallback({op,
true});
2664 llvm::scope_exit done([&]() {
2666 ps.addCallback({op, false});
2672 signPreference = signRequirement;
2674 bool bitCastAdded =
false;
2675 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2677 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2678 ps.addAsString(inType.getWidth());
2679 ps <<
"'(" << PP::ibox0;
2680 bitCastAdded =
true;
2684 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2685 isAssignmentLikeContext);
2686 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2692 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2694 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2696 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2697 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2700 expInfo.signedness = IsSigned;
2701 expInfo.precedence = Selection;
2702 }
else if (signRequirement == RequireUnsigned &&
2703 expInfo.signedness == IsSigned) {
2706 expInfo.signedness = IsUnsigned;
2707 expInfo.precedence = Selection;
2708 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2715 expInfo.precedence = Selection;
2722 emittedExprs.insert(exp.getDefiningOp());
2726SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2727 auto openFn = [&]() {
2729 ps.addAsString(op.getMultiple());
2732 auto closeFn = [&]() { ps <<
"}}"; };
2736 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2737 if (op.getOperand().hasOneUse()) {
2738 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2739 return {Symbol, IsUnsigned};
2742 emitBracedList(op.getOperand(), openFn, closeFn);
2743 return {Symbol, IsUnsigned};
2746SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2747 emitBracedList(op.getOperands());
2748 return {Symbol, IsUnsigned};
2751SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2755 Type toType = op.getType();
2757 toType, op.getInput().getType(), op.getLoc(),
2758 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2760 ps.invokeWithStringOS(
2761 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2764 return emitSubExpr(op.getInput(), LowestPrecedence);
2767SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2768 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2769 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2770 SubExprSignRequirement signop[] = {
2772 NoRequirement, NoRequirement,
2774 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2776 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2778 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2780 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2781 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2784 if (op.isEqualAllOnes())
2785 return emitUnary(op,
"&",
true);
2788 if (op.isNotEqualZero())
2789 return emitUnary(op,
"|",
true);
2791 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2795 result.signedness = IsUnsigned;
2799SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2801 emitError(op,
"SV attributes emission is unimplemented for the op");
2803 unsigned loBit = op.getLowBit();
2804 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2806 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2807 assert((x.precedence == Symbol ||
2809 "should be handled by isExpressionUnableToInline");
2814 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2818 ps.addAsString(hiBit);
2819 if (hiBit != loBit) {
2821 ps.addAsString(loBit);
2824 return {Unary, IsUnsigned};
2827SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2829 emitError(op,
"SV attributes emission is unimplemented for the op");
2831 auto decl = op.getReferencedDecl(state.symbolCache);
2834 return {Selection, IsUnsigned};
2837SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2839 emitError(op,
"SV attributes emission is unimplemented for the op");
2842 ps.scopedBox(PP::ibox0, [&]() {
2844 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2845 [&]() { ps <<
"," << PP::space; });
2848 return {Symbol, IsUnsigned};
2851SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2853 emitError(op,
"SV attributes emission is unimplemented for the op");
2855 auto decl = op.getReferencedDecl(state.symbolCache);
2859 return {Selection, IsUnsigned};
2862SubExprInfo ExprEmitter::visitSV(XMROp op) {
2864 emitError(op,
"SV attributes emission is unimplemented for the op");
2866 if (op.getIsRooted())
2868 for (
auto s : op.getPath())
2869 ps <<
PPExtString(cast<StringAttr>(
s).getValue()) <<
".";
2871 return {Selection, IsUnsigned};
2876SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2878 emitError(op,
"SV attributes emission is unimplemented for the op");
2881 auto globalRef = op.getReferencedPath(&state.symbolCache);
2882 auto namepath = globalRef.getNamepathAttr().getValue();
2883 auto *
module = state.symbolCache.getDefinition(
2884 cast<InnerRefAttr>(namepath.front()).getModule());
2886 for (
auto sym : namepath) {
2888 auto innerRef = cast<InnerRefAttr>(sym);
2889 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2890 innerRef.getName());
2891 if (ref.hasPort()) {
2897 auto leaf = op.getVerbatimSuffixAttr();
2898 if (leaf && leaf.size())
2900 return {Selection, IsUnsigned};
2903SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2905 emitError(op,
"SV attributes emission is unimplemented for the op");
2907 emitTextWithSubstitutions(
2908 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2909 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2911 return {Unary, IsUnsigned};
2914template <
typename MacroTy>
2915SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2917 emitError(op,
"SV attributes emission is unimplemented for the op");
2920 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2921 assert(macroOp &&
"Invalid IR");
2923 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2925 if (!op.getInputs().empty()) {
2927 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2928 emitExpression(val, LowestPrecedence, false);
2932 return {LowestPrecedence, IsUnsigned};
2935SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2936 return emitMacroCall(op);
2939SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2940 return emitMacroCall(op);
2943SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2945 emitError(op,
"SV attributes emission is unimplemented for the op");
2947 ps.addAsString(op.getWidth());
2949 return {Unary, IsUnsigned};
2952SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2954 emitError(op,
"SV attributes emission is unimplemented for the op");
2956 ps.writeQuotedEscaped(op.getStr());
2957 return {Symbol, IsUnsigned};
2960SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2962 emitError(op,
"SV attributes emission is unimplemented for the op");
2964 ps.addAsString(op.getWidth());
2966 return {Unary, IsUnsigned};
2969SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2970 bool isNegated =
false;
2973 if (signPreference == RequireSigned && value.isNegative() &&
2974 !value.isMinSignedValue()) {
2979 ps.addAsString(type.getWidth());
2983 if (signPreference == RequireSigned)
2989 SmallString<32> valueStr;
2991 (-value).toStringUnsigned(valueStr, 16);
2993 value.toStringUnsigned(valueStr, 16);
2996 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2999SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
3001 emitError(op,
"SV attributes emission is unimplemented for the op");
3003 auto value = op.getValue();
3007 if (value.getBitWidth() == 0) {
3008 emitOpError(op,
"will not emit zero width constants in the general case");
3009 ps <<
"<<unsupported zero width constant: "
3010 <<
PPExtString(op->getName().getStringRef()) <<
">>";
3011 return {Unary, IsUnsigned};
3014 return printConstantScalar(value, cast<IntegerType>(op.getType()));
3017void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3018 bool printAsPattern, Operation *op) {
3019 if (printAsPattern && !isAssignmentLikeContext)
3020 emitAssignmentPatternContextError(op);
3021 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3024 elementValues, [&]() { ps << openDelim; },
3025 [&](Attribute elementValue) {
3026 printConstantAggregate(elementValue,
elementType, op);
3028 [&]() { ps <<
"}"; });
3031void ExprEmitter::printConstantStruct(
3032 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3033 bool printAsPattern, Operation *op) {
3034 if (printAsPattern && !isAssignmentLikeContext)
3035 emitAssignmentPatternContextError(op);
3042 auto fieldRange = llvm::make_filter_range(
3043 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3048 if (printAsPattern) {
3050 fieldRange, [&]() { ps <<
"'{"; },
3051 [&](
const auto &fieldAndValue) {
3052 ps.scopedBox(PP::ibox2, [&]() {
3053 const auto &[field, value] = fieldAndValue;
3054 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3055 <<
":" << PP::space;
3056 printConstantAggregate(value, field.type, op);
3059 [&]() { ps <<
"}"; });
3062 fieldRange, [&]() { ps <<
"{"; },
3063 [&](
const auto &fieldAndValue) {
3064 ps.scopedBox(PP::ibox2, [&]() {
3065 const auto &[field, value] = fieldAndValue;
3066 printConstantAggregate(value, field.type, op);
3069 [&]() { ps <<
"}"; });
3073void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3076 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3077 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3078 isAssignmentLikeContext, op);
3081 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3082 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3086 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3087 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3088 isAssignmentLikeContext, op);
3090 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3091 auto value = cast<IntegerAttr>(attr).getValue();
3092 printConstantScalar(value, intType);
3096 emitOpError(op,
"contains constant of type ")
3097 << type <<
" which cannot be emitted as Verilog";
3100SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3102 emitError(op,
"SV attributes emission is unimplemented for the op");
3106 "zero-bit types not allowed at this point");
3108 printConstantAggregate(op.getFields(), op.getType(), op);
3109 return {Symbol, IsUnsigned};
3112SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3114 emitError(op,
"SV attributes emission is unimplemented for the op");
3116 return ps.invokeWithStringOS([&](
auto &os) {
3117 return emitter.printParamValue(op.getValue(), os, [&]() {
3118 return op->emitOpError(
"invalid parameter use");
3127 emitError(op,
"SV attributes emission is unimplemented for the op");
3129 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3131 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3133 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3135 ps.addAsString(dstWidth);
3137 return {Selection, arrayPrec.signedness};
3140SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3141 emitSubExpr(op.getInput(), Selection);
3146 emitSubExpr(op.getIndex(), LowestPrecedence);
3148 emitSVAttributes(op);
3149 return {Selection, IsUnsigned};
3155 emitError(op,
"SV attributes emission is unimplemented for the op");
3157 if (op.isUniform()) {
3159 ps.addAsString(op.getInputs().size());
3161 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3165 op.getInputs(), [&]() { ps <<
"{"; },
3168 emitSubExprIBox2(v);
3171 [&]() { ps <<
"}"; });
3173 return {Unary, IsUnsigned};
3176SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3178 emitError(op,
"SV attributes emission is unimplemented for the op");
3181 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3182 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3183 return {Unary, IsUnsigned};
3188 emitError(op,
"SV attributes emission is unimplemented for the op");
3190 emitBracedList(op.getOperands());
3191 return {Unary, IsUnsigned};
3194SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3196 emitError(op,
"SV attributes emission is unimplemented for the op");
3198 auto index = op.getIndex();
3199 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3204 emitSubExpr(index, LowestPrecedence);
3206 return {Selection, arrayPrec.signedness};
3209SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3211 emitError(op,
"SV attributes emission is unimplemented for the op");
3213 auto prec = emitSubExpr(op.getInput(), Selection);
3215 emitSubExpr(op.getBase(), LowestPrecedence);
3216 if (op.getDecrement())
3220 ps.addAsString(op.getWidth());
3222 return {Selection, prec.signedness};
3225SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3227 emitError(op,
"SV attributes emission is unimplemented for the op");
3229 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3231 emitSubExpr(op.getBase(), LowestPrecedence);
3232 if (op.getDecrement())
3236 ps.addAsString(op.getWidth());
3241SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3243 emitError(op,
"SV attributes emission is unimplemented for the op");
3245 auto prec = emitSubExpr(op.getInput(), Selection);
3247 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3248 return {Selection, prec.signedness};
3251SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3253 emitError(op,
"SV attributes emission is unimplemented for the op");
3256 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3261SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3263 emitError(op,
"SV attributes emission is unimplemented for the op");
3266 ps.scopedBox(PP::ibox0, [&]() {
3267 ps.writeQuotedEscaped(op.getFormatString());
3274 for (
auto operand : op.getSubstitutions()) {
3275 ps <<
"," << PP::space;
3276 emitSubExpr(operand, LowestPrecedence);
3280 return {Symbol, IsUnsigned};
3283SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3285 emitError(op,
"SV attributes emission is unimplemented for the op");
3288 return {Symbol, IsUnsigned};
3291SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3293 emitError(op,
"SV attributes emission is unimplemented for the op");
3296 return {Symbol, IsUnsigned};
3299SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3313 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3314 ps.scopedBox(PP::ibox0, [&]() {
3315 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3319 emitSVAttributes(op);
3321 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3322 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3326 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3327 return emitSubExpr(op.getFalseValue(), Conditional);
3330 SubExprSignResult signedness = IsUnsigned;
3331 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3332 signedness = IsSigned;
3334 return {Conditional, signedness};
3338SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3340 emitError(op,
"SV attributes emission is unimplemented for the op");
3343 emitSubExpr(op.getInput(), LowestPrecedence);
3346 return {Symbol, IsUnsigned};
3349SubExprInfo ExprEmitter::printStructCreate(
3350 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3352 bool printAsPattern, Operation *op) {
3353 if (printAsPattern && !isAssignmentLikeContext)
3354 emitAssignmentPatternContextError(op);
3357 auto filteredFields = llvm::make_filter_range(
3358 llvm::enumerate(fieldInfos),
3359 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3361 if (printAsPattern) {
3363 filteredFields, [&]() { ps <<
"'{"; },
3364 [&](
const auto &field) {
3365 ps.scopedBox(PP::ibox2, [&]() {
3367 emitter.getVerilogStructFieldName(field.value().name))
3368 <<
":" << PP::space;
3369 fieldFn(field.value(), field.index());
3372 [&]() { ps <<
"}"; });
3375 filteredFields, [&]() { ps <<
"{"; },
3376 [&](
const auto &field) {
3377 ps.scopedBox(PP::ibox2,
3378 [&]() { fieldFn(field.value(), field.index()); });
3380 [&]() { ps <<
"}"; });
3383 return {Selection, IsUnsigned};
3388 emitError(op,
"SV attributes emission is unimplemented for the op");
3392 bool printAsPattern = isAssignmentLikeContext;
3393 StructType structType = op.getType();
3394 return printStructCreate(
3395 structType.getElements(),
3396 [&](
const auto &field,
auto index) {
3397 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3399 isAssignmentLikeContext);
3401 printAsPattern, op);
3406 emitError(op,
"SV attributes emission is unimplemented for the op");
3408 emitSubExpr(op.getInput(), Selection);
3410 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3411 return {Selection, IsUnsigned};
3414SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3416 emitError(op,
"SV attributes emission is unimplemented for the op");
3420 bool printAsPattern = isAssignmentLikeContext;
3421 StructType structType = op.getType();
3422 return printStructCreate(
3423 structType.getElements(),
3424 [&](
const auto &field,
auto index) {
3425 if (field.name == op.getFieldNameAttr()) {
3426 emitSubExpr(op.getNewValue(), Selection);
3428 emitSubExpr(op.getInput(), Selection);
3430 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3433 printAsPattern, op);
3436SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3437 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3438 return {Selection, IsUnsigned};
3441SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3443 emitError(op,
"SV attributes emission is unimplemented for the op");
3444 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3447 result.signedness = IsUnsigned;
3451SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3453 emitError(op,
"SV attributes emission is unimplemented for the op");
3457 auto unionWidth = hw::getBitWidth(unionType);
3458 auto &element = unionType.getElements()[op.getFieldIndex()];
3459 auto elementWidth = hw::getBitWidth(element.type);
3462 if (!elementWidth) {
3463 ps.addAsString(unionWidth);
3465 return {Unary, IsUnsigned};
3469 if (elementWidth == unionWidth) {
3470 emitSubExpr(op.getInput(), LowestPrecedence);
3471 return {Unary, IsUnsigned};
3476 ps.scopedBox(PP::ibox0, [&]() {
3477 if (
auto prePadding = element.offset) {
3478 ps.addAsString(prePadding);
3479 ps <<
"'h0," << PP::space;
3481 emitSubExpr(op.getInput(), Selection);
3482 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3483 ps <<
"," << PP::space;
3484 ps.addAsString(postPadding);
3490 return {Unary, IsUnsigned};
3493SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3495 emitError(op,
"SV attributes emission is unimplemented for the op");
3496 emitSubExpr(op.getInput(), Selection);
3499 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3500 auto unionWidth = hw::getBitWidth(unionType);
3501 auto &element = unionType.getElements()[op.getFieldIndex()];
3502 auto elementWidth = hw::getBitWidth(element.type);
3503 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3504 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3513 return {Selection, IsUnsigned};
3516SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3517 emitOpError(op,
"cannot emit this expression to Verilog");
3518 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3520 return {Symbol, IsUnsigned};
3536enum class PropertyPrecedence {
3556struct EmittedProperty {
3558 PropertyPrecedence precedence;
3563class PropertyEmitter :
public EmitterBase,
3564 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3568 PropertyEmitter(ModuleEmitter &emitter,
3569 SmallPtrSetImpl<Operation *> &emittedOps)
3570 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3571 PropertyEmitter(ModuleEmitter &emitter,
3572 SmallPtrSetImpl<Operation *> &emittedOps,
3574 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3576 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3577 assert(state.pp.getListener() == &state.saver);
3580 void emitAssertPropertyDisable(
3581 Value property, Value disable,
3582 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3584 void emitAssertPropertyBody(
3585 Value property, Value disable,
3586 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3588 void emitAssertPropertyBody(
3589 Value property, sv::EventControl event, Value clock, Value disable,
3590 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3595 emitNestedProperty(Value property,
3596 PropertyPrecedence parenthesizeIfLooserThan);
3597 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3598 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3600 EmittedProperty visitUnhandledLTL(Operation *op);
3601 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3602 EmittedProperty visitLTL(ltl::AndOp op);
3603 EmittedProperty visitLTL(ltl::OrOp op);
3604 EmittedProperty visitLTL(ltl::IntersectOp op);
3605 EmittedProperty visitLTL(ltl::DelayOp op);
3606 EmittedProperty visitLTL(ltl::ConcatOp op);
3607 EmittedProperty visitLTL(ltl::RepeatOp op);
3608 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3609 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3610 EmittedProperty visitLTL(ltl::NotOp op);
3611 EmittedProperty visitLTL(ltl::ImplicationOp op);
3612 EmittedProperty visitLTL(ltl::UntilOp op);
3613 EmittedProperty visitLTL(ltl::EventuallyOp op);
3614 EmittedProperty visitLTL(ltl::ClockOp op);
3616 void emitLTLConcat(ValueRange inputs);
3619 ModuleEmitter &emitter;
3624 SmallPtrSetImpl<Operation *> &emittedOps;
3627 SmallVector<Token> localTokens;
3640void PropertyEmitter::emitAssertPropertyDisable(
3641 Value property, Value disable,
3642 PropertyPrecedence parenthesizeIfLooserThan) {
3645 ps <<
"disable iff" << PP::nbsp <<
"(";
3647 emitNestedProperty(disable, PropertyPrecedence::Unary);
3653 ps.scopedBox(PP::ibox0,
3654 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3660void PropertyEmitter::emitAssertPropertyBody(
3661 Value property, Value disable,
3662 PropertyPrecedence parenthesizeIfLooserThan) {
3663 assert(localTokens.empty());
3665 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3670 if (&buffer.tokens == &localTokens)
3671 buffer.flush(state.pp);
3674void PropertyEmitter::emitAssertPropertyBody(
3675 Value property, sv::EventControl event, Value clock, Value disable,
3676 PropertyPrecedence parenthesizeIfLooserThan) {
3677 assert(localTokens.empty());
3680 ps.scopedBox(PP::ibox2, [&] {
3681 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3682 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3688 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3693 if (&buffer.tokens == &localTokens)
3694 buffer.flush(state.pp);
3697EmittedProperty PropertyEmitter::emitNestedProperty(
3698 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3708 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3709 ExprEmitter(emitter, emittedOps, buffer.tokens)
3710 .emitExpression(property, LowestPrecedence,
3712 return {PropertyPrecedence::Symbol};
3715 unsigned startIndex = buffer.tokens.size();
3716 auto info = dispatchLTLVisitor(property.getDefiningOp());
3721 if (
info.precedence > parenthesizeIfLooserThan) {
3723 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3724 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3726 ps << PP::end <<
")";
3728 info.precedence = PropertyPrecedence::Symbol;
3732 emittedOps.insert(property.getDefiningOp());
3736EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3737 emitOpError(op,
"emission as Verilog property or sequence not supported");
3738 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3739 return {PropertyPrecedence::Symbol};
3742EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3744 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3745 return {PropertyPrecedence::Symbol};
3748EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3751 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3752 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3753 return {PropertyPrecedence::And};
3756EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3759 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3760 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3761 return {PropertyPrecedence::Or};
3764EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3768 emitNestedProperty(input, PropertyPrecedence::Intersect);
3770 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3771 return {PropertyPrecedence::Intersect};
3774EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3776 if (
auto length = op.getLength()) {
3778 ps.addAsString(op.getDelay());
3781 ps.addAsString(op.getDelay());
3783 ps.addAsString(op.getDelay() + *length);
3787 if (op.getDelay() == 0) {
3789 }
else if (op.getDelay() == 1) {
3793 ps.addAsString(op.getDelay());
3798 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3799 return {PropertyPrecedence::Concat};
3802void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3803 bool addSeparator =
false;
3804 for (
auto input : inputs) {
3807 if (!input.getDefiningOp<ltl::DelayOp>())
3808 ps <<
"##0" << PP::space;
3810 addSeparator =
true;
3811 emitNestedProperty(input, PropertyPrecedence::Concat);
3815EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3816 emitLTLConcat(op.getInputs());
3817 return {PropertyPrecedence::Concat};
3820EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3821 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3822 if (
auto more = op.getMore()) {
3824 ps.addAsString(op.getBase());
3827 ps.addAsString(op.getBase() + *more);
3831 if (op.getBase() == 0) {
3833 }
else if (op.getBase() == 1) {
3837 ps.addAsString(op.getBase());
3841 return {PropertyPrecedence::Repeat};
3844EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3845 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3847 auto more = op.getMore();
3849 ps.addAsString(op.getBase());
3852 ps.addAsString(op.getBase() + more);
3856 return {PropertyPrecedence::Repeat};
3859EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3860 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3862 auto more = op.getMore();
3864 ps.addAsString(op.getBase());
3867 ps.addAsString(op.getBase() + more);
3871 return {PropertyPrecedence::Repeat};
3874EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3875 ps <<
"not" << PP::space;
3876 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3877 return {PropertyPrecedence::Unary};
3883 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3884 if (!concatOp || concatOp.getInputs().size() < 2)
3886 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3887 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3889 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3890 if (!constOp || !constOp.getValue().isOne())
3892 return concatOp.getInputs().drop_back();
3895EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3899 emitLTLConcat(range);
3900 ps << PP::space <<
"|=>" << PP::nbsp;
3902 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3903 ps << PP::space <<
"|->" << PP::nbsp;
3905 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3906 return {PropertyPrecedence::Implication};
3909EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3910 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3911 ps << PP::space <<
"until" << PP::space;
3912 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3913 return {PropertyPrecedence::Until};
3916EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3917 ps <<
"s_eventually" << PP::space;
3918 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3919 return {PropertyPrecedence::Qualifier};
3922EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3924 ps.scopedBox(PP::ibox2, [&] {
3925 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3926 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3930 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3931 return {PropertyPrecedence::Clocking};
3941class NameCollector {
3943 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3947 void collectNames(Block &block);
3949 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3950 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3953 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3954 ModuleEmitter &moduleEmitter;
3959 static constexpr size_t maxTypeWidthBound = 32;
3964void NameCollector::collectNames(Block &block) {
3967 for (
auto &op : block) {
3971 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
3974 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3978 for (
auto result : op.getResults()) {
3980 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3981 SmallString<16> typeString;
3985 llvm::raw_svector_ostream stringStream(typeString);
3987 stringStream, op.getLoc());
3989 if (typeString.size() <= maxTypeWidthBound)
3990 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3997 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3998 for (
auto ®ion : op.getRegions()) {
3999 if (!region.empty())
4000 collectNames(region.front());
4014class StmtEmitter :
public EmitterBase,
4022 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4024 void emitStatement(Operation *op);
4025 void emitStatementBlock(Block &body);
4028 LogicalResult emitDeclaration(Operation *op);
4031 void collectNamesAndCalculateDeclarationWidths(Block &block);
4034 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4035 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4036 bool isAssignmentLikeContext =
false);
4037 void emitSVAttributes(Operation *op);
4040 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4043 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4047 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4048 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4049 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4050 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4051 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4052 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4054 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4055 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4056 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4057 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4058 template <
typename Op>
4061 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4062 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4063 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4065 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4066 LogicalResult visitSV(
AssignOp op);
4067 LogicalResult visitSV(BPAssignOp op);
4068 LogicalResult visitSV(PAssignOp op);
4069 LogicalResult visitSV(ForceOp op);
4070 LogicalResult visitSV(ReleaseOp op);
4071 LogicalResult visitSV(AliasOp op);
4072 LogicalResult visitSV(InterfaceInstanceOp op);
4073 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4074 LogicalResult visitStmt(OutputOp op);
4076 LogicalResult visitStmt(InstanceOp op);
4077 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4078 ArrayRef<Value> instPortValues);
4083 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4084 LogicalResult visitSV(OrderedOutputOp op);
4085 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4086 LogicalResult visitSV(IfDefProceduralOp op) {
4087 return emitIfDef(op, op.getCond());
4089 LogicalResult visitSV(IfOp op);
4090 LogicalResult visitSV(AlwaysOp op);
4091 LogicalResult visitSV(AlwaysCombOp op);
4092 LogicalResult visitSV(AlwaysFFOp op);
4093 LogicalResult visitSV(InitialOp op);
4094 LogicalResult visitSV(CaseOp op);
4095 template <
typename OpTy,
typename EmitPrefixFn>
4097 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4098 ValueRange substitutions, EmitPrefixFn emitPrefix);
4099 LogicalResult visitSV(WriteOp op);
4100 LogicalResult visitSV(FWriteOp op);
4101 LogicalResult visitSV(FFlushOp op);
4102 LogicalResult visitSV(VerbatimOp op);
4103 LogicalResult visitSV(MacroRefOp op);
4105 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4106 std::optional<unsigned> verbosity);
4107 LogicalResult visitSV(StopOp op);
4108 LogicalResult visitSV(FinishOp op);
4109 LogicalResult visitSV(ExitOp op);
4111 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4112 std::optional<unsigned> verbosity,
4114 ValueRange operands);
4117 template <
typename OpTy>
4118 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4119 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4120 op.getMessageAttr(), op.getSubstitutions());
4124 template <
typename OpTy>
4125 LogicalResult emitFatalMessageOp(OpTy op) {
4126 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4127 op.getMessageAttr(), op.getSubstitutions());
4130 LogicalResult visitSV(FatalProceduralOp op);
4131 LogicalResult visitSV(FatalOp op);
4132 LogicalResult visitSV(ErrorProceduralOp op);
4133 LogicalResult visitSV(WarningProceduralOp op);
4134 LogicalResult visitSV(InfoProceduralOp op);
4135 LogicalResult visitSV(ErrorOp op);
4136 LogicalResult visitSV(WarningOp op);
4137 LogicalResult visitSV(InfoOp op);
4139 LogicalResult visitSV(ReadMemOp op);
4141 LogicalResult visitSV(GenerateOp op);
4142 LogicalResult visitSV(GenerateCaseOp op);
4144 LogicalResult visitSV(
ForOp op);
4146 void emitAssertionLabel(Operation *op);
4147 void emitAssertionMessage(StringAttr message, ValueRange args,
4148 SmallPtrSetImpl<Operation *> &ops,
4150 template <
typename Op>
4151 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4152 LogicalResult visitSV(AssertOp op);
4153 LogicalResult visitSV(AssumeOp op);
4154 LogicalResult visitSV(CoverOp op);
4155 template <
typename Op>
4156 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4157 LogicalResult visitSV(AssertConcurrentOp op);
4158 LogicalResult visitSV(AssumeConcurrentOp op);
4159 LogicalResult visitSV(CoverConcurrentOp op);
4160 template <
typename Op>
4161 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4162 LogicalResult visitSV(AssertPropertyOp op);
4163 LogicalResult visitSV(AssumePropertyOp op);
4164 LogicalResult visitSV(CoverPropertyOp op);
4166 LogicalResult visitSV(BindOp op);
4167 LogicalResult visitSV(InterfaceOp op);
4169 LogicalResult visitSV(InterfaceSignalOp op);
4170 LogicalResult visitSV(InterfaceModportOp op);
4171 LogicalResult visitSV(AssignInterfaceSignalOp op);
4172 LogicalResult visitSV(MacroErrorOp op);
4173 LogicalResult visitSV(MacroDefOp op);
4175 void emitBlockAsStatement(Block *block,
4176 const SmallPtrSetImpl<Operation *> &locationOps,
4177 StringRef multiLineComment = StringRef());
4179 LogicalResult visitSV(FuncDPIImportOp op);
4180 template <
typename CallOp>
4181 LogicalResult emitFunctionCall(CallOp callOp);
4182 LogicalResult visitSV(FuncCallProceduralOp op);
4183 LogicalResult visitSV(FuncCallOp op);
4184 LogicalResult visitSV(ReturnOp op);
4185 LogicalResult visitSV(IncludeOp op);
4188 ModuleEmitter &emitter;
4193 size_t maxDeclNameWidth = 0;
4194 size_t maxTypeWidth = 0;
4205void StmtEmitter::emitExpression(Value exp,
4206 SmallPtrSetImpl<Operation *> &emittedExprs,
4207 VerilogPrecedence parenthesizeIfLooserThan,
4208 bool isAssignmentLikeContext) {
4209 ExprEmitter(emitter, emittedExprs)
4210 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4215void StmtEmitter::emitSVAttributes(Operation *op) {
4223 setPendingNewline();
4226void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4227 llvm::function_ref<
void()> emitRHS,
4229 std::optional<PPExtString> wordBeforeLHS) {
4231 ps.scopedBox(PP::ibox2, [&]() {
4232 if (wordBeforeLHS) {
4233 ps << *wordBeforeLHS << PP::space;
4237 ps << PP::space << syntax << PP::space;
4239 ps.scopedBox(PP::ibox0, [&]() {
4246template <
typename Op>
4248StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4249 std::optional<PPExtString> wordBeforeLHS) {
4250 SmallPtrSet<Operation *, 8> ops;
4254 ps.addCallback({op,
true});
4255 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4257 emitExpression(op.getSrc(), ops, LowestPrecedence,
4262 ps.addCallback({op,
false});
4263 emitLocationInfoAndNewLine(ops);
4267LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4270 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4273 if (emitter.assignsInlined.count(op))
4277 emitSVAttributes(op);
4282LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4283 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4287 if (emitter.assignsInlined.count(op))
4291 emitSVAttributes(op);
4296LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4298 emitSVAttributes(op);
4303LogicalResult StmtEmitter::visitSV(ForceOp op) {
4305 emitError(op,
"SV attributes emission is unimplemented for the op");
4310LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4312 emitError(op,
"SV attributes emission is unimplemented for the op");
4315 SmallPtrSet<Operation *, 8> ops;
4317 ps.addCallback({op,
true});
4318 ps.scopedBox(PP::ibox2, [&]() {
4319 ps <<
"release" << PP::space;
4320 emitExpression(op.getDest(), ops);
4323 ps.addCallback({op,
false});
4324 emitLocationInfoAndNewLine(ops);
4328LogicalResult StmtEmitter::visitSV(AliasOp op) {
4330 emitError(op,
"SV attributes emission is unimplemented for the op");
4333 SmallPtrSet<Operation *, 8> ops;
4335 ps.addCallback({op,
true});
4336 ps.scopedBox(PP::ibox2, [&]() {
4337 ps <<
"alias" << PP::space;
4338 ps.scopedBox(PP::cbox0, [&]() {
4340 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4341 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4345 ps.addCallback({op,
false});
4346 emitLocationInfoAndNewLine(ops);
4350LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4351 auto doNotPrint = op.getDoNotPrint();
4352 if (doNotPrint && !state.options.emitBindComments)
4356 emitError(op,
"SV attributes emission is unimplemented for the op");
4359 StringRef prefix =
"";
4360 ps.addCallback({op,
true});
4363 ps <<
"// This interface is elsewhere emitted as a bind statement."
4367 SmallPtrSet<Operation *, 8> ops;
4370 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4371 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4372 "point to an interface");
4375 if (!prefix.empty())
4381 ps.addCallback({op,
false});
4382 emitLocationInfoAndNewLine(ops);
4390LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4392 SmallPtrSet<Operation *, 8> ops;
4393 size_t operandIndex = 0;
4395 for (
PortInfo port : ports.getOutputs()) {
4396 auto operand = op->getOperand(operandIndex);
4400 if (operand.hasOneUse() && operand.getDefiningOp() &&
4401 isa<InstanceOp>(operand.getDefiningOp())) {
4410 ps.addCallback({op,
true});
4412 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4414 ps <<
"// Zero width: ";
4417 ps <<
"assign" << PP::space;
4419 ps << PP::space <<
"=" << PP::space;
4420 ps.scopedBox(PP::ibox0, [&]() {
4424 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4425 ps <<
"/*Zero width*/";
4427 emitExpression(operand, ops, LowestPrecedence,
4432 ps.addCallback({op,
false});
4433 emitLocationInfoAndNewLine(ops);
4440LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4441 auto parent = op->getParentOfType<PortList>();
4443 return emitOutputLikeOp(op, ports);
4446LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4448 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4449 ps <<
"`ifndef " << typescopeDef << PP::newline;
4450 ps <<
"`define " << typescopeDef;
4451 setPendingNewline();
4452 emitStatementBlock(*op.getBodyBlock());
4454 ps <<
"`endif // " << typescopeDef;
4455 setPendingNewline();
4459LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4461 emitError(op,
"SV attributes emission is unimplemented for the op");
4466 ps << PP::neverbox <<
"// ";
4468 SmallPtrSet<Operation *, 8> ops;
4470 ps.scopedBox(PP::ibox2, [&]() {
4471 ps <<
"typedef" << PP::space;
4472 ps.invokeWithStringOS([&](
auto &os) {
4474 op.getAliasType(),
false);
4476 ps << PP::space <<
PPExtString(op.getPreferredName());
4477 ps.invokeWithStringOS(
4478 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4483 emitLocationInfoAndNewLine(ops);
4487template <
typename CallOpTy>
4488LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4492 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4494 SmallPtrSet<Operation *, 8> ops;
4498 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4499 if (explicitReturn) {
4500 assert(explicitReturn.hasOneUse());
4501 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4502 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4503 emitExpression(bpassignOp.getDest(), ops);
4505 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4506 ps <<
"assign" << PP::nbsp;
4507 emitExpression(assignOp.getDest(), ops);
4509 ps << PP::nbsp <<
"=" << PP::nbsp;
4512 auto arguments = callee.getPortList(
true);
4516 bool needsComma =
false;
4517 auto printArg = [&](Value value) {
4519 ps <<
"," << PP::space;
4520 emitExpression(value, ops);
4524 ps.scopedBox(PP::ibox0, [&] {
4525 unsigned inputIndex = 0, outputIndex = 0;
4526 for (
auto arg : arguments) {
4529 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4531 printArg(op.getInputs()[inputIndex++]);
4536 emitLocationInfoAndNewLine(ops);
4540LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4541 return emitFunctionCall(op);
4544LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4545 return emitFunctionCall(op);
4548template <
typename PPS>
4550 bool isAutomatic =
false,
4551 bool emitAsTwoStateType =
false) {
4552 ps <<
"function" << PP::nbsp;
4554 ps <<
"automatic" << PP::nbsp;
4555 auto retType = op.getExplicitlyReturnedType();
4557 ps.invokeWithStringOS([&](
auto &os) {
4558 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4559 emitAsTwoStateType);
4565 emitter.emitPortList(
4569LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4570 auto parent = op->getParentOfType<sv::FuncOp>();
4572 return emitOutputLikeOp(op, ports);
4575LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4577 ps <<
"`include" << PP::nbsp;
4579 if (op.getStyle() == IncludeStyle::System)
4580 ps <<
"<" << op.getTarget() <<
">";
4582 ps <<
"\"" << op.getTarget() <<
"\"";
4584 emitLocationInfo(op.getLoc());
4585 setPendingNewline();
4589LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4592 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4596 if (
auto linkageName = importOp.getLinkageName())
4597 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4599 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4600 assert(op.isDeclaration() &&
"function must be a declaration");
4603 assert(state.pendingNewline);
4609LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4611 emitError(op,
"SV attributes emission is unimplemented for the op");
4614 SmallPtrSet<Operation *, 8> ops;
4617 ps.addCallback({op,
true});
4619 if (
auto fd = op.getFd())
4620 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4623 ps.addCallback({op,
false});
4624 emitLocationInfoAndNewLine(ops);
4628template <
typename OpTy,
typename EmitPrefixFn>
4629LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4630 StringRef formatString,
4631 ValueRange substitutions,
4632 EmitPrefixFn emitPrefix) {
4634 emitError(op,
"SV attributes emission is unimplemented for the op");
4637 SmallPtrSet<Operation *, 8> ops;
4640 ps.addCallback({op,
true});
4642 ps.scopedBox(PP::ibox0, [&]() {
4644 ps.writeQuotedEscaped(formatString);
4651 for (
auto operand : substitutions) {
4652 ps <<
"," << PP::space;
4653 emitExpression(operand, ops);
4657 ps.addCallback({op,
false});
4658 emitLocationInfoAndNewLine(ops);
4662LogicalResult StmtEmitter::visitSV(WriteOp op) {
4663 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4664 op.getSubstitutions(),
4665 [&](SmallPtrSetImpl<Operation *> &) {});
4668LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4669 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4670 op.getSubstitutions(),
4671 [&](SmallPtrSetImpl<Operation *> &ops) {
4672 emitExpression(op.getFd(), ops);
4673 ps <<
"," << PP::space;
4677LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4679 emitError(op,
"SV attributes emission is unimplemented for the op");
4682 SmallPtrSet<Operation *, 8> ops;
4687 StringRef
string = op.getFormatString();
4688 if (
string.ends_with(
"\n"))
4689 string =
string.drop_back();
4694 bool isFirst =
true;
4697 while (!
string.
empty()) {
4698 auto lhsRhs =
string.split(
'\n');
4702 ps << PP::end << PP::newline << PP::neverbox;
4706 emitTextWithSubstitutions(
4707 ps, lhsRhs.first, op,
4708 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4709 string = lhsRhs.second;
4714 emitLocationInfoAndNewLine(ops);
4719LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4721 emitError(op,
"SV attributes emission is unimplemented for the op");
4725 SmallPtrSet<Operation *, 8> ops;
4730 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4731 assert(macroOp &&
"Invalid IR");
4733 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4735 if (!op.getInputs().empty()) {
4737 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4738 emitExpression(val, ops, LowestPrecedence,
4744 emitLocationInfoAndNewLine(ops);
4750StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4751 std::optional<unsigned> verbosity) {
4753 emitError(op,
"SV attributes emission is unimplemented for the op");
4756 SmallPtrSet<Operation *, 8> ops;
4758 ps.addCallback({op,
true});
4760 if (verbosity && *verbosity != 1) {
4762 ps.addAsString(*verbosity);
4766 ps.addCallback({op,
false});
4767 emitLocationInfoAndNewLine(ops);
4771LogicalResult StmtEmitter::visitSV(StopOp op) {
4772 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4775LogicalResult StmtEmitter::visitSV(FinishOp op) {
4776 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4780LogicalResult StmtEmitter::visitSV(ExitOp op) {
4781 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4787StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4788 std::optional<unsigned> verbosity,
4789 StringAttr message, ValueRange operands) {
4791 emitError(op,
"SV attributes emission is unimplemented for the op");
4794 SmallPtrSet<Operation *, 8> ops;
4796 ps.addCallback({op,
true});
4802 if ((verbosity && *verbosity != 1) || message) {
4804 ps.scopedBox(PP::ibox0, [&]() {
4808 ps.addAsString(*verbosity);
4813 ps <<
"," << PP::space;
4814 ps.writeQuotedEscaped(message.getValue());
4816 for (
auto operand : operands) {
4817 ps <<
"," << PP::space;
4818 emitExpression(operand, ops);
4827 ps.addCallback({op,
false});
4828 emitLocationInfoAndNewLine(ops);
4832LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4833 return emitFatalMessageOp(op);
4836LogicalResult StmtEmitter::visitSV(FatalOp op) {
4837 return emitFatalMessageOp(op);
4840LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4841 return emitNonfatalMessageOp(op,
"$error");
4844LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4845 return emitNonfatalMessageOp(op,
"$warning");
4848LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4849 return emitNonfatalMessageOp(op,
"$info");
4852LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4853 return emitNonfatalMessageOp(op,
"$error");
4856LogicalResult StmtEmitter::visitSV(WarningOp op) {
4857 return emitNonfatalMessageOp(op,
"$warning");
4860LogicalResult StmtEmitter::visitSV(InfoOp op) {
4861 return emitNonfatalMessageOp(op,
"$info");
4864LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4865 SmallPtrSet<Operation *, 8> ops({op});
4868 ps.addCallback({op,
true});
4870 switch (op.getBaseAttr().getValue()) {
4871 case MemBaseTypeAttr::MemBaseBin:
4874 case MemBaseTypeAttr::MemBaseHex:
4879 ps.scopedBox(PP::ibox0, [&]() {
4880 ps.writeQuotedEscaped(op.getFilename());
4881 ps <<
"," << PP::space;
4882 emitExpression(op.getDest(), ops);
4886 ps.addCallback({op,
false});
4887 emitLocationInfoAndNewLine(ops);
4891LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4892 emitSVAttributes(op);
4895 ps.addCallback({op,
true});
4896 ps <<
"generate" << PP::newline;
4898 setPendingNewline();
4899 emitStatementBlock(op.getBody().getBlocks().front());
4902 ps <<
"endgenerate";
4903 ps.addCallback({op,
false});
4904 setPendingNewline();
4908LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4909 emitSVAttributes(op);
4912 ps.addCallback({op,
true});
4914 ps.invokeWithStringOS([&](
auto &os) {
4915 emitter.printParamValue(
4916 op.getCond(), os, VerilogPrecedence::Selection,
4917 [&]() { return op->emitOpError(
"invalid case parameter"); });
4920 setPendingNewline();
4923 ArrayAttr
patterns = op.getCasePatterns();
4924 ArrayAttr caseNames = op.getCaseNames();
4925 MutableArrayRef<Region> regions = op.getCaseRegions();
4932 llvm::StringMap<size_t> nextGenIds;
4933 ps.scopedBox(PP::bbox2, [&]() {
4935 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4936 auto ®ion = regions[i];
4937 assert(region.hasOneBlock());
4938 Attribute patternAttr =
patterns[i];
4941 if (!isa<mlir::TypedAttr>(patternAttr))
4944 ps.invokeWithStringOS([&](
auto &os) {
4945 emitter.printParamValue(
4946 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4947 [&]() {
return op->emitOpError(
"invalid case value"); });
4950 StringRef legalName =
4951 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4954 setPendingNewline();
4955 emitStatementBlock(region.getBlocks().front());
4958 setPendingNewline();
4964 ps.addCallback({op,
false});
4965 setPendingNewline();
4969LogicalResult StmtEmitter::visitSV(
ForOp op) {
4970 emitSVAttributes(op);
4971 llvm::SmallPtrSet<Operation *, 8> ops;
4972 ps.addCallback({op,
true});
4974 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4977 ps.scopedBox(PP::cbox0, [&]() {
4981 ps <<
"logic" << PP::nbsp;
4982 ps.invokeWithStringOS([&](
auto &os) {
4983 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4988 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4993 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4994 [&]() { emitExpression(op.getUpperBound(), ops); },
5000 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
5001 [&]() { emitExpression(op.getStep(), ops); },
5005 ps << PP::neverbreak;
5006 setPendingNewline();
5007 emitStatementBlock(op.getBody().getBlocks().front());
5010 ps.addCallback({op,
false});
5011 emitLocationInfoAndNewLine(ops);
5016void StmtEmitter::emitAssertionLabel(Operation *op) {
5017 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5023void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5024 SmallPtrSetImpl<Operation *> &ops,
5025 bool isConcurrent =
false) {
5028 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5029 ps.scopedBox(PP::ibox0, [&]() {
5030 ps.writeQuotedEscaped(message.getValue());
5032 for (
auto arg : args) {
5033 ps <<
"," << PP::space;
5034 emitExpression(arg, ops);
5040template <
typename Op>
5041LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5043 emitError(op,
"SV attributes emission is unimplemented for the op");
5046 SmallPtrSet<Operation *, 8> ops;
5048 ps.addCallback({op,
true});
5049 ps.scopedBox(PP::ibox2, [&]() {
5050 emitAssertionLabel(op);
5051 ps.scopedBox(PP::cbox0, [&]() {
5053 switch (op.getDefer()) {
5054 case DeferAssert::Immediate:
5056 case DeferAssert::Observed:
5059 case DeferAssert::Final:
5064 ps.scopedBox(PP::ibox0, [&]() {
5065 emitExpression(op.getExpression(), ops);
5068 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5072 ps.addCallback({op,
false});
5073 emitLocationInfoAndNewLine(ops);
5077LogicalResult StmtEmitter::visitSV(AssertOp op) {
5078 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5081LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5082 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5085LogicalResult StmtEmitter::visitSV(CoverOp op) {
5086 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5089template <
typename Op>
5090LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5092 emitError(op,
"SV attributes emission is unimplemented for the op");
5095 SmallPtrSet<Operation *, 8> ops;
5097 ps.addCallback({op,
true});
5098 ps.scopedBox(PP::ibox2, [&]() {
5099 emitAssertionLabel(op);
5100 ps.scopedBox(PP::cbox0, [&]() {
5101 ps << opName << PP::nbsp <<
"property (";
5102 ps.scopedBox(PP::ibox0, [&]() {
5103 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5105 emitExpression(op.getClock(), ops);
5106 ps <<
")" << PP::space;
5107 emitExpression(op.getProperty(), ops);
5110 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5115 ps.addCallback({op,
false});
5116 emitLocationInfoAndNewLine(ops);
5120LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5121 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5124LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5125 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5128LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5129 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5134template <
typename Op>
5135LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5137 emitError(op,
"SV attributes emission is unimplemented for the op");
5147 Operation *parent = op->getParentOp();
5148 Value
property = op.getProperty();
5149 bool isTemporal = !
property.getType().isSignlessInteger(1);
5151 bool emitAsImmediate = !isTemporal && isProcedural;
5154 SmallPtrSet<Operation *, 8> ops;
5156 ps.addCallback({op,
true});
5157 ps.scopedBox(PP::ibox2, [&]() {
5159 emitAssertionLabel(op);
5161 ps.scopedBox(PP::cbox0, [&]() {
5162 if (emitAsImmediate)
5163 ps << opName <<
"(";
5165 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5167 Value clock = op.getClock();
5168 auto event = op.getEvent();
5170 ps.scopedBox(PP::ibox2, [&]() {
5171 PropertyEmitter(emitter, ops)
5172 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5175 ps.scopedBox(PP::ibox2, [&]() {
5176 PropertyEmitter(emitter, ops)
5177 .emitAssertPropertyBody(property, op.getDisable());
5182 ps.addCallback({op,
false});
5183 emitLocationInfoAndNewLine(ops);
5187LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5188 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5191LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5192 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5195LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5196 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5199LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5201 emitError(op,
"SV attributes emission is unimplemented for the op");
5204 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5205 .getMacroIdentifier());
5208 bool hasEmptyThen = op->getRegion(0).front().empty();
5210 ps <<
"`ifndef " << ident;
5212 ps <<
"`ifdef " << ident;
5214 SmallPtrSet<Operation *, 8> ops;
5216 emitLocationInfoAndNewLine(ops);
5219 emitStatementBlock(op->getRegion(0).front());
5221 if (!op->getRegion(1).empty()) {
5222 if (!hasEmptyThen) {
5224 ps <<
"`else // " << ident;
5225 setPendingNewline();
5227 emitStatementBlock(op->getRegion(1).front());
5234 setPendingNewline();
5242void StmtEmitter::emitBlockAsStatement(
5243 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5244 StringRef multiLineComment) {
5251 emitLocationInfoAndNewLine(locationOps);
5254 emitStatementBlock(*block);
5256 if (needsBeginEnd) {
5260 if (!multiLineComment.empty())
5261 ps <<
" // " << multiLineComment;
5262 setPendingNewline();
5266LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5268 for (
auto &op : ooop.getBody().front())
5273LogicalResult StmtEmitter::visitSV(IfOp op) {
5274 SmallPtrSet<Operation *, 8> ops;
5276 auto ifcondBox = PP::ibox2;
5278 emitSVAttributes(op);
5280 ps.addCallback({op,
true});
5281 ps <<
"if (" << ifcondBox;
5291 emitExpression(ifOp.getCond(), ops);
5292 ps << PP::end <<
")";
5293 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5295 if (!ifOp.hasElse())
5299 Block *elseBlock = ifOp.getElseBlock();
5301 if (!nestedElseIfOp) {
5306 emitBlockAsStatement(elseBlock, ops);
5312 ifOp = nestedElseIfOp;
5313 ps <<
"else if (" << ifcondBox;
5315 ps.addCallback({op,
false});
5320LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5321 emitSVAttributes(op);
5322 SmallPtrSet<Operation *, 8> ops;
5326 auto printEvent = [&](AlwaysOp::Condition cond) {
5327 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5328 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5330 ps.addCallback({op,
true});
5332 switch (op.getNumConditions()) {
5338 printEvent(op.getCondition(0));
5343 ps.scopedBox(PP::cbox0, [&]() {
5344 printEvent(op.getCondition(0));
5345 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5346 ps << PP::space <<
"or" << PP::space;
5347 printEvent(op.getCondition(i));
5356 std::string comment;
5357 if (op.getNumConditions() == 0) {
5358 comment =
"always @*";
5360 comment =
"always @(";
5363 [&](Attribute eventAttr) {
5364 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5365 comment += stringifyEventControl(event);
5367 [&]() { comment +=
", "; });
5371 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5372 ps.addCallback({op,
false});
5376LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5377 emitSVAttributes(op);
5378 SmallPtrSet<Operation *, 8> ops;
5382 ps.addCallback({op,
true});
5383 StringRef opString =
"always_comb";
5384 if (state.options.noAlwaysComb)
5385 opString =
"always @(*)";
5388 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5389 ps.addCallback({op,
false});
5393LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5394 emitSVAttributes(op);
5396 SmallPtrSet<Operation *, 8> ops;
5400 ps.addCallback({op,
true});
5401 ps <<
"always_ff @(";
5402 ps.scopedBox(PP::cbox0, [&]() {
5403 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5404 emitExpression(op.getClock(), ops);
5405 if (op.getResetStyle() == ResetType::AsyncReset) {
5406 ps << PP::nbsp <<
"or" << PP::space
5407 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5408 emitExpression(op.getReset(), ops);
5415 std::string comment;
5416 comment +=
"always_ff @(";
5417 comment += stringifyEventControl(op.getClockEdge());
5418 if (op.getResetStyle() == ResetType::AsyncReset) {
5420 comment += stringifyEventControl(*op.getResetEdge());
5424 if (op.getResetStyle() == ResetType::NoReset)
5425 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5428 emitLocationInfoAndNewLine(ops);
5429 ps.scopedBox(PP::bbox2, [&]() {
5435 if (op.getResetStyle() == ResetType::AsyncReset &&
5436 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5438 emitExpression(op.getReset(), ops);
5440 emitBlockAsStatement(op.getResetBlock(), ops);
5443 emitBlockAsStatement(op.getBodyBlock(), ops);
5448 ps <<
" // " << comment;
5449 setPendingNewline();
5451 ps.addCallback({op,
false});
5455LogicalResult StmtEmitter::visitSV(InitialOp op) {
5456 emitSVAttributes(op);
5457 SmallPtrSet<Operation *, 8> ops;
5460 ps.addCallback({op,
true});
5462 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5463 ps.addCallback({op,
false});
5467LogicalResult StmtEmitter::visitSV(CaseOp op) {
5468 emitSVAttributes(op);
5469 SmallPtrSet<Operation *, 8> ops, emptyOps;
5472 ps.addCallback({op,
true});
5473 if (op.getValidationQualifier() !=
5474 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5475 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5476 op.getValidationQualifier()))
5478 const char *opname =
nullptr;
5479 switch (op.getCaseStyle()) {
5480 case CaseStmtType::CaseStmt:
5483 case CaseStmtType::CaseXStmt:
5486 case CaseStmtType::CaseZStmt:
5490 ps << opname <<
" (";
5491 ps.scopedBox(PP::ibox0, [&]() {
5492 emitExpression(op.getCond(), ops);
5495 emitLocationInfoAndNewLine(ops);
5497 size_t caseValueIndex = 0;
5498 ps.scopedBox(PP::bbox2, [&]() {
5499 for (
auto &caseInfo : op.getCases()) {
5501 auto &
pattern = caseInfo.pattern;
5503 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5504 .Case<CaseBitPattern>([&](
auto bitPattern) {
5507 ps.invokeWithStringOS([&](
auto &os) {
5508 os << bitPattern->getWidth() <<
"'b";
5509 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5510 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5513 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5514 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5515 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5517 .Case<CaseExprPattern>([&](
auto) {
5518 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5520 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5521 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5524 emitBlockAsStatement(caseInfo.block, emptyOps);
5530 ps.addCallback({op,
false});
5531 emitLocationInfoAndNewLine(ops);
5535LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5536 bool doNotPrint = op.getDoNotPrint();
5537 if (doNotPrint && !state.options.emitBindComments)
5542 emitSVAttributes(op);
5544 ps.addCallback({op,
true});
5547 <<
"/* This instance is elsewhere emitted as a bind statement."
5550 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5551 "attributes. The attributes will not be emitted.";
5554 SmallPtrSet<Operation *, 8> ops;
5559 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5560 assert(moduleOp &&
"Invalid IR");
5564 if (!op.getParameters().empty()) {
5567 bool printed =
false;
5569 llvm::zip(op.getParameters(),
5570 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5571 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5572 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5574 if (param.getValue() == modParam.getValue())
5579 ps <<
" #(" << PP::bbox2 << PP::newline;
5582 ps <<
"," << PP::newline;
5586 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5588 ps.invokeWithStringOS([&](
auto &os) {
5589 emitter.printParamValue(param.getValue(), os, [&]() {
5590 return op->emitOpError(
"invalid instance parameter '")
5591 << param.getName().getValue() <<
"' value";
5597 ps << PP::end << PP::newline <<
")";
5604 SmallVector<Value> instPortValues(modPortInfo.size());
5605 op.getValues(instPortValues, modPortInfo);
5606 emitInstancePortList(op, modPortInfo, instPortValues);
5608 ps.addCallback({op,
false});
5609 emitLocationInfoAndNewLine(ops);
5614 setPendingNewline();
5619void StmtEmitter::emitInstancePortList(Operation *op,
5621 ArrayRef<Value> instPortValues) {
5622 SmallPtrSet<Operation *, 8> ops;
5625 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5626 ModulePortInfo containingPortList(containingModule.getPortList());
5631 size_t maxNameLength = 0;
5632 for (
auto &elt : modPortInfo) {
5633 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5636 auto getWireForValue = [&](Value result) {
5637 return result.getUsers().begin()->getOperand(0);
5641 bool isFirst =
true;
5642 bool isZeroWidth =
false;
5644 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5646 auto &modPort = modPortInfo.
at(portNum);
5648 Value portVal = instPortValues[portNum];
5653 bool shouldPrintComma =
true;
5655 shouldPrintComma =
false;
5656 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5658 shouldPrintComma =
true;
5663 if (shouldPrintComma)
5666 emitLocationInfoAndNewLine(ops);
5681 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5682 auto modPortName = modPort.getVerilogName();
5684 ps.spaces(maxNameLength - modPortName.size() + 1);
5686 ps.scopedBox(PP::ibox0, [&]() {
5693 if (!modPort.isOutput()) {
5695 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5696 ps <<
"/* Zero width */";
5698 emitExpression(portVal, ops, LowestPrecedence);
5699 }
else if (portVal.use_empty()) {
5700 ps <<
"/* unused */";
5701 }
else if (portVal.hasOneUse() &&
5702 (output = dyn_cast_or_null<OutputOp>(
5703 portVal.getUses().begin()->getOwner()))) {
5708 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5710 containingPortList.atOutput(outputPortNo).getVerilogName());
5712 portVal = getWireForValue(portVal);
5713 emitExpression(portVal, ops);
5719 if (!isFirst || isZeroWidth) {
5720 emitLocationInfoAndNewLine(ops);
5733LogicalResult StmtEmitter::visitSV(BindOp op) {
5734 emitter.emitBind(op);
5735 assert(state.pendingNewline);
5739LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5740 emitComment(op.getCommentAttr());
5742 emitSVAttributes(op);
5745 ps.addCallback({op,
true});
5747 setPendingNewline();
5749 emitStatementBlock(*op.getBodyBlock());
5751 ps <<
"endinterface" << PP::newline;
5752 ps.addCallback({op,
false});
5753 setPendingNewline();
5758 emitSVAttributes(op);
5760 ps.addCallback({op,
true});
5762 ps << op.getContent();
5764 ps.addCallback({op,
false});
5765 setPendingNewline();
5769LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5771 emitSVAttributes(op);
5773 ps.addCallback({op,
true});
5775 ps << PP::neverbox <<
"// ";
5776 ps.invokeWithStringOS([&](
auto &os) {
5781 ps.invokeWithStringOS(
5782 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5786 ps.addCallback({op,
false});
5787 setPendingNewline();
5791LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5793 ps.addCallback({op,
true});
5797 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5798 auto port = cast<ModportStructAttr>(portAttr);
5799 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5800 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5801 ps << PPExtString(getSymOpName(signalDecl));
5805 ps.addCallback({op,
false});
5806 setPendingNewline();
5810LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5812 ps.addCallback({op,
true});
5813 SmallPtrSet<Operation *, 8> emitted;
5816 emitExpression(op.getIface(), emitted);
5817 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5818 emitExpression(op.getRhs(), emitted);
5820 ps.addCallback({op,
false});
5821 setPendingNewline();
5825LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5827 ps <<
"`" << op.getMacroIdentifier();
5828 setPendingNewline();
5832LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5833 auto decl = op.getReferencedMacro(&state.symbolCache);
5836 ps.addCallback({op,
true});
5838 if (decl.getArgs()) {
5840 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5841 ps << cast<StringAttr>(name);
5845 if (!op.getFormatString().empty()) {
5847 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5850 ps.addCallback({op,
false});
5851 setPendingNewline();
5855void StmtEmitter::emitStatement(Operation *op) {
5862 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5866 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5867 succeeded(dispatchVerifVisitor(op)))
5870 emitOpError(op,
"emission to Verilog not supported");
5871 emitPendingNewlineIfNeeded();
5872 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5873 setPendingNewline();
5884 StmtEmitter &stmtEmitter) {
5891 if (isa<IfDefProceduralOp>(op->getParentOp()))
5899 SmallVector<Value, 8> exprsToScan(op->getOperands());
5904 while (!exprsToScan.empty()) {
5905 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5912 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5913 auto *defOp = readInout.getOperand().getDefiningOp();
5920 if (isa<sv::WireOp>(defOp))
5925 if (!isa<RegOp, LogicOp>(defOp))
5931 if (isa<LogicOp>(defOp) &&
5932 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5936 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5937 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5945 exprsToScan.append(expr->getOperands().begin(),
5946 expr->getOperands().end());
5952 if (expr->getBlock() != op->getBlock())
5957 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5964template <
class AssignTy>
5966 AssignTy singleAssign;
5967 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5968 if (hasSVAttributes(user))
5971 if (auto assign = dyn_cast<AssignTy>(user)) {
5974 singleAssign = assign;
5978 return isa<ReadInOutOp>(user);
5980 return singleAssign;
5986 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5990 if (op1->getBlock() != user->getBlock())
5996 return op1->isBeforeInBlock(user);
6000LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6001 emitSVAttributes(op);
6002 auto value = op->getResult(0);
6003 SmallPtrSet<Operation *, 8> opsForLocation;
6004 opsForLocation.insert(op);
6006 ps.addCallback({op,
true});
6009 auto type = value.getType();
6015 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6017 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6018 unsigned targetColumn = 0;
6019 unsigned column = 0;
6022 if (maxDeclNameWidth > 0)
6023 targetColumn += maxDeclNameWidth + 1;
6026 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6027 }
else if (!word.empty()) {
6029 column += word.size();
6030 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6031 ps.spaces(numSpaces);
6032 column += numSpaces;
6035 SmallString<8> typeString;
6038 llvm::raw_svector_ostream stringStream(typeString);
6041 true, singleBitDefaultType);
6044 if (maxTypeWidth > 0)
6045 targetColumn += maxTypeWidth + 1;
6046 unsigned numSpaces = 0;
6047 if (!typeString.empty()) {
6049 column += typeString.size();
6052 if (targetColumn > column)
6053 numSpaces = targetColumn - column;
6054 ps.spaces(numSpaces);
6055 column += numSpaces;
6061 ps.invokeWithStringOS(
6062 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6065 if (state.options.printDebugInfo) {
6066 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6067 auto innerSym = innerSymOp.getInnerSymAttr();
6068 if (innerSym && !innerSym.empty()) {
6070 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6076 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6077 ps << PP::space <<
"=" << PP::space;
6078 ps.invokeWithStringOS([&](
auto &os) {
6079 emitter.printParamValue(localparam.getValue(), os, [&]() {
6080 return op->emitOpError(
"invalid localparam value");
6085 if (
auto regOp = dyn_cast<RegOp>(op)) {
6086 if (
auto initValue = regOp.getInit()) {
6087 ps << PP::space <<
"=" << PP::space;
6088 ps.scopedBox(PP::ibox0, [&]() {
6089 emitExpression(initValue, opsForLocation, LowestPrecedence,
6098 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6102 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6103 auto *source = singleAssign.getSrc().getDefiningOp();
6107 if (!source || isa<ConstantOp>(source) ||
6108 op->getNextNode() == singleAssign) {
6109 ps << PP::space <<
"=" << PP::space;
6110 ps.scopedBox(PP::ibox0, [&]() {
6111 emitExpression(singleAssign.getSrc(), opsForLocation,
6115 emitter.assignsInlined.insert(singleAssign);
6123 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6127 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6130 auto *source = singleAssign.getSrc().getDefiningOp();
6134 if (!source || isa<ConstantOp>(source) ||
6137 ps << PP::space <<
"=" << PP::space;
6138 ps.scopedBox(PP::ibox0, [&]() {
6139 emitExpression(singleAssign.getSrc(), opsForLocation,
6144 emitter.assignsInlined.insert(singleAssign);
6145 emitter.expressionsEmittedIntoDecl.insert(op);
6152 ps.addCallback({op,
false});
6153 emitLocationInfoAndNewLine(opsForLocation);
6157void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6160 NameCollector collector(emitter);
6161 collector.collectNames(block);
6164 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6165 maxTypeWidth = collector.getMaxTypeWidth();
6168void StmtEmitter::emitStatementBlock(Block &body) {
6169 ps.scopedBox(PP::bbox2, [&]() {
6174 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6175 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6180 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6181 collectNamesAndCalculateDeclarationWidths(body);
6184 for (
auto &op : body) {
6191void ModuleEmitter::emitStatement(Operation *op) {
6192 StmtEmitter(*
this, state.options).emitStatement(op);
6197void ModuleEmitter::emitSVAttributes(Operation *op) {
6205 setPendingNewline();
6212void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6213 auto verilogName =
module.getVerilogModuleNameAttr();
6215 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6217 setPendingNewline();
6226void ModuleEmitter::emitBind(BindOp op) {
6228 emitError(op,
"SV attributes emission is unimplemented for the op");
6229 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6235 Operation *childMod =
6236 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6240 ps.addCallback({op,
true});
6241 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6242 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6244 bool isFirst =
true;
6245 ps.scopedBox(PP::bbox2, [&]() {
6246 auto parentPortInfo = parentMod.getPortList();
6250 size_t maxNameLength = 0;
6251 for (
auto &elt : childPortInfo) {
6252 auto portName = elt.getVerilogName();
6253 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6254 maxNameLength = std::max(maxNameLength, elt.getName().size());
6257 SmallVector<Value> instPortValues(childPortInfo.size());
6258 inst.getValues(instPortValues, childPortInfo);
6260 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6262 Value portVal = instPortValues[idx];
6268 bool shouldPrintComma =
true;
6270 shouldPrintComma =
false;
6271 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6273 shouldPrintComma =
true;
6278 if (shouldPrintComma)
6291 ps << PP::neverbox <<
"//";
6295 ps.nbsp(maxNameLength - elt.getName().size());
6297 llvm::SmallPtrSet<Operation *, 4> ops;
6298 if (elt.isOutput()) {
6299 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6300 "output port must have either single or no use");
6301 if (portVal.use_empty()) {
6302 ps <<
"/* unused */";
6303 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6304 portVal.getUses().begin()->getOwner())) {
6307 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6309 parentPortList.atOutput(outputPortNo).getVerilogName());
6311 portVal = portVal.getUsers().begin()->getOperand(0);
6312 ExprEmitter(*
this, ops)
6313 .emitExpression(portVal, LowestPrecedence,
6317 ExprEmitter(*
this, ops)
6318 .emitExpression(portVal, LowestPrecedence,
6331 ps.addCallback({op,
false});
6332 setPendingNewline();
6335void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6337 emitError(op,
"SV attributes emission is unimplemented for the op");
6339 auto instance = op.getReferencedInstance(&state.symbolCache);
6341 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6342 instance.getInterfaceType().getInterface());
6344 ps.addCallback({op,
true});
6345 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6346 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6348 ps.addCallback({op,
false});
6349 setPendingNewline();
6352void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6356 auto printParamType = [&](Type type, Attribute defaultValue,
6357 SmallString<8> &result) {
6359 llvm::raw_svector_ostream sstream(result);
6364 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6365 if (intAttr.getValue().getBitWidth() == 32)
6367 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6368 if (fpAttr.getType().isF64())
6371 if (isa<NoneType>(type))
6378 if (
auto intType = type_dyn_cast<IntegerType>(type))
6379 if (intType.getWidth() == 32) {
6380 sstream <<
"/*integer*/";
6384 printPackedType(type, sstream, module->getLoc(),
6392 size_t maxTypeWidth = 0;
6393 SmallString<8> scratch;
6394 for (
auto param : params) {
6395 auto paramAttr = cast<ParamDeclAttr>(param);
6397 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6398 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6401 if (maxTypeWidth > 0)
6404 ps.scopedBox(PP::bbox2, [&]() {
6405 ps << PP::newline <<
"#(";
6406 ps.scopedBox(PP::cbox0, [&]() {
6409 [&](Attribute param) {
6410 auto paramAttr = cast<ParamDeclAttr>(param);
6411 auto defaultValue = paramAttr.getValue();
6413 printParamType(paramAttr.getType(), defaultValue, scratch);
6414 if (!scratch.empty())
6416 if (scratch.size() < maxTypeWidth)
6417 ps.nbsp(maxTypeWidth - scratch.size());
6419 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6420 module, paramAttr.getName()));
6424 ps.invokeWithStringOS([&](
auto &os) {
6426 return module->emitError("parameter '")
6427 << paramAttr.getName().getValue()
6428 << "' has invalid value";
6433 [&]() { ps <<
"," << PP::newline; });
6439void ModuleEmitter::emitPortList(Operation *module,
6441 bool emitAsTwoStateType) {
6443 if (portInfo.
size())
6444 emitLocationInfo(module->getLoc());
6448 bool hasOutputs =
false, hasZeroWidth =
false;
6449 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6450 SmallVector<SmallString<8>, 16> portTypeStrings;
6452 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6453 auto port = portInfo.
at(i);
6457 lastNonZeroPort = i;
6460 portTypeStrings.push_back({});
6462 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6464 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6467 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6470 if (maxTypeWidth > 0)
6474 ps.scopedBox(PP::bbox2, [&]() {
6475 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6476 auto lastPort = e - 1;
6479 auto portType = portInfo.
at(portIdx).
type;
6483 bool isZeroWidth =
false;
6488 ps << (isZeroWidth ?
"// " :
" ");
6492 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6493 size_t startOfNamePos = (hasOutputs ? 7 : 6) +
6494 (state.options.emitWireInPorts ? 5 : 0) +
6499 if (!isa<ModportType>(portType)) {
6500 switch (thisPortDirection) {
6501 case ModulePort::Direction::Output:
6504 case ModulePort::Direction::Input:
6505 ps << (hasOutputs ?
"input " :
"input ");
6507 case ModulePort::Direction::InOut:
6508 ps << (hasOutputs ?
"inout " :
"inout ");
6511 if (state.options.emitWireInPorts)
6513 if (!portTypeStrings[portIdx].
empty())
6514 ps << portTypeStrings[portIdx];
6515 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6516 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6518 ps << portTypeStrings[portIdx];
6519 if (portTypeStrings[portIdx].size() < startOfNamePos)
6520 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6527 ps.invokeWithStringOS(
6528 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6531 auto innerSym = portInfo.
at(portIdx).
getSym();
6532 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6534 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6539 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6543 if (
auto loc = portInfo.
at(portIdx).
loc)
6544 emitLocationInfo(loc);
6554 if (!state.options.disallowPortDeclSharing) {
6555 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6558 auto port = portInfo.
at(portIdx);
6562 bool isZeroWidth =
false;
6567 ps << (isZeroWidth ?
"// " :
" ");
6570 ps.nbsp(startOfNamePos);
6573 StringRef name = port.getVerilogName();
6577 ps.invokeWithStringOS(
6578 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6581 auto sym = port.getSym();
6582 if (state.options.printDebugInfo && sym && !sym.empty())
6583 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6587 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6591 if (
auto loc = port.loc)
6592 emitLocationInfo(loc);
6603 if (!portInfo.
size()) {
6605 SmallPtrSet<Operation *, 8> moduleOpSet;
6606 moduleOpSet.insert(module);
6607 emitLocationInfoAndNewLine(moduleOpSet);
6610 ps <<
");" << PP::newline;
6611 setPendingNewline();
6615void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6616 currentModuleOp =
module;
6618 emitComment(module.getCommentAttr());
6619 emitSVAttributes(module);
6621 ps.addCallback({module,
true});
6625 emitParameters(module, module.getParameters());
6629 assert(state.pendingNewline);
6632 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6635 ps.addCallback({module,
false});
6637 setPendingNewline();
6639 currentModuleOp =
nullptr;
6642void ModuleEmitter::emitFunc(FuncOp func) {
6644 if (func.isDeclaration())
6647 currentModuleOp = func;
6649 ps.addCallback({func,
true});
6653 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6655 ps <<
"endfunction";
6657 currentModuleOp =
nullptr;
6666 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6673 void emit(emit::FileListOp op);
6676 void emit(Block *block);
6678 void emitOp(emit::RefOp op);
6679 void emitOp(emit::VerbatimOp op);
6683 for (Operation &op : *block) {
6684 TypeSwitch<Operation *>(&op)
6685 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6686 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6687 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6688 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6689 .Case<BindInterfaceOp>(
6690 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6691 .Case<TypeScopeOp>([&](
auto typedecls) {
6692 ModuleEmitter(state).emitStatement(typedecls);
6695 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6701 for (
auto sym : op.getFiles()) {
6702 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6704 auto it = state.fileMapping.find(fileName);
6705 if (it == state.fileMapping.end()) {
6706 emitOpError(op,
" references an invalid file: ") << sym;
6710 auto file = cast<emit::FileOp>(it->second);
6711 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6718 StringAttr target = op.getTargetAttr().getAttr();
6719 auto *targetOp = state.symbolCache.getDefinition(target);
6720 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6722 TypeSwitch<Operation *>(targetOp)
6723 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6724 .Case<hw::HWModuleOp>(
6725 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6726 .Case<TypeScopeOp>([&](
auto typedecls) {
6727 ModuleEmitter(state).emitStatement(typedecls);
6730 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6736 SmallPtrSet<Operation *, 8> ops;
6741 StringRef text = op.getText();
6745 const auto &[lhs, rhs] = text.split(
'\n');
6749 ps << PP::end << PP::newline << PP::neverbox;
6751 }
while (!text.empty());
6754 emitLocationInfoAndNewLine(ops);
6772 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6773 moduleOp->walk([&](Operation *op) {
6775 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6778 SymbolTable::getSymbolAttrName()),
6779 name.getSymName(), op);
6780 if (isa<BindOp>(op))
6786 auto collectPorts = [&](
auto moduleOp) {
6787 auto portInfo = moduleOp.getPortList();
6788 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6789 if (!p.attrs || p.attrs.empty())
6791 for (NamedAttribute portAttr : p.attrs) {
6792 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6801 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6802 for (
auto file :
designOp.getOps<emit::FileOp>())
6803 for (
auto refs : file.getOps<emit::RefOp>())
6804 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6806 SmallString<32> outputPath;
6807 for (
auto &op : *
designOp.getBody()) {
6810 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6812 bool hasFileName =
false;
6813 bool emitReplicatedOps = !isFileOp;
6814 bool addToFilelist = !isFileOp;
6820 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6822 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6823 <<
" on " << op <<
"\n";);
6824 if (!attr.isDirectory())
6827 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6828 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6831 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6836 if (!defaultFileName.isTriviallyEmpty()) {
6837 llvm::sys::path::append(outputPath, defaultFileName);
6839 op->emitError(
"file name unspecified");
6841 llvm::sys::path::append(outputPath,
"error.out");
6845 auto destFile = StringAttr::get(op->getContext(), outputPath);
6846 auto &file =
files[destFile];
6847 file.ops.push_back(info);
6848 file.emitReplicatedOps = emitReplicatedOps;
6849 file.addToFilelist = addToFilelist;
6850 file.isVerilog = outputPath.ends_with(
".sv");
6855 if (!attr || attr.isDirectory()) {
6856 auto excludeFromFileListAttr =
6857 BoolAttr::get(op->getContext(), !addToFilelist);
6858 auto includeReplicatedOpsAttr =
6859 BoolAttr::get(op->getContext(), emitReplicatedOps);
6860 auto outputFileAttr = hw::OutputFileAttr::get(
6861 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6862 op->setAttr(
"output_file", outputFileAttr);
6868 TypeSwitch<Operation *>(&op)
6869 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6871 fileMapping.try_emplace(file.getSymNameAttr(), file);
6872 separateFile(file, file.getFileName());
6874 .Case<emit::FragmentOp>([&](
auto fragment) {
6877 .Case<HWModuleOp>([&](
auto mod) {
6879 auto sym = mod.getNameAttr();
6882 collectInstanceSymbolsAndBinds(mod);
6884 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6885 if (it->second.size() != 1 || attr) {
6888 op.emitError(
"modules can be emitted to a single file");
6896 if (attr || separateModules)
6902 .Case<InterfaceOp>([&](InterfaceOp intf) {
6907 for (
auto &op : *intf.getBodyBlock())
6908 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6909 if (
auto name = symOp.getNameAttr())
6913 if (attr || separateModules)
6914 separateFile(intf, intf.getSymName() +
".sv");
6920 separateFile(op, op.getOutputFile().getFilename().getValue());
6922 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
6928 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6929 [&](Operation *op) {
6935 separateFile(op,
"");
6937 .Case<FuncOp>([&](
auto op) {
6943 separateFile(op,
"");
6947 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6950 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6959 separateFile(op,
"");
6961 .Case<BindOp>([&](
auto op) {
6963 separateFile(op,
"bindfile.sv");
6968 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
6969 .Case<MacroDeclOp>([&](
auto op) {
6972 .Case<sv::ReserveNamesOp>([](
auto op) {
6975 .Case<om::ClassLike>([&](
auto op) {
6978 .Case<om::ConstantOp>([&](
auto op) {
6981 .Default([&](
auto *) {
6982 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7002 size_t lastReplicatedOp = 0;
7004 bool emitHeaderInclude =
7007 if (emitHeaderInclude)
7010 size_t numReplicatedOps =
7015 DenseSet<emit::FragmentOp> includedFragments;
7016 for (
const auto &opInfo : file.
ops) {
7017 Operation *op = opInfo.op;
7021 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7027 if (
auto fragments =
7029 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7033 op->emitError(
"cannot find referenced fragment ") << sym;
7036 emit::FragmentOp fragment = it->second;
7037 if (includedFragments.insert(fragment).second) {
7038 thingsToEmit.emplace_back(it->second);
7044 thingsToEmit.emplace_back(op);
7049 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7054 TypeSwitch<Operation *>(op)
7055 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7056 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7059 .Case<HWModuleGeneratedOp>(
7060 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7061 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7062 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7063 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7064 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7065 .Case<TypeScopeOp>([&](
auto typedecls) {
7066 ModuleEmitter(state).emitStatement(typedecls);
7068 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7070 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7071 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7072 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7073 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7074 .Default([&](
auto *op) {
7075 state.encounteredError =
true;
7076 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7083 llvm::formatted_raw_ostream &os,
7084 StringAttr fileName,
bool parallelize) {
7089 parallelize &=
context->isMultithreadingEnabled();
7100 size_t lineOffset = 0;
7101 for (
auto &entry : thingsToEmit) {
7102 entry.verilogLocs.setStream(os);
7103 if (
auto *op = entry.getOperation()) {
7108 state.addVerilogLocToOps(lineOffset, fileName);
7110 os << entry.getStringData();
7115 if (state.encounteredError)
7133 SmallString<256> buffer;
7134 llvm::raw_svector_ostream tmpStream(buffer);
7135 llvm::formatted_raw_ostream rs(tmpStream);
7143 if (state.encounteredError)
7148 for (
auto &entry : thingsToEmit) {
7151 auto *op = entry.getOperation();
7153 auto lineOffset = os.getLine() + 1;
7154 os << entry.getStringData();
7158 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7161 entry.verilogLocs.setStream(os);
7168 state.addVerilogLocToOps(0, fileName);
7169 if (state.encounteredError) {
7188 module.emitWarning()
7189 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7190 "created only at SplitExportVerilog";
7199 for (
const auto &it : emitter.
files) {
7200 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7201 "\" ----- 8< -----\n\n");
7207 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7208 "\" ----- 8< -----\n\n");
7209 for (
auto &name : it.second)
7210 contents += name.str() +
"\n";
7211 list.emplace_back(contents);
7214 llvm::formatted_raw_ostream rs(os);
7218 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7225 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7227 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7228 if (failed(failableParallelForEach(
7229 module->getContext(), modulesToPrepare,
7230 [&](
auto op) { return prepareHWModule(op, options); })))
7237struct ExportVerilogPass
7238 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7239 ExportVerilogPass(raw_ostream &os) : os(os) {}
7240 void runOnOperation()
override {
7242 mlir::OpPassManager preparePM(
"builtin.module");
7243 preparePM.addPass(createLegalizeAnonEnums());
7244 auto &modulePM = preparePM.nestAny();
7245 modulePM.addPass(createPrepareForEmission());
7246 if (failed(runPipeline(preparePM, getOperation())))
7247 return signalPassFailure();
7250 return signalPassFailure();
7257struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7258 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7259 : ExportVerilogPass{*os} {
7260 owned = std::move(os);
7264 std::unique_ptr<llvm::raw_ostream> owned;
7268std::unique_ptr<mlir::Pass>
7270 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7273std::unique_ptr<mlir::Pass>
7275 return std::make_unique<ExportVerilogPass>(os);
7286static std::unique_ptr<llvm::ToolOutputFile>
7290 SmallString<128> outputFilename(dirname);
7292 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7295 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7297 emitter.
designOp.emitError(
"cannot create output directory \"")
7298 << outputDir <<
"\": " << error.message();
7304 std::string errorMessage;
7305 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7307 emitter.
designOp.emitError(errorMessage);
7324 llvm::formatted_raw_ostream rs(output->os());
7330 StringAttr::get(fileName.getContext(), output->getFilename()),
7336 StringRef dirname) {
7347 bool insertSuccess =
7349 .insert({StringAttr::get(module.getContext(),
circtHeader),
7355 if (!insertSuccess) {
7356 module.emitError() << "tried to emit a heder to " << circtHeader
7357 << ", but the file is used as an output too.";
7363 parallelForEach(module->getContext(), emitter.
files.begin(),
7364 emitter.
files.end(), [&](
auto &it) {
7365 createSplitOutputFile(it.first, it.second, dirname,
7370 SmallString<128> filelistPath(dirname);
7371 llvm::sys::path::append(filelistPath,
"filelist.f");
7373 std::string errorMessage;
7374 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7376 module->emitError(errorMessage);
7380 for (
const auto &it : emitter.
files) {
7381 if (it.second.addToFilelist)
7382 output->os() << it.first.str() <<
"\n";
7391 for (
auto &name : it.second)
7392 output->os() << name.str() <<
"\n";
7401 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7403 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7404 if (failed(failableParallelForEach(
7405 module->getContext(), modulesToPrepare,
7406 [&](
auto op) { return prepareHWModule(op, options); })))
7414struct ExportSplitVerilogPass
7415 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7416 ExportSplitVerilogPass(StringRef directory) {
7417 directoryName = directory.str();
7419 void runOnOperation()
override {
7421 mlir::OpPassManager preparePM(
"builtin.module");
7424 modulePM.addPass(createPrepareForEmission());
7425 if (failed(runPipeline(preparePM, getOperation())))
7426 return signalPassFailure();
7429 return signalPassFailure();
7434std::unique_ptr<mlir::Pass>
7436 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.