40#include "mlir/IR/BuiltinOps.h"
41#include "mlir/IR/ImplicitLocOpBuilder.h"
42#include "mlir/IR/Location.h"
43#include "mlir/IR/Threading.h"
44#include "mlir/Interfaces/FunctionImplementation.h"
45#include "mlir/Pass/PassManager.h"
46#include "mlir/Support/FileUtilities.h"
47#include "llvm/ADT/MapVector.h"
48#include "llvm/ADT/STLExtras.h"
49#include "llvm/ADT/StringSet.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/Support/FileSystem.h"
52#include "llvm/Support/FormattedStream.h"
53#include "llvm/Support/Path.h"
54#include "llvm/Support/SaveAndRestore.h"
55#include "llvm/Support/ToolOutputFile.h"
56#include "llvm/Support/raw_ostream.h"
59#define GEN_PASS_DEF_EXPORTSPLITVERILOG
60#define GEN_PASS_DEF_EXPORTVERILOG
61#include "circt/Conversion/Passes.h.inc"
68using namespace ExportVerilog;
70using namespace pretty;
72#define DEBUG_TYPE "export-verilog"
80enum VerilogPrecedence {
101enum SubExprSignResult { IsSigned, IsUnsigned };
107 VerilogPrecedence precedence;
110 SubExprSignResult signedness;
112 SubExprInfo(VerilogPrecedence precedence, SubExprSignResult signedness)
113 : precedence(precedence), signedness(signedness) {}
123 return Builder(ctx).getI32IntegerAttr(value);
126static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
127 return Builder(ctx).getIntegerAttr(t, value);
126static TypedAttr
getIntAttr(MLIRContext *ctx, Type t,
const APInt &value) {
…}
143 if (isa<VerbatimExprOp>(op)) {
144 if (op->getNumOperands() == 0 &&
145 op->getAttrOfType<StringAttr>(
"format_string").getValue().size() <= 32)
150 if (isa<XMRRefOp>(op))
154 if (isa<MacroRefExprOp>(op))
164 if (op->getNumOperands() == 0)
168 if (isa<comb::ExtractOp, hw::StructExtractOp, hw::UnionExtractOp>(op))
172 if (
auto array = dyn_cast<hw::ArrayGetOp>(op)) {
173 auto *indexOp = array.getIndex().getDefiningOp();
174 if (!indexOp || isa<ConstantOp>(indexOp))
176 if (
auto read = dyn_cast<ReadInOutOp>(indexOp)) {
177 auto *readSrc = read.getInput().getDefiningOp();
179 return !readSrc || isa<sv::WireOp, LogicOp>(readSrc);
194 if (
auto attr = symOp->getAttrOfType<StringAttr>(
"hw.verilogName"))
195 return attr.getValue();
196 return TypeSwitch<Operation *, StringRef>(symOp)
199 .Case<InterfaceOp>([&](InterfaceOp op) {
202 .Case<InterfaceSignalOp>(
203 [&](InterfaceSignalOp op) {
return op.getSymName(); })
204 .Case<InterfaceModportOp>(
205 [&](InterfaceModportOp op) {
return op.getSymName(); })
206 .Default([&](Operation *op) {
207 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
208 return attr.getValue();
209 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
210 return attr.getValue();
211 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
212 return attr.getValue();
214 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
215 return attr.getValue();
216 return StringRef(
"");
221template <
typename PPS>
223 os <<
"/*Zero width*/ 1\'b0";
228 auto hml = cast<HWModuleLike>(module);
229 return hml.getPort(portArgNum).getVerilogName();
234 auto hml = cast<HWModuleLike>(module);
235 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
236 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
237 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
238 return updatedName.getValue();
239 return hml.getHWModuleType().getPortName(pId);
248 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
249 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
250 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp,
251 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
252 UnpackedOpenArrayCastOp>(op))
256 if (isa<verif::ContractOp>(op))
266static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
268 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
269 if (integer.getWidth() != 1)
270 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
273 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
274 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
279 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
280 dims.push_back(intType.getWidth());
284 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
285 return getTypeDims(dims, inout.getElementType(), loc);
286 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
287 return getTypeDims(dims, uarray.getElementType(), loc);
288 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
289 return getTypeDims(dims, uarray.getElementType(), loc);
291 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
294 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
266static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type, {
…}
300 SmallVector<Attribute, 4> aDims;
303 SmallVector<Attribute, 4> bDims;
306 return aDims == bDims;
312 if (
auto intType = dyn_cast<IntegerType>(type))
313 return intType.getWidth() == 0;
314 if (
auto inout = dyn_cast<hw::InOutType>(type))
316 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
317 return uarray.getNumElements() == 0 ||
319 if (
auto array = dyn_cast<hw::ArrayType>(type))
320 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
321 if (
auto structType = dyn_cast<hw::StructType>(type))
322 return llvm::all_of(structType.getElements(),
323 [](
auto elem) { return isZeroBitType(elem.type); });
324 if (
auto enumType = dyn_cast<hw::EnumType>(type))
325 return enumType.getFields().empty();
326 if (
auto unionType = dyn_cast<hw::UnionType>(type))
327 return hw::getBitWidth(unionType) == 0;
339 return TypeSwitch<Type, Type>(type)
340 .Case<InOutType>([](InOutType inoutType) {
343 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
346 .Default([](Type type) {
return type; });
351 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
352 auto elementType = cast<hw::InOutType>(type).getElementType();
358 return TypeSwitch<Type, bool>(type)
359 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
362 .Case<StructType>([](
auto) {
return true; })
363 .Default([](
auto) {
return false; });
377 if (
auto name = lhs.getName().compare(rhs.getName()))
379 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
384 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
386 if (lhs.getLine() != rhs.getLine())
387 return lhs.getLine() < rhs.getLine() ? -1 : 1;
388 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
393 Location lhsCallee = lhs.getCallee();
394 Location rhsCallee = rhs.getCallee();
398 Location lhsCaller = lhs.getCaller();
399 Location rhsCaller = rhs.getCaller();
403template <
typename TTargetLoc>
405 auto lhsT = dyn_cast<TTargetLoc>(lhs);
406 auto rhsT = dyn_cast<TTargetLoc>(rhs);
433 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
438 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
443 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
460 SmallPtrSetImpl<Attribute> &locationSet) {
461 llvm::TypeSwitch<Location, void>(loc)
462 .Case<FusedLoc>([&](
auto fusedLoc) {
463 for (
auto subLoc : fusedLoc.getLocations())
466 .Default([&](
auto loc) { locationSet.insert(loc); });
470template <
typename TVector>
472 llvm::array_pod_sort(
473 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
474 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
482 SmallPtrSet<Attribute, 8> locationSet;
483 locationSet.insert(loc);
484 llvm::raw_string_ostream os(
output);
490 const SmallPtrSetImpl<Operation *> &ops) {
494 SmallPtrSet<Attribute, 8> locationSet;
497 llvm::raw_string_ostream os(
output);
506 const SmallPtrSetImpl<Attribute> &locationSet) {
507 if (style == LoweringOptions::LocationInfoStyle::None)
510 llvm::raw_string_ostream sstr(resstr);
512 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
516 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
517 "other styles must be already handled");
518 os <<
"@[" << resstr <<
"]";
527 const SmallPtrSetImpl<Attribute> &locationSet)
543 bool withName = !loc.getName().empty();
545 os <<
"'" << loc.getName().strref() <<
"'(";
554 os << loc.getFilename().getValue();
555 if (
auto line = loc.getLine()) {
557 if (
auto col = loc.getColumn())
569 StringRef lastFileName;
570 for (
size_t i = 0, e = locVector.size(); i != e;) {
575 auto first = locVector[i];
576 if (first.getFilename() != lastFileName) {
577 lastFileName = first.getFilename();
584 first.getFilename() == locVector[
end].getFilename() &&
585 first.getLine() == locVector[
end].getLine())
590 if (
auto line = first.getLine()) {
592 if (
auto col = first.getColumn())
600 os <<
':' << first.getLine() <<
":{";
602 os << locVector[i++].getColumn();
614 llvm::TypeSwitch<Location, void>(loc)
615 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
617 .Case<mlir::FusedLoc>([&](
auto loc) {
618 SmallPtrSet<Attribute, 8> locationSet;
622 .Default([&](
auto loc) {
634 switch (locationSet.size()) {
645 SmallVector<FileLineColLoc, 8> flcLocs;
646 SmallVector<Attribute, 8> otherLocs;
647 flcLocs.reserve(locationSet.size());
648 otherLocs.reserve(locationSet.size());
649 for (Attribute loc : locationSet) {
650 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
651 flcLocs.push_back(flcLoc);
653 otherLocs.push_back(loc);
664 size_t sstrSize =
os.tell();
665 bool emittedAnything =
false;
666 auto recheckEmittedSomething = [&]() {
667 size_t currSize =
os.tell();
668 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
669 emittedAnything |= emittedSomethingSinceLastCheck;
671 return emittedSomethingSinceLastCheck;
680 if (recheckEmittedSomething()) {
682 recheckEmittedSomething();
688 if (emittedAnything && !flcLocs.empty())
693 llvm::raw_string_ostream &
os;
705 if (isa<BlockArgument>(v))
714 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
719 if (v.getDefiningOp<ReadInterfaceSignalOp>())
732 if (
auto cast = dyn_cast<BitcastOp>(op))
733 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
737 if (op->hasOneUse() &&
738 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
746 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp>(op))
751 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
755 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
756 if (verbatim.getFormatString().size() > 32)
761 for (
auto &use : op->getUses()) {
762 auto *user = use.getOwner();
772 UnionExtractOp, IndexedPartSelectOp>(user))
773 if (use.getOperandNumber() == 0 &&
784 auto usedInExprControl = [user, &use]() {
785 return TypeSwitch<Operation *, bool>(user)
786 .Case<ltl::ClockOp>([&](
auto clockOp) {
788 return clockOp.getClock() == use.get();
790 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
791 sv::CoverConcurrentOp>(
792 [&](
auto op) {
return op.getClock() == use.get(); })
793 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
794 sv::CoverPropertyOp>([&](
auto op) {
795 return op.getDisable() == use.get() || op.getClock() == use.get();
797 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
802 .Default([](
auto) {
return false; });
805 if (!usedInExprControl())
809 auto read = dyn_cast<ReadInOutOp>(op);
812 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
823 unsigned numStatements = 0;
824 block.walk([&](Operation *op) {
826 return WalkResult::advance();
828 TypeSwitch<Operation *, unsigned>(op)
829 .Case<VerbatimOp>([&](
auto) {
835 .Case<IfOp>([&](
auto) {
846 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
847 .Case<OutputOp>([&](OutputOp oop) {
850 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
851 Operation *op = operand.getDefiningOp();
852 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
855 .Default([](
auto) {
return 1; });
856 if (numStatements > 1)
857 return WalkResult::interrupt();
858 return WalkResult::advance();
860 if (numStatements == 0)
862 if (numStatements == 1)
872 if (op->getResult(0).use_empty())
877 if (op->hasOneUse() &&
878 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
879 *op->getUsers().begin()))
901 for (
auto &op : *elseBlock) {
902 if (
auto opIf = dyn_cast<IfOp>(op)) {
919template <
typename PPS>
921 enum Container { NoContainer, InComment, InAttr };
922 Container currentContainer = NoContainer;
924 auto closeContainer = [&] {
925 if (currentContainer == NoContainer)
927 if (currentContainer == InComment)
929 else if (currentContainer == InAttr)
931 ps << PP::end << PP::end;
933 currentContainer = NoContainer;
936 bool isFirstContainer =
true;
937 auto openContainer = [&](Container newContainer) {
938 assert(newContainer != NoContainer);
939 if (currentContainer == newContainer)
943 if (!isFirstContainer)
944 ps << (mayBreak ? PP::space : PP::nbsp);
945 isFirstContainer =
false;
948 if (newContainer == InComment)
950 else if (newContainer == InAttr)
952 currentContainer = newContainer;
960 ps.scopedBox(PP::cbox0, [&]() {
961 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
962 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
964 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
966 if (attr.getExpression())
967 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
976 if (
auto *op = val.getDefiningOp())
979 if (
auto port = dyn_cast<BlockArgument>(val)) {
981 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
982 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
984 port.getArgNumber());
986 assert(
false &&
"unhandled value");
998class VerilogEmitterState {
1000 explicit VerilogEmitterState(ModuleOp designOp,
1006 llvm::formatted_raw_ostream &os,
1007 StringAttr fileName,
OpLocMap &verilogLocMap)
1008 : designOp(designOp), shared(shared), options(options),
1009 symbolCache(symbolCache), globalNames(globalNames),
1010 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1011 pp(os, options.emittedLineLength), fileName(fileName) {
1012 pp.setListener(&saver);
1035 llvm::formatted_raw_ostream &os;
1037 bool encounteredError =
false;
1046 bool pendingNewline =
false;
1060 StringAttr fileName;
1066 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1069 verilogLocMap.
clear();
1073 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1074 void operator=(
const VerilogEmitterState &) =
delete;
1087using CallbackDataTy = std::pair<Operation *, bool>;
1091 VerilogEmitterState &state;
1096 explicit EmitterBase(VerilogEmitterState &state)
1098 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1100 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1101 state.encounteredError =
true;
1102 return op->emitError(message);
1105 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1106 state.encounteredError =
true;
1107 return op->emitOpError(message);
1110 void emitLocationImpl(llvm::StringRef location) {
1113 ps << PP::neverbreak;
1114 if (!location.empty())
1115 ps <<
"\t// " << location;
1118 void emitLocationInfo(Location loc) {
1126 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1129 setPendingNewline();
1132 template <
typename PPS>
1133 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1134 llvm::function_ref<
void(Value)> operandEmitter,
1135 ArrayAttr symAttrs);
1141 void emitComment(StringAttr comment);
1145 void emitPendingNewlineIfNeeded() {
1146 if (state.pendingNewline) {
1147 state.pendingNewline =
false;
1151 void setPendingNewline() {
1152 assert(!state.pendingNewline);
1153 state.pendingNewline =
true;
1156 void startStatement() { emitPendingNewlineIfNeeded(); }
1159 void operator=(
const EmitterBase &) =
delete;
1160 EmitterBase(
const EmitterBase &) =
delete;
1164template <
typename PPS>
1165void EmitterBase::emitTextWithSubstitutions(
1166 PPS &ps, StringRef
string, Operation *op,
1167 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1178 if (
auto *itemOp = item.getOp()) {
1179 if (item.hasPort()) {
1183 if (!symOpName.empty())
1185 emitError(itemOp,
"cannot get name for symbol ") << sym;
1187 emitError(op,
"cannot get name for symbol ") << sym;
1189 return StringRef(
"<INVALID>");
1195 unsigned numSymOps = symAttrs.size();
1196 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1199 next =
string.find(
"{{", next);
1200 if (next == StringRef::npos)
1207 while (next <
string.size() &&
isdigit(
string[next]))
1210 if (start == next) {
1214 size_t operandNoLength = next - start;
1217 StringRef fmtOptsStr;
1218 if (
string[next] ==
':') {
1219 size_t startFmtOpts = next + 1;
1220 while (next <
string.size() &&
string[next] !=
'}')
1222 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1226 if (!
string.substr(next).starts_with(
"}}"))
1230 unsigned operandNo = 0;
1231 if (
string.drop_front(start)
1232 .take_front(operandNoLength)
1233 .getAsInteger(10, operandNo)) {
1234 emitError(op,
"operand substitution too large");
1240 auto before =
string.take_front(start - 2);
1241 if (!before.empty())
1246 if (operandNo < op->getNumOperands())
1248 operandEmitter(op->getOperand(operandNo));
1249 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1250 unsigned symOpNum = operandNo - op->getNumOperands();
1251 auto sym = symAttrs[symOpNum];
1252 StringRef symVerilogName;
1253 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1254 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1255 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1256 auto namepath = globalRef.getNamepathAttr().getValue();
1257 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1260 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1262 auto innerRef = cast<InnerRefAttr>(sym);
1263 auto ref = state.symbolCache.getInnerDefinition(
1264 innerRef.getModule(), innerRef.getName());
1265 ps << namify(innerRef, ref);
1268 symVerilogName = namify(sym, symOp);
1271 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1272 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1274 symVerilogName = namify(sym, symOp);
1276 if (!symVerilogName.empty())
1279 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1283 string =
string.drop_front(next);
1289 while (emitUntilSubstitution())
1293 if (!
string.
empty())
1297void EmitterBase::emitComment(StringAttr comment) {
1304 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1308 auto ref = comment.getValue();
1310 while (!ref.empty()) {
1311 std::tie(line, ref) = ref.split(
"\n");
1318 if (line.size() <= lineLength) {
1320 setPendingNewline();
1331 auto breakPos = line.rfind(
' ', lineLength);
1333 if (breakPos == StringRef::npos) {
1334 breakPos = line.find(
' ', lineLength);
1337 if (breakPos == StringRef::npos)
1338 breakPos = line.size();
1345 setPendingNewline();
1346 breakPos = line.find_first_not_of(
' ', breakPos);
1348 if (breakPos == StringRef::npos)
1351 line = line.drop_front(breakPos);
1361 bool addPrefixUnderScore =
true;
1364 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1368 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1370 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1372 result = StringAttr::get(expr.getContext(), name);
1374 }
else if (
auto *op = expr.getDefiningOp()) {
1376 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1378 result = StringAttr::get(expr.getContext(), name);
1380 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1386 addPrefixUnderScore =
false;
1388 TypeSwitch<Operation *>(op)
1391 .Case([&result](VerbatimExprOp verbatim) {
1392 verbatim.getAsmResultNames([&](Value, StringRef name) {
1393 result = StringAttr::get(verbatim.getContext(), name);
1396 .Case([&result](VerbatimExprSEOp verbatim) {
1397 verbatim.getAsmResultNames([&](Value, StringRef name) {
1398 result = StringAttr::get(verbatim.getContext(), name);
1404 if (
auto operandName =
1407 cast<IntegerType>(extract.getType()).getWidth();
1409 result = StringAttr::get(extract.getContext(),
1410 operandName.strref() +
"_" +
1411 Twine(extract.getLowBit()));
1413 result = StringAttr::get(
1414 extract.getContext(),
1415 operandName.strref() +
"_" +
1416 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1417 Twine(extract.getLowBit()));
1425 if (!result || result.strref().empty())
1429 if (addPrefixUnderScore && result.strref().front() !=
'_')
1430 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1442class ModuleEmitter :
public EmitterBase {
1444 explicit ModuleEmitter(VerilogEmitterState &state)
1445 : EmitterBase(state), currentModuleOp(nullptr),
1449 emitPendingNewlineIfNeeded();
1453 void emitParameters(Operation *module, ArrayAttr params);
1454 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1455 bool emitAsTwoStateType =
false);
1458 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1459 void emitFunc(FuncOp);
1462 void emitStatement(Operation *op);
1463 void emitBind(BindOp op);
1464 void emitBindInterface(BindInterfaceOp op);
1466 void emitSVAttributes(Operation *op);
1469 StringRef getVerilogStructFieldName(StringAttr field) {
1470 return fieldNameResolver.getRenamedFieldName(field).getValue();
1477 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1489 bool printPackedType(Type type, raw_ostream &os, Location loc,
1490 Type optionalAliasType = {},
bool implicitIntType =
true,
1491 bool singleBitDefaultType =
true,
1492 bool emitAsTwoStateType =
false);
1496 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1504 function_ref<InFlightDiagnostic()> emitError);
1507 VerilogPrecedence parenthesizeIfLooserThan,
1508 function_ref<InFlightDiagnostic()> emitError);
1514 Operation *currentModuleOp;
1520 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1526 SmallPtrSet<Operation *, 16> assignsInlined;
1535 const ModuleEmitter &emitter) {
1536 if (isa<RegOp>(op)) {
1541 cast<InOutType>(op->getResult(0).getType()).getElementType();
1548 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1549 while (isa<ArrayType>(innerType.getElementType()))
1550 innerType = cast<ArrayType>(innerType.getElementType());
1551 if (isa<StructType>(innerType.getElementType()) ||
1552 isa<TypeAliasType>(innerType.getElementType()))
1560 if (isa<sv::WireOp>(op))
1562 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1563 return "localparam";
1566 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1567 return interface.getInterfaceType().getInterface().getValue();
1571 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1575 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1577 if (isa<LogicOp>(op)) {
1583 if (isProcedural && !stripAutomatic)
1584 return hasStruct ?
"automatic" :
"automatic logic";
1585 return hasStruct ?
"" :
"logic";
1592 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1595 assert(!emitter.state.options.disallowLocalVariables &&
1596 "automatic variables not allowed");
1600 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1601 :
"automatic logic";
1608static void emitDim(Attribute width, raw_ostream &os, Location loc,
1609 ModuleEmitter &emitter,
bool downTo) {
1611 os <<
"<<invalid type>>";
1614 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1615 if (intAttr.getValue().isZero()) {
1616 os <<
"/*Zero Width*/";
1621 os << (intAttr.getValue().getZExtValue() - 1);
1631 auto typedAttr = dyn_cast<TypedAttr>(width);
1633 mlir::emitError(loc,
"untyped dimension attribute ") << width;
1637 getIntAttr(loc.getContext(), typedAttr.getType(),
1638 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1639 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1643 emitter.printParamValue(width, os, [loc]() {
1644 return mlir::emitError(loc,
"invalid parameter in type");
1608static void emitDim(Attribute width, raw_ostream &os, Location loc, {
…}
1652static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1653 ModuleEmitter &emitter) {
1654 for (Attribute width : dims) {
1655 emitDim(width, os, loc, emitter,
true);
1652static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc, {
…}
1660void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1661 SmallVector<Attribute, 4> dims;
1693 SmallVectorImpl<Attribute> &dims,
1694 bool implicitIntType,
bool singleBitDefaultType,
1695 ModuleEmitter &emitter,
1696 Type optionalAliasType = {},
1697 bool emitAsTwoStateType =
false) {
1698 return TypeSwitch<Type, bool>(type)
1699 .Case<IntegerType>([&](IntegerType integerType) {
1700 if (emitAsTwoStateType && dims.empty()) {
1702 if (!typeName.empty()) {
1707 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1709 getInt32Attr(type.getContext(), integerType.getWidth()));
1711 StringRef typeName =
1712 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1713 if (!typeName.empty()) {
1720 return !dims.empty() || !implicitIntType;
1722 .Case<IntType>([&](IntType intType) {
1723 if (!implicitIntType)
1725 dims.push_back(intType.getWidth());
1729 .Case<ArrayType>([&](ArrayType arrayType) {
1730 dims.push_back(arrayType.getSizeAttr());
1732 implicitIntType, singleBitDefaultType,
1734 emitAsTwoStateType);
1736 .Case<InOutType>([&](InOutType inoutType) {
1738 implicitIntType, singleBitDefaultType,
1740 emitAsTwoStateType);
1742 .Case<EnumType>([&](EnumType enumType) {
1744 if (enumType.getBitWidth() != 32)
1745 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1747 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1748 llvm::interleaveComma(
1749 enumType.getFields().getAsRange<StringAttr>(), os,
1750 [&](
auto enumerator) {
1751 os << emitter.fieldNameResolver.getEnumFieldName(
1752 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1757 .Case<StructType>([&](StructType structType) {
1758 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1759 os <<
"/*Zero Width*/";
1762 os <<
"struct packed {";
1763 for (
auto &element : structType.getElements()) {
1765 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1766 <<
": Zero Width;*/ ";
1769 SmallVector<Attribute, 8> structDims;
1774 {}, emitAsTwoStateType);
1775 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1776 emitter.printUnpackedTypePostfix(element.type, os);
1783 .Case<UnionType>([&](UnionType unionType) {
1784 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1785 os <<
"/*Zero Width*/";
1789 int64_t unionWidth = hw::getBitWidth(unionType);
1790 os <<
"union packed {";
1791 for (
auto &element : unionType.getElements()) {
1793 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1794 <<
": Zero Width;*/ ";
1797 int64_t elementWidth = hw::getBitWidth(element.type);
1798 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1800 os <<
" struct packed {";
1801 if (element.offset) {
1802 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1803 << element.offset - 1 <<
":0] "
1804 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1808 SmallVector<Attribute, 8> structDims;
1812 true, emitter, {}, emitAsTwoStateType);
1813 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1814 emitter.printUnpackedTypePostfix(element.type, os);
1818 if (elementWidth + (int64_t)element.offset < unionWidth) {
1819 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1820 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1821 <<
"__post_padding_" << element.name.getValue() <<
";";
1823 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1832 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1833 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1834 os <<
"<<unexpected unpacked array>>";
1835 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1839 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1840 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1842 mlir::emitError(loc,
"unresolvable type reference");
1845 if (typedecl.getType() != typeRef.getInnerType()) {
1846 mlir::emitError(loc,
"declared type did not match aliased type");
1850 os << typedecl.getPreferredName();
1851 emitDims(dims, os, typedecl->getLoc(), emitter);
1854 .Default([&](Type type) {
1855 os <<
"<<invalid type '" << type <<
"'>>";
1856 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1872bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1873 Type optionalAliasType,
1874 bool implicitIntType,
1875 bool singleBitDefaultType,
1876 bool emitAsTwoStateType) {
1877 SmallVector<Attribute, 8> packedDimensions;
1879 singleBitDefaultType, *
this, optionalAliasType,
1880 emitAsTwoStateType);
1886void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1887 TypeSwitch<Type, void>(type)
1889 printUnpackedTypePostfix(inoutType.getElementType(), os);
1891 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1892 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1893 : state.designOp->getLoc();
1894 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1896 printUnpackedTypePostfix(arrayType.getElementType(), os);
1898 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1900 printUnpackedTypePostfix(arrayType.getElementType(), os);
1902 .Case<InterfaceType>([&](
auto) {
1916ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1917 function_ref<InFlightDiagnostic()> emitError) {
1918 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1926ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1927 VerilogPrecedence parenthesizeIfLooserThan,
1928 function_ref<InFlightDiagnostic()> emitError) {
1929 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1930 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1931 APInt value = intAttr.getValue();
1935 if (intTy.getWidth() > 32) {
1937 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1941 if (intTy.isSigned())
1942 os << intTy.getWidth() <<
"'sd";
1944 os << intTy.getWidth() <<
"'d";
1946 value.print(os, intTy.isSigned());
1947 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1949 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1951 os.write_escaped(strAttr.getValue());
1953 return {Symbol, IsUnsigned};
1955 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1957 os << fpAttr.getValueAsDouble();
1958 return {Symbol, IsUnsigned};
1960 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1961 os << verbatimParam.getValue().getValue();
1962 return {Symbol, IsUnsigned};
1964 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1966 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1967 parameterRef.getName());
1970 return {Symbol, IsUnsigned};
1974 auto expr = dyn_cast<ParamExprAttr>(value);
1976 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1977 emitError() <<
" = " << value;
1978 return {LowestPrecedence, IsUnsigned};
1981 StringRef operatorStr;
1982 StringRef openStr, closeStr;
1983 VerilogPrecedence subprecedence = LowestPrecedence;
1984 VerilogPrecedence prec;
1985 std::optional<SubExprSignResult> operandSign;
1986 bool isUnary =
false;
1987 bool hasOpenClose =
false;
1989 switch (expr.getOpcode()) {
1991 operatorStr =
" + ";
1992 subprecedence = Addition;
1995 operatorStr =
" * ";
1996 subprecedence = Multiply;
1999 operatorStr =
" & ";
2000 subprecedence = And;
2003 operatorStr =
" | ";
2007 operatorStr =
" ^ ";
2008 subprecedence = Xor;
2011 operatorStr =
" << ";
2012 subprecedence = Shift;
2016 operatorStr =
" >> ";
2017 subprecedence = Shift;
2021 operatorStr =
" >>> ";
2022 subprecedence = Shift;
2023 operandSign = IsSigned;
2026 operatorStr =
" / ";
2027 subprecedence = Multiply;
2028 operandSign = IsUnsigned;
2031 operatorStr =
" / ";
2032 subprecedence = Multiply;
2033 operandSign = IsSigned;
2036 operatorStr =
" % ";
2037 subprecedence = Multiply;
2038 operandSign = IsUnsigned;
2041 operatorStr =
" % ";
2042 subprecedence = Multiply;
2043 operandSign = IsSigned;
2046 openStr =
"$clog2(";
2048 operandSign = IsUnsigned;
2049 hasOpenClose =
true;
2052 case PEO::StrConcat:
2055 hasOpenClose =
true;
2058 subprecedence = LowestPrecedence;
2063 prec = subprecedence;
2066 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2068 assert(isUnary || hasOpenClose ||
2069 !llvm::hasSingleElement(expr.getOperands()));
2076 auto emitOperand = [&](Attribute operand) ->
bool {
2078 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2079 if (operandSign.has_value())
2080 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2083 if (operandSign.has_value()) {
2085 signedness = *operandSign;
2087 return signedness == IsSigned;
2091 if (prec > parenthesizeIfLooserThan)
2100 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2101 for (
auto op : expr.getOperands().drop_front()) {
2104 if (expr.getOpcode() == PEO::Add) {
2105 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2106 const APInt &value = integer.getValue();
2107 if (value.isNegative() && !value.isMinSignedValue()) {
2109 allOperandsSigned &=
2110 emitOperand(IntegerAttr::get(op.getType(), -value));
2117 allOperandsSigned &= emitOperand(op);
2121 if (prec > parenthesizeIfLooserThan) {
2125 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2140class ExprEmitter :
public EmitterBase,
2142 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2147 ExprEmitter(ModuleEmitter &emitter,
2148 SmallPtrSetImpl<Operation *> &emittedExprs)
2149 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2151 ExprEmitter(ModuleEmitter &emitter,
2152 SmallPtrSetImpl<Operation *> &emittedExprs,
2154 : EmitterBase(emitter.state), emitter(emitter),
2155 emittedExprs(emittedExprs), buffer(tokens),
2156 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2157 assert(state.pp.getListener() == &state.saver);
2164 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2165 bool isAssignmentLikeContext) {
2166 assert(localTokens.empty());
2168 ps.scopedBox(PP::ibox0, [&]() {
2169 emitSubExpr(exp, parenthesizeIfLooserThan,
2172 isAssignmentLikeContext);
2177 if (&buffer.tokens == &localTokens)
2178 buffer.flush(state.pp);
2183 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2184 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2186 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2194 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2195 SubExprSignRequirement signReq = NoRequirement,
2196 bool isSelfDeterminedUnsignedValue =
false,
2197 bool isAssignmentLikeContext =
false);
2201 void emitSVAttributes(Operation *op);
2203 SubExprInfo visitUnhandledExpr(Operation *op);
2204 SubExprInfo visitInvalidComb(Operation *op) {
2207 SubExprInfo visitUnhandledComb(Operation *op) {
2208 return visitUnhandledExpr(op);
2211 return dispatchSVVisitor(op);
2214 return visitUnhandledExpr(op);
2216 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2219 enum EmitBinaryFlags {
2220 EB_RequireSignedOperands = RequireSigned,
2221 EB_RequireUnsignedOperands = RequireUnsigned,
2222 EB_OperandSignRequirementMask = 0x3,
2227 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2231 EB_ForceResultSigned = 0x8,
2236 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2237 const char *syntax,
unsigned emitBinaryFlags = 0);
2239 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2240 bool resultAlwaysUnsigned =
false);
2243 void emitSubExprIBox2(
2244 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2245 ps.scopedBox(PP::ibox2,
2246 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2251 template <
typename Container,
typename EachFn>
2252 void interleaveComma(
const Container &c, EachFn eachFn) {
2253 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2258 void interleaveComma(ValueRange ops) {
2259 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2276 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2278 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2279 CloseFunc closeFn) {
2281 ps.scopedBox(PP::cbox0, [&]() {
2282 interleaveComma(c, eachFn);
2288 template <
typename OpenFunc,
typename CloseFunc>
2289 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2290 return emitBracedList(
2291 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2295 void emitBracedList(ValueRange ops) {
2296 return emitBracedList(
2297 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2301 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2304 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2305 bool printAsPattern, Operation *op);
2307 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2308 ArrayAttr fieldValues,
bool printAsPattern,
2311 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2313 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2314 SubExprInfo visitSV(GetModportOp op);
2315 SubExprInfo visitSV(SystemFunctionOp op);
2316 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2317 SubExprInfo visitSV(XMROp op);
2318 SubExprInfo visitSV(XMRRefOp op);
2319 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2320 SubExprInfo visitSV(VerbatimExprOp op) {
2321 return visitVerbatimExprOp(op, op.getSymbols());
2323 SubExprInfo visitSV(VerbatimExprSEOp op) {
2324 return visitVerbatimExprOp(op, op.getSymbols());
2326 SubExprInfo visitSV(MacroRefExprOp op);
2327 SubExprInfo visitSV(MacroRefExprSEOp op);
2328 template <
typename MacroTy>
2329 SubExprInfo emitMacroCall(MacroTy op);
2331 SubExprInfo visitSV(ConstantXOp op);
2332 SubExprInfo visitSV(ConstantZOp op);
2333 SubExprInfo visitSV(ConstantStrOp op);
2335 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2336 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2338 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2343 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2344 emitSVAttributes(op);
2347 SubExprInfo visitSV(ArrayIndexInOutOp op);
2348 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2349 SubExprInfo visitSV(IndexedPartSelectOp op);
2350 SubExprInfo visitSV(StructFieldInOutOp op);
2353 SubExprInfo visitSV(SampledOp op);
2356 SubExprInfo visitSV(TimeOp op);
2357 SubExprInfo visitSV(STimeOp op);
2360 using TypeOpVisitor::visitTypeOp;
2362 SubExprInfo visitTypeOp(AggregateConstantOp op);
2364 SubExprInfo visitTypeOp(ParamValueOp op);
2371 SubExprInfo visitTypeOp(StructInjectOp op);
2372 SubExprInfo visitTypeOp(UnionCreateOp op);
2373 SubExprInfo visitTypeOp(UnionExtractOp op);
2374 SubExprInfo visitTypeOp(EnumCmpOp op);
2375 SubExprInfo visitTypeOp(EnumConstantOp op);
2378 using CombinationalVisitor::visitComb;
2379 SubExprInfo visitComb(
MuxOp op);
2380 SubExprInfo visitComb(
AddOp op) {
2381 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2382 return emitBinary(op, Addition,
"+");
2384 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2385 SubExprInfo visitComb(
MulOp op) {
2386 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2387 return emitBinary(op, Multiply,
"*");
2389 SubExprInfo visitComb(
DivUOp op) {
2390 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2392 SubExprInfo visitComb(
DivSOp op) {
2393 return emitBinary(op, Multiply,
"/",
2394 EB_RequireSignedOperands | EB_ForceResultSigned);
2396 SubExprInfo visitComb(
ModUOp op) {
2397 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2399 SubExprInfo visitComb(
ModSOp op) {
2400 return emitBinary(op, Multiply,
"%",
2401 EB_RequireSignedOperands | EB_ForceResultSigned);
2403 SubExprInfo visitComb(
ShlOp op) {
2404 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2406 SubExprInfo visitComb(
ShrUOp op) {
2408 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2410 SubExprInfo visitComb(
ShrSOp op) {
2413 return emitBinary(op, Shift,
">>>",
2414 EB_RequireSignedOperands | EB_ForceResultSigned |
2415 EB_RHS_UnsignedWithSelfDeterminedWidth);
2417 SubExprInfo visitComb(
AndOp op) {
2418 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2419 return emitBinary(op, And,
"&");
2421 SubExprInfo visitComb(
OrOp op) {
2422 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2423 return emitBinary(op, Or,
"|");
2425 SubExprInfo visitComb(
XorOp op) {
2426 if (op.isBinaryNot())
2427 return emitUnary(op,
"~");
2428 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2429 return emitBinary(op, Xor,
"^");
2434 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2436 SubExprInfo visitComb(ReplicateOp op);
2437 SubExprInfo visitComb(
ConcatOp op);
2439 SubExprInfo visitComb(ICmpOp op);
2441 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2442 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2443 "not printed within an assignment-like context");
2444 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2445 "supposed to spill such expressions";
2449 SubExprInfo printStructCreate(
2450 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2452 bool printAsPattern, Operation *op);
2455 ModuleEmitter &emitter;
2462 SubExprSignRequirement signPreference = NoRequirement;
2466 SmallPtrSetImpl<Operation *> &emittedExprs;
2469 SmallVector<Token> localTokens;
2483 bool isAssignmentLikeContext =
false;
2487SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2489 unsigned emitBinaryFlags) {
2491 emitError(op,
"SV attributes emission is unimplemented for the op");
2502 if (emitBinaryFlags & EB_ForceResultSigned)
2503 ps <<
"$signed(" << PP::ibox0;
2504 auto operandSignReq =
2505 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2506 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2508 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2510 ps << lhsSpace << syntax << PP::nbsp;
2517 auto rhsPrec = prec;
2518 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2519 rhsPrec = VerilogPrecedence(prec - 1);
2524 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2525 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2526 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2527 operandSignReq = NoRequirement;
2530 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2531 rhsIsUnsignedValueWithSelfDeterminedWidth);
2535 SubExprSignResult signedness = IsUnsigned;
2536 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2537 signedness = IsSigned;
2539 if (emitBinaryFlags & EB_ForceResultSigned) {
2540 ps << PP::end <<
")";
2541 signedness = IsSigned;
2545 return {prec, signedness};
2548SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2549 bool resultAlwaysUnsigned) {
2551 emitError(op,
"SV attributes emission is unimplemented for the op");
2554 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2558 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2559 resultAlwaysUnsigned ? IsUnsigned : signedness};
2564void ExprEmitter::emitSVAttributes(Operation *op) {
2583 if (constant && constant.getValue().isZero())
2584 return concat.getOperand(1);
2594SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2595 VerilogPrecedence parenthesizeIfLooserThan,
2596 SubExprSignRequirement signRequirement,
2597 bool isSelfDeterminedUnsignedValue,
2598 bool isAssignmentLikeContext) {
2600 if (
auto result = dyn_cast<OpResult>(exp))
2601 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2602 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2603 parenthesizeIfLooserThan, signRequirement,
2604 isSelfDeterminedUnsignedValue,
2605 isAssignmentLikeContext);
2609 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2614 auto *op = exp.getDefiningOp();
2618 if (!shouldEmitInlineExpr) {
2621 if (signRequirement == RequireSigned) {
2623 return {Symbol, IsSigned};
2627 return {Symbol, IsUnsigned};
2630 unsigned subExprStartIndex = buffer.tokens.size();
2632 ps.addCallback({op,
true});
2633 auto done = llvm::make_scope_exit([&]() {
2635 ps.addCallback({op, false});
2641 signPreference = signRequirement;
2643 bool bitCastAdded =
false;
2644 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2646 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2647 ps.addAsString(inType.getWidth());
2648 ps <<
"'(" << PP::ibox0;
2649 bitCastAdded =
true;
2653 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2654 isAssignmentLikeContext);
2655 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2661 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2663 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2665 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2666 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2669 expInfo.signedness = IsSigned;
2670 expInfo.precedence = Selection;
2671 }
else if (signRequirement == RequireUnsigned &&
2672 expInfo.signedness == IsSigned) {
2675 expInfo.signedness = IsUnsigned;
2676 expInfo.precedence = Selection;
2677 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2684 expInfo.precedence = Selection;
2691 emittedExprs.insert(exp.getDefiningOp());
2695SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2696 auto openFn = [&]() {
2698 ps.addAsString(op.getMultiple());
2701 auto closeFn = [&]() { ps <<
"}}"; };
2705 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2706 if (op.getOperand().hasOneUse()) {
2707 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2708 return {Symbol, IsUnsigned};
2711 emitBracedList(op.getOperand(), openFn, closeFn);
2712 return {Symbol, IsUnsigned};
2715SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2716 emitBracedList(op.getOperands());
2717 return {Symbol, IsUnsigned};
2720SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2724 Type toType = op.getType();
2727 ps.invokeWithStringOS(
2728 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2731 return emitSubExpr(op.getInput(), LowestPrecedence);
2734SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2735 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2736 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2737 SubExprSignRequirement signop[] = {
2739 NoRequirement, NoRequirement,
2741 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2743 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2745 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2747 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2748 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2751 if (op.isEqualAllOnes())
2752 return emitUnary(op,
"&",
true);
2755 if (op.isNotEqualZero())
2756 return emitUnary(op,
"|",
true);
2758 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2762 result.signedness = IsUnsigned;
2766SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2768 emitError(op,
"SV attributes emission is unimplemented for the op");
2770 unsigned loBit = op.getLowBit();
2771 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2773 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2774 assert((x.precedence == Symbol ||
2776 "should be handled by isExpressionUnableToInline");
2781 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2785 ps.addAsString(hiBit);
2786 if (hiBit != loBit) {
2788 ps.addAsString(loBit);
2791 return {Unary, IsUnsigned};
2794SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2796 emitError(op,
"SV attributes emission is unimplemented for the op");
2798 auto decl = op.getReferencedDecl(state.symbolCache);
2801 return {Selection, IsUnsigned};
2804SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2806 emitError(op,
"SV attributes emission is unimplemented for the op");
2809 ps.scopedBox(PP::ibox0, [&]() {
2811 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2812 [&]() { ps <<
"," << PP::space; });
2815 return {Symbol, IsUnsigned};
2818SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2820 emitError(op,
"SV attributes emission is unimplemented for the op");
2822 auto decl = op.getReferencedDecl(state.symbolCache);
2826 return {Selection, IsUnsigned};
2829SubExprInfo ExprEmitter::visitSV(XMROp op) {
2831 emitError(op,
"SV attributes emission is unimplemented for the op");
2833 if (op.getIsRooted())
2835 for (
auto s : op.getPath())
2836 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2838 return {Selection, IsUnsigned};
2843SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2845 emitError(op,
"SV attributes emission is unimplemented for the op");
2848 auto globalRef = op.getReferencedPath(&state.symbolCache);
2849 auto namepath = globalRef.getNamepathAttr().getValue();
2850 auto *
module = state.symbolCache.getDefinition(
2851 cast<InnerRefAttr>(namepath.front()).getModule());
2853 for (
auto sym : namepath) {
2855 auto innerRef = cast<InnerRefAttr>(sym);
2856 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2857 innerRef.getName());
2858 if (ref.hasPort()) {
2864 auto leaf = op.getVerbatimSuffixAttr();
2865 if (leaf && leaf.size())
2867 return {Selection, IsUnsigned};
2870SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2872 emitError(op,
"SV attributes emission is unimplemented for the op");
2874 emitTextWithSubstitutions(
2875 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2876 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2878 return {Unary, IsUnsigned};
2881template <
typename MacroTy>
2882SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2884 emitError(op,
"SV attributes emission is unimplemented for the op");
2887 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2888 assert(macroOp &&
"Invalid IR");
2890 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2892 if (!op.getInputs().empty()) {
2894 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2895 emitExpression(val, LowestPrecedence, false);
2899 return {LowestPrecedence, IsUnsigned};
2902SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2903 return emitMacroCall(op);
2906SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2907 return emitMacroCall(op);
2910SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2912 emitError(op,
"SV attributes emission is unimplemented for the op");
2914 ps.addAsString(op.getWidth());
2916 return {Unary, IsUnsigned};
2919SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2921 emitError(op,
"SV attributes emission is unimplemented for the op");
2923 ps.writeQuotedEscaped(op.getStr());
2924 return {Symbol, IsUnsigned};
2927SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2929 emitError(op,
"SV attributes emission is unimplemented for the op");
2931 ps.addAsString(op.getWidth());
2933 return {Unary, IsUnsigned};
2936SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2937 bool isNegated =
false;
2940 if (signPreference == RequireSigned && value.isNegative() &&
2941 !value.isMinSignedValue()) {
2946 ps.addAsString(type.getWidth());
2950 if (signPreference == RequireSigned)
2956 SmallString<32> valueStr;
2958 (-value).toStringUnsigned(valueStr, 16);
2960 value.toStringUnsigned(valueStr, 16);
2963 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2966SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2968 emitError(op,
"SV attributes emission is unimplemented for the op");
2970 auto value = op.getValue();
2974 if (value.getBitWidth() == 0) {
2975 emitOpError(op,
"will not emit zero width constants in the general case");
2976 ps <<
"<<unsupported zero width constant: "
2977 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2978 return {Unary, IsUnsigned};
2981 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2984void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2985 bool printAsPattern, Operation *op) {
2986 if (printAsPattern && !isAssignmentLikeContext)
2987 emitAssignmentPatternContextError(op);
2988 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2991 elementValues, [&]() { ps << openDelim; },
2992 [&](Attribute elementValue) {
2993 printConstantAggregate(elementValue,
elementType, op);
2995 [&]() { ps <<
"}"; });
2998void ExprEmitter::printConstantStruct(
2999 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3000 bool printAsPattern, Operation *op) {
3001 if (printAsPattern && !isAssignmentLikeContext)
3002 emitAssignmentPatternContextError(op);
3009 auto fieldRange = llvm::make_filter_range(
3010 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3015 if (printAsPattern) {
3017 fieldRange, [&]() { ps <<
"'{"; },
3018 [&](
const auto &fieldAndValue) {
3019 ps.scopedBox(PP::ibox2, [&]() {
3020 const auto &[field, value] = fieldAndValue;
3021 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3022 <<
":" << PP::space;
3023 printConstantAggregate(value, field.type, op);
3026 [&]() { ps <<
"}"; });
3029 fieldRange, [&]() { ps <<
"{"; },
3030 [&](
const auto &fieldAndValue) {
3031 ps.scopedBox(PP::ibox2, [&]() {
3032 const auto &[field, value] = fieldAndValue;
3033 printConstantAggregate(value, field.type, op);
3036 [&]() { ps <<
"}"; });
3040void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3043 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3044 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3045 isAssignmentLikeContext, op);
3048 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3049 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3053 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3054 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3055 isAssignmentLikeContext, op);
3057 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3058 auto value = cast<IntegerAttr>(attr).getValue();
3059 printConstantScalar(value, intType);
3063 emitOpError(op,
"contains constant of type ")
3064 << type <<
" which cannot be emitted as Verilog";
3067SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3069 emitError(op,
"SV attributes emission is unimplemented for the op");
3073 "zero-bit types not allowed at this point");
3075 printConstantAggregate(op.getFields(), op.getType(), op);
3076 return {Symbol, IsUnsigned};
3079SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3081 emitError(op,
"SV attributes emission is unimplemented for the op");
3083 return ps.invokeWithStringOS([&](
auto &os) {
3084 return emitter.printParamValue(op.getValue(), os, [&]() {
3085 return op->emitOpError(
"invalid parameter use");
3094 emitError(op,
"SV attributes emission is unimplemented for the op");
3096 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3098 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3100 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3102 ps.addAsString(dstWidth);
3104 return {Selection, arrayPrec.signedness};
3107SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3108 emitSubExpr(op.getInput(), Selection);
3113 emitSubExpr(op.getIndex(), LowestPrecedence);
3115 emitSVAttributes(op);
3116 return {Selection, IsUnsigned};
3122 emitError(op,
"SV attributes emission is unimplemented for the op");
3124 if (op.isUniform()) {
3126 ps.addAsString(op.getInputs().size());
3128 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3132 op.getInputs(), [&]() { ps <<
"{"; },
3135 emitSubExprIBox2(v);
3138 [&]() { ps <<
"}"; });
3140 return {Unary, IsUnsigned};
3143SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3145 emitError(op,
"SV attributes emission is unimplemented for the op");
3148 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3149 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3150 return {Unary, IsUnsigned};
3155 emitError(op,
"SV attributes emission is unimplemented for the op");
3157 emitBracedList(op.getOperands());
3158 return {Unary, IsUnsigned};
3161SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3163 emitError(op,
"SV attributes emission is unimplemented for the op");
3165 auto index = op.getIndex();
3166 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3171 emitSubExpr(index, LowestPrecedence);
3173 return {Selection, arrayPrec.signedness};
3176SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3178 emitError(op,
"SV attributes emission is unimplemented for the op");
3180 auto prec = emitSubExpr(op.getInput(), Selection);
3182 emitSubExpr(op.getBase(), LowestPrecedence);
3183 if (op.getDecrement())
3187 ps.addAsString(op.getWidth());
3189 return {Selection, prec.signedness};
3192SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3194 emitError(op,
"SV attributes emission is unimplemented for the op");
3196 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3198 emitSubExpr(op.getBase(), LowestPrecedence);
3199 if (op.getDecrement())
3203 ps.addAsString(op.getWidth());
3208SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3210 emitError(op,
"SV attributes emission is unimplemented for the op");
3212 auto prec = emitSubExpr(op.getInput(), Selection);
3214 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3215 return {Selection, prec.signedness};
3218SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3220 emitError(op,
"SV attributes emission is unimplemented for the op");
3223 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3228SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3230 emitError(op,
"SV attributes emission is unimplemented for the op");
3233 return {Symbol, IsUnsigned};
3236SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3238 emitError(op,
"SV attributes emission is unimplemented for the op");
3241 return {Symbol, IsUnsigned};
3244SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3258 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3259 ps.scopedBox(PP::ibox0, [&]() {
3260 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3264 emitSVAttributes(op);
3266 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3267 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3271 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3272 return emitSubExpr(op.getFalseValue(), Conditional);
3275 SubExprSignResult signedness = IsUnsigned;
3276 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3277 signedness = IsSigned;
3279 return {Conditional, signedness};
3283SubExprInfo ExprEmitter::printStructCreate(
3284 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3286 bool printAsPattern, Operation *op) {
3287 if (printAsPattern && !isAssignmentLikeContext)
3288 emitAssignmentPatternContextError(op);
3291 auto filteredFields = llvm::make_filter_range(
3292 llvm::enumerate(fieldInfos),
3293 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3295 if (printAsPattern) {
3297 filteredFields, [&]() { ps <<
"'{"; },
3298 [&](
const auto &field) {
3299 ps.scopedBox(PP::ibox2, [&]() {
3301 emitter.getVerilogStructFieldName(field.value().name))
3302 <<
":" << PP::space;
3303 fieldFn(field.value(), field.index());
3306 [&]() { ps <<
"}"; });
3309 filteredFields, [&]() { ps <<
"{"; },
3310 [&](
const auto &field) {
3311 ps.scopedBox(PP::ibox2,
3312 [&]() { fieldFn(field.value(), field.index()); });
3314 [&]() { ps <<
"}"; });
3317 return {Selection, IsUnsigned};
3322 emitError(op,
"SV attributes emission is unimplemented for the op");
3326 bool printAsPattern = isAssignmentLikeContext;
3327 StructType structType = op.getType();
3328 return printStructCreate(
3329 structType.getElements(),
3330 [&](
const auto &field,
auto index) {
3331 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3333 isAssignmentLikeContext);
3335 printAsPattern, op);
3340 emitError(op,
"SV attributes emission is unimplemented for the op");
3342 emitSubExpr(op.getInput(), Selection);
3344 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3345 return {Selection, IsUnsigned};
3348SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3350 emitError(op,
"SV attributes emission is unimplemented for the op");
3354 bool printAsPattern = isAssignmentLikeContext;
3355 StructType structType = op.getType();
3356 return printStructCreate(
3357 structType.getElements(),
3358 [&](
const auto &field,
auto index) {
3359 if (field.name == op.getFieldNameAttr()) {
3360 emitSubExpr(op.getNewValue(), Selection);
3362 emitSubExpr(op.getInput(), Selection);
3364 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3367 printAsPattern, op);
3370SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3371 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3372 return {Selection, IsUnsigned};
3375SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3377 emitError(op,
"SV attributes emission is unimplemented for the op");
3378 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3381 result.signedness = IsUnsigned;
3385SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3387 emitError(op,
"SV attributes emission is unimplemented for the op");
3391 auto unionWidth = hw::getBitWidth(unionType);
3392 auto &element = unionType.getElements()[op.getFieldIndex()];
3393 auto elementWidth = hw::getBitWidth(element.type);
3396 if (!elementWidth) {
3397 ps.addAsString(unionWidth);
3399 return {Unary, IsUnsigned};
3403 if (elementWidth == unionWidth) {
3404 emitSubExpr(op.getInput(), LowestPrecedence);
3405 return {Unary, IsUnsigned};
3410 ps.scopedBox(PP::ibox0, [&]() {
3411 if (
auto prePadding = element.offset) {
3412 ps.addAsString(prePadding);
3413 ps <<
"'h0," << PP::space;
3415 emitSubExpr(op.getInput(), Selection);
3416 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3417 ps <<
"," << PP::space;
3418 ps.addAsString(postPadding);
3424 return {Unary, IsUnsigned};
3427SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3429 emitError(op,
"SV attributes emission is unimplemented for the op");
3430 emitSubExpr(op.getInput(), Selection);
3433 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3434 auto unionWidth = hw::getBitWidth(unionType);
3435 auto &element = unionType.getElements()[op.getFieldIndex()];
3436 auto elementWidth = hw::getBitWidth(element.type);
3437 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3438 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3447 return {Selection, IsUnsigned};
3450SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3451 emitOpError(op,
"cannot emit this expression to Verilog");
3452 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3454 return {Symbol, IsUnsigned};
3470enum class PropertyPrecedence {
3490struct EmittedProperty {
3492 PropertyPrecedence precedence;
3497class PropertyEmitter :
public EmitterBase,
3498 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3502 PropertyEmitter(ModuleEmitter &emitter,
3503 SmallPtrSetImpl<Operation *> &emittedOps)
3504 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3505 PropertyEmitter(ModuleEmitter &emitter,
3506 SmallPtrSetImpl<Operation *> &emittedOps,
3508 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3510 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3511 assert(state.pp.getListener() == &state.saver);
3514 void emitAssertPropertyDisable(
3515 Value property, Value disable,
3516 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3518 void emitAssertPropertyBody(
3519 Value property, Value disable,
3520 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3522 void emitAssertPropertyBody(
3523 Value property, sv::EventControl event, Value clock, Value disable,
3524 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3529 emitNestedProperty(Value property,
3530 PropertyPrecedence parenthesizeIfLooserThan);
3531 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3532 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3534 EmittedProperty visitUnhandledLTL(Operation *op);
3535 EmittedProperty visitLTL(ltl::AndOp op);
3536 EmittedProperty visitLTL(ltl::OrOp op);
3537 EmittedProperty visitLTL(ltl::IntersectOp op);
3538 EmittedProperty visitLTL(ltl::DelayOp op);
3539 EmittedProperty visitLTL(ltl::ConcatOp op);
3540 EmittedProperty visitLTL(ltl::RepeatOp op);
3541 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3542 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3543 EmittedProperty visitLTL(ltl::NotOp op);
3544 EmittedProperty visitLTL(ltl::ImplicationOp op);
3545 EmittedProperty visitLTL(ltl::UntilOp op);
3546 EmittedProperty visitLTL(ltl::EventuallyOp op);
3547 EmittedProperty visitLTL(ltl::ClockOp op);
3549 void emitLTLConcat(ValueRange inputs);
3552 ModuleEmitter &emitter;
3557 SmallPtrSetImpl<Operation *> &emittedOps;
3560 SmallVector<Token> localTokens;
3573void PropertyEmitter::emitAssertPropertyDisable(
3574 Value property, Value disable,
3575 PropertyPrecedence parenthesizeIfLooserThan) {
3578 ps <<
"disable iff" << PP::nbsp <<
"(";
3580 emitNestedProperty(disable, PropertyPrecedence::Unary);
3586 ps.scopedBox(PP::ibox0,
3587 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3593void PropertyEmitter::emitAssertPropertyBody(
3594 Value property, Value disable,
3595 PropertyPrecedence parenthesizeIfLooserThan) {
3596 assert(localTokens.empty());
3598 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3603 if (&buffer.tokens == &localTokens)
3604 buffer.flush(state.pp);
3607void PropertyEmitter::emitAssertPropertyBody(
3608 Value property, sv::EventControl event, Value clock, Value disable,
3609 PropertyPrecedence parenthesizeIfLooserThan) {
3610 assert(localTokens.empty());
3613 ps.scopedBox(PP::ibox2, [&] {
3614 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3615 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3621 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3626 if (&buffer.tokens == &localTokens)
3627 buffer.flush(state.pp);
3630EmittedProperty PropertyEmitter::emitNestedProperty(
3631 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3641 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3642 ExprEmitter(emitter, emittedOps, buffer.tokens)
3643 .emitExpression(property, LowestPrecedence,
3645 return {PropertyPrecedence::Symbol};
3648 unsigned startIndex = buffer.tokens.size();
3649 auto info = dispatchLTLVisitor(property.getDefiningOp());
3654 if (
info.precedence > parenthesizeIfLooserThan) {
3656 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3657 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3659 ps << PP::end <<
")";
3661 info.precedence = PropertyPrecedence::Symbol;
3665 emittedOps.insert(property.getDefiningOp());
3669EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3670 emitOpError(op,
"emission as Verilog property or sequence not supported");
3671 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3672 return {PropertyPrecedence::Symbol};
3675EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3678 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3679 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3680 return {PropertyPrecedence::And};
3683EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3686 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3687 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3688 return {PropertyPrecedence::Or};
3691EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3695 emitNestedProperty(input, PropertyPrecedence::Intersect);
3697 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3698 return {PropertyPrecedence::Intersect};
3701EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3703 if (
auto length = op.getLength()) {
3705 ps.addAsString(op.getDelay());
3708 ps.addAsString(op.getDelay());
3710 ps.addAsString(op.getDelay() + *length);
3714 if (op.getDelay() == 0) {
3716 }
else if (op.getDelay() == 1) {
3720 ps.addAsString(op.getDelay());
3725 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3726 return {PropertyPrecedence::Concat};
3729void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3730 bool addSeparator =
false;
3731 for (
auto input : inputs) {
3734 if (!input.getDefiningOp<ltl::DelayOp>())
3735 ps <<
"##0" << PP::space;
3737 addSeparator =
true;
3738 emitNestedProperty(input, PropertyPrecedence::Concat);
3742EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3743 emitLTLConcat(op.getInputs());
3744 return {PropertyPrecedence::Concat};
3747EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3748 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3749 if (
auto more = op.getMore()) {
3751 ps.addAsString(op.getBase());
3754 ps.addAsString(op.getBase() + *more);
3758 if (op.getBase() == 0) {
3760 }
else if (op.getBase() == 1) {
3764 ps.addAsString(op.getBase());
3768 return {PropertyPrecedence::Repeat};
3771EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3772 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3774 auto more = op.getMore();
3776 ps.addAsString(op.getBase());
3779 ps.addAsString(op.getBase() + more);
3783 return {PropertyPrecedence::Repeat};
3786EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3787 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3789 auto more = op.getMore();
3791 ps.addAsString(op.getBase());
3794 ps.addAsString(op.getBase() + more);
3798 return {PropertyPrecedence::Repeat};
3801EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3802 ps <<
"not" << PP::space;
3803 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3804 return {PropertyPrecedence::Unary};
3810 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3811 if (!concatOp || concatOp.getInputs().size() < 2)
3813 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3814 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3816 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3817 if (!constOp || !constOp.getValue().isOne())
3819 return concatOp.getInputs().drop_back();
3822EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3826 emitLTLConcat(range);
3827 ps << PP::space <<
"|=>" << PP::nbsp;
3829 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3830 ps << PP::space <<
"|->" << PP::nbsp;
3832 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3833 return {PropertyPrecedence::Implication};
3836EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3837 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3838 ps << PP::space <<
"until" << PP::space;
3839 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3840 return {PropertyPrecedence::Until};
3843EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3844 ps <<
"s_eventually" << PP::space;
3845 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3846 return {PropertyPrecedence::Qualifier};
3849EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3851 ps.scopedBox(PP::ibox2, [&] {
3852 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3853 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3857 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3858 return {PropertyPrecedence::Clocking};
3868class NameCollector {
3870 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3874 void collectNames(Block &block);
3876 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3877 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3880 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3881 ModuleEmitter &moduleEmitter;
3886 static constexpr size_t maxTypeWidthBound = 32;
3891void NameCollector::collectNames(Block &block) {
3894 for (
auto &op : block) {
3898 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3899 FuncCallProceduralOp, FuncCallOp>(op))
3901 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3905 for (
auto result : op.getResults()) {
3907 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3908 SmallString<16> typeString;
3912 llvm::raw_svector_ostream stringStream(typeString);
3914 stringStream, op.getLoc());
3916 if (typeString.size() <= maxTypeWidthBound)
3917 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3924 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3925 for (
auto ®ion : op.getRegions()) {
3926 if (!region.empty())
3927 collectNames(region.front());
3941class StmtEmitter :
public EmitterBase,
3949 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3951 void emitStatement(Operation *op);
3952 void emitStatementBlock(Block &body);
3955 LogicalResult emitDeclaration(Operation *op);
3958 void collectNamesAndCalculateDeclarationWidths(Block &block);
3961 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
3962 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
3963 bool isAssignmentLikeContext =
false);
3964 void emitSVAttributes(Operation *op);
3967 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
3970 friend class sv::Visitor<StmtEmitter, LogicalResult>;
3974 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
3975 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
3976 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
3977 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
3978 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
3979 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
3981 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
3982 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
3983 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
3984 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
3985 template <
typename Op>
3988 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3989 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
3990 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
3992 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
3993 LogicalResult visitSV(
AssignOp op);
3994 LogicalResult visitSV(BPAssignOp op);
3995 LogicalResult visitSV(PAssignOp op);
3996 LogicalResult visitSV(ForceOp op);
3997 LogicalResult visitSV(ReleaseOp op);
3998 LogicalResult visitSV(AliasOp op);
3999 LogicalResult visitSV(InterfaceInstanceOp op);
4000 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4001 LogicalResult visitStmt(OutputOp op);
4003 LogicalResult visitStmt(InstanceOp op);
4004 LogicalResult visitStmt(InstanceChoiceOp op);
4005 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4006 ArrayRef<Value> instPortValues);
4011 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4012 LogicalResult visitSV(OrderedOutputOp op);
4013 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4014 LogicalResult visitSV(IfDefProceduralOp op) {
4015 return emitIfDef(op, op.getCond());
4017 LogicalResult visitSV(IfOp op);
4018 LogicalResult visitSV(AlwaysOp op);
4019 LogicalResult visitSV(AlwaysCombOp op);
4020 LogicalResult visitSV(AlwaysFFOp op);
4021 LogicalResult visitSV(InitialOp op);
4022 LogicalResult visitSV(CaseOp op);
4023 LogicalResult visitSV(FWriteOp op);
4024 LogicalResult visitSV(VerbatimOp op);
4025 LogicalResult visitSV(MacroRefOp op);
4027 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4028 std::optional<unsigned> verbosity);
4029 LogicalResult visitSV(StopOp op);
4030 LogicalResult visitSV(FinishOp op);
4031 LogicalResult visitSV(ExitOp op);
4033 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4034 std::optional<unsigned> verbosity,
4036 ValueRange operands);
4037 LogicalResult visitSV(FatalOp op);
4038 LogicalResult visitSV(ErrorOp op);
4039 LogicalResult visitSV(WarningOp op);
4040 LogicalResult visitSV(InfoOp op);
4042 LogicalResult visitSV(ReadMemOp op);
4044 LogicalResult visitSV(GenerateOp op);
4045 LogicalResult visitSV(GenerateCaseOp op);
4047 LogicalResult visitSV(
ForOp op);
4049 void emitAssertionLabel(Operation *op);
4050 void emitAssertionMessage(StringAttr message, ValueRange args,
4051 SmallPtrSetImpl<Operation *> &ops,
4053 template <
typename Op>
4054 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4055 LogicalResult visitSV(AssertOp op);
4056 LogicalResult visitSV(AssumeOp op);
4057 LogicalResult visitSV(CoverOp op);
4058 template <
typename Op>
4059 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4060 LogicalResult visitSV(AssertConcurrentOp op);
4061 LogicalResult visitSV(AssumeConcurrentOp op);
4062 LogicalResult visitSV(CoverConcurrentOp op);
4063 template <
typename Op>
4064 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4065 LogicalResult visitSV(AssertPropertyOp op);
4066 LogicalResult visitSV(AssumePropertyOp op);
4067 LogicalResult visitSV(CoverPropertyOp op);
4069 LogicalResult visitSV(BindOp op);
4070 LogicalResult visitSV(InterfaceOp op);
4071 LogicalResult visitSV(InterfaceSignalOp op);
4072 LogicalResult visitSV(InterfaceModportOp op);
4073 LogicalResult visitSV(AssignInterfaceSignalOp op);
4074 LogicalResult visitSV(MacroDefOp op);
4076 void emitBlockAsStatement(Block *block,
4077 const SmallPtrSetImpl<Operation *> &locationOps,
4078 StringRef multiLineComment = StringRef());
4080 LogicalResult visitSV(FuncDPIImportOp op);
4081 template <
typename CallOp>
4082 LogicalResult emitFunctionCall(CallOp callOp);
4083 LogicalResult visitSV(FuncCallProceduralOp op);
4084 LogicalResult visitSV(FuncCallOp op);
4085 LogicalResult visitSV(ReturnOp op);
4086 LogicalResult visitSV(IncludeOp op);
4089 ModuleEmitter &emitter;
4094 size_t maxDeclNameWidth = 0;
4095 size_t maxTypeWidth = 0;
4106void StmtEmitter::emitExpression(Value exp,
4107 SmallPtrSetImpl<Operation *> &emittedExprs,
4108 VerilogPrecedence parenthesizeIfLooserThan,
4109 bool isAssignmentLikeContext) {
4110 ExprEmitter(emitter, emittedExprs)
4111 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4116void StmtEmitter::emitSVAttributes(Operation *op) {
4124 setPendingNewline();
4127void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4128 llvm::function_ref<
void()> emitRHS,
4130 std::optional<PPExtString> wordBeforeLHS) {
4132 ps.scopedBox(PP::ibox2, [&]() {
4133 if (wordBeforeLHS) {
4134 ps << *wordBeforeLHS << PP::space;
4138 ps << PP::space << syntax << PP::space;
4140 ps.scopedBox(PP::ibox0, [&]() {
4147template <
typename Op>
4149StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4150 std::optional<PPExtString> wordBeforeLHS) {
4151 SmallPtrSet<Operation *, 8> ops;
4155 ps.addCallback({op,
true});
4156 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4158 emitExpression(op.getSrc(), ops, LowestPrecedence,
4163 ps.addCallback({op,
false});
4164 emitLocationInfoAndNewLine(ops);
4168LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4171 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4174 if (emitter.assignsInlined.count(op))
4178 emitSVAttributes(op);
4183LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4184 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4188 if (emitter.assignsInlined.count(op))
4192 emitSVAttributes(op);
4197LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4199 emitSVAttributes(op);
4204LogicalResult StmtEmitter::visitSV(ForceOp op) {
4206 emitError(op,
"SV attributes emission is unimplemented for the op");
4211LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4213 emitError(op,
"SV attributes emission is unimplemented for the op");
4216 SmallPtrSet<Operation *, 8> ops;
4218 ps.addCallback({op,
true});
4219 ps.scopedBox(PP::ibox2, [&]() {
4220 ps <<
"release" << PP::space;
4221 emitExpression(op.getDest(), ops);
4224 ps.addCallback({op,
false});
4225 emitLocationInfoAndNewLine(ops);
4229LogicalResult StmtEmitter::visitSV(AliasOp op) {
4231 emitError(op,
"SV attributes emission is unimplemented for the op");
4234 SmallPtrSet<Operation *, 8> ops;
4236 ps.addCallback({op,
true});
4237 ps.scopedBox(PP::ibox2, [&]() {
4238 ps <<
"alias" << PP::space;
4239 ps.scopedBox(PP::cbox0, [&]() {
4241 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4242 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4246 ps.addCallback({op,
false});
4247 emitLocationInfoAndNewLine(ops);
4251LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4252 auto doNotPrint = op.getDoNotPrint();
4253 if (doNotPrint && !state.options.emitBindComments)
4257 emitError(op,
"SV attributes emission is unimplemented for the op");
4260 StringRef prefix =
"";
4261 ps.addCallback({op,
true});
4264 ps <<
"// This interface is elsewhere emitted as a bind statement."
4268 SmallPtrSet<Operation *, 8> ops;
4271 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4272 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4273 "point to an interface");
4276 if (!prefix.empty())
4282 ps.addCallback({op,
false});
4283 emitLocationInfoAndNewLine(ops);
4291LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4293 SmallPtrSet<Operation *, 8> ops;
4294 size_t operandIndex = 0;
4295 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4296 for (
PortInfo port : ports.getOutputs()) {
4297 auto operand = op->getOperand(operandIndex);
4301 if (operand.hasOneUse() && operand.getDefiningOp() &&
4302 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4311 ps.addCallback({op,
true});
4313 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4315 ps <<
"// Zero width: ";
4318 ps <<
"assign" << PP::space;
4320 ps << PP::space <<
"=" << PP::space;
4321 ps.scopedBox(PP::ibox0, [&]() {
4325 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4326 ps <<
"/*Zero width*/";
4328 emitExpression(operand, ops, LowestPrecedence,
4333 ps.addCallback({op,
false});
4334 emitLocationInfoAndNewLine(ops);
4341LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4342 auto parent = op->getParentOfType<PortList>();
4344 return emitOutputLikeOp(op, ports);
4347LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4349 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4350 ps <<
"`ifndef " << typescopeDef << PP::newline;
4351 ps <<
"`define " << typescopeDef;
4352 setPendingNewline();
4353 emitStatementBlock(*op.getBodyBlock());
4355 ps <<
"`endif // " << typescopeDef;
4356 setPendingNewline();
4360LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4362 emitError(op,
"SV attributes emission is unimplemented for the op");
4367 ps << PP::neverbox <<
"// ";
4369 SmallPtrSet<Operation *, 8> ops;
4371 ps.scopedBox(PP::ibox2, [&]() {
4372 ps <<
"typedef" << PP::space;
4373 ps.invokeWithStringOS([&](
auto &os) {
4375 op.getAliasType(),
false);
4377 ps << PP::space <<
PPExtString(op.getPreferredName());
4378 ps.invokeWithStringOS(
4379 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4384 emitLocationInfoAndNewLine(ops);
4388template <
typename CallOpTy>
4389LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4393 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4395 SmallPtrSet<Operation *, 8> ops;
4399 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4400 if (explicitReturn) {
4401 assert(explicitReturn.hasOneUse());
4402 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4403 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4404 emitExpression(bpassignOp.getDest(), ops);
4406 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4407 ps <<
"assign" << PP::nbsp;
4408 emitExpression(assignOp.getDest(), ops);
4410 ps << PP::nbsp <<
"=" << PP::nbsp;
4413 auto arguments = callee.getPortList(
true);
4417 bool needsComma =
false;
4418 auto printArg = [&](Value value) {
4420 ps <<
"," << PP::space;
4421 emitExpression(value, ops);
4425 ps.scopedBox(PP::ibox0, [&] {
4426 unsigned inputIndex = 0, outputIndex = 0;
4427 for (
auto arg : arguments) {
4430 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4432 printArg(op.getInputs()[inputIndex++]);
4437 emitLocationInfoAndNewLine(ops);
4441LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4442 return emitFunctionCall(op);
4445LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4446 return emitFunctionCall(op);
4449template <
typename PPS>
4451 bool isAutomatic =
false,
4452 bool emitAsTwoStateType =
false) {
4453 ps <<
"function" << PP::nbsp;
4455 ps <<
"automatic" << PP::nbsp;
4456 auto retType = op.getExplicitlyReturnedType();
4458 ps.invokeWithStringOS([&](
auto &os) {
4459 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4460 emitAsTwoStateType);
4466 emitter.emitPortList(
4470LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4471 auto parent = op->getParentOfType<sv::FuncOp>();
4473 return emitOutputLikeOp(op, ports);
4476LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4478 ps <<
"`include" << PP::nbsp;
4480 if (op.getStyle() == IncludeStyle::System)
4481 ps <<
"<" << op.getTarget() <<
">";
4483 ps <<
"\"" << op.getTarget() <<
"\"";
4485 emitLocationInfo(op.getLoc());
4486 setPendingNewline();
4490LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4493 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4497 if (
auto linkageName = importOp.getLinkageName())
4498 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4500 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4501 assert(op.isDeclaration() &&
"function must be a declaration");
4504 assert(state.pendingNewline);
4510LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4512 emitError(op,
"SV attributes emission is unimplemented for the op");
4515 SmallPtrSet<Operation *, 8> ops;
4518 ps.addCallback({op,
true});
4520 ps.scopedBox(PP::ibox0, [&]() {
4521 emitExpression(op.getFd(), ops);
4523 ps <<
"," << PP::space;
4524 ps.writeQuotedEscaped(op.getFormatString());
4532 for (
auto operand : op.getSubstitutions()) {
4533 ps <<
"," << PP::space;
4534 emitExpression(operand, ops);
4538 ps.addCallback({op,
false});
4539 emitLocationInfoAndNewLine(ops);
4543LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4545 emitError(op,
"SV attributes emission is unimplemented for the op");
4548 SmallPtrSet<Operation *, 8> ops;
4553 StringRef
string = op.getFormatString();
4554 if (
string.ends_with(
"\n"))
4555 string =
string.drop_back();
4560 bool isFirst =
true;
4563 while (!
string.
empty()) {
4564 auto lhsRhs =
string.split(
'\n');
4568 ps << PP::end << PP::newline << PP::neverbox;
4572 emitTextWithSubstitutions(
4573 ps, lhsRhs.first, op,
4574 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4575 string = lhsRhs.second;
4580 emitLocationInfoAndNewLine(ops);
4585LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4587 emitError(op,
"SV attributes emission is unimplemented for the op");
4591 SmallPtrSet<Operation *, 8> ops;
4596 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4597 assert(macroOp &&
"Invalid IR");
4599 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4601 if (!op.getInputs().empty()) {
4603 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4604 emitExpression(val, ops, LowestPrecedence,
4610 emitLocationInfoAndNewLine(ops);
4616StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4617 std::optional<unsigned> verbosity) {
4619 emitError(op,
"SV attributes emission is unimplemented for the op");
4622 SmallPtrSet<Operation *, 8> ops;
4624 ps.addCallback({op,
true});
4626 if (verbosity && *verbosity != 1) {
4628 ps.addAsString(*verbosity);
4632 ps.addCallback({op,
false});
4633 emitLocationInfoAndNewLine(ops);
4637LogicalResult StmtEmitter::visitSV(StopOp op) {
4638 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4641LogicalResult StmtEmitter::visitSV(FinishOp op) {
4642 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4646LogicalResult StmtEmitter::visitSV(ExitOp op) {
4647 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4653StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4654 std::optional<unsigned> verbosity,
4655 StringAttr message, ValueRange operands) {
4657 emitError(op,
"SV attributes emission is unimplemented for the op");
4660 SmallPtrSet<Operation *, 8> ops;
4662 ps.addCallback({op,
true});
4668 if ((verbosity && *verbosity != 1) || message) {
4670 ps.scopedBox(PP::ibox0, [&]() {
4674 ps.addAsString(*verbosity);
4679 ps <<
"," << PP::space;
4680 ps.writeQuotedEscaped(message.getValue());
4682 for (
auto operand : operands) {
4683 ps <<
"," << PP::space;
4684 emitExpression(operand, ops);
4693 ps.addCallback({op,
false});
4694 emitLocationInfoAndNewLine(ops);
4698LogicalResult StmtEmitter::visitSV(FatalOp op) {
4699 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4700 op.getMessageAttr(), op.getSubstitutions());
4703LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4704 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4705 op.getMessageAttr(), op.getSubstitutions());
4708LogicalResult StmtEmitter::visitSV(WarningOp op) {
4709 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4710 op.getMessageAttr(), op.getSubstitutions());
4713LogicalResult StmtEmitter::visitSV(InfoOp op) {
4714 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4715 op.getMessageAttr(), op.getSubstitutions());
4718LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4719 SmallPtrSet<Operation *, 8> ops({op});
4722 ps.addCallback({op,
true});
4724 switch (op.getBaseAttr().getValue()) {
4725 case MemBaseTypeAttr::MemBaseBin:
4728 case MemBaseTypeAttr::MemBaseHex:
4733 ps.scopedBox(PP::ibox0, [&]() {
4734 ps.writeQuotedEscaped(op.getFilename());
4735 ps <<
"," << PP::space;
4736 emitExpression(op.getDest(), ops);
4740 ps.addCallback({op,
false});
4741 emitLocationInfoAndNewLine(ops);
4745LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4746 emitSVAttributes(op);
4749 ps.addCallback({op,
true});
4750 ps <<
"generate" << PP::newline;
4752 setPendingNewline();
4753 emitStatementBlock(op.getBody().getBlocks().front());
4756 ps <<
"endgenerate";
4757 ps.addCallback({op,
false});
4758 setPendingNewline();
4762LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4763 emitSVAttributes(op);
4766 ps.addCallback({op,
true});
4768 ps.invokeWithStringOS([&](
auto &os) {
4769 emitter.printParamValue(
4770 op.getCond(), os, VerilogPrecedence::Selection,
4771 [&]() { return op->emitOpError(
"invalid case parameter"); });
4774 setPendingNewline();
4777 ArrayAttr
patterns = op.getCasePatterns();
4778 ArrayAttr caseNames = op.getCaseNames();
4779 MutableArrayRef<Region> regions = op.getCaseRegions();
4786 llvm::StringMap<size_t> nextGenIds;
4787 ps.scopedBox(PP::bbox2, [&]() {
4789 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4790 auto ®ion = regions[i];
4791 assert(region.hasOneBlock());
4792 Attribute patternAttr =
patterns[i];
4795 if (!isa<mlir::TypedAttr>(patternAttr))
4798 ps.invokeWithStringOS([&](
auto &os) {
4799 emitter.printParamValue(
4800 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4801 [&]() {
return op->emitOpError(
"invalid case value"); });
4804 StringRef legalName =
4805 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4808 setPendingNewline();
4809 emitStatementBlock(region.getBlocks().front());
4812 setPendingNewline();
4818 ps.addCallback({op,
false});
4819 setPendingNewline();
4823LogicalResult StmtEmitter::visitSV(
ForOp op) {
4824 emitSVAttributes(op);
4825 llvm::SmallPtrSet<Operation *, 8> ops;
4826 ps.addCallback({op,
true});
4828 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4831 ps.scopedBox(PP::cbox0, [&]() {
4835 ps <<
"logic" << PP::nbsp;
4836 ps.invokeWithStringOS([&](
auto &os) {
4837 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4842 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4847 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4848 [&]() { emitExpression(op.getUpperBound(), ops); },
4854 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4855 [&]() { emitExpression(op.getStep(), ops); },
4859 ps << PP::neverbreak;
4860 setPendingNewline();
4861 emitStatementBlock(op.getBody().getBlocks().front());
4864 ps.addCallback({op,
false});
4865 emitLocationInfoAndNewLine(ops);
4870void StmtEmitter::emitAssertionLabel(Operation *op) {
4871 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4877void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4878 SmallPtrSetImpl<Operation *> &ops,
4879 bool isConcurrent =
false) {
4882 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4883 ps.scopedBox(PP::ibox0, [&]() {
4884 ps.writeQuotedEscaped(message.getValue());
4886 for (
auto arg : args) {
4887 ps <<
"," << PP::space;
4888 emitExpression(arg, ops);
4894template <
typename Op>
4895LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4897 emitError(op,
"SV attributes emission is unimplemented for the op");
4900 SmallPtrSet<Operation *, 8> ops;
4902 ps.addCallback({op,
true});
4903 ps.scopedBox(PP::ibox2, [&]() {
4904 emitAssertionLabel(op);
4905 ps.scopedBox(PP::cbox0, [&]() {
4907 switch (op.getDefer()) {
4908 case DeferAssert::Immediate:
4910 case DeferAssert::Observed:
4913 case DeferAssert::Final:
4918 ps.scopedBox(PP::ibox0, [&]() {
4919 emitExpression(op.getExpression(), ops);
4922 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4926 ps.addCallback({op,
false});
4927 emitLocationInfoAndNewLine(ops);
4931LogicalResult StmtEmitter::visitSV(AssertOp op) {
4932 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4935LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4936 return emitImmediateAssertion(op,
PPExtString(
"assume"));
4939LogicalResult StmtEmitter::visitSV(CoverOp op) {
4940 return emitImmediateAssertion(op,
PPExtString(
"cover"));
4943template <
typename Op>
4944LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
4946 emitError(op,
"SV attributes emission is unimplemented for the op");
4949 SmallPtrSet<Operation *, 8> ops;
4951 ps.addCallback({op,
true});
4952 ps.scopedBox(PP::ibox2, [&]() {
4953 emitAssertionLabel(op);
4954 ps.scopedBox(PP::cbox0, [&]() {
4955 ps << opName << PP::nbsp <<
"property (";
4956 ps.scopedBox(PP::ibox0, [&]() {
4957 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
4959 emitExpression(op.getClock(), ops);
4960 ps <<
")" << PP::space;
4961 emitExpression(op.getProperty(), ops);
4964 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
4969 ps.addCallback({op,
false});
4970 emitLocationInfoAndNewLine(ops);
4974LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
4975 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
4978LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
4979 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
4982LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
4983 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
4988template <
typename Op>
4989LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
4991 emitError(op,
"SV attributes emission is unimplemented for the op");
5001 Operation *parent = op->getParentOp();
5002 Value
property = op.getProperty();
5003 bool isTemporal = !
property.getType().isSignlessInteger(1);
5004 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5005 bool emitAsImmediate = !isTemporal && isProcedural;
5008 SmallPtrSet<Operation *, 8> ops;
5010 ps.addCallback({op,
true});
5011 ps.scopedBox(PP::ibox2, [&]() {
5013 emitAssertionLabel(op);
5015 ps.scopedBox(PP::cbox0, [&]() {
5016 if (emitAsImmediate)
5017 ps << opName <<
"(";
5019 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5021 Value clock = op.getClock();
5022 auto event = op.getEvent();
5024 ps.scopedBox(PP::ibox2, [&]() {
5025 PropertyEmitter(emitter, ops)
5026 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5029 ps.scopedBox(PP::ibox2, [&]() {
5030 PropertyEmitter(emitter, ops)
5031 .emitAssertPropertyBody(property, op.getDisable());
5036 ps.addCallback({op,
false});
5037 emitLocationInfoAndNewLine(ops);
5041LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5042 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5045LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5046 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5049LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5050 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5053LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5055 emitError(op,
"SV attributes emission is unimplemented for the op");
5058 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5059 .getMacroIdentifier());
5062 bool hasEmptyThen = op->getRegion(0).front().empty();
5064 ps <<
"`ifndef " << ident;
5066 ps <<
"`ifdef " << ident;
5068 SmallPtrSet<Operation *, 8> ops;
5070 emitLocationInfoAndNewLine(ops);
5073 emitStatementBlock(op->getRegion(0).front());
5075 if (!op->getRegion(1).empty()) {
5076 if (!hasEmptyThen) {
5078 ps <<
"`else // " << ident;
5079 setPendingNewline();
5081 emitStatementBlock(op->getRegion(1).front());
5088 setPendingNewline();
5096void StmtEmitter::emitBlockAsStatement(
5097 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5098 StringRef multiLineComment) {
5105 emitLocationInfoAndNewLine(locationOps);
5108 emitStatementBlock(*block);
5110 if (needsBeginEnd) {
5114 if (!multiLineComment.empty())
5115 ps <<
" // " << multiLineComment;
5116 setPendingNewline();
5120LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5122 for (
auto &op : ooop.getBody().front())
5127LogicalResult StmtEmitter::visitSV(IfOp op) {
5128 SmallPtrSet<Operation *, 8> ops;
5130 auto ifcondBox = PP::ibox2;
5132 emitSVAttributes(op);
5134 ps.addCallback({op,
true});
5135 ps <<
"if (" << ifcondBox;
5145 emitExpression(ifOp.getCond(), ops);
5146 ps << PP::end <<
")";
5147 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5149 if (!ifOp.hasElse())
5153 Block *elseBlock = ifOp.getElseBlock();
5155 if (!nestedElseIfOp) {
5160 emitBlockAsStatement(elseBlock, ops);
5166 ifOp = nestedElseIfOp;
5167 ps <<
"else if (" << ifcondBox;
5169 ps.addCallback({op,
false});
5174LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5175 emitSVAttributes(op);
5176 SmallPtrSet<Operation *, 8> ops;
5180 auto printEvent = [&](AlwaysOp::Condition cond) {
5181 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5182 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5184 ps.addCallback({op,
true});
5186 switch (op.getNumConditions()) {
5192 printEvent(op.getCondition(0));
5197 ps.scopedBox(PP::cbox0, [&]() {
5198 printEvent(op.getCondition(0));
5199 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5200 ps << PP::space <<
"or" << PP::space;
5201 printEvent(op.getCondition(i));
5210 std::string comment;
5211 if (op.getNumConditions() == 0) {
5212 comment =
"always @*";
5214 comment =
"always @(";
5217 [&](Attribute eventAttr) {
5218 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5219 comment += stringifyEventControl(event);
5221 [&]() { comment +=
", "; });
5225 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5226 ps.addCallback({op,
false});
5230LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5231 emitSVAttributes(op);
5232 SmallPtrSet<Operation *, 8> ops;
5236 ps.addCallback({op,
true});
5237 StringRef opString =
"always_comb";
5238 if (state.options.noAlwaysComb)
5239 opString =
"always @(*)";
5242 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5243 ps.addCallback({op,
false});
5247LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5248 emitSVAttributes(op);
5250 SmallPtrSet<Operation *, 8> ops;
5254 ps.addCallback({op,
true});
5255 ps <<
"always_ff @(";
5256 ps.scopedBox(PP::cbox0, [&]() {
5257 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5258 emitExpression(op.getClock(), ops);
5259 if (op.getResetStyle() == ResetType::AsyncReset) {
5260 ps << PP::nbsp <<
"or" << PP::space
5261 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5262 emitExpression(op.getReset(), ops);
5269 std::string comment;
5270 comment +=
"always_ff @(";
5271 comment += stringifyEventControl(op.getClockEdge());
5272 if (op.getResetStyle() == ResetType::AsyncReset) {
5274 comment += stringifyEventControl(*op.getResetEdge());
5278 if (op.getResetStyle() == ResetType::NoReset)
5279 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5282 emitLocationInfoAndNewLine(ops);
5283 ps.scopedBox(PP::bbox2, [&]() {
5289 if (op.getResetStyle() == ResetType::AsyncReset &&
5290 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5292 emitExpression(op.getReset(), ops);
5294 emitBlockAsStatement(op.getResetBlock(), ops);
5297 emitBlockAsStatement(op.getBodyBlock(), ops);
5302 ps <<
" // " << comment;
5303 setPendingNewline();
5305 ps.addCallback({op,
false});
5309LogicalResult StmtEmitter::visitSV(InitialOp op) {
5310 emitSVAttributes(op);
5311 SmallPtrSet<Operation *, 8> ops;
5314 ps.addCallback({op,
true});
5316 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5317 ps.addCallback({op,
false});
5321LogicalResult StmtEmitter::visitSV(CaseOp op) {
5322 emitSVAttributes(op);
5323 SmallPtrSet<Operation *, 8> ops, emptyOps;
5326 ps.addCallback({op,
true});
5327 if (op.getValidationQualifier() !=
5328 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5329 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5330 op.getValidationQualifier()))
5332 const char *opname =
nullptr;
5333 switch (op.getCaseStyle()) {
5334 case CaseStmtType::CaseStmt:
5337 case CaseStmtType::CaseXStmt:
5340 case CaseStmtType::CaseZStmt:
5344 ps << opname <<
" (";
5345 ps.scopedBox(PP::ibox0, [&]() {
5346 emitExpression(op.getCond(), ops);
5349 emitLocationInfoAndNewLine(ops);
5351 ps.scopedBox(PP::bbox2, [&]() {
5352 for (
auto &caseInfo : op.getCases()) {
5354 auto &
pattern = caseInfo.pattern;
5356 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5357 .Case<CaseBitPattern>([&](
auto bitPattern) {
5360 ps.invokeWithStringOS([&](
auto &os) {
5361 os << bitPattern->getWidth() <<
"'b";
5362 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5363 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5366 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5367 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5368 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5370 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5371 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5374 emitBlockAsStatement(caseInfo.block, emptyOps);
5380 ps.addCallback({op,
false});
5381 emitLocationInfoAndNewLine(ops);
5385LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5386 bool doNotPrint = op.getDoNotPrint();
5387 if (doNotPrint && !state.options.emitBindComments)
5392 emitSVAttributes(op);
5394 ps.addCallback({op,
true});
5397 <<
"/* This instance is elsewhere emitted as a bind statement."
5400 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5401 "attributes. The attributes will not be emitted.";
5404 SmallPtrSet<Operation *, 8> ops;
5409 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5410 assert(moduleOp &&
"Invalid IR");
5414 if (!op.getParameters().empty()) {
5417 bool printed =
false;
5419 llvm::zip(op.getParameters(),
5420 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5421 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5422 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5424 if (param.getValue() == modParam.getValue())
5429 ps <<
" #(" << PP::bbox2 << PP::newline;
5432 ps <<
"," << PP::newline;
5436 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5438 ps.invokeWithStringOS([&](
auto &os) {
5439 emitter.printParamValue(param.getValue(), os, [&]() {
5440 return op->emitOpError(
"invalid instance parameter '")
5441 << param.getName().getValue() <<
"' value";
5447 ps << PP::end << PP::newline <<
")";
5454 SmallVector<Value> instPortValues(modPortInfo.size());
5455 op.getValues(instPortValues, modPortInfo);
5456 emitInstancePortList(op, modPortInfo, instPortValues);
5458 ps.addCallback({op,
false});
5459 emitLocationInfoAndNewLine(ops);
5464 setPendingNewline();
5469LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5471 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5472 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5477 Operation *defaultModuleOp =
5478 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5480 SmallVector<Value> instPortValues(modPortInfo.size());
5481 op.getValues(instPortValues, modPortInfo);
5482 emitInstancePortList(op, modPortInfo, instPortValues);
5484 SmallPtrSet<Operation *, 8> ops;
5486 ps.addCallback({op,
false});
5487 emitLocationInfoAndNewLine(ops);
5492void StmtEmitter::emitInstancePortList(Operation *op,
5494 ArrayRef<Value> instPortValues) {
5495 SmallPtrSet<Operation *, 8> ops;
5498 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5499 ModulePortInfo containingPortList(containingModule.getPortList());
5504 size_t maxNameLength = 0;
5505 for (
auto &elt : modPortInfo) {
5506 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5509 auto getWireForValue = [&](Value result) {
5510 return result.getUsers().begin()->getOperand(0);
5514 bool isFirst =
true;
5515 bool isZeroWidth =
false;
5517 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5519 auto &modPort = modPortInfo.
at(portNum);
5521 Value portVal = instPortValues[portNum];
5526 bool shouldPrintComma =
true;
5528 shouldPrintComma =
false;
5529 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5531 shouldPrintComma =
true;
5536 if (shouldPrintComma)
5539 emitLocationInfoAndNewLine(ops);
5554 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5555 auto modPortName = modPort.getVerilogName();
5557 ps.spaces(maxNameLength - modPortName.size() + 1);
5559 ps.scopedBox(PP::ibox0, [&]() {
5566 if (!modPort.isOutput()) {
5568 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5569 ps <<
"/* Zero width */";
5571 emitExpression(portVal, ops, LowestPrecedence);
5572 }
else if (portVal.use_empty()) {
5573 ps <<
"/* unused */";
5574 }
else if (portVal.hasOneUse() &&
5575 (output = dyn_cast_or_null<OutputOp>(
5576 portVal.getUses().begin()->getOwner()))) {
5581 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5583 containingPortList.atOutput(outputPortNo).getVerilogName());
5585 portVal = getWireForValue(portVal);
5586 emitExpression(portVal, ops);
5592 if (!isFirst || isZeroWidth) {
5593 emitLocationInfoAndNewLine(ops);
5606LogicalResult StmtEmitter::visitSV(BindOp op) {
5607 emitter.emitBind(op);
5608 assert(state.pendingNewline);
5612LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5613 emitComment(op.getCommentAttr());
5615 emitSVAttributes(op);
5618 ps.addCallback({op,
true});
5620 setPendingNewline();
5622 emitStatementBlock(*op.getBodyBlock());
5624 ps <<
"endinterface" << PP::newline;
5625 ps.addCallback({op,
false});
5626 setPendingNewline();
5630LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5632 emitSVAttributes(op);
5634 ps.addCallback({op,
true});
5636 ps << PP::neverbox <<
"// ";
5637 ps.invokeWithStringOS([&](
auto &os) {
5642 ps.invokeWithStringOS(
5643 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5647 ps.addCallback({op,
false});
5648 setPendingNewline();
5652LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5654 ps.addCallback({op,
true});
5658 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5659 auto port = cast<ModportStructAttr>(portAttr);
5660 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5661 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5662 ps << PPExtString(getSymOpName(signalDecl));
5666 ps.addCallback({op,
false});
5667 setPendingNewline();
5671LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5673 ps.addCallback({op,
true});
5674 SmallPtrSet<Operation *, 8> emitted;
5677 emitExpression(op.getIface(), emitted);
5678 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5679 emitExpression(op.getRhs(), emitted);
5681 ps.addCallback({op,
false});
5682 setPendingNewline();
5686LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5687 auto decl = op.getReferencedMacro(&state.symbolCache);
5690 ps.addCallback({op,
true});
5692 if (decl.getArgs()) {
5694 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5695 ps << cast<StringAttr>(name);
5699 if (!op.getFormatString().empty()) {
5701 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5704 ps.addCallback({op,
false});
5705 setPendingNewline();
5709void StmtEmitter::emitStatement(Operation *op) {
5716 if (isa<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5720 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5721 succeeded(dispatchVerifVisitor(op)))
5724 emitOpError(op,
"emission to Verilog not supported");
5725 emitPendingNewlineIfNeeded();
5726 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5727 setPendingNewline();
5738 StmtEmitter &stmtEmitter) {
5745 if (isa<IfDefProceduralOp>(op->getParentOp()))
5753 SmallVector<Value, 8> exprsToScan(op->getOperands());
5758 while (!exprsToScan.empty()) {
5759 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5766 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5767 auto *defOp = readInout.getOperand().getDefiningOp();
5774 if (isa<sv::WireOp>(defOp))
5779 if (!isa<RegOp, LogicOp>(defOp))
5785 if (isa<LogicOp>(defOp) &&
5786 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5790 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5791 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5799 exprsToScan.append(expr->getOperands().begin(),
5800 expr->getOperands().end());
5806 if (expr->getBlock() != op->getBlock())
5811 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5818template <
class AssignTy>
5820 AssignTy singleAssign;
5821 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5822 if (hasSVAttributes(user))
5825 if (auto assign = dyn_cast<AssignTy>(user)) {
5828 singleAssign = assign;
5832 return isa<ReadInOutOp>(user);
5834 return singleAssign;
5840 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5844 if (op1->getBlock() != user->getBlock())
5850 return op1->isBeforeInBlock(user);
5854LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5855 emitSVAttributes(op);
5856 auto value = op->getResult(0);
5857 SmallPtrSet<Operation *, 8> opsForLocation;
5858 opsForLocation.insert(op);
5860 ps.addCallback({op,
true});
5863 auto type = value.getType();
5866 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5867 unsigned targetColumn = 0;
5868 unsigned column = 0;
5871 if (maxDeclNameWidth > 0)
5872 targetColumn += maxDeclNameWidth + 1;
5875 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5876 }
else if (!word.empty()) {
5878 column += word.size();
5879 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5880 ps.spaces(numSpaces);
5881 column += numSpaces;
5884 SmallString<8> typeString;
5887 llvm::raw_svector_ostream stringStream(typeString);
5892 if (maxTypeWidth > 0)
5893 targetColumn += maxTypeWidth + 1;
5894 unsigned numSpaces = 0;
5895 if (!typeString.empty()) {
5897 column += typeString.size();
5900 if (targetColumn > column)
5901 numSpaces = targetColumn - column;
5902 ps.spaces(numSpaces);
5903 column += numSpaces;
5909 ps.invokeWithStringOS(
5910 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5913 if (state.options.printDebugInfo) {
5914 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5915 auto innerSym = innerSymOp.getInnerSymAttr();
5916 if (innerSym && !innerSym.empty()) {
5918 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5924 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5925 ps << PP::space <<
"=" << PP::space;
5926 ps.invokeWithStringOS([&](
auto &os) {
5927 emitter.printParamValue(localparam.getValue(), os, [&]() {
5928 return op->emitOpError(
"invalid localparam value");
5933 if (
auto regOp = dyn_cast<RegOp>(op)) {
5934 if (
auto initValue = regOp.getInit()) {
5935 ps << PP::space <<
"=" << PP::space;
5936 ps.scopedBox(PP::ibox0, [&]() {
5937 emitExpression(initValue, opsForLocation, LowestPrecedence,
5946 if (isa<sv::WireOp>(op) &&
5947 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
5950 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
5951 auto *source = singleAssign.getSrc().getDefiningOp();
5955 if (!source || isa<ConstantOp>(source) ||
5956 op->getNextNode() == singleAssign) {
5957 ps << PP::space <<
"=" << PP::space;
5958 ps.scopedBox(PP::ibox0, [&]() {
5959 emitExpression(singleAssign.getSrc(), opsForLocation,
5963 emitter.assignsInlined.insert(singleAssign);
5971 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
5974 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
5977 auto *source = singleAssign.getSrc().getDefiningOp();
5981 if (!source || isa<ConstantOp>(source) ||
5984 ps << PP::space <<
"=" << PP::space;
5985 ps.scopedBox(PP::ibox0, [&]() {
5986 emitExpression(singleAssign.getSrc(), opsForLocation,
5991 emitter.assignsInlined.insert(singleAssign);
5992 emitter.expressionsEmittedIntoDecl.insert(op);
5999 ps.addCallback({op,
false});
6000 emitLocationInfoAndNewLine(opsForLocation);
6004void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6007 NameCollector collector(emitter);
6008 collector.collectNames(block);
6011 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6012 maxTypeWidth = collector.getMaxTypeWidth();
6015void StmtEmitter::emitStatementBlock(Block &body) {
6016 ps.scopedBox(PP::bbox2, [&]() {
6021 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6022 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6027 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6028 collectNamesAndCalculateDeclarationWidths(body);
6031 for (
auto &op : body) {
6038void ModuleEmitter::emitStatement(Operation *op) {
6039 StmtEmitter(*
this, state.options).emitStatement(op);
6044void ModuleEmitter::emitSVAttributes(Operation *op) {
6052 setPendingNewline();
6059void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6060 auto verilogName =
module.getVerilogModuleNameAttr();
6062 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6064 setPendingNewline();
6073void ModuleEmitter::emitBind(BindOp op) {
6075 emitError(op,
"SV attributes emission is unimplemented for the op");
6076 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6082 Operation *childMod =
6083 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6087 ps.addCallback({op,
true});
6088 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6089 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6091 bool isFirst =
true;
6092 ps.scopedBox(PP::bbox2, [&]() {
6093 auto parentPortInfo = parentMod.getPortList();
6097 size_t maxNameLength = 0;
6098 for (
auto &elt : childPortInfo) {
6099 auto portName = elt.getVerilogName();
6100 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6101 maxNameLength = std::max(maxNameLength, elt.getName().size());
6104 SmallVector<Value> instPortValues(childPortInfo.size());
6105 inst.getValues(instPortValues, childPortInfo);
6107 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6109 Value portVal = instPortValues[idx];
6115 bool shouldPrintComma =
true;
6117 shouldPrintComma =
false;
6118 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6120 shouldPrintComma =
true;
6125 if (shouldPrintComma)
6138 ps << PP::neverbox <<
"//";
6142 ps.nbsp(maxNameLength - elt.getName().size());
6144 llvm::SmallPtrSet<Operation *, 4> ops;
6145 if (elt.isOutput()) {
6146 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6147 "output port must have either single or no use");
6148 if (portVal.use_empty()) {
6149 ps <<
"/* unused */";
6150 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6151 portVal.getUses().begin()->getOwner())) {
6154 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6156 parentPortList.atOutput(outputPortNo).getVerilogName());
6158 portVal = portVal.getUsers().begin()->getOperand(0);
6159 ExprEmitter(*
this, ops)
6160 .emitExpression(portVal, LowestPrecedence,
6164 ExprEmitter(*
this, ops)
6165 .emitExpression(portVal, LowestPrecedence,
6178 ps.addCallback({op,
false});
6179 setPendingNewline();
6182void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6184 emitError(op,
"SV attributes emission is unimplemented for the op");
6186 auto instance = op.getReferencedInstance(&state.symbolCache);
6188 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6189 instance.getInterfaceType().getInterface());
6191 ps.addCallback({op,
true});
6192 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6193 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6195 ps.addCallback({op,
false});
6196 setPendingNewline();
6199void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6203 auto printParamType = [&](Type type, Attribute defaultValue,
6204 SmallString<8> &result) {
6206 llvm::raw_svector_ostream sstream(result);
6211 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6212 if (intAttr.getValue().getBitWidth() == 32)
6214 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6215 if (fpAttr.getType().isF64())
6218 if (isa<NoneType>(type))
6225 if (
auto intType = type_dyn_cast<IntegerType>(type))
6226 if (intType.getWidth() == 32) {
6227 sstream <<
"/*integer*/";
6231 printPackedType(type, sstream, module->getLoc(),
6239 size_t maxTypeWidth = 0;
6240 SmallString<8> scratch;
6241 for (
auto param : params) {
6242 auto paramAttr = cast<ParamDeclAttr>(param);
6244 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6245 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6248 if (maxTypeWidth > 0)
6251 ps.scopedBox(PP::bbox2, [&]() {
6252 ps << PP::newline <<
"#(";
6253 ps.scopedBox(PP::cbox0, [&]() {
6256 [&](Attribute param) {
6257 auto paramAttr = cast<ParamDeclAttr>(param);
6258 auto defaultValue = paramAttr.getValue();
6260 printParamType(paramAttr.getType(), defaultValue, scratch);
6261 if (!scratch.empty())
6263 if (scratch.size() < maxTypeWidth)
6264 ps.nbsp(maxTypeWidth - scratch.size());
6266 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6267 module, paramAttr.getName()));
6271 ps.invokeWithStringOS([&](
auto &os) {
6273 return module->emitError("parameter '")
6274 << paramAttr.getName().getValue()
6275 << "' has invalid value";
6280 [&]() { ps <<
"," << PP::newline; });
6286void ModuleEmitter::emitPortList(Operation *module,
6288 bool emitAsTwoStateType) {
6290 if (portInfo.
size())
6291 emitLocationInfo(module->getLoc());
6295 bool hasOutputs =
false, hasZeroWidth =
false;
6296 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6297 SmallVector<SmallString<8>, 16> portTypeStrings;
6299 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6300 auto port = portInfo.
at(i);
6304 lastNonZeroPort = i;
6307 portTypeStrings.push_back({});
6309 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6311 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6314 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6317 if (maxTypeWidth > 0)
6321 ps.scopedBox(PP::bbox2, [&]() {
6322 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6323 auto lastPort = e - 1;
6326 auto portType = portInfo.
at(portIdx).
type;
6330 bool isZeroWidth =
false;
6335 ps << (isZeroWidth ?
"// " :
" ");
6339 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6340 switch (thisPortDirection) {
6341 case ModulePort::Direction::Output:
6344 case ModulePort::Direction::Input:
6345 ps << (hasOutputs ?
"input " :
"input ");
6347 case ModulePort::Direction::InOut:
6348 ps << (hasOutputs ?
"inout " :
"inout ");
6351 bool emitWireInPorts = state.options.emitWireInPorts;
6352 if (emitWireInPorts)
6356 if (!portTypeStrings[portIdx].
empty())
6357 ps << portTypeStrings[portIdx];
6358 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6359 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6361 size_t startOfNamePos =
6362 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6368 ps.invokeWithStringOS(
6369 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6372 auto innerSym = portInfo.
at(portIdx).
getSym();
6373 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6375 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6380 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6384 if (
auto loc = portInfo.
at(portIdx).
loc)
6385 emitLocationInfo(loc);
6395 if (!state.options.disallowPortDeclSharing) {
6396 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6399 auto port = portInfo.
at(portIdx);
6403 bool isZeroWidth =
false;
6408 ps << (isZeroWidth ?
"// " :
" ");
6411 ps.nbsp(startOfNamePos);
6414 StringRef name = port.getVerilogName();
6418 ps.invokeWithStringOS(
6419 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6422 auto sym = port.getSym();
6423 if (state.options.printDebugInfo && sym && !sym.empty())
6424 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6428 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6432 if (
auto loc = port.loc)
6433 emitLocationInfo(loc);
6444 if (!portInfo.
size()) {
6446 SmallPtrSet<Operation *, 8> moduleOpSet;
6447 moduleOpSet.insert(module);
6448 emitLocationInfoAndNewLine(moduleOpSet);
6451 ps <<
");" << PP::newline;
6452 setPendingNewline();
6456void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6457 currentModuleOp =
module;
6459 emitComment(module.getCommentAttr());
6460 emitSVAttributes(module);
6462 ps.addCallback({module,
true});
6466 emitParameters(module, module.getParameters());
6470 assert(state.pendingNewline);
6473 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6476 ps.addCallback({module,
false});
6478 setPendingNewline();
6480 currentModuleOp =
nullptr;
6483void ModuleEmitter::emitFunc(FuncOp func) {
6485 if (func.isDeclaration())
6488 currentModuleOp = func;
6490 ps.addCallback({func,
true});
6494 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6496 ps <<
"endfunction";
6498 currentModuleOp =
nullptr;
6507 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6514 void emit(emit::FileListOp op);
6517 void emit(Block *block);
6519 void emitOp(emit::RefOp op);
6520 void emitOp(emit::VerbatimOp op);
6524 for (Operation &op : *block) {
6525 TypeSwitch<Operation *>(&op)
6526 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6527 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6528 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6529 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6530 .Case<BindInterfaceOp>(
6531 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6532 .Case<TypeScopeOp>([&](
auto typedecls) {
6533 ModuleEmitter(state).emitStatement(typedecls);
6536 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6542 for (
auto sym : op.getFiles()) {
6543 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6545 auto it = state.fileMapping.find(fileName);
6546 if (it == state.fileMapping.end()) {
6547 emitOpError(op,
" references an invalid file: ") << sym;
6551 auto file = cast<emit::FileOp>(it->second);
6552 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6559 StringAttr target = op.getTargetAttr().getAttr();
6560 auto *targetOp = state.symbolCache.getDefinition(target);
6561 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6563 TypeSwitch<Operation *>(targetOp)
6564 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6565 .Case<hw::HWModuleOp>(
6566 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6567 .Case<TypeScopeOp>([&](
auto typedecls) {
6568 ModuleEmitter(state).emitStatement(typedecls);
6571 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6577 SmallPtrSet<Operation *, 8> ops;
6582 StringRef text = op.getText();
6586 const auto &[lhs, rhs] = text.split(
'\n');
6590 ps << PP::end << PP::newline << PP::neverbox;
6592 }
while (!text.empty());
6595 emitLocationInfoAndNewLine(ops);
6613 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6614 moduleOp->walk([&](Operation *op) {
6616 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6619 SymbolTable::getSymbolAttrName()),
6620 name.getSymName(), op);
6621 if (isa<BindOp>(op))
6627 auto collectPorts = [&](
auto moduleOp) {
6628 auto portInfo = moduleOp.getPortList();
6629 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6630 if (!p.attrs || p.attrs.empty())
6632 for (NamedAttribute portAttr : p.attrs) {
6633 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6642 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6643 for (
auto file :
designOp.getOps<emit::FileOp>())
6644 for (
auto refs : file.getOps<emit::RefOp>())
6645 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6647 SmallString<32> outputPath;
6648 for (
auto &op : *
designOp.getBody()) {
6651 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6653 bool hasFileName =
false;
6654 bool emitReplicatedOps = !isFileOp;
6655 bool addToFilelist = !isFileOp;
6661 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6663 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6664 <<
" on " << op <<
"\n";);
6665 if (!attr.isDirectory())
6668 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6669 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6672 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6677 if (!defaultFileName.isTriviallyEmpty()) {
6678 llvm::sys::path::append(outputPath, defaultFileName);
6680 op->emitError(
"file name unspecified");
6682 llvm::sys::path::append(outputPath,
"error.out");
6686 auto destFile = StringAttr::get(op->getContext(), outputPath);
6687 auto &file =
files[destFile];
6688 file.ops.push_back(info);
6689 file.emitReplicatedOps = emitReplicatedOps;
6690 file.addToFilelist = addToFilelist;
6691 file.isVerilog = outputPath.ends_with(
".sv");
6696 if (!attr || attr.isDirectory()) {
6697 auto excludeFromFileListAttr =
6698 BoolAttr::get(op->getContext(), !addToFilelist);
6699 auto includeReplicatedOpsAttr =
6700 BoolAttr::get(op->getContext(), emitReplicatedOps);
6701 auto outputFileAttr = hw::OutputFileAttr::get(
6702 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6703 op->setAttr(
"output_file", outputFileAttr);
6709 TypeSwitch<Operation *>(&op)
6710 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6712 fileMapping.try_emplace(file.getSymNameAttr(), file);
6713 separateFile(file, file.getFileName());
6715 .Case<emit::FragmentOp>([&](
auto fragment) {
6718 .Case<HWModuleOp>([&](
auto mod) {
6720 auto sym = mod.getNameAttr();
6723 collectInstanceSymbolsAndBinds(mod);
6725 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6726 if (it->second.size() != 1 || attr) {
6729 op.emitError(
"modules can be emitted to a single file");
6737 if (attr || separateModules)
6743 .Case<InterfaceOp>([&](InterfaceOp intf) {
6748 for (
auto &op : *intf.getBodyBlock())
6749 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6750 if (
auto name = symOp.getNameAttr())
6754 if (attr || separateModules)
6755 separateFile(intf, intf.getSymName() +
".sv");
6765 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6766 [&](Operation *op) {
6772 separateFile(op,
"");
6774 .Case<FuncOp>([&](
auto op) {
6780 separateFile(op,
"");
6784 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6787 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6796 separateFile(op,
"");
6798 .Case<BindOp>([&](
auto op) {
6800 separateFile(op,
"bindfile.sv");
6805 .Case<MacroDeclOp>([&](
auto op) {
6808 .Case<sv::ReserveNamesOp>([](
auto op) {
6811 .Case<om::ClassLike>([&](
auto op) {
6814 .Case<om::ConstantOp>([&](
auto op) {
6817 .Default([&](
auto *) {
6818 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6838 size_t lastReplicatedOp = 0;
6840 bool emitHeaderInclude =
6843 if (emitHeaderInclude)
6846 size_t numReplicatedOps =
6851 DenseSet<emit::FragmentOp> includedFragments;
6852 for (
const auto &opInfo : file.
ops) {
6853 Operation *op = opInfo.op;
6857 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6863 if (
auto fragments =
6865 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6869 op->emitError(
"cannot find referenced fragment ") << sym;
6872 emit::FragmentOp fragment = it->second;
6873 if (includedFragments.insert(fragment).second) {
6874 thingsToEmit.emplace_back(it->second);
6880 thingsToEmit.emplace_back(op);
6885 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6890 TypeSwitch<Operation *>(op)
6891 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6892 .Case<HWModuleExternOp>([&](
auto op) {
6895 .Case<HWModuleGeneratedOp>(
6896 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6897 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6898 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6899 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6900 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6901 .Case<TypeScopeOp>([&](
auto typedecls) {
6902 ModuleEmitter(state).emitStatement(typedecls);
6904 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6906 .Case<MacroDefOp, FuncDPIImportOp>(
6907 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6908 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6909 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6910 .Default([&](
auto *op) {
6911 state.encounteredError =
true;
6912 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6919 llvm::formatted_raw_ostream &os,
6920 StringAttr fileName,
bool parallelize) {
6921 MLIRContext *context =
designOp->getContext();
6925 parallelize &= context->isMultithreadingEnabled();
6936 size_t lineOffset = 0;
6937 for (
auto &entry : thingsToEmit) {
6938 entry.verilogLocs.setStream(os);
6939 if (
auto *op = entry.getOperation()) {
6944 state.addVerilogLocToOps(lineOffset, fileName);
6946 os << entry.getStringData();
6951 if (state.encounteredError)
6969 SmallString<256> buffer;
6970 llvm::raw_svector_ostream tmpStream(buffer);
6971 llvm::formatted_raw_ostream rs(tmpStream);
6982 for (
auto &entry : thingsToEmit) {
6985 auto *op = entry.getOperation();
6987 auto lineOffset = os.getLine() + 1;
6988 os << entry.getStringData();
6992 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
6995 entry.verilogLocs.setStream(os);
7002 state.addVerilogLocToOps(0, fileName);
7018 module.emitWarning()
7019 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7020 "created only at SplitExportVerilog";
7029 for (
const auto &it : emitter.
files) {
7030 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7031 "\" ----- 8< -----\n\n");
7037 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7038 "\" ----- 8< -----\n\n");
7039 for (
auto &name : it.second)
7040 contents += name.str() +
"\n";
7041 list.emplace_back(contents);
7044 llvm::formatted_raw_ostream rs(os);
7048 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7057 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7059 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7060 if (failed(failableParallelForEach(
7061 module->getContext(), modulesToPrepare,
7062 [&](
auto op) { return prepareHWModule(op, options); })))
7069struct ExportVerilogPass
7070 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7071 ExportVerilogPass(raw_ostream &os) : os(os) {}
7072 void runOnOperation()
override {
7074 mlir::OpPassManager preparePM(
"builtin.module");
7077 auto &modulePM = preparePM.nestAny();
7079 if (failed(runPipeline(preparePM, getOperation())))
7080 return signalPassFailure();
7083 return signalPassFailure();
7090struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7091 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7092 : ExportVerilogPass{*os} {
7093 owned = std::move(os);
7097 std::unique_ptr<llvm::raw_ostream> owned;
7101std::unique_ptr<mlir::Pass>
7103 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7106std::unique_ptr<mlir::Pass>
7108 return std::make_unique<ExportVerilogPass>(os);
7119static std::unique_ptr<llvm::ToolOutputFile>
7123 SmallString<128> outputFilename(dirname);
7125 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7128 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7130 emitter.
designOp.emitError(
"cannot create output directory \"")
7131 << outputDir <<
"\": " << error.message();
7137 std::string errorMessage;
7138 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7140 emitter.
designOp.emitError(errorMessage);
7157 llvm::formatted_raw_ostream rs(output->os());
7163 StringAttr::get(fileName.getContext(), output->getFilename()),
7169 StringRef dirname) {
7180 bool insertSuccess =
7182 .insert({StringAttr::get(module.getContext(),
circtHeader),
7188 if (!insertSuccess) {
7189 module.emitError() << "tried to emit a heder to " << circtHeader
7190 << ", but the file is used as an output too.";
7196 parallelForEach(module->getContext(), emitter.
files.begin(),
7197 emitter.
files.end(), [&](
auto &it) {
7198 createSplitOutputFile(it.first, it.second, dirname,
7203 SmallString<128> filelistPath(dirname);
7204 llvm::sys::path::append(filelistPath,
"filelist.f");
7206 std::string errorMessage;
7207 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7209 module->emitError(errorMessage);
7213 for (
const auto &it : emitter.
files) {
7214 if (it.second.addToFilelist)
7215 output->os() << it.first.str() <<
"\n";
7224 for (
auto &name : it.second)
7225 output->os() << name.str() <<
"\n";
7236 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7238 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7239 if (failed(failableParallelForEach(
7240 module->getContext(), modulesToPrepare,
7241 [&](
auto op) { return prepareHWModule(op, options); })))
7249struct ExportSplitVerilogPass
7250 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7251 ExportSplitVerilogPass(StringRef directory) {
7252 directoryName = directory.str();
7254 void runOnOperation()
override {
7256 mlir::OpPassManager preparePM(
"builtin.module");
7261 if (failed(runPipeline(preparePM, getOperation())))
7262 return signalPassFailure();
7265 return signalPassFailure();
7270std::unique_ptr<mlir::Pass>
7272 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 type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef fileName, StringRef dirname, SharedEmitterState &emitter)
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 void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc)
Push this type's dimension into a vector.
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 bool haveMatchingDims(Type a, Type b, Location loc)
True iff 'a' and 'b' have the same wire dims.
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 isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static 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)
static InstancePath empty
RewritePatternSet pattern
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
This stores lookup tables to make manipulating and working with the IR more efficient.
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=~0ULL)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
This helps visit TypeOp nodes.
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module)
Generates the macros used by instance choices.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
PP
Send one of these to TokenStream to add the corresponding token.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoicesPass()
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< mlir::Pass > createLegalizeAnonEnumsPass()
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.
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
Information to control the emission of a single operation into a file.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
const LoweringOptions & options
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.