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, UnionType>(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) {
1749 assert(enumType.getBitWidth().has_value() &&
1750 "enum type must have bitwidth");
1752 if (enumType.getBitWidth() != 32)
1753 os <<
"bit [" << *enumType.getBitWidth() - 1 <<
":0] ";
1755 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1756 llvm::interleaveComma(
1757 enumType.getFields().getAsRange<StringAttr>(), os,
1758 [&](
auto enumerator) {
1759 os << emitter.fieldNameResolver.getEnumFieldName(
1760 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1765 .Case<StructType>([&](StructType structType) {
1766 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1767 os <<
"/*Zero Width*/";
1770 os <<
"struct packed {";
1771 for (
auto &element : structType.getElements()) {
1773 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1774 <<
": Zero Width;*/ ";
1777 SmallVector<Attribute, 8> structDims;
1782 {}, emitAsTwoStateType);
1783 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1784 emitter.printUnpackedTypePostfix(element.type, os);
1791 .Case<UnionType>([&](UnionType unionType) {
1792 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1793 os <<
"/*Zero Width*/";
1797 int64_t unionWidth = hw::getBitWidth(unionType);
1798 os <<
"union packed {";
1799 for (
auto &element : unionType.getElements()) {
1801 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1802 <<
": Zero Width;*/ ";
1805 int64_t elementWidth = hw::getBitWidth(element.type);
1806 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1808 os <<
" struct packed {";
1809 if (element.offset) {
1810 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1811 << element.offset - 1 <<
":0] "
1812 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1816 SmallVector<Attribute, 8> structDims;
1820 true, emitter, {}, emitAsTwoStateType);
1821 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1822 emitter.printUnpackedTypePostfix(element.type, os);
1826 if (elementWidth + (int64_t)element.offset < unionWidth) {
1827 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1828 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1829 <<
"__post_padding_" << element.name.getValue() <<
";";
1831 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1840 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1841 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1842 os <<
"<<unexpected unpacked array>>";
1843 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1847 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1848 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1850 mlir::emitError(loc,
"unresolvable type reference");
1853 if (typedecl.getType() != typeRef.getInnerType()) {
1854 mlir::emitError(loc,
"declared type did not match aliased type");
1858 os << typedecl.getPreferredName();
1859 emitDims(dims, os, typedecl->getLoc(), emitter);
1862 .Default([&](Type type) {
1863 os <<
"<<invalid type '" << type <<
"'>>";
1864 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1880bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1881 Type optionalAliasType,
1882 bool implicitIntType,
1883 bool singleBitDefaultType,
1884 bool emitAsTwoStateType) {
1885 SmallVector<Attribute, 8> packedDimensions;
1887 singleBitDefaultType, *
this, optionalAliasType,
1888 emitAsTwoStateType);
1894void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1895 TypeSwitch<Type, void>(type)
1897 printUnpackedTypePostfix(inoutType.getElementType(), os);
1899 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1900 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1901 : state.designOp->getLoc();
1902 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1904 printUnpackedTypePostfix(arrayType.getElementType(), os);
1906 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1908 printUnpackedTypePostfix(arrayType.getElementType(), os);
1910 .Case<InterfaceType>([&](
auto) {
1924ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1925 function_ref<InFlightDiagnostic()> emitError) {
1926 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1934ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1935 VerilogPrecedence parenthesizeIfLooserThan,
1936 function_ref<InFlightDiagnostic()> emitError) {
1937 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1938 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1939 APInt value = intAttr.getValue();
1943 if (intTy.getWidth() > 32) {
1945 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1949 if (intTy.isSigned())
1950 os << intTy.getWidth() <<
"'sd";
1952 os << intTy.getWidth() <<
"'d";
1954 value.print(os, intTy.isSigned());
1955 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1957 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1959 os.write_escaped(strAttr.getValue());
1961 return {Symbol, IsUnsigned};
1963 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1965 os << fpAttr.getValueAsDouble();
1966 return {Symbol, IsUnsigned};
1968 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1969 os << verbatimParam.getValue().getValue();
1970 return {Symbol, IsUnsigned};
1972 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1974 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1975 parameterRef.getName());
1978 return {Symbol, IsUnsigned};
1982 auto expr = dyn_cast<ParamExprAttr>(value);
1984 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1985 emitError() <<
" = " << value;
1986 return {LowestPrecedence, IsUnsigned};
1989 StringRef operatorStr;
1990 StringRef openStr, closeStr;
1991 VerilogPrecedence subprecedence = LowestPrecedence;
1992 VerilogPrecedence prec;
1993 std::optional<SubExprSignResult> operandSign;
1994 bool isUnary =
false;
1995 bool hasOpenClose =
false;
1997 switch (expr.getOpcode()) {
1999 operatorStr =
" + ";
2000 subprecedence = Addition;
2003 operatorStr =
" * ";
2004 subprecedence = Multiply;
2007 operatorStr =
" & ";
2008 subprecedence = And;
2011 operatorStr =
" | ";
2015 operatorStr =
" ^ ";
2016 subprecedence = Xor;
2019 operatorStr =
" << ";
2020 subprecedence = Shift;
2024 operatorStr =
" >> ";
2025 subprecedence = Shift;
2029 operatorStr =
" >>> ";
2030 subprecedence = Shift;
2031 operandSign = IsSigned;
2034 operatorStr =
" / ";
2035 subprecedence = Multiply;
2036 operandSign = IsUnsigned;
2039 operatorStr =
" / ";
2040 subprecedence = Multiply;
2041 operandSign = IsSigned;
2044 operatorStr =
" % ";
2045 subprecedence = Multiply;
2046 operandSign = IsUnsigned;
2049 operatorStr =
" % ";
2050 subprecedence = Multiply;
2051 operandSign = IsSigned;
2054 openStr =
"$clog2(";
2056 operandSign = IsUnsigned;
2057 hasOpenClose =
true;
2060 case PEO::StrConcat:
2063 hasOpenClose =
true;
2066 subprecedence = LowestPrecedence;
2071 prec = subprecedence;
2074 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2076 assert(isUnary || hasOpenClose ||
2077 !llvm::hasSingleElement(expr.getOperands()));
2084 auto emitOperand = [&](Attribute operand) ->
bool {
2086 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2087 if (operandSign.has_value())
2088 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2091 if (operandSign.has_value()) {
2093 signedness = *operandSign;
2095 return signedness == IsSigned;
2099 if (prec > parenthesizeIfLooserThan)
2108 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2109 for (
auto op : expr.getOperands().drop_front()) {
2112 if (expr.getOpcode() == PEO::Add) {
2113 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2114 const APInt &value = integer.getValue();
2115 if (value.isNegative() && !value.isMinSignedValue()) {
2117 allOperandsSigned &=
2118 emitOperand(IntegerAttr::get(op.getType(), -value));
2125 allOperandsSigned &= emitOperand(op);
2129 if (prec > parenthesizeIfLooserThan) {
2133 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2148class ExprEmitter :
public EmitterBase,
2150 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2155 ExprEmitter(ModuleEmitter &emitter,
2156 SmallPtrSetImpl<Operation *> &emittedExprs)
2157 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2159 ExprEmitter(ModuleEmitter &emitter,
2160 SmallPtrSetImpl<Operation *> &emittedExprs,
2162 : EmitterBase(emitter.state), emitter(emitter),
2163 emittedExprs(emittedExprs), buffer(tokens),
2164 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2165 assert(state.pp.getListener() == &state.saver);
2172 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2173 bool isAssignmentLikeContext) {
2174 assert(localTokens.empty());
2176 ps.scopedBox(PP::ibox0, [&]() {
2179 emitSubExpr(exp, parenthesizeIfLooserThan,
2181 isAssignmentLikeContext ? RequireUnsigned : NoRequirement,
2183 isAssignmentLikeContext);
2188 if (&buffer.tokens == &localTokens)
2189 buffer.flush(state.pp);
2194 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2195 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2197 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2205 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2206 SubExprSignRequirement signReq = NoRequirement,
2207 bool isSelfDeterminedUnsignedValue =
false,
2208 bool isAssignmentLikeContext =
false);
2212 void emitSVAttributes(Operation *op);
2214 SubExprInfo visitUnhandledExpr(Operation *op);
2215 SubExprInfo visitInvalidComb(Operation *op) {
2218 SubExprInfo visitUnhandledComb(Operation *op) {
2219 return visitUnhandledExpr(op);
2222 return dispatchSVVisitor(op);
2225 return visitUnhandledExpr(op);
2227 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2230 enum EmitBinaryFlags {
2231 EB_RequireSignedOperands = RequireSigned,
2232 EB_RequireUnsignedOperands = RequireUnsigned,
2233 EB_OperandSignRequirementMask = 0x3,
2238 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2242 EB_ForceResultSigned = 0x8,
2247 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2248 const char *syntax,
unsigned emitBinaryFlags = 0);
2250 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2251 bool resultAlwaysUnsigned =
false);
2254 void emitSubExprIBox2(
2255 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2256 ps.scopedBox(PP::ibox2,
2257 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2262 template <
typename Container,
typename EachFn>
2263 void interleaveComma(
const Container &c, EachFn eachFn) {
2264 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2269 void interleaveComma(ValueRange ops) {
2270 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2287 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2289 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2290 CloseFunc closeFn) {
2292 ps.scopedBox(PP::cbox0, [&]() {
2293 interleaveComma(c, eachFn);
2299 template <
typename OpenFunc,
typename CloseFunc>
2300 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2301 return emitBracedList(
2302 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2306 void emitBracedList(ValueRange ops) {
2307 return emitBracedList(
2308 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2312 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2315 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2316 bool printAsPattern, Operation *op);
2318 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2319 ArrayAttr fieldValues,
bool printAsPattern,
2322 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2324 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2325 SubExprInfo visitSV(GetModportOp op);
2326 SubExprInfo visitSV(SystemFunctionOp op);
2327 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2328 SubExprInfo visitSV(XMROp op);
2329 SubExprInfo visitSV(SFormatFOp op);
2330 SubExprInfo visitSV(XMRRefOp op);
2331 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2332 SubExprInfo visitSV(VerbatimExprOp op) {
2333 return visitVerbatimExprOp(op, op.getSymbols());
2335 SubExprInfo visitSV(VerbatimExprSEOp op) {
2336 return visitVerbatimExprOp(op, op.getSymbols());
2338 SubExprInfo visitSV(MacroRefExprOp op);
2339 SubExprInfo visitSV(MacroRefExprSEOp op);
2340 template <
typename MacroTy>
2341 SubExprInfo emitMacroCall(MacroTy op);
2343 SubExprInfo visitSV(ConstantXOp op);
2344 SubExprInfo visitSV(ConstantZOp op);
2345 SubExprInfo visitSV(ConstantStrOp op);
2347 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2348 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2350 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2355 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2356 emitSVAttributes(op);
2359 SubExprInfo visitSV(ArrayIndexInOutOp op);
2360 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2361 SubExprInfo visitSV(IndexedPartSelectOp op);
2362 SubExprInfo visitSV(StructFieldInOutOp op);
2365 SubExprInfo visitSV(SampledOp op);
2368 SubExprInfo visitSV(TimeOp op);
2369 SubExprInfo visitSV(STimeOp op);
2372 using TypeOpVisitor::visitTypeOp;
2374 SubExprInfo visitTypeOp(AggregateConstantOp op);
2376 SubExprInfo visitTypeOp(ParamValueOp op);
2383 SubExprInfo visitTypeOp(StructInjectOp op);
2384 SubExprInfo visitTypeOp(UnionCreateOp op);
2385 SubExprInfo visitTypeOp(UnionExtractOp op);
2386 SubExprInfo visitTypeOp(EnumCmpOp op);
2387 SubExprInfo visitTypeOp(EnumConstantOp op);
2390 using CombinationalVisitor::visitComb;
2391 SubExprInfo visitComb(
MuxOp op);
2392 SubExprInfo visitComb(ReverseOp op);
2393 SubExprInfo visitComb(
AddOp op) {
2394 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2395 return emitBinary(op, Addition,
"+");
2397 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2398 SubExprInfo visitComb(
MulOp op) {
2399 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2400 return emitBinary(op, Multiply,
"*");
2402 SubExprInfo visitComb(
DivUOp op) {
2403 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2405 SubExprInfo visitComb(
DivSOp op) {
2406 return emitBinary(op, Multiply,
"/",
2407 EB_RequireSignedOperands | EB_ForceResultSigned);
2409 SubExprInfo visitComb(
ModUOp op) {
2410 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2412 SubExprInfo visitComb(
ModSOp op) {
2413 return emitBinary(op, Multiply,
"%",
2414 EB_RequireSignedOperands | EB_ForceResultSigned);
2416 SubExprInfo visitComb(
ShlOp op) {
2417 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2419 SubExprInfo visitComb(
ShrUOp op) {
2421 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2423 SubExprInfo visitComb(
ShrSOp op) {
2426 return emitBinary(op, Shift,
">>>",
2427 EB_RequireSignedOperands | EB_ForceResultSigned |
2428 EB_RHS_UnsignedWithSelfDeterminedWidth);
2430 SubExprInfo visitComb(
AndOp op) {
2431 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2432 return emitBinary(op, And,
"&");
2434 SubExprInfo visitComb(
OrOp op) {
2435 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2436 return emitBinary(op, Or,
"|");
2438 SubExprInfo visitComb(
XorOp op) {
2439 if (op.isBinaryNot())
2440 return emitUnary(op,
"~");
2441 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2442 return emitBinary(op, Xor,
"^");
2447 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2449 SubExprInfo visitComb(ReplicateOp op);
2450 SubExprInfo visitComb(
ConcatOp op);
2452 SubExprInfo visitComb(ICmpOp op);
2454 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2455 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2456 "not printed within an assignment-like context");
2457 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2458 "supposed to spill such expressions";
2462 SubExprInfo printStructCreate(
2463 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2465 bool printAsPattern, Operation *op);
2468 ModuleEmitter &emitter;
2475 SubExprSignRequirement signPreference = NoRequirement;
2479 SmallPtrSetImpl<Operation *> &emittedExprs;
2482 SmallVector<Token> localTokens;
2496 bool isAssignmentLikeContext =
false;
2500SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2502 unsigned emitBinaryFlags) {
2504 emitError(op,
"SV attributes emission is unimplemented for the op");
2515 if (emitBinaryFlags & EB_ForceResultSigned)
2516 ps <<
"$signed(" << PP::ibox0;
2517 auto operandSignReq =
2518 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2519 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2521 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2523 ps << lhsSpace << syntax << PP::nbsp;
2530 auto rhsPrec = prec;
2531 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2532 rhsPrec = VerilogPrecedence(prec - 1);
2537 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2538 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2539 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2540 operandSignReq = NoRequirement;
2543 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2544 rhsIsUnsignedValueWithSelfDeterminedWidth);
2548 SubExprSignResult signedness = IsUnsigned;
2549 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2550 signedness = IsSigned;
2552 if (emitBinaryFlags & EB_ForceResultSigned) {
2553 ps << PP::end <<
")";
2554 signedness = IsSigned;
2558 return {prec, signedness};
2561SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2562 bool resultAlwaysUnsigned) {
2564 emitError(op,
"SV attributes emission is unimplemented for the op");
2567 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2571 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2572 resultAlwaysUnsigned ? IsUnsigned : signedness};
2577void ExprEmitter::emitSVAttributes(Operation *op) {
2591 auto concat = value.getDefiningOp<
ConcatOp>();
2592 if (!concat || concat.getNumOperands() != 2)
2595 auto constant = concat.getOperand(0).getDefiningOp<
ConstantOp>();
2596 if (constant && constant.getValue().isZero())
2597 return concat.getOperand(1);
2607SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2608 VerilogPrecedence parenthesizeIfLooserThan,
2609 SubExprSignRequirement signRequirement,
2610 bool isSelfDeterminedUnsignedValue,
2611 bool isAssignmentLikeContext) {
2613 if (
auto result = dyn_cast<OpResult>(exp))
2614 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2615 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2616 parenthesizeIfLooserThan, signRequirement,
2617 isSelfDeterminedUnsignedValue,
2618 isAssignmentLikeContext);
2622 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2627 auto *op = exp.getDefiningOp();
2631 if (!shouldEmitInlineExpr) {
2634 if (signRequirement == RequireSigned) {
2636 return {Symbol, IsSigned};
2640 return {Symbol, IsUnsigned};
2643 unsigned subExprStartIndex = buffer.tokens.size();
2645 ps.addCallback({op,
true});
2646 llvm::scope_exit done([&]() {
2648 ps.addCallback({op, false});
2654 signPreference = signRequirement;
2656 bool bitCastAdded =
false;
2657 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2659 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2660 ps.addAsString(inType.getWidth());
2661 ps <<
"'(" << PP::ibox0;
2662 bitCastAdded =
true;
2666 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2667 isAssignmentLikeContext);
2668 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2674 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2676 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2678 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2679 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2682 expInfo.signedness = IsSigned;
2683 expInfo.precedence = Selection;
2684 }
else if (signRequirement == RequireUnsigned &&
2685 expInfo.signedness == IsSigned) {
2688 expInfo.signedness = IsUnsigned;
2689 expInfo.precedence = Selection;
2690 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2697 expInfo.precedence = Selection;
2704 emittedExprs.insert(exp.getDefiningOp());
2708SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2709 auto openFn = [&]() {
2711 ps.addAsString(op.getMultiple());
2714 auto closeFn = [&]() { ps <<
"}}"; };
2718 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2719 if (op.getOperand().hasOneUse()) {
2720 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2721 return {Symbol, IsUnsigned};
2724 emitBracedList(op.getOperand(), openFn, closeFn);
2725 return {Symbol, IsUnsigned};
2728SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2729 emitBracedList(op.getOperands());
2730 return {Symbol, IsUnsigned};
2733SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2737 Type toType = op.getType();
2740 ps.invokeWithStringOS(
2741 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2744 return emitSubExpr(op.getInput(), LowestPrecedence);
2747SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2748 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2749 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2750 SubExprSignRequirement signop[] = {
2752 NoRequirement, NoRequirement,
2754 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2756 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2758 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2760 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2761 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2764 if (op.isEqualAllOnes())
2765 return emitUnary(op,
"&",
true);
2768 if (op.isNotEqualZero())
2769 return emitUnary(op,
"|",
true);
2771 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2775 result.signedness = IsUnsigned;
2779SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2781 emitError(op,
"SV attributes emission is unimplemented for the op");
2783 unsigned loBit = op.getLowBit();
2784 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2786 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2787 assert((x.precedence == Symbol ||
2789 "should be handled by isExpressionUnableToInline");
2794 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2798 ps.addAsString(hiBit);
2799 if (hiBit != loBit) {
2801 ps.addAsString(loBit);
2804 return {Unary, IsUnsigned};
2807SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2809 emitError(op,
"SV attributes emission is unimplemented for the op");
2811 auto decl = op.getReferencedDecl(state.symbolCache);
2814 return {Selection, IsUnsigned};
2817SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2819 emitError(op,
"SV attributes emission is unimplemented for the op");
2822 ps.scopedBox(PP::ibox0, [&]() {
2824 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2825 [&]() { ps <<
"," << PP::space; });
2828 return {Symbol, IsUnsigned};
2831SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2833 emitError(op,
"SV attributes emission is unimplemented for the op");
2835 auto decl = op.getReferencedDecl(state.symbolCache);
2839 return {Selection, IsUnsigned};
2842SubExprInfo ExprEmitter::visitSV(XMROp op) {
2844 emitError(op,
"SV attributes emission is unimplemented for the op");
2846 if (op.getIsRooted())
2848 for (
auto s : op.getPath())
2849 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2851 return {Selection, IsUnsigned};
2856SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2858 emitError(op,
"SV attributes emission is unimplemented for the op");
2861 auto globalRef = op.getReferencedPath(&state.symbolCache);
2862 auto namepath = globalRef.getNamepathAttr().getValue();
2863 auto *
module = state.symbolCache.getDefinition(
2864 cast<InnerRefAttr>(namepath.front()).getModule());
2866 for (
auto sym : namepath) {
2868 auto innerRef = cast<InnerRefAttr>(sym);
2869 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2870 innerRef.getName());
2871 if (ref.hasPort()) {
2877 auto leaf = op.getVerbatimSuffixAttr();
2878 if (leaf && leaf.size())
2880 return {Selection, IsUnsigned};
2883SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2885 emitError(op,
"SV attributes emission is unimplemented for the op");
2887 emitTextWithSubstitutions(
2888 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2889 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2891 return {Unary, IsUnsigned};
2894template <
typename MacroTy>
2895SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2897 emitError(op,
"SV attributes emission is unimplemented for the op");
2900 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2901 assert(macroOp &&
"Invalid IR");
2903 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2905 if (!op.getInputs().empty()) {
2907 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2908 emitExpression(val, LowestPrecedence, false);
2912 return {LowestPrecedence, IsUnsigned};
2915SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2916 return emitMacroCall(op);
2919SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2920 return emitMacroCall(op);
2923SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2925 emitError(op,
"SV attributes emission is unimplemented for the op");
2927 ps.addAsString(op.getWidth());
2929 return {Unary, IsUnsigned};
2932SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2934 emitError(op,
"SV attributes emission is unimplemented for the op");
2936 ps.writeQuotedEscaped(op.getStr());
2937 return {Symbol, IsUnsigned};
2940SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2942 emitError(op,
"SV attributes emission is unimplemented for the op");
2944 ps.addAsString(op.getWidth());
2946 return {Unary, IsUnsigned};
2949SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2950 bool isNegated =
false;
2953 if (signPreference == RequireSigned && value.isNegative() &&
2954 !value.isMinSignedValue()) {
2959 ps.addAsString(type.getWidth());
2963 if (signPreference == RequireSigned)
2969 SmallString<32> valueStr;
2971 (-value).toStringUnsigned(valueStr, 16);
2973 value.toStringUnsigned(valueStr, 16);
2976 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2979SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2981 emitError(op,
"SV attributes emission is unimplemented for the op");
2983 auto value = op.getValue();
2987 if (value.getBitWidth() == 0) {
2988 emitOpError(op,
"will not emit zero width constants in the general case");
2989 ps <<
"<<unsupported zero width constant: "
2990 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2991 return {Unary, IsUnsigned};
2994 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2997void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2998 bool printAsPattern, Operation *op) {
2999 if (printAsPattern && !isAssignmentLikeContext)
3000 emitAssignmentPatternContextError(op);
3001 StringRef openDelim = printAsPattern ?
"'{" :
"{";
3004 elementValues, [&]() { ps << openDelim; },
3005 [&](Attribute elementValue) {
3006 printConstantAggregate(elementValue,
elementType, op);
3008 [&]() { ps <<
"}"; });
3011void ExprEmitter::printConstantStruct(
3012 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3013 bool printAsPattern, Operation *op) {
3014 if (printAsPattern && !isAssignmentLikeContext)
3015 emitAssignmentPatternContextError(op);
3022 auto fieldRange = llvm::make_filter_range(
3023 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3028 if (printAsPattern) {
3030 fieldRange, [&]() { ps <<
"'{"; },
3031 [&](
const auto &fieldAndValue) {
3032 ps.scopedBox(PP::ibox2, [&]() {
3033 const auto &[field, value] = fieldAndValue;
3034 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3035 <<
":" << PP::space;
3036 printConstantAggregate(value, field.type, op);
3039 [&]() { ps <<
"}"; });
3042 fieldRange, [&]() { ps <<
"{"; },
3043 [&](
const auto &fieldAndValue) {
3044 ps.scopedBox(PP::ibox2, [&]() {
3045 const auto &[field, value] = fieldAndValue;
3046 printConstantAggregate(value, field.type, op);
3049 [&]() { ps <<
"}"; });
3053void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3056 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3057 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3058 isAssignmentLikeContext, op);
3061 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3062 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3066 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3067 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3068 isAssignmentLikeContext, op);
3070 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3071 auto value = cast<IntegerAttr>(attr).getValue();
3072 printConstantScalar(value, intType);
3076 emitOpError(op,
"contains constant of type ")
3077 << type <<
" which cannot be emitted as Verilog";
3080SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3082 emitError(op,
"SV attributes emission is unimplemented for the op");
3086 "zero-bit types not allowed at this point");
3088 printConstantAggregate(op.getFields(), op.getType(), op);
3089 return {Symbol, IsUnsigned};
3092SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3094 emitError(op,
"SV attributes emission is unimplemented for the op");
3096 return ps.invokeWithStringOS([&](
auto &os) {
3097 return emitter.printParamValue(op.getValue(), os, [&]() {
3098 return op->emitOpError(
"invalid parameter use");
3107 emitError(op,
"SV attributes emission is unimplemented for the op");
3109 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3111 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3113 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3115 ps.addAsString(dstWidth);
3117 return {Selection, arrayPrec.signedness};
3120SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3121 emitSubExpr(op.getInput(), Selection);
3126 emitSubExpr(op.getIndex(), LowestPrecedence);
3128 emitSVAttributes(op);
3129 return {Selection, IsUnsigned};
3135 emitError(op,
"SV attributes emission is unimplemented for the op");
3137 if (op.isUniform()) {
3139 ps.addAsString(op.getInputs().size());
3141 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3145 op.getInputs(), [&]() { ps <<
"{"; },
3148 emitSubExprIBox2(v);
3151 [&]() { ps <<
"}"; });
3153 return {Unary, IsUnsigned};
3156SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3158 emitError(op,
"SV attributes emission is unimplemented for the op");
3161 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3162 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3163 return {Unary, IsUnsigned};
3168 emitError(op,
"SV attributes emission is unimplemented for the op");
3170 emitBracedList(op.getOperands());
3171 return {Unary, IsUnsigned};
3174SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3176 emitError(op,
"SV attributes emission is unimplemented for the op");
3178 auto index = op.getIndex();
3179 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3184 emitSubExpr(index, LowestPrecedence);
3186 return {Selection, arrayPrec.signedness};
3189SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3191 emitError(op,
"SV attributes emission is unimplemented for the op");
3193 auto prec = emitSubExpr(op.getInput(), Selection);
3195 emitSubExpr(op.getBase(), LowestPrecedence);
3196 if (op.getDecrement())
3200 ps.addAsString(op.getWidth());
3202 return {Selection, prec.signedness};
3205SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3207 emitError(op,
"SV attributes emission is unimplemented for the op");
3209 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3211 emitSubExpr(op.getBase(), LowestPrecedence);
3212 if (op.getDecrement())
3216 ps.addAsString(op.getWidth());
3221SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3223 emitError(op,
"SV attributes emission is unimplemented for the op");
3225 auto prec = emitSubExpr(op.getInput(), Selection);
3227 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3228 return {Selection, prec.signedness};
3231SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3233 emitError(op,
"SV attributes emission is unimplemented for the op");
3236 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3241SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3243 emitError(op,
"SV attributes emission is unimplemented for the op");
3246 ps.scopedBox(PP::ibox0, [&]() {
3247 ps.writeQuotedEscaped(op.getFormatString());
3254 for (
auto operand : op.getSubstitutions()) {
3255 ps <<
"," << PP::space;
3256 emitSubExpr(operand, LowestPrecedence);
3260 return {Symbol, IsUnsigned};
3263SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3265 emitError(op,
"SV attributes emission is unimplemented for the op");
3268 return {Symbol, IsUnsigned};
3271SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3273 emitError(op,
"SV attributes emission is unimplemented for the op");
3276 return {Symbol, IsUnsigned};
3279SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3293 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3294 ps.scopedBox(PP::ibox0, [&]() {
3295 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3299 emitSVAttributes(op);
3301 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3302 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3306 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3307 return emitSubExpr(op.getFalseValue(), Conditional);
3310 SubExprSignResult signedness = IsUnsigned;
3311 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3312 signedness = IsSigned;
3314 return {Conditional, signedness};
3318SubExprInfo ExprEmitter::visitComb(ReverseOp op) {
3320 emitError(op,
"SV attributes emission is unimplemented for the op");
3323 emitSubExpr(op.getInput(), LowestPrecedence);
3326 return {Symbol, IsUnsigned};
3329SubExprInfo ExprEmitter::printStructCreate(
3330 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3332 bool printAsPattern, Operation *op) {
3333 if (printAsPattern && !isAssignmentLikeContext)
3334 emitAssignmentPatternContextError(op);
3337 auto filteredFields = llvm::make_filter_range(
3338 llvm::enumerate(fieldInfos),
3339 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3341 if (printAsPattern) {
3343 filteredFields, [&]() { ps <<
"'{"; },
3344 [&](
const auto &field) {
3345 ps.scopedBox(PP::ibox2, [&]() {
3347 emitter.getVerilogStructFieldName(field.value().name))
3348 <<
":" << PP::space;
3349 fieldFn(field.value(), field.index());
3352 [&]() { ps <<
"}"; });
3355 filteredFields, [&]() { ps <<
"{"; },
3356 [&](
const auto &field) {
3357 ps.scopedBox(PP::ibox2,
3358 [&]() { fieldFn(field.value(), field.index()); });
3360 [&]() { ps <<
"}"; });
3363 return {Selection, IsUnsigned};
3368 emitError(op,
"SV attributes emission is unimplemented for the op");
3372 bool printAsPattern = isAssignmentLikeContext;
3373 StructType structType = op.getType();
3374 return printStructCreate(
3375 structType.getElements(),
3376 [&](
const auto &field,
auto index) {
3377 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3379 isAssignmentLikeContext);
3381 printAsPattern, op);
3386 emitError(op,
"SV attributes emission is unimplemented for the op");
3388 emitSubExpr(op.getInput(), Selection);
3390 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3391 return {Selection, IsUnsigned};
3394SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3396 emitError(op,
"SV attributes emission is unimplemented for the op");
3400 bool printAsPattern = isAssignmentLikeContext;
3401 StructType structType = op.getType();
3402 return printStructCreate(
3403 structType.getElements(),
3404 [&](
const auto &field,
auto index) {
3405 if (field.name == op.getFieldNameAttr()) {
3406 emitSubExpr(op.getNewValue(), Selection);
3408 emitSubExpr(op.getInput(), Selection);
3410 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3413 printAsPattern, op);
3416SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3417 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3418 return {Selection, IsUnsigned};
3421SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3423 emitError(op,
"SV attributes emission is unimplemented for the op");
3424 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3427 result.signedness = IsUnsigned;
3431SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3433 emitError(op,
"SV attributes emission is unimplemented for the op");
3437 auto unionWidth = hw::getBitWidth(unionType);
3438 auto &element = unionType.getElements()[op.getFieldIndex()];
3439 auto elementWidth = hw::getBitWidth(element.type);
3442 if (!elementWidth) {
3443 ps.addAsString(unionWidth);
3445 return {Unary, IsUnsigned};
3449 if (elementWidth == unionWidth) {
3450 emitSubExpr(op.getInput(), LowestPrecedence);
3451 return {Unary, IsUnsigned};
3456 ps.scopedBox(PP::ibox0, [&]() {
3457 if (
auto prePadding = element.offset) {
3458 ps.addAsString(prePadding);
3459 ps <<
"'h0," << PP::space;
3461 emitSubExpr(op.getInput(), Selection);
3462 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3463 ps <<
"," << PP::space;
3464 ps.addAsString(postPadding);
3470 return {Unary, IsUnsigned};
3473SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3475 emitError(op,
"SV attributes emission is unimplemented for the op");
3476 emitSubExpr(op.getInput(), Selection);
3479 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3480 auto unionWidth = hw::getBitWidth(unionType);
3481 auto &element = unionType.getElements()[op.getFieldIndex()];
3482 auto elementWidth = hw::getBitWidth(element.type);
3483 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3484 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3493 return {Selection, IsUnsigned};
3496SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3497 emitOpError(op,
"cannot emit this expression to Verilog");
3498 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3500 return {Symbol, IsUnsigned};
3516enum class PropertyPrecedence {
3536struct EmittedProperty {
3538 PropertyPrecedence precedence;
3543class PropertyEmitter :
public EmitterBase,
3544 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3548 PropertyEmitter(ModuleEmitter &emitter,
3549 SmallPtrSetImpl<Operation *> &emittedOps)
3550 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3551 PropertyEmitter(ModuleEmitter &emitter,
3552 SmallPtrSetImpl<Operation *> &emittedOps,
3554 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3556 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3557 assert(state.pp.getListener() == &state.saver);
3560 void emitAssertPropertyDisable(
3561 Value property, Value disable,
3562 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3564 void emitAssertPropertyBody(
3565 Value property, Value disable,
3566 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3568 void emitAssertPropertyBody(
3569 Value property, sv::EventControl event, Value clock, Value disable,
3570 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3575 emitNestedProperty(Value property,
3576 PropertyPrecedence parenthesizeIfLooserThan);
3577 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3578 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3580 EmittedProperty visitUnhandledLTL(Operation *op);
3581 EmittedProperty visitLTL(ltl::BooleanConstantOp op);
3582 EmittedProperty visitLTL(ltl::AndOp op);
3583 EmittedProperty visitLTL(ltl::OrOp op);
3584 EmittedProperty visitLTL(ltl::IntersectOp op);
3585 EmittedProperty visitLTL(ltl::DelayOp op);
3586 EmittedProperty visitLTL(ltl::ConcatOp op);
3587 EmittedProperty visitLTL(ltl::RepeatOp op);
3588 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3589 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3590 EmittedProperty visitLTL(ltl::NotOp op);
3591 EmittedProperty visitLTL(ltl::ImplicationOp op);
3592 EmittedProperty visitLTL(ltl::UntilOp op);
3593 EmittedProperty visitLTL(ltl::EventuallyOp op);
3594 EmittedProperty visitLTL(ltl::ClockOp op);
3596 void emitLTLConcat(ValueRange inputs);
3599 ModuleEmitter &emitter;
3604 SmallPtrSetImpl<Operation *> &emittedOps;
3607 SmallVector<Token> localTokens;
3620void PropertyEmitter::emitAssertPropertyDisable(
3621 Value property, Value disable,
3622 PropertyPrecedence parenthesizeIfLooserThan) {
3625 ps <<
"disable iff" << PP::nbsp <<
"(";
3627 emitNestedProperty(disable, PropertyPrecedence::Unary);
3633 ps.scopedBox(PP::ibox0,
3634 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3640void PropertyEmitter::emitAssertPropertyBody(
3641 Value property, Value disable,
3642 PropertyPrecedence parenthesizeIfLooserThan) {
3643 assert(localTokens.empty());
3645 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3650 if (&buffer.tokens == &localTokens)
3651 buffer.flush(state.pp);
3654void PropertyEmitter::emitAssertPropertyBody(
3655 Value property, sv::EventControl event, Value clock, Value disable,
3656 PropertyPrecedence parenthesizeIfLooserThan) {
3657 assert(localTokens.empty());
3660 ps.scopedBox(PP::ibox2, [&] {
3661 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3662 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3668 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3673 if (&buffer.tokens == &localTokens)
3674 buffer.flush(state.pp);
3677EmittedProperty PropertyEmitter::emitNestedProperty(
3678 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3688 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3689 ExprEmitter(emitter, emittedOps, buffer.tokens)
3690 .emitExpression(property, LowestPrecedence,
3692 return {PropertyPrecedence::Symbol};
3695 unsigned startIndex = buffer.tokens.size();
3696 auto info = dispatchLTLVisitor(property.getDefiningOp());
3701 if (
info.precedence > parenthesizeIfLooserThan) {
3703 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3704 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3706 ps << PP::end <<
")";
3708 info.precedence = PropertyPrecedence::Symbol;
3712 emittedOps.insert(property.getDefiningOp());
3716EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3717 emitOpError(op,
"emission as Verilog property or sequence not supported");
3718 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3719 return {PropertyPrecedence::Symbol};
3722EmittedProperty PropertyEmitter::visitLTL(ltl::BooleanConstantOp op) {
3724 ps << (op.getValueAttr().getValue() ?
"1'h1" :
"1'h0");
3725 return {PropertyPrecedence::Symbol};
3728EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3731 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3732 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3733 return {PropertyPrecedence::And};
3736EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3739 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3740 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3741 return {PropertyPrecedence::Or};
3744EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3748 emitNestedProperty(input, PropertyPrecedence::Intersect);
3750 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3751 return {PropertyPrecedence::Intersect};
3754EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3756 if (
auto length = op.getLength()) {
3758 ps.addAsString(op.getDelay());
3761 ps.addAsString(op.getDelay());
3763 ps.addAsString(op.getDelay() + *length);
3767 if (op.getDelay() == 0) {
3769 }
else if (op.getDelay() == 1) {
3773 ps.addAsString(op.getDelay());
3778 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3779 return {PropertyPrecedence::Concat};
3782void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3783 bool addSeparator =
false;
3784 for (
auto input : inputs) {
3787 if (!input.getDefiningOp<ltl::DelayOp>())
3788 ps <<
"##0" << PP::space;
3790 addSeparator =
true;
3791 emitNestedProperty(input, PropertyPrecedence::Concat);
3795EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3796 emitLTLConcat(op.getInputs());
3797 return {PropertyPrecedence::Concat};
3800EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3801 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3802 if (
auto more = op.getMore()) {
3804 ps.addAsString(op.getBase());
3807 ps.addAsString(op.getBase() + *more);
3811 if (op.getBase() == 0) {
3813 }
else if (op.getBase() == 1) {
3817 ps.addAsString(op.getBase());
3821 return {PropertyPrecedence::Repeat};
3824EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3825 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3827 auto more = op.getMore();
3829 ps.addAsString(op.getBase());
3832 ps.addAsString(op.getBase() + more);
3836 return {PropertyPrecedence::Repeat};
3839EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3840 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3842 auto more = op.getMore();
3844 ps.addAsString(op.getBase());
3847 ps.addAsString(op.getBase() + more);
3851 return {PropertyPrecedence::Repeat};
3854EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3855 ps <<
"not" << PP::space;
3856 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3857 return {PropertyPrecedence::Unary};
3863 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3864 if (!concatOp || concatOp.getInputs().size() < 2)
3866 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3867 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3869 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3870 if (!constOp || !constOp.getValue().isOne())
3872 return concatOp.getInputs().drop_back();
3875EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3879 emitLTLConcat(range);
3880 ps << PP::space <<
"|=>" << PP::nbsp;
3882 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3883 ps << PP::space <<
"|->" << PP::nbsp;
3885 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3886 return {PropertyPrecedence::Implication};
3889EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3890 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3891 ps << PP::space <<
"until" << PP::space;
3892 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3893 return {PropertyPrecedence::Until};
3896EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3897 ps <<
"s_eventually" << PP::space;
3898 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3899 return {PropertyPrecedence::Qualifier};
3902EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3904 ps.scopedBox(PP::ibox2, [&] {
3905 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3906 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3910 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3911 return {PropertyPrecedence::Clocking};
3921class NameCollector {
3923 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3927 void collectNames(Block &block);
3929 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3930 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3933 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3934 ModuleEmitter &moduleEmitter;
3939 static constexpr size_t maxTypeWidthBound = 32;
3944void NameCollector::collectNames(Block &block) {
3947 for (
auto &op : block) {
3951 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3952 FuncCallProceduralOp, FuncCallOp>(op))
3954 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3958 for (
auto result : op.getResults()) {
3960 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3961 SmallString<16> typeString;
3965 llvm::raw_svector_ostream stringStream(typeString);
3967 stringStream, op.getLoc());
3969 if (typeString.size() <= maxTypeWidthBound)
3970 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3977 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3978 for (
auto ®ion : op.getRegions()) {
3979 if (!region.empty())
3980 collectNames(region.front());
3994class StmtEmitter :
public EmitterBase,
4002 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
4004 void emitStatement(Operation *op);
4005 void emitStatementBlock(Block &body);
4008 LogicalResult emitDeclaration(Operation *op);
4011 void collectNamesAndCalculateDeclarationWidths(Block &block);
4014 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
4015 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
4016 bool isAssignmentLikeContext =
false);
4017 void emitSVAttributes(Operation *op);
4020 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
4023 friend class sv::Visitor<StmtEmitter, LogicalResult>;
4027 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
4028 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
4029 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4030 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4031 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4032 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4034 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4035 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4036 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4037 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4038 template <
typename Op>
4041 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4042 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4043 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4045 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4046 LogicalResult visitSV(
AssignOp op);
4047 LogicalResult visitSV(BPAssignOp op);
4048 LogicalResult visitSV(PAssignOp op);
4049 LogicalResult visitSV(ForceOp op);
4050 LogicalResult visitSV(ReleaseOp op);
4051 LogicalResult visitSV(AliasOp op);
4052 LogicalResult visitSV(InterfaceInstanceOp op);
4053 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4054 LogicalResult visitStmt(OutputOp op);
4056 LogicalResult visitStmt(InstanceOp op);
4057 LogicalResult visitStmt(InstanceChoiceOp op);
4058 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4059 ArrayRef<Value> instPortValues);
4064 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4065 LogicalResult visitSV(OrderedOutputOp op);
4066 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4067 LogicalResult visitSV(IfDefProceduralOp op) {
4068 return emitIfDef(op, op.getCond());
4070 LogicalResult visitSV(IfOp op);
4071 LogicalResult visitSV(AlwaysOp op);
4072 LogicalResult visitSV(AlwaysCombOp op);
4073 LogicalResult visitSV(AlwaysFFOp op);
4074 LogicalResult visitSV(InitialOp op);
4075 LogicalResult visitSV(CaseOp op);
4076 LogicalResult visitSV(FWriteOp op);
4077 LogicalResult visitSV(FFlushOp op);
4078 LogicalResult visitSV(VerbatimOp op);
4079 LogicalResult visitSV(MacroRefOp op);
4081 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4082 std::optional<unsigned> verbosity);
4083 LogicalResult visitSV(StopOp op);
4084 LogicalResult visitSV(FinishOp op);
4085 LogicalResult visitSV(ExitOp op);
4087 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4088 std::optional<unsigned> verbosity,
4090 ValueRange operands);
4093 template <
typename OpTy>
4094 LogicalResult emitNonfatalMessageOp(OpTy op,
const char *taskName) {
4095 return emitSeverityMessageTask(op,
PPExtString(taskName), {},
4096 op.getMessageAttr(), op.getSubstitutions());
4100 template <
typename OpTy>
4101 LogicalResult emitFatalMessageOp(OpTy op) {
4102 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4103 op.getMessageAttr(), op.getSubstitutions());
4106 LogicalResult visitSV(FatalProceduralOp op);
4107 LogicalResult visitSV(FatalOp op);
4108 LogicalResult visitSV(ErrorProceduralOp op);
4109 LogicalResult visitSV(WarningProceduralOp op);
4110 LogicalResult visitSV(InfoProceduralOp op);
4111 LogicalResult visitSV(ErrorOp op);
4112 LogicalResult visitSV(WarningOp op);
4113 LogicalResult visitSV(InfoOp op);
4115 LogicalResult visitSV(ReadMemOp op);
4117 LogicalResult visitSV(GenerateOp op);
4118 LogicalResult visitSV(GenerateCaseOp op);
4120 LogicalResult visitSV(
ForOp op);
4122 void emitAssertionLabel(Operation *op);
4123 void emitAssertionMessage(StringAttr message, ValueRange args,
4124 SmallPtrSetImpl<Operation *> &ops,
4126 template <
typename Op>
4127 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4128 LogicalResult visitSV(AssertOp op);
4129 LogicalResult visitSV(AssumeOp op);
4130 LogicalResult visitSV(CoverOp op);
4131 template <
typename Op>
4132 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4133 LogicalResult visitSV(AssertConcurrentOp op);
4134 LogicalResult visitSV(AssumeConcurrentOp op);
4135 LogicalResult visitSV(CoverConcurrentOp op);
4136 template <
typename Op>
4137 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4138 LogicalResult visitSV(AssertPropertyOp op);
4139 LogicalResult visitSV(AssumePropertyOp op);
4140 LogicalResult visitSV(CoverPropertyOp op);
4142 LogicalResult visitSV(BindOp op);
4143 LogicalResult visitSV(InterfaceOp op);
4145 LogicalResult visitSV(InterfaceSignalOp op);
4146 LogicalResult visitSV(InterfaceModportOp op);
4147 LogicalResult visitSV(AssignInterfaceSignalOp op);
4148 LogicalResult visitSV(MacroErrorOp op);
4149 LogicalResult visitSV(MacroDefOp op);
4151 void emitBlockAsStatement(Block *block,
4152 const SmallPtrSetImpl<Operation *> &locationOps,
4153 StringRef multiLineComment = StringRef());
4155 LogicalResult visitSV(FuncDPIImportOp op);
4156 template <
typename CallOp>
4157 LogicalResult emitFunctionCall(CallOp callOp);
4158 LogicalResult visitSV(FuncCallProceduralOp op);
4159 LogicalResult visitSV(FuncCallOp op);
4160 LogicalResult visitSV(ReturnOp op);
4161 LogicalResult visitSV(IncludeOp op);
4164 ModuleEmitter &emitter;
4169 size_t maxDeclNameWidth = 0;
4170 size_t maxTypeWidth = 0;
4181void StmtEmitter::emitExpression(Value exp,
4182 SmallPtrSetImpl<Operation *> &emittedExprs,
4183 VerilogPrecedence parenthesizeIfLooserThan,
4184 bool isAssignmentLikeContext) {
4185 ExprEmitter(emitter, emittedExprs)
4186 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4191void StmtEmitter::emitSVAttributes(Operation *op) {
4199 setPendingNewline();
4202void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4203 llvm::function_ref<
void()> emitRHS,
4205 std::optional<PPExtString> wordBeforeLHS) {
4207 ps.scopedBox(PP::ibox2, [&]() {
4208 if (wordBeforeLHS) {
4209 ps << *wordBeforeLHS << PP::space;
4213 ps << PP::space << syntax << PP::space;
4215 ps.scopedBox(PP::ibox0, [&]() {
4222template <
typename Op>
4224StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4225 std::optional<PPExtString> wordBeforeLHS) {
4226 SmallPtrSet<Operation *, 8> ops;
4230 ps.addCallback({op,
true});
4231 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4233 emitExpression(op.getSrc(), ops, LowestPrecedence,
4238 ps.addCallback({op,
false});
4239 emitLocationInfoAndNewLine(ops);
4243LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4246 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4249 if (emitter.assignsInlined.count(op))
4253 emitSVAttributes(op);
4258LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4259 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4263 if (emitter.assignsInlined.count(op))
4267 emitSVAttributes(op);
4272LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4274 emitSVAttributes(op);
4279LogicalResult StmtEmitter::visitSV(ForceOp op) {
4281 emitError(op,
"SV attributes emission is unimplemented for the op");
4286LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4288 emitError(op,
"SV attributes emission is unimplemented for the op");
4291 SmallPtrSet<Operation *, 8> ops;
4293 ps.addCallback({op,
true});
4294 ps.scopedBox(PP::ibox2, [&]() {
4295 ps <<
"release" << PP::space;
4296 emitExpression(op.getDest(), ops);
4299 ps.addCallback({op,
false});
4300 emitLocationInfoAndNewLine(ops);
4304LogicalResult StmtEmitter::visitSV(AliasOp op) {
4306 emitError(op,
"SV attributes emission is unimplemented for the op");
4309 SmallPtrSet<Operation *, 8> ops;
4311 ps.addCallback({op,
true});
4312 ps.scopedBox(PP::ibox2, [&]() {
4313 ps <<
"alias" << PP::space;
4314 ps.scopedBox(PP::cbox0, [&]() {
4316 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4317 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4321 ps.addCallback({op,
false});
4322 emitLocationInfoAndNewLine(ops);
4326LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4327 auto doNotPrint = op.getDoNotPrint();
4328 if (doNotPrint && !state.options.emitBindComments)
4332 emitError(op,
"SV attributes emission is unimplemented for the op");
4335 StringRef prefix =
"";
4336 ps.addCallback({op,
true});
4339 ps <<
"// This interface is elsewhere emitted as a bind statement."
4343 SmallPtrSet<Operation *, 8> ops;
4346 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4347 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4348 "point to an interface");
4351 if (!prefix.empty())
4357 ps.addCallback({op,
false});
4358 emitLocationInfoAndNewLine(ops);
4366LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4368 SmallPtrSet<Operation *, 8> ops;
4369 size_t operandIndex = 0;
4370 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4371 for (
PortInfo port : ports.getOutputs()) {
4372 auto operand = op->getOperand(operandIndex);
4376 if (operand.hasOneUse() && operand.getDefiningOp() &&
4377 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4386 ps.addCallback({op,
true});
4388 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4390 ps <<
"// Zero width: ";
4393 ps <<
"assign" << PP::space;
4395 ps << PP::space <<
"=" << PP::space;
4396 ps.scopedBox(PP::ibox0, [&]() {
4400 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4401 ps <<
"/*Zero width*/";
4403 emitExpression(operand, ops, LowestPrecedence,
4408 ps.addCallback({op,
false});
4409 emitLocationInfoAndNewLine(ops);
4416LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4417 auto parent = op->getParentOfType<PortList>();
4419 return emitOutputLikeOp(op, ports);
4422LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4424 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4425 ps <<
"`ifndef " << typescopeDef << PP::newline;
4426 ps <<
"`define " << typescopeDef;
4427 setPendingNewline();
4428 emitStatementBlock(*op.getBodyBlock());
4430 ps <<
"`endif // " << typescopeDef;
4431 setPendingNewline();
4435LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4437 emitError(op,
"SV attributes emission is unimplemented for the op");
4442 ps << PP::neverbox <<
"// ";
4444 SmallPtrSet<Operation *, 8> ops;
4446 ps.scopedBox(PP::ibox2, [&]() {
4447 ps <<
"typedef" << PP::space;
4448 ps.invokeWithStringOS([&](
auto &os) {
4450 op.getAliasType(),
false);
4452 ps << PP::space <<
PPExtString(op.getPreferredName());
4453 ps.invokeWithStringOS(
4454 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4459 emitLocationInfoAndNewLine(ops);
4463template <
typename CallOpTy>
4464LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4468 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4470 SmallPtrSet<Operation *, 8> ops;
4474 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4475 if (explicitReturn) {
4476 assert(explicitReturn.hasOneUse());
4477 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4478 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4479 emitExpression(bpassignOp.getDest(), ops);
4481 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4482 ps <<
"assign" << PP::nbsp;
4483 emitExpression(assignOp.getDest(), ops);
4485 ps << PP::nbsp <<
"=" << PP::nbsp;
4488 auto arguments = callee.getPortList(
true);
4492 bool needsComma =
false;
4493 auto printArg = [&](Value value) {
4495 ps <<
"," << PP::space;
4496 emitExpression(value, ops);
4500 ps.scopedBox(PP::ibox0, [&] {
4501 unsigned inputIndex = 0, outputIndex = 0;
4502 for (
auto arg : arguments) {
4505 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4507 printArg(op.getInputs()[inputIndex++]);
4512 emitLocationInfoAndNewLine(ops);
4516LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4517 return emitFunctionCall(op);
4520LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4521 return emitFunctionCall(op);
4524template <
typename PPS>
4526 bool isAutomatic =
false,
4527 bool emitAsTwoStateType =
false) {
4528 ps <<
"function" << PP::nbsp;
4530 ps <<
"automatic" << PP::nbsp;
4531 auto retType = op.getExplicitlyReturnedType();
4533 ps.invokeWithStringOS([&](
auto &os) {
4534 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4535 emitAsTwoStateType);
4541 emitter.emitPortList(
4545LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4546 auto parent = op->getParentOfType<sv::FuncOp>();
4548 return emitOutputLikeOp(op, ports);
4551LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4553 ps <<
"`include" << PP::nbsp;
4555 if (op.getStyle() == IncludeStyle::System)
4556 ps <<
"<" << op.getTarget() <<
">";
4558 ps <<
"\"" << op.getTarget() <<
"\"";
4560 emitLocationInfo(op.getLoc());
4561 setPendingNewline();
4565LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4568 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4572 if (
auto linkageName = importOp.getLinkageName())
4573 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4575 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4576 assert(op.isDeclaration() &&
"function must be a declaration");
4579 assert(state.pendingNewline);
4585LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4587 emitError(op,
"SV attributes emission is unimplemented for the op");
4590 SmallPtrSet<Operation *, 8> ops;
4593 ps.addCallback({op,
true});
4595 if (
auto fd = op.getFd())
4596 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4599 ps.addCallback({op,
false});
4600 emitLocationInfoAndNewLine(ops);
4604LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4606 emitError(op,
"SV attributes emission is unimplemented for the op");
4609 SmallPtrSet<Operation *, 8> ops;
4612 ps.addCallback({op,
true});
4614 ps.scopedBox(PP::ibox0, [&]() {
4615 emitExpression(op.getFd(), ops);
4617 ps <<
"," << PP::space;
4618 ps.writeQuotedEscaped(op.getFormatString());
4626 for (
auto operand : op.getSubstitutions()) {
4627 ps <<
"," << PP::space;
4628 emitExpression(operand, ops);
4632 ps.addCallback({op,
false});
4633 emitLocationInfoAndNewLine(ops);
4637LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4639 emitError(op,
"SV attributes emission is unimplemented for the op");
4642 SmallPtrSet<Operation *, 8> ops;
4647 StringRef
string = op.getFormatString();
4648 if (
string.ends_with(
"\n"))
4649 string =
string.drop_back();
4654 bool isFirst =
true;
4657 while (!
string.
empty()) {
4658 auto lhsRhs =
string.split(
'\n');
4662 ps << PP::end << PP::newline << PP::neverbox;
4666 emitTextWithSubstitutions(
4667 ps, lhsRhs.first, op,
4668 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4669 string = lhsRhs.second;
4674 emitLocationInfoAndNewLine(ops);
4679LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4681 emitError(op,
"SV attributes emission is unimplemented for the op");
4685 SmallPtrSet<Operation *, 8> ops;
4690 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4691 assert(macroOp &&
"Invalid IR");
4693 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4695 if (!op.getInputs().empty()) {
4697 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4698 emitExpression(val, ops, LowestPrecedence,
4704 emitLocationInfoAndNewLine(ops);
4710StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4711 std::optional<unsigned> verbosity) {
4713 emitError(op,
"SV attributes emission is unimplemented for the op");
4716 SmallPtrSet<Operation *, 8> ops;
4718 ps.addCallback({op,
true});
4720 if (verbosity && *verbosity != 1) {
4722 ps.addAsString(*verbosity);
4726 ps.addCallback({op,
false});
4727 emitLocationInfoAndNewLine(ops);
4731LogicalResult StmtEmitter::visitSV(StopOp op) {
4732 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4735LogicalResult StmtEmitter::visitSV(FinishOp op) {
4736 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4740LogicalResult StmtEmitter::visitSV(ExitOp op) {
4741 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4747StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4748 std::optional<unsigned> verbosity,
4749 StringAttr message, ValueRange operands) {
4751 emitError(op,
"SV attributes emission is unimplemented for the op");
4754 SmallPtrSet<Operation *, 8> ops;
4756 ps.addCallback({op,
true});
4762 if ((verbosity && *verbosity != 1) || message) {
4764 ps.scopedBox(PP::ibox0, [&]() {
4768 ps.addAsString(*verbosity);
4773 ps <<
"," << PP::space;
4774 ps.writeQuotedEscaped(message.getValue());
4776 for (
auto operand : operands) {
4777 ps <<
"," << PP::space;
4778 emitExpression(operand, ops);
4787 ps.addCallback({op,
false});
4788 emitLocationInfoAndNewLine(ops);
4792LogicalResult StmtEmitter::visitSV(FatalProceduralOp op) {
4793 return emitFatalMessageOp(op);
4796LogicalResult StmtEmitter::visitSV(FatalOp op) {
4797 return emitFatalMessageOp(op);
4800LogicalResult StmtEmitter::visitSV(ErrorProceduralOp op) {
4801 return emitNonfatalMessageOp(op,
"$error");
4804LogicalResult StmtEmitter::visitSV(WarningProceduralOp op) {
4805 return emitNonfatalMessageOp(op,
"$warning");
4808LogicalResult StmtEmitter::visitSV(InfoProceduralOp op) {
4809 return emitNonfatalMessageOp(op,
"$info");
4812LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4813 return emitNonfatalMessageOp(op,
"$error");
4816LogicalResult StmtEmitter::visitSV(WarningOp op) {
4817 return emitNonfatalMessageOp(op,
"$warning");
4820LogicalResult StmtEmitter::visitSV(InfoOp op) {
4821 return emitNonfatalMessageOp(op,
"$info");
4824LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4825 SmallPtrSet<Operation *, 8> ops({op});
4828 ps.addCallback({op,
true});
4830 switch (op.getBaseAttr().getValue()) {
4831 case MemBaseTypeAttr::MemBaseBin:
4834 case MemBaseTypeAttr::MemBaseHex:
4839 ps.scopedBox(PP::ibox0, [&]() {
4840 ps.writeQuotedEscaped(op.getFilename());
4841 ps <<
"," << PP::space;
4842 emitExpression(op.getDest(), ops);
4846 ps.addCallback({op,
false});
4847 emitLocationInfoAndNewLine(ops);
4851LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4852 emitSVAttributes(op);
4855 ps.addCallback({op,
true});
4856 ps <<
"generate" << PP::newline;
4858 setPendingNewline();
4859 emitStatementBlock(op.getBody().getBlocks().front());
4862 ps <<
"endgenerate";
4863 ps.addCallback({op,
false});
4864 setPendingNewline();
4868LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4869 emitSVAttributes(op);
4872 ps.addCallback({op,
true});
4874 ps.invokeWithStringOS([&](
auto &os) {
4875 emitter.printParamValue(
4876 op.getCond(), os, VerilogPrecedence::Selection,
4877 [&]() { return op->emitOpError(
"invalid case parameter"); });
4880 setPendingNewline();
4883 ArrayAttr
patterns = op.getCasePatterns();
4884 ArrayAttr caseNames = op.getCaseNames();
4885 MutableArrayRef<Region> regions = op.getCaseRegions();
4892 llvm::StringMap<size_t> nextGenIds;
4893 ps.scopedBox(PP::bbox2, [&]() {
4895 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4896 auto ®ion = regions[i];
4897 assert(region.hasOneBlock());
4898 Attribute patternAttr =
patterns[i];
4901 if (!isa<mlir::TypedAttr>(patternAttr))
4904 ps.invokeWithStringOS([&](
auto &os) {
4905 emitter.printParamValue(
4906 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4907 [&]() {
return op->emitOpError(
"invalid case value"); });
4910 StringRef legalName =
4911 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4914 setPendingNewline();
4915 emitStatementBlock(region.getBlocks().front());
4918 setPendingNewline();
4924 ps.addCallback({op,
false});
4925 setPendingNewline();
4929LogicalResult StmtEmitter::visitSV(
ForOp op) {
4930 emitSVAttributes(op);
4931 llvm::SmallPtrSet<Operation *, 8> ops;
4932 ps.addCallback({op,
true});
4934 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4937 ps.scopedBox(PP::cbox0, [&]() {
4941 ps <<
"logic" << PP::nbsp;
4942 ps.invokeWithStringOS([&](
auto &os) {
4943 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4948 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4953 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4954 [&]() { emitExpression(op.getUpperBound(), ops); },
4960 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4961 [&]() { emitExpression(op.getStep(), ops); },
4965 ps << PP::neverbreak;
4966 setPendingNewline();
4967 emitStatementBlock(op.getBody().getBlocks().front());
4970 ps.addCallback({op,
false});
4971 emitLocationInfoAndNewLine(ops);
4976void StmtEmitter::emitAssertionLabel(Operation *op) {
4977 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4983void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4984 SmallPtrSetImpl<Operation *> &ops,
4985 bool isConcurrent =
false) {
4988 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4989 ps.scopedBox(PP::ibox0, [&]() {
4990 ps.writeQuotedEscaped(message.getValue());
4992 for (
auto arg : args) {
4993 ps <<
"," << PP::space;
4994 emitExpression(arg, ops);
5000template <
typename Op>
5001LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
5003 emitError(op,
"SV attributes emission is unimplemented for the op");
5006 SmallPtrSet<Operation *, 8> ops;
5008 ps.addCallback({op,
true});
5009 ps.scopedBox(PP::ibox2, [&]() {
5010 emitAssertionLabel(op);
5011 ps.scopedBox(PP::cbox0, [&]() {
5013 switch (op.getDefer()) {
5014 case DeferAssert::Immediate:
5016 case DeferAssert::Observed:
5019 case DeferAssert::Final:
5024 ps.scopedBox(PP::ibox0, [&]() {
5025 emitExpression(op.getExpression(), ops);
5028 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
5032 ps.addCallback({op,
false});
5033 emitLocationInfoAndNewLine(ops);
5037LogicalResult StmtEmitter::visitSV(AssertOp op) {
5038 return emitImmediateAssertion(op,
PPExtString(
"assert"));
5041LogicalResult StmtEmitter::visitSV(AssumeOp op) {
5042 return emitImmediateAssertion(op,
PPExtString(
"assume"));
5045LogicalResult StmtEmitter::visitSV(CoverOp op) {
5046 return emitImmediateAssertion(op,
PPExtString(
"cover"));
5049template <
typename Op>
5050LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
5052 emitError(op,
"SV attributes emission is unimplemented for the op");
5055 SmallPtrSet<Operation *, 8> ops;
5057 ps.addCallback({op,
true});
5058 ps.scopedBox(PP::ibox2, [&]() {
5059 emitAssertionLabel(op);
5060 ps.scopedBox(PP::cbox0, [&]() {
5061 ps << opName << PP::nbsp <<
"property (";
5062 ps.scopedBox(PP::ibox0, [&]() {
5063 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5065 emitExpression(op.getClock(), ops);
5066 ps <<
")" << PP::space;
5067 emitExpression(op.getProperty(), ops);
5070 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5075 ps.addCallback({op,
false});
5076 emitLocationInfoAndNewLine(ops);
5080LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5081 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5084LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5085 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5088LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5089 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5094template <
typename Op>
5095LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5097 emitError(op,
"SV attributes emission is unimplemented for the op");
5107 Operation *parent = op->getParentOp();
5108 Value
property = op.getProperty();
5109 bool isTemporal = !
property.getType().isSignlessInteger(1);
5110 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5111 bool emitAsImmediate = !isTemporal && isProcedural;
5114 SmallPtrSet<Operation *, 8> ops;
5116 ps.addCallback({op,
true});
5117 ps.scopedBox(PP::ibox2, [&]() {
5119 emitAssertionLabel(op);
5121 ps.scopedBox(PP::cbox0, [&]() {
5122 if (emitAsImmediate)
5123 ps << opName <<
"(";
5125 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5127 Value clock = op.getClock();
5128 auto event = op.getEvent();
5130 ps.scopedBox(PP::ibox2, [&]() {
5131 PropertyEmitter(emitter, ops)
5132 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5135 ps.scopedBox(PP::ibox2, [&]() {
5136 PropertyEmitter(emitter, ops)
5137 .emitAssertPropertyBody(property, op.getDisable());
5142 ps.addCallback({op,
false});
5143 emitLocationInfoAndNewLine(ops);
5147LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5148 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5151LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5152 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5155LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5156 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5159LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5161 emitError(op,
"SV attributes emission is unimplemented for the op");
5164 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5165 .getMacroIdentifier());
5168 bool hasEmptyThen = op->getRegion(0).front().empty();
5170 ps <<
"`ifndef " << ident;
5172 ps <<
"`ifdef " << ident;
5174 SmallPtrSet<Operation *, 8> ops;
5176 emitLocationInfoAndNewLine(ops);
5179 emitStatementBlock(op->getRegion(0).front());
5181 if (!op->getRegion(1).empty()) {
5182 if (!hasEmptyThen) {
5184 ps <<
"`else // " << ident;
5185 setPendingNewline();
5187 emitStatementBlock(op->getRegion(1).front());
5194 setPendingNewline();
5202void StmtEmitter::emitBlockAsStatement(
5203 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5204 StringRef multiLineComment) {
5211 emitLocationInfoAndNewLine(locationOps);
5214 emitStatementBlock(*block);
5216 if (needsBeginEnd) {
5220 if (!multiLineComment.empty())
5221 ps <<
" // " << multiLineComment;
5222 setPendingNewline();
5226LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5228 for (
auto &op : ooop.getBody().front())
5233LogicalResult StmtEmitter::visitSV(IfOp op) {
5234 SmallPtrSet<Operation *, 8> ops;
5236 auto ifcondBox = PP::ibox2;
5238 emitSVAttributes(op);
5240 ps.addCallback({op,
true});
5241 ps <<
"if (" << ifcondBox;
5251 emitExpression(ifOp.getCond(), ops);
5252 ps << PP::end <<
")";
5253 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5255 if (!ifOp.hasElse())
5259 Block *elseBlock = ifOp.getElseBlock();
5261 if (!nestedElseIfOp) {
5266 emitBlockAsStatement(elseBlock, ops);
5272 ifOp = nestedElseIfOp;
5273 ps <<
"else if (" << ifcondBox;
5275 ps.addCallback({op,
false});
5280LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5281 emitSVAttributes(op);
5282 SmallPtrSet<Operation *, 8> ops;
5286 auto printEvent = [&](AlwaysOp::Condition cond) {
5287 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5288 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5290 ps.addCallback({op,
true});
5292 switch (op.getNumConditions()) {
5298 printEvent(op.getCondition(0));
5303 ps.scopedBox(PP::cbox0, [&]() {
5304 printEvent(op.getCondition(0));
5305 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5306 ps << PP::space <<
"or" << PP::space;
5307 printEvent(op.getCondition(i));
5316 std::string comment;
5317 if (op.getNumConditions() == 0) {
5318 comment =
"always @*";
5320 comment =
"always @(";
5323 [&](Attribute eventAttr) {
5324 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5325 comment += stringifyEventControl(event);
5327 [&]() { comment +=
", "; });
5331 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5332 ps.addCallback({op,
false});
5336LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5337 emitSVAttributes(op);
5338 SmallPtrSet<Operation *, 8> ops;
5342 ps.addCallback({op,
true});
5343 StringRef opString =
"always_comb";
5344 if (state.options.noAlwaysComb)
5345 opString =
"always @(*)";
5348 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5349 ps.addCallback({op,
false});
5353LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5354 emitSVAttributes(op);
5356 SmallPtrSet<Operation *, 8> ops;
5360 ps.addCallback({op,
true});
5361 ps <<
"always_ff @(";
5362 ps.scopedBox(PP::cbox0, [&]() {
5363 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5364 emitExpression(op.getClock(), ops);
5365 if (op.getResetStyle() == ResetType::AsyncReset) {
5366 ps << PP::nbsp <<
"or" << PP::space
5367 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5368 emitExpression(op.getReset(), ops);
5375 std::string comment;
5376 comment +=
"always_ff @(";
5377 comment += stringifyEventControl(op.getClockEdge());
5378 if (op.getResetStyle() == ResetType::AsyncReset) {
5380 comment += stringifyEventControl(*op.getResetEdge());
5384 if (op.getResetStyle() == ResetType::NoReset)
5385 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5388 emitLocationInfoAndNewLine(ops);
5389 ps.scopedBox(PP::bbox2, [&]() {
5395 if (op.getResetStyle() == ResetType::AsyncReset &&
5396 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5398 emitExpression(op.getReset(), ops);
5400 emitBlockAsStatement(op.getResetBlock(), ops);
5403 emitBlockAsStatement(op.getBodyBlock(), ops);
5408 ps <<
" // " << comment;
5409 setPendingNewline();
5411 ps.addCallback({op,
false});
5415LogicalResult StmtEmitter::visitSV(InitialOp op) {
5416 emitSVAttributes(op);
5417 SmallPtrSet<Operation *, 8> ops;
5420 ps.addCallback({op,
true});
5422 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5423 ps.addCallback({op,
false});
5427LogicalResult StmtEmitter::visitSV(CaseOp op) {
5428 emitSVAttributes(op);
5429 SmallPtrSet<Operation *, 8> ops, emptyOps;
5432 ps.addCallback({op,
true});
5433 if (op.getValidationQualifier() !=
5434 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5435 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5436 op.getValidationQualifier()))
5438 const char *opname =
nullptr;
5439 switch (op.getCaseStyle()) {
5440 case CaseStmtType::CaseStmt:
5443 case CaseStmtType::CaseXStmt:
5446 case CaseStmtType::CaseZStmt:
5450 ps << opname <<
" (";
5451 ps.scopedBox(PP::ibox0, [&]() {
5452 emitExpression(op.getCond(), ops);
5455 emitLocationInfoAndNewLine(ops);
5457 size_t caseValueIndex = 0;
5458 ps.scopedBox(PP::bbox2, [&]() {
5459 for (
auto &caseInfo : op.getCases()) {
5461 auto &
pattern = caseInfo.pattern;
5463 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5464 .Case<CaseBitPattern>([&](
auto bitPattern) {
5467 ps.invokeWithStringOS([&](
auto &os) {
5468 os << bitPattern->getWidth() <<
"'b";
5469 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5470 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5473 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5474 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5475 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5477 .Case<CaseExprPattern>([&](
auto) {
5478 emitExpression(op.getCaseValues()[caseValueIndex++], ops);
5480 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5481 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5484 emitBlockAsStatement(caseInfo.block, emptyOps);
5490 ps.addCallback({op,
false});
5491 emitLocationInfoAndNewLine(ops);
5495LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5496 bool doNotPrint = op.getDoNotPrint();
5497 if (doNotPrint && !state.options.emitBindComments)
5502 emitSVAttributes(op);
5504 ps.addCallback({op,
true});
5507 <<
"/* This instance is elsewhere emitted as a bind statement."
5510 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5511 "attributes. The attributes will not be emitted.";
5514 SmallPtrSet<Operation *, 8> ops;
5519 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5520 assert(moduleOp &&
"Invalid IR");
5524 if (!op.getParameters().empty()) {
5527 bool printed =
false;
5529 llvm::zip(op.getParameters(),
5530 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5531 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5532 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5534 if (param.getValue() == modParam.getValue())
5539 ps <<
" #(" << PP::bbox2 << PP::newline;
5542 ps <<
"," << PP::newline;
5546 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5548 ps.invokeWithStringOS([&](
auto &os) {
5549 emitter.printParamValue(param.getValue(), os, [&]() {
5550 return op->emitOpError(
"invalid instance parameter '")
5551 << param.getName().getValue() <<
"' value";
5557 ps << PP::end << PP::newline <<
")";
5564 SmallVector<Value> instPortValues(modPortInfo.size());
5565 op.getValues(instPortValues, modPortInfo);
5566 emitInstancePortList(op, modPortInfo, instPortValues);
5568 ps.addCallback({op,
false});
5569 emitLocationInfoAndNewLine(ops);
5574 setPendingNewline();
5579LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5581 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5582 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5587 Operation *defaultModuleOp =
5588 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5590 SmallVector<Value> instPortValues(modPortInfo.size());
5591 op.getValues(instPortValues, modPortInfo);
5592 emitInstancePortList(op, modPortInfo, instPortValues);
5594 SmallPtrSet<Operation *, 8> ops;
5596 ps.addCallback({op,
false});
5597 emitLocationInfoAndNewLine(ops);
5602void StmtEmitter::emitInstancePortList(Operation *op,
5604 ArrayRef<Value> instPortValues) {
5605 SmallPtrSet<Operation *, 8> ops;
5608 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5609 ModulePortInfo containingPortList(containingModule.getPortList());
5614 size_t maxNameLength = 0;
5615 for (
auto &elt : modPortInfo) {
5616 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5619 auto getWireForValue = [&](Value result) {
5620 return result.getUsers().begin()->getOperand(0);
5624 bool isFirst =
true;
5625 bool isZeroWidth =
false;
5627 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5629 auto &modPort = modPortInfo.
at(portNum);
5631 Value portVal = instPortValues[portNum];
5636 bool shouldPrintComma =
true;
5638 shouldPrintComma =
false;
5639 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5641 shouldPrintComma =
true;
5646 if (shouldPrintComma)
5649 emitLocationInfoAndNewLine(ops);
5664 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5665 auto modPortName = modPort.getVerilogName();
5667 ps.spaces(maxNameLength - modPortName.size() + 1);
5669 ps.scopedBox(PP::ibox0, [&]() {
5676 if (!modPort.isOutput()) {
5678 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5679 ps <<
"/* Zero width */";
5681 emitExpression(portVal, ops, LowestPrecedence);
5682 }
else if (portVal.use_empty()) {
5683 ps <<
"/* unused */";
5684 }
else if (portVal.hasOneUse() &&
5685 (output = dyn_cast_or_null<OutputOp>(
5686 portVal.getUses().begin()->getOwner()))) {
5691 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5693 containingPortList.atOutput(outputPortNo).getVerilogName());
5695 portVal = getWireForValue(portVal);
5696 emitExpression(portVal, ops);
5702 if (!isFirst || isZeroWidth) {
5703 emitLocationInfoAndNewLine(ops);
5716LogicalResult StmtEmitter::visitSV(BindOp op) {
5717 emitter.emitBind(op);
5718 assert(state.pendingNewline);
5722LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5723 emitComment(op.getCommentAttr());
5725 emitSVAttributes(op);
5728 ps.addCallback({op,
true});
5730 setPendingNewline();
5732 emitStatementBlock(*op.getBodyBlock());
5734 ps <<
"endinterface" << PP::newline;
5735 ps.addCallback({op,
false});
5736 setPendingNewline();
5741 emitSVAttributes(op);
5743 ps.addCallback({op,
true});
5745 ps << op.getContent();
5747 ps.addCallback({op,
false});
5748 setPendingNewline();
5752LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5754 emitSVAttributes(op);
5756 ps.addCallback({op,
true});
5758 ps << PP::neverbox <<
"// ";
5759 ps.invokeWithStringOS([&](
auto &os) {
5764 ps.invokeWithStringOS(
5765 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5769 ps.addCallback({op,
false});
5770 setPendingNewline();
5774LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5776 ps.addCallback({op,
true});
5780 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5781 auto port = cast<ModportStructAttr>(portAttr);
5782 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5783 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5784 ps << PPExtString(getSymOpName(signalDecl));
5788 ps.addCallback({op,
false});
5789 setPendingNewline();
5793LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5795 ps.addCallback({op,
true});
5796 SmallPtrSet<Operation *, 8> emitted;
5799 emitExpression(op.getIface(), emitted);
5800 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5801 emitExpression(op.getRhs(), emitted);
5803 ps.addCallback({op,
false});
5804 setPendingNewline();
5808LogicalResult StmtEmitter::visitSV(MacroErrorOp op) {
5810 ps <<
"`" << op.getMacroIdentifier();
5811 setPendingNewline();
5815LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5816 auto decl = op.getReferencedMacro(&state.symbolCache);
5819 ps.addCallback({op,
true});
5821 if (decl.getArgs()) {
5823 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5824 ps << cast<StringAttr>(name);
5828 if (!op.getFormatString().empty()) {
5830 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5833 ps.addCallback({op,
false});
5834 setPendingNewline();
5838void StmtEmitter::emitStatement(Operation *op) {
5845 if (isa_and_nonnull<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5849 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5850 succeeded(dispatchVerifVisitor(op)))
5853 emitOpError(op,
"emission to Verilog not supported");
5854 emitPendingNewlineIfNeeded();
5855 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5856 setPendingNewline();
5867 StmtEmitter &stmtEmitter) {
5874 if (isa<IfDefProceduralOp>(op->getParentOp()))
5882 SmallVector<Value, 8> exprsToScan(op->getOperands());
5887 while (!exprsToScan.empty()) {
5888 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5895 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5896 auto *defOp = readInout.getOperand().getDefiningOp();
5903 if (isa<sv::WireOp>(defOp))
5908 if (!isa<RegOp, LogicOp>(defOp))
5914 if (isa<LogicOp>(defOp) &&
5915 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5919 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5920 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5928 exprsToScan.append(expr->getOperands().begin(),
5929 expr->getOperands().end());
5935 if (expr->getBlock() != op->getBlock())
5940 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5947template <
class AssignTy>
5949 AssignTy singleAssign;
5950 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5951 if (hasSVAttributes(user))
5954 if (auto assign = dyn_cast<AssignTy>(user)) {
5957 singleAssign = assign;
5961 return isa<ReadInOutOp>(user);
5963 return singleAssign;
5969 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5973 if (op1->getBlock() != user->getBlock())
5979 return op1->isBeforeInBlock(user);
5983LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5984 emitSVAttributes(op);
5985 auto value = op->getResult(0);
5986 SmallPtrSet<Operation *, 8> opsForLocation;
5987 opsForLocation.insert(op);
5989 ps.addCallback({op,
true});
5992 auto type = value.getType();
5998 bool singleBitDefaultType = !isa<LocalParamOp>(op);
6000 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
6001 unsigned targetColumn = 0;
6002 unsigned column = 0;
6005 if (maxDeclNameWidth > 0)
6006 targetColumn += maxDeclNameWidth + 1;
6009 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
6010 }
else if (!word.empty()) {
6012 column += word.size();
6013 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
6014 ps.spaces(numSpaces);
6015 column += numSpaces;
6018 SmallString<8> typeString;
6021 llvm::raw_svector_ostream stringStream(typeString);
6024 true, singleBitDefaultType);
6027 if (maxTypeWidth > 0)
6028 targetColumn += maxTypeWidth + 1;
6029 unsigned numSpaces = 0;
6030 if (!typeString.empty()) {
6032 column += typeString.size();
6035 if (targetColumn > column)
6036 numSpaces = targetColumn - column;
6037 ps.spaces(numSpaces);
6038 column += numSpaces;
6044 ps.invokeWithStringOS(
6045 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
6048 if (state.options.printDebugInfo) {
6049 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
6050 auto innerSym = innerSymOp.getInnerSymAttr();
6051 if (innerSym && !innerSym.empty()) {
6053 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6059 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
6060 ps << PP::space <<
"=" << PP::space;
6061 ps.invokeWithStringOS([&](
auto &os) {
6062 emitter.printParamValue(localparam.getValue(), os, [&]() {
6063 return op->emitOpError(
"invalid localparam value");
6068 if (
auto regOp = dyn_cast<RegOp>(op)) {
6069 if (
auto initValue = regOp.getInit()) {
6070 ps << PP::space <<
"=" << PP::space;
6071 ps.scopedBox(PP::ibox0, [&]() {
6072 emitExpression(initValue, opsForLocation, LowestPrecedence,
6081 if (!state.options.disallowDeclAssignments && isa<sv::WireOp>(op) &&
6082 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
6085 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
6086 auto *source = singleAssign.getSrc().getDefiningOp();
6090 if (!source || isa<ConstantOp>(source) ||
6091 op->getNextNode() == singleAssign) {
6092 ps << PP::space <<
"=" << PP::space;
6093 ps.scopedBox(PP::ibox0, [&]() {
6094 emitExpression(singleAssign.getSrc(), opsForLocation,
6098 emitter.assignsInlined.insert(singleAssign);
6106 if (!state.options.disallowDeclAssignments && isa<LogicOp>(op) &&
6107 op->getParentOp()->hasTrait<ProceduralRegion>() &&
6110 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6113 auto *source = singleAssign.getSrc().getDefiningOp();
6117 if (!source || isa<ConstantOp>(source) ||
6120 ps << PP::space <<
"=" << PP::space;
6121 ps.scopedBox(PP::ibox0, [&]() {
6122 emitExpression(singleAssign.getSrc(), opsForLocation,
6127 emitter.assignsInlined.insert(singleAssign);
6128 emitter.expressionsEmittedIntoDecl.insert(op);
6135 ps.addCallback({op,
false});
6136 emitLocationInfoAndNewLine(opsForLocation);
6140void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6143 NameCollector collector(emitter);
6144 collector.collectNames(block);
6147 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6148 maxTypeWidth = collector.getMaxTypeWidth();
6151void StmtEmitter::emitStatementBlock(Block &body) {
6152 ps.scopedBox(PP::bbox2, [&]() {
6157 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6158 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6163 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6164 collectNamesAndCalculateDeclarationWidths(body);
6167 for (
auto &op : body) {
6174void ModuleEmitter::emitStatement(Operation *op) {
6175 StmtEmitter(*
this, state.options).emitStatement(op);
6180void ModuleEmitter::emitSVAttributes(Operation *op) {
6188 setPendingNewline();
6195void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6196 auto verilogName =
module.getVerilogModuleNameAttr();
6198 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6200 setPendingNewline();
6209void ModuleEmitter::emitBind(BindOp op) {
6211 emitError(op,
"SV attributes emission is unimplemented for the op");
6212 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6218 Operation *childMod =
6219 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6223 ps.addCallback({op,
true});
6224 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6225 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6227 bool isFirst =
true;
6228 ps.scopedBox(PP::bbox2, [&]() {
6229 auto parentPortInfo = parentMod.getPortList();
6233 size_t maxNameLength = 0;
6234 for (
auto &elt : childPortInfo) {
6235 auto portName = elt.getVerilogName();
6236 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6237 maxNameLength = std::max(maxNameLength, elt.getName().size());
6240 SmallVector<Value> instPortValues(childPortInfo.size());
6241 inst.getValues(instPortValues, childPortInfo);
6243 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6245 Value portVal = instPortValues[idx];
6251 bool shouldPrintComma =
true;
6253 shouldPrintComma =
false;
6254 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6256 shouldPrintComma =
true;
6261 if (shouldPrintComma)
6274 ps << PP::neverbox <<
"//";
6278 ps.nbsp(maxNameLength - elt.getName().size());
6280 llvm::SmallPtrSet<Operation *, 4> ops;
6281 if (elt.isOutput()) {
6282 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6283 "output port must have either single or no use");
6284 if (portVal.use_empty()) {
6285 ps <<
"/* unused */";
6286 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6287 portVal.getUses().begin()->getOwner())) {
6290 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6292 parentPortList.atOutput(outputPortNo).getVerilogName());
6294 portVal = portVal.getUsers().begin()->getOperand(0);
6295 ExprEmitter(*
this, ops)
6296 .emitExpression(portVal, LowestPrecedence,
6300 ExprEmitter(*
this, ops)
6301 .emitExpression(portVal, LowestPrecedence,
6314 ps.addCallback({op,
false});
6315 setPendingNewline();
6318void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6320 emitError(op,
"SV attributes emission is unimplemented for the op");
6322 auto instance = op.getReferencedInstance(&state.symbolCache);
6324 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6325 instance.getInterfaceType().getInterface());
6327 ps.addCallback({op,
true});
6328 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6329 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6331 ps.addCallback({op,
false});
6332 setPendingNewline();
6335void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6339 auto printParamType = [&](Type type, Attribute defaultValue,
6340 SmallString<8> &result) {
6342 llvm::raw_svector_ostream sstream(result);
6347 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6348 if (intAttr.getValue().getBitWidth() == 32)
6350 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6351 if (fpAttr.getType().isF64())
6354 if (isa<NoneType>(type))
6361 if (
auto intType = type_dyn_cast<IntegerType>(type))
6362 if (intType.getWidth() == 32) {
6363 sstream <<
"/*integer*/";
6367 printPackedType(type, sstream, module->getLoc(),
6375 size_t maxTypeWidth = 0;
6376 SmallString<8> scratch;
6377 for (
auto param : params) {
6378 auto paramAttr = cast<ParamDeclAttr>(param);
6380 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6381 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6384 if (maxTypeWidth > 0)
6387 ps.scopedBox(PP::bbox2, [&]() {
6388 ps << PP::newline <<
"#(";
6389 ps.scopedBox(PP::cbox0, [&]() {
6392 [&](Attribute param) {
6393 auto paramAttr = cast<ParamDeclAttr>(param);
6394 auto defaultValue = paramAttr.getValue();
6396 printParamType(paramAttr.getType(), defaultValue, scratch);
6397 if (!scratch.empty())
6399 if (scratch.size() < maxTypeWidth)
6400 ps.nbsp(maxTypeWidth - scratch.size());
6402 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6403 module, paramAttr.getName()));
6407 ps.invokeWithStringOS([&](
auto &os) {
6409 return module->emitError("parameter '")
6410 << paramAttr.getName().getValue()
6411 << "' has invalid value";
6416 [&]() { ps <<
"," << PP::newline; });
6422void ModuleEmitter::emitPortList(Operation *module,
6424 bool emitAsTwoStateType) {
6426 if (portInfo.
size())
6427 emitLocationInfo(module->getLoc());
6431 bool hasOutputs =
false, hasZeroWidth =
false;
6432 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6433 SmallVector<SmallString<8>, 16> portTypeStrings;
6435 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6436 auto port = portInfo.
at(i);
6440 lastNonZeroPort = i;
6443 portTypeStrings.push_back({});
6445 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6447 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6450 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6453 if (maxTypeWidth > 0)
6457 ps.scopedBox(PP::bbox2, [&]() {
6458 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6459 auto lastPort = e - 1;
6462 auto portType = portInfo.
at(portIdx).
type;
6466 bool isZeroWidth =
false;
6471 ps << (isZeroWidth ?
"// " :
" ");
6475 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6476 switch (thisPortDirection) {
6477 case ModulePort::Direction::Output:
6480 case ModulePort::Direction::Input:
6481 ps << (hasOutputs ?
"input " :
"input ");
6483 case ModulePort::Direction::InOut:
6484 ps << (hasOutputs ?
"inout " :
"inout ");
6487 bool emitWireInPorts = state.options.emitWireInPorts;
6488 if (emitWireInPorts)
6492 if (!portTypeStrings[portIdx].
empty())
6493 ps << portTypeStrings[portIdx];
6494 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6495 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6497 size_t startOfNamePos =
6498 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6504 ps.invokeWithStringOS(
6505 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6508 auto innerSym = portInfo.
at(portIdx).
getSym();
6509 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6511 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6516 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6520 if (
auto loc = portInfo.
at(portIdx).
loc)
6521 emitLocationInfo(loc);
6531 if (!state.options.disallowPortDeclSharing) {
6532 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6535 auto port = portInfo.
at(portIdx);
6539 bool isZeroWidth =
false;
6544 ps << (isZeroWidth ?
"// " :
" ");
6547 ps.nbsp(startOfNamePos);
6550 StringRef name = port.getVerilogName();
6554 ps.invokeWithStringOS(
6555 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6558 auto sym = port.getSym();
6559 if (state.options.printDebugInfo && sym && !sym.empty())
6560 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6564 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6568 if (
auto loc = port.loc)
6569 emitLocationInfo(loc);
6580 if (!portInfo.
size()) {
6582 SmallPtrSet<Operation *, 8> moduleOpSet;
6583 moduleOpSet.insert(module);
6584 emitLocationInfoAndNewLine(moduleOpSet);
6587 ps <<
");" << PP::newline;
6588 setPendingNewline();
6592void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6593 currentModuleOp =
module;
6595 emitComment(module.getCommentAttr());
6596 emitSVAttributes(module);
6598 ps.addCallback({module,
true});
6602 emitParameters(module, module.getParameters());
6606 assert(state.pendingNewline);
6609 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6612 ps.addCallback({module,
false});
6614 setPendingNewline();
6616 currentModuleOp =
nullptr;
6619void ModuleEmitter::emitFunc(FuncOp func) {
6621 if (func.isDeclaration())
6624 currentModuleOp = func;
6626 ps.addCallback({func,
true});
6630 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6632 ps <<
"endfunction";
6634 currentModuleOp =
nullptr;
6643 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6650 void emit(emit::FileListOp op);
6653 void emit(Block *block);
6655 void emitOp(emit::RefOp op);
6656 void emitOp(emit::VerbatimOp op);
6660 for (Operation &op : *block) {
6661 TypeSwitch<Operation *>(&op)
6662 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6663 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6664 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6665 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6666 .Case<BindInterfaceOp>(
6667 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6668 .Case<TypeScopeOp>([&](
auto typedecls) {
6669 ModuleEmitter(state).emitStatement(typedecls);
6672 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6678 for (
auto sym : op.getFiles()) {
6679 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6681 auto it = state.fileMapping.find(fileName);
6682 if (it == state.fileMapping.end()) {
6683 emitOpError(op,
" references an invalid file: ") << sym;
6687 auto file = cast<emit::FileOp>(it->second);
6688 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6695 StringAttr target = op.getTargetAttr().getAttr();
6696 auto *targetOp = state.symbolCache.getDefinition(target);
6697 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6699 TypeSwitch<Operation *>(targetOp)
6700 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6701 .Case<hw::HWModuleOp>(
6702 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6703 .Case<TypeScopeOp>([&](
auto typedecls) {
6704 ModuleEmitter(state).emitStatement(typedecls);
6707 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6713 SmallPtrSet<Operation *, 8> ops;
6718 StringRef text = op.getText();
6722 const auto &[lhs, rhs] = text.split(
'\n');
6726 ps << PP::end << PP::newline << PP::neverbox;
6728 }
while (!text.empty());
6731 emitLocationInfoAndNewLine(ops);
6749 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6750 moduleOp->walk([&](Operation *op) {
6752 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6755 SymbolTable::getSymbolAttrName()),
6756 name.getSymName(), op);
6757 if (isa<BindOp>(op))
6763 auto collectPorts = [&](
auto moduleOp) {
6764 auto portInfo = moduleOp.getPortList();
6765 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6766 if (!p.attrs || p.attrs.empty())
6768 for (NamedAttribute portAttr : p.attrs) {
6769 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6778 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6779 for (
auto file :
designOp.getOps<emit::FileOp>())
6780 for (
auto refs : file.getOps<emit::RefOp>())
6781 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6783 SmallString<32> outputPath;
6784 for (
auto &op : *
designOp.getBody()) {
6787 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6789 bool hasFileName =
false;
6790 bool emitReplicatedOps = !isFileOp;
6791 bool addToFilelist = !isFileOp;
6797 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6799 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6800 <<
" on " << op <<
"\n";);
6801 if (!attr.isDirectory())
6804 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6805 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6808 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6813 if (!defaultFileName.isTriviallyEmpty()) {
6814 llvm::sys::path::append(outputPath, defaultFileName);
6816 op->emitError(
"file name unspecified");
6818 llvm::sys::path::append(outputPath,
"error.out");
6822 auto destFile = StringAttr::get(op->getContext(), outputPath);
6823 auto &file =
files[destFile];
6824 file.ops.push_back(info);
6825 file.emitReplicatedOps = emitReplicatedOps;
6826 file.addToFilelist = addToFilelist;
6827 file.isVerilog = outputPath.ends_with(
".sv");
6832 if (!attr || attr.isDirectory()) {
6833 auto excludeFromFileListAttr =
6834 BoolAttr::get(op->getContext(), !addToFilelist);
6835 auto includeReplicatedOpsAttr =
6836 BoolAttr::get(op->getContext(), emitReplicatedOps);
6837 auto outputFileAttr = hw::OutputFileAttr::get(
6838 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6839 op->setAttr(
"output_file", outputFileAttr);
6845 TypeSwitch<Operation *>(&op)
6846 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6848 fileMapping.try_emplace(file.getSymNameAttr(), file);
6849 separateFile(file, file.getFileName());
6851 .Case<emit::FragmentOp>([&](
auto fragment) {
6854 .Case<HWModuleOp>([&](
auto mod) {
6856 auto sym = mod.getNameAttr();
6859 collectInstanceSymbolsAndBinds(mod);
6861 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6862 if (it->second.size() != 1 || attr) {
6865 op.emitError(
"modules can be emitted to a single file");
6873 if (attr || separateModules)
6879 .Case<InterfaceOp>([&](InterfaceOp intf) {
6884 for (
auto &op : *intf.getBodyBlock())
6885 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6886 if (
auto name = symOp.getNameAttr())
6890 if (attr || separateModules)
6891 separateFile(intf, intf.getSymName() +
".sv");
6897 separateFile(op, op.getOutputFile().getFilename().getValue());
6899 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
6905 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6906 [&](Operation *op) {
6912 separateFile(op,
"");
6914 .Case<FuncOp>([&](
auto op) {
6920 separateFile(op,
"");
6924 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6927 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6936 separateFile(op,
"");
6938 .Case<BindOp>([&](
auto op) {
6940 separateFile(op,
"bindfile.sv");
6945 .Case<MacroErrorOp>([&](
auto op) {
replicatedOps.push_back(op); })
6946 .Case<MacroDeclOp>([&](
auto op) {
6949 .Case<sv::ReserveNamesOp>([](
auto op) {
6952 .Case<om::ClassLike>([&](
auto op) {
6955 .Case<om::ConstantOp>([&](
auto op) {
6958 .Default([&](
auto *) {
6959 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6979 size_t lastReplicatedOp = 0;
6981 bool emitHeaderInclude =
6984 if (emitHeaderInclude)
6987 size_t numReplicatedOps =
6992 DenseSet<emit::FragmentOp> includedFragments;
6993 for (
const auto &opInfo : file.
ops) {
6994 Operation *op = opInfo.op;
6998 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
7004 if (
auto fragments =
7006 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
7010 op->emitError(
"cannot find referenced fragment ") << sym;
7013 emit::FragmentOp fragment = it->second;
7014 if (includedFragments.insert(fragment).second) {
7015 thingsToEmit.emplace_back(it->second);
7021 thingsToEmit.emplace_back(op);
7026 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
7031 TypeSwitch<Operation *>(op)
7032 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
7033 .Case<HWModuleExternOp, sv::SVVerbatimModuleOp>([&](
auto op) {
7036 .Case<HWModuleGeneratedOp>(
7037 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
7038 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
7039 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
7040 .Case<InterfaceOp, VerbatimOp, IfDefOp, sv::SVVerbatimSourceOp>(
7041 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7042 .Case<TypeScopeOp>([&](
auto typedecls) {
7043 ModuleEmitter(state).emitStatement(typedecls);
7045 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
7047 .Case<MacroErrorOp, MacroDefOp, FuncDPIImportOp>(
7048 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7049 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
7050 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
7051 .Default([&](
auto *op) {
7052 state.encounteredError =
true;
7053 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
7060 llvm::formatted_raw_ostream &os,
7061 StringAttr fileName,
bool parallelize) {
7066 parallelize &=
context->isMultithreadingEnabled();
7077 size_t lineOffset = 0;
7078 for (
auto &entry : thingsToEmit) {
7079 entry.verilogLocs.setStream(os);
7080 if (
auto *op = entry.getOperation()) {
7085 state.addVerilogLocToOps(lineOffset, fileName);
7087 os << entry.getStringData();
7092 if (state.encounteredError)
7110 SmallString<256> buffer;
7111 llvm::raw_svector_ostream tmpStream(buffer);
7112 llvm::formatted_raw_ostream rs(tmpStream);
7123 for (
auto &entry : thingsToEmit) {
7126 auto *op = entry.getOperation();
7128 auto lineOffset = os.getLine() + 1;
7129 os << entry.getStringData();
7133 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName,
context);
7136 entry.verilogLocs.setStream(os);
7143 state.addVerilogLocToOps(0, fileName);
7159 module.emitWarning()
7160 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7161 "created only at SplitExportVerilog";
7170 for (
const auto &it : emitter.
files) {
7171 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7172 "\" ----- 8< -----\n\n");
7178 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7179 "\" ----- 8< -----\n\n");
7180 for (
auto &name : it.second)
7181 contents += name.str() +
"\n";
7182 list.emplace_back(contents);
7185 llvm::formatted_raw_ostream rs(os);
7189 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7198 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7200 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7201 if (failed(failableParallelForEach(
7202 module->getContext(), modulesToPrepare,
7203 [&](
auto op) { return prepareHWModule(op, options); })))
7210struct ExportVerilogPass
7211 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7212 ExportVerilogPass(raw_ostream &os) : os(os) {}
7213 void runOnOperation()
override {
7215 mlir::OpPassManager preparePM(
"builtin.module");
7216 preparePM.addPass(createLegalizeAnonEnums());
7218 auto &modulePM = preparePM.nestAny();
7219 modulePM.addPass(createPrepareForEmission());
7220 if (failed(runPipeline(preparePM, getOperation())))
7221 return signalPassFailure();
7224 return signalPassFailure();
7231struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7232 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7233 : ExportVerilogPass{*os} {
7234 owned = std::move(os);
7238 std::unique_ptr<llvm::raw_ostream> owned;
7242std::unique_ptr<mlir::Pass>
7244 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7247std::unique_ptr<mlir::Pass>
7249 return std::make_unique<ExportVerilogPass>(os);
7260static std::unique_ptr<llvm::ToolOutputFile>
7264 SmallString<128> outputFilename(dirname);
7266 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7269 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7271 emitter.
designOp.emitError(
"cannot create output directory \"")
7272 << outputDir <<
"\": " << error.message();
7278 std::string errorMessage;
7279 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7281 emitter.
designOp.emitError(errorMessage);
7298 llvm::formatted_raw_ostream rs(output->os());
7304 StringAttr::get(fileName.getContext(), output->getFilename()),
7310 StringRef dirname) {
7321 bool insertSuccess =
7323 .insert({StringAttr::get(module.getContext(),
circtHeader),
7329 if (!insertSuccess) {
7330 module.emitError() << "tried to emit a heder to " << circtHeader
7331 << ", but the file is used as an output too.";
7337 parallelForEach(module->getContext(), emitter.
files.begin(),
7338 emitter.
files.end(), [&](
auto &it) {
7339 createSplitOutputFile(it.first, it.second, dirname,
7344 SmallString<128> filelistPath(dirname);
7345 llvm::sys::path::append(filelistPath,
"filelist.f");
7347 std::string errorMessage;
7348 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7350 module->emitError(errorMessage);
7354 for (
const auto &it : emitter.
files) {
7355 if (it.second.addToFilelist)
7356 output->os() << it.first.str() <<
"\n";
7365 for (
auto &name : it.second)
7366 output->os() << name.str() <<
"\n";
7377 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7379 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7380 if (failed(failableParallelForEach(
7381 module->getContext(), modulesToPrepare,
7382 [&](
auto op) { return prepareHWModule(op, options); })))
7390struct ExportSplitVerilogPass
7391 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7392 ExportSplitVerilogPass(StringRef directory) {
7393 directoryName = directory.str();
7395 void runOnOperation()
override {
7397 mlir::OpPassManager preparePM(
"builtin.module");
7401 modulePM.addPass(createPrepareForEmission());
7402 if (failed(runPipeline(preparePM, getOperation())))
7403 return signalPassFailure();
7406 return signalPassFailure();
7411std::unique_ptr<mlir::Pass>
7413 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static bool hasSVAttributes(Operation *op)
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static 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 std::unique_ptr< Context > context
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
RewritePatternSet pattern
static InstancePath empty
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
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.