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"
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<PackageExternOp>([](PackageExternOp op) {
206 return op.getVerilogName().value_or(op.getSymName());
208 .Case<InterfaceOp>([&](InterfaceOp op) {
211 .Case<InterfaceSignalOp>(
212 [&](InterfaceSignalOp op) {
return op.getSymName(); })
213 .Case<InterfaceModportOp>(
214 [&](InterfaceModportOp op) {
return op.getSymName(); })
215 .Case<GenerateOp>([](GenerateOp op) {
return op.getSymName(); })
216 .Default([&](Operation *op) {
217 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
218 return attr.getValue();
219 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
220 return attr.getValue();
221 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
222 return attr.getValue();
223 if (
auto symbol = dyn_cast<mlir::SymbolOpInterface>(op))
224 return symbol.getName();
225 return StringRef(
"");
230template <
typename PPS>
232 os <<
"/*Zero width*/ 1\'b0";
237 auto hml = cast<HWModuleLike>(module);
238 return hml.getPort(portArgNum).getVerilogName();
243 auto hml = cast<HWModuleLike>(module);
244 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
245 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
246 if (
auto updatedName =
attrs.getAs<StringAttr>(
"hw.verilogName"))
247 return updatedName.getValue();
248 return hml.getHWModuleType().getPortName(pId);
257 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
258 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
259 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
260 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
261 UnpackedOpenArrayCastOp, ConcatStrOp>(op))
265 if (isa<verif::ContractOp>(op))
276 SmallVectorImpl<Attribute> &dims, Type type, Location loc,
277 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
278 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
279 if (integer.getWidth() != 1)
280 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
283 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
284 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
285 getTypeDims(dims, array.getElementType(), loc, errorHandler);
289 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
290 dims.push_back(intType.getWidth());
294 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
295 return getTypeDims(dims, inout.getElementType(), loc, errorHandler);
296 if (
auto net = dyn_cast<sv::NetType>(type))
297 return getTypeDims(dims, net.getElementType(), loc, errorHandler);
298 if (
auto var = dyn_cast<sv::VarType>(type))
299 return getTypeDims(dims, var.getElementType(), loc, errorHandler);
300 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
301 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
302 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
303 return getTypeDims(dims, uarray.getElementType(), loc, errorHandler);
304 if (hw::type_isa<InterfaceType, StructType, EnumType, UnionType>(type))
307 errorHandler(loc) <<
"value has an unsupported verilog type " << type;
313 Type a, Type b, Location loc,
314 llvm::function_ref<mlir::InFlightDiagnostic(Location)> errorHandler) {
315 SmallVector<Attribute, 4> aDims;
318 SmallVector<Attribute, 4> bDims;
321 return aDims == bDims;
327 if (
auto intType = dyn_cast<IntegerType>(type))
328 return intType.getWidth() == 0;
329 if (
auto inout = dyn_cast<hw::InOutType>(type))
331 if (
auto net = dyn_cast<sv::NetType>(type))
333 if (
auto var = dyn_cast<sv::VarType>(type))
335 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
336 return uarray.getNumElements() == 0 ||
338 if (
auto array = dyn_cast<hw::ArrayType>(type))
339 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
340 if (
auto structType = dyn_cast<hw::StructType>(type))
341 return llvm::all_of(structType.getElements(),
342 [](
auto elem) { return isZeroBitType(elem.type); });
343 if (
auto enumType = dyn_cast<hw::EnumType>(type))
344 return enumType.getFields().empty();
345 if (
auto unionType = dyn_cast<hw::UnionType>(type))
346 return hw::getBitWidth(unionType) == 0;
358 return TypeSwitch<Type, Type>(type)
359 .Case<InOutType, NetType, VarType>([](
auto valueType) {
362 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
365 .Default([](Type type) {
return type; });
378 return TypeSwitch<Type, bool>(type)
379 .Case<InOutType, VarType, UnpackedArrayType, ArrayType>(
380 [](
auto parentType) {
383 .Case<StructType, UnionType, EnumType, TypeAliasType>(
384 [](
auto) {
return true; })
385 .Default([](
auto) {
return false; });
399 if (
auto name = lhs.getName().compare(rhs.getName()))
401 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
406 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
408 if (lhs.getLine() != rhs.getLine())
409 return lhs.getLine() < rhs.getLine() ? -1 : 1;
410 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
415 Location lhsCallee = lhs.getCallee();
416 Location rhsCallee = rhs.getCallee();
420 Location lhsCaller = lhs.getCaller();
421 Location rhsCaller = rhs.getCaller();
425template <
typename TTargetLoc>
427 auto lhsT = dyn_cast<TTargetLoc>(lhs);
428 auto rhsT = dyn_cast<TTargetLoc>(rhs);
482 SmallPtrSetImpl<Attribute> &locationSet) {
483 llvm::TypeSwitch<Location, void>(loc)
484 .Case<FusedLoc>([&](
auto fusedLoc) {
485 for (
auto subLoc : fusedLoc.getLocations())
488 .Default([&](
auto loc) { locationSet.insert(loc); });
492template <
typename TVector>
494 llvm::array_pod_sort(
495 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
496 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
504 SmallPtrSet<Attribute, 8> locationSet;
505 locationSet.insert(loc);
506 llvm::raw_string_ostream os(
output);
512 const SmallPtrSetImpl<Operation *> &ops) {
516 SmallPtrSet<Attribute, 8> locationSet;
519 llvm::raw_string_ostream os(
output);
528 const SmallPtrSetImpl<Attribute> &locationSet) {
532 llvm::raw_string_ostream sstr(resstr);
539 "other styles must be already handled");
540 os <<
"@[" << resstr <<
"]";
549 const SmallPtrSetImpl<Attribute> &locationSet)
565 bool withName = !loc.getName().empty();
567 os <<
"'" << loc.getName().strref() <<
"'(";
576 os << loc.getFilename().getValue();
577 if (
auto line = loc.getLine()) {
579 if (
auto col = loc.getColumn())
591 StringRef lastFileName;
592 for (
size_t i = 0, e = locVector.size(); i != e;) {
597 auto first = locVector[i];
598 if (first.getFilename() != lastFileName) {
599 lastFileName = first.getFilename();
606 first.getFilename() == locVector[
end].getFilename() &&
607 first.getLine() == locVector[
end].getLine())
612 if (
auto line = first.getLine()) {
614 if (
auto col = first.getColumn())
622 os <<
':' << first.getLine() <<
":{";
624 os << locVector[i++].getColumn();
636 llvm::TypeSwitch<Location, void>(loc)
637 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
639 .Case<mlir::FusedLoc>([&](
auto loc) {
640 SmallPtrSet<Attribute, 8> locationSet;
644 .Default([&](
auto loc) {
656 switch (locationSet.size()) {
667 SmallVector<FileLineColLoc, 8> flcLocs;
668 SmallVector<Attribute, 8> otherLocs;
669 flcLocs.reserve(locationSet.size());
670 otherLocs.reserve(locationSet.size());
671 for (Attribute loc : locationSet) {
672 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
673 flcLocs.push_back(flcLoc);
675 otherLocs.push_back(loc);
686 size_t sstrSize =
os.tell();
687 bool emittedAnything =
false;
688 auto recheckEmittedSomething = [&]() {
689 size_t currSize =
os.tell();
690 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
691 emittedAnything |= emittedSomethingSinceLastCheck;
693 return emittedSomethingSinceLastCheck;
702 if (recheckEmittedSomething()) {
704 recheckEmittedSomething();
710 if (emittedAnything && !flcLocs.empty())
715 llvm::raw_string_ostream &
os;
727 if (isa<BlockArgument>(v))
736 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
741 if (v.getDefiningOp<ReadInterfaceSignalOp>())
754 if (
auto cast = dyn_cast<BitcastOp>(op))
755 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
757 [&](Location loc) { return emitError(loc); })) {
760 if (op->hasOneUse() &&
761 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
769 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
775 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
779 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
780 if (verbatim.getFormatString().size() > 32)
785 for (
auto &use : op->getUses()) {
786 auto *user = use.getOwner();
796 StructInjectOp, StructExplodeOp, UnionExtractOp,
797 IndexedPartSelectOp>(user))
798 if (use.getOperandNumber() == 0 &&
809 auto usedInExprControl = [user, &use]() {
810 return TypeSwitch<Operation *, bool>(user)
811 .Case<ltl::ClockOp>([&](
auto clockOp) {
813 return clockOp.getClock() == use.get();
815 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
816 sv::CoverConcurrentOp>(
817 [&](
auto op) {
return op.getClock() == use.get(); })
818 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
819 sv::CoverPropertyOp>([&](
auto op) {
820 return op.getDisable() == use.get() || op.getClock() == use.get();
822 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
827 .Default([](
auto) {
return false; });
830 if (!usedInExprControl())
834 auto read = dyn_cast<ReadInOutOp>(op);
837 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
848 unsigned numStatements = 0;
849 block.walk([&](Operation *op) {
851 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
852 return WalkResult::advance();
854 TypeSwitch<Operation *, unsigned>(op)
855 .Case<VerbatimOp>([&](
auto) {
861 .Case<IfOp>([&](
auto) {
872 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
873 .Case<OutputOp>([&](OutputOp oop) {
876 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
877 Operation *op = operand.getDefiningOp();
878 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
881 .Default([](
auto) {
return 1; });
882 if (numStatements > 1)
883 return WalkResult::interrupt();
884 return WalkResult::advance();
886 if (numStatements == 0)
888 if (numStatements == 1)
898 if (op->getResult(0).use_empty())
903 if (op->hasOneUse() &&
904 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
905 *op->getUsers().begin()))
927 for (
auto &op : *elseBlock) {
928 if (
auto opIf = dyn_cast<IfOp>(op)) {
945template <
typename PPS>
947 enum Container { NoContainer, InComment, InAttr };
948 Container currentContainer = NoContainer;
950 auto closeContainer = [&] {
951 if (currentContainer == NoContainer)
953 if (currentContainer == InComment)
955 else if (currentContainer == InAttr)
957 ps << PP::end << PP::end;
959 currentContainer = NoContainer;
962 bool isFirstContainer =
true;
963 auto openContainer = [&](Container newContainer) {
964 assert(newContainer != NoContainer);
965 if (currentContainer == newContainer)
969 if (!isFirstContainer)
970 ps << (mayBreak ? PP::space : PP::nbsp);
971 isFirstContainer =
false;
974 if (newContainer == InComment)
976 else if (newContainer == InAttr)
978 currentContainer = newContainer;
986 ps.scopedBox(PP::cbox0, [&]() {
987 for (
auto attr :
attrs.getAsRange<SVAttributeAttr>()) {
988 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
990 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
992 if (attr.getExpression())
993 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
1002 if (
auto *op = val.getDefiningOp())
1005 if (
auto port = dyn_cast<BlockArgument>(val)) {
1007 auto *parent = port.getParentBlock()->getParentOp();
1008 if (isa<ForOp, GenerateForOp>(parent))
1009 return parent->getAttrOfType<StringAttr>(
"hw.verilogName");
1011 port.getArgNumber());
1013 assert(
false &&
"unhandled value");
1025class VerilogEmitterState {
1027 explicit VerilogEmitterState(ModuleOp designOp,
1028 const SharedEmitterState &shared,
1029 const LoweringOptions &options,
1030 const HWSymbolCache &symbolCache,
1031 const GlobalNameTable &globalNames,
1033 llvm::formatted_raw_ostream &os,
1034 StringAttr fileName, OpLocMap &verilogLocMap)
1035 : designOp(designOp), shared(shared), options(options),
1036 symbolCache(symbolCache), globalNames(globalNames),
1037 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1038 pp(os, options.getEmittedLineLength().value_or(0)), fileName(fileName) {
1039 pp.setListener(&saver);
1044 const SharedEmitterState &shared;
1047 const LoweringOptions &options;
1050 const HWSymbolCache &symbolCache;
1054 const GlobalNameTable &globalNames;
1062 llvm::formatted_raw_ostream &os;
1064 bool encounteredError =
false;
1073 bool pendingNewline =
false;
1076 OpLocMap &verilogLocMap;
1079 PrintEventAndStorageListener<OpLocMap, std::pair<Operation *, bool>> saver =
1080 PrintEventAndStorageListener<OpLocMap, std::pair<Operation *, bool>>(
1087 StringAttr fileName;
1093 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1094 verilogLocMap.updateIRWithLoc(lineOffset, fileName,
1095 shared.designOp->getContext());
1096 verilogLocMap.clear();
1100 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1101 void operator=(
const VerilogEmitterState &) =
delete;
1114using CallbackDataTy = std::pair<Operation *, bool>;
1118 VerilogEmitterState &state;
1121 TokenStreamWithCallback<OpLocMap, CallbackDataTy> ps;
1123 explicit EmitterBase(VerilogEmitterState &state)
1125 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1127 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1128 state.encounteredError =
true;
1129 return op->emitError(message);
1132 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1133 state.encounteredError =
true;
1134 return op->emitOpError(message);
1137 InFlightDiagnostic emitError(Location loc,
const Twine &message =
"") {
1138 state.encounteredError =
true;
1139 return mlir::emitError(loc, message);
1142 void emitLocationImpl(llvm::StringRef location) {
1145 ps << PP::neverbreak;
1146 if (!location.empty())
1147 ps <<
"\t// " << location;
1150 void emitLocationInfo(Location loc) {
1152 LocationEmitter(state.options.locationInfoStyle, loc).strref());
1158 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1160 LocationEmitter(state.options.locationInfoStyle, ops).strref());
1161 setPendingNewline();
1164 template <
typename PPS>
1165 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1166 llvm::function_ref<
void(Value)> operandEmitter,
1167 ArrayAttr symAttrs);
1173 void emitComment(StringAttr comment);
1177 void emitPendingNewlineIfNeeded() {
1178 if (state.pendingNewline) {
1179 state.pendingNewline =
false;
1183 void setPendingNewline() {
1184 assert(!state.pendingNewline);
1185 state.pendingNewline =
true;
1188 void startStatement() { emitPendingNewlineIfNeeded(); }
1191 void operator=(
const EmitterBase &) =
delete;
1192 EmitterBase(
const EmitterBase &) =
delete;
1196template <
typename PPS>
1197void EmitterBase::emitTextWithSubstitutions(
1198 PPS &ps, StringRef
string, Operation *op,
1199 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1203 auto namify = [&](Attribute sym, HWSymbolCache::Item item) {
1210 if (
auto *itemOp = item.getOp()) {
1211 if (item.hasPort()) {
1215 if (!symOpName.empty())
1217 emitError(itemOp,
"cannot get name for symbol ") << sym;
1219 emitError(op,
"cannot get name for symbol ") << sym;
1221 return StringRef(
"<INVALID>");
1227 unsigned numSymOps = symAttrs.size();
1228 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1231 next =
string.find(
"{{", next);
1232 if (next == StringRef::npos)
1239 while (next <
string.size() &&
isdigit(
string[next]))
1242 if (start == next) {
1246 size_t operandNoLength = next - start;
1249 StringRef fmtOptsStr;
1250 if (
string[next] ==
':') {
1251 size_t startFmtOpts = next + 1;
1252 while (next <
string.size() &&
string[next] !=
'}')
1254 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1258 if (!
string.substr(next).starts_with(
"}}"))
1262 unsigned operandNo = 0;
1263 if (
string.drop_front(start)
1264 .take_front(operandNoLength)
1265 .getAsInteger(10, operandNo)) {
1266 emitError(op,
"operand substitution too large");
1272 auto before =
string.take_front(start - 2);
1273 if (!before.empty())
1274 ps << PPExtString(before);
1278 if (operandNo < op->getNumOperands())
1280 operandEmitter(op->getOperand(operandNo));
1281 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1282 unsigned symOpNum = operandNo - op->getNumOperands();
1283 auto sym = symAttrs[symOpNum];
1284 StringRef symVerilogName;
1285 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1287 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1288 auto namepath = globalRef.getNamepathAttr().getValue();
1289 for (
auto [index, sym] : llvm::enumerate(namepath)) {
1292 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1294 auto innerRef = cast<InnerRefAttr>(sym);
1296 innerRef.getModule(), innerRef.getName());
1297 ps << namify(innerRef, ref);
1300 symVerilogName = namify(sym, symOp);
1303 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1306 symVerilogName = namify(sym, symOp);
1308 if (!symVerilogName.empty())
1309 ps << PPExtString(symVerilogName);
1311 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1315 string =
string.drop_front(next);
1321 while (emitUntilSubstitution())
1325 if (!
string.
empty())
1326 ps << PPExtString(
string);
1329void EmitterBase::emitComment(StringAttr comment) {
1338 lineLength = std::max<size_t>(*lineLength, 3) - 3;
1342 auto ref = comment.getValue();
1344 while (!ref.empty()) {
1345 std::tie(line, ref) = ref.split(
"\n");
1352 if (!lineLength || line.size() <= lineLength) {
1353 ps << PPExtString(line);
1354 setPendingNewline();
1365 auto breakPos = line.rfind(
' ', *lineLength);
1367 if (breakPos == StringRef::npos) {
1368 breakPos = line.find(
' ', *lineLength);
1371 if (breakPos == StringRef::npos)
1372 breakPos = line.size();
1378 ps << PPExtString(line.take_front(breakPos));
1379 setPendingNewline();
1380 breakPos = line.find_first_not_of(
' ', breakPos);
1382 if (breakPos == StringRef::npos)
1385 line = line.drop_front(breakPos);
1395 bool addPrefixUnderScore =
true;
1398 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1402 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1404 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1406 result = StringAttr::get(expr.getContext(), name);
1408 }
else if (
auto *op = expr.getDefiningOp()) {
1410 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1412 result = StringAttr::get(expr.getContext(), name);
1414 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1420 addPrefixUnderScore =
false;
1422 TypeSwitch<Operation *>(op)
1425 .Case([&result](VerbatimExprOp verbatim) {
1426 verbatim.getAsmResultNames([&](Value, StringRef name) {
1427 result = StringAttr::get(verbatim.getContext(), name);
1430 .Case([&result](VerbatimExprSEOp verbatim) {
1431 verbatim.getAsmResultNames([&](Value, StringRef name) {
1432 result = StringAttr::get(verbatim.getContext(), name);
1438 if (
auto operandName =
1441 cast<IntegerType>(extract.getType()).getWidth();
1443 result = StringAttr::get(extract.getContext(),
1444 operandName.strref() +
"_" +
1445 Twine(extract.getLowBit()));
1447 result = StringAttr::get(
1448 extract.getContext(),
1449 operandName.strref() +
"_" +
1450 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1451 Twine(extract.getLowBit()));
1459 if (!result || result.strref().empty())
1463 if (addPrefixUnderScore && result.strref().front() !=
'_')
1464 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1476class ModuleEmitter :
public EmitterBase {
1478 explicit ModuleEmitter(VerilogEmitterState &state)
1479 : EmitterBase(state), currentModuleOp(nullptr),
1483 emitPendingNewlineIfNeeded();
1487 void emitParameters(Operation *module, ArrayAttr params);
1488 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1489 bool emitAsTwoStateType =
false);
1491 void emitHWModule(HWModuleOp module);
1492 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1493 void emitFunc(FuncOp);
1496 void emitStatement(Operation *op);
1497 void emitBind(BindOp op);
1498 void emitBindInterface(BindInterfaceOp op);
1500 void emitSVAttributes(Operation *op);
1503 StringRef getVerilogStructFieldName(StringAttr field) {
1511 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1523 bool printPackedType(Type type, raw_ostream &os, Location loc,
1524 Type optionalAliasType = {},
bool implicitIntType =
true,
1525 bool singleBitDefaultType =
true,
1526 bool emitAsTwoStateType =
false);
1530 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1538 function_ref<InFlightDiagnostic()> emitError);
1541 VerilogPrecedence parenthesizeIfLooserThan,
1542 function_ref<InFlightDiagnostic()> emitError);
1548 Operation *currentModuleOp;
1549 Operation *currentPackage =
nullptr;
1555 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1558 FieldNameResolver fieldNameResolver;
1561 SmallPtrSet<Operation *, 16> assignsInlined;
1572 return TypeSwitch<Type, bool>(type)
1573 .Case<UnpackedArrayType, ArrayType>(
1575 .Case<IntegerType>([](
auto) {
return false; })
1576 .Default([](
auto) {
return true; });
1583 const ModuleEmitter &emitter) {
1584 if (isa<RegOp>(op)) {
1589 cast<InOutType>(op->getResult(0).getType()).getElementType();
1592 while (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(
elementType))
1594 while (
auto arrayType = hw::type_dyn_cast<ArrayType>(
elementType))
1597 if (isa<StructType, UnionType, EnumType, TypeAliasType>(
elementType))
1603 if (isa<sv::WireOp>(op))
1605 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1606 return "localparam";
1609 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1610 return interface.getInterfaceType().getInterface().getValue();
1618 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1620 if (
auto var = dyn_cast<VarOp>(op)) {
1627 if (isProcedural && !stripAutomatic && !var->hasAttr(
"sv.no_automatic"))
1629 :
"automatic logic";
1631 switch (emitter.state.options.varOpDeclStyle) {
1633 return hasExplicitType ?
"var" :
"var logic";
1635 return hasExplicitType ?
"" :
"logic";
1637 return hasExplicitType ?
"" :
"reg";
1641 if (isa<LogicOp>(op)) {
1647 if (isProcedural && !stripAutomatic)
1648 return hasStruct ?
"automatic" :
"automatic logic";
1649 return hasStruct ?
"" :
"logic";
1656 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1659 assert(!emitter.state.options.disallowLocalVariables &&
1660 "automatic variables not allowed");
1664 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1665 :
"automatic logic";
1672static void emitDim(Attribute width, raw_ostream &os, Location loc,
1673 ModuleEmitter &emitter,
bool downTo) {
1675 os <<
"<<invalid type>>";
1678 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1679 if (intAttr.getValue().isZero()) {
1680 os <<
"/*Zero Width*/";
1685 os << (intAttr.getValue().getZExtValue() - 1);
1695 auto typedAttr = dyn_cast<TypedAttr>(width);
1697 emitter.emitError(loc,
"untyped dimension attribute ") << width;
1701 getIntAttr(loc.getContext(), typedAttr.getType(),
1702 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1703 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1707 emitter.printParamValue(width, os, [loc, &emitter]() {
1708 return emitter.emitError(loc,
"invalid parameter in type");
1716static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1717 ModuleEmitter &emitter) {
1718 for (Attribute width : dims) {
1719 emitDim(width, os, loc, emitter,
true);
1724void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1725 SmallVector<Attribute, 4> dims;
1727 [&](Location loc) {
return this->emitError(loc); });
1758 SmallVectorImpl<Attribute> &dims,
1759 bool implicitIntType,
bool singleBitDefaultType,
1760 ModuleEmitter &emitter,
1761 Type optionalAliasType = {},
1762 bool emitAsTwoStateType =
false) {
1763 return TypeSwitch<Type, bool>(type)
1764 .Case<IntegerType>([&](IntegerType integerType) ->
bool {
1765 if (emitAsTwoStateType && dims.empty()) {
1767 if (!typeName.empty()) {
1772 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1774 getInt32Attr(type.getContext(), integerType.getWidth()));
1776 StringRef typeName =
1777 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1778 if (!typeName.empty()) {
1785 return !dims.empty() || !implicitIntType;
1787 .Case<IntType>([&](IntType intType) {
1788 if (!implicitIntType)
1790 dims.push_back(intType.getWidth());
1794 .Case<ArrayType>([&](ArrayType arrayType) {
1795 dims.push_back(arrayType.getSizeAttr());
1797 implicitIntType, singleBitDefaultType,
1799 emitAsTwoStateType);
1801 .Case<InOutType, sv::NetType, sv::VarType>([&](
auto handleType) {
1803 implicitIntType, singleBitDefaultType,
1805 emitAsTwoStateType);
1807 .Case<EnumType>([&](EnumType enumType) {
1808 assert(enumType.getBitWidth().has_value() &&
1809 "enum type must have bitwidth");
1811 if (enumType.getBitWidth() != 32)
1812 os <<
"bit [" << *enumType.getBitWidth() - 1 <<
":0] ";
1814 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1815 llvm::interleaveComma(
1816 enumType.getFields().getAsRange<StringAttr>(), os,
1817 [&](
auto enumerator) {
1818 os << emitter.fieldNameResolver.getEnumFieldName(
1819 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType),
1820 emitter.currentPackage);
1825 .Case<StructType>([&](StructType structType) {
1826 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1827 os <<
"/*Zero Width*/";
1830 os <<
"struct packed {";
1831 for (
auto &element : structType.getElements()) {
1833 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1834 <<
": Zero Width;*/ ";
1837 SmallVector<Attribute, 8> structDims;
1842 {}, emitAsTwoStateType);
1843 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1844 emitter.printUnpackedTypePostfix(element.type, os);
1851 .Case<UnionType>([&](UnionType unionType) {
1852 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1853 os <<
"/*Zero Width*/";
1857 int64_t unionWidth = hw::getBitWidth(unionType);
1859 "SV packed union must have known positive bitwidth");
1860 os <<
"union packed {";
1861 for (
auto &element : unionType.getElements()) {
1863 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1864 <<
": Zero Width;*/ ";
1867 int64_t elementWidth = hw::getBitWidth(element.type);
1870 auto elementOffset =
static_cast<int64_t
>(element.offset);
1871 bool needsPadding = elementWidth < unionWidth || elementOffset > 0;
1872 int64_t prePadding = unionWidth - (elementWidth + elementOffset);
1874 os <<
" struct packed {";
1875 if (prePadding > 0) {
1876 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1877 << prePadding - 1 <<
":0] "
1878 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1882 SmallVector<Attribute, 8> structDims;
1886 true, emitter, {}, emitAsTwoStateType);
1887 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1888 emitter.printUnpackedTypePostfix(element.type, os);
1892 if (elementOffset > 0) {
1893 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1894 << elementOffset - 1 <<
":0] "
1895 <<
"__post_padding_" << element.name.getValue() <<
";";
1897 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1906 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1907 .Case<ModportType>([&](ModportType modportType) {
1908 auto modportAttr = modportType.getModport();
1909 os << modportAttr.getRootReference().getValue() <<
"."
1910 << modportAttr.getNestedReferences().front().getValue();
1913 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1914 os <<
"<<unexpected unpacked array>>";
1915 emitter.emitError(loc,
"Unexpected unpacked array in packed type ")
1919 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1920 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1922 emitter.emitError(loc,
"unresolvable type reference");
1925 if (typedecl.getType() != typeRef.getInnerType()) {
1926 emitter.emitError(loc,
"declared type did not match aliased type");
1930 if (
auto *package = typedecl->getParentOp();
1931 isa<PackageOp, PackageExternOp>(package)) {
1932 if (package != emitter.currentPackage)
1935 os << typedecl.getPreferredName();
1936 emitDims(dims, os, typedecl->getLoc(), emitter);
1939 .Default([&](Type type) {
1940 os <<
"<<invalid type '" << type <<
"'>>";
1941 emitter.emitError(loc,
"value has an unsupported verilog type ")
1958bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1959 Type optionalAliasType,
1960 bool implicitIntType,
1961 bool singleBitDefaultType,
1962 bool emitAsTwoStateType) {
1963 SmallVector<Attribute, 8> packedDimensions;
1965 singleBitDefaultType, *
this, optionalAliasType,
1966 emitAsTwoStateType);
1972void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1973 TypeSwitch<Type, void>(type)
1974 .Case<
InOutType, NetType, VarType>([&](
auto valueType) {
1975 printUnpackedTypePostfix(valueType.getElementType(), os);
1977 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1978 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1979 : state.designOp->getLoc();
1980 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1982 printUnpackedTypePostfix(arrayType.getElementType(), os);
1984 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1986 printUnpackedTypePostfix(arrayType.getElementType(), os);
1988 .Case<InterfaceType>([&](
auto) {
2002ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
2003 function_ref<InFlightDiagnostic()> emitError) {
2004 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
2012ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
2013 VerilogPrecedence parenthesizeIfLooserThan,
2014 function_ref<InFlightDiagnostic()> emitError) {
2015 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
2016 IntegerType intTy = cast<IntegerType>(intAttr.getType());
2017 APInt value = intAttr.getValue();
2021 if (intTy.getWidth() > 32) {
2023 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
2027 if (intTy.isSigned())
2028 os << intTy.getWidth() <<
"'sd";
2030 os << intTy.getWidth() <<
"'d";
2032 value.print(os, intTy.isSigned());
2033 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
2035 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
2037 os.write_escaped(strAttr.getValue());
2039 return {Symbol, IsUnsigned};
2041 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
2043 os << fpAttr.getValueAsDouble();
2044 return {Symbol, IsUnsigned};
2046 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
2047 os << verbatimParam.getValue().getValue();
2048 return {Symbol, IsUnsigned};
2050 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
2053 parameterRef.getName());
2056 return {Symbol, IsUnsigned};
2060 auto expr = dyn_cast<ParamExprAttr>(value);
2062 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
2063 emitError() <<
" = " << value;
2064 return {LowestPrecedence, IsUnsigned};
2067 StringRef operatorStr;
2068 StringRef openStr, closeStr;
2069 VerilogPrecedence subprecedence = LowestPrecedence;
2070 VerilogPrecedence prec;
2071 std::optional<SubExprSignResult> operandSign;
2072 bool isUnary =
false;
2073 bool hasOpenClose =
false;
2075 switch (expr.getOpcode()) {
2077 operatorStr =
" + ";
2078 subprecedence = Addition;
2081 operatorStr =
" * ";
2082 subprecedence = Multiply;
2085 operatorStr =
" & ";
2086 subprecedence = And;
2089 operatorStr =
" | ";
2093 operatorStr =
" ^ ";
2094 subprecedence = Xor;
2097 operatorStr =
" << ";
2098 subprecedence = Shift;
2102 operatorStr =
" >> ";
2103 subprecedence = Shift;
2107 operatorStr =
" >>> ";
2108 subprecedence = Shift;
2109 operandSign = IsSigned;
2112 operatorStr =
" / ";
2113 subprecedence = Multiply;
2114 operandSign = IsUnsigned;
2117 operatorStr =
" / ";
2118 subprecedence = Multiply;
2119 operandSign = IsSigned;
2122 operatorStr =
" % ";
2123 subprecedence = Multiply;
2124 operandSign = IsUnsigned;
2127 operatorStr =
" % ";
2128 subprecedence = Multiply;
2129 operandSign = IsSigned;
2132 openStr =
"$clog2(";
2134 operandSign = IsUnsigned;
2135 hasOpenClose =
true;
2138 case PEO::StrConcat:
2141 hasOpenClose =
true;
2144 subprecedence = LowestPrecedence;
2149 prec = subprecedence;
2152 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2154 assert(isUnary || hasOpenClose ||
2155 !llvm::hasSingleElement(expr.getOperands()));
2162 auto emitOperand = [&](Attribute operand) ->
bool {
2164 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2165 if (operandSign.has_value())
2166 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2169 if (operandSign.has_value()) {
2171 signedness = *operandSign;
2173 return signedness == IsSigned;
2177 if (prec > parenthesizeIfLooserThan)
2186 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2187 for (
auto op : expr.getOperands().drop_front()) {
2190 if (expr.getOpcode() == PEO::Add) {
2191 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2192 const APInt &value = integer.getValue();
2193 if (value.isNegative() && !value.isMinSignedValue()) {
2195 allOperandsSigned &=
2196 emitOperand(IntegerAttr::get(op.getType(), -value));
2203 allOperandsSigned &= emitOperand(op);
2207 if (prec > parenthesizeIfLooserThan) {
2211 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2226class ExprEmitter :
public EmitterBase,
2227 public TypeOpVisitor<ExprEmitter, SubExprInfo>,
2228 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2229 public sv::Visitor<ExprEmitter, SubExprInfo> {
2233 ExprEmitter(ModuleEmitter &emitter,
2234 SmallPtrSetImpl<Operation *> &emittedExprs)
2235 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2237 ExprEmitter(ModuleEmitter &emitter,
2238 SmallPtrSetImpl<Operation *> &emittedExprs,
2240 : EmitterBase(emitter.state), emitter(emitter),
2241 emittedExprs(emittedExprs), buffer(tokens),
2242 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2250 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2251 bool isAssignmentLikeContext) {
2252 assert(localTokens.empty());
2254 ps.scopedBox(PP::ibox0, [&]() {
2257 emitSubExpr(exp, parenthesizeIfLooserThan,
2259 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2261 isAssignmentLikeContext);
2266 if (&buffer.
tokens == &localTokens)
2267 buffer.
flush(state.pp);
2271 friend class TypeOpVisitor<ExprEmitter, SubExprInfo>;
2272 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2273 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2275 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2283 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2284 SubExprSignRequirement signReq = NoRequirement,
2285 bool isSelfDeterminedUnsignedValue =
false,
2286 bool isAssignmentLikeContext =
false);
2290 void emitSVAttributes(Operation *op);
2292 SubExprInfo visitUnhandledExpr(Operation *op);
2293 SubExprInfo visitInvalidComb(Operation *op) {
2294 return dispatchTypeOpVisitor(op);
2296 SubExprInfo visitUnhandledComb(Operation *op) {
2297 return visitUnhandledExpr(op);
2299 SubExprInfo visitInvalidTypeOp(Operation *op) {
2300 return dispatchSVVisitor(op);
2302 SubExprInfo visitUnhandledTypeOp(Operation *op) {
2303 return visitUnhandledExpr(op);
2305 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2308 enum EmitBinaryFlags {
2309 EB_RequireSignedOperands = RequireSigned,
2310 EB_RequireUnsignedOperands = RequireUnsigned,
2311 EB_OperandSignRequirementMask = 0x3,
2316 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2320 EB_ForceResultSigned = 0x8,
2325 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2326 const char *syntax,
unsigned emitBinaryFlags = 0);
2328 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2329 bool resultAlwaysUnsigned =
false);
2332 void emitSubExprIBox2(
2333 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2334 ps.scopedBox(PP::ibox2,
2335 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2340 template <
typename Container,
typename EachFn>
2341 void interleaveComma(
const Container &c, EachFn eachFn) {
2342 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2347 void interleaveComma(ValueRange ops) {
2348 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2365 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2367 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2368 CloseFunc closeFn) {
2370 ps.scopedBox(PP::cbox0, [&]() {
2371 interleaveComma(c, eachFn);
2377 template <
typename OpenFunc,
typename CloseFunc>
2378 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2379 return emitBracedList(
2380 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2384 void emitBracedList(ValueRange ops) {
2385 return emitBracedList(
2386 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2390 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2393 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2394 bool printAsPattern, Operation *op);
2396 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2397 ArrayAttr fieldValues,
bool printAsPattern,
2400 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2402 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2403 SubExprInfo visitSV(GetModportOp op);
2404 SubExprInfo visitSV(SystemFunctionOp op);
2405 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2406 SubExprInfo visitSV(XMROp op);
2407 SubExprInfo visitSV(SFormatFOp op);
2408 SubExprInfo visitSV(XMRRefOp op);
2409 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2410 SubExprInfo visitSV(VerbatimExprOp op) {
2411 return visitVerbatimExprOp(op, op.getSymbols());
2413 SubExprInfo visitSV(VerbatimExprSEOp op) {
2414 return visitVerbatimExprOp(op, op.getSymbols());
2416 SubExprInfo visitSV(MacroRefExprOp op);
2417 SubExprInfo visitSV(MacroRefExprSEOp op);
2418 template <
typename MacroTy>
2419 SubExprInfo emitMacroCall(MacroTy op);
2421 SubExprInfo visitSV(ConstantXOp op);
2422 SubExprInfo visitSV(ConstantZOp op);
2423 SubExprInfo visitSV(ConstantStrOp op);
2424 SubExprInfo visitSV(ConcatStrOp op);
2426 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2427 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2429 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2433 SubExprInfo visitSV(ReadInOutOp op) {
2434 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2435 emitSVAttributes(op);
2438 SubExprInfo visitSV(ArrayIndexInOutOp op);
2439 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2440 SubExprInfo visitSV(IndexedPartSelectOp op);
2441 SubExprInfo visitSV(StructFieldInOutOp op);
2444 SubExprInfo visitSV(SampledOp op);
2447 SubExprInfo visitSV(TimeOp op);
2448 SubExprInfo visitSV(STimeOp op);
2451 using TypeOpVisitor::visitTypeOp;
2452 SubExprInfo visitTypeOp(ConstantOp op);
2453 SubExprInfo visitTypeOp(AggregateConstantOp op);
2454 SubExprInfo visitTypeOp(BitcastOp op);
2455 SubExprInfo visitTypeOp(ParamValueOp op);
2456 SubExprInfo visitTypeOp(ArraySliceOp op);
2457 SubExprInfo visitTypeOp(ArrayGetOp op);
2458 SubExprInfo visitTypeOp(ArrayCreateOp op);
2459 SubExprInfo visitTypeOp(ArrayConcatOp op);
2460 SubExprInfo visitTypeOp(StructCreateOp op);
2461 SubExprInfo visitTypeOp(StructExtractOp op);
2462 SubExprInfo visitTypeOp(StructInjectOp op);
2463 SubExprInfo visitTypeOp(UnionCreateOp op);
2464 SubExprInfo visitTypeOp(UnionExtractOp op);
2465 SubExprInfo visitTypeOp(EnumCmpOp op);
2466 SubExprInfo visitTypeOp(EnumConstantOp op);
2469 using CombinationalVisitor::visitComb;
2470 SubExprInfo visitComb(MuxOp op);
2471 SubExprInfo visitComb(ReverseOp op);
2472 SubExprInfo visitComb(AddOp op) {
2473 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2474 return emitBinary(op, Addition,
"+");
2476 SubExprInfo visitComb(SubOp op) {
return emitBinary(op, Addition,
"-"); }
2477 SubExprInfo visitComb(MulOp op) {
2478 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2479 return emitBinary(op, Multiply,
"*");
2481 SubExprInfo visitComb(DivUOp op) {
2482 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2484 SubExprInfo visitComb(DivSOp op) {
2485 return emitBinary(op, Multiply,
"/",
2486 EB_RequireSignedOperands | EB_ForceResultSigned);
2488 SubExprInfo visitComb(ModUOp op) {
2489 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2491 SubExprInfo visitComb(ModSOp op) {
2492 return emitBinary(op, Multiply,
"%",
2493 EB_RequireSignedOperands | EB_ForceResultSigned);
2495 SubExprInfo visitComb(ShlOp op) {
2496 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2498 SubExprInfo visitComb(ShrUOp op) {
2500 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2502 SubExprInfo visitComb(ShrSOp op) {
2505 return emitBinary(op, Shift,
">>>",
2506 EB_RequireSignedOperands | EB_ForceResultSigned |
2507 EB_RHS_UnsignedWithSelfDeterminedWidth);
2509 SubExprInfo visitComb(AndOp op) {
2510 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2511 return emitBinary(op, And,
"&");
2513 SubExprInfo visitComb(OrOp op) {
2514 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2515 return emitBinary(op, Or,
"|");
2517 SubExprInfo visitComb(XorOp op) {
2518 if (op.isBinaryNot())
2519 return emitUnary(op,
"~");
2520 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2521 return emitBinary(op, Xor,
"^");
2526 SubExprInfo visitComb(ParityOp op) {
return emitUnary(op,
"^",
true); }
2528 SubExprInfo visitComb(ReplicateOp op);
2529 SubExprInfo visitComb(ConcatOp op);
2530 SubExprInfo visitComb(ExtractOp op);
2531 SubExprInfo visitComb(ICmpOp op);
2533 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2534 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2535 "not printed within an assignment-like context");
2536 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2537 "supposed to spill such expressions";
2541 SubExprInfo printStructCreate(
2542 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2543 llvm::function_ref<
void(
const hw::detail::FieldInfo &,
unsigned)> fieldFn,
2544 bool printAsPattern, Operation *op);
2547 ModuleEmitter &emitter;
2554 SubExprSignRequirement signPreference = NoRequirement;
2558 SmallPtrSetImpl<Operation *> &emittedExprs;
2561 SmallVector<Token> localTokens;
2567 TokenStreamWithCallback<OpLocMap, CallbackDataTy, BufferingPP> ps;
2575 bool isAssignmentLikeContext =
false;
2579SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2581 unsigned emitBinaryFlags) {
2583 emitError(op,
"SV attributes emission is unimplemented for the op");
2594 if (emitBinaryFlags & EB_ForceResultSigned)
2595 ps <<
"$signed(" << PP::ibox0;
2596 auto operandSignReq =
2597 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2598 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2601 auto lhsSpace = (prec == VerilogPrecedence::Comparison ||
2602 prec == VerilogPrecedence::Equality)
2606 ps << lhsSpace << syntax << PP::nbsp;
2613 auto rhsPrec = prec;
2614 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2615 rhsPrec = VerilogPrecedence(prec - 1);
2620 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2621 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2622 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2623 operandSignReq = NoRequirement;
2626 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2627 rhsIsUnsignedValueWithSelfDeterminedWidth);
2631 SubExprSignResult signedness = IsUnsigned;
2632 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2633 signedness = IsSigned;
2635 if (emitBinaryFlags & EB_ForceResultSigned) {
2636 ps << PP::end <<
")";
2637 signedness = IsSigned;
2641 return {prec, signedness};
2644SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2645 bool resultAlwaysUnsigned) {
2647 emitError(op,
"SV attributes emission is unimplemented for the op");
2650 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2654 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2655 resultAlwaysUnsigned ? IsUnsigned : signedness};
2660void ExprEmitter::emitSVAttributes(Operation *op) {
2679 if (constant && constant.getValue().isZero())
2680 return concat.getOperand(1);
2690SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2691 VerilogPrecedence parenthesizeIfLooserThan,
2692 SubExprSignRequirement signRequirement,
2693 bool isSelfDeterminedUnsignedValue,
2694 bool isAssignmentLikeContext) {
2696 if (
auto result = dyn_cast<OpResult>(exp))
2697 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2698 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2699 parenthesizeIfLooserThan, signRequirement,
2700 isSelfDeterminedUnsignedValue,
2701 isAssignmentLikeContext);
2705 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2710 auto *op = exp.getDefiningOp();
2714 if (!shouldEmitInlineExpr) {
2717 if (signRequirement == RequireSigned) {
2719 return {Symbol, IsSigned};
2723 return {Symbol, IsUnsigned};
2726 unsigned subExprStartIndex = buffer.
tokens.size();
2728 ps.addCallback({op,
true});
2729 llvm::scope_exit done([&]() {
2731 ps.addCallback({op,
false});
2737 signPreference = signRequirement;
2739 bool bitCastAdded =
false;
2742 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2743 ps.addAsString(inType.getWidth());
2744 ps <<
"'(" << PP::ibox0;
2745 bitCastAdded =
true;
2749 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2750 isAssignmentLikeContext);
2751 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2755 auto addPrefix = [&](StringToken &&t) {
2757 buffer.
tokens.insert(buffer.
tokens.begin() + subExprStartIndex,
2759 buffer.
tokens.insert(buffer.
tokens.begin() + subExprStartIndex, t);
2761 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2762 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2763 addPrefix(StringToken(
"$signed("));
2765 expInfo.signedness = IsSigned;
2766 expInfo.precedence = Selection;
2767 }
else if (signRequirement == RequireUnsigned &&
2768 expInfo.signedness == IsSigned) {
2769 addPrefix(StringToken(
"$unsigned("));
2771 expInfo.signedness = IsUnsigned;
2772 expInfo.precedence = Selection;
2773 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2777 addPrefix(StringToken(
"("));
2780 expInfo.precedence = Selection;
2787 emittedExprs.insert(exp.getDefiningOp());
2791SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2795 emitBracedList(SmallVector<Value>(op.getMultiple(), op.getOperand()));
2796 return {Symbol, IsUnsigned};
2799 auto openFn = [&]() {
2801 ps.addAsString(op.getMultiple());
2804 auto closeFn = [&]() { ps <<
"}}"; };
2808 if (
auto concatOp = op.getOperand().getDefiningOp<ConcatOp>()) {
2809 if (op.getOperand().hasOneUse()) {
2810 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2811 return {Symbol, IsUnsigned};
2814 emitBracedList(op.getOperand(), openFn, closeFn);
2815 return {Symbol, IsUnsigned};
2818SubExprInfo ExprEmitter::visitComb(ConcatOp op) {
2819 emitBracedList(op.getOperands());
2820 return {Symbol, IsUnsigned};
2823SubExprInfo ExprEmitter::visitTypeOp(BitcastOp op) {
2827 Type toType = op.getType();
2829 toType, op.getInput().getType(), op.getLoc(),
2830 [&](Location loc) { return emitter.emitError(loc,
""); })) {
2832 ps.invokeWithStringOS(
2833 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2836 return emitSubExpr(op.getInput(), LowestPrecedence);
2839SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2840 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2841 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2842 SubExprSignRequirement signop[] = {
2844 NoRequirement, NoRequirement,
2846 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2848 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2850 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2852 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2853 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2856 if (op.isEqualAllOnes())
2857 return emitUnary(op,
"&",
true);
2860 if (op.isNotEqualZero())
2861 return emitUnary(op,
"|",
true);
2863 VerilogPrecedence precedence = Comparison;
2864 switch (op.getPredicate()) {
2865 case ICmpPredicate::eq:
2866 case ICmpPredicate::ne:
2867 case ICmpPredicate::ceq:
2868 case ICmpPredicate::cne:
2869 case ICmpPredicate::weq:
2870 case ICmpPredicate::wne:
2871 precedence = Equality;
2874 precedence = Comparison;
2877 auto result = emitBinary(op, precedence, symop[pred], signop[pred]);
2881 result.signedness = IsUnsigned;
2885SubExprInfo ExprEmitter::visitComb(ExtractOp op) {
2887 emitError(op,
"SV attributes emission is unimplemented for the op");
2889 unsigned loBit = op.getLowBit();
2890 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2892 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2893 assert((x.precedence == Symbol ||
2895 "should be handled by isExpressionUnableToInline");
2900 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2904 ps.addAsString(hiBit);
2905 if (hiBit != loBit) {
2907 ps.addAsString(loBit);
2910 return {Unary, IsUnsigned};
2913SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2915 emitError(op,
"SV attributes emission is unimplemented for the op");
2917 auto decl = op.getReferencedDecl(state.symbolCache);
2920 return {Selection, IsUnsigned};
2923SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2925 emitError(op,
"SV attributes emission is unimplemented for the op");
2927 ps <<
"$" << PPExtString(op.getFnName()) <<
"(";
2928 ps.scopedBox(PP::ibox0, [&]() {
2930 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2931 [&]() { ps <<
"," << PP::space; });
2934 return {Symbol, IsUnsigned};
2937SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2939 emitError(op,
"SV attributes emission is unimplemented for the op");
2941 auto decl = op.getReferencedDecl(state.symbolCache);
2945 return {Selection, IsUnsigned};
2948SubExprInfo ExprEmitter::visitSV(XMROp op) {
2950 emitError(op,
"SV attributes emission is unimplemented for the op");
2952 if (op.getIsRooted())
2954 for (
auto s : op.getPath())
2955 ps << PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2956 ps << PPExtString(op.getTerminal());
2957 return {Selection, IsUnsigned};
2962SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2964 emitError(op,
"SV attributes emission is unimplemented for the op");
2967 auto globalRef = op.getReferencedPath(&state.symbolCache);
2968 auto namepath = globalRef.getNamepathAttr().getValue();
2969 auto *
module = state.symbolCache.getDefinition(
2970 cast<InnerRefAttr>(namepath.front()).getModule());
2972 for (
auto sym : namepath) {
2974 auto innerRef = cast<InnerRefAttr>(sym);
2976 innerRef.getName());
2977 if (ref.hasPort()) {
2983 auto leaf = op.getVerbatimSuffixAttr();
2984 if (leaf && leaf.size())
2985 ps << PPExtString(leaf);
2986 return {Selection, IsUnsigned};
2989SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2991 emitError(op,
"SV attributes emission is unimplemented for the op");
2993 emitTextWithSubstitutions(
2994 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2995 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2997 return {Unary, IsUnsigned};
3000template <
typename MacroTy>
3001SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
3003 emitError(op,
"SV attributes emission is unimplemented for the op");
3006 auto macroOp = op.getReferencedMacro(&state.symbolCache);
3007 assert(macroOp &&
"Invalid IR");
3009 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
3010 ps <<
"`" << PPExtString(name);
3011 if (!op.getInputs().empty()) {
3013 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
3014 emitExpression(val, LowestPrecedence, false);
3018 return {LowestPrecedence, IsUnsigned};
3021SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
3022 return emitMacroCall(op);
3025SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
3026 return emitMacroCall(op);
3029SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
3031 emitError(op,
"SV attributes emission is unimplemented for the op");
3033 ps.addAsString(op.getWidth());
3035 return {Unary, IsUnsigned};
3038SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
3040 emitError(op,
"SV attributes emission is unimplemented for the op");
3042 ps.writeQuotedEscaped(op.getStr());
3043 return {Symbol, IsUnsigned};
3046SubExprInfo ExprEmitter::visitSV(ConcatStrOp op) {
3048 emitError(op,
"SV attributes emission is unimplemented for the op");
3052 emitBracedList(op.getInputs());
3053 return {Symbol, IsUnsigned};
3056SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
3058 emitError(op,
"SV attributes emission is unimplemented for the op");
3060 ps.addAsString(op.getWidth());
3062 return {Unary, IsUnsigned};
3065SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
3066 bool isNegated =
false;
3069 if (signPreference == RequireSigned && value.isNegative() &&
3070 !value.isMinSignedValue()) {
3075 ps.addAsString(type.getWidth());
3079 if (signPreference == RequireSigned)
3085 SmallString<32> valueStr;
3087 (-value).toStringUnsigned(valueStr, 16);
3089 value.toStringUnsigned(valueStr, 16);
3092 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
3095SubExprInfo ExprEmitter::visitTypeOp(ConstantOp op) {
3097 emitError(op,
"SV attributes emission is unimplemented for the op");
3099 auto value = op.getValue();
3103 if (value.getBitWidth() == 0) {
3104 emitOpError(op,
"will not emit zero width constants in the general case");
3105 ps <<
"<<unsupported zero width constant: "
3106 << PPExtString(op->getName().getStringRef()) <<
">>";
3107 return {Unary, IsUnsigned};
3110 return printConstantScalar(value, hw::type_cast<IntegerType>(op.getType()));
3113void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
3114 bool printAsPattern, Operation *op) {
3115 if (printAsPattern && !isAssignmentLikeContext)
3116 emitAssignmentPatternContextError(op);
3117 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3120 elementValues, [&]() { ps << openDelim; },
3121 [&](Attribute elementValue) {
3122 printConstantAggregate(elementValue,
elementType, op);
3124 [&]() { ps <<
"}"; });
3127void ExprEmitter::printConstantStruct(
3128 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3129 bool printAsPattern, Operation *op) {
3130 if (printAsPattern && !isAssignmentLikeContext)
3131 emitAssignmentPatternContextError(op);
3138 auto fieldRange = llvm::make_filter_range(
3139 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3144 if (printAsPattern) {
3146 fieldRange, [&]() { ps <<
"'{"; },
3147 [&](
const auto &fieldAndValue) {
3148 ps.scopedBox(PP::ibox2, [&]() {
3149 const auto &[field, value] = fieldAndValue;
3150 ps << PPExtString(emitter.getVerilogStructFieldName(field.name))
3151 <<
":" << PP::space;
3152 printConstantAggregate(value, field.type, op);
3155 [&]() { ps <<
"}"; });
3158 fieldRange, [&]() { ps <<
"{"; },
3159 [&](
const auto &fieldAndValue) {
3160 ps.scopedBox(PP::ibox2, [&]() {
3161 const auto &[field, value] = fieldAndValue;
3162 printConstantAggregate(value, field.type, op);
3165 [&]() { ps <<
"}"; });
3169void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3172 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3173 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3174 isAssignmentLikeContext, op);
3177 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3178 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3182 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3183 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3184 isAssignmentLikeContext, op);
3186 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3187 auto value = cast<IntegerAttr>(attr).getValue();
3188 printConstantScalar(value, intType);
3192 emitOpError(op,
"contains constant of type ")
3193 << type <<
" which cannot be emitted as Verilog";
3196SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3198 emitError(op,
"SV attributes emission is unimplemented for the op");
3202 "zero-bit types not allowed at this point");
3204 printConstantAggregate(op.getFields(), op.getType(), op);
3205 return {Symbol, IsUnsigned};
3208SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3210 emitError(op,
"SV attributes emission is unimplemented for the op");
3212 return ps.invokeWithStringOS([&](
auto &os) {
3213 return emitter.printParamValue(op.getValue(), os, [&]() {
3214 return op->emitOpError(
"invalid parameter use");
3221SubExprInfo ExprEmitter::visitTypeOp(ArraySliceOp op) {
3223 emitError(op,
"SV attributes emission is unimplemented for the op");
3225 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3229 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3231 ps.addAsString(dstWidth);
3233 return {Selection, arrayPrec.signedness};
3236SubExprInfo ExprEmitter::visitTypeOp(ArrayGetOp op) {
3237 emitSubExpr(op.getInput(), Selection);
3242 emitSubExpr(op.getIndex(), LowestPrecedence);
3244 emitSVAttributes(op);
3245 return {Selection, IsUnsigned};
3249SubExprInfo ExprEmitter::visitTypeOp(ArrayCreateOp op) {
3251 emitError(op,
"SV attributes emission is unimplemented for the op");
3253 if (op.isUniform()) {
3255 ps.addAsString(op.getInputs().size());
3257 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3261 op.getInputs(), [&]() { ps <<
"{"; },
3264 emitSubExprIBox2(v);
3267 [&]() { ps <<
"}"; });
3269 return {Unary, IsUnsigned};
3272SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3274 emitError(op,
"SV attributes emission is unimplemented for the op");
3277 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3278 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3279 return {Unary, IsUnsigned};
3282SubExprInfo ExprEmitter::visitTypeOp(ArrayConcatOp op) {
3284 emitError(op,
"SV attributes emission is unimplemented for the op");
3286 emitBracedList(op.getOperands());
3287 return {Unary, IsUnsigned};
3290SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3292 emitError(op,
"SV attributes emission is unimplemented for the op");
3294 auto index = op.getIndex();
3295 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3300 emitSubExpr(index, LowestPrecedence);
3302 return {Selection, arrayPrec.signedness};
3305SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3307 emitError(op,
"SV attributes emission is unimplemented for the op");
3309 auto prec = emitSubExpr(op.getInput(), Selection);
3311 emitSubExpr(op.getBase(), LowestPrecedence);
3312 if (op.getDecrement())
3316 ps.addAsString(op.getWidth());
3318 return {Selection, prec.signedness};
3321SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3323 emitError(op,
"SV attributes emission is unimplemented for the op");
3325 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3327 emitSubExpr(op.getBase(), LowestPrecedence);
3328 if (op.getDecrement())
3332 ps.addAsString(op.getWidth());
3337SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3339 emitError(op,
"SV attributes emission is unimplemented for the op");
3341 auto prec = emitSubExpr(op.getInput(), Selection);
3343 << PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3344 return {Selection, prec.signedness};
3347SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3349 emitError(op,
"SV attributes emission is unimplemented for the op");
3352 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3357SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3359 emitError(op,
"SV attributes emission is unimplemented for the op");
3362 ps.scopedBox(PP::ibox0, [&]() {
3363 ps.writeQuotedEscaped(op.getFormatString());
3370 for (
auto operand : op.getSubstitutions()) {
3371 ps <<
"," << PP::space;
3372 emitSubExpr(operand, LowestPrecedence);
3376 return {Symbol, IsUnsigned};
3379SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3381 emitError(op,
"SV attributes emission is unimplemented for the op");
3384 return {Symbol, IsUnsigned};
3387SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3389 emitError(op,
"SV attributes emission is unimplemented for the op");
3392 return {Symbol, IsUnsigned};
3395SubExprInfo ExprEmitter::visitComb(MuxOp op) {
3409 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3410 ps.scopedBox(PP::ibox0, [&]() {
3411 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3413 ps << BreakToken(1, 2);
3415 emitSVAttributes(op);
3417 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3418 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3420 ps << BreakToken(1, 2) <<
": ";
3422 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3423 return emitSubExpr(op.getFalseValue(), Conditional);
3426 SubExprSignResult signedness = IsUnsigned;
3427 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3428 signedness = IsSigned;
3430 return {Conditional, signedness};
3434SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3436 emitError(op,
"SV attributes emission is unimplemented for the op");
3439 emitSubExpr(op.getInput(), LowestPrecedence);
3442 return {Symbol, IsUnsigned};
3445SubExprInfo ExprEmitter::printStructCreate(
3446 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3447 llvm::function_ref<
void(
const hw::detail::FieldInfo &,
unsigned)> fieldFn,
3448 bool printAsPattern, Operation *op) {
3449 if (printAsPattern && !isAssignmentLikeContext)
3450 emitAssignmentPatternContextError(op);
3453 auto filteredFields = llvm::make_filter_range(
3454 llvm::enumerate(fieldInfos),
3455 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3457 if (printAsPattern) {
3459 filteredFields, [&]() { ps <<
"'{"; },
3460 [&](
const auto &field) {
3461 ps.scopedBox(PP::ibox2, [&]() {
3463 emitter.getVerilogStructFieldName(field.value().name))
3464 <<
":" << PP::space;
3465 fieldFn(field.value(), field.index());
3468 [&]() { ps <<
"}"; });
3471 filteredFields, [&]() { ps <<
"{"; },
3472 [&](
const auto &field) {
3473 ps.scopedBox(PP::ibox2,
3474 [&]() { fieldFn(field.value(), field.index()); });
3476 [&]() { ps <<
"}"; });
3479 return {Selection, IsUnsigned};
3482SubExprInfo ExprEmitter::visitTypeOp(StructCreateOp op) {
3484 emitError(op,
"SV attributes emission is unimplemented for the op");
3488 bool printAsPattern = isAssignmentLikeContext;
3489 StructType structType = op.getType();
3490 return printStructCreate(
3491 structType.getElements(),
3492 [&](
const auto &field,
auto index) {
3493 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3495 isAssignmentLikeContext);
3497 printAsPattern, op);
3500SubExprInfo ExprEmitter::visitTypeOp(StructExtractOp op) {
3502 emitError(op,
"SV attributes emission is unimplemented for the op");
3504 emitSubExpr(op.getInput(), Selection);
3506 << PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3507 return {Selection, IsUnsigned};
3510SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3512 emitError(op,
"SV attributes emission is unimplemented for the op");
3516 bool printAsPattern = isAssignmentLikeContext;
3517 StructType structType = op.getType();
3518 return printStructCreate(
3519 structType.getElements(),
3520 [&](
const auto &field,
auto index) {
3521 if (field.name == op.getFieldNameAttr()) {
3522 emitSubExpr(op.getNewValue(), Selection);
3524 emitSubExpr(op.getInput(), Selection);
3526 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3529 printAsPattern, op);
3532SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3533 ps << PPSaveString(emitter.fieldNameResolver.
getEnumFieldName(op.getField()));
3534 return {Selection, IsUnsigned};
3537SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3539 emitError(op,
"SV attributes emission is unimplemented for the op");
3540 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3543 result.signedness = IsUnsigned;
3547SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3549 emitError(op,
"SV attributes emission is unimplemented for the op");
3553 auto unionWidth = hw::getBitWidth(unionType);
3554 auto &element = unionType.getElements()[op.getFieldIndex()];
3555 auto elementWidth = hw::getBitWidth(element.type);
3558 if (!elementWidth) {
3559 ps.addAsString(unionWidth);
3561 return {Unary, IsUnsigned};
3565 if (elementWidth == unionWidth) {
3566 emitSubExpr(op.getInput(), LowestPrecedence);
3567 return {Unary, IsUnsigned};
3572 auto elementOffset =
static_cast<int64_t
>(element.offset);
3573 int64_t prePadding = unionWidth - elementWidth - elementOffset;
3575 ps.scopedBox(PP::ibox0, [&]() {
3576 if (prePadding > 0) {
3577 ps.addAsString(prePadding);
3578 ps <<
"'h0," << PP::space;
3580 emitSubExpr(op.getInput(), Selection);
3581 if (elementOffset > 0) {
3582 ps <<
"," << PP::space;
3583 ps.addAsString(elementOffset);
3589 return {Unary, IsUnsigned};
3592SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3594 emitError(op,
"SV attributes emission is unimplemented for the op");
3595 emitSubExpr(op.getInput(), Selection);
3598 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3599 auto unionWidth = hw::getBitWidth(unionType);
3600 auto &element = unionType.getElements()[op.getFieldIndex()];
3601 auto elementWidth = hw::getBitWidth(element.type);
3602 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3603 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3608 ps <<
"." << PPExtString(verilogFieldName);
3611 ps <<
"." << PPExtString(verilogFieldName);
3612 return {Selection, IsUnsigned};
3615SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3616 emitOpError(op,
"cannot emit this expression to Verilog");
3617 ps <<
"<<unsupported expr: " << PPExtString(op->getName().getStringRef())
3619 return {Symbol, IsUnsigned};
3635enum class PropertyPrecedence {
3655struct EmittedProperty {
3657 PropertyPrecedence precedence;
3662class PropertyEmitter :
public EmitterBase,
3663 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3667 PropertyEmitter(ModuleEmitter &emitter,
3668 SmallPtrSetImpl<Operation *> &emittedOps)
3669 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3670 PropertyEmitter(ModuleEmitter &emitter,
3671 SmallPtrSetImpl<Operation *> &emittedOps,
3673 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3675 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3679 void emitAssertPropertyDisable(
3680 Value property, Value disable,
3681 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3683 void emitAssertPropertyBody(
3684 Value property, Value disable,
3685 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3687 void emitAssertPropertyBody(
3688 Value property, sv::EventControl event, Value clock, Value disable,
3689 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3694 emitNestedProperty(Value property,
3695 PropertyPrecedence parenthesizeIfLooserThan);
3696 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3697 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3699 EmittedProperty visitUnhandledLTL(Operation *op);
3700 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3701 EmittedProperty visitLTL(ltl::AndOp op);
3702 EmittedProperty visitLTL(ltl::OrOp op);
3703 EmittedProperty visitLTL(ltl::IntersectOp op);
3704 EmittedProperty visitLTL(ltl::DelayOp op);
3705 EmittedProperty visitLTL(ltl::ClockedDelayOp op);
3706 EmittedProperty visitLTL(ltl::ConcatOp op);
3707 EmittedProperty visitLTL(ltl::RepeatOp op);
3708 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3709 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3710 EmittedProperty visitLTL(ltl::NotOp op);
3711 EmittedProperty visitLTL(ltl::ImplicationOp op);
3712 EmittedProperty visitLTL(ltl::UntilOp op);
3713 EmittedProperty visitLTL(ltl::EventuallyOp op);
3714 EmittedProperty visitLTL(ltl::AcceptOnOp op);
3715 EmittedProperty emitAcceptRejectOn(StringRef mnemonic, Value condition,
3717 EmittedProperty visitLTL(ltl::ClockOp op);
3718 EmittedProperty visitLTL(ltl::WeakOp op);
3719 EmittedProperty visitLTL(ltl::StrongOp op);
3721 EmittedProperty emitWeakStrongOp(StringRef mnemonic, Value input);
3722 void emitLTLDelay(int64_t delay, std::optional<int64_t> length);
3723 void emitLTLClockingEvent(ltl::ClockEdge edge, Value clock);
3724 void emitLTLConcat(ValueRange inputs);
3727 ModuleEmitter &emitter;
3732 SmallPtrSetImpl<Operation *> &emittedOps;
3735 SmallVector<Token> localTokens;
3741 TokenStreamWithCallback<OpLocMap, CallbackDataTy, BufferingPP> ps;
3748void PropertyEmitter::emitAssertPropertyDisable(
3749 Value property, Value disable,
3750 PropertyPrecedence parenthesizeIfLooserThan) {
3753 ps <<
"disable iff" << PP::nbsp <<
"(";
3754 ps.scopedBox(PP::ibox2, [&] {
3755 emitNestedProperty(disable, PropertyPrecedence::Unary);
3761 ps.scopedBox(PP::ibox0,
3762 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3768void PropertyEmitter::emitAssertPropertyBody(
3769 Value property, Value disable,
3770 PropertyPrecedence parenthesizeIfLooserThan) {
3771 assert(localTokens.empty());
3773 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3778 if (&buffer.
tokens == &localTokens)
3779 buffer.
flush(state.pp);
3782void PropertyEmitter::emitAssertPropertyBody(
3783 Value property, sv::EventControl event, Value clock, Value disable,
3784 PropertyPrecedence parenthesizeIfLooserThan) {
3785 assert(localTokens.empty());
3788 ps.scopedBox(PP::ibox2, [&] {
3789 ps << PPExtString(stringifyEventControl(event)) << PP::space;
3790 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3796 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3801 if (&buffer.
tokens == &localTokens)
3802 buffer.
flush(state.pp);
3805EmittedProperty PropertyEmitter::emitNestedProperty(
3806 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3816 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3817 ExprEmitter(emitter, emittedOps, buffer.
tokens)
3818 .emitExpression(property, LowestPrecedence,
3820 return {PropertyPrecedence::Symbol};
3823 unsigned startIndex = buffer.
tokens.size();
3824 auto info = dispatchLTLVisitor(property.getDefiningOp());
3829 if (
info.precedence > parenthesizeIfLooserThan) {
3831 buffer.
tokens.insert(buffer.
tokens.begin() + startIndex, BeginToken(0));
3832 buffer.
tokens.insert(buffer.
tokens.begin() + startIndex, StringToken(
"("));
3834 ps << PP::end <<
")";
3836 info.precedence = PropertyPrecedence::Symbol;
3840 emittedOps.insert(property.getDefiningOp());
3844EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3845 emitOpError(op,
"emission as Verilog property or sequence not supported");
3846 ps <<
"<<unsupported: " << PPExtString(op->getName().getStringRef()) <<
">>";
3847 return {PropertyPrecedence::Symbol};
3850EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3852 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3853 return {PropertyPrecedence::Symbol};
3856EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3859 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3860 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3861 return {PropertyPrecedence::And};
3864EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3867 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3868 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3869 return {PropertyPrecedence::Or};
3872EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3876 emitNestedProperty(input, PropertyPrecedence::Intersect);
3878 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3879 return {PropertyPrecedence::Intersect};
3882void PropertyEmitter::emitLTLDelay(int64_t delay,
3883 std::optional<int64_t> length) {
3887 ps.addAsString(delay);
3890 ps.addAsString(delay);
3892 ps.addAsString(delay + *length);
3898 }
else if (delay == 1) {
3902 ps.addAsString(delay);
3908void PropertyEmitter::emitLTLClockingEvent(ltl::ClockEdge edge, Value clock) {
3910 ps.scopedBox(PP::ibox2, [&] {
3911 ps << PPExtString(stringifyClockEdge(edge)) << PP::space;
3912 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3917EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3918 emitLTLDelay(op.getDelay(), op.getLength());
3920 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3921 return {PropertyPrecedence::Concat};
3924EmittedProperty PropertyEmitter::visitLTL(ltl::ClockedDelayOp op) {
3925 emitLTLClockingEvent(op.getEdge(), op.getClock());
3927 emitLTLDelay(op.getDelay(), op.getLength());
3929 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3930 return {PropertyPrecedence::Clocking};
3933void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3934 bool addSeparator =
false;
3935 for (
auto input : inputs) {
3938 if (!input.getDefiningOp<ltl::DelayOp>())
3939 ps <<
"##0" << PP::space;
3941 addSeparator =
true;
3942 emitNestedProperty(input, PropertyPrecedence::Concat);
3946EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3947 emitLTLConcat(op.getInputs());
3948 return {PropertyPrecedence::Concat};
3951EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3952 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3953 if (
auto more = op.getMore()) {
3955 ps.addAsString(op.getBase());
3958 ps.addAsString(op.getBase() + *more);
3962 if (op.getBase() == 0) {
3964 }
else if (op.getBase() == 1) {
3968 ps.addAsString(op.getBase());
3972 return {PropertyPrecedence::Repeat};
3975EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3976 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3978 auto more = op.getMore();
3980 ps.addAsString(op.getBase());
3983 ps.addAsString(op.getBase() + more);
3987 return {PropertyPrecedence::Repeat};
3990EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3991 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3993 auto more = op.getMore();
3995 ps.addAsString(op.getBase());
3998 ps.addAsString(op.getBase() + more);
4002 return {PropertyPrecedence::Repeat};
4005EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
4008 if (
auto accept = op.getInput().getDefiningOp<ltl::AcceptOnOp>())
4009 if (
auto innerNot = accept.getInput().getDefiningOp<ltl::NotOp>())
4010 return emitAcceptRejectOn(accept.getSync() ?
"sync_reject_on"
4012 accept.getCondition(), innerNot.getInput());
4015 if (
auto ev = op.getInput().getDefiningOp<ltl::EventuallyOp>()) {
4016 ps <<
"always" << PP::space;
4017 if (
auto innerNot = ev.getInput().getDefiningOp<ltl::NotOp>()) {
4019 emitNestedProperty(innerNot.getInput(), PropertyPrecedence::Qualifier);
4022 ps <<
"not" << PP::space;
4023 emitNestedProperty(ev.getInput(), PropertyPrecedence::Unary);
4025 return {PropertyPrecedence::Qualifier};
4027 ps <<
"not" << PP::space;
4028 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
4029 return {PropertyPrecedence::Unary};
4035 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
4036 if (!concatOp || concatOp.getInputs().size() < 2)
4038 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
4039 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
4041 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
4042 if (!constOp || !constOp.getValue().isOne())
4044 return concatOp.getInputs().drop_back();
4047EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
4051 emitLTLConcat(range);
4052 ps << PP::space <<
"|=>" << PP::nbsp;
4054 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
4055 ps << PP::space <<
"|->" << PP::nbsp;
4057 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
4058 return {PropertyPrecedence::Implication};
4061EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
4062 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
4063 ps << PP::space <<
"until" << PP::space;
4064 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
4065 return {PropertyPrecedence::Until};
4068EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
4069 ps <<
"s_eventually" << PP::space;
4070 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
4071 return {PropertyPrecedence::Qualifier};
4074EmittedProperty PropertyEmitter::emitAcceptRejectOn(StringRef mnemonic,
4077 ps << mnemonic << PP::nbsp <<
"(";
4078 ps.scopedBox(PP::ibox0, [&] {
4079 emitNestedProperty(condition, PropertyPrecedence::Lowest);
4083 emitNestedProperty(input, PropertyPrecedence::Qualifier);
4084 return {PropertyPrecedence::Qualifier};
4087EmittedProperty PropertyEmitter::visitLTL(ltl::AcceptOnOp op) {
4088 return emitAcceptRejectOn(op.getSync() ?
"sync_accept_on" :
"accept_on",
4089 op.getCondition(), op.getInput());
4092EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
4093 emitLTLClockingEvent(op.getEdge(), op.getClock());
4095 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
4096 return {PropertyPrecedence::Clocking};
4100EmittedProperty PropertyEmitter::emitWeakStrongOp(StringRef mnemonic,
4102 ps << mnemonic << PP::space <<
"(";
4103 ps.scopedBox(PP::ibox2, [&] {
4104 emitNestedProperty(input, PropertyPrecedence::Unary);
4107 return {PropertyPrecedence::Lowest};
4110EmittedProperty PropertyEmitter::visitLTL(ltl::WeakOp op) {
4111 return emitWeakStrongOp(
"weak", op.getInput());
4114EmittedProperty PropertyEmitter::visitLTL(ltl::StrongOp op) {
4115 return emitWeakStrongOp(
"strong", op.getInput());
4125class NameCollector {
4127 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
4131 void collectNames(
Block &block);
4133 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
4134 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
4137 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
4138 ModuleEmitter &moduleEmitter;
4143 static constexpr size_t maxTypeWidthBound = 32;
4148void NameCollector::collectNames(
Block &block) {
4151 for (
auto &op : block) {
4155 if (isa<InstanceOp, InterfaceInstanceOp, FuncCallProceduralOp, FuncCallOp>(
4158 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
4162 for (
auto result : op.getResults()) {
4164 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
4165 SmallString<16> typeString;
4169 llvm::raw_svector_ostream stringStream(typeString);
4171 stringStream, op.getLoc());
4173 if (typeString.size() <= maxTypeWidthBound)
4174 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
4181 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
4182 for (
auto ®ion : op.getRegions()) {
4183 if (!region.empty())
4184 collectNames(region.front());
4198class StmtEmitter :
public EmitterBase,
4199 public hw::StmtVisitor<StmtEmitter, LogicalResult>,
4200 public sv::Visitor<StmtEmitter, LogicalResult>,
4201 public verif::Visitor<StmtEmitter, LogicalResult> {
4205 StmtEmitter(ModuleEmitter &emitter,
const LoweringOptions &options)
4206 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4208 void emitStatement(Operation *op);
4209 void emitStatementBlock(
Block &body);
4212 LogicalResult emitDeclaration(Operation *op);
4215 void collectNamesAndCalculateDeclarationWidths(
Block &block);
4218 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4219 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4220 bool isAssignmentLikeContext =
false);
4221 void emitSVAttributes(Operation *op);
4223 using hw::StmtVisitor<StmtEmitter, LogicalResult>::visitStmt;
4224 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4225 using verif::Visitor<StmtEmitter, LogicalResult>::visitVerif;
4226 friend class hw::StmtVisitor<StmtEmitter, LogicalResult>;
4227 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4228 friend class verif::Visitor<StmtEmitter, LogicalResult>;
4231 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4232 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4233 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4234 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4235 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4236 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4238 LogicalResult visitSV(sv::WireOp op) {
return emitDeclaration(op); }
4239 LogicalResult visitSV(VarOp op) {
return emitDeclaration(op); }
4240 LogicalResult visitSV(RegOp op) {
return emitDeclaration(op); }
4241 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4242 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4243 template <
typename Op>
4245 emitAssignLike(Op op, PPExtString syntax,
4246 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4247 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4248 llvm::function_ref<
void()> emitRHS, PPExtString syntax,
4249 PPExtString postSyntax = PPExtString(
";"),
4250 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4251 LogicalResult visitSV(AssignOp op);
4252 LogicalResult visitSV(VarAssignOp op);
4253 LogicalResult visitSV(BPAssignOp op);
4254 LogicalResult visitSV(PAssignOp op);
4255 LogicalResult visitSV(ForceOp op);
4256 LogicalResult visitSV(ReleaseOp op);
4257 LogicalResult visitSV(AliasOp op);
4258 LogicalResult visitSV(InterfaceInstanceOp op);
4259 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4260 LogicalResult visitStmt(OutputOp op);
4262 LogicalResult visitStmt(InstanceOp op);
4263 void emitInstancePortList(Operation *op, ModulePortInfo &modPortInfo,
4264 ArrayRef<Value> instPortValues);
4266 LogicalResult visitStmt(TypeScopeOp op);
4267 LogicalResult visitStmt(TypedeclOp op);
4268 LogicalResult visitSV(PackageOp op);
4270 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4271 LogicalResult visitSV(OrderedOutputOp op);
4272 LogicalResult visitSV(IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4273 LogicalResult visitSV(IfDefProceduralOp op) {
4274 return emitIfDef(op, op.getCond());
4276 LogicalResult visitSV(IfOp op);
4277 LogicalResult visitSV(AlwaysOp op);
4278 LogicalResult visitSV(AlwaysCombOp op);
4279 LogicalResult visitSV(AlwaysFFOp op);
4280 LogicalResult visitSV(InitialOp op);
4281 LogicalResult visitSV(CaseOp op);
4282 template <
typename OpTy,
typename EmitPrefixFn>
4284 emitFormattedWriteLikeOp(OpTy op, StringRef callee, StringRef formatString,
4285 ValueRange substitutions, EmitPrefixFn emitPrefix);
4286 LogicalResult visitSV(WriteOp op);
4287 LogicalResult visitSV(FWriteOp op);
4288 LogicalResult visitSV(FFlushOp op);
4289 LogicalResult visitSV(FCloseOp op);
4290 LogicalResult visitSV(VerbatimOp op);
4291 LogicalResult visitSV(MacroRefOp op);
4293 LogicalResult emitSimulationControlTask(Operation *op, PPExtString taskName,
4294 std::optional<unsigned> verbosity);
4295 LogicalResult visitSV(StopOp op);
4296 LogicalResult visitSV(FinishOp op);
4297 LogicalResult visitSV(ExitOp op);
4299 LogicalResult emitSeverityMessageTask(Operation *op, PPExtString taskName,
4300 std::optional<unsigned> verbosity,
4302 ValueRange operands);
4305 template <
typename OpTy>
4306 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4307 return emitSeverityMessageTask(op, PPExtString(taskName), {},
4308 op.getMessageAttr(), op.getSubstitutions());
4312 template <
typename OpTy>
4313 LogicalResult emitFatalMessageOp(OpTy op) {
4314 return emitSeverityMessageTask(op, PPExtString(
"$fatal"), op.getVerbosity(),
4315 op.getMessageAttr(), op.getSubstitutions());
4318 LogicalResult visitSV(FatalProceduralOp op);
4319 LogicalResult visitSV(FatalOp op);
4320 LogicalResult visitSV(ErrorProceduralOp op);
4321 LogicalResult visitSV(WarningProceduralOp op);
4322 LogicalResult visitSV(InfoProceduralOp op);
4323 LogicalResult visitSV(ErrorOp op);
4324 LogicalResult visitSV(WarningOp op);
4325 LogicalResult visitSV(InfoOp op);
4327 LogicalResult visitSV(ReadMemOp op);
4329 LogicalResult visitSV(GenerateOp op);
4330 LogicalResult visitSV(GenerateCaseOp op);
4331 LogicalResult visitSV(GenerateForOp op);
4333 LogicalResult visitSV(ForOp op);
4335 void emitAssertionLabel(Operation *op);
4336 void emitAssertionMessage(StringAttr message, ValueRange args,
4337 SmallPtrSetImpl<Operation *> &ops,
4339 template <
typename Op>
4340 LogicalResult emitImmediateAssertion(Op op, PPExtString opName);
4341 LogicalResult visitSV(AssertOp op);
4342 LogicalResult visitSV(AssumeOp op);
4343 LogicalResult visitSV(CoverOp op);
4344 template <
typename Op>
4345 LogicalResult emitConcurrentAssertion(Op op, PPExtString opName);
4346 LogicalResult visitSV(AssertConcurrentOp op);
4347 LogicalResult visitSV(AssumeConcurrentOp op);
4348 LogicalResult visitSV(CoverConcurrentOp op);
4349 template <
typename Op>
4350 LogicalResult emitPropertyAssertion(Op op, PPExtString opName);
4351 LogicalResult visitSV(AssertPropertyOp op);
4352 LogicalResult visitSV(AssumePropertyOp op);
4353 LogicalResult visitSV(CoverPropertyOp op);
4355 LogicalResult visitSV(BindOp op);
4356 LogicalResult visitSV(InterfaceOp op);
4357 LogicalResult visitSV(sv::SVVerbatimSourceOp op);
4358 LogicalResult visitSV(InterfaceSignalOp op);
4359 LogicalResult visitSV(InterfaceModportOp op);
4360 LogicalResult visitSV(AssignInterfaceSignalOp op);
4361 LogicalResult visitSV(MacroErrorOp op);
4362 LogicalResult visitSV(MacroDefOp op);
4364 void emitBlockAsStatement(
Block *block,
4365 const SmallPtrSetImpl<Operation *> &locationOps,
4366 StringRef multiLineComment = StringRef());
4368 LogicalResult visitSV(FuncDPIImportOp op);
4369 template <
typename CallOp>
4370 LogicalResult emitFunctionCall(CallOp callOp);
4371 LogicalResult visitSV(FuncCallProceduralOp op);
4372 LogicalResult visitSV(FuncCallOp op);
4373 LogicalResult visitSV(ReturnOp op);
4374 LogicalResult visitSV(IncludeOp op);
4377 ModuleEmitter &emitter;
4382 size_t maxDeclNameWidth = 0;
4383 size_t maxTypeWidth = 0;
4385 const LoweringOptions &options;
4394void StmtEmitter::emitExpression(Value exp,
4395 SmallPtrSetImpl<Operation *> &emittedExprs,
4396 VerilogPrecedence parenthesizeIfLooserThan,
4397 bool isAssignmentLikeContext) {
4398 ExprEmitter(emitter, emittedExprs)
4399 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4404void StmtEmitter::emitSVAttributes(Operation *op) {
4412 setPendingNewline();
4415void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4416 llvm::function_ref<
void()> emitRHS,
4417 PPExtString syntax, PPExtString postSyntax,
4418 std::optional<PPExtString> wordBeforeLHS) {
4420 ps.scopedBox(PP::ibox2, [&]() {
4421 if (wordBeforeLHS) {
4422 ps << *wordBeforeLHS << PP::space;
4426 ps << PP::space << syntax << PP::space;
4428 ps.scopedBox(PP::ibox0, [&]() {
4435template <
typename Op>
4437StmtEmitter::emitAssignLike(Op op, PPExtString syntax,
4438 std::optional<PPExtString> wordBeforeLHS) {
4439 SmallPtrSet<Operation *, 8> ops;
4443 ps.addCallback({op,
true});
4444 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4446 emitExpression(op.getSrc(), ops, LowestPrecedence,
4449 syntax, PPExtString(
";"), wordBeforeLHS);
4451 ps.addCallback({op,
false});
4452 emitLocationInfoAndNewLine(ops);
4456LogicalResult StmtEmitter::visitSV(AssignOp op) {
4459 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4462 if (emitter.assignsInlined.count(op))
4466 emitSVAttributes(op);
4468 return emitAssignLike(op, PPExtString(
"="), PPExtString(
"assign"));
4471LogicalResult StmtEmitter::visitSV(VarAssignOp op) {
4473 emitSVAttributes(op);
4475 return emitAssignLike(op, PPExtString(
"="), PPExtString(
"assign"));
4478LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4479 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4483 if (emitter.assignsInlined.count(op))
4487 emitSVAttributes(op);
4489 return emitAssignLike(op, PPExtString(
"="));
4492LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4494 emitSVAttributes(op);
4496 return emitAssignLike(op, PPExtString(
"<="));
4499LogicalResult StmtEmitter::visitSV(ForceOp op) {
4501 emitError(op,
"SV attributes emission is unimplemented for the op");
4503 return emitAssignLike(op, PPExtString(
"="), PPExtString(
"force"));
4506LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4508 emitError(op,
"SV attributes emission is unimplemented for the op");
4511 SmallPtrSet<Operation *, 8> ops;
4513 ps.addCallback({op,
true});
4514 ps.scopedBox(PP::ibox2, [&]() {
4515 ps <<
"release" << PP::space;
4516 emitExpression(op.getDest(), ops);
4519 ps.addCallback({op,
false});
4520 emitLocationInfoAndNewLine(ops);
4524LogicalResult StmtEmitter::visitSV(AliasOp op) {
4526 emitError(op,
"SV attributes emission is unimplemented for the op");
4529 SmallPtrSet<Operation *, 8> ops;
4531 ps.addCallback({op,
true});
4532 ps.scopedBox(PP::ibox2, [&]() {
4533 ps <<
"alias" << PP::space;
4534 ps.scopedBox(PP::cbox0, [&]() {
4536 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4537 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4541 ps.addCallback({op,
false});
4542 emitLocationInfoAndNewLine(ops);
4546LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4547 auto doNotPrint = op.getDoNotPrint();
4552 emitError(op,
"SV attributes emission is unimplemented for the op");
4555 StringRef prefix =
"";
4556 ps.addCallback({op,
true});
4559 ps <<
"// This interface is elsewhere emitted as a bind statement."
4563 SmallPtrSet<Operation *, 8> ops;
4566 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4567 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4568 "point to an interface");
4571 if (!prefix.empty())
4572 ps << PPExtString(prefix);
4573 ps << PPExtString(verilogName)
4575 << PPExtString(op.getName()) <<
"();";
4577 ps.addCallback({op,
false});
4578 emitLocationInfoAndNewLine(ops);
4586LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4587 const ModulePortInfo &ports) {
4588 SmallPtrSet<Operation *, 8> ops;
4589 size_t operandIndex = 0;
4590 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4592 auto operand = op->getOperand(operandIndex);
4596 if (operand.hasOneUse() && operand.getDefiningOp() &&
4597 isa<InstanceOp>(operand.getDefiningOp())) {
4606 ps.addCallback({op,
true});
4608 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
4610 ps <<
"// Zero width: ";
4613 ps <<
"assign" << PP::space;
4614 ps << PPExtString(port.getVerilogName());
4615 ps << PP::space <<
"=" << PP::space;
4616 ps.scopedBox(PP::ibox0, [&]() {
4620 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4621 ps <<
"/*Zero width*/";
4623 emitExpression(operand, ops, LowestPrecedence,
4628 ps.addCallback({op,
false});
4629 emitLocationInfoAndNewLine(ops);
4636LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4637 auto parent = op->getParentOfType<PortList>();
4638 ModulePortInfo ports(parent.getPortList());
4639 return emitOutputLikeOp(op, ports);
4642LogicalResult StmtEmitter::visitStmt(TypeScopeOp op) {
4644 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4645 ps <<
"`ifndef " << typescopeDef << PP::newline;
4646 ps <<
"`define " << typescopeDef;
4647 setPendingNewline();
4648 emitStatementBlock(*op.getBodyBlock());
4650 ps <<
"`endif // " << typescopeDef;
4651 setPendingNewline();
4655LogicalResult StmtEmitter::visitStmt(TypedeclOp op) {
4657 emitError(op,
"SV attributes emission is unimplemented for the op");
4662 ps << PP::neverbox <<
"// ";
4664 SmallPtrSet<Operation *, 8> ops;
4666 ps.scopedBox(PP::ibox2, [&]() {
4667 ps <<
"typedef" << PP::space;
4668 ps.invokeWithStringOS([&](
auto &os) {
4670 op.getAliasType(),
false);
4672 ps << PP::space << PPExtString(op.getPreferredName());
4673 ps.invokeWithStringOS(
4674 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4679 emitLocationInfoAndNewLine(ops);
4683LogicalResult StmtEmitter::visitSV(PackageOp op) {
4684 llvm::SaveAndRestore<Operation *> package(emitter.currentPackage, op);
4686 ps.addCallback({op,
true});
4687 ps <<
"package " << PPExtString(
getSymOpName(op)) <<
";";
4688 setPendingNewline();
4689 ps.scopedBox(PP::bbox2, [&]() { emitStatementBlock(*op.getBodyBlock()); });
4692 ps.addCallback({op,
false});
4693 setPendingNewline();
4697template <
typename CallOpTy>
4698LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4702 dyn_cast<FuncOp>(state.symbolCache.
getDefinition(op.getCalleeAttr()));
4704 SmallPtrSet<Operation *, 8> ops;
4708 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4709 if (explicitReturn) {
4710 assert(explicitReturn.hasOneUse());
4711 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4712 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4713 emitExpression(bpassignOp.getDest(), ops);
4715 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4716 ps <<
"assign" << PP::nbsp;
4717 emitExpression(assignOp.getDest(), ops);
4719 ps << PP::nbsp <<
"=" << PP::nbsp;
4722 auto arguments = callee.getPortList(
true);
4726 bool needsComma =
false;
4727 auto printArg = [&](Value value) {
4729 ps <<
"," << PP::space;
4730 emitExpression(value, ops);
4734 ps.scopedBox(PP::ibox0, [&] {
4735 unsigned inputIndex = 0, outputIndex = 0;
4736 for (
auto arg : arguments) {
4737 if (arg.dir == hw::ModulePort::Output)
4739 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4741 printArg(op.getInputs()[inputIndex++]);
4746 emitLocationInfoAndNewLine(ops);
4750LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4751 return emitFunctionCall(op);
4754LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4755 return emitFunctionCall(op);
4758template <
typename PPS>
4760 bool isAutomatic =
false,
4761 bool emitAsTwoStateType =
false) {
4762 ps <<
"function" << PP::nbsp;
4764 ps <<
"automatic" << PP::nbsp;
4765 auto retType = op.getExplicitlyReturnedType();
4767 ps.invokeWithStringOS([&](
auto &os) {
4768 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4769 emitAsTwoStateType);
4775 emitter.emitPortList(
4779LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4780 auto parent = op->getParentOfType<sv::FuncOp>();
4781 ModulePortInfo ports(parent.getPortList(
false));
4782 return emitOutputLikeOp(op, ports);
4785LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4787 ps <<
"`include" << PP::nbsp;
4789 if (op.getStyle() == IncludeStyle::System)
4790 ps <<
"<" << op.getTarget() <<
">";
4792 ps <<
"\"" << op.getTarget() <<
"\"";
4794 emitLocationInfo(op.getLoc());
4795 setPendingNewline();
4799LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4802 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4806 if (
auto linkageName = importOp.getLinkageName())
4807 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4809 cast<FuncOp>(state.symbolCache.
getDefinition(importOp.getCalleeAttr()));
4810 assert(op.isDeclaration() &&
"function must be a declaration");
4813 assert(state.pendingNewline);
4819LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4821 emitError(op,
"SV attributes emission is unimplemented for the op");
4824 SmallPtrSet<Operation *, 8> ops;
4827 ps.addCallback({op,
true});
4829 if (
auto fd = op.getFd())
4830 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4833 ps.addCallback({op,
false});
4834 emitLocationInfoAndNewLine(ops);
4838LogicalResult StmtEmitter::visitSV(FCloseOp op) {
4840 emitError(op,
"SV attributes emission is unimplemented for the op");
4843 SmallPtrSet<Operation *, 8> ops;
4846 ps.addCallback({op,
true});
4848 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4850 ps.addCallback({op,
false});
4851 emitLocationInfoAndNewLine(ops);
4855template <
typename OpTy,
typename EmitPrefixFn>
4856LogicalResult StmtEmitter::emitFormattedWriteLikeOp(OpTy op, StringRef callee,
4857 StringRef formatString,
4858 ValueRange substitutions,
4859 EmitPrefixFn emitPrefix) {
4861 emitError(op,
"SV attributes emission is unimplemented for the op");
4864 SmallPtrSet<Operation *, 8> ops;
4867 ps.addCallback({op,
true});
4869 ps.scopedBox(PP::ibox0, [&]() {
4871 ps.writeQuotedEscaped(formatString);
4878 for (
auto operand : substitutions) {
4879 ps <<
"," << PP::space;
4880 emitExpression(operand, ops);
4884 ps.addCallback({op,
false});
4885 emitLocationInfoAndNewLine(ops);
4889LogicalResult StmtEmitter::visitSV(WriteOp op) {
4890 return emitFormattedWriteLikeOp(op,
"$write(", op.getFormatString(),
4891 op.getSubstitutions(),
4892 [&](SmallPtrSetImpl<Operation *> &) {});
4895LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4896 return emitFormattedWriteLikeOp(op,
"$fwrite(", op.getFormatString(),
4897 op.getSubstitutions(),
4898 [&](SmallPtrSetImpl<Operation *> &ops) {
4899 emitExpression(op.getFd(), ops);
4900 ps <<
"," << PP::space;
4904LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4906 emitError(op,
"SV attributes emission is unimplemented for the op");
4909 SmallPtrSet<Operation *, 8> ops;
4914 StringRef
string = op.getFormatString();
4915 if (
string.ends_with(
"\n"))
4916 string =
string.drop_back();
4921 bool isFirst =
true;
4924 while (!
string.
empty()) {
4925 auto lhsRhs =
string.split(
'\n');
4929 ps << PP::end << PP::newline << PP::neverbox;
4933 emitTextWithSubstitutions(
4934 ps, lhsRhs.first, op,
4935 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4936 string = lhsRhs.second;
4941 emitLocationInfoAndNewLine(ops);
4946LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4948 emitError(op,
"SV attributes emission is unimplemented for the op");
4952 SmallPtrSet<Operation *, 8> ops;
4957 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4958 assert(macroOp &&
"Invalid IR");
4960 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4961 ps <<
"`" << PPExtString(name);
4962 if (!op.getInputs().empty()) {
4964 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4965 emitExpression(val, ops, LowestPrecedence,
4971 emitLocationInfoAndNewLine(ops);
4977StmtEmitter::emitSimulationControlTask(Operation *op, PPExtString taskName,
4978 std::optional<unsigned> verbosity) {
4980 emitError(op,
"SV attributes emission is unimplemented for the op");
4983 SmallPtrSet<Operation *, 8> ops;
4985 ps.addCallback({op,
true});
4987 if (verbosity && *verbosity != 1) {
4989 ps.addAsString(*verbosity);
4993 ps.addCallback({op,
false});
4994 emitLocationInfoAndNewLine(ops);
4998LogicalResult StmtEmitter::visitSV(StopOp op) {
4999 return emitSimulationControlTask(op, PPExtString(
"$stop"), op.getVerbosity());
5002LogicalResult StmtEmitter::visitSV(FinishOp op) {
5003 return emitSimulationControlTask(op, PPExtString(
"$finish"),
5007LogicalResult StmtEmitter::visitSV(ExitOp op) {
5008 return emitSimulationControlTask(op, PPExtString(
"$exit"), {});
5014StmtEmitter::emitSeverityMessageTask(Operation *op, PPExtString taskName,
5015 std::optional<unsigned> verbosity,
5016 StringAttr message, ValueRange operands) {
5018 emitError(op,
"SV attributes emission is unimplemented for the op");
5021 SmallPtrSet<Operation *, 8> ops;
5023 ps.addCallback({op,
true});
5029 if ((verbosity && *verbosity != 1) || message) {
5031 ps.scopedBox(PP::ibox0, [&]() {
5035 ps.addAsString(*verbosity);
5040 ps <<
"," << PP::space;
5041 ps.writeQuotedEscaped(message.getValue());
5043 for (
auto operand : operands) {
5044 ps <<
"," << PP::space;
5045 emitExpression(operand, ops);
5054 ps.addCallback({op,
false});
5055 emitLocationInfoAndNewLine(ops);
5059LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
5060 return emitFatalMessageOp(op);
5063LogicalResult StmtEmitter::visitSV(FatalOp op) {
5064 return emitFatalMessageOp(op);
5067LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
5068 return emitNonfatalMessageOp(op,
"$error");
5071LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
5072 return emitNonfatalMessageOp(op,
"$warning");
5075LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
5076 return emitNonfatalMessageOp(op,
"$info");
5079LogicalResult StmtEmitter::visitSV(ErrorOp op) {
5080 return emitNonfatalMessageOp(op,
"$error");
5083LogicalResult StmtEmitter::visitSV(WarningOp op) {
5084 return emitNonfatalMessageOp(op,
"$warning");
5087LogicalResult StmtEmitter::visitSV(InfoOp op) {
5088 return emitNonfatalMessageOp(op,
"$info");
5091LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
5092 SmallPtrSet<Operation *, 8> ops({op});
5095 ps.addCallback({op,
true});
5097 switch (op.getBaseAttr().getValue()) {
5098 case MemBaseTypeAttr::MemBaseBin:
5101 case MemBaseTypeAttr::MemBaseHex:
5106 ps.scopedBox(PP::ibox0, [&]() {
5107 ps.writeQuotedEscaped(op.getFilename());
5108 ps <<
"," << PP::space;
5109 emitExpression(op.getDest(), ops);
5113 ps.addCallback({op,
false});
5114 emitLocationInfoAndNewLine(ops);
5118LogicalResult StmtEmitter::visitSV(GenerateOp op) {
5119 emitSVAttributes(op);
5122 ps.addCallback({op,
true});
5123 ps <<
"generate" << PP::newline;
5125 setPendingNewline();
5126 emitStatementBlock(op.getBody().getBlocks().front());
5128 ps <<
"end: " << PPExtString(
getSymOpName(op)) << PP::newline;
5129 ps <<
"endgenerate";
5130 ps.addCallback({op,
false});
5131 setPendingNewline();
5135LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
5136 emitSVAttributes(op);
5139 ps.addCallback({op,
true});
5141 ps.invokeWithStringOS([&](
auto &os) {
5142 emitter.printParamValue(
5143 op.getCond(), os, VerilogPrecedence::Selection,
5144 [&]() { return op->emitOpError(
"invalid case parameter"); });
5147 setPendingNewline();
5150 ArrayAttr patterns = op.getCasePatterns();
5151 ArrayAttr caseNames = op.getCaseNames();
5152 MutableArrayRef<Region> regions = op.getCaseRegions();
5153 assert(patterns.size() == regions.size());
5154 assert(patterns.size() == caseNames.size());
5159 llvm::StringMap<size_t> nextGenIds;
5160 ps.scopedBox(PP::bbox2, [&]() {
5162 for (
size_t i = 0, e = patterns.size(); i < e; ++i) {
5163 auto ®ion = regions[i];
5164 assert(region.hasOneBlock());
5165 Attribute patternAttr = patterns[i];
5168 if (!isa<mlir::TypedAttr>(patternAttr))
5171 ps.invokeWithStringOS([&](
auto &os) {
5172 emitter.printParamValue(
5173 patternAttr, os, VerilogPrecedence::LowestPrecedence,
5174 [&]() {
return op->emitOpError(
"invalid case value"); });
5177 StringRef legalName =
5178 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
5180 ps <<
": begin: " << PPExtString(legalName);
5181 setPendingNewline();
5182 emitStatementBlock(region.getBlocks().front());
5184 ps <<
"end: " << PPExtString(legalName);
5185 setPendingNewline();
5191 ps.addCallback({op,
false});
5192 setPendingNewline();
5196LogicalResult StmtEmitter::visitSV(GenerateForOp op) {
5197 emitSVAttributes(op);
5198 llvm::SmallPtrSet<Operation *, 8> ops;
5199 ps.addCallback({op,
true});
5202 StringRef inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5205 ps.scopedBox(PP::cbox0, [&]() {
5207 [&]() { ps <<
"genvar" << PP::nbsp << PPExtString(inductionVarName); },
5209 ps.invokeWithStringOS([&](
auto &os) {
5210 emitter.printParamValue(
5211 op.getLowerBound(), os, VerilogPrecedence::LowestPrecedence,
5212 [&]() { return op->emitOpError(
"invalid lower bound"); });
5219 [&]() { ps << PPExtString(inductionVarName); },
5221 ps.invokeWithStringOS([&](
auto &os) {
5222 emitter.printParamValue(
5223 op.getUpperBound(), os, VerilogPrecedence::LowestPrecedence,
5224 [&]() { return op->emitOpError(
"invalid upper bound"); });
5230 ps << PPExtString(inductionVarName) << PP::nbsp <<
"+=" << PP::nbsp;
5231 ps.invokeWithStringOS([&](
auto &os) {
5232 emitter.printParamValue(
5233 op.getStep(), os, VerilogPrecedence::LowestPrecedence,
5234 [&]() { return op->emitOpError(
"invalid step"); });
5237 StringRef blockName = op.getGenBlockName();
5238 if (!blockName.empty())
5239 ps <<
" : " << PPExtString(blockName);
5242 ps << PP::neverbreak;
5243 setPendingNewline();
5244 emitStatementBlock(op.getBody().getBlocks().front());
5247 if (StringRef blockName = op.getGenBlockName(); !blockName.empty())
5248 ps <<
" // " << PPExtString(blockName);
5249 ps.addCallback({op,
false});
5250 setPendingNewline();
5254LogicalResult StmtEmitter::visitSV(ForOp op) {
5255 emitSVAttributes(op);
5256 llvm::SmallPtrSet<Operation *, 8> ops;
5257 ps.addCallback({op,
true});
5259 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
5262 ps.scopedBox(PP::cbox0, [&]() {
5266 ps <<
"logic" << PP::nbsp;
5267 ps.invokeWithStringOS([&](
auto &os) {
5268 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
5271 ps << PP::nbsp << PPExtString(inductionVarName);
5273 [&]() { emitExpression(op.getLowerBound(), ops); }, PPExtString(
"="));
5278 emitAssignLike([&]() { ps << PPExtString(inductionVarName); },
5279 [&]() { emitExpression(op.getUpperBound(), ops); },
5285 emitAssignLike([&]() { ps << PPExtString(inductionVarName); },
5286 [&]() { emitExpression(op.getStep(), ops); },
5287 PPExtString(
"+="), PPExtString(
") begin"));
5290 ps << PP::neverbreak;
5291 setPendingNewline();
5292 emitStatementBlock(op.getBody().getBlocks().front());
5295 ps.addCallback({op,
false});
5296 emitLocationInfoAndNewLine(ops);
5301void StmtEmitter::emitAssertionLabel(Operation *op) {
5302 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
5303 ps << PPExtString(label) <<
":" << PP::space;
5308void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
5309 SmallPtrSetImpl<Operation *> &ops,
5310 bool isConcurrent =
false) {
5313 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
5314 ps.scopedBox(PP::ibox0, [&]() {
5315 ps.writeQuotedEscaped(message.getValue());
5317 for (
auto arg : args) {
5318 ps <<
"," << PP::space;
5319 emitExpression(arg, ops);
5325template <
typename Op>
5326LogicalResult StmtEmitter::emitImmediateAssertion(Op op, PPExtString opName) {
5328 emitError(op,
"SV attributes emission is unimplemented for the op");
5331 SmallPtrSet<Operation *, 8> ops;
5333 ps.addCallback({op,
true});
5334 ps.scopedBox(PP::ibox2, [&]() {
5335 emitAssertionLabel(op);
5336 ps.scopedBox(PP::cbox0, [&]() {
5338 switch (op.getDefer()) {
5339 case DeferAssert::Immediate:
5341 case DeferAssert::Observed:
5344 case DeferAssert::Final:
5349 ps.scopedBox(PP::ibox0, [&]() {
5350 emitExpression(op.getExpression(), ops);
5353 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5357 ps.addCallback({op,
false});
5358 emitLocationInfoAndNewLine(ops);
5362LogicalResult StmtEmitter::visitSV(AssertOp op) {
5363 return emitImmediateAssertion(op, PPExtString(
"assert"));
5366LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5367 return emitImmediateAssertion(op, PPExtString(
"assume"));
5370LogicalResult StmtEmitter::visitSV(CoverOp op) {
5371 return emitImmediateAssertion(op, PPExtString(
"cover"));
5374template <
typename Op>
5375LogicalResult StmtEmitter::emitConcurrentAssertion(Op op, PPExtString opName) {
5377 emitError(op,
"SV attributes emission is unimplemented for the op");
5380 SmallPtrSet<Operation *, 8> ops;
5382 ps.addCallback({op,
true});
5383 ps.scopedBox(PP::ibox2, [&]() {
5384 emitAssertionLabel(op);
5385 ps.scopedBox(PP::cbox0, [&]() {
5386 ps << opName << PP::nbsp <<
"property (";
5387 ps.scopedBox(PP::ibox0, [&]() {
5388 ps <<
"@(" << PPExtString(stringifyEventControl(op.getEvent()))
5390 emitExpression(op.getClock(), ops);
5391 ps <<
")" << PP::space;
5392 emitExpression(op.getProperty(), ops);
5395 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5400 ps.addCallback({op,
false});
5401 emitLocationInfoAndNewLine(ops);
5405LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5406 return emitConcurrentAssertion(op, PPExtString(
"assert"));
5409LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5410 return emitConcurrentAssertion(op, PPExtString(
"assume"));
5413LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5414 return emitConcurrentAssertion(op, PPExtString(
"cover"));
5419template <
typename Op>
5420LogicalResult StmtEmitter::emitPropertyAssertion(Op op, PPExtString opName) {
5422 emitError(op,
"SV attributes emission is unimplemented for the op");
5432 Operation *parent = op->getParentOp();
5433 Value
property = op.getProperty();
5434 bool isTemporal = !
property.getType().isSignlessInteger(1);
5435 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5436 bool emitAsImmediate = !isTemporal && isProcedural;
5439 SmallPtrSet<Operation *, 8> ops;
5441 ps.addCallback({op,
true});
5442 ps.scopedBox(PP::ibox2, [&]() {
5444 emitAssertionLabel(op);
5446 ps.scopedBox(PP::cbox0, [&]() {
5447 if (emitAsImmediate)
5448 ps << opName <<
"(";
5450 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5452 Value clock = op.getClock();
5453 auto event = op.getEvent();
5455 ps.scopedBox(PP::ibox2, [&]() {
5456 PropertyEmitter(emitter, ops)
5457 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5460 ps.scopedBox(PP::ibox2, [&]() {
5461 PropertyEmitter(emitter, ops)
5462 .emitAssertPropertyBody(property, op.getDisable());
5467 ps.addCallback({op,
false});
5468 emitLocationInfoAndNewLine(ops);
5472LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5473 return emitPropertyAssertion(op, PPExtString(
"assert"));
5476LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5477 return emitPropertyAssertion(op, PPExtString(
"assume"));
5480LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5481 return emitPropertyAssertion(op, PPExtString(
"cover"));
5484LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5486 emitError(op,
"SV attributes emission is unimplemented for the op");
5488 auto ident = PPExtString(
5489 cast<MacroDeclOp>(state.symbolCache.
getDefinition(cond.getIdent()))
5490 .getMacroIdentifier());
5493 bool hasEmptyThen = op->getRegion(0).front().empty();
5495 ps <<
"`ifndef " << ident;
5497 ps <<
"`ifdef " << ident;
5499 SmallPtrSet<Operation *, 8> ops;
5501 emitLocationInfoAndNewLine(ops);
5504 emitStatementBlock(op->getRegion(0).front());
5506 if (!op->getRegion(1).empty()) {
5507 if (!hasEmptyThen) {
5509 ps <<
"`else // " << ident;
5510 setPendingNewline();
5512 emitStatementBlock(op->getRegion(1).front());
5519 setPendingNewline();
5527void StmtEmitter::emitBlockAsStatement(
5528 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5529 StringRef multiLineComment) {
5533 auto needsBeginEnd =
5537 emitLocationInfoAndNewLine(locationOps);
5540 emitStatementBlock(*block);
5542 if (needsBeginEnd) {
5546 if (!multiLineComment.empty())
5547 ps <<
" // " << multiLineComment;
5548 setPendingNewline();
5552LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5554 for (
auto &op : ooop.getBody().front())
5559LogicalResult StmtEmitter::visitSV(IfOp op) {
5560 SmallPtrSet<Operation *, 8> ops;
5562 auto ifcondBox = PP::ibox2;
5564 emitSVAttributes(op);
5566 ps.addCallback({op,
true});
5567 ps <<
"if (" << ifcondBox;
5577 emitExpression(ifOp.getCond(), ops);
5578 ps << PP::end <<
")";
5579 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5581 if (!ifOp.hasElse())
5585 Block *elseBlock = ifOp.getElseBlock();
5587 if (!nestedElseIfOp) {
5592 emitBlockAsStatement(elseBlock, ops);
5598 ifOp = nestedElseIfOp;
5599 ps <<
"else if (" << ifcondBox;
5601 ps.addCallback({op,
false});
5606LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5607 emitSVAttributes(op);
5608 SmallPtrSet<Operation *, 8> ops;
5612 auto printEvent = [&](AlwaysOp::Condition cond) {
5613 ps << PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5614 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5616 ps.addCallback({op,
true});
5618 switch (op.getNumConditions()) {
5624 printEvent(op.getCondition(0));
5629 ps.scopedBox(PP::cbox0, [&]() {
5630 printEvent(op.getCondition(0));
5631 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5632 ps << PP::space <<
"or" << PP::space;
5633 printEvent(op.getCondition(i));
5642 std::string comment;
5643 if (op.getNumConditions() == 0) {
5644 comment =
"always @*";
5646 comment =
"always @(";
5649 [&](Attribute eventAttr) {
5650 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5651 comment += stringifyEventControl(event);
5653 [&]() { comment +=
", "; });
5657 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5658 ps.addCallback({op,
false});
5662LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5663 emitSVAttributes(op);
5664 SmallPtrSet<Operation *, 8> ops;
5668 ps.addCallback({op,
true});
5669 StringRef opString =
"always_comb";
5671 opString =
"always @(*)";
5673 ps << PPExtString(opString);
5674 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5675 ps.addCallback({op,
false});
5679LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5680 emitSVAttributes(op);
5682 SmallPtrSet<Operation *, 8> ops;
5686 ps.addCallback({op,
true});
5687 ps <<
"always_ff @(";
5688 ps.scopedBox(PP::cbox0, [&]() {
5689 ps << PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5690 emitExpression(op.getClock(), ops);
5691 if (op.getResetStyle() == ResetType::AsyncReset) {
5692 ps << PP::nbsp <<
"or" << PP::space
5693 << PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5694 emitExpression(op.getReset(), ops);
5701 std::string comment;
5702 comment +=
"always_ff @(";
5703 comment += stringifyEventControl(op.getClockEdge());
5704 if (op.getResetStyle() == ResetType::AsyncReset) {
5706 comment += stringifyEventControl(*op.getResetEdge());
5710 if (op.getResetStyle() == ResetType::NoReset)
5711 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5714 emitLocationInfoAndNewLine(ops);
5715 ps.scopedBox(PP::bbox2, [&]() {
5721 if (op.getResetStyle() == ResetType::AsyncReset &&
5722 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5724 emitExpression(op.getReset(), ops);
5726 emitBlockAsStatement(op.getResetBlock(), ops);
5729 emitBlockAsStatement(op.getBodyBlock(), ops);
5734 ps <<
" // " << comment;
5735 setPendingNewline();
5737 ps.addCallback({op,
false});
5741LogicalResult StmtEmitter::visitSV(InitialOp op) {
5742 emitSVAttributes(op);
5743 SmallPtrSet<Operation *, 8> ops;
5746 ps.addCallback({op,
true});
5748 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5749 ps.addCallback({op,
false});
5753LogicalResult StmtEmitter::visitSV(CaseOp op) {
5754 emitSVAttributes(op);
5755 SmallPtrSet<Operation *, 8> ops, emptyOps;
5758 ps.addCallback({op,
true});
5759 if (op.getValidationQualifier() !=
5760 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5761 ps << PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5762 op.getValidationQualifier()))
5764 const char *opname =
nullptr;
5765 switch (op.getCaseStyle()) {
5766 case CaseStmtType::CaseStmt:
5769 case CaseStmtType::CaseXStmt:
5772 case CaseStmtType::CaseZStmt:
5776 ps << opname <<
" (";
5777 ps.scopedBox(PP::ibox0, [&]() {
5778 emitExpression(op.getCond(), ops);
5781 emitLocationInfoAndNewLine(ops);
5783 size_t caseValueIndex = 0;
5784 ps.scopedBox(PP::bbox2, [&]() {
5785 for (
auto &caseInfo : op.getCases()) {
5787 auto &pattern = caseInfo.pattern;
5789 llvm::TypeSwitch<CasePattern *>(pattern.get())
5790 .Case<CaseBitPattern>([&](
auto bitPattern) {
5793 ps.invokeWithStringOS([&](
auto &os) {
5794 os << bitPattern->getWidth() <<
"'b";
5795 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5796 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5799 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5801 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5803 .Case<CaseExprPattern>([&](
auto) {
5804 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5806 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5807 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5810 emitBlockAsStatement(caseInfo.block, emptyOps);
5816 ps.addCallback({op,
false});
5817 emitLocationInfoAndNewLine(ops);
5821LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5822 bool doNotPrint = op.getDoNotPrint();
5828 emitSVAttributes(op);
5830 ps.addCallback({op,
true});
5833 <<
"/* This instance is elsewhere emitted as a bind statement."
5836 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5837 "attributes. The attributes will not be emitted.";
5840 SmallPtrSet<Operation *, 8> ops;
5845 state.symbolCache.
getDefinition(op.getReferencedModuleNameAttr());
5846 assert(moduleOp &&
"Invalid IR");
5850 if (!op.getParameters().empty()) {
5853 bool printed =
false;
5855 llvm::zip(op.getParameters(),
5856 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5857 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5858 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5860 if (param.getValue() == modParam.getValue())
5865 ps <<
" #(" << PP::bbox2 << PP::newline;
5868 ps <<
"," << PP::newline;
5874 ps.invokeWithStringOS([&](
auto &os) {
5875 emitter.printParamValue(param.getValue(), os, [&]() {
5876 return op->emitOpError(
"invalid instance parameter '")
5877 << param.getName().getValue() <<
"' value";
5883 ps << PP::end << PP::newline <<
")";
5889 ModulePortInfo modPortInfo(cast<PortList>(moduleOp).
getPortList());
5890 SmallVector<Value> instPortValues(modPortInfo.size());
5891 op.getValues(instPortValues, modPortInfo);
5892 emitInstancePortList(op, modPortInfo, instPortValues);
5894 ps.addCallback({op,
false});
5895 emitLocationInfoAndNewLine(ops);
5900 setPendingNewline();
5905void StmtEmitter::emitInstancePortList(Operation *op,
5906 ModulePortInfo &modPortInfo,
5907 ArrayRef<Value> instPortValues) {
5908 SmallPtrSet<Operation *, 8> ops;
5911 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5912 ModulePortInfo containingPortList(containingModule.getPortList());
5918 size_t maxNameLength = 0;
5920 for (
auto &elt : modPortInfo) {
5921 size_t nameLength = elt.getVerilogName().size();
5922 if (!lineLength || nameLength <= *lineLength / 3)
5923 maxNameLength = std::max(maxNameLength, nameLength);
5926 auto getWireForValue = [&](Value result) {
5927 return result.getUsers().begin()->getOperand(0);
5931 bool isFirst =
true;
5932 bool isZeroWidth =
false;
5934 for (
size_t portNum = 0, portEnd = modPortInfo.size(); portNum < portEnd;
5936 auto &modPort = modPortInfo.at(portNum);
5938 Value portVal = instPortValues[portNum];
5943 bool shouldPrintComma =
true;
5945 shouldPrintComma =
false;
5946 for (
size_t i = portNum + 1, e = modPortInfo.size(); i != e; ++i)
5948 shouldPrintComma =
true;
5953 if (shouldPrintComma)
5956 emitLocationInfoAndNewLine(ops);
5971 ps.scopedBox(isZeroWidth ? PP::neverbox : PP::ibox2, [&]() {
5972 auto modPortName = modPort.getVerilogName();
5973 ps <<
"." << PPExtString(modPortName);
5975 if (modPortName.size() <= maxNameLength)
5976 ps.spaces(maxNameLength - modPortName.size() + 1);
5980 ps.scopedBox(PP::ibox0, [&]() {
5987 if (!modPort.isOutput()) {
5989 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5990 ps <<
"/* Zero width */";
5992 emitExpression(portVal, ops, LowestPrecedence);
5993 }
else if (portVal.use_empty()) {
5994 ps <<
"/* unused */";
5995 }
else if (portVal.hasOneUse() &&
5996 (output = dyn_cast_or_null<OutputOp>(
5997 portVal.getUses().begin()->getOwner()))) {
6002 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6004 containingPortList.atOutput(outputPortNo).getVerilogName());
6006 portVal = getWireForValue(portVal);
6007 emitExpression(portVal, ops);
6013 if (!isFirst || isZeroWidth) {
6014 emitLocationInfoAndNewLine(ops);
6027LogicalResult StmtEmitter::visitSV(BindOp op) {
6028 emitter.emitBind(op);
6029 assert(state.pendingNewline);
6033LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
6034 emitComment(op.getCommentAttr());
6036 emitSVAttributes(op);
6039 ps.addCallback({op,
true});
6040 ps <<
"interface " << PPExtString(
getSymOpName(op)) <<
";";
6041 setPendingNewline();
6043 emitStatementBlock(*op.getBodyBlock());
6045 ps <<
"endinterface" << PP::newline;
6046 ps.addCallback({op,
false});
6047 setPendingNewline();
6052 emitSVAttributes(op);
6054 ps.addCallback({op,
true});
6056 ps << op.getContent();
6058 ps.addCallback({op,
false});
6059 setPendingNewline();
6063LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
6065 emitSVAttributes(op);
6067 ps.addCallback({op,
true});
6069 ps << PP::neverbox <<
"// ";
6070 ps.invokeWithStringOS([&](
auto &os) {
6075 ps.invokeWithStringOS(
6076 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
6080 ps.addCallback({op,
false});
6081 setPendingNewline();
6085LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
6087 ps.addCallback({op,
true});
6088 ps <<
"modport " << PPExtString(
getSymOpName(op)) <<
"(";
6091 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
6092 auto port = cast<ModportStructAttr>(portAttr);
6093 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
6094 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
6095 ps << PPExtString(getSymOpName(signalDecl));
6099 ps.addCallback({op,
false});
6100 setPendingNewline();
6104LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
6106 ps.addCallback({op,
true});
6107 SmallPtrSet<Operation *, 8> emitted;
6110 emitExpression(op.getIface(), emitted);
6111 ps <<
"." << PPExtString(op.getSignalName()) <<
" = ";
6112 emitExpression(op.getRhs(), emitted);
6114 ps.addCallback({op,
false});
6115 setPendingNewline();
6119LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
6121 ps <<
"`" << op.getMacroIdentifier();
6122 setPendingNewline();
6126LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
6127 auto decl = op.getReferencedMacro(&state.symbolCache);
6130 ps.addCallback({op,
true});
6132 if (decl.getArgs()) {
6134 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
6135 ps << cast<StringAttr>(name);
6139 if (!op.getFormatString().empty()) {
6141 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
6144 ps.addCallback({op,
false});
6145 setPendingNewline();
6149void StmtEmitter::emitStatement(Operation *op) {
6156 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
6160 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
6161 succeeded(dispatchVerifVisitor(op)))
6164 emitOpError(op,
"emission to Verilog not supported");
6165 emitPendingNewlineIfNeeded();
6166 ps <<
"unknown MLIR operation " << PPExtString(op->getName().getStringRef());
6167 setPendingNewline();
6178 StmtEmitter &stmtEmitter) {
6185 if (isa<IfDefProceduralOp>(op->getParentOp()))
6193 SmallVector<Value, 8> exprsToScan(op->getOperands());
6198 while (!exprsToScan.empty()) {
6199 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
6206 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
6207 auto *defOp = readInout.getOperand().getDefiningOp();
6214 if (isa<sv::WireOp>(defOp))
6219 if (!isa<RegOp, LogicOp>(defOp))
6225 if (isa<LogicOp>(defOp) &&
6226 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
6230 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
6231 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
6239 exprsToScan.append(expr->getOperands().begin(),
6240 expr->getOperands().end());
6246 if (expr->getBlock() != op->getBlock())
6251 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
6258template <
class AssignTy>
6260 AssignTy singleAssign;
6261 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
6262 if (hasSVAttributes(user))
6265 if (auto assign = dyn_cast<AssignTy>(user)) {
6268 singleAssign = assign;
6272 return isa<ReadInOutOp>(user);
6274 return singleAssign;
6280 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
6284 if (op1->getBlock() != user->getBlock())
6290 return op1->isBeforeInBlock(user);
6294LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
6295 emitSVAttributes(op);
6296 auto value = op->getResult(0);
6297 SmallPtrSet<Operation *, 8> opsForLocation;
6298 opsForLocation.insert(op);
6300 ps.addCallback({op,
true});
6303 auto type = value.getType();
6309 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6311 ps.scopedBox(isZeroBit ? PP::neverbox : PP::ibox2, [&]() {
6312 unsigned targetColumn = 0;
6313 unsigned column = 0;
6316 if (maxDeclNameWidth > 0)
6317 targetColumn += maxDeclNameWidth + 1;
6320 ps <<
"// Zero width: " << PPExtString(word) << PP::space;
6321 }
else if (!word.empty()) {
6322 ps << PPExtString(word);
6323 column += word.size();
6324 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6325 ps.spaces(numSpaces);
6326 column += numSpaces;
6329 SmallString<8> typeString;
6332 llvm::raw_svector_ostream stringStream(typeString);
6335 true, singleBitDefaultType);
6338 if (maxTypeWidth > 0)
6339 targetColumn += maxTypeWidth + 1;
6340 unsigned numSpaces = 0;
6341 if (!typeString.empty()) {
6343 column += typeString.size();
6346 if (targetColumn > column)
6347 numSpaces = targetColumn - column;
6348 ps.spaces(numSpaces);
6349 column += numSpaces;
6355 ps.invokeWithStringOS(
6356 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6360 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6361 auto innerSym = innerSymOp.getInnerSymAttr();
6362 if (innerSym && !innerSym.empty()) {
6364 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6370 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6371 ps << PP::space <<
"=" << PP::space;
6372 ps.invokeWithStringOS([&](
auto &os) {
6373 emitter.printParamValue(localparam.getValue(), os, [&]() {
6374 return op->emitOpError(
"invalid localparam value");
6379 if (
auto varOp = dyn_cast<VarOp>(op)) {
6380 if (
auto initValue = varOp.getInit()) {
6381 ps << PP::space <<
"=" << PP::space;
6382 ps.scopedBox(PP::ibox0, [&]() {
6383 emitExpression(initValue, opsForLocation, LowestPrecedence,
6389 if (
auto regOp = dyn_cast<RegOp>(op)) {
6390 if (
auto initValue = regOp.getInit()) {
6391 ps << PP::space <<
"=" << PP::space;
6392 ps.scopedBox(PP::ibox0, [&]() {
6393 emitExpression(initValue, opsForLocation, LowestPrecedence,
6403 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6407 auto *source = singleAssign.getSrc().getDefiningOp();
6411 if (!source || isa<ConstantOp>(source) ||
6412 op->getNextNode() == singleAssign) {
6413 ps << PP::space <<
"=" << PP::space;
6414 ps.scopedBox(PP::ibox0, [&]() {
6415 emitExpression(singleAssign.getSrc(), opsForLocation,
6419 emitter.assignsInlined.insert(singleAssign);
6428 op->getParentOp()->hasTrait<ProceduralRegion>() &&
6434 auto *source = singleAssign.getSrc().getDefiningOp();
6438 if (!source || isa<ConstantOp>(source) ||
6441 ps << PP::space <<
"=" << PP::space;
6442 ps.scopedBox(PP::ibox0, [&]() {
6443 emitExpression(singleAssign.getSrc(), opsForLocation,
6448 emitter.assignsInlined.insert(singleAssign);
6449 emitter.expressionsEmittedIntoDecl.insert(op);
6456 ps.addCallback({op,
false});
6457 emitLocationInfoAndNewLine(opsForLocation);
6461void StmtEmitter::collectNamesAndCalculateDeclarationWidths(
Block &block) {
6464 NameCollector collector(emitter);
6465 collector.collectNames(block);
6468 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6469 maxTypeWidth = collector.getMaxTypeWidth();
6472void StmtEmitter::emitStatementBlock(
Block &body) {
6473 ps.scopedBox(PP::bbox2, [&]() {
6478 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6479 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6484 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6485 collectNamesAndCalculateDeclarationWidths(body);
6488 for (
auto &op : body) {
6495void ModuleEmitter::emitStatement(Operation *op) {
6496 StmtEmitter(*
this, state.options).emitStatement(op);
6501void ModuleEmitter::emitSVAttributes(Operation *op) {
6509 setPendingNewline();
6516void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6517 auto verilogName =
module.getVerilogModuleNameAttr();
6519 ps <<
"// external generated module " << PPExtString(verilogName.getValue())
6521 setPendingNewline();
6530void ModuleEmitter::emitBind(BindOp op) {
6532 emitError(op,
"SV attributes emission is unimplemented for the op");
6533 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6535 HWModuleOp parentMod = inst->getParentOfType<hw::HWModuleOp>();
6536 ModulePortInfo parentPortList(parentMod.getPortList());
6539 Operation *childMod =
6540 state.symbolCache.
getDefinition(inst.getReferencedModuleNameAttr());
6544 ps.addCallback({op,
true});
6545 ps <<
"bind " << PPExtString(parentVerilogName.getValue()) << PP::nbsp
6546 << PPExtString(childVerilogName.getValue()) << PP::nbsp
6548 bool isFirst =
true;
6549 ps.scopedBox(PP::bbox2, [&]() {
6550 auto parentPortInfo = parentMod.getPortList();
6551 ModulePortInfo childPortInfo(cast<PortList>(childMod).
getPortList());
6555 size_t maxNameLength = 0;
6557 for (
auto &elt : childPortInfo) {
6558 auto portName = elt.getVerilogName();
6559 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6560 size_t nameLength = elt.getName().size();
6561 if (!lineLength || nameLength <= *lineLength / 3)
6562 maxNameLength = std::max(maxNameLength, nameLength);
6565 SmallVector<Value> instPortValues(childPortInfo.size());
6566 inst.getValues(instPortValues, childPortInfo);
6568 for (
auto [idx, elt] : llvm::enumerate(childPortInfo)) {
6570 Value portVal = instPortValues[idx];
6576 bool shouldPrintComma =
true;
6578 shouldPrintComma =
false;
6579 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6581 shouldPrintComma =
true;
6586 if (shouldPrintComma)
6599 ps << PP::neverbox <<
"//";
6602 ps <<
"." << PPExtString(elt.getName());
6604 if (elt.getName().size() <= maxNameLength)
6605 ps.nbsp(maxNameLength - elt.getName().size());
6607 llvm::SmallPtrSet<Operation *, 4> ops;
6608 if (elt.isOutput()) {
6609 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6610 "output port must have either single or no use");
6611 if (portVal.use_empty()) {
6612 ps <<
"/* unused */";
6613 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6614 portVal.getUses().begin()->getOwner())) {
6617 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6619 parentPortList.atOutput(outputPortNo).getVerilogName());
6621 portVal = portVal.getUsers().begin()->getOperand(0);
6622 ExprEmitter(*
this, ops)
6623 .emitExpression(portVal, LowestPrecedence,
6627 ExprEmitter(*
this, ops)
6628 .emitExpression(portVal, LowestPrecedence,
6641 ps.addCallback({op,
false});
6642 setPendingNewline();
6645void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6647 emitError(op,
"SV attributes emission is unimplemented for the op");
6649 auto instance = op.getReferencedInstance(&state.symbolCache);
6650 auto instantiator = instance->getParentOfType<HWModuleOp>().
getName();
6651 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6652 instance.getInterfaceType().getInterface());
6654 ps.addCallback({op,
true});
6655 ps <<
"bind " << PPExtString(instantiator) << PP::nbsp
6656 << PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6657 << PPExtString(
getSymOpName(instance)) <<
" (.*);" << PP::newline;
6658 ps.addCallback({op,
false});
6659 setPendingNewline();
6662void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6666 auto printParamType = [&](Type type, Attribute defaultValue,
6667 SmallString<8> &result) {
6669 llvm::raw_svector_ostream sstream(result);
6674 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6675 if (intAttr.getValue().getBitWidth() == 32)
6677 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6678 if (fpAttr.getType().isF64())
6681 if (isa<NoneType>(type))
6689 if (intType.getWidth() == 32) {
6690 sstream <<
"/*integer*/";
6694 printPackedType(type, sstream, module->getLoc(),
6702 size_t maxTypeWidth = 0;
6703 SmallString<8> scratch;
6704 for (
auto param : params) {
6705 auto paramAttr = cast<ParamDeclAttr>(param);
6707 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6708 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6711 if (maxTypeWidth > 0)
6714 ps.scopedBox(PP::bbox2, [&]() {
6715 ps << PP::newline <<
"#(";
6716 ps.scopedBox(PP::cbox0, [&]() {
6719 [&](Attribute param) {
6720 auto paramAttr = cast<ParamDeclAttr>(param);
6721 auto defaultValue = paramAttr.getValue();
6723 printParamType(paramAttr.getType(), defaultValue, scratch);
6724 if (!scratch.empty())
6726 if (scratch.size() < maxTypeWidth)
6727 ps.nbsp(maxTypeWidth - scratch.size());
6730 module, paramAttr.getName()));
6734 ps.invokeWithStringOS([&](
auto &os) {
6736 return module->emitError("parameter '")
6737 << paramAttr.getName().getValue()
6738 << "' has invalid value";
6743 [&]() { ps <<
"," << PP::newline; });
6749void ModuleEmitter::emitPortList(Operation *module,
6750 const ModulePortInfo &portInfo,
6751 bool emitAsTwoStateType) {
6753 if (portInfo.
size())
6754 emitLocationInfo(module->getLoc());
6758 bool hasOutputs =
false, hasZeroWidth =
false;
6759 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6760 SmallVector<SmallString<8>, 16> portTypeStrings;
6762 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6763 auto port = portInfo.
at(i);
6764 hasOutputs |= port.isOutput();
6767 lastNonZeroPort = i;
6770 portTypeStrings.push_back({});
6772 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6774 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6777 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6780 if (maxTypeWidth > 0)
6783 auto getPortSVAttrs = [](
const PortInfo &port) -> ArrayAttr {
6786 auto attrs = port.attrs.getAs<ArrayAttr>(
6787 sv::SVAttributeAttr::getSVAttributesAttrName());
6794 ps.scopedBox(PP::bbox2, [&]() {
6795 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6796 auto lastPort = e - 1;
6799 auto portType = portInfo.
at(portIdx).
type;
6803 bool isZeroWidth =
false;
6808 ps << (isZeroWidth ?
"// " :
" ");
6812 auto thisPortInfo = portInfo.
at(portIdx);
6813 auto svAttrs = getPortSVAttrs(thisPortInfo);
6814 if (svAttrs && !isZeroWidth) {
6820 auto thisPortDirection = thisPortInfo.dir;
6821 bool isInOut = isa<InOutType, sv::NetType>(portType);
6822 StringRef direction;
6824 direction = hasOutputs ?
"inout " :
"inout ";
6826 switch (thisPortDirection) {
6827 case ModulePort::Direction::Output:
6828 direction =
"output ";
6830 case ModulePort::Direction::Input:
6831 direction = hasOutputs ?
"input " :
"input ";
6842 portDecl = emitAsTwoStateType ?
"var " :
"var logic ";
6847 size_t startOfNamePos = direction.size() + portDecl.size() + maxTypeWidth;
6851 if (!isa<ModportType>(portType)) {
6853 if (!portDecl.empty())
6855 if (!portTypeStrings[portIdx].
empty())
6856 ps << portTypeStrings[portIdx];
6857 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6858 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6860 ps << portTypeStrings[portIdx];
6861 if (portTypeStrings[portIdx].size() < startOfNamePos)
6862 ps.nbsp(startOfNamePos - portTypeStrings[portIdx].size());
6869 ps.invokeWithStringOS(
6870 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6873 auto innerSym = portInfo.
at(portIdx).
getSym();
6874 if (state.options.
printDebugInfo && innerSym && !innerSym.empty()) {
6876 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6881 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6885 if (
auto loc = portInfo.
at(portIdx).
loc)
6886 emitLocationInfo(loc);
6900 while (portIdx != e && !getPortSVAttrs(portInfo.
at(portIdx)) &&
6901 portInfo.
at(portIdx).
dir == thisPortDirection &&
6903 isa<InOutType, sv::NetType>(portInfo.
at(portIdx).
type) &&
6906 auto port = portInfo.
at(portIdx);
6910 bool isZeroWidth =
false;
6915 ps << (isZeroWidth ?
"// " :
" ");
6918 ps.nbsp(startOfNamePos);
6921 StringRef name = port.getVerilogName();
6922 ps << PPExtString(name);
6925 ps.invokeWithStringOS(
6926 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6929 auto sym = port.getSym();
6931 ps <<
" /* inner_sym: " << PPExtString(sym.getSymName().getValue())
6935 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6939 if (
auto loc = port.loc)
6940 emitLocationInfo(loc);
6951 if (!portInfo.
size()) {
6953 SmallPtrSet<Operation *, 8> moduleOpSet;
6954 moduleOpSet.insert(module);
6955 emitLocationInfoAndNewLine(moduleOpSet);
6958 ps <<
");" << PP::newline;
6959 setPendingNewline();
6963void ModuleEmitter::emitHWModule(HWModuleOp module) {
6964 currentModuleOp =
module;
6966 emitComment(module.getCommentAttr());
6967 emitSVAttributes(module);
6969 ps.addCallback({module,
true});
6973 emitParameters(module, module.getParameters());
6975 emitPortList(module, ModulePortInfo(module.getPortList()));
6977 assert(state.pendingNewline);
6980 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6983 ps.addCallback({module,
false});
6985 setPendingNewline();
6987 currentModuleOp =
nullptr;
6990void ModuleEmitter::emitFunc(FuncOp func) {
6992 if (func.isDeclaration())
6995 currentModuleOp = func;
6997 ps.addCallback({func,
true});
7001 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
7003 ps <<
"endfunction";
7005 currentModuleOp =
nullptr;
7014 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
7021 void emit(emit::FileListOp op);
7026 void emitOp(emit::RefOp op);
7027 void emitOp(emit::VerbatimOp op);
7031 for (Operation &op : *block) {
7032 TypeSwitch<Operation *>(&op)
7033 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
7034 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
7035 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7036 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7037 .Case<BindInterfaceOp>(
7038 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
7039 .Case<TypeScopeOp>([&](
auto typedecls) {
7040 ModuleEmitter(state).emitStatement(typedecls);
7043 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
7049 for (
auto sym : op.getFiles()) {
7050 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
7052 auto it = state.fileMapping.find(fileName);
7053 if (it == state.fileMapping.end()) {
7054 emitOpError(op,
" references an invalid file: ") << sym;
7058 auto file = cast<emit::FileOp>(it->second);
7066 StringAttr target = op.getTargetAttr().getAttr();
7067 auto *targetOp = state.symbolCache.getDefinition(target);
7068 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
7070 TypeSwitch<Operation *>(targetOp)
7071 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
7072 .Case<hw::HWModuleOp>(
7073 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
7074 .Case<TypeScopeOp, PackageOp>([&](
auto typedecls) {
7075 ModuleEmitter(state).emitStatement(typedecls);
7078 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
7084 SmallPtrSet<Operation *, 8> ops;
7089 StringRef text = op.getText();
7093 const auto &[lhs, rhs] = text.split(
'\n');
7099 }
while (!text.empty());
7102 emitLocationInfoAndNewLine(ops);
7120 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
7121 moduleOp->walk([&](Operation *op) {
7123 if (
auto name = op->getAttrOfType<InnerSymAttr>(
7124 hw::InnerSymbolTable::getInnerSymbolAttrName()))
7126 cast<mlir::SymbolOpInterface>(moduleOp).getNameAttr(),
7127 name.getSymName(), op);
7128 if (isa<BindOp>(op))
7134 auto collectPorts = [&](
auto moduleOp) {
7135 auto portInfo = moduleOp.getPortList();
7136 for (
auto [i, p] : llvm::enumerate(portInfo)) {
7137 if (!p.attrs || p.attrs.empty())
7139 for (NamedAttribute portAttr : p.attrs) {
7140 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
7141 symbolCache.addDefinition(moduleOp.getNameAttr(), sym.getSymName(),
7149 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
7150 for (
auto file :
designOp.getOps<emit::FileOp>())
7151 for (
auto refs : file.getOps<emit::RefOp>())
7152 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
7154 SmallString<32> outputPath;
7155 for (
auto &op : *
designOp.getBody()) {
7158 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
7160 bool hasFileName =
false;
7161 bool emitReplicatedOps = !isFileOp;
7162 bool addToFilelist = !isFileOp;
7168 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
7170 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
7171 <<
" on " << op <<
"\n";);
7172 if (!attr.isDirectory())
7175 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
7176 addToFilelist = !attr.getExcludeFromFilelist().getValue();
7179 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
7184 if (!defaultFileName.isTriviallyEmpty()) {
7185 llvm::sys::path::append(outputPath, defaultFileName);
7187 op->emitError(
"file name unspecified");
7189 llvm::sys::path::append(outputPath,
"error.out");
7193 auto destFile = StringAttr::get(op->getContext(), outputPath);
7194 auto &file =
files[destFile];
7195 file.ops.push_back(info);
7196 file.emitReplicatedOps = emitReplicatedOps;
7197 file.addToFilelist = addToFilelist;
7198 file.isVerilog = outputPath.ends_with(
".sv");
7203 if (!attr || attr.isDirectory()) {
7204 auto excludeFromFileListAttr =
7205 BoolAttr::get(op->getContext(), !addToFilelist);
7206 auto includeReplicatedOpsAttr =
7207 BoolAttr::get(op->getContext(), emitReplicatedOps);
7208 auto outputFileAttr = hw::OutputFileAttr::get(
7209 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
7210 op->setAttr(
"output_file", outputFileAttr);
7216 TypeSwitch<Operation *>(&op)
7217 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
7219 fileMapping.try_emplace(file.getSymNameAttr(), file);
7220 separateFile(file, file.getFileName());
7222 .Case<emit::FragmentOp>([&](
auto fragment) {
7225 .Case<HWModuleOp>([&](
auto mod) {
7227 auto sym = mod.getNameAttr();
7230 collectInstanceSymbolsAndBinds(mod);
7232 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
7233 if (it->second.size() != 1 || attr) {
7236 op.emitError(
"modules can be emitted to a single file");
7244 if (attr || separateModules)
7250 .Case<InterfaceOp>([&](InterfaceOp intf) {
7252 symbolCache.addDefinition(intf.getNameAttr(), intf);
7255 for (
auto &op : *intf.getBodyBlock())
7256 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
7257 if (
auto name = symOp.getNameAttr())
7261 if (attr || separateModules)
7262 separateFile(intf, intf.getSymName() +
".sv");
7266 .Case<PackageOp>([&](PackageOp package) {
7268 auto sym = package.getSymNameAttr();
7271 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
7272 if (it->second.size() != 1 || attr) {
7274 "packages can only be emitted to a single file");
7283 if (attr || separateModules)
7288 .Case<PackageExternOp>([&](PackageExternOp package) {
7289 symbolCache.addDefinition(package.getSymNameAttr(), package);
7293 separateFile(op, op.getOutputFile().getFilename().getValue());
7295 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7301 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
7302 [&](Operation *op) {
7308 separateFile(op,
"");
7310 .Case<FuncOp>([&](
auto op) {
7316 separateFile(op,
"");
7320 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
7321 symbolCache.addDefinition(schemaOp.getNameAttr(), schemaOp);
7323 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
7324 symbolCache.addDefinition(hierPathOp.getSymNameAttr(), hierPathOp);
7327 symbolCache.addDefinition(op.getSymNameAttr(), op);
7332 separateFile(op,
"");
7334 .Case<BindOp>([&](
auto op) {
7336 separateFile(op,
"bindfile.sv");
7341 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
7342 .Case<MacroDeclOp>([&](
auto op) {
7343 symbolCache.addDefinition(op.getSymNameAttr(), op);
7345 .Case<sv::ReserveNamesOp>([](
auto op) {
7348 .Case<om::ClassLike>([&](
auto op) {
7349 symbolCache.addDefinition(op.getSymNameAttr(), op);
7351 .Case<om::ConstantOp>([&](
auto op) {
7354 .Default([&](
auto *) {
7355 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
7375 size_t lastReplicatedOp = 0;
7377 bool emitHeaderInclude =
7380 if (emitHeaderInclude)
7383 size_t numReplicatedOps =
7388 DenseSet<emit::FragmentOp> includedFragments;
7389 for (
const auto &opInfo : file.
ops) {
7390 Operation *op = opInfo.op;
7394 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7400 if (
auto fragments =
7402 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7406 op->emitError(
"cannot find referenced fragment ") << sym;
7409 emit::FragmentOp fragment = it->second;
7410 if (includedFragments.insert(fragment).second) {
7411 thingsToEmit.emplace_back(it->second);
7417 thingsToEmit.emplace_back(op);
7422 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7427 TypeSwitch<Operation *>(op)
7428 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7429 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp, PackageExternOp>(
7433 .Case<HWModuleGeneratedOp>(
7434 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7435 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7436 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7437 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7438 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7439 .Case<TypeScopeOp, PackageOp>([&](
auto typedecls) {
7440 ModuleEmitter(state).emitStatement(typedecls);
7442 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7444 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7445 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7446 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7447 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7448 .Default([&](
auto *op) {
7449 state.encounteredError =
true;
7450 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7457 llvm::formatted_raw_ostream &os,
7458 StringAttr fileName,
bool parallelize) {
7463 parallelize &=
context->isMultithreadingEnabled();
7474 size_t lineOffset = 0;
7475 for (
auto &entry : thingsToEmit) {
7476 entry.verilogLocs.setStream(os);
7477 if (
auto *op = entry.getOperation()) {
7482 state.addVerilogLocToOps(lineOffset, fileName);
7484 os << entry.getStringData();
7489 if (state.encounteredError)
7507 SmallString<256> buffer;
7508 llvm::raw_svector_ostream tmpStream(buffer);
7509 llvm::formatted_raw_ostream rs(tmpStream);
7517 if (state.encounteredError)
7522 for (
auto &entry : thingsToEmit) {
7525 auto *op = entry.getOperation();
7527 auto lineOffset = os.getLine() + 1;
7528 os << entry.getStringData();
7532 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7535 entry.verilogLocs.setStream(os);
7542 state.addVerilogLocToOps(0, fileName);
7543 if (state.encounteredError) {
7562 module.emitWarning()
7563 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7564 "created only at SplitExportVerilog";
7573 for (
const auto &it : emitter.
files) {
7574 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7575 "\" ----- 8< -----\n\n");
7581 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7582 "\" ----- 8< -----\n\n");
7583 for (
auto &name : it.second)
7584 contents += name.str() +
"\n";
7585 list.emplace_back(contents);
7588 llvm::formatted_raw_ostream rs(os);
7592 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7598 LoweringOptions options(module);
7599 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7601 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7602 if (failed(failableParallelForEach(
7603 module->getContext(), modulesToPrepare,
7604 [&](
auto op) { return prepareHWModule(op, options); })))
7611struct ExportVerilogPass
7612 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7613 ExportVerilogPass(raw_ostream &os) : os(os) {}
7614 void runOnOperation()
override {
7616 mlir::OpPassManager preparePM(
"builtin.module");
7617 preparePM.addPass(createLegalizeAnonEnums());
7618 auto &modulePM = preparePM.nestAny();
7619 modulePM.addPass(createPrepareForEmission());
7620 if (failed(runPipeline(preparePM, getOperation())))
7621 return signalPassFailure();
7624 return signalPassFailure();
7631struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7632 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7633 : ExportVerilogPass{*os} {
7634 owned = std::move(os);
7638 std::unique_ptr<llvm::raw_ostream> owned;
7642std::unique_ptr<mlir::Pass>
7644 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7647std::unique_ptr<mlir::Pass>
7649 return std::make_unique<ExportVerilogPass>(os);
7660static std::unique_ptr<llvm::ToolOutputFile>
7664 SmallString<128> outputFilename(dirname);
7666 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7669 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7671 emitter.
designOp.emitError(
"cannot create output directory \"")
7672 << outputDir <<
"\": " << error.message();
7678 std::string errorMessage;
7679 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7681 emitter.
designOp.emitError(errorMessage);
7698 llvm::formatted_raw_ostream rs(output->os());
7704 StringAttr::get(fileName.getContext(), output->getFilename()),
7710 StringRef dirname) {
7721 bool insertSuccess =
7723 .insert({StringAttr::get(module.getContext(),
circtHeader),
7729 if (!insertSuccess) {
7730 module.emitError() << "tried to emit a heder to " << circtHeader
7731 << ", but the file is used as an output too.";
7737 parallelForEach(module->getContext(), emitter.
files.begin(),
7738 emitter.
files.end(), [&](
auto &it) {
7739 createSplitOutputFile(it.first, it.second, dirname,
7744 SmallString<128> filelistPath(dirname);
7745 llvm::sys::path::append(filelistPath,
"filelist.f");
7747 std::string errorMessage;
7748 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7750 module->emitError(errorMessage);
7754 for (
const auto &it : emitter.
files) {
7755 if (it.second.addToFilelist)
7756 output->os() << it.first.str() <<
"\n";
7765 for (
auto &name : it.second)
7766 output->os() << name.str() <<
"\n";
7774 LoweringOptions options(module);
7775 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7777 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7778 if (failed(failableParallelForEach(
7779 module->getContext(), modulesToPrepare,
7780 [&](
auto op) { return prepareHWModule(op, options); })))
7788struct ExportSplitVerilogPass
7789 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7790 ExportSplitVerilogPass(StringRef directory) {
7791 directoryName = directory.str();
7793 void runOnOperation()
override {
7795 mlir::OpPassManager preparePM(
"builtin.module");
7797 auto &modulePM = preparePM.nest<hw::HWModuleOp>();
7798 modulePM.addPass(createPrepareForEmission());
7799 if (failed(runPipeline(preparePM, getOperation())))
7800 return signalPassFailure();
7803 return signalPassFailure();
7808std::unique_ptr<mlir::Pass>
7810 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static bool hasSVAttributes(Operation *op)
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if the element type is emitted as a type name or a keyword (struct, union,...
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 hasExplicitDataType(Type type)
Returns true if type prints its own SystemVerilog data_type, so its declaration must not be prefixed ...
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)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
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.
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.
HWSymbolCache::Item getInnerDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name) const
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
auto * getListener() const
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
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...
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.
BaseTy type_cast(Type type)
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
BaseTy type_dyn_cast(Type type)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
Recursively remove HW type aliases from a type and its subelements.
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)
StringAttr getRenamedFieldName(StringAttr fieldName)
std::string getEnumFieldName(hw::EnumFieldAttr attr, Operation *currentPackage=nullptr)
Returns the field name for an enum field of a given enum field attr.
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 noAlwaysComb
If true, emits sv.alwayscomb as Verilog always @(*) statements.
bool printDebugInfo
Print debug info.
LocationInfoStyle
This option controls emitted location information style.
enum circt::LoweringOptions::EmitPortType emitPortType
bool disallowPortDeclSharing
If true, every port is declared separately (each includes direction and type (e.g....
std::optional< unsigned > getEmittedLineLength() const
Return std::nullopt if not wrapping, else the target emission line length.
bool explicitBitcast
Add an explicit bitcast for avoiding bitwidth mismatch LINT errors.
bool disallowReplicate
If true, expand comb.replicate into an explicit concatenation instead of emitting the {N{....
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 disallowDeclAssignments
bool alwaysEmitBeginEnd
If true, always emit begin/end blocks around if/else/always bodies, even when there is only a single ...
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...
bool emitBindComments
If true, emit a comment wherever an instance wasn't printed, because it's emitted elsewhere as a bind...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
PortDirectionRange getOutputs()
StringRef getVerilogName() const
InnerSymAttr getSym() const
SmallVectorImpl< Token > BufferVec
void flush(PrettyPrinter &pp)
Flush buffered tokens to the specified pretty printer.
String wrapper to indicate string has external storage.