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);
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)
201 return op.getVerilogNameAttr().getValue();
203 .Case<InterfaceOp>([&](InterfaceOp op) {
206 .Case<InterfaceSignalOp>(
207 [&](InterfaceSignalOp op) {
return op.getSymName(); })
208 .Case<InterfaceModportOp>(
209 [&](InterfaceModportOp op) {
return op.getSymName(); })
210 .Default([&](Operation *op) {
211 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
212 return attr.getValue();
213 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
214 return attr.getValue();
215 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
216 return attr.getValue();
218 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
219 return attr.getValue();
220 return StringRef(
"");
225template <
typename PPS>
227 os <<
"/*Zero width*/ 1\'b0";
232 auto hml = cast<HWModuleLike>(module);
233 return hml.getPort(portArgNum).getVerilogName();
238 auto hml = cast<HWModuleLike>(module);
239 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
240 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
241 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
242 return updatedName.getValue();
243 return hml.getHWModuleType().getPortName(pId);
252 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
253 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
254 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
255 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
256 UnpackedOpenArrayCastOp>(op))
260 if (isa<verif::ContractOp>(op))
270static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
272 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
273 if (integer.getWidth() != 1)
274 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
277 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
278 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
283 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
284 dims.push_back(intType.getWidth());
288 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
289 return getTypeDims(dims, inout.getElementType(), loc);
290 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
291 return getTypeDims(dims, uarray.getElementType(), loc);
292 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
293 return getTypeDims(dims, uarray.getElementType(), loc);
295 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
298 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
304 SmallVector<Attribute, 4> aDims;
307 SmallVector<Attribute, 4> bDims;
310 return aDims == bDims;
316 if (
auto intType = dyn_cast<IntegerType>(type))
317 return intType.getWidth() == 0;
318 if (
auto inout = dyn_cast<hw::InOutType>(type))
320 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
321 return uarray.getNumElements() == 0 ||
323 if (
auto array = dyn_cast<hw::ArrayType>(type))
324 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
325 if (
auto structType = dyn_cast<hw::StructType>(type))
326 return llvm::all_of(structType.getElements(),
327 [](
auto elem) { return isZeroBitType(elem.type); });
328 if (
auto enumType = dyn_cast<hw::EnumType>(type))
329 return enumType.getFields().empty();
330 if (
auto unionType = dyn_cast<hw::UnionType>(type))
331 return hw::getBitWidth(unionType) == 0;
343 return TypeSwitch<Type, Type>(type)
344 .Case<InOutType>([](InOutType inoutType) {
347 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
350 .Default([](Type type) {
return type; });
355 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
356 auto elementType = cast<hw::InOutType>(type).getElementType();
362 return TypeSwitch<Type, bool>(type)
363 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
366 .Case<StructType>([](
auto) {
return true; })
367 .Default([](
auto) {
return false; });
381 if (
auto name = lhs.getName().compare(rhs.getName()))
383 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
388 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
390 if (lhs.getLine() != rhs.getLine())
391 return lhs.getLine() < rhs.getLine() ? -1 : 1;
392 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
397 Location lhsCallee = lhs.getCallee();
398 Location rhsCallee = rhs.getCallee();
402 Location lhsCaller = lhs.getCaller();
403 Location rhsCaller = rhs.getCaller();
407template <
typename TTargetLoc>
409 auto lhsT = dyn_cast<TTargetLoc>(lhs);
410 auto rhsT = dyn_cast<TTargetLoc>(rhs);
437 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
442 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
447 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
464 SmallPtrSetImpl<Attribute> &locationSet) {
465 llvm::TypeSwitch<Location, void>(loc)
466 .Case<FusedLoc>([&](
auto fusedLoc) {
467 for (
auto subLoc : fusedLoc.getLocations())
470 .Default([&](
auto loc) { locationSet.insert(loc); });
474template <
typename TVector>
476 llvm::array_pod_sort(
477 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
478 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
486 SmallPtrSet<Attribute, 8> locationSet;
487 locationSet.insert(loc);
488 llvm::raw_string_ostream os(
output);
494 const SmallPtrSetImpl<Operation *> &ops) {
498 SmallPtrSet<Attribute, 8> locationSet;
501 llvm::raw_string_ostream os(
output);
510 const SmallPtrSetImpl<Attribute> &locationSet) {
511 if (style == LoweringOptions::LocationInfoStyle::None)
514 llvm::raw_string_ostream sstr(resstr);
516 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
520 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
521 "other styles must be already handled");
522 os <<
"@[" << resstr <<
"]";
531 const SmallPtrSetImpl<Attribute> &locationSet)
547 bool withName = !loc.getName().empty();
549 os <<
"'" << loc.getName().strref() <<
"'(";
558 os << loc.getFilename().getValue();
559 if (
auto line = loc.getLine()) {
561 if (
auto col = loc.getColumn())
573 StringRef lastFileName;
574 for (
size_t i = 0, e = locVector.size(); i != e;) {
579 auto first = locVector[i];
580 if (first.getFilename() != lastFileName) {
581 lastFileName = first.getFilename();
588 first.getFilename() == locVector[
end].getFilename() &&
589 first.getLine() == locVector[
end].getLine())
594 if (
auto line = first.getLine()) {
596 if (
auto col = first.getColumn())
604 os <<
':' << first.getLine() <<
":{";
606 os << locVector[i++].getColumn();
618 llvm::TypeSwitch<Location, void>(loc)
619 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
621 .Case<mlir::FusedLoc>([&](
auto loc) {
622 SmallPtrSet<Attribute, 8> locationSet;
626 .Default([&](
auto loc) {
638 switch (locationSet.size()) {
649 SmallVector<FileLineColLoc, 8> flcLocs;
650 SmallVector<Attribute, 8> otherLocs;
651 flcLocs.reserve(locationSet.size());
652 otherLocs.reserve(locationSet.size());
653 for (Attribute loc : locationSet) {
654 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
655 flcLocs.push_back(flcLoc);
657 otherLocs.push_back(loc);
668 size_t sstrSize =
os.tell();
669 bool emittedAnything =
false;
670 auto recheckEmittedSomething = [&]() {
671 size_t currSize =
os.tell();
672 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
673 emittedAnything |= emittedSomethingSinceLastCheck;
675 return emittedSomethingSinceLastCheck;
684 if (recheckEmittedSomething()) {
686 recheckEmittedSomething();
692 if (emittedAnything && !flcLocs.empty())
697 llvm::raw_string_ostream &
os;
709 if (isa<BlockArgument>(v))
718 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
723 if (v.getDefiningOp<ReadInterfaceSignalOp>())
736 if (
auto cast = dyn_cast<BitcastOp>(op))
737 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
741 if (op->hasOneUse() &&
742 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
750 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp, ArrayInjectOp>(
756 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
760 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
761 if (verbatim.getFormatString().size() > 32)
766 for (
auto &use : op->getUses()) {
767 auto *user = use.getOwner();
777 UnionExtractOp, IndexedPartSelectOp>(user))
778 if (use.getOperandNumber() == 0 &&
789 auto usedInExprControl = [user, &use]() {
790 return TypeSwitch<Operation *, bool>(user)
791 .Case<ltl::ClockOp>([&](
auto clockOp) {
793 return clockOp.getClock() == use.get();
795 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
796 sv::CoverConcurrentOp>(
797 [&](
auto op) {
return op.getClock() == use.get(); })
798 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
799 sv::CoverPropertyOp>([&](
auto op) {
800 return op.getDisable() == use.get() || op.getClock() == use.get();
802 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
807 .Default([](
auto) {
return false; });
810 if (!usedInExprControl())
814 auto read = dyn_cast<ReadInOutOp>(op);
817 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
828 unsigned numStatements = 0;
829 block.walk([&](Operation *op) {
831 isa_and_nonnull<ltl::LTLDialect>(op->getDialect()))
832 return WalkResult::advance();
834 TypeSwitch<Operation *, unsigned>(op)
835 .Case<VerbatimOp>([&](
auto) {
841 .Case<IfOp>([&](
auto) {
852 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
853 .Case<OutputOp>([&](OutputOp oop) {
856 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
857 Operation *op = operand.getDefiningOp();
858 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
861 .Default([](
auto) {
return 1; });
862 if (numStatements > 1)
863 return WalkResult::interrupt();
864 return WalkResult::advance();
866 if (numStatements == 0)
868 if (numStatements == 1)
878 if (op->getResult(0).use_empty())
883 if (op->hasOneUse() &&
884 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
885 *op->getUsers().begin()))
907 for (
auto &op : *elseBlock) {
908 if (
auto opIf = dyn_cast<IfOp>(op)) {
925template <
typename PPS>
927 enum Container { NoContainer, InComment, InAttr };
928 Container currentContainer = NoContainer;
930 auto closeContainer = [&] {
931 if (currentContainer == NoContainer)
933 if (currentContainer == InComment)
935 else if (currentContainer == InAttr)
937 ps << PP::end << PP::end;
939 currentContainer = NoContainer;
942 bool isFirstContainer =
true;
943 auto openContainer = [&](Container newContainer) {
944 assert(newContainer != NoContainer);
945 if (currentContainer == newContainer)
949 if (!isFirstContainer)
950 ps << (mayBreak ? PP::space : PP::nbsp);
951 isFirstContainer =
false;
954 if (newContainer == InComment)
956 else if (newContainer == InAttr)
958 currentContainer = newContainer;
966 ps.scopedBox(PP::cbox0, [&]() {
967 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
968 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
970 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
972 if (attr.getExpression())
973 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
982 if (
auto *op = val.getDefiningOp())
985 if (
auto port = dyn_cast<BlockArgument>(val)) {
987 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
988 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
990 port.getArgNumber());
992 assert(
false &&
"unhandled value");
1004class VerilogEmitterState {
1006 explicit VerilogEmitterState(ModuleOp designOp,
1012 llvm::formatted_raw_ostream &os,
1013 StringAttr fileName,
OpLocMap &verilogLocMap)
1014 : designOp(designOp), shared(shared), options(options),
1015 symbolCache(symbolCache), globalNames(globalNames),
1016 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1017 pp(os, options.emittedLineLength), fileName(fileName) {
1018 pp.setListener(&saver);
1041 llvm::formatted_raw_ostream &os;
1043 bool encounteredError =
false;
1052 bool pendingNewline =
false;
1066 StringAttr fileName;
1072 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1075 verilogLocMap.
clear();
1079 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1080 void operator=(
const VerilogEmitterState &) =
delete;
1093using CallbackDataTy = std::pair<Operation *, bool>;
1097 VerilogEmitterState &state;
1102 explicit EmitterBase(VerilogEmitterState &state)
1104 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1106 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1107 state.encounteredError =
true;
1108 return op->emitError(message);
1111 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1112 state.encounteredError =
true;
1113 return op->emitOpError(message);
1116 void emitLocationImpl(llvm::StringRef location) {
1119 ps << PP::neverbreak;
1120 if (!location.empty())
1121 ps <<
"\t// " << location;
1124 void emitLocationInfo(Location loc) {
1132 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1135 setPendingNewline();
1138 template <
typename PPS>
1139 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1140 llvm::function_ref<
void(Value)> operandEmitter,
1141 ArrayAttr symAttrs);
1147 void emitComment(StringAttr comment);
1151 void emitPendingNewlineIfNeeded() {
1152 if (state.pendingNewline) {
1153 state.pendingNewline =
false;
1157 void setPendingNewline() {
1158 assert(!state.pendingNewline);
1159 state.pendingNewline =
true;
1162 void startStatement() { emitPendingNewlineIfNeeded(); }
1165 void operator=(
const EmitterBase &) =
delete;
1166 EmitterBase(
const EmitterBase &) =
delete;
1170template <
typename PPS>
1171void EmitterBase::emitTextWithSubstitutions(
1172 PPS &ps, StringRef
string, Operation *op,
1173 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1184 if (
auto *itemOp = item.getOp()) {
1185 if (item.hasPort()) {
1189 if (!symOpName.empty())
1191 emitError(itemOp,
"cannot get name for symbol ") << sym;
1193 emitError(op,
"cannot get name for symbol ") << sym;
1195 return StringRef(
"<INVALID>");
1201 unsigned numSymOps = symAttrs.size();
1202 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1205 next =
string.find(
"{{", next);
1206 if (next == StringRef::npos)
1213 while (next <
string.size() &&
isdigit(
string[next]))
1216 if (start == next) {
1220 size_t operandNoLength = next - start;
1223 StringRef fmtOptsStr;
1224 if (
string[next] ==
':') {
1225 size_t startFmtOpts = next + 1;
1226 while (next <
string.size() &&
string[next] !=
'}')
1228 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1232 if (!
string.substr(next).starts_with(
"}}"))
1236 unsigned operandNo = 0;
1237 if (
string.drop_front(start)
1238 .take_front(operandNoLength)
1239 .getAsInteger(10, operandNo)) {
1240 emitError(op,
"operand substitution too large");
1246 auto before =
string.take_front(start - 2);
1247 if (!before.empty())
1252 if (operandNo < op->getNumOperands())
1254 operandEmitter(op->getOperand(operandNo));
1255 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1256 unsigned symOpNum = operandNo - op->getNumOperands();
1257 auto sym = symAttrs[symOpNum];
1258 StringRef symVerilogName;
1259 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1260 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1261 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1262 auto namepath = globalRef.getNamepathAttr().getValue();
1263 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1266 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1268 auto innerRef = cast<InnerRefAttr>(sym);
1269 auto ref = state.symbolCache.getInnerDefinition(
1270 innerRef.getModule(), innerRef.getName());
1271 ps << namify(innerRef, ref);
1274 symVerilogName = namify(sym, symOp);
1277 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1278 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1280 symVerilogName = namify(sym, symOp);
1282 if (!symVerilogName.empty())
1285 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1289 string =
string.drop_front(next);
1295 while (emitUntilSubstitution())
1299 if (!
string.
empty())
1303void EmitterBase::emitComment(StringAttr comment) {
1310 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1314 auto ref = comment.getValue();
1316 while (!ref.empty()) {
1317 std::tie(line, ref) = ref.split(
"\n");
1324 if (line.size() <= lineLength) {
1326 setPendingNewline();
1337 auto breakPos = line.rfind(
' ', lineLength);
1339 if (breakPos == StringRef::npos) {
1340 breakPos = line.find(
' ', lineLength);
1343 if (breakPos == StringRef::npos)
1344 breakPos = line.size();
1351 setPendingNewline();
1352 breakPos = line.find_first_not_of(
' ', breakPos);
1354 if (breakPos == StringRef::npos)
1357 line = line.drop_front(breakPos);
1367 bool addPrefixUnderScore =
true;
1370 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1374 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1376 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1378 result = StringAttr::get(expr.getContext(), name);
1380 }
else if (
auto *op = expr.getDefiningOp()) {
1382 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1384 result = StringAttr::get(expr.getContext(), name);
1386 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1392 addPrefixUnderScore =
false;
1394 TypeSwitch<Operation *>(op)
1397 .Case([&result](VerbatimExprOp verbatim) {
1398 verbatim.getAsmResultNames([&](Value, StringRef name) {
1399 result = StringAttr::get(verbatim.getContext(), name);
1402 .Case([&result](VerbatimExprSEOp verbatim) {
1403 verbatim.getAsmResultNames([&](Value, StringRef name) {
1404 result = StringAttr::get(verbatim.getContext(), name);
1410 if (
auto operandName =
1413 cast<IntegerType>(extract.getType()).getWidth();
1415 result = StringAttr::get(extract.getContext(),
1416 operandName.strref() +
"_" +
1417 Twine(extract.getLowBit()));
1419 result = StringAttr::get(
1420 extract.getContext(),
1421 operandName.strref() +
"_" +
1422 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1423 Twine(extract.getLowBit()));
1431 if (!result || result.strref().empty())
1435 if (addPrefixUnderScore && result.strref().front() !=
'_')
1436 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1448class ModuleEmitter :
public EmitterBase {
1450 explicit ModuleEmitter(VerilogEmitterState &state)
1451 : EmitterBase(state), currentModuleOp(nullptr),
1455 emitPendingNewlineIfNeeded();
1459 void emitParameters(Operation *module, ArrayAttr params);
1460 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1461 bool emitAsTwoStateType =
false);
1464 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1465 void emitFunc(FuncOp);
1468 void emitStatement(Operation *op);
1469 void emitBind(BindOp op);
1470 void emitBindInterface(BindInterfaceOp op);
1472 void emitSVAttributes(Operation *op);
1475 StringRef getVerilogStructFieldName(StringAttr field) {
1476 return fieldNameResolver.getRenamedFieldName(field).getValue();
1483 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1495 bool printPackedType(Type type, raw_ostream &os, Location loc,
1496 Type optionalAliasType = {},
bool implicitIntType =
true,
1497 bool singleBitDefaultType =
true,
1498 bool emitAsTwoStateType =
false);
1502 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1510 function_ref<InFlightDiagnostic()> emitError);
1513 VerilogPrecedence parenthesizeIfLooserThan,
1514 function_ref<InFlightDiagnostic()> emitError);
1520 Operation *currentModuleOp;
1526 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1532 SmallPtrSet<Operation *, 16> assignsInlined;
1541 const ModuleEmitter &emitter) {
1542 if (isa<RegOp>(op)) {
1547 cast<InOutType>(op->getResult(0).getType()).getElementType();
1554 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1555 while (isa<ArrayType>(innerType.getElementType()))
1556 innerType = cast<ArrayType>(innerType.getElementType());
1557 if (isa<StructType>(innerType.getElementType()) ||
1558 isa<TypeAliasType>(innerType.getElementType()))
1566 if (isa<sv::WireOp>(op))
1568 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1569 return "localparam";
1572 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1573 return interface.getInterfaceType().getInterface().getValue();
1577 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1581 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1583 if (isa<LogicOp>(op)) {
1589 if (isProcedural && !stripAutomatic)
1590 return hasStruct ?
"automatic" :
"automatic logic";
1591 return hasStruct ?
"" :
"logic";
1598 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1601 assert(!emitter.state.options.disallowLocalVariables &&
1602 "automatic variables not allowed");
1606 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1607 :
"automatic logic";
1614static void emitDim(Attribute width, raw_ostream &os, Location loc,
1615 ModuleEmitter &emitter,
bool downTo) {
1617 os <<
"<<invalid type>>";
1620 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1621 if (intAttr.getValue().isZero()) {
1622 os <<
"/*Zero Width*/";
1627 os << (intAttr.getValue().getZExtValue() - 1);
1637 auto typedAttr = dyn_cast<TypedAttr>(width);
1639 mlir::emitError(loc,
"untyped dimension attribute ") << width;
1643 getIntAttr(loc.getContext(), typedAttr.getType(),
1644 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1645 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1649 emitter.printParamValue(width, os, [loc]() {
1650 return mlir::emitError(loc,
"invalid parameter in type");
1658static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1659 ModuleEmitter &emitter) {
1660 for (Attribute width : dims) {
1661 emitDim(width, os, loc, emitter,
true);
1666void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1667 SmallVector<Attribute, 4> dims;
1699 SmallVectorImpl<Attribute> &dims,
1700 bool implicitIntType,
bool singleBitDefaultType,
1701 ModuleEmitter &emitter,
1702 Type optionalAliasType = {},
1703 bool emitAsTwoStateType =
false) {
1704 return TypeSwitch<Type, bool>(type)
1705 .Case<IntegerType>([&](IntegerType integerType) {
1706 if (emitAsTwoStateType && dims.empty()) {
1708 if (!typeName.empty()) {
1713 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1715 getInt32Attr(type.getContext(), integerType.getWidth()));
1717 StringRef typeName =
1718 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1719 if (!typeName.empty()) {
1726 return !dims.empty() || !implicitIntType;
1728 .Case<IntType>([&](IntType intType) {
1729 if (!implicitIntType)
1731 dims.push_back(intType.getWidth());
1735 .Case<ArrayType>([&](ArrayType arrayType) {
1736 dims.push_back(arrayType.getSizeAttr());
1738 implicitIntType, singleBitDefaultType,
1740 emitAsTwoStateType);
1742 .Case<InOutType>([&](InOutType inoutType) {
1744 implicitIntType, singleBitDefaultType,
1746 emitAsTwoStateType);
1748 .Case<EnumType>([&](EnumType enumType) {
1750 if (enumType.getBitWidth() != 32)
1751 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1753 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1754 llvm::interleaveComma(
1755 enumType.getFields().getAsRange<StringAttr>(), os,
1756 [&](
auto enumerator) {
1757 os << emitter.fieldNameResolver.getEnumFieldName(
1758 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1763 .Case<StructType>([&](StructType structType) {
1764 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1765 os <<
"/*Zero Width*/";
1768 os <<
"struct packed {";
1769 for (
auto &element : structType.getElements()) {
1771 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1772 <<
": Zero Width;*/ ";
1775 SmallVector<Attribute, 8> structDims;
1780 {}, emitAsTwoStateType);
1781 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1782 emitter.printUnpackedTypePostfix(element.type, os);
1789 .Case<UnionType>([&](UnionType unionType) {
1790 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1791 os <<
"/*Zero Width*/";
1795 int64_t unionWidth = hw::getBitWidth(unionType);
1796 os <<
"union packed {";
1797 for (
auto &element : unionType.getElements()) {
1799 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1800 <<
": Zero Width;*/ ";
1803 int64_t elementWidth = hw::getBitWidth(element.type);
1804 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1806 os <<
" struct packed {";
1807 if (element.offset) {
1808 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1809 << element.offset - 1 <<
":0] "
1810 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1814 SmallVector<Attribute, 8> structDims;
1818 true, emitter, {}, emitAsTwoStateType);
1819 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1820 emitter.printUnpackedTypePostfix(element.type, os);
1824 if (elementWidth + (int64_t)element.offset < unionWidth) {
1825 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1826 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1827 <<
"__post_padding_" << element.name.getValue() <<
";";
1829 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1838 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1839 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1840 os <<
"<<unexpected unpacked array>>";
1841 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1845 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1846 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1848 mlir::emitError(loc,
"unresolvable type reference");
1851 if (typedecl.getType() != typeRef.getInnerType()) {
1852 mlir::emitError(loc,
"declared type did not match aliased type");
1856 os << typedecl.getPreferredName();
1857 emitDims(dims, os, typedecl->getLoc(), emitter);
1860 .Default([&](Type type) {
1861 os <<
"<<invalid type '" << type <<
"'>>";
1862 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1878bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1879 Type optionalAliasType,
1880 bool implicitIntType,
1881 bool singleBitDefaultType,
1882 bool emitAsTwoStateType) {
1883 SmallVector<Attribute, 8> packedDimensions;
1885 singleBitDefaultType, *
this, optionalAliasType,
1886 emitAsTwoStateType);
1892void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1893 TypeSwitch<Type, void>(type)
1895 printUnpackedTypePostfix(inoutType.getElementType(), os);
1897 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1898 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1899 : state.designOp->getLoc();
1900 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1902 printUnpackedTypePostfix(arrayType.getElementType(), os);
1904 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1906 printUnpackedTypePostfix(arrayType.getElementType(), os);
1908 .Case<InterfaceType>([&](
auto) {
1922ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1923 function_ref<InFlightDiagnostic()> emitError) {
1924 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1932ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1933 VerilogPrecedence parenthesizeIfLooserThan,
1934 function_ref<InFlightDiagnostic()> emitError) {
1935 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1936 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1937 APInt value = intAttr.getValue();
1941 if (intTy.getWidth() > 32) {
1943 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1947 if (intTy.isSigned())
1948 os << intTy.getWidth() <<
"'sd";
1950 os << intTy.getWidth() <<
"'d";
1952 value.print(os, intTy.isSigned());
1953 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1955 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1957 os.write_escaped(strAttr.getValue());
1959 return {Symbol, IsUnsigned};
1961 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1963 os << fpAttr.getValueAsDouble();
1964 return {Symbol, IsUnsigned};
1966 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1967 os << verbatimParam.getValue().getValue();
1968 return {Symbol, IsUnsigned};
1970 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1972 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1973 parameterRef.getName());
1976 return {Symbol, IsUnsigned};
1980 auto expr = dyn_cast<ParamExprAttr>(value);
1982 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1983 emitError() <<
" = " << value;
1984 return {LowestPrecedence, IsUnsigned};
1987 StringRef operatorStr;
1988 StringRef openStr, closeStr;
1989 VerilogPrecedence subprecedence = LowestPrecedence;
1990 VerilogPrecedence prec;
1991 std::optional<SubExprSignResult> operandSign;
1992 bool isUnary =
false;
1993 bool hasOpenClose =
false;
1995 switch (expr.getOpcode()) {
1997 operatorStr =
" + ";
1998 subprecedence = Addition;
2001 operatorStr =
" * ";
2002 subprecedence = Multiply;
2005 operatorStr =
" & ";
2006 subprecedence = And;
2009 operatorStr =
" | ";
2013 operatorStr =
" ^ ";
2014 subprecedence = Xor;
2017 operatorStr =
" << ";
2018 subprecedence = Shift;
2022 operatorStr =
" >> ";
2023 subprecedence = Shift;
2027 operatorStr =
" >>> ";
2028 subprecedence = Shift;
2029 operandSign = IsSigned;
2032 operatorStr =
" / ";
2033 subprecedence = Multiply;
2034 operandSign = IsUnsigned;
2037 operatorStr =
" / ";
2038 subprecedence = Multiply;
2039 operandSign = IsSigned;
2042 operatorStr =
" % ";
2043 subprecedence = Multiply;
2044 operandSign = IsUnsigned;
2047 operatorStr =
" % ";
2048 subprecedence = Multiply;
2049 operandSign = IsSigned;
2052 openStr =
"$clog2(";
2054 operandSign = IsUnsigned;
2055 hasOpenClose =
true;
2058 case PEO::StrConcat:
2061 hasOpenClose =
true;
2064 subprecedence = LowestPrecedence;
2069 prec = subprecedence;
2072 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2074 assert(isUnary || hasOpenClose ||
2075 !llvm::hasSingleElement(expr.getOperands()));
2082 auto emitOperand = [&](Attribute operand) ->
bool {
2084 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2085 if (operandSign.has_value())
2086 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2089 if (operandSign.has_value()) {
2091 signedness = *operandSign;
2093 return signedness == IsSigned;
2097 if (prec > parenthesizeIfLooserThan)
2106 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2107 for (
auto op : expr.getOperands().drop_front()) {
2110 if (expr.getOpcode() == PEO::Add) {
2111 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2112 const APInt &value = integer.getValue();
2113 if (value.isNegative() && !value.isMinSignedValue()) {
2115 allOperandsSigned &=
2116 emitOperand(IntegerAttr::get(op.getType(), -value));
2123 allOperandsSigned &= emitOperand(op);
2127 if (prec > parenthesizeIfLooserThan) {
2131 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2146class ExprEmitter :
public EmitterBase,
2148 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2153 ExprEmitter(ModuleEmitter &emitter,
2154 SmallPtrSetImpl<Operation *> &emittedExprs)
2155 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2157 ExprEmitter(ModuleEmitter &emitter,
2158 SmallPtrSetImpl<Operation *> &emittedExprs,
2160 : EmitterBase(emitter.state), emitter(emitter),
2161 emittedExprs(emittedExprs), buffer(tokens),
2162 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2163 assert(state.pp.getListener() == &state.saver);
2170 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2171 bool isAssignmentLikeContext) {
2172 assert(localTokens.empty());
2174 ps.scopedBox(PP::ibox0, [&]() {
2177 emitSubExpr(exp, parenthesizeIfLooserThan,
2179 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2181 isAssignmentLikeContext);
2186 if (&buffer.tokens == &localTokens)
2187 buffer.flush(state.pp);
2192 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2193 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2195 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2203 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2204 SubExprSignRequirement signReq = NoRequirement,
2205 bool isSelfDeterminedUnsignedValue =
false,
2206 bool isAssignmentLikeContext =
false);
2210 void emitSVAttributes(Operation *op);
2212 SubExprInfo visitUnhandledExpr(Operation *op);
2213 SubExprInfo visitInvalidComb(Operation *op) {
2216 SubExprInfo visitUnhandledComb(Operation *op) {
2217 return visitUnhandledExpr(op);
2220 return dispatchSVVisitor(op);
2223 return visitUnhandledExpr(op);
2225 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2228 enum EmitBinaryFlags {
2229 EB_RequireSignedOperands = RequireSigned,
2230 EB_RequireUnsignedOperands = RequireUnsigned,
2231 EB_OperandSignRequirementMask = 0x3,
2236 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2240 EB_ForceResultSigned = 0x8,
2245 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2246 const char *syntax,
unsigned emitBinaryFlags = 0);
2248 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2249 bool resultAlwaysUnsigned =
false);
2252 void emitSubExprIBox2(
2253 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2254 ps.scopedBox(PP::ibox2,
2255 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2260 template <
typename Container,
typename EachFn>
2261 void interleaveComma(
const Container &c, EachFn eachFn) {
2262 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2267 void interleaveComma(ValueRange ops) {
2268 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2285 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2287 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2288 CloseFunc closeFn) {
2290 ps.scopedBox(PP::cbox0, [&]() {
2291 interleaveComma(c, eachFn);
2297 template <
typename OpenFunc,
typename CloseFunc>
2298 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2299 return emitBracedList(
2300 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2304 void emitBracedList(ValueRange ops) {
2305 return emitBracedList(
2306 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2310 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2313 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2314 bool printAsPattern, Operation *op);
2316 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2317 ArrayAttr fieldValues,
bool printAsPattern,
2320 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2322 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2323 SubExprInfo visitSV(GetModportOp op);
2324 SubExprInfo visitSV(SystemFunctionOp op);
2325 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2326 SubExprInfo visitSV(XMROp op);
2327 SubExprInfo visitSV(SFormatFOp op);
2328 SubExprInfo visitSV(XMRRefOp op);
2329 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2330 SubExprInfo visitSV(VerbatimExprOp op) {
2331 return visitVerbatimExprOp(op, op.getSymbols());
2333 SubExprInfo visitSV(VerbatimExprSEOp op) {
2334 return visitVerbatimExprOp(op, op.getSymbols());
2336 SubExprInfo visitSV(MacroRefExprOp op);
2337 SubExprInfo visitSV(MacroRefExprSEOp op);
2338 template <
typename MacroTy>
2339 SubExprInfo emitMacroCall(MacroTy op);
2341 SubExprInfo visitSV(ConstantXOp op);
2342 SubExprInfo visitSV(ConstantZOp op);
2343 SubExprInfo visitSV(ConstantStrOp op);
2345 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2346 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2348 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2353 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2354 emitSVAttributes(op);
2357 SubExprInfo visitSV(ArrayIndexInOutOp op);
2358 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2359 SubExprInfo visitSV(IndexedPartSelectOp op);
2360 SubExprInfo visitSV(StructFieldInOutOp op);
2363 SubExprInfo visitSV(SampledOp op);
2366 SubExprInfo visitSV(TimeOp op);
2367 SubExprInfo visitSV(STimeOp op);
2370 using TypeOpVisitor::visitTypeOp;
2372 SubExprInfo visitTypeOp(AggregateConstantOp op);
2374 SubExprInfo visitTypeOp(ParamValueOp op);
2381 SubExprInfo visitTypeOp(StructInjectOp op);
2382 SubExprInfo visitTypeOp(UnionCreateOp op);
2383 SubExprInfo visitTypeOp(UnionExtractOp op);
2384 SubExprInfo visitTypeOp(EnumCmpOp op);
2385 SubExprInfo visitTypeOp(EnumConstantOp op);
2388 using CombinationalVisitor::visitComb;
2389 SubExprInfo visitComb(
MuxOp op);
2390 SubExprInfo visitComb(ReverseOp op);
2391 SubExprInfo visitComb(
AddOp op) {
2392 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2393 return emitBinary(op, Addition,
"+");
2395 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2396 SubExprInfo visitComb(
MulOp op) {
2397 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2398 return emitBinary(op, Multiply,
"*");
2400 SubExprInfo visitComb(
DivUOp op) {
2401 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2403 SubExprInfo visitComb(
DivSOp op) {
2404 return emitBinary(op, Multiply,
"/",
2405 EB_RequireSignedOperands | EB_ForceResultSigned);
2407 SubExprInfo visitComb(
ModUOp op) {
2408 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2410 SubExprInfo visitComb(
ModSOp op) {
2411 return emitBinary(op, Multiply,
"%",
2412 EB_RequireSignedOperands | EB_ForceResultSigned);
2414 SubExprInfo visitComb(
ShlOp op) {
2415 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2417 SubExprInfo visitComb(
ShrUOp op) {
2419 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2421 SubExprInfo visitComb(
ShrSOp op) {
2424 return emitBinary(op, Shift,
">>>",
2425 EB_RequireSignedOperands | EB_ForceResultSigned |
2426 EB_RHS_UnsignedWithSelfDeterminedWidth);
2428 SubExprInfo visitComb(
AndOp op) {
2429 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2430 return emitBinary(op, And,
"&");
2432 SubExprInfo visitComb(
OrOp op) {
2433 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2434 return emitBinary(op, Or,
"|");
2436 SubExprInfo visitComb(
XorOp op) {
2437 if (op.isBinaryNot())
2438 return emitUnary(op,
"~");
2439 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2440 return emitBinary(op, Xor,
"^");
2445 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2447 SubExprInfo visitComb(ReplicateOp op);
2448 SubExprInfo visitComb(
ConcatOp op);
2450 SubExprInfo visitComb(ICmpOp op);
2452 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2453 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2454 "not printed within an assignment-like context");
2455 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2456 "supposed to spill such expressions";
2460 SubExprInfo printStructCreate(
2461 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2463 bool printAsPattern, Operation *op);
2466 ModuleEmitter &emitter;
2473 SubExprSignRequirement signPreference = NoRequirement;
2477 SmallPtrSetImpl<Operation *> &emittedExprs;
2480 SmallVector<Token> localTokens;
2494 bool isAssignmentLikeContext =
false;
2498SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2500 unsigned emitBinaryFlags) {
2502 emitError(op,
"SV attributes emission is unimplemented for the op");
2513 if (emitBinaryFlags & EB_ForceResultSigned)
2514 ps <<
"$signed(" << PP::ibox0;
2515 auto operandSignReq =
2516 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2517 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2519 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2521 ps << lhsSpace << syntax << PP::nbsp;
2528 auto rhsPrec = prec;
2529 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2530 rhsPrec = VerilogPrecedence(prec - 1);
2535 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2536 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2537 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2538 operandSignReq = NoRequirement;
2541 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2542 rhsIsUnsignedValueWithSelfDeterminedWidth);
2546 SubExprSignResult signedness = IsUnsigned;
2547 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2548 signedness = IsSigned;
2550 if (emitBinaryFlags & EB_ForceResultSigned) {
2551 ps << PP::end <<
")";
2552 signedness = IsSigned;
2556 return {prec, signedness};
2559SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2560 bool resultAlwaysUnsigned) {
2562 emitError(op,
"SV attributes emission is unimplemented for the op");
2565 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2569 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2570 resultAlwaysUnsigned ? IsUnsigned : signedness};
2575void ExprEmitter::emitSVAttributes(Operation *op) {
2594 if (constant && constant.getValue().isZero())
2595 return concat.getOperand(1);
2605SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2606 VerilogPrecedence parenthesizeIfLooserThan,
2607 SubExprSignRequirement signRequirement,
2608 bool isSelfDeterminedUnsignedValue,
2609 bool isAssignmentLikeContext) {
2611 if (
auto result = dyn_cast<OpResult>(exp))
2612 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2613 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2614 parenthesizeIfLooserThan, signRequirement,
2615 isSelfDeterminedUnsignedValue,
2616 isAssignmentLikeContext);
2620 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2625 auto *op = exp.getDefiningOp();
2629 if (!shouldEmitInlineExpr) {
2632 if (signRequirement == RequireSigned) {
2634 return {Symbol, IsSigned};
2638 return {Symbol, IsUnsigned};
2641 unsigned subExprStartIndex = buffer.tokens.size();
2643 ps.addCallback({op,
true});
2644 auto done = llvm::make_scope_exit([&]() {
2646 ps.addCallback({op, false});
2652 signPreference = signRequirement;
2654 bool bitCastAdded =
false;
2655 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2657 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2658 ps.addAsString(inType.getWidth());
2659 ps <<
"'(" << PP::ibox0;
2660 bitCastAdded =
true;
2664 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2665 isAssignmentLikeContext);
2666 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2672 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2674 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2676 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2677 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2680 expInfo.signedness = IsSigned;
2681 expInfo.precedence = Selection;
2682 }
else if (signRequirement == RequireUnsigned &&
2683 expInfo.signedness == IsSigned) {
2686 expInfo.signedness = IsUnsigned;
2687 expInfo.precedence = Selection;
2688 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2695 expInfo.precedence = Selection;
2702 emittedExprs.insert(exp.getDefiningOp());
2706SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2707 auto openFn = [&]() {
2709 ps.addAsString(op.getMultiple());
2712 auto closeFn = [&]() { ps <<
"}}"; };
2716 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2717 if (op.getOperand().hasOneUse()) {
2718 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2719 return {Symbol, IsUnsigned};
2722 emitBracedList(op.getOperand(), openFn, closeFn);
2723 return {Symbol, IsUnsigned};
2726SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2727 emitBracedList(op.getOperands());
2728 return {Symbol, IsUnsigned};
2731SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2735 Type toType = op.getType();
2738 ps.invokeWithStringOS(
2739 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2742 return emitSubExpr(op.getInput(), LowestPrecedence);
2745SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2746 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2747 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2748 SubExprSignRequirement signop[] = {
2750 NoRequirement, NoRequirement,
2752 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2754 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2756 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2758 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2759 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2762 if (op.isEqualAllOnes())
2763 return emitUnary(op,
"&",
true);
2766 if (op.isNotEqualZero())
2767 return emitUnary(op,
"|",
true);
2769 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2773 result.signedness = IsUnsigned;
2777SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2779 emitError(op,
"SV attributes emission is unimplemented for the op");
2781 unsigned loBit = op.getLowBit();
2782 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2784 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2785 assert((x.precedence == Symbol ||
2787 "should be handled by isExpressionUnableToInline");
2792 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2796 ps.addAsString(hiBit);
2797 if (hiBit != loBit) {
2799 ps.addAsString(loBit);
2802 return {Unary, IsUnsigned};
2805SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2807 emitError(op,
"SV attributes emission is unimplemented for the op");
2809 auto decl = op.getReferencedDecl(state.symbolCache);
2812 return {Selection, IsUnsigned};
2815SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2817 emitError(op,
"SV attributes emission is unimplemented for the op");
2820 ps.scopedBox(PP::ibox0, [&]() {
2822 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2823 [&]() { ps <<
"," << PP::space; });
2826 return {Symbol, IsUnsigned};
2829SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2831 emitError(op,
"SV attributes emission is unimplemented for the op");
2833 auto decl = op.getReferencedDecl(state.symbolCache);
2837 return {Selection, IsUnsigned};
2840SubExprInfo ExprEmitter::visitSV(XMROp op) {
2842 emitError(op,
"SV attributes emission is unimplemented for the op");
2844 if (op.getIsRooted())
2846 for (
auto s : op.getPath())
2847 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2849 return {Selection, IsUnsigned};
2854SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2856 emitError(op,
"SV attributes emission is unimplemented for the op");
2859 auto globalRef = op.getReferencedPath(&state.symbolCache);
2860 auto namepath = globalRef.getNamepathAttr().getValue();
2861 auto *
module = state.symbolCache.getDefinition(
2862 cast<InnerRefAttr>(namepath.front()).getModule());
2864 for (
auto sym : namepath) {
2866 auto innerRef = cast<InnerRefAttr>(sym);
2867 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2868 innerRef.getName());
2869 if (ref.hasPort()) {
2875 auto leaf = op.getVerbatimSuffixAttr();
2876 if (leaf && leaf.size())
2878 return {Selection, IsUnsigned};
2881SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2883 emitError(op,
"SV attributes emission is unimplemented for the op");
2885 emitTextWithSubstitutions(
2886 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2887 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2889 return {Unary, IsUnsigned};
2892template <
typename MacroTy>
2893SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2895 emitError(op,
"SV attributes emission is unimplemented for the op");
2898 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2899 assert(macroOp &&
"Invalid IR");
2901 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2903 if (!op.getInputs().empty()) {
2905 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2906 emitExpression(val, LowestPrecedence, false);
2910 return {LowestPrecedence, IsUnsigned};
2913SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2914 return emitMacroCall(op);
2917SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2918 return emitMacroCall(op);
2921SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2923 emitError(op,
"SV attributes emission is unimplemented for the op");
2925 ps.addAsString(op.getWidth());
2927 return {Unary, IsUnsigned};
2930SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2932 emitError(op,
"SV attributes emission is unimplemented for the op");
2934 ps.writeQuotedEscaped(op.getStr());
2935 return {Symbol, IsUnsigned};
2938SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2940 emitError(op,
"SV attributes emission is unimplemented for the op");
2942 ps.addAsString(op.getWidth());
2944 return {Unary, IsUnsigned};
2947SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2948 bool isNegated =
false;
2951 if (signPreference == RequireSigned && value.isNegative() &&
2952 !value.isMinSignedValue()) {
2957 ps.addAsString(type.getWidth());
2961 if (signPreference == RequireSigned)
2967 SmallString<32> valueStr;
2969 (-value).toStringUnsigned(valueStr, 16);
2971 value.toStringUnsigned(valueStr, 16);
2974 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2977SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2979 emitError(op,
"SV attributes emission is unimplemented for the op");
2981 auto value = op.getValue();
2985 if (value.getBitWidth() == 0) {
2986 emitOpError(op,
"will not emit zero width constants in the general case");
2987 ps <<
"<<unsupported zero width constant: "
2988 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2989 return {Unary, IsUnsigned};
2992 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2995void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2996 bool printAsPattern, Operation *op) {
2997 if (printAsPattern && !isAssignmentLikeContext)
2998 emitAssignmentPatternContextError(op);
2999 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3002 elementValues, [&]() { ps << openDelim; },
3003 [&](Attribute elementValue) {
3004 printConstantAggregate(elementValue,
elementType, op);
3006 [&]() { ps <<
"}"; });
3009void ExprEmitter::printConstantStruct(
3010 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3011 bool printAsPattern, Operation *op) {
3012 if (printAsPattern && !isAssignmentLikeContext)
3013 emitAssignmentPatternContextError(op);
3020 auto fieldRange = llvm::make_filter_range(
3021 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3026 if (printAsPattern) {
3028 fieldRange, [&]() { ps <<
"'{"; },
3029 [&](
const auto &fieldAndValue) {
3030 ps.scopedBox(PP::ibox2, [&]() {
3031 const auto &[field, value] = fieldAndValue;
3032 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3033 <<
":" << PP::space;
3034 printConstantAggregate(value, field.type, op);
3037 [&]() { ps <<
"}"; });
3040 fieldRange, [&]() { ps <<
"{"; },
3041 [&](
const auto &fieldAndValue) {
3042 ps.scopedBox(PP::ibox2, [&]() {
3043 const auto &[field, value] = fieldAndValue;
3044 printConstantAggregate(value, field.type, op);
3047 [&]() { ps <<
"}"; });
3051void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3054 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3055 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3056 isAssignmentLikeContext, op);
3059 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3060 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3064 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3065 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3066 isAssignmentLikeContext, op);
3068 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3069 auto value = cast<IntegerAttr>(attr).getValue();
3070 printConstantScalar(value, intType);
3074 emitOpError(op,
"contains constant of type ")
3075 << type <<
" which cannot be emitted as Verilog";
3078SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3080 emitError(op,
"SV attributes emission is unimplemented for the op");
3084 "zero-bit types not allowed at this point");
3086 printConstantAggregate(op.getFields(), op.getType(), op);
3087 return {Symbol, IsUnsigned};
3090SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3092 emitError(op,
"SV attributes emission is unimplemented for the op");
3094 return ps.invokeWithStringOS([&](
auto &os) {
3095 return emitter.printParamValue(op.getValue(), os, [&]() {
3096 return op->emitOpError(
"invalid parameter use");
3105 emitError(op,
"SV attributes emission is unimplemented for the op");
3107 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3109 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3111 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3113 ps.addAsString(dstWidth);
3115 return {Selection, arrayPrec.signedness};
3118SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3119 emitSubExpr(op.getInput(), Selection);
3124 emitSubExpr(op.getIndex(), LowestPrecedence);
3126 emitSVAttributes(op);
3127 return {Selection, IsUnsigned};
3133 emitError(op,
"SV attributes emission is unimplemented for the op");
3135 if (op.isUniform()) {
3137 ps.addAsString(op.getInputs().size());
3139 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3143 op.getInputs(), [&]() { ps <<
"{"; },
3146 emitSubExprIBox2(v);
3149 [&]() { ps <<
"}"; });
3151 return {Unary, IsUnsigned};
3154SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3156 emitError(op,
"SV attributes emission is unimplemented for the op");
3159 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3160 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3161 return {Unary, IsUnsigned};
3166 emitError(op,
"SV attributes emission is unimplemented for the op");
3168 emitBracedList(op.getOperands());
3169 return {Unary, IsUnsigned};
3172SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3174 emitError(op,
"SV attributes emission is unimplemented for the op");
3176 auto index = op.getIndex();
3177 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3182 emitSubExpr(index, LowestPrecedence);
3184 return {Selection, arrayPrec.signedness};
3187SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3189 emitError(op,
"SV attributes emission is unimplemented for the op");
3191 auto prec = emitSubExpr(op.getInput(), Selection);
3193 emitSubExpr(op.getBase(), LowestPrecedence);
3194 if (op.getDecrement())
3198 ps.addAsString(op.getWidth());
3200 return {Selection, prec.signedness};
3203SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3205 emitError(op,
"SV attributes emission is unimplemented for the op");
3207 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3209 emitSubExpr(op.getBase(), LowestPrecedence);
3210 if (op.getDecrement())
3214 ps.addAsString(op.getWidth());
3219SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3221 emitError(op,
"SV attributes emission is unimplemented for the op");
3223 auto prec = emitSubExpr(op.getInput(), Selection);
3225 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3226 return {Selection, prec.signedness};
3229SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3231 emitError(op,
"SV attributes emission is unimplemented for the op");
3234 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3239SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3241 emitError(op,
"SV attributes emission is unimplemented for the op");
3244 ps.scopedBox(PP::ibox0, [&]() {
3245 ps.writeQuotedEscaped(op.getFormatString());
3252 for (
auto operand : op.getSubstitutions()) {
3253 ps <<
"," << PP::space;
3254 emitSubExpr(operand, LowestPrecedence);
3258 return {Symbol, IsUnsigned};
3261SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3263 emitError(op,
"SV attributes emission is unimplemented for the op");
3266 return {Symbol, IsUnsigned};
3269SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3271 emitError(op,
"SV attributes emission is unimplemented for the op");
3274 return {Symbol, IsUnsigned};
3277SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3291 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3292 ps.scopedBox(PP::ibox0, [&]() {
3293 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3297 emitSVAttributes(op);
3299 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3300 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3304 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3305 return emitSubExpr(op.getFalseValue(), Conditional);
3308 SubExprSignResult signedness = IsUnsigned;
3309 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3310 signedness = IsSigned;
3312 return {Conditional, signedness};
3316SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3318 emitError(op,
"SV attributes emission is unimplemented for the op");
3321 emitSubExpr(op.getInput(), LowestPrecedence);
3324 return {Symbol, IsUnsigned};
3327SubExprInfo ExprEmitter::printStructCreate(
3328 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3330 bool printAsPattern, Operation *op) {
3331 if (printAsPattern && !isAssignmentLikeContext)
3332 emitAssignmentPatternContextError(op);
3335 auto filteredFields = llvm::make_filter_range(
3336 llvm::enumerate(fieldInfos),
3337 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3339 if (printAsPattern) {
3341 filteredFields, [&]() { ps <<
"'{"; },
3342 [&](
const auto &field) {
3343 ps.scopedBox(PP::ibox2, [&]() {
3345 emitter.getVerilogStructFieldName(field.value().name))
3346 <<
":" << PP::space;
3347 fieldFn(field.value(), field.index());
3350 [&]() { ps <<
"}"; });
3353 filteredFields, [&]() { ps <<
"{"; },
3354 [&](
const auto &field) {
3355 ps.scopedBox(PP::ibox2,
3356 [&]() { fieldFn(field.value(), field.index()); });
3358 [&]() { ps <<
"}"; });
3361 return {Selection, IsUnsigned};
3366 emitError(op,
"SV attributes emission is unimplemented for the op");
3370 bool printAsPattern = isAssignmentLikeContext;
3371 StructType structType = op.getType();
3372 return printStructCreate(
3373 structType.getElements(),
3374 [&](
const auto &field,
auto index) {
3375 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3377 isAssignmentLikeContext);
3379 printAsPattern, op);
3384 emitError(op,
"SV attributes emission is unimplemented for the op");
3386 emitSubExpr(op.getInput(), Selection);
3388 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3389 return {Selection, IsUnsigned};
3392SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3394 emitError(op,
"SV attributes emission is unimplemented for the op");
3398 bool printAsPattern = isAssignmentLikeContext;
3399 StructType structType = op.getType();
3400 return printStructCreate(
3401 structType.getElements(),
3402 [&](
const auto &field,
auto index) {
3403 if (field.name == op.getFieldNameAttr()) {
3404 emitSubExpr(op.getNewValue(), Selection);
3406 emitSubExpr(op.getInput(), Selection);
3408 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3411 printAsPattern, op);
3414SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3415 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3416 return {Selection, IsUnsigned};
3419SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3421 emitError(op,
"SV attributes emission is unimplemented for the op");
3422 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3425 result.signedness = IsUnsigned;
3429SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3431 emitError(op,
"SV attributes emission is unimplemented for the op");
3435 auto unionWidth = hw::getBitWidth(unionType);
3436 auto &element = unionType.getElements()[op.getFieldIndex()];
3437 auto elementWidth = hw::getBitWidth(element.type);
3440 if (!elementWidth) {
3441 ps.addAsString(unionWidth);
3443 return {Unary, IsUnsigned};
3447 if (elementWidth == unionWidth) {
3448 emitSubExpr(op.getInput(), LowestPrecedence);
3449 return {Unary, IsUnsigned};
3454 ps.scopedBox(PP::ibox0, [&]() {
3455 if (
auto prePadding = element.offset) {
3456 ps.addAsString(prePadding);
3457 ps <<
"'h0," << PP::space;
3459 emitSubExpr(op.getInput(), Selection);
3460 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3461 ps <<
"," << PP::space;
3462 ps.addAsString(postPadding);
3468 return {Unary, IsUnsigned};
3471SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3473 emitError(op,
"SV attributes emission is unimplemented for the op");
3474 emitSubExpr(op.getInput(), Selection);
3477 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3478 auto unionWidth = hw::getBitWidth(unionType);
3479 auto &element = unionType.getElements()[op.getFieldIndex()];
3480 auto elementWidth = hw::getBitWidth(element.type);
3481 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3482 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3491 return {Selection, IsUnsigned};
3494SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3495 emitOpError(op,
"cannot emit this expression to Verilog");
3496 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3498 return {Symbol, IsUnsigned};
3514enum class PropertyPrecedence {
3534struct EmittedProperty {
3536 PropertyPrecedence precedence;
3541class PropertyEmitter :
public EmitterBase,
3542 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3546 PropertyEmitter(ModuleEmitter &emitter,
3547 SmallPtrSetImpl<Operation *> &emittedOps)
3548 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3549 PropertyEmitter(ModuleEmitter &emitter,
3550 SmallPtrSetImpl<Operation *> &emittedOps,
3552 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3554 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3555 assert(state.pp.getListener() == &state.saver);
3558 void emitAssertPropertyDisable(
3559 Value property, Value disable,
3560 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3562 void emitAssertPropertyBody(
3563 Value property, Value disable,
3564 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3566 void emitAssertPropertyBody(
3567 Value property, sv::EventControl event, Value clock, Value disable,
3568 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3573 emitNestedProperty(Value property,
3574 PropertyPrecedence parenthesizeIfLooserThan);
3575 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3576 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3578 EmittedProperty visitUnhandledLTL(Operation *op);
3579 EmittedProperty visitLTL(ltl::AndOp op);
3580 EmittedProperty visitLTL(ltl::OrOp op);
3581 EmittedProperty visitLTL(ltl::IntersectOp op);
3582 EmittedProperty visitLTL(ltl::DelayOp op);
3583 EmittedProperty visitLTL(ltl::ConcatOp op);
3584 EmittedProperty visitLTL(ltl::RepeatOp op);
3585 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3586 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3587 EmittedProperty visitLTL(ltl::NotOp op);
3588 EmittedProperty visitLTL(ltl::ImplicationOp op);
3589 EmittedProperty visitLTL(ltl::UntilOp op);
3590 EmittedProperty visitLTL(ltl::EventuallyOp op);
3591 EmittedProperty visitLTL(ltl::ClockOp op);
3593 void emitLTLConcat(ValueRange inputs);
3596 ModuleEmitter &emitter;
3601 SmallPtrSetImpl<Operation *> &emittedOps;
3604 SmallVector<Token> localTokens;
3617void PropertyEmitter::emitAssertPropertyDisable(
3618 Value property, Value disable,
3619 PropertyPrecedence parenthesizeIfLooserThan) {
3622 ps <<
"disable iff" << PP::nbsp <<
"(";
3624 emitNestedProperty(disable, PropertyPrecedence::Unary);
3630 ps.scopedBox(PP::ibox0,
3631 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3637void PropertyEmitter::emitAssertPropertyBody(
3638 Value property, Value disable,
3639 PropertyPrecedence parenthesizeIfLooserThan) {
3640 assert(localTokens.empty());
3642 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3647 if (&buffer.tokens == &localTokens)
3648 buffer.flush(state.pp);
3651void PropertyEmitter::emitAssertPropertyBody(
3652 Value property, sv::EventControl event, Value clock, Value disable,
3653 PropertyPrecedence parenthesizeIfLooserThan) {
3654 assert(localTokens.empty());
3657 ps.scopedBox(PP::ibox2, [&] {
3658 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3659 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3665 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3670 if (&buffer.tokens == &localTokens)
3671 buffer.flush(state.pp);
3674EmittedProperty PropertyEmitter::emitNestedProperty(
3675 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3685 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3686 ExprEmitter(emitter, emittedOps, buffer.tokens)
3687 .emitExpression(property, LowestPrecedence,
3689 return {PropertyPrecedence::Symbol};
3692 unsigned startIndex = buffer.tokens.size();
3693 auto info = dispatchLTLVisitor(property.getDefiningOp());
3698 if (
info.precedence > parenthesizeIfLooserThan) {
3700 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3701 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3703 ps << PP::end <<
")";
3705 info.precedence = PropertyPrecedence::Symbol;
3709 emittedOps.insert(property.getDefiningOp());
3713EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3714 emitOpError(op,
"emission as Verilog property or sequence not supported");
3715 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3716 return {PropertyPrecedence::Symbol};
3719EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3722 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3723 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3724 return {PropertyPrecedence::And};
3727EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3730 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3731 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3732 return {PropertyPrecedence::Or};
3735EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3739 emitNestedProperty(input, PropertyPrecedence::Intersect);
3741 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3742 return {PropertyPrecedence::Intersect};
3745EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3747 if (
auto length = op.getLength()) {
3749 ps.addAsString(op.getDelay());
3752 ps.addAsString(op.getDelay());
3754 ps.addAsString(op.getDelay() + *length);
3758 if (op.getDelay() == 0) {
3760 }
else if (op.getDelay() == 1) {
3764 ps.addAsString(op.getDelay());
3769 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3770 return {PropertyPrecedence::Concat};
3773void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3774 bool addSeparator =
false;
3775 for (
auto input : inputs) {
3778 if (!input.getDefiningOp<ltl::DelayOp>())
3779 ps <<
"##0" << PP::space;
3781 addSeparator =
true;
3782 emitNestedProperty(input, PropertyPrecedence::Concat);
3786EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3787 emitLTLConcat(op.getInputs());
3788 return {PropertyPrecedence::Concat};
3791EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3792 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3793 if (
auto more = op.getMore()) {
3795 ps.addAsString(op.getBase());
3798 ps.addAsString(op.getBase() + *more);
3802 if (op.getBase() == 0) {
3804 }
else if (op.getBase() == 1) {
3808 ps.addAsString(op.getBase());
3812 return {PropertyPrecedence::Repeat};
3815EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3816 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3818 auto more = op.getMore();
3820 ps.addAsString(op.getBase());
3823 ps.addAsString(op.getBase() + more);
3827 return {PropertyPrecedence::Repeat};
3830EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3831 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3833 auto more = op.getMore();
3835 ps.addAsString(op.getBase());
3838 ps.addAsString(op.getBase() + more);
3842 return {PropertyPrecedence::Repeat};
3845EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3846 ps <<
"not" << PP::space;
3847 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3848 return {PropertyPrecedence::Unary};
3854 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3855 if (!concatOp || concatOp.getInputs().size() < 2)
3857 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3858 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3860 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3861 if (!constOp || !constOp.getValue().isOne())
3863 return concatOp.getInputs().drop_back();
3866EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3870 emitLTLConcat(range);
3871 ps << PP::space <<
"|=>" << PP::nbsp;
3873 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3874 ps << PP::space <<
"|->" << PP::nbsp;
3876 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3877 return {PropertyPrecedence::Implication};
3880EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3881 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3882 ps << PP::space <<
"until" << PP::space;
3883 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3884 return {PropertyPrecedence::Until};
3887EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3888 ps <<
"s_eventually" << PP::space;
3889 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3890 return {PropertyPrecedence::Qualifier};
3893EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3895 ps.scopedBox(PP::ibox2, [&] {
3896 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3897 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3901 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3902 return {PropertyPrecedence::Clocking};
3912class NameCollector {
3914 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3918 void collectNames(Block &block);
3920 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3921 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3924 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3925 ModuleEmitter &moduleEmitter;
3930 static constexpr size_t maxTypeWidthBound = 32;
3935void NameCollector::collectNames(Block &block) {
3938 for (
auto &op : block) {
3942 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3943 FuncCallProceduralOp, FuncCallOp>(op))
3945 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3949 for (
auto result : op.getResults()) {
3951 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3952 SmallString<16> typeString;
3956 llvm::raw_svector_ostream stringStream(typeString);
3958 stringStream, op.getLoc());
3960 if (typeString.size() <= maxTypeWidthBound)
3961 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3968 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3969 for (
auto ®ion : op.getRegions()) {
3970 if (!region.empty())
3971 collectNames(region.front());
3985class StmtEmitter :
public EmitterBase,
3993 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3995 void emitStatement(Operation *op);
3996 void emitStatementBlock(Block &body);
3999 LogicalResult emitDeclaration(Operation *op);
4002 void collectNamesAndCalculateDeclarationWidths(Block &block);
4005 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4006 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4007 bool isAssignmentLikeContext =
false);
4008 void emitSVAttributes(Operation *op);
4011 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4014 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4018 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4019 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4020 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4021 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4022 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4023 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4025 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4026 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4027 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4028 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4029 template <
typename Op>
4032 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4033 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4034 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4036 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4037 LogicalResult visitSV(
AssignOp op);
4038 LogicalResult visitSV(BPAssignOp op);
4039 LogicalResult visitSV(PAssignOp op);
4040 LogicalResult visitSV(ForceOp op);
4041 LogicalResult visitSV(ReleaseOp op);
4042 LogicalResult visitSV(AliasOp op);
4043 LogicalResult visitSV(InterfaceInstanceOp op);
4044 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4045 LogicalResult visitStmt(OutputOp op);
4047 LogicalResult visitStmt(InstanceOp op);
4048 LogicalResult visitStmt(InstanceChoiceOp op);
4049 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4050 ArrayRef<Value> instPortValues);
4055 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4056 LogicalResult visitSV(OrderedOutputOp op);
4057 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4058 LogicalResult visitSV(IfDefProceduralOp op) {
4059 return emitIfDef(op, op.getCond());
4061 LogicalResult visitSV(IfOp op);
4062 LogicalResult visitSV(AlwaysOp op);
4063 LogicalResult visitSV(AlwaysCombOp op);
4064 LogicalResult visitSV(AlwaysFFOp op);
4065 LogicalResult visitSV(InitialOp op);
4066 LogicalResult visitSV(CaseOp op);
4067 LogicalResult visitSV(FWriteOp op);
4068 LogicalResult visitSV(FFlushOp op);
4069 LogicalResult visitSV(VerbatimOp op);
4070 LogicalResult visitSV(MacroRefOp op);
4072 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4073 std::optional<unsigned> verbosity);
4074 LogicalResult visitSV(StopOp op);
4075 LogicalResult visitSV(FinishOp op);
4076 LogicalResult visitSV(ExitOp op);
4078 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4079 std::optional<unsigned> verbosity,
4081 ValueRange operands);
4082 LogicalResult visitSV(FatalOp op);
4083 LogicalResult visitSV(ErrorOp op);
4084 LogicalResult visitSV(WarningOp op);
4085 LogicalResult visitSV(InfoOp op);
4087 LogicalResult visitSV(ReadMemOp op);
4089 LogicalResult visitSV(GenerateOp op);
4090 LogicalResult visitSV(GenerateCaseOp op);
4092 LogicalResult visitSV(
ForOp op);
4094 void emitAssertionLabel(Operation *op);
4095 void emitAssertionMessage(StringAttr message, ValueRange args,
4096 SmallPtrSetImpl<Operation *> &ops,
4098 template <
typename Op>
4099 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4100 LogicalResult visitSV(AssertOp op);
4101 LogicalResult visitSV(AssumeOp op);
4102 LogicalResult visitSV(CoverOp op);
4103 template <
typename Op>
4104 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4105 LogicalResult visitSV(AssertConcurrentOp op);
4106 LogicalResult visitSV(AssumeConcurrentOp op);
4107 LogicalResult visitSV(CoverConcurrentOp op);
4108 template <
typename Op>
4109 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4110 LogicalResult visitSV(AssertPropertyOp op);
4111 LogicalResult visitSV(AssumePropertyOp op);
4112 LogicalResult visitSV(CoverPropertyOp op);
4114 LogicalResult visitSV(BindOp op);
4115 LogicalResult visitSV(InterfaceOp op);
4117 LogicalResult visitSV(InterfaceSignalOp op);
4118 LogicalResult visitSV(InterfaceModportOp op);
4119 LogicalResult visitSV(AssignInterfaceSignalOp op);
4120 LogicalResult visitSV(MacroErrorOp op);
4121 LogicalResult visitSV(MacroDefOp op);
4123 void emitBlockAsStatement(Block *block,
4124 const SmallPtrSetImpl<Operation *> &locationOps,
4125 StringRef multiLineComment = StringRef());
4127 LogicalResult visitSV(FuncDPIImportOp op);
4128 template <
typename CallOp>
4129 LogicalResult emitFunctionCall(CallOp callOp);
4130 LogicalResult visitSV(FuncCallProceduralOp op);
4131 LogicalResult visitSV(FuncCallOp op);
4132 LogicalResult visitSV(ReturnOp op);
4133 LogicalResult visitSV(IncludeOp op);
4136 ModuleEmitter &emitter;
4141 size_t maxDeclNameWidth = 0;
4142 size_t maxTypeWidth = 0;
4153void StmtEmitter::emitExpression(Value exp,
4154 SmallPtrSetImpl<Operation *> &emittedExprs,
4155 VerilogPrecedence parenthesizeIfLooserThan,
4156 bool isAssignmentLikeContext) {
4157 ExprEmitter(emitter, emittedExprs)
4158 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4163void StmtEmitter::emitSVAttributes(Operation *op) {
4171 setPendingNewline();
4174void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4175 llvm::function_ref<
void()> emitRHS,
4177 std::optional<PPExtString> wordBeforeLHS) {
4179 ps.scopedBox(PP::ibox2, [&]() {
4180 if (wordBeforeLHS) {
4181 ps << *wordBeforeLHS << PP::space;
4185 ps << PP::space << syntax << PP::space;
4187 ps.scopedBox(PP::ibox0, [&]() {
4194template <
typename Op>
4196StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4197 std::optional<PPExtString> wordBeforeLHS) {
4198 SmallPtrSet<Operation *, 8> ops;
4202 ps.addCallback({op,
true});
4203 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4205 emitExpression(op.getSrc(), ops, LowestPrecedence,
4210 ps.addCallback({op,
false});
4211 emitLocationInfoAndNewLine(ops);
4215LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4218 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4221 if (emitter.assignsInlined.count(op))
4225 emitSVAttributes(op);
4230LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4231 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4235 if (emitter.assignsInlined.count(op))
4239 emitSVAttributes(op);
4244LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4246 emitSVAttributes(op);
4251LogicalResult StmtEmitter::visitSV(ForceOp op) {
4253 emitError(op,
"SV attributes emission is unimplemented for the op");
4258LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4260 emitError(op,
"SV attributes emission is unimplemented for the op");
4263 SmallPtrSet<Operation *, 8> ops;
4265 ps.addCallback({op,
true});
4266 ps.scopedBox(PP::ibox2, [&]() {
4267 ps <<
"release" << PP::space;
4268 emitExpression(op.getDest(), ops);
4271 ps.addCallback({op,
false});
4272 emitLocationInfoAndNewLine(ops);
4276LogicalResult StmtEmitter::visitSV(AliasOp op) {
4278 emitError(op,
"SV attributes emission is unimplemented for the op");
4281 SmallPtrSet<Operation *, 8> ops;
4283 ps.addCallback({op,
true});
4284 ps.scopedBox(PP::ibox2, [&]() {
4285 ps <<
"alias" << PP::space;
4286 ps.scopedBox(PP::cbox0, [&]() {
4288 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4289 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4293 ps.addCallback({op,
false});
4294 emitLocationInfoAndNewLine(ops);
4298LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4299 auto doNotPrint = op.getDoNotPrint();
4300 if (doNotPrint && !state.options.emitBindComments)
4304 emitError(op,
"SV attributes emission is unimplemented for the op");
4307 StringRef prefix =
"";
4308 ps.addCallback({op,
true});
4311 ps <<
"// This interface is elsewhere emitted as a bind statement."
4315 SmallPtrSet<Operation *, 8> ops;
4318 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4319 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4320 "point to an interface");
4323 if (!prefix.empty())
4329 ps.addCallback({op,
false});
4330 emitLocationInfoAndNewLine(ops);
4338LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4340 SmallPtrSet<Operation *, 8> ops;
4341 size_t operandIndex = 0;
4342 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4343 for (
PortInfo port : ports.getOutputs()) {
4344 auto operand = op->getOperand(operandIndex);
4348 if (operand.hasOneUse() && operand.getDefiningOp() &&
4349 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4358 ps.addCallback({op,
true});
4360 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4362 ps <<
"// Zero width: ";
4365 ps <<
"assign" << PP::space;
4367 ps << PP::space <<
"=" << PP::space;
4368 ps.scopedBox(PP::ibox0, [&]() {
4372 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4373 ps <<
"/*Zero width*/";
4375 emitExpression(operand, ops, LowestPrecedence,
4380 ps.addCallback({op,
false});
4381 emitLocationInfoAndNewLine(ops);
4388LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4389 auto parent = op->getParentOfType<PortList>();
4391 return emitOutputLikeOp(op, ports);
4394LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4396 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4397 ps <<
"`ifndef " << typescopeDef << PP::newline;
4398 ps <<
"`define " << typescopeDef;
4399 setPendingNewline();
4400 emitStatementBlock(*op.getBodyBlock());
4402 ps <<
"`endif // " << typescopeDef;
4403 setPendingNewline();
4407LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4409 emitError(op,
"SV attributes emission is unimplemented for the op");
4414 ps << PP::neverbox <<
"// ";
4416 SmallPtrSet<Operation *, 8> ops;
4418 ps.scopedBox(PP::ibox2, [&]() {
4419 ps <<
"typedef" << PP::space;
4420 ps.invokeWithStringOS([&](
auto &os) {
4422 op.getAliasType(),
false);
4424 ps << PP::space <<
PPExtString(op.getPreferredName());
4425 ps.invokeWithStringOS(
4426 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4431 emitLocationInfoAndNewLine(ops);
4435template <
typename CallOpTy>
4436LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4440 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4442 SmallPtrSet<Operation *, 8> ops;
4446 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4447 if (explicitReturn) {
4448 assert(explicitReturn.hasOneUse());
4449 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4450 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4451 emitExpression(bpassignOp.getDest(), ops);
4453 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4454 ps <<
"assign" << PP::nbsp;
4455 emitExpression(assignOp.getDest(), ops);
4457 ps << PP::nbsp <<
"=" << PP::nbsp;
4460 auto arguments = callee.getPortList(
true);
4464 bool needsComma =
false;
4465 auto printArg = [&](Value value) {
4467 ps <<
"," << PP::space;
4468 emitExpression(value, ops);
4472 ps.scopedBox(PP::ibox0, [&] {
4473 unsigned inputIndex = 0, outputIndex = 0;
4474 for (
auto arg : arguments) {
4477 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4479 printArg(op.getInputs()[inputIndex++]);
4484 emitLocationInfoAndNewLine(ops);
4488LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4489 return emitFunctionCall(op);
4492LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4493 return emitFunctionCall(op);
4496template <
typename PPS>
4498 bool isAutomatic =
false,
4499 bool emitAsTwoStateType =
false) {
4500 ps <<
"function" << PP::nbsp;
4502 ps <<
"automatic" << PP::nbsp;
4503 auto retType = op.getExplicitlyReturnedType();
4505 ps.invokeWithStringOS([&](
auto &os) {
4506 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4507 emitAsTwoStateType);
4513 emitter.emitPortList(
4517LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4518 auto parent = op->getParentOfType<sv::FuncOp>();
4520 return emitOutputLikeOp(op, ports);
4523LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4525 ps <<
"`include" << PP::nbsp;
4527 if (op.getStyle() == IncludeStyle::System)
4528 ps <<
"<" << op.getTarget() <<
">";
4530 ps <<
"\"" << op.getTarget() <<
"\"";
4532 emitLocationInfo(op.getLoc());
4533 setPendingNewline();
4537LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4540 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4544 if (
auto linkageName = importOp.getLinkageName())
4545 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4547 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4548 assert(op.isDeclaration() &&
"function must be a declaration");
4551 assert(state.pendingNewline);
4557LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4559 emitError(op,
"SV attributes emission is unimplemented for the op");
4562 SmallPtrSet<Operation *, 8> ops;
4565 ps.addCallback({op,
true});
4567 if (
auto fd = op.getFd())
4568 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4571 ps.addCallback({op,
false});
4572 emitLocationInfoAndNewLine(ops);
4576LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4578 emitError(op,
"SV attributes emission is unimplemented for the op");
4581 SmallPtrSet<Operation *, 8> ops;
4584 ps.addCallback({op,
true});
4586 ps.scopedBox(PP::ibox0, [&]() {
4587 emitExpression(op.getFd(), ops);
4589 ps <<
"," << PP::space;
4590 ps.writeQuotedEscaped(op.getFormatString());
4598 for (
auto operand : op.getSubstitutions()) {
4599 ps <<
"," << PP::space;
4600 emitExpression(operand, ops);
4604 ps.addCallback({op,
false});
4605 emitLocationInfoAndNewLine(ops);
4609LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4611 emitError(op,
"SV attributes emission is unimplemented for the op");
4614 SmallPtrSet<Operation *, 8> ops;
4619 StringRef
string = op.getFormatString();
4620 if (
string.ends_with(
"\n"))
4621 string =
string.drop_back();
4626 bool isFirst =
true;
4629 while (!
string.
empty()) {
4630 auto lhsRhs =
string.split(
'\n');
4634 ps << PP::end << PP::newline << PP::neverbox;
4638 emitTextWithSubstitutions(
4639 ps, lhsRhs.first, op,
4640 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4641 string = lhsRhs.second;
4646 emitLocationInfoAndNewLine(ops);
4651LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4653 emitError(op,
"SV attributes emission is unimplemented for the op");
4657 SmallPtrSet<Operation *, 8> ops;
4662 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4663 assert(macroOp &&
"Invalid IR");
4665 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4667 if (!op.getInputs().empty()) {
4669 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4670 emitExpression(val, ops, LowestPrecedence,
4676 emitLocationInfoAndNewLine(ops);
4682StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4683 std::optional<unsigned> verbosity) {
4685 emitError(op,
"SV attributes emission is unimplemented for the op");
4688 SmallPtrSet<Operation *, 8> ops;
4690 ps.addCallback({op,
true});
4692 if (verbosity && *verbosity != 1) {
4694 ps.addAsString(*verbosity);
4698 ps.addCallback({op,
false});
4699 emitLocationInfoAndNewLine(ops);
4703LogicalResult StmtEmitter::visitSV(StopOp op) {
4704 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4707LogicalResult StmtEmitter::visitSV(FinishOp op) {
4708 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4712LogicalResult StmtEmitter::visitSV(ExitOp op) {
4713 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4719StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4720 std::optional<unsigned> verbosity,
4721 StringAttr message, ValueRange operands) {
4723 emitError(op,
"SV attributes emission is unimplemented for the op");
4726 SmallPtrSet<Operation *, 8> ops;
4728 ps.addCallback({op,
true});
4734 if ((verbosity && *verbosity != 1) || message) {
4736 ps.scopedBox(PP::ibox0, [&]() {
4740 ps.addAsString(*verbosity);
4745 ps <<
"," << PP::space;
4746 ps.writeQuotedEscaped(message.getValue());
4748 for (
auto operand : operands) {
4749 ps <<
"," << PP::space;
4750 emitExpression(operand, ops);
4759 ps.addCallback({op,
false});
4760 emitLocationInfoAndNewLine(ops);
4764LogicalResult StmtEmitter::visitSV(FatalOp op) {
4765 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4766 op.getMessageAttr(), op.getSubstitutions());
4769LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4770 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4771 op.getMessageAttr(), op.getSubstitutions());
4774LogicalResult StmtEmitter::visitSV(WarningOp op) {
4775 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4776 op.getMessageAttr(), op.getSubstitutions());
4779LogicalResult StmtEmitter::visitSV(InfoOp op) {
4780 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4781 op.getMessageAttr(), op.getSubstitutions());
4784LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4785 SmallPtrSet<Operation *, 8> ops({op});
4788 ps.addCallback({op,
true});
4790 switch (op.getBaseAttr().getValue()) {
4791 case MemBaseTypeAttr::MemBaseBin:
4794 case MemBaseTypeAttr::MemBaseHex:
4799 ps.scopedBox(PP::ibox0, [&]() {
4800 ps.writeQuotedEscaped(op.getFilename());
4801 ps <<
"," << PP::space;
4802 emitExpression(op.getDest(), ops);
4806 ps.addCallback({op,
false});
4807 emitLocationInfoAndNewLine(ops);
4811LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4812 emitSVAttributes(op);
4815 ps.addCallback({op,
true});
4816 ps <<
"generate" << PP::newline;
4818 setPendingNewline();
4819 emitStatementBlock(op.getBody().getBlocks().front());
4822 ps <<
"endgenerate";
4823 ps.addCallback({op,
false});
4824 setPendingNewline();
4828LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4829 emitSVAttributes(op);
4832 ps.addCallback({op,
true});
4834 ps.invokeWithStringOS([&](
auto &os) {
4835 emitter.printParamValue(
4836 op.getCond(), os, VerilogPrecedence::Selection,
4837 [&]() { return op->emitOpError(
"invalid case parameter"); });
4840 setPendingNewline();
4843 ArrayAttr
patterns = op.getCasePatterns();
4844 ArrayAttr caseNames = op.getCaseNames();
4845 MutableArrayRef<Region> regions = op.getCaseRegions();
4852 llvm::StringMap<size_t> nextGenIds;
4853 ps.scopedBox(PP::bbox2, [&]() {
4855 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4856 auto ®ion = regions[i];
4857 assert(region.hasOneBlock());
4858 Attribute patternAttr =
patterns[i];
4861 if (!isa<mlir::TypedAttr>(patternAttr))
4864 ps.invokeWithStringOS([&](
auto &os) {
4865 emitter.printParamValue(
4866 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4867 [&]() {
return op->emitOpError(
"invalid case value"); });
4870 StringRef legalName =
4871 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4874 setPendingNewline();
4875 emitStatementBlock(region.getBlocks().front());
4878 setPendingNewline();
4884 ps.addCallback({op,
false});
4885 setPendingNewline();
4889LogicalResult StmtEmitter::visitSV(
ForOp op) {
4890 emitSVAttributes(op);
4891 llvm::SmallPtrSet<Operation *, 8> ops;
4892 ps.addCallback({op,
true});
4894 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4897 ps.scopedBox(PP::cbox0, [&]() {
4901 ps <<
"logic" << PP::nbsp;
4902 ps.invokeWithStringOS([&](
auto &os) {
4903 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4908 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4913 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4914 [&]() { emitExpression(op.getUpperBound(), ops); },
4920 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4921 [&]() { emitExpression(op.getStep(), ops); },
4925 ps << PP::neverbreak;
4926 setPendingNewline();
4927 emitStatementBlock(op.getBody().getBlocks().front());
4930 ps.addCallback({op,
false});
4931 emitLocationInfoAndNewLine(ops);
4936void StmtEmitter::emitAssertionLabel(Operation *op) {
4937 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4943void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4944 SmallPtrSetImpl<Operation *> &ops,
4945 bool isConcurrent =
false) {
4948 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4949 ps.scopedBox(PP::ibox0, [&]() {
4950 ps.writeQuotedEscaped(message.getValue());
4952 for (
auto arg : args) {
4953 ps <<
"," << PP::space;
4954 emitExpression(arg, ops);
4960template <
typename Op>
4961LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4963 emitError(op,
"SV attributes emission is unimplemented for the op");
4966 SmallPtrSet<Operation *, 8> ops;
4968 ps.addCallback({op,
true});
4969 ps.scopedBox(PP::ibox2, [&]() {
4970 emitAssertionLabel(op);
4971 ps.scopedBox(PP::cbox0, [&]() {
4973 switch (op.getDefer()) {
4974 case DeferAssert::Immediate:
4976 case DeferAssert::Observed:
4979 case DeferAssert::Final:
4984 ps.scopedBox(PP::ibox0, [&]() {
4985 emitExpression(op.getExpression(), ops);
4988 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4992 ps.addCallback({op,
false});
4993 emitLocationInfoAndNewLine(ops);
4997LogicalResult StmtEmitter::visitSV(AssertOp op) {
4998 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5001LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5002 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5005LogicalResult StmtEmitter::visitSV(CoverOp op) {
5006 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5009template <
typename Op>
5010LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5012 emitError(op,
"SV attributes emission is unimplemented for the op");
5015 SmallPtrSet<Operation *, 8> ops;
5017 ps.addCallback({op,
true});
5018 ps.scopedBox(PP::ibox2, [&]() {
5019 emitAssertionLabel(op);
5020 ps.scopedBox(PP::cbox0, [&]() {
5021 ps << opName << PP::nbsp <<
"property (";
5022 ps.scopedBox(PP::ibox0, [&]() {
5023 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5025 emitExpression(op.getClock(), ops);
5026 ps <<
")" << PP::space;
5027 emitExpression(op.getProperty(), ops);
5030 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5035 ps.addCallback({op,
false});
5036 emitLocationInfoAndNewLine(ops);
5040LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5041 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5044LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5045 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5048LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5049 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5054template <
typename Op>
5055LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5057 emitError(op,
"SV attributes emission is unimplemented for the op");
5067 Operation *parent = op->getParentOp();
5068 Value
property = op.getProperty();
5069 bool isTemporal = !
property.getType().isSignlessInteger(1);
5070 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5071 bool emitAsImmediate = !isTemporal && isProcedural;
5074 SmallPtrSet<Operation *, 8> ops;
5076 ps.addCallback({op,
true});
5077 ps.scopedBox(PP::ibox2, [&]() {
5079 emitAssertionLabel(op);
5081 ps.scopedBox(PP::cbox0, [&]() {
5082 if (emitAsImmediate)
5083 ps << opName <<
"(";
5085 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5087 Value clock = op.getClock();
5088 auto event = op.getEvent();
5090 ps.scopedBox(PP::ibox2, [&]() {
5091 PropertyEmitter(emitter, ops)
5092 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5095 ps.scopedBox(PP::ibox2, [&]() {
5096 PropertyEmitter(emitter, ops)
5097 .emitAssertPropertyBody(property, op.getDisable());
5102 ps.addCallback({op,
false});
5103 emitLocationInfoAndNewLine(ops);
5107LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5108 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5111LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5112 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5115LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5116 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5119LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5121 emitError(op,
"SV attributes emission is unimplemented for the op");
5124 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5125 .getMacroIdentifier());
5128 bool hasEmptyThen = op->getRegion(0).front().empty();
5130 ps <<
"`ifndef " << ident;
5132 ps <<
"`ifdef " << ident;
5134 SmallPtrSet<Operation *, 8> ops;
5136 emitLocationInfoAndNewLine(ops);
5139 emitStatementBlock(op->getRegion(0).front());
5141 if (!op->getRegion(1).empty()) {
5142 if (!hasEmptyThen) {
5144 ps <<
"`else // " << ident;
5145 setPendingNewline();
5147 emitStatementBlock(op->getRegion(1).front());
5154 setPendingNewline();
5162void StmtEmitter::emitBlockAsStatement(
5163 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5164 StringRef multiLineComment) {
5171 emitLocationInfoAndNewLine(locationOps);
5174 emitStatementBlock(*block);
5176 if (needsBeginEnd) {
5180 if (!multiLineComment.empty())
5181 ps <<
" // " << multiLineComment;
5182 setPendingNewline();
5186LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5188 for (
auto &op : ooop.getBody().front())
5193LogicalResult StmtEmitter::visitSV(IfOp op) {
5194 SmallPtrSet<Operation *, 8> ops;
5196 auto ifcondBox = PP::ibox2;
5198 emitSVAttributes(op);
5200 ps.addCallback({op,
true});
5201 ps <<
"if (" << ifcondBox;
5211 emitExpression(ifOp.getCond(), ops);
5212 ps << PP::end <<
")";
5213 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5215 if (!ifOp.hasElse())
5219 Block *elseBlock = ifOp.getElseBlock();
5221 if (!nestedElseIfOp) {
5226 emitBlockAsStatement(elseBlock, ops);
5232 ifOp = nestedElseIfOp;
5233 ps <<
"else if (" << ifcondBox;
5235 ps.addCallback({op,
false});
5240LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5241 emitSVAttributes(op);
5242 SmallPtrSet<Operation *, 8> ops;
5246 auto printEvent = [&](AlwaysOp::Condition cond) {
5247 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5248 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5250 ps.addCallback({op,
true});
5252 switch (op.getNumConditions()) {
5258 printEvent(op.getCondition(0));
5263 ps.scopedBox(PP::cbox0, [&]() {
5264 printEvent(op.getCondition(0));
5265 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5266 ps << PP::space <<
"or" << PP::space;
5267 printEvent(op.getCondition(i));
5276 std::string comment;
5277 if (op.getNumConditions() == 0) {
5278 comment =
"always @*";
5280 comment =
"always @(";
5283 [&](Attribute eventAttr) {
5284 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5285 comment += stringifyEventControl(event);
5287 [&]() { comment +=
", "; });
5291 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5292 ps.addCallback({op,
false});
5296LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5297 emitSVAttributes(op);
5298 SmallPtrSet<Operation *, 8> ops;
5302 ps.addCallback({op,
true});
5303 StringRef opString =
"always_comb";
5304 if (state.options.noAlwaysComb)
5305 opString =
"always @(*)";
5308 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5309 ps.addCallback({op,
false});
5313LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5314 emitSVAttributes(op);
5316 SmallPtrSet<Operation *, 8> ops;
5320 ps.addCallback({op,
true});
5321 ps <<
"always_ff @(";
5322 ps.scopedBox(PP::cbox0, [&]() {
5323 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5324 emitExpression(op.getClock(), ops);
5325 if (op.getResetStyle() == ResetType::AsyncReset) {
5326 ps << PP::nbsp <<
"or" << PP::space
5327 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5328 emitExpression(op.getReset(), ops);
5335 std::string comment;
5336 comment +=
"always_ff @(";
5337 comment += stringifyEventControl(op.getClockEdge());
5338 if (op.getResetStyle() == ResetType::AsyncReset) {
5340 comment += stringifyEventControl(*op.getResetEdge());
5344 if (op.getResetStyle() == ResetType::NoReset)
5345 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5348 emitLocationInfoAndNewLine(ops);
5349 ps.scopedBox(PP::bbox2, [&]() {
5355 if (op.getResetStyle() == ResetType::AsyncReset &&
5356 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5358 emitExpression(op.getReset(), ops);
5360 emitBlockAsStatement(op.getResetBlock(), ops);
5363 emitBlockAsStatement(op.getBodyBlock(), ops);
5368 ps <<
" // " << comment;
5369 setPendingNewline();
5371 ps.addCallback({op,
false});
5375LogicalResult StmtEmitter::visitSV(InitialOp op) {
5376 emitSVAttributes(op);
5377 SmallPtrSet<Operation *, 8> ops;
5380 ps.addCallback({op,
true});
5382 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5383 ps.addCallback({op,
false});
5387LogicalResult StmtEmitter::visitSV(CaseOp op) {
5388 emitSVAttributes(op);
5389 SmallPtrSet<Operation *, 8> ops, emptyOps;
5392 ps.addCallback({op,
true});
5393 if (op.getValidationQualifier() !=
5394 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5395 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5396 op.getValidationQualifier()))
5398 const char *opname =
nullptr;
5399 switch (op.getCaseStyle()) {
5400 case CaseStmtType::CaseStmt:
5403 case CaseStmtType::CaseXStmt:
5406 case CaseStmtType::CaseZStmt:
5410 ps << opname <<
" (";
5411 ps.scopedBox(PP::ibox0, [&]() {
5412 emitExpression(op.getCond(), ops);
5415 emitLocationInfoAndNewLine(ops);
5417 size_t caseValueIndex = 0;
5418 ps.scopedBox(PP::bbox2, [&]() {
5419 for (
auto &caseInfo : op.getCases()) {
5421 auto &
pattern = caseInfo.pattern;
5423 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5424 .Case<CaseBitPattern>([&](
auto bitPattern) {
5427 ps.invokeWithStringOS([&](
auto &os) {
5428 os << bitPattern->getWidth() <<
"'b";
5429 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5430 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5433 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5434 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5435 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5437 .Case<CaseExprPattern>([&](
auto) {
5438 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5440 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5441 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5444 emitBlockAsStatement(caseInfo.block, emptyOps);
5450 ps.addCallback({op,
false});
5451 emitLocationInfoAndNewLine(ops);
5455LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5456 bool doNotPrint = op.getDoNotPrint();
5457 if (doNotPrint && !state.options.emitBindComments)
5462 emitSVAttributes(op);
5464 ps.addCallback({op,
true});
5467 <<
"/* This instance is elsewhere emitted as a bind statement."
5470 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5471 "attributes. The attributes will not be emitted.";
5474 SmallPtrSet<Operation *, 8> ops;
5479 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5480 assert(moduleOp &&
"Invalid IR");
5484 if (!op.getParameters().empty()) {
5487 bool printed =
false;
5489 llvm::zip(op.getParameters(),
5490 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5491 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5492 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5494 if (param.getValue() == modParam.getValue())
5499 ps <<
" #(" << PP::bbox2 << PP::newline;
5502 ps <<
"," << PP::newline;
5506 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5508 ps.invokeWithStringOS([&](
auto &os) {
5509 emitter.printParamValue(param.getValue(), os, [&]() {
5510 return op->emitOpError(
"invalid instance parameter '")
5511 << param.getName().getValue() <<
"' value";
5517 ps << PP::end << PP::newline <<
")";
5524 SmallVector<Value> instPortValues(modPortInfo.size());
5525 op.getValues(instPortValues, modPortInfo);
5526 emitInstancePortList(op, modPortInfo, instPortValues);
5528 ps.addCallback({op,
false});
5529 emitLocationInfoAndNewLine(ops);
5534 setPendingNewline();
5539LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5541 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5542 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5547 Operation *defaultModuleOp =
5548 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5550 SmallVector<Value> instPortValues(modPortInfo.size());
5551 op.getValues(instPortValues, modPortInfo);
5552 emitInstancePortList(op, modPortInfo, instPortValues);
5554 SmallPtrSet<Operation *, 8> ops;
5556 ps.addCallback({op,
false});
5557 emitLocationInfoAndNewLine(ops);
5562void StmtEmitter::emitInstancePortList(Operation *op,
5564 ArrayRef<Value> instPortValues) {
5565 SmallPtrSet<Operation *, 8> ops;
5568 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5569 ModulePortInfo containingPortList(containingModule.getPortList());
5574 size_t maxNameLength = 0;
5575 for (
auto &elt : modPortInfo) {
5576 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5579 auto getWireForValue = [&](Value result) {
5580 return result.getUsers().begin()->getOperand(0);
5584 bool isFirst =
true;
5585 bool isZeroWidth =
false;
5587 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5589 auto &modPort = modPortInfo.
at(portNum);
5591 Value portVal = instPortValues[portNum];
5596 bool shouldPrintComma =
true;
5598 shouldPrintComma =
false;
5599 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5601 shouldPrintComma =
true;
5606 if (shouldPrintComma)
5609 emitLocationInfoAndNewLine(ops);
5624 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5625 auto modPortName = modPort.getVerilogName();
5627 ps.spaces(maxNameLength - modPortName.size() + 1);
5629 ps.scopedBox(PP::ibox0, [&]() {
5636 if (!modPort.isOutput()) {
5638 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5639 ps <<
"/* Zero width */";
5641 emitExpression(portVal, ops, LowestPrecedence);
5642 }
else if (portVal.use_empty()) {
5643 ps <<
"/* unused */";
5644 }
else if (portVal.hasOneUse() &&
5645 (output = dyn_cast_or_null<OutputOp>(
5646 portVal.getUses().begin()->getOwner()))) {
5651 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5653 containingPortList.atOutput(outputPortNo).getVerilogName());
5655 portVal = getWireForValue(portVal);
5656 emitExpression(portVal, ops);
5662 if (!isFirst || isZeroWidth) {
5663 emitLocationInfoAndNewLine(ops);
5676LogicalResult StmtEmitter::visitSV(BindOp op) {
5677 emitter.emitBind(op);
5678 assert(state.pendingNewline);
5682LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5683 emitComment(op.getCommentAttr());
5685 emitSVAttributes(op);
5688 ps.addCallback({op,
true});
5690 setPendingNewline();
5692 emitStatementBlock(*op.getBodyBlock());
5694 ps <<
"endinterface" << PP::newline;
5695 ps.addCallback({op,
false});
5696 setPendingNewline();
5701 emitSVAttributes(op);
5703 ps.addCallback({op,
true});
5705 ps << op.getContent();
5707 ps.addCallback({op,
false});
5708 setPendingNewline();
5712LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5714 emitSVAttributes(op);
5716 ps.addCallback({op,
true});
5718 ps << PP::neverbox <<
"// ";
5719 ps.invokeWithStringOS([&](
auto &os) {
5724 ps.invokeWithStringOS(
5725 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5729 ps.addCallback({op,
false});
5730 setPendingNewline();
5734LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5736 ps.addCallback({op,
true});
5740 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5741 auto port = cast<ModportStructAttr>(portAttr);
5742 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5743 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5744 ps << PPExtString(getSymOpName(signalDecl));
5748 ps.addCallback({op,
false});
5749 setPendingNewline();
5753LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5755 ps.addCallback({op,
true});
5756 SmallPtrSet<Operation *, 8> emitted;
5759 emitExpression(op.getIface(), emitted);
5760 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5761 emitExpression(op.getRhs(), emitted);
5763 ps.addCallback({op,
false});
5764 setPendingNewline();
5768LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5770 ps <<
"`" << op.getMacroIdentifier();
5771 setPendingNewline();
5775LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5776 auto decl = op.getReferencedMacro(&state.symbolCache);
5779 ps.addCallback({op,
true});
5781 if (decl.getArgs()) {
5783 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5784 ps << cast<StringAttr>(name);
5788 if (!op.getFormatString().empty()) {
5790 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5793 ps.addCallback({op,
false});
5794 setPendingNewline();
5798void StmtEmitter::emitStatement(Operation *op) {
5805 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5809 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5810 succeeded(dispatchVerifVisitor(op)))
5813 emitOpError(op,
"emission to Verilog not supported");
5814 emitPendingNewlineIfNeeded();
5815 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5816 setPendingNewline();
5827 StmtEmitter &stmtEmitter) {
5834 if (isa<IfDefProceduralOp>(op->getParentOp()))
5842 SmallVector<Value, 8> exprsToScan(op->getOperands());
5847 while (!exprsToScan.empty()) {
5848 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5855 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5856 auto *defOp = readInout.getOperand().getDefiningOp();
5863 if (isa<sv::WireOp>(defOp))
5868 if (!isa<RegOp, LogicOp>(defOp))
5874 if (isa<LogicOp>(defOp) &&
5875 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5879 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5880 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5888 exprsToScan.append(expr->getOperands().begin(),
5889 expr->getOperands().end());
5895 if (expr->getBlock() != op->getBlock())
5900 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5907template <
class AssignTy>
5909 AssignTy singleAssign;
5910 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5911 if (hasSVAttributes(user))
5914 if (auto assign = dyn_cast<AssignTy>(user)) {
5917 singleAssign = assign;
5921 return isa<ReadInOutOp>(user);
5923 return singleAssign;
5929 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5933 if (op1->getBlock() != user->getBlock())
5939 return op1->isBeforeInBlock(user);
5943LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5944 emitSVAttributes(op);
5945 auto value = op->getResult(0);
5946 SmallPtrSet<Operation *, 8> opsForLocation;
5947 opsForLocation.insert(op);
5949 ps.addCallback({op,
true});
5952 auto type = value.getType();
5958 bool singleBitDefaultType = !isa<LocalParamOp>(op);
5960 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5961 unsigned targetColumn = 0;
5962 unsigned column = 0;
5965 if (maxDeclNameWidth > 0)
5966 targetColumn += maxDeclNameWidth + 1;
5969 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5970 }
else if (!word.empty()) {
5972 column += word.size();
5973 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5974 ps.spaces(numSpaces);
5975 column += numSpaces;
5978 SmallString<8> typeString;
5981 llvm::raw_svector_ostream stringStream(typeString);
5984 true, singleBitDefaultType);
5987 if (maxTypeWidth > 0)
5988 targetColumn += maxTypeWidth + 1;
5989 unsigned numSpaces = 0;
5990 if (!typeString.empty()) {
5992 column += typeString.size();
5995 if (targetColumn > column)
5996 numSpaces = targetColumn - column;
5997 ps.spaces(numSpaces);
5998 column += numSpaces;
6004 ps.invokeWithStringOS(
6005 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6008 if (state.options.printDebugInfo) {
6009 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6010 auto innerSym = innerSymOp.getInnerSymAttr();
6011 if (innerSym && !innerSym.empty()) {
6013 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6019 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6020 ps << PP::space <<
"=" << PP::space;
6021 ps.invokeWithStringOS([&](
auto &os) {
6022 emitter.printParamValue(localparam.getValue(), os, [&]() {
6023 return op->emitOpError(
"invalid localparam value");
6028 if (
auto regOp = dyn_cast<RegOp>(op)) {
6029 if (
auto initValue = regOp.getInit()) {
6030 ps << PP::space <<
"=" << PP::space;
6031 ps.scopedBox(PP::ibox0, [&]() {
6032 emitExpression(initValue, opsForLocation, LowestPrecedence,
6041 if (isa<sv::WireOp>(op) &&
6042 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6045 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6046 auto *source = singleAssign.getSrc().getDefiningOp();
6050 if (!source || isa<ConstantOp>(source) ||
6051 op->getNextNode() == singleAssign) {
6052 ps << PP::space <<
"=" << PP::space;
6053 ps.scopedBox(PP::ibox0, [&]() {
6054 emitExpression(singleAssign.getSrc(), opsForLocation,
6058 emitter.assignsInlined.insert(singleAssign);
6066 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
6069 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6072 auto *source = singleAssign.getSrc().getDefiningOp();
6076 if (!source || isa<ConstantOp>(source) ||
6079 ps << PP::space <<
"=" << PP::space;
6080 ps.scopedBox(PP::ibox0, [&]() {
6081 emitExpression(singleAssign.getSrc(), opsForLocation,
6086 emitter.assignsInlined.insert(singleAssign);
6087 emitter.expressionsEmittedIntoDecl.insert(op);
6094 ps.addCallback({op,
false});
6095 emitLocationInfoAndNewLine(opsForLocation);
6099void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6102 NameCollector collector(emitter);
6103 collector.collectNames(block);
6106 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6107 maxTypeWidth = collector.getMaxTypeWidth();
6110void StmtEmitter::emitStatementBlock(Block &body) {
6111 ps.scopedBox(PP::bbox2, [&]() {
6116 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6117 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6122 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6123 collectNamesAndCalculateDeclarationWidths(body);
6126 for (
auto &op : body) {
6133void ModuleEmitter::emitStatement(Operation *op) {
6134 StmtEmitter(*
this, state.options).emitStatement(op);
6139void ModuleEmitter::emitSVAttributes(Operation *op) {
6147 setPendingNewline();
6154void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6155 auto verilogName =
module.getVerilogModuleNameAttr();
6157 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6159 setPendingNewline();
6168void ModuleEmitter::emitBind(BindOp op) {
6170 emitError(op,
"SV attributes emission is unimplemented for the op");
6171 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6177 Operation *childMod =
6178 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6182 ps.addCallback({op,
true});
6183 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6184 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6186 bool isFirst =
true;
6187 ps.scopedBox(PP::bbox2, [&]() {
6188 auto parentPortInfo = parentMod.getPortList();
6192 size_t maxNameLength = 0;
6193 for (
auto &elt : childPortInfo) {
6194 auto portName = elt.getVerilogName();
6195 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6196 maxNameLength = std::max(maxNameLength, elt.getName().size());
6199 SmallVector<Value> instPortValues(childPortInfo.size());
6200 inst.getValues(instPortValues, childPortInfo);
6202 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6204 Value portVal = instPortValues[idx];
6210 bool shouldPrintComma =
true;
6212 shouldPrintComma =
false;
6213 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6215 shouldPrintComma =
true;
6220 if (shouldPrintComma)
6233 ps << PP::neverbox <<
"//";
6237 ps.nbsp(maxNameLength - elt.getName().size());
6239 llvm::SmallPtrSet<Operation *, 4> ops;
6240 if (elt.isOutput()) {
6241 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6242 "output port must have either single or no use");
6243 if (portVal.use_empty()) {
6244 ps <<
"/* unused */";
6245 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6246 portVal.getUses().begin()->getOwner())) {
6249 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6251 parentPortList.atOutput(outputPortNo).getVerilogName());
6253 portVal = portVal.getUsers().begin()->getOperand(0);
6254 ExprEmitter(*
this, ops)
6255 .emitExpression(portVal, LowestPrecedence,
6259 ExprEmitter(*
this, ops)
6260 .emitExpression(portVal, LowestPrecedence,
6273 ps.addCallback({op,
false});
6274 setPendingNewline();
6277void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6279 emitError(op,
"SV attributes emission is unimplemented for the op");
6281 auto instance = op.getReferencedInstance(&state.symbolCache);
6283 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6284 instance.getInterfaceType().getInterface());
6286 ps.addCallback({op,
true});
6287 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6288 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6290 ps.addCallback({op,
false});
6291 setPendingNewline();
6294void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6298 auto printParamType = [&](Type type, Attribute defaultValue,
6299 SmallString<8> &result) {
6301 llvm::raw_svector_ostream sstream(result);
6306 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6307 if (intAttr.getValue().getBitWidth() == 32)
6309 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6310 if (fpAttr.getType().isF64())
6313 if (isa<NoneType>(type))
6320 if (
auto intType = type_dyn_cast<IntegerType>(type))
6321 if (intType.getWidth() == 32) {
6322 sstream <<
"/*integer*/";
6326 printPackedType(type, sstream, module->getLoc(),
6334 size_t maxTypeWidth = 0;
6335 SmallString<8> scratch;
6336 for (
auto param : params) {
6337 auto paramAttr = cast<ParamDeclAttr>(param);
6339 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6340 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6343 if (maxTypeWidth > 0)
6346 ps.scopedBox(PP::bbox2, [&]() {
6347 ps << PP::newline <<
"#(";
6348 ps.scopedBox(PP::cbox0, [&]() {
6351 [&](Attribute param) {
6352 auto paramAttr = cast<ParamDeclAttr>(param);
6353 auto defaultValue = paramAttr.getValue();
6355 printParamType(paramAttr.getType(), defaultValue, scratch);
6356 if (!scratch.empty())
6358 if (scratch.size() < maxTypeWidth)
6359 ps.nbsp(maxTypeWidth - scratch.size());
6361 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6362 module, paramAttr.getName()));
6366 ps.invokeWithStringOS([&](
auto &os) {
6368 return module->emitError("parameter '")
6369 << paramAttr.getName().getValue()
6370 << "' has invalid value";
6375 [&]() { ps <<
"," << PP::newline; });
6381void ModuleEmitter::emitPortList(Operation *module,
6383 bool emitAsTwoStateType) {
6385 if (portInfo.
size())
6386 emitLocationInfo(module->getLoc());
6390 bool hasOutputs =
false, hasZeroWidth =
false;
6391 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6392 SmallVector<SmallString<8>, 16> portTypeStrings;
6394 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6395 auto port = portInfo.
at(i);
6399 lastNonZeroPort = i;
6402 portTypeStrings.push_back({});
6404 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6406 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6409 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6412 if (maxTypeWidth > 0)
6416 ps.scopedBox(PP::bbox2, [&]() {
6417 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6418 auto lastPort = e - 1;
6421 auto portType = portInfo.
at(portIdx).
type;
6425 bool isZeroWidth =
false;
6430 ps << (isZeroWidth ?
"// " :
" ");
6434 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6435 switch (thisPortDirection) {
6436 case ModulePort::Direction::Output:
6439 case ModulePort::Direction::Input:
6440 ps << (hasOutputs ?
"input " :
"input ");
6442 case ModulePort::Direction::InOut:
6443 ps << (hasOutputs ?
"inout " :
"inout ");
6446 bool emitWireInPorts = state.options.emitWireInPorts;
6447 if (emitWireInPorts)
6451 if (!portTypeStrings[portIdx].
empty())
6452 ps << portTypeStrings[portIdx];
6453 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6454 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6456 size_t startOfNamePos =
6457 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6463 ps.invokeWithStringOS(
6464 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6467 auto innerSym = portInfo.
at(portIdx).
getSym();
6468 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6470 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6475 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6479 if (
auto loc = portInfo.
at(portIdx).
loc)
6480 emitLocationInfo(loc);
6490 if (!state.options.disallowPortDeclSharing) {
6491 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6494 auto port = portInfo.
at(portIdx);
6498 bool isZeroWidth =
false;
6503 ps << (isZeroWidth ?
"// " :
" ");
6506 ps.nbsp(startOfNamePos);
6509 StringRef name = port.getVerilogName();
6513 ps.invokeWithStringOS(
6514 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6517 auto sym = port.getSym();
6518 if (state.options.printDebugInfo && sym && !sym.empty())
6519 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6523 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6527 if (
auto loc = port.loc)
6528 emitLocationInfo(loc);
6539 if (!portInfo.
size()) {
6541 SmallPtrSet<Operation *, 8> moduleOpSet;
6542 moduleOpSet.insert(module);
6543 emitLocationInfoAndNewLine(moduleOpSet);
6546 ps <<
");" << PP::newline;
6547 setPendingNewline();
6551void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6552 currentModuleOp =
module;
6554 emitComment(module.getCommentAttr());
6555 emitSVAttributes(module);
6557 ps.addCallback({module,
true});
6561 emitParameters(module, module.getParameters());
6565 assert(state.pendingNewline);
6568 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6571 ps.addCallback({module,
false});
6573 setPendingNewline();
6575 currentModuleOp =
nullptr;
6578void ModuleEmitter::emitFunc(FuncOp func) {
6580 if (func.isDeclaration())
6583 currentModuleOp = func;
6585 ps.addCallback({func,
true});
6589 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6591 ps <<
"endfunction";
6593 currentModuleOp =
nullptr;
6602 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6609 void emit(emit::FileListOp op);
6612 void emit(Block *block);
6614 void emitOp(emit::RefOp op);
6615 void emitOp(emit::VerbatimOp op);
6619 for (Operation &op : *block) {
6620 TypeSwitch<Operation *>(&op)
6621 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6622 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6623 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6624 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6625 .Case<BindInterfaceOp>(
6626 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6627 .Case<TypeScopeOp>([&](
auto typedecls) {
6628 ModuleEmitter(state).emitStatement(typedecls);
6631 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6637 for (
auto sym : op.getFiles()) {
6638 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6640 auto it = state.fileMapping.find(fileName);
6641 if (it == state.fileMapping.end()) {
6642 emitOpError(op,
" references an invalid file: ") << sym;
6646 auto file = cast<emit::FileOp>(it->second);
6647 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6654 StringAttr target = op.getTargetAttr().getAttr();
6655 auto *targetOp = state.symbolCache.getDefinition(target);
6656 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6658 TypeSwitch<Operation *>(targetOp)
6659 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6660 .Case<hw::HWModuleOp>(
6661 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6662 .Case<TypeScopeOp>([&](
auto typedecls) {
6663 ModuleEmitter(state).emitStatement(typedecls);
6666 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6672 SmallPtrSet<Operation *, 8> ops;
6677 StringRef text = op.getText();
6681 const auto &[lhs, rhs] = text.split(
'\n');
6685 ps << PP::end << PP::newline << PP::neverbox;
6687 }
while (!text.empty());
6690 emitLocationInfoAndNewLine(ops);
6708 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6709 moduleOp->walk([&](Operation *op) {
6711 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6714 SymbolTable::getSymbolAttrName()),
6715 name.getSymName(), op);
6716 if (isa<BindOp>(op))
6722 auto collectPorts = [&](
auto moduleOp) {
6723 auto portInfo = moduleOp.getPortList();
6724 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6725 if (!p.attrs || p.attrs.empty())
6727 for (NamedAttribute portAttr : p.attrs) {
6728 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6737 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6738 for (
auto file :
designOp.getOps<emit::FileOp>())
6739 for (
auto refs : file.getOps<emit::RefOp>())
6740 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6742 SmallString<32> outputPath;
6743 for (
auto &op : *
designOp.getBody()) {
6746 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6748 bool hasFileName =
false;
6749 bool emitReplicatedOps = !isFileOp;
6750 bool addToFilelist = !isFileOp;
6756 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6758 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6759 <<
" on " << op <<
"\n";);
6760 if (!attr.isDirectory())
6763 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6764 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6767 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6772 if (!defaultFileName.isTriviallyEmpty()) {
6773 llvm::sys::path::append(outputPath, defaultFileName);
6775 op->emitError(
"file name unspecified");
6777 llvm::sys::path::append(outputPath,
"error.out");
6781 auto destFile = StringAttr::get(op->getContext(), outputPath);
6782 auto &file =
files[destFile];
6783 file.ops.push_back(info);
6784 file.emitReplicatedOps = emitReplicatedOps;
6785 file.addToFilelist = addToFilelist;
6786 file.isVerilog = outputPath.ends_with(
".sv");
6791 if (!attr || attr.isDirectory()) {
6792 auto excludeFromFileListAttr =
6793 BoolAttr::get(op->getContext(), !addToFilelist);
6794 auto includeReplicatedOpsAttr =
6795 BoolAttr::get(op->getContext(), emitReplicatedOps);
6796 auto outputFileAttr = hw::OutputFileAttr::get(
6797 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6798 op->setAttr(
"output_file", outputFileAttr);
6804 TypeSwitch<Operation *>(&op)
6805 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6807 fileMapping.try_emplace(file.getSymNameAttr(), file);
6808 separateFile(file, file.getFileName());
6810 .Case<emit::FragmentOp>([&](
auto fragment) {
6813 .Case<HWModuleOp>([&](
auto mod) {
6815 auto sym = mod.getNameAttr();
6818 collectInstanceSymbolsAndBinds(mod);
6820 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6821 if (it->second.size() != 1 || attr) {
6824 op.emitError(
"modules can be emitted to a single file");
6832 if (attr || separateModules)
6838 .Case<InterfaceOp>([&](InterfaceOp intf) {
6843 for (
auto &op : *intf.getBodyBlock())
6844 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6845 if (
auto name = symOp.getNameAttr())
6849 if (attr || separateModules)
6850 separateFile(intf, intf.getSymName() +
".sv");
6856 separateFile(op, op.getOutputFile().getFilename().getValue());
6858 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
6864 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6865 [&](Operation *op) {
6871 separateFile(op,
"");
6873 .Case<FuncOp>([&](
auto op) {
6879 separateFile(op,
"");
6883 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6886 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6895 separateFile(op,
"");
6897 .Case<BindOp>([&](
auto op) {
6899 separateFile(op,
"bindfile.sv");
6904 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
6905 .Case<MacroDeclOp>([&](
auto op) {
6908 .Case<sv::ReserveNamesOp>([](
auto op) {
6911 .Case<om::ClassLike>([&](
auto op) {
6914 .Case<om::ConstantOp>([&](
auto op) {
6917 .Default([&](
auto *) {
6918 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6938 size_t lastReplicatedOp = 0;
6940 bool emitHeaderInclude =
6943 if (emitHeaderInclude)
6946 size_t numReplicatedOps =
6951 DenseSet<emit::FragmentOp> includedFragments;
6952 for (
const auto &opInfo : file.
ops) {
6953 Operation *op = opInfo.op;
6957 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6963 if (
auto fragments =
6965 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6969 op->emitError(
"cannot find referenced fragment ") << sym;
6972 emit::FragmentOp fragment = it->second;
6973 if (includedFragments.insert(fragment).second) {
6974 thingsToEmit.emplace_back(it->second);
6980 thingsToEmit.emplace_back(op);
6985 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6990 TypeSwitch<Operation *>(op)
6991 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6992 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
6995 .Case<HWModuleGeneratedOp>(
6996 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6997 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6998 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6999 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7000 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7001 .Case<TypeScopeOp>([&](
auto typedecls) {
7002 ModuleEmitter(state).emitStatement(typedecls);
7004 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7006 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7007 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7008 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7009 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7010 .Default([&](
auto *op) {
7011 state.encounteredError =
true;
7012 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7019 llvm::formatted_raw_ostream &os,
7020 StringAttr fileName,
bool parallelize) {
7021 MLIRContext *context =
designOp->getContext();
7025 parallelize &= context->isMultithreadingEnabled();
7036 size_t lineOffset = 0;
7037 for (
auto &entry : thingsToEmit) {
7038 entry.verilogLocs.setStream(os);
7039 if (
auto *op = entry.getOperation()) {
7044 state.addVerilogLocToOps(lineOffset, fileName);
7046 os << entry.getStringData();
7051 if (state.encounteredError)
7069 SmallString<256> buffer;
7070 llvm::raw_svector_ostream tmpStream(buffer);
7071 llvm::formatted_raw_ostream rs(tmpStream);
7082 for (
auto &entry : thingsToEmit) {
7085 auto *op = entry.getOperation();
7087 auto lineOffset = os.getLine() + 1;
7088 os << entry.getStringData();
7092 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7095 entry.verilogLocs.setStream(os);
7102 state.addVerilogLocToOps(0, fileName);
7118 module.emitWarning()
7119 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7120 "created only at SplitExportVerilog";
7129 for (
const auto &it : emitter.
files) {
7130 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7131 "\" ----- 8< -----\n\n");
7137 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7138 "\" ----- 8< -----\n\n");
7139 for (
auto &name : it.second)
7140 contents += name.str() +
"\n";
7141 list.emplace_back(contents);
7144 llvm::formatted_raw_ostream rs(os);
7148 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7157 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7159 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7160 if (failed(failableParallelForEach(
7161 module->getContext(), modulesToPrepare,
7162 [&](
auto op) { return prepareHWModule(op, options); })))
7169struct ExportVerilogPass
7170 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7171 ExportVerilogPass(raw_ostream &os) : os(os) {}
7172 void runOnOperation()
override {
7174 mlir::OpPassManager preparePM(
"builtin.module");
7175 preparePM.addPass(createLegalizeAnonEnums());
7177 auto &modulePM = preparePM.nestAny();
7178 modulePM.addPass(createPrepareForEmission());
7179 if (failed(runPipeline(preparePM, getOperation())))
7180 return signalPassFailure();
7183 return signalPassFailure();
7190struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7191 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7192 : ExportVerilogPass{*os} {
7193 owned = std::move(os);
7197 std::unique_ptr<llvm::raw_ostream> owned;
7201std::unique_ptr<mlir::Pass>
7203 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7206std::unique_ptr<mlir::Pass>
7208 return std::make_unique<ExportVerilogPass>(os);
7219static std::unique_ptr<llvm::ToolOutputFile>
7223 SmallString<128> outputFilename(dirname);
7225 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7228 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7230 emitter.
designOp.emitError(
"cannot create output directory \"")
7231 << outputDir <<
"\": " << error.message();
7237 std::string errorMessage;
7238 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7240 emitter.
designOp.emitError(errorMessage);
7257 llvm::formatted_raw_ostream rs(output->os());
7263 StringAttr::get(fileName.getContext(), output->getFilename()),
7269 StringRef dirname) {
7280 bool insertSuccess =
7282 .insert({StringAttr::get(module.getContext(),
circtHeader),
7288 if (!insertSuccess) {
7289 module.emitError() << "tried to emit a heder to " << circtHeader
7290 << ", but the file is used as an output too.";
7296 parallelForEach(module->getContext(), emitter.
files.begin(),
7297 emitter.
files.end(), [&](
auto &it) {
7298 createSplitOutputFile(it.first, it.second, dirname,
7303 SmallString<128> filelistPath(dirname);
7304 llvm::sys::path::append(filelistPath,
"filelist.f");
7306 std::string errorMessage;
7307 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7309 module->emitError(errorMessage);
7313 for (
const auto &it : emitter.
files) {
7314 if (it.second.addToFilelist)
7315 output->os() << it.first.str() <<
"\n";
7324 for (
auto &name : it.second)
7325 output->os() << name.str() <<
"\n";
7336 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7338 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7339 if (failed(failableParallelForEach(
7340 module->getContext(), modulesToPrepare,
7341 [&](
auto op) { return prepareHWModule(op, options); })))
7349struct ExportSplitVerilogPass
7350 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7351 ExportSplitVerilogPass(StringRef directory) {
7352 directoryName = directory.str();
7354 void runOnOperation()
override {
7356 mlir::OpPassManager preparePM(
"builtin.module");
7360 modulePM.addPass(createPrepareForEmission());
7361 if (failed(runPipeline(preparePM, getOperation())))
7362 return signalPassFailure();
7365 return signalPassFailure();
7370std::unique_ptr<mlir::Pass>
7372 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 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)
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
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.
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoices()
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
Information to control the emission of a single operation into a file.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
const LoweringOptions & options
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.