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)
199 .Case<InterfaceOp>([&](InterfaceOp op) {
202 .Case<InterfaceSignalOp>(
203 [&](InterfaceSignalOp op) {
return op.getSymName(); })
204 .Case<InterfaceModportOp>(
205 [&](InterfaceModportOp op) {
return op.getSymName(); })
206 .Default([&](Operation *op) {
207 if (
auto attr = op->getAttrOfType<StringAttr>(
"name"))
208 return attr.getValue();
209 if (
auto attr = op->getAttrOfType<StringAttr>(
"instanceName"))
210 return attr.getValue();
211 if (
auto attr = op->getAttrOfType<StringAttr>(
"sv.namehint"))
212 return attr.getValue();
214 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
215 return attr.getValue();
216 return StringRef(
"");
221template <
typename PPS>
223 os <<
"/*Zero width*/ 1\'b0";
228 auto hml = cast<HWModuleLike>(module);
229 return hml.getPort(portArgNum).getVerilogName();
234 auto hml = cast<HWModuleLike>(module);
235 auto pId = hml.getHWModuleType().getPortIdForInputId(portArgNum);
236 if (
auto attrs = dyn_cast_or_null<DictionaryAttr>(hml.getPortAttrs(pId)))
237 if (
auto updatedName = attrs.getAs<StringAttr>(
"hw.verilogName"))
238 return updatedName.getValue();
239 return hml.getHWModuleType().getPortName(pId);
248 if (isa<
ReadInOutOp, AggregateConstantOp, ArrayIndexInOutOp,
249 IndexedPartSelectInOutOp, StructFieldInOutOp, IndexedPartSelectOp,
250 ParamValueOp, XMROp, XMRRefOp, SampledOp, EnumConstantOp, SFormatFOp,
251 SystemFunctionOp, STimeOp, TimeOp, UnpackedArrayCreateOp,
252 UnpackedOpenArrayCastOp>(op))
256 if (isa<verif::ContractOp>(op))
266static void getTypeDims(SmallVectorImpl<Attribute> &dims, Type type,
268 if (
auto integer = hw::type_dyn_cast<IntegerType>(type)) {
269 if (integer.getWidth() != 1)
270 dims.push_back(
getInt32Attr(type.getContext(), integer.getWidth()));
273 if (
auto array = hw::type_dyn_cast<ArrayType>(type)) {
274 dims.push_back(
getInt32Attr(type.getContext(), array.getNumElements()));
279 if (
auto intType = hw::type_dyn_cast<IntType>(type)) {
280 dims.push_back(intType.getWidth());
284 if (
auto inout = hw::type_dyn_cast<InOutType>(type))
285 return getTypeDims(dims, inout.getElementType(), loc);
286 if (
auto uarray = hw::type_dyn_cast<hw::UnpackedArrayType>(type))
287 return getTypeDims(dims, uarray.getElementType(), loc);
288 if (
auto uarray = hw::type_dyn_cast<sv::UnpackedOpenArrayType>(type))
289 return getTypeDims(dims, uarray.getElementType(), loc);
291 if (hw::type_isa<InterfaceType, StructType, EnumType>(type))
294 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
300 SmallVector<Attribute, 4> aDims;
303 SmallVector<Attribute, 4> bDims;
306 return aDims == bDims;
312 if (
auto intType = dyn_cast<IntegerType>(type))
313 return intType.getWidth() == 0;
314 if (
auto inout = dyn_cast<hw::InOutType>(type))
316 if (
auto uarray = dyn_cast<hw::UnpackedArrayType>(type))
317 return uarray.getNumElements() == 0 ||
319 if (
auto array = dyn_cast<hw::ArrayType>(type))
320 return array.getNumElements() == 0 ||
isZeroBitType(array.getElementType());
321 if (
auto structType = dyn_cast<hw::StructType>(type))
322 return llvm::all_of(structType.getElements(),
323 [](
auto elem) { return isZeroBitType(elem.type); });
324 if (
auto enumType = dyn_cast<hw::EnumType>(type))
325 return enumType.getFields().empty();
326 if (
auto unionType = dyn_cast<hw::UnionType>(type))
327 return hw::getBitWidth(unionType) == 0;
339 return TypeSwitch<Type, Type>(type)
340 .Case<InOutType>([](InOutType inoutType) {
343 .Case<UnpackedArrayType, sv::UnpackedOpenArrayType>([](
auto arrayType) {
346 .Default([](Type type) {
return type; });
351 assert(isa<hw::InOutType>(type) &&
"inout type is expected");
352 auto elementType = cast<hw::InOutType>(type).getElementType();
358 return TypeSwitch<Type, bool>(type)
359 .Case<InOutType, UnpackedArrayType, ArrayType>([](
auto parentType) {
362 .Case<StructType>([](
auto) {
return true; })
363 .Default([](
auto) {
return false; });
377 if (
auto name = lhs.getName().compare(rhs.getName()))
379 return compareLocs(lhs.getChildLoc(), rhs.getChildLoc());
384 if (
auto fn = lhs.getFilename().compare(rhs.getFilename()))
386 if (lhs.getLine() != rhs.getLine())
387 return lhs.getLine() < rhs.getLine() ? -1 : 1;
388 return lhs.getColumn() < rhs.getColumn() ? -1 : 1;
393 Location lhsCallee = lhs.getCallee();
394 Location rhsCallee = rhs.getCallee();
398 Location lhsCaller = lhs.getCaller();
399 Location rhsCaller = rhs.getCaller();
403template <
typename TTargetLoc>
405 auto lhsT = dyn_cast<TTargetLoc>(lhs);
406 auto rhsT = dyn_cast<TTargetLoc>(rhs);
433 if (
auto res = dispatchCompareLocations<mlir::FileLineColLoc>(lhs, rhs);
438 if (
auto res = dispatchCompareLocations<mlir::NameLoc>(lhs, rhs);
443 if (
auto res = dispatchCompareLocations<mlir::CallSiteLoc>(lhs, rhs);
460 SmallPtrSetImpl<Attribute> &locationSet) {
461 llvm::TypeSwitch<Location, void>(loc)
462 .Case<FusedLoc>([&](
auto fusedLoc) {
463 for (
auto subLoc : fusedLoc.getLocations())
466 .Default([&](
auto loc) { locationSet.insert(loc); });
470template <
typename TVector>
472 llvm::array_pod_sort(
473 vec.begin(), vec.end(), [](
const auto *lhs,
const auto *rhs) ->
int {
474 return compareLocs(cast<Location>(*lhs), cast<Location>(*rhs));
482 SmallPtrSet<Attribute, 8> locationSet;
483 locationSet.insert(loc);
484 llvm::raw_string_ostream os(
output);
490 const SmallPtrSetImpl<Operation *> &ops) {
494 SmallPtrSet<Attribute, 8> locationSet;
497 llvm::raw_string_ostream os(
output);
506 const SmallPtrSetImpl<Attribute> &locationSet) {
507 if (style == LoweringOptions::LocationInfoStyle::None)
510 llvm::raw_string_ostream sstr(resstr);
512 if (resstr.empty() || style == LoweringOptions::LocationInfoStyle::Plain) {
516 assert(style == LoweringOptions::LocationInfoStyle::WrapInAtSquareBracket &&
517 "other styles must be already handled");
518 os <<
"@[" << resstr <<
"]";
527 const SmallPtrSetImpl<Attribute> &locationSet)
543 bool withName = !loc.getName().empty();
545 os <<
"'" << loc.getName().strref() <<
"'(";
554 os << loc.getFilename().getValue();
555 if (
auto line = loc.getLine()) {
557 if (
auto col = loc.getColumn())
569 StringRef lastFileName;
570 for (
size_t i = 0, e = locVector.size(); i != e;) {
575 auto first = locVector[i];
576 if (first.getFilename() != lastFileName) {
577 lastFileName = first.getFilename();
584 first.getFilename() == locVector[
end].getFilename() &&
585 first.getLine() == locVector[
end].getLine())
590 if (
auto line = first.getLine()) {
592 if (
auto col = first.getColumn())
600 os <<
':' << first.getLine() <<
":{";
602 os << locVector[i++].getColumn();
614 llvm::TypeSwitch<Location, void>(loc)
615 .Case<mlir::CallSiteLoc, mlir::NameLoc, mlir::FileLineColLoc>(
617 .Case<mlir::FusedLoc>([&](
auto loc) {
618 SmallPtrSet<Attribute, 8> locationSet;
622 .Default([&](
auto loc) {
634 switch (locationSet.size()) {
645 SmallVector<FileLineColLoc, 8> flcLocs;
646 SmallVector<Attribute, 8> otherLocs;
647 flcLocs.reserve(locationSet.size());
648 otherLocs.reserve(locationSet.size());
649 for (Attribute loc : locationSet) {
650 if (
auto flcLoc = dyn_cast<FileLineColLoc>(loc))
651 flcLocs.push_back(flcLoc);
653 otherLocs.push_back(loc);
664 size_t sstrSize =
os.tell();
665 bool emittedAnything =
false;
666 auto recheckEmittedSomething = [&]() {
667 size_t currSize =
os.tell();
668 bool emittedSomethingSinceLastCheck = currSize != sstrSize;
669 emittedAnything |= emittedSomethingSinceLastCheck;
671 return emittedSomethingSinceLastCheck;
680 if (recheckEmittedSomething()) {
682 recheckEmittedSomething();
688 if (emittedAnything && !flcLocs.empty())
693 llvm::raw_string_ostream &
os;
705 if (isa<BlockArgument>(v))
714 if (isa_and_nonnull<StructExtractOp, UnionExtractOp, ArrayGetOp>(
719 if (v.getDefiningOp<ReadInterfaceSignalOp>())
732 if (
auto cast = dyn_cast<BitcastOp>(op))
733 if (!
haveMatchingDims(cast.getInput().getType(), cast.getResult().getType(),
737 if (op->hasOneUse() &&
738 isa<comb::ConcatOp, hw::ArrayConcatOp>(*op->getUsers().begin()))
746 if (isa<StructCreateOp, UnionCreateOp, UnpackedArrayCreateOp>(op))
751 if (
auto aggConstantOp = dyn_cast<AggregateConstantOp>(op))
755 if (
auto verbatim = dyn_cast<VerbatimExprOp>(op))
756 if (verbatim.getFormatString().size() > 32)
761 for (
auto &use : op->getUses()) {
762 auto *user = use.getOwner();
772 UnionExtractOp, IndexedPartSelectOp>(user))
773 if (use.getOperandNumber() == 0 &&
784 auto usedInExprControl = [user, &use]() {
785 return TypeSwitch<Operation *, bool>(user)
786 .Case<ltl::ClockOp>([&](
auto clockOp) {
788 return clockOp.getClock() == use.get();
790 .Case<sv::AssertConcurrentOp, sv::AssumeConcurrentOp,
791 sv::CoverConcurrentOp>(
792 [&](
auto op) {
return op.getClock() == use.get(); })
793 .Case<sv::AssertPropertyOp, sv::AssumePropertyOp,
794 sv::CoverPropertyOp>([&](
auto op) {
795 return op.getDisable() == use.get() || op.getClock() == use.get();
797 .Case<AlwaysOp, AlwaysFFOp>([](
auto) {
802 .Default([](
auto) {
return false; });
805 if (!usedInExprControl())
809 auto read = dyn_cast<ReadInOutOp>(op);
812 if (!isa_and_nonnull<sv::WireOp, RegOp>(read.getInput().getDefiningOp()))
823 unsigned numStatements = 0;
824 block.walk([&](Operation *op) {
826 return WalkResult::advance();
828 TypeSwitch<Operation *, unsigned>(op)
829 .Case<VerbatimOp>([&](
auto) {
835 .Case<IfOp>([&](
auto) {
846 .Case<IfDefOp, IfDefProceduralOp>([&](
auto) {
return 3; })
847 .Case<OutputOp>([&](OutputOp oop) {
850 return llvm::count_if(oop->getOperands(), [&](
auto operand) {
851 Operation *op = operand.getDefiningOp();
852 return !operand.hasOneUse() || !op || !isa<HWInstanceLike>(op);
855 .Default([](
auto) {
return 1; });
856 if (numStatements > 1)
857 return WalkResult::interrupt();
858 return WalkResult::advance();
860 if (numStatements == 0)
862 if (numStatements == 1)
872 if (op->getResult(0).use_empty())
877 if (op->hasOneUse() &&
878 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, sv::PAssignOp>(
879 *op->getUsers().begin()))
901 for (
auto &op : *elseBlock) {
902 if (
auto opIf = dyn_cast<IfOp>(op)) {
919template <
typename PPS>
921 enum Container { NoContainer, InComment, InAttr };
922 Container currentContainer = NoContainer;
924 auto closeContainer = [&] {
925 if (currentContainer == NoContainer)
927 if (currentContainer == InComment)
929 else if (currentContainer == InAttr)
931 ps << PP::end << PP::end;
933 currentContainer = NoContainer;
936 bool isFirstContainer =
true;
937 auto openContainer = [&](Container newContainer) {
938 assert(newContainer != NoContainer);
939 if (currentContainer == newContainer)
943 if (!isFirstContainer)
944 ps << (mayBreak ? PP::space : PP::nbsp);
945 isFirstContainer =
false;
948 if (newContainer == InComment)
950 else if (newContainer == InAttr)
952 currentContainer = newContainer;
960 ps.scopedBox(PP::cbox0, [&]() {
961 for (
auto attr : attrs.getAsRange<SVAttributeAttr>()) {
962 if (!openContainer(attr.getEmitAsComment().getValue() ? InComment
964 ps <<
"," << (mayBreak ? PP::space : PP::nbsp);
966 if (attr.getExpression())
967 ps <<
" = " <<
PPExtString(attr.getExpression().getValue());
976 if (
auto *op = val.getDefiningOp())
979 if (
auto port = dyn_cast<BlockArgument>(val)) {
981 if (
auto forOp = dyn_cast<ForOp>(port.getParentBlock()->getParentOp()))
982 return forOp->getAttrOfType<StringAttr>(
"hw.verilogName");
984 port.getArgNumber());
986 assert(
false &&
"unhandled value");
998class VerilogEmitterState {
1000 explicit VerilogEmitterState(ModuleOp designOp,
1006 llvm::formatted_raw_ostream &os,
1007 StringAttr fileName,
OpLocMap &verilogLocMap)
1008 : designOp(designOp), shared(shared), options(options),
1009 symbolCache(symbolCache), globalNames(globalNames),
1010 fileMapping(fileMapping), os(os), verilogLocMap(verilogLocMap),
1011 pp(os, options.emittedLineLength), fileName(fileName) {
1012 pp.setListener(&saver);
1035 llvm::formatted_raw_ostream &os;
1037 bool encounteredError =
false;
1046 bool pendingNewline =
false;
1060 StringAttr fileName;
1066 void addVerilogLocToOps(
unsigned int lineOffset, StringAttr fileName) {
1069 verilogLocMap.
clear();
1073 VerilogEmitterState(
const VerilogEmitterState &) =
delete;
1074 void operator=(
const VerilogEmitterState &) =
delete;
1087using CallbackDataTy = std::pair<Operation *, bool>;
1091 VerilogEmitterState &state;
1096 explicit EmitterBase(VerilogEmitterState &state)
1098 ps(state.pp, state.saver, state.options.emitVerilogLocations) {}
1100 InFlightDiagnostic emitError(Operation *op,
const Twine &message) {
1101 state.encounteredError =
true;
1102 return op->emitError(message);
1105 InFlightDiagnostic emitOpError(Operation *op,
const Twine &message) {
1106 state.encounteredError =
true;
1107 return op->emitOpError(message);
1110 void emitLocationImpl(llvm::StringRef location) {
1113 ps << PP::neverbreak;
1114 if (!location.empty())
1115 ps <<
"\t// " << location;
1118 void emitLocationInfo(Location loc) {
1126 void emitLocationInfoAndNewLine(
const SmallPtrSetImpl<Operation *> &ops) {
1129 setPendingNewline();
1132 template <
typename PPS>
1133 void emitTextWithSubstitutions(PPS &ps, StringRef
string, Operation *op,
1134 llvm::function_ref<
void(Value)> operandEmitter,
1135 ArrayAttr symAttrs);
1141 void emitComment(StringAttr comment);
1145 void emitPendingNewlineIfNeeded() {
1146 if (state.pendingNewline) {
1147 state.pendingNewline =
false;
1151 void setPendingNewline() {
1152 assert(!state.pendingNewline);
1153 state.pendingNewline =
true;
1156 void startStatement() { emitPendingNewlineIfNeeded(); }
1159 void operator=(
const EmitterBase &) =
delete;
1160 EmitterBase(
const EmitterBase &) =
delete;
1164template <
typename PPS>
1165void EmitterBase::emitTextWithSubstitutions(
1166 PPS &ps, StringRef
string, Operation *op,
1167 llvm::function_ref<
void(Value)> operandEmitter, ArrayAttr symAttrs) {
1178 if (
auto *itemOp = item.getOp()) {
1179 if (item.hasPort()) {
1183 if (!symOpName.empty())
1185 emitError(itemOp,
"cannot get name for symbol ") << sym;
1187 emitError(op,
"cannot get name for symbol ") << sym;
1189 return StringRef(
"<INVALID>");
1195 unsigned numSymOps = symAttrs.size();
1196 auto emitUntilSubstitution = [&](
size_t next = 0) ->
bool {
1199 next =
string.find(
"{{", next);
1200 if (next == StringRef::npos)
1207 while (next <
string.size() &&
isdigit(
string[next]))
1210 if (start == next) {
1214 size_t operandNoLength = next - start;
1217 StringRef fmtOptsStr;
1218 if (
string[next] ==
':') {
1219 size_t startFmtOpts = next + 1;
1220 while (next <
string.size() &&
string[next] !=
'}')
1222 fmtOptsStr =
string.substr(startFmtOpts, next - startFmtOpts);
1226 if (!
string.substr(next).starts_with(
"}}"))
1230 unsigned operandNo = 0;
1231 if (
string.drop_front(start)
1232 .take_front(operandNoLength)
1233 .getAsInteger(10, operandNo)) {
1234 emitError(op,
"operand substitution too large");
1240 auto before =
string.take_front(start - 2);
1241 if (!before.empty())
1246 if (operandNo < op->getNumOperands())
1248 operandEmitter(op->getOperand(operandNo));
1249 else if ((operandNo - op->getNumOperands()) < numSymOps) {
1250 unsigned symOpNum = operandNo - op->getNumOperands();
1251 auto sym = symAttrs[symOpNum];
1252 StringRef symVerilogName;
1253 if (
auto fsym = dyn_cast<FlatSymbolRefAttr>(sym)) {
1254 if (
auto *symOp = state.symbolCache.getDefinition(fsym)) {
1255 if (
auto globalRef = dyn_cast<HierPathOp>(symOp)) {
1256 auto namepath = globalRef.getNamepathAttr().getValue();
1257 for (
auto [index, sym] :
llvm::enumerate(namepath)) {
1260 ps << (fmtOptsStr.empty() ?
"." : fmtOptsStr);
1262 auto innerRef = cast<InnerRefAttr>(sym);
1263 auto ref = state.symbolCache.getInnerDefinition(
1264 innerRef.getModule(), innerRef.getName());
1265 ps << namify(innerRef, ref);
1268 symVerilogName = namify(sym, symOp);
1271 }
else if (
auto isym = dyn_cast<InnerRefAttr>(sym)) {
1272 auto symOp = state.symbolCache.getInnerDefinition(isym.getModule(),
1274 symVerilogName = namify(sym, symOp);
1276 if (!symVerilogName.empty())
1279 emitError(op,
"operand " + llvm::utostr(operandNo) +
" isn't valid");
1283 string =
string.drop_front(next);
1289 while (emitUntilSubstitution())
1293 if (!
string.
empty())
1297void EmitterBase::emitComment(StringAttr comment) {
1304 auto lineLength = std::max<size_t>(state.options.emittedLineLength, 3) - 3;
1308 auto ref = comment.getValue();
1310 while (!ref.empty()) {
1311 std::tie(line, ref) = ref.split(
"\n");
1318 if (line.size() <= lineLength) {
1320 setPendingNewline();
1331 auto breakPos = line.rfind(
' ', lineLength);
1333 if (breakPos == StringRef::npos) {
1334 breakPos = line.find(
' ', lineLength);
1337 if (breakPos == StringRef::npos)
1338 breakPos = line.size();
1345 setPendingNewline();
1346 breakPos = line.find_first_not_of(
' ', breakPos);
1348 if (breakPos == StringRef::npos)
1351 line = line.drop_front(breakPos);
1361 bool addPrefixUnderScore =
true;
1364 if (
auto read = expr.getDefiningOp<
ReadInOutOp>())
1368 if (
auto blockArg = dyn_cast<BlockArgument>(expr)) {
1370 cast<HWEmittableModuleLike>(blockArg.getOwner()->getParentOp());
1372 result = StringAttr::get(expr.getContext(), name);
1374 }
else if (
auto *op = expr.getDefiningOp()) {
1376 if (isa<sv::WireOp, RegOp, LogicOp>(op)) {
1378 result = StringAttr::get(expr.getContext(), name);
1380 }
else if (
auto nameHint = op->getAttrOfType<StringAttr>(
"sv.namehint")) {
1386 addPrefixUnderScore =
false;
1388 TypeSwitch<Operation *>(op)
1391 .Case([&result](VerbatimExprOp verbatim) {
1392 verbatim.getAsmResultNames([&](Value, StringRef name) {
1393 result = StringAttr::get(verbatim.getContext(), name);
1396 .Case([&result](VerbatimExprSEOp verbatim) {
1397 verbatim.getAsmResultNames([&](Value, StringRef name) {
1398 result = StringAttr::get(verbatim.getContext(), name);
1404 if (
auto operandName =
1407 cast<IntegerType>(extract.getType()).getWidth();
1409 result = StringAttr::get(extract.getContext(),
1410 operandName.strref() +
"_" +
1411 Twine(extract.getLowBit()));
1413 result = StringAttr::get(
1414 extract.getContext(),
1415 operandName.strref() +
"_" +
1416 Twine(extract.getLowBit() + numBits - 1) +
"to" +
1417 Twine(extract.getLowBit()));
1425 if (!result || result.strref().empty())
1429 if (addPrefixUnderScore && result.strref().front() !=
'_')
1430 result = StringAttr::get(expr.getContext(),
"_" + result.strref());
1442class ModuleEmitter :
public EmitterBase {
1444 explicit ModuleEmitter(VerilogEmitterState &state)
1445 : EmitterBase(state), currentModuleOp(nullptr),
1449 emitPendingNewlineIfNeeded();
1453 void emitParameters(Operation *module, ArrayAttr params);
1454 void emitPortList(Operation *module,
const ModulePortInfo &portInfo,
1455 bool emitAsTwoStateType =
false);
1458 void emitHWGeneratedModule(HWModuleGeneratedOp module);
1459 void emitFunc(FuncOp);
1462 void emitStatement(Operation *op);
1463 void emitBind(BindOp op);
1464 void emitBindInterface(BindInterfaceOp op);
1466 void emitSVAttributes(Operation *op);
1469 StringRef getVerilogStructFieldName(StringAttr field) {
1470 return fieldNameResolver.getRenamedFieldName(field).getValue();
1477 void emitTypeDims(Type type, Location loc, raw_ostream &os);
1489 bool printPackedType(Type type, raw_ostream &os, Location loc,
1490 Type optionalAliasType = {},
bool implicitIntType =
true,
1491 bool singleBitDefaultType =
true,
1492 bool emitAsTwoStateType =
false);
1496 void printUnpackedTypePostfix(Type type, raw_ostream &os);
1504 function_ref<InFlightDiagnostic()> emitError);
1507 VerilogPrecedence parenthesizeIfLooserThan,
1508 function_ref<InFlightDiagnostic()> emitError);
1514 Operation *currentModuleOp;
1520 SmallPtrSet<Operation *, 16> expressionsEmittedIntoDecl;
1526 SmallPtrSet<Operation *, 16> assignsInlined;
1535 const ModuleEmitter &emitter) {
1536 if (isa<RegOp>(op)) {
1541 cast<InOutType>(op->getResult(0).getType()).getElementType();
1548 if (
auto innerType = dyn_cast<ArrayType>(
elementType)) {
1549 while (isa<ArrayType>(innerType.getElementType()))
1550 innerType = cast<ArrayType>(innerType.getElementType());
1551 if (isa<StructType>(innerType.getElementType()) ||
1552 isa<TypeAliasType>(innerType.getElementType()))
1560 if (isa<sv::WireOp>(op))
1562 if (isa<ConstantOp, AggregateConstantOp, LocalParamOp, ParamValueOp>(op))
1563 return "localparam";
1566 if (
auto interface = dyn_cast<InterfaceInstanceOp>(op))
1567 return interface.getInterfaceType().getInterface().getValue();
1571 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
1575 bool stripAutomatic = isa_and_nonnull<FuncOp>(emitter.currentModuleOp);
1577 if (isa<LogicOp>(op)) {
1583 if (isProcedural && !stripAutomatic)
1584 return hasStruct ?
"automatic" :
"automatic logic";
1585 return hasStruct ?
"" :
"logic";
1592 return hasStructType(op->getResult(0).getType()) ?
"" :
"logic";
1595 assert(!emitter.state.options.disallowLocalVariables &&
1596 "automatic variables not allowed");
1600 return hasStructType(op->getResult(0).getType()) ?
"automatic"
1601 :
"automatic logic";
1608static void emitDim(Attribute width, raw_ostream &os, Location loc,
1609 ModuleEmitter &emitter,
bool downTo) {
1611 os <<
"<<invalid type>>";
1614 if (
auto intAttr = dyn_cast<IntegerAttr>(width)) {
1615 if (intAttr.getValue().isZero()) {
1616 os <<
"/*Zero Width*/";
1621 os << (intAttr.getValue().getZExtValue() - 1);
1631 auto typedAttr = dyn_cast<TypedAttr>(width);
1633 mlir::emitError(loc,
"untyped dimension attribute ") << width;
1637 getIntAttr(loc.getContext(), typedAttr.getType(),
1638 APInt(typedAttr.getType().getIntOrFloatBitWidth(), -1L,
true));
1639 width = ParamExprAttr::get(PEO::Add, typedAttr, negOne);
1643 emitter.printParamValue(width, os, [loc]() {
1644 return mlir::emitError(loc,
"invalid parameter in type");
1652static void emitDims(ArrayRef<Attribute> dims, raw_ostream &os, Location loc,
1653 ModuleEmitter &emitter) {
1654 for (Attribute width : dims) {
1655 emitDim(width, os, loc, emitter,
true);
1660void ModuleEmitter::emitTypeDims(Type type, Location loc, raw_ostream &os) {
1661 SmallVector<Attribute, 4> dims;
1693 SmallVectorImpl<Attribute> &dims,
1694 bool implicitIntType,
bool singleBitDefaultType,
1695 ModuleEmitter &emitter,
1696 Type optionalAliasType = {},
1697 bool emitAsTwoStateType =
false) {
1698 return TypeSwitch<Type, bool>(type)
1699 .Case<IntegerType>([&](IntegerType integerType) {
1700 if (emitAsTwoStateType && dims.empty()) {
1702 if (!typeName.empty()) {
1707 if (integerType.getWidth() != 1 || !singleBitDefaultType)
1709 getInt32Attr(type.getContext(), integerType.getWidth()));
1711 StringRef typeName =
1712 (emitAsTwoStateType ?
"bit" : (implicitIntType ?
"" :
"logic"));
1713 if (!typeName.empty()) {
1720 return !dims.empty() || !implicitIntType;
1722 .Case<IntType>([&](IntType intType) {
1723 if (!implicitIntType)
1725 dims.push_back(intType.getWidth());
1729 .Case<ArrayType>([&](ArrayType arrayType) {
1730 dims.push_back(arrayType.getSizeAttr());
1732 implicitIntType, singleBitDefaultType,
1734 emitAsTwoStateType);
1736 .Case<InOutType>([&](InOutType inoutType) {
1738 implicitIntType, singleBitDefaultType,
1740 emitAsTwoStateType);
1742 .Case<EnumType>([&](EnumType enumType) {
1744 if (enumType.getBitWidth() != 32)
1745 os <<
"bit [" << enumType.getBitWidth() - 1 <<
":0] ";
1747 Type enumPrefixType = optionalAliasType ? optionalAliasType : enumType;
1748 llvm::interleaveComma(
1749 enumType.getFields().getAsRange<StringAttr>(), os,
1750 [&](
auto enumerator) {
1751 os << emitter.fieldNameResolver.getEnumFieldName(
1752 hw::EnumFieldAttr::get(loc, enumerator, enumPrefixType));
1757 .Case<StructType>([&](StructType structType) {
1758 if (structType.getElements().empty() ||
isZeroBitType(structType)) {
1759 os <<
"/*Zero Width*/";
1762 os <<
"struct packed {";
1763 for (
auto &element : structType.getElements()) {
1765 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1766 <<
": Zero Width;*/ ";
1769 SmallVector<Attribute, 8> structDims;
1774 {}, emitAsTwoStateType);
1775 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1776 emitter.printUnpackedTypePostfix(element.type, os);
1783 .Case<UnionType>([&](UnionType unionType) {
1784 if (unionType.getElements().empty() ||
isZeroBitType(unionType)) {
1785 os <<
"/*Zero Width*/";
1789 int64_t unionWidth = hw::getBitWidth(unionType);
1790 os <<
"union packed {";
1791 for (
auto &element : unionType.getElements()) {
1793 os <<
"/*" << emitter.getVerilogStructFieldName(element.name)
1794 <<
": Zero Width;*/ ";
1797 int64_t elementWidth = hw::getBitWidth(element.type);
1798 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
1800 os <<
" struct packed {";
1801 if (element.offset) {
1802 os << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1803 << element.offset - 1 <<
":0] "
1804 <<
"__pre_padding_" << element.name.getValue() <<
"; ";
1808 SmallVector<Attribute, 8> structDims;
1812 true, emitter, {}, emitAsTwoStateType);
1813 os <<
' ' << emitter.getVerilogStructFieldName(element.name);
1814 emitter.printUnpackedTypePostfix(element.type, os);
1818 if (elementWidth + (int64_t)element.offset < unionWidth) {
1819 os <<
" " << (emitAsTwoStateType ?
"bit" :
"logic") <<
" ["
1820 << unionWidth - (elementWidth + element.offset) - 1 <<
":0] "
1821 <<
"__post_padding_" << element.name.getValue() <<
";";
1823 os <<
"} " << emitter.getVerilogStructFieldName(element.name)
1832 .Case<InterfaceType>([](InterfaceType ifaceType) {
return false; })
1833 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1834 os <<
"<<unexpected unpacked array>>";
1835 mlir::emitError(loc,
"Unexpected unpacked array in packed type ")
1839 .Case<TypeAliasType>([&](TypeAliasType typeRef) {
1840 auto typedecl = typeRef.getTypeDecl(emitter.state.symbolCache);
1842 mlir::emitError(loc,
"unresolvable type reference");
1845 if (typedecl.getType() != typeRef.getInnerType()) {
1846 mlir::emitError(loc,
"declared type did not match aliased type");
1850 os << typedecl.getPreferredName();
1851 emitDims(dims, os, typedecl->getLoc(), emitter);
1854 .Default([&](Type type) {
1855 os <<
"<<invalid type '" << type <<
"'>>";
1856 mlir::emitError(loc,
"value has an unsupported verilog type ") << type;
1872bool ModuleEmitter::printPackedType(Type type, raw_ostream &os, Location loc,
1873 Type optionalAliasType,
1874 bool implicitIntType,
1875 bool singleBitDefaultType,
1876 bool emitAsTwoStateType) {
1877 SmallVector<Attribute, 8> packedDimensions;
1879 singleBitDefaultType, *
this, optionalAliasType,
1880 emitAsTwoStateType);
1886void ModuleEmitter::printUnpackedTypePostfix(Type type, raw_ostream &os) {
1887 TypeSwitch<Type, void>(type)
1889 printUnpackedTypePostfix(inoutType.getElementType(), os);
1891 .Case<UnpackedArrayType>([&](UnpackedArrayType arrayType) {
1892 auto loc = currentModuleOp ? currentModuleOp->getLoc()
1893 : state.designOp->getLoc();
1894 emitDim(arrayType.getSizeAttr(), os, loc, *
this,
1896 printUnpackedTypePostfix(arrayType.getElementType(), os);
1898 .Case<sv::UnpackedOpenArrayType>([&](
auto arrayType) {
1900 printUnpackedTypePostfix(arrayType.getElementType(), os);
1902 .Case<InterfaceType>([&](
auto) {
1916ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1917 function_ref<InFlightDiagnostic()> emitError) {
1918 return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
1926ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
1927 VerilogPrecedence parenthesizeIfLooserThan,
1928 function_ref<InFlightDiagnostic()> emitError) {
1929 if (
auto intAttr = dyn_cast<IntegerAttr>(value)) {
1930 IntegerType intTy = cast<IntegerType>(intAttr.getType());
1931 APInt value = intAttr.getValue();
1935 if (intTy.getWidth() > 32) {
1937 if (value.isNegative() && (intTy.isSigned() || intTy.isSignless())) {
1941 if (intTy.isSigned())
1942 os << intTy.getWidth() <<
"'sd";
1944 os << intTy.getWidth() <<
"'d";
1946 value.print(os, intTy.isSigned());
1947 return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
1949 if (
auto strAttr = dyn_cast<StringAttr>(value)) {
1951 os.write_escaped(strAttr.getValue());
1953 return {Symbol, IsUnsigned};
1955 if (
auto fpAttr = dyn_cast<FloatAttr>(value)) {
1957 os << fpAttr.getValueAsDouble();
1958 return {Symbol, IsUnsigned};
1960 if (
auto verbatimParam = dyn_cast<ParamVerbatimAttr>(value)) {
1961 os << verbatimParam.getValue().getValue();
1962 return {Symbol, IsUnsigned};
1964 if (
auto parameterRef = dyn_cast<ParamDeclRefAttr>(value)) {
1966 os << state.globalNames.getParameterVerilogName(currentModuleOp,
1967 parameterRef.getName());
1970 return {Symbol, IsUnsigned};
1974 auto expr = dyn_cast<ParamExprAttr>(value);
1976 os <<
"<<UNKNOWN MLIRATTR: " << value <<
">>";
1977 emitError() <<
" = " << value;
1978 return {LowestPrecedence, IsUnsigned};
1981 StringRef operatorStr;
1982 StringRef openStr, closeStr;
1983 VerilogPrecedence subprecedence = LowestPrecedence;
1984 VerilogPrecedence prec;
1985 std::optional<SubExprSignResult> operandSign;
1986 bool isUnary =
false;
1987 bool hasOpenClose =
false;
1989 switch (expr.getOpcode()) {
1991 operatorStr =
" + ";
1992 subprecedence = Addition;
1995 operatorStr =
" * ";
1996 subprecedence = Multiply;
1999 operatorStr =
" & ";
2000 subprecedence = And;
2003 operatorStr =
" | ";
2007 operatorStr =
" ^ ";
2008 subprecedence = Xor;
2011 operatorStr =
" << ";
2012 subprecedence = Shift;
2016 operatorStr =
" >> ";
2017 subprecedence = Shift;
2021 operatorStr =
" >>> ";
2022 subprecedence = Shift;
2023 operandSign = IsSigned;
2026 operatorStr =
" / ";
2027 subprecedence = Multiply;
2028 operandSign = IsUnsigned;
2031 operatorStr =
" / ";
2032 subprecedence = Multiply;
2033 operandSign = IsSigned;
2036 operatorStr =
" % ";
2037 subprecedence = Multiply;
2038 operandSign = IsUnsigned;
2041 operatorStr =
" % ";
2042 subprecedence = Multiply;
2043 operandSign = IsSigned;
2046 openStr =
"$clog2(";
2048 operandSign = IsUnsigned;
2049 hasOpenClose =
true;
2052 case PEO::StrConcat:
2055 hasOpenClose =
true;
2058 subprecedence = LowestPrecedence;
2063 prec = subprecedence;
2066 assert(!isUnary || llvm::hasSingleElement(expr.getOperands()));
2068 assert(isUnary || hasOpenClose ||
2069 !llvm::hasSingleElement(expr.getOperands()));
2076 auto emitOperand = [&](Attribute operand) ->
bool {
2078 auto subprec = operandSign.has_value() ? LowestPrecedence : subprecedence;
2079 if (operandSign.has_value())
2080 os << (*operandSign == IsSigned ?
"$signed(" :
"$unsigned(");
2083 if (operandSign.has_value()) {
2085 signedness = *operandSign;
2087 return signedness == IsSigned;
2091 if (prec > parenthesizeIfLooserThan)
2100 bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
2101 for (
auto op : expr.getOperands().drop_front()) {
2104 if (expr.getOpcode() == PEO::Add) {
2105 if (
auto integer = dyn_cast<IntegerAttr>(op)) {
2106 const APInt &value = integer.getValue();
2107 if (value.isNegative() && !value.isMinSignedValue()) {
2109 allOperandsSigned &=
2110 emitOperand(IntegerAttr::get(op.getType(), -value));
2117 allOperandsSigned &= emitOperand(op);
2121 if (prec > parenthesizeIfLooserThan) {
2125 return {prec, allOperandsSigned ? IsSigned : IsUnsigned};
2140class ExprEmitter :
public EmitterBase,
2142 public CombinationalVisitor<ExprEmitter, SubExprInfo>,
2147 ExprEmitter(ModuleEmitter &emitter,
2148 SmallPtrSetImpl<Operation *> &emittedExprs)
2149 : ExprEmitter(emitter, emittedExprs, localTokens) {}
2151 ExprEmitter(ModuleEmitter &emitter,
2152 SmallPtrSetImpl<Operation *> &emittedExprs,
2154 : EmitterBase(emitter.state), emitter(emitter),
2155 emittedExprs(emittedExprs), buffer(tokens),
2156 ps(buffer, state.saver, state.options.emitVerilogLocations) {
2157 assert(state.pp.getListener() == &state.saver);
2164 void emitExpression(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2165 bool isAssignmentLikeContext) {
2166 assert(localTokens.empty());
2168 ps.scopedBox(PP::ibox0, [&]() {
2169 emitSubExpr(exp, parenthesizeIfLooserThan,
2172 isAssignmentLikeContext);
2177 if (&buffer.tokens == &localTokens)
2178 buffer.flush(state.pp);
2183 friend class CombinationalVisitor<ExprEmitter, SubExprInfo>;
2184 friend class sv::Visitor<ExprEmitter, SubExprInfo>;
2186 enum SubExprSignRequirement { NoRequirement, RequireSigned, RequireUnsigned };
2194 SubExprInfo emitSubExpr(Value exp, VerilogPrecedence parenthesizeIfLooserThan,
2195 SubExprSignRequirement signReq = NoRequirement,
2196 bool isSelfDeterminedUnsignedValue =
false,
2197 bool isAssignmentLikeContext =
false);
2201 void emitSVAttributes(Operation *op);
2203 SubExprInfo visitUnhandledExpr(Operation *op);
2204 SubExprInfo visitInvalidComb(Operation *op) {
2207 SubExprInfo visitUnhandledComb(Operation *op) {
2208 return visitUnhandledExpr(op);
2211 return dispatchSVVisitor(op);
2214 return visitUnhandledExpr(op);
2216 SubExprInfo visitUnhandledSV(Operation *op) {
return visitUnhandledExpr(op); }
2219 enum EmitBinaryFlags {
2220 EB_RequireSignedOperands = RequireSigned,
2221 EB_RequireUnsignedOperands = RequireUnsigned,
2222 EB_OperandSignRequirementMask = 0x3,
2227 EB_RHS_UnsignedWithSelfDeterminedWidth = 0x4,
2231 EB_ForceResultSigned = 0x8,
2236 SubExprInfo emitBinary(Operation *op, VerilogPrecedence prec,
2237 const char *syntax,
unsigned emitBinaryFlags = 0);
2239 SubExprInfo emitUnary(Operation *op,
const char *syntax,
2240 bool resultAlwaysUnsigned =
false);
2243 void emitSubExprIBox2(
2244 Value v, VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence) {
2245 ps.scopedBox(PP::ibox2,
2246 [&]() { emitSubExpr(v, parenthesizeIfLooserThan); });
2251 template <
typename Container,
typename EachFn>
2252 void interleaveComma(
const Container &c, EachFn eachFn) {
2253 llvm::interleave(c, eachFn, [&]() { ps <<
"," << PP::space; });
2258 void interleaveComma(ValueRange ops) {
2259 return interleaveComma(ops, [&](Value v) { emitSubExprIBox2(v); });
2276 template <
typename Container,
typename OpenFunc,
typename CloseFunc,
2278 void emitBracedList(
const Container &c, OpenFunc openFn, EachFunc eachFn,
2279 CloseFunc closeFn) {
2281 ps.scopedBox(PP::cbox0, [&]() {
2282 interleaveComma(c, eachFn);
2288 template <
typename OpenFunc,
typename CloseFunc>
2289 void emitBracedList(ValueRange ops, OpenFunc openFn, CloseFunc closeFn) {
2290 return emitBracedList(
2291 ops, openFn, [&](Value v) { emitSubExprIBox2(v); }, closeFn);
2295 void emitBracedList(ValueRange ops) {
2296 return emitBracedList(
2297 ops, [&]() { ps <<
"{"; }, [&]() { ps <<
"}"; });
2301 SubExprInfo printConstantScalar(APInt &value, IntegerType type);
2304 void printConstantArray(ArrayAttr elementValues, Type
elementType,
2305 bool printAsPattern, Operation *op);
2307 void printConstantStruct(ArrayRef<hw::detail::FieldInfo> fieldInfos,
2308 ArrayAttr fieldValues,
bool printAsPattern,
2311 void printConstantAggregate(Attribute attr, Type type, Operation *op);
2313 using sv::Visitor<ExprEmitter, SubExprInfo>::visitSV;
2314 SubExprInfo visitSV(GetModportOp op);
2315 SubExprInfo visitSV(SystemFunctionOp op);
2316 SubExprInfo visitSV(ReadInterfaceSignalOp op);
2317 SubExprInfo visitSV(XMROp op);
2318 SubExprInfo visitSV(SFormatFOp op);
2319 SubExprInfo visitSV(XMRRefOp op);
2320 SubExprInfo visitVerbatimExprOp(Operation *op, ArrayAttr symbols);
2321 SubExprInfo visitSV(VerbatimExprOp op) {
2322 return visitVerbatimExprOp(op, op.getSymbols());
2324 SubExprInfo visitSV(VerbatimExprSEOp op) {
2325 return visitVerbatimExprOp(op, op.getSymbols());
2327 SubExprInfo visitSV(MacroRefExprOp op);
2328 SubExprInfo visitSV(MacroRefExprSEOp op);
2329 template <
typename MacroTy>
2330 SubExprInfo emitMacroCall(MacroTy op);
2332 SubExprInfo visitSV(ConstantXOp op);
2333 SubExprInfo visitSV(ConstantZOp op);
2334 SubExprInfo visitSV(ConstantStrOp op);
2336 SubExprInfo visitSV(sv::UnpackedArrayCreateOp op);
2337 SubExprInfo visitSV(sv::UnpackedOpenArrayCastOp op) {
2339 return emitSubExpr(op->getOperand(0), LowestPrecedence);
2344 auto result = emitSubExpr(op->getOperand(0), LowestPrecedence);
2345 emitSVAttributes(op);
2348 SubExprInfo visitSV(ArrayIndexInOutOp op);
2349 SubExprInfo visitSV(IndexedPartSelectInOutOp op);
2350 SubExprInfo visitSV(IndexedPartSelectOp op);
2351 SubExprInfo visitSV(StructFieldInOutOp op);
2354 SubExprInfo visitSV(SampledOp op);
2357 SubExprInfo visitSV(TimeOp op);
2358 SubExprInfo visitSV(STimeOp op);
2361 using TypeOpVisitor::visitTypeOp;
2363 SubExprInfo visitTypeOp(AggregateConstantOp op);
2365 SubExprInfo visitTypeOp(ParamValueOp op);
2372 SubExprInfo visitTypeOp(StructInjectOp op);
2373 SubExprInfo visitTypeOp(UnionCreateOp op);
2374 SubExprInfo visitTypeOp(UnionExtractOp op);
2375 SubExprInfo visitTypeOp(EnumCmpOp op);
2376 SubExprInfo visitTypeOp(EnumConstantOp op);
2379 using CombinationalVisitor::visitComb;
2380 SubExprInfo visitComb(
MuxOp op);
2381 SubExprInfo visitComb(
AddOp op) {
2382 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2383 return emitBinary(op, Addition,
"+");
2385 SubExprInfo visitComb(
SubOp op) {
return emitBinary(op, Addition,
"-"); }
2386 SubExprInfo visitComb(
MulOp op) {
2387 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2388 return emitBinary(op, Multiply,
"*");
2390 SubExprInfo visitComb(
DivUOp op) {
2391 return emitBinary(op, Multiply,
"/", EB_RequireUnsignedOperands);
2393 SubExprInfo visitComb(
DivSOp op) {
2394 return emitBinary(op, Multiply,
"/",
2395 EB_RequireSignedOperands | EB_ForceResultSigned);
2397 SubExprInfo visitComb(
ModUOp op) {
2398 return emitBinary(op, Multiply,
"%", EB_RequireUnsignedOperands);
2400 SubExprInfo visitComb(
ModSOp op) {
2401 return emitBinary(op, Multiply,
"%",
2402 EB_RequireSignedOperands | EB_ForceResultSigned);
2404 SubExprInfo visitComb(
ShlOp op) {
2405 return emitBinary(op, Shift,
"<<", EB_RHS_UnsignedWithSelfDeterminedWidth);
2407 SubExprInfo visitComb(
ShrUOp op) {
2409 return emitBinary(op, Shift,
">>", EB_RHS_UnsignedWithSelfDeterminedWidth);
2411 SubExprInfo visitComb(
ShrSOp op) {
2414 return emitBinary(op, Shift,
">>>",
2415 EB_RequireSignedOperands | EB_ForceResultSigned |
2416 EB_RHS_UnsignedWithSelfDeterminedWidth);
2418 SubExprInfo visitComb(
AndOp op) {
2419 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2420 return emitBinary(op, And,
"&");
2422 SubExprInfo visitComb(
OrOp op) {
2423 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2424 return emitBinary(op, Or,
"|");
2426 SubExprInfo visitComb(
XorOp op) {
2427 if (op.isBinaryNot())
2428 return emitUnary(op,
"~");
2429 assert(op.getNumOperands() == 2 &&
"prelowering should handle variadics");
2430 return emitBinary(op, Xor,
"^");
2435 SubExprInfo visitComb(
ParityOp op) {
return emitUnary(op,
"^",
true); }
2437 SubExprInfo visitComb(ReplicateOp op);
2438 SubExprInfo visitComb(
ConcatOp op);
2440 SubExprInfo visitComb(ICmpOp op);
2442 InFlightDiagnostic emitAssignmentPatternContextError(Operation *op) {
2443 auto d = emitOpError(op,
"must be printed as assignment pattern, but is "
2444 "not printed within an assignment-like context");
2445 d.attachNote() <<
"this is likely a bug in PrepareForEmission, which is "
2446 "supposed to spill such expressions";
2450 SubExprInfo printStructCreate(
2451 ArrayRef<hw::detail::FieldInfo> fieldInfos,
2453 bool printAsPattern, Operation *op);
2456 ModuleEmitter &emitter;
2463 SubExprSignRequirement signPreference = NoRequirement;
2467 SmallPtrSetImpl<Operation *> &emittedExprs;
2470 SmallVector<Token> localTokens;
2484 bool isAssignmentLikeContext =
false;
2488SubExprInfo ExprEmitter::emitBinary(Operation *op, VerilogPrecedence prec,
2490 unsigned emitBinaryFlags) {
2492 emitError(op,
"SV attributes emission is unimplemented for the op");
2503 if (emitBinaryFlags & EB_ForceResultSigned)
2504 ps <<
"$signed(" << PP::ibox0;
2505 auto operandSignReq =
2506 SubExprSignRequirement(emitBinaryFlags & EB_OperandSignRequirementMask);
2507 auto lhsInfo = emitSubExpr(op->getOperand(0), prec, operandSignReq);
2509 auto lhsSpace = prec == VerilogPrecedence::Comparison ? PP::nbsp : PP::space;
2511 ps << lhsSpace << syntax << PP::nbsp;
2518 auto rhsPrec = prec;
2519 if (!isa<AddOp, MulOp, AndOp, OrOp, XorOp>(op))
2520 rhsPrec = VerilogPrecedence(prec - 1);
2525 bool rhsIsUnsignedValueWithSelfDeterminedWidth =
false;
2526 if (emitBinaryFlags & EB_RHS_UnsignedWithSelfDeterminedWidth) {
2527 rhsIsUnsignedValueWithSelfDeterminedWidth =
true;
2528 operandSignReq = NoRequirement;
2531 auto rhsInfo = emitSubExpr(op->getOperand(1), rhsPrec, operandSignReq,
2532 rhsIsUnsignedValueWithSelfDeterminedWidth);
2536 SubExprSignResult signedness = IsUnsigned;
2537 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
2538 signedness = IsSigned;
2540 if (emitBinaryFlags & EB_ForceResultSigned) {
2541 ps << PP::end <<
")";
2542 signedness = IsSigned;
2546 return {prec, signedness};
2549SubExprInfo ExprEmitter::emitUnary(Operation *op,
const char *syntax,
2550 bool resultAlwaysUnsigned) {
2552 emitError(op,
"SV attributes emission is unimplemented for the op");
2555 auto signedness = emitSubExpr(op->getOperand(0), Selection).signedness;
2559 return {isa<ICmpOp>(op) ? LowestPrecedence : Unary,
2560 resultAlwaysUnsigned ? IsUnsigned : signedness};
2565void ExprEmitter::emitSVAttributes(Operation *op) {
2584 if (constant && constant.getValue().isZero())
2585 return concat.getOperand(1);
2595SubExprInfo ExprEmitter::emitSubExpr(Value exp,
2596 VerilogPrecedence parenthesizeIfLooserThan,
2597 SubExprSignRequirement signRequirement,
2598 bool isSelfDeterminedUnsignedValue,
2599 bool isAssignmentLikeContext) {
2601 if (
auto result = dyn_cast<OpResult>(exp))
2602 if (
auto contract = dyn_cast<verif::ContractOp>(result.getOwner()))
2603 return emitSubExpr(contract.getInputs()[result.getResultNumber()],
2604 parenthesizeIfLooserThan, signRequirement,
2605 isSelfDeterminedUnsignedValue,
2606 isAssignmentLikeContext);
2610 if (isSelfDeterminedUnsignedValue && exp.hasOneUse()) {
2615 auto *op = exp.getDefiningOp();
2619 if (!shouldEmitInlineExpr) {
2622 if (signRequirement == RequireSigned) {
2624 return {Symbol, IsSigned};
2628 return {Symbol, IsUnsigned};
2631 unsigned subExprStartIndex = buffer.tokens.size();
2633 ps.addCallback({op,
true});
2634 auto done = llvm::make_scope_exit([&]() {
2636 ps.addCallback({op, false});
2642 signPreference = signRequirement;
2644 bool bitCastAdded =
false;
2645 if (state.options.explicitBitcast && isa<AddOp, MulOp, SubOp>(op))
2647 dyn_cast_or_null<IntegerType>(op->getResult(0).getType())) {
2648 ps.addAsString(inType.getWidth());
2649 ps <<
"'(" << PP::ibox0;
2650 bitCastAdded =
true;
2654 llvm::SaveAndRestore restoreALC(this->isAssignmentLikeContext,
2655 isAssignmentLikeContext);
2656 auto expInfo = dispatchCombinationalVisitor(exp.getDefiningOp());
2662 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex,
2664 buffer.tokens.insert(buffer.tokens.begin() + subExprStartIndex, t);
2666 auto closeBoxAndParen = [&]() { ps << PP::end <<
")"; };
2667 if (signRequirement == RequireSigned && expInfo.signedness == IsUnsigned) {
2670 expInfo.signedness = IsSigned;
2671 expInfo.precedence = Selection;
2672 }
else if (signRequirement == RequireUnsigned &&
2673 expInfo.signedness == IsSigned) {
2676 expInfo.signedness = IsUnsigned;
2677 expInfo.precedence = Selection;
2678 }
else if (expInfo.precedence > parenthesizeIfLooserThan) {
2685 expInfo.precedence = Selection;
2692 emittedExprs.insert(exp.getDefiningOp());
2696SubExprInfo ExprEmitter::visitComb(ReplicateOp op) {
2697 auto openFn = [&]() {
2699 ps.addAsString(op.getMultiple());
2702 auto closeFn = [&]() { ps <<
"}}"; };
2706 if (
auto concatOp = op.getOperand().getDefiningOp<
ConcatOp>()) {
2707 if (op.getOperand().hasOneUse()) {
2708 emitBracedList(concatOp.getOperands(), openFn, closeFn);
2709 return {Symbol, IsUnsigned};
2712 emitBracedList(op.getOperand(), openFn, closeFn);
2713 return {Symbol, IsUnsigned};
2716SubExprInfo ExprEmitter::visitComb(
ConcatOp op) {
2717 emitBracedList(op.getOperands());
2718 return {Symbol, IsUnsigned};
2721SubExprInfo ExprEmitter::visitTypeOp(
BitcastOp op) {
2725 Type toType = op.getType();
2728 ps.invokeWithStringOS(
2729 [&](
auto &os) { emitter.emitTypeDims(toType, op.getLoc(), os); });
2732 return emitSubExpr(op.getInput(), LowestPrecedence);
2735SubExprInfo ExprEmitter::visitComb(ICmpOp op) {
2736 const char *symop[] = {
"==",
"!=",
"<",
"<=",
">",
">=",
"<",
2737 "<=",
">",
">=",
"===",
"!==",
"==?",
"!=?"};
2738 SubExprSignRequirement signop[] = {
2740 NoRequirement, NoRequirement,
2742 RequireSigned, RequireSigned, RequireSigned, RequireSigned,
2744 RequireUnsigned, RequireUnsigned, RequireUnsigned, RequireUnsigned,
2746 NoRequirement, NoRequirement, NoRequirement, NoRequirement};
2748 auto pred =
static_cast<uint64_t
>(op.getPredicate());
2749 assert(pred <
sizeof(symop) /
sizeof(symop[0]));
2752 if (op.isEqualAllOnes())
2753 return emitUnary(op,
"&",
true);
2756 if (op.isNotEqualZero())
2757 return emitUnary(op,
"|",
true);
2759 auto result = emitBinary(op, Comparison, symop[pred], signop[pred]);
2763 result.signedness = IsUnsigned;
2767SubExprInfo ExprEmitter::visitComb(
ExtractOp op) {
2769 emitError(op,
"SV attributes emission is unimplemented for the op");
2771 unsigned loBit = op.getLowBit();
2772 unsigned hiBit = loBit + cast<IntegerType>(op.getType()).getWidth() - 1;
2774 auto x = emitSubExpr(op.getInput(), LowestPrecedence);
2775 assert((x.precedence == Symbol ||
2777 "should be handled by isExpressionUnableToInline");
2782 op.getInput().getType().getIntOrFloatBitWidth() == hiBit + 1)
2786 ps.addAsString(hiBit);
2787 if (hiBit != loBit) {
2789 ps.addAsString(loBit);
2792 return {Unary, IsUnsigned};
2795SubExprInfo ExprEmitter::visitSV(GetModportOp op) {
2797 emitError(op,
"SV attributes emission is unimplemented for the op");
2799 auto decl = op.getReferencedDecl(state.symbolCache);
2802 return {Selection, IsUnsigned};
2805SubExprInfo ExprEmitter::visitSV(SystemFunctionOp op) {
2807 emitError(op,
"SV attributes emission is unimplemented for the op");
2810 ps.scopedBox(PP::ibox0, [&]() {
2812 op.getOperands(), [&](Value v) { emitSubExpr(v, LowestPrecedence); },
2813 [&]() { ps <<
"," << PP::space; });
2816 return {Symbol, IsUnsigned};
2819SubExprInfo ExprEmitter::visitSV(ReadInterfaceSignalOp op) {
2821 emitError(op,
"SV attributes emission is unimplemented for the op");
2823 auto decl = op.getReferencedDecl(state.symbolCache);
2827 return {Selection, IsUnsigned};
2830SubExprInfo ExprEmitter::visitSV(XMROp op) {
2832 emitError(op,
"SV attributes emission is unimplemented for the op");
2834 if (op.getIsRooted())
2836 for (
auto s : op.getPath())
2837 ps <<
PPExtString(cast<StringAttr>(s).getValue()) <<
".";
2839 return {Selection, IsUnsigned};
2844SubExprInfo ExprEmitter::visitSV(XMRRefOp op) {
2846 emitError(op,
"SV attributes emission is unimplemented for the op");
2849 auto globalRef = op.getReferencedPath(&state.symbolCache);
2850 auto namepath = globalRef.getNamepathAttr().getValue();
2851 auto *
module = state.symbolCache.getDefinition(
2852 cast<InnerRefAttr>(namepath.front()).getModule());
2854 for (
auto sym : namepath) {
2856 auto innerRef = cast<InnerRefAttr>(sym);
2857 auto ref = state.symbolCache.getInnerDefinition(innerRef.getModule(),
2858 innerRef.getName());
2859 if (ref.hasPort()) {
2865 auto leaf = op.getVerbatimSuffixAttr();
2866 if (leaf && leaf.size())
2868 return {Selection, IsUnsigned};
2871SubExprInfo ExprEmitter::visitVerbatimExprOp(Operation *op, ArrayAttr symbols) {
2873 emitError(op,
"SV attributes emission is unimplemented for the op");
2875 emitTextWithSubstitutions(
2876 ps, op->getAttrOfType<StringAttr>(
"format_string").getValue(), op,
2877 [&](Value operand) { emitSubExpr(operand, LowestPrecedence); }, symbols);
2879 return {Unary, IsUnsigned};
2882template <
typename MacroTy>
2883SubExprInfo ExprEmitter::emitMacroCall(MacroTy op) {
2885 emitError(op,
"SV attributes emission is unimplemented for the op");
2888 auto macroOp = op.getReferencedMacro(&state.symbolCache);
2889 assert(macroOp &&
"Invalid IR");
2891 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
2893 if (!op.getInputs().empty()) {
2895 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
2896 emitExpression(val, LowestPrecedence, false);
2900 return {LowestPrecedence, IsUnsigned};
2903SubExprInfo ExprEmitter::visitSV(MacroRefExprOp op) {
2904 return emitMacroCall(op);
2907SubExprInfo ExprEmitter::visitSV(MacroRefExprSEOp op) {
2908 return emitMacroCall(op);
2911SubExprInfo ExprEmitter::visitSV(ConstantXOp op) {
2913 emitError(op,
"SV attributes emission is unimplemented for the op");
2915 ps.addAsString(op.getWidth());
2917 return {Unary, IsUnsigned};
2920SubExprInfo ExprEmitter::visitSV(ConstantStrOp op) {
2922 emitError(op,
"SV attributes emission is unimplemented for the op");
2924 ps.writeQuotedEscaped(op.getStr());
2925 return {Symbol, IsUnsigned};
2928SubExprInfo ExprEmitter::visitSV(ConstantZOp op) {
2930 emitError(op,
"SV attributes emission is unimplemented for the op");
2932 ps.addAsString(op.getWidth());
2934 return {Unary, IsUnsigned};
2937SubExprInfo ExprEmitter::printConstantScalar(APInt &value, IntegerType type) {
2938 bool isNegated =
false;
2941 if (signPreference == RequireSigned && value.isNegative() &&
2942 !value.isMinSignedValue()) {
2947 ps.addAsString(type.getWidth());
2951 if (signPreference == RequireSigned)
2957 SmallString<32> valueStr;
2959 (-value).toStringUnsigned(valueStr, 16);
2961 value.toStringUnsigned(valueStr, 16);
2964 return {Unary, signPreference == RequireSigned ? IsSigned : IsUnsigned};
2967SubExprInfo ExprEmitter::visitTypeOp(
ConstantOp op) {
2969 emitError(op,
"SV attributes emission is unimplemented for the op");
2971 auto value = op.getValue();
2975 if (value.getBitWidth() == 0) {
2976 emitOpError(op,
"will not emit zero width constants in the general case");
2977 ps <<
"<<unsupported zero width constant: "
2978 <<
PPExtString(op->getName().getStringRef()) <<
">>";
2979 return {Unary, IsUnsigned};
2982 return printConstantScalar(value, cast<IntegerType>(op.getType()));
2985void ExprEmitter::printConstantArray(ArrayAttr elementValues, Type
elementType,
2986 bool printAsPattern, Operation *op) {
2987 if (printAsPattern && !isAssignmentLikeContext)
2988 emitAssignmentPatternContextError(op);
2989 StringRef openDelim = printAsPattern ?
"'{" :
"{";
2992 elementValues, [&]() { ps << openDelim; },
2993 [&](Attribute elementValue) {
2994 printConstantAggregate(elementValue,
elementType, op);
2996 [&]() { ps <<
"}"; });
2999void ExprEmitter::printConstantStruct(
3000 ArrayRef<hw::detail::FieldInfo> fieldInfos, ArrayAttr fieldValues,
3001 bool printAsPattern, Operation *op) {
3002 if (printAsPattern && !isAssignmentLikeContext)
3003 emitAssignmentPatternContextError(op);
3010 auto fieldRange = llvm::make_filter_range(
3011 llvm::zip(fieldInfos, fieldValues), [](
const auto &fieldAndValue) {
3016 if (printAsPattern) {
3018 fieldRange, [&]() { ps <<
"'{"; },
3019 [&](
const auto &fieldAndValue) {
3020 ps.scopedBox(PP::ibox2, [&]() {
3021 const auto &[field, value] = fieldAndValue;
3022 ps <<
PPExtString(emitter.getVerilogStructFieldName(field.name))
3023 <<
":" << PP::space;
3024 printConstantAggregate(value, field.type, op);
3027 [&]() { ps <<
"}"; });
3030 fieldRange, [&]() { ps <<
"{"; },
3031 [&](
const auto &fieldAndValue) {
3032 ps.scopedBox(PP::ibox2, [&]() {
3033 const auto &[field, value] = fieldAndValue;
3034 printConstantAggregate(value, field.type, op);
3037 [&]() { ps <<
"}"; });
3041void ExprEmitter::printConstantAggregate(Attribute attr, Type type,
3044 if (
auto arrayType = hw::type_dyn_cast<ArrayType>(type))
3045 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3046 isAssignmentLikeContext, op);
3049 if (
auto arrayType = hw::type_dyn_cast<UnpackedArrayType>(type))
3050 return printConstantArray(cast<ArrayAttr>(attr), arrayType.getElementType(),
3054 if (
auto structType = hw::type_dyn_cast<StructType>(type))
3055 return printConstantStruct(structType.getElements(), cast<ArrayAttr>(attr),
3056 isAssignmentLikeContext, op);
3058 if (
auto intType = hw::type_dyn_cast<IntegerType>(type)) {
3059 auto value = cast<IntegerAttr>(attr).getValue();
3060 printConstantScalar(value, intType);
3064 emitOpError(op,
"contains constant of type ")
3065 << type <<
" which cannot be emitted as Verilog";
3068SubExprInfo ExprEmitter::visitTypeOp(AggregateConstantOp op) {
3070 emitError(op,
"SV attributes emission is unimplemented for the op");
3074 "zero-bit types not allowed at this point");
3076 printConstantAggregate(op.getFields(), op.getType(), op);
3077 return {Symbol, IsUnsigned};
3080SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
3082 emitError(op,
"SV attributes emission is unimplemented for the op");
3084 return ps.invokeWithStringOS([&](
auto &os) {
3085 return emitter.printParamValue(op.getValue(), os, [&]() {
3086 return op->emitOpError(
"invalid parameter use");
3095 emitError(op,
"SV attributes emission is unimplemented for the op");
3097 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3099 unsigned dstWidth = type_cast<ArrayType>(op.getType()).getNumElements();
3101 emitSubExpr(op.getLowIndex(), LowestPrecedence);
3103 ps.addAsString(dstWidth);
3105 return {Selection, arrayPrec.signedness};
3108SubExprInfo ExprEmitter::visitTypeOp(
ArrayGetOp op) {
3109 emitSubExpr(op.getInput(), Selection);
3114 emitSubExpr(op.getIndex(), LowestPrecedence);
3116 emitSVAttributes(op);
3117 return {Selection, IsUnsigned};
3123 emitError(op,
"SV attributes emission is unimplemented for the op");
3125 if (op.isUniform()) {
3127 ps.addAsString(op.getInputs().size());
3129 emitSubExpr(op.getUniformElement(), LowestPrecedence);
3133 op.getInputs(), [&]() { ps <<
"{"; },
3136 emitSubExprIBox2(v);
3139 [&]() { ps <<
"}"; });
3141 return {Unary, IsUnsigned};
3144SubExprInfo ExprEmitter::visitSV(UnpackedArrayCreateOp op) {
3146 emitError(op,
"SV attributes emission is unimplemented for the op");
3149 llvm::reverse(op.getInputs()), [&]() { ps <<
"'{"; },
3150 [&](Value v) { emitSubExprIBox2(v); }, [&]() { ps <<
"}"; });
3151 return {Unary, IsUnsigned};
3156 emitError(op,
"SV attributes emission is unimplemented for the op");
3158 emitBracedList(op.getOperands());
3159 return {Unary, IsUnsigned};
3162SubExprInfo ExprEmitter::visitSV(ArrayIndexInOutOp op) {
3164 emitError(op,
"SV attributes emission is unimplemented for the op");
3166 auto index = op.getIndex();
3167 auto arrayPrec = emitSubExpr(op.getInput(), Selection);
3172 emitSubExpr(index, LowestPrecedence);
3174 return {Selection, arrayPrec.signedness};
3177SubExprInfo ExprEmitter::visitSV(IndexedPartSelectInOutOp op) {
3179 emitError(op,
"SV attributes emission is unimplemented for the op");
3181 auto prec = emitSubExpr(op.getInput(), Selection);
3183 emitSubExpr(op.getBase(), LowestPrecedence);
3184 if (op.getDecrement())
3188 ps.addAsString(op.getWidth());
3190 return {Selection, prec.signedness};
3193SubExprInfo ExprEmitter::visitSV(IndexedPartSelectOp op) {
3195 emitError(op,
"SV attributes emission is unimplemented for the op");
3197 auto info = emitSubExpr(op.getInput(), LowestPrecedence);
3199 emitSubExpr(op.getBase(), LowestPrecedence);
3200 if (op.getDecrement())
3204 ps.addAsString(op.getWidth());
3209SubExprInfo ExprEmitter::visitSV(StructFieldInOutOp op) {
3211 emitError(op,
"SV attributes emission is unimplemented for the op");
3213 auto prec = emitSubExpr(op.getInput(), Selection);
3215 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldAttr()));
3216 return {Selection, prec.signedness};
3219SubExprInfo ExprEmitter::visitSV(SampledOp op) {
3221 emitError(op,
"SV attributes emission is unimplemented for the op");
3224 auto info = emitSubExpr(op.getExpression(), LowestPrecedence);
3229SubExprInfo ExprEmitter::visitSV(SFormatFOp op) {
3231 emitError(op,
"SV attributes emission is unimplemented for the op");
3234 ps.scopedBox(PP::ibox0, [&]() {
3235 ps.writeQuotedEscaped(op.getFormatString());
3242 for (
auto operand : op.getSubstitutions()) {
3243 ps <<
"," << PP::space;
3244 emitSubExpr(operand, LowestPrecedence);
3248 return {Symbol, IsUnsigned};
3251SubExprInfo ExprEmitter::visitSV(TimeOp op) {
3253 emitError(op,
"SV attributes emission is unimplemented for the op");
3256 return {Symbol, IsUnsigned};
3259SubExprInfo ExprEmitter::visitSV(STimeOp op) {
3261 emitError(op,
"SV attributes emission is unimplemented for the op");
3264 return {Symbol, IsUnsigned};
3267SubExprInfo ExprEmitter::visitComb(
MuxOp op) {
3281 return ps.scopedBox(PP::cbox0, [&]() -> SubExprInfo {
3282 ps.scopedBox(PP::ibox0, [&]() {
3283 emitSubExpr(op.getCond(), VerilogPrecedence(Conditional - 1));
3287 emitSVAttributes(op);
3289 auto lhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3290 return emitSubExpr(op.getTrueValue(), VerilogPrecedence(Conditional - 1));
3294 auto rhsInfo = ps.scopedBox(PP::ibox0, [&]() {
3295 return emitSubExpr(op.getFalseValue(), Conditional);
3298 SubExprSignResult signedness = IsUnsigned;
3299 if (lhsInfo.signedness == IsSigned && rhsInfo.signedness == IsSigned)
3300 signedness = IsSigned;
3302 return {Conditional, signedness};
3306SubExprInfo ExprEmitter::printStructCreate(
3307 ArrayRef<hw::detail::FieldInfo> fieldInfos,
3309 bool printAsPattern, Operation *op) {
3310 if (printAsPattern && !isAssignmentLikeContext)
3311 emitAssignmentPatternContextError(op);
3314 auto filteredFields = llvm::make_filter_range(
3315 llvm::enumerate(fieldInfos),
3316 [](
const auto &field) {
return !
isZeroBitType(field.value().type); });
3318 if (printAsPattern) {
3320 filteredFields, [&]() { ps <<
"'{"; },
3321 [&](
const auto &field) {
3322 ps.scopedBox(PP::ibox2, [&]() {
3324 emitter.getVerilogStructFieldName(field.value().name))
3325 <<
":" << PP::space;
3326 fieldFn(field.value(), field.index());
3329 [&]() { ps <<
"}"; });
3332 filteredFields, [&]() { ps <<
"{"; },
3333 [&](
const auto &field) {
3334 ps.scopedBox(PP::ibox2,
3335 [&]() { fieldFn(field.value(), field.index()); });
3337 [&]() { ps <<
"}"; });
3340 return {Selection, IsUnsigned};
3345 emitError(op,
"SV attributes emission is unimplemented for the op");
3349 bool printAsPattern = isAssignmentLikeContext;
3350 StructType structType = op.getType();
3351 return printStructCreate(
3352 structType.getElements(),
3353 [&](
const auto &field,
auto index) {
3354 emitSubExpr(op.getOperand(index), Selection, NoRequirement,
3356 isAssignmentLikeContext);
3358 printAsPattern, op);
3363 emitError(op,
"SV attributes emission is unimplemented for the op");
3365 emitSubExpr(op.getInput(), Selection);
3367 <<
PPExtString(emitter.getVerilogStructFieldName(op.getFieldNameAttr()));
3368 return {Selection, IsUnsigned};
3371SubExprInfo ExprEmitter::visitTypeOp(StructInjectOp op) {
3373 emitError(op,
"SV attributes emission is unimplemented for the op");
3377 bool printAsPattern = isAssignmentLikeContext;
3378 StructType structType = op.getType();
3379 return printStructCreate(
3380 structType.getElements(),
3381 [&](
const auto &field,
auto index) {
3382 if (field.name == op.getFieldNameAttr()) {
3383 emitSubExpr(op.getNewValue(), Selection);
3385 emitSubExpr(op.getInput(), Selection);
3387 << PPExtString(emitter.getVerilogStructFieldName(field.name));
3390 printAsPattern, op);
3393SubExprInfo ExprEmitter::visitTypeOp(EnumConstantOp op) {
3394 ps <<
PPSaveString(emitter.fieldNameResolver.getEnumFieldName(op.getField()));
3395 return {Selection, IsUnsigned};
3398SubExprInfo ExprEmitter::visitTypeOp(EnumCmpOp op) {
3400 emitError(op,
"SV attributes emission is unimplemented for the op");
3401 auto result = emitBinary(op, Comparison,
"==", NoRequirement);
3404 result.signedness = IsUnsigned;
3408SubExprInfo ExprEmitter::visitTypeOp(UnionCreateOp op) {
3410 emitError(op,
"SV attributes emission is unimplemented for the op");
3414 auto unionWidth = hw::getBitWidth(unionType);
3415 auto &element = unionType.getElements()[op.getFieldIndex()];
3416 auto elementWidth = hw::getBitWidth(element.type);
3419 if (!elementWidth) {
3420 ps.addAsString(unionWidth);
3422 return {Unary, IsUnsigned};
3426 if (elementWidth == unionWidth) {
3427 emitSubExpr(op.getInput(), LowestPrecedence);
3428 return {Unary, IsUnsigned};
3433 ps.scopedBox(PP::ibox0, [&]() {
3434 if (
auto prePadding = element.offset) {
3435 ps.addAsString(prePadding);
3436 ps <<
"'h0," << PP::space;
3438 emitSubExpr(op.getInput(), Selection);
3439 if (
auto postPadding = unionWidth - elementWidth - element.offset) {
3440 ps <<
"," << PP::space;
3441 ps.addAsString(postPadding);
3447 return {Unary, IsUnsigned};
3450SubExprInfo ExprEmitter::visitTypeOp(UnionExtractOp op) {
3452 emitError(op,
"SV attributes emission is unimplemented for the op");
3453 emitSubExpr(op.getInput(), Selection);
3456 auto unionType = cast<UnionType>(
getCanonicalType(op.getInput().getType()));
3457 auto unionWidth = hw::getBitWidth(unionType);
3458 auto &element = unionType.getElements()[op.getFieldIndex()];
3459 auto elementWidth = hw::getBitWidth(element.type);
3460 bool needsPadding = elementWidth < unionWidth || element.offset > 0;
3461 auto verilogFieldName = emitter.getVerilogStructFieldName(element.name);
3470 return {Selection, IsUnsigned};
3473SubExprInfo ExprEmitter::visitUnhandledExpr(Operation *op) {
3474 emitOpError(op,
"cannot emit this expression to Verilog");
3475 ps <<
"<<unsupported expr: " <<
PPExtString(op->getName().getStringRef())
3477 return {Symbol, IsUnsigned};
3493enum class PropertyPrecedence {
3513struct EmittedProperty {
3515 PropertyPrecedence precedence;
3520class PropertyEmitter :
public EmitterBase,
3521 public ltl::Visitor<PropertyEmitter, EmittedProperty> {
3525 PropertyEmitter(ModuleEmitter &emitter,
3526 SmallPtrSetImpl<Operation *> &emittedOps)
3527 : PropertyEmitter(emitter, emittedOps, localTokens) {}
3528 PropertyEmitter(ModuleEmitter &emitter,
3529 SmallPtrSetImpl<Operation *> &emittedOps,
3531 : EmitterBase(emitter.state), emitter(emitter), emittedOps(emittedOps),
3533 ps(buffer, state.saver, state.options.emitVerilogLocations) {
3534 assert(state.pp.getListener() == &state.saver);
3537 void emitAssertPropertyDisable(
3538 Value property, Value disable,
3539 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3541 void emitAssertPropertyBody(
3542 Value property, Value disable,
3543 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3545 void emitAssertPropertyBody(
3546 Value property, sv::EventControl event, Value clock, Value disable,
3547 PropertyPrecedence parenthesizeIfLooserThan = PropertyPrecedence::Lowest);
3552 emitNestedProperty(Value property,
3553 PropertyPrecedence parenthesizeIfLooserThan);
3554 using ltl::Visitor<PropertyEmitter, EmittedProperty>::visitLTL;
3555 friend class ltl::Visitor<PropertyEmitter, EmittedProperty>;
3557 EmittedProperty visitUnhandledLTL(Operation *op);
3558 EmittedProperty visitLTL(ltl::AndOp op);
3559 EmittedProperty visitLTL(ltl::OrOp op);
3560 EmittedProperty visitLTL(ltl::IntersectOp op);
3561 EmittedProperty visitLTL(ltl::DelayOp op);
3562 EmittedProperty visitLTL(ltl::ConcatOp op);
3563 EmittedProperty visitLTL(ltl::RepeatOp op);
3564 EmittedProperty visitLTL(ltl::GoToRepeatOp op);
3565 EmittedProperty visitLTL(ltl::NonConsecutiveRepeatOp op);
3566 EmittedProperty visitLTL(ltl::NotOp op);
3567 EmittedProperty visitLTL(ltl::ImplicationOp op);
3568 EmittedProperty visitLTL(ltl::UntilOp op);
3569 EmittedProperty visitLTL(ltl::EventuallyOp op);
3570 EmittedProperty visitLTL(ltl::ClockOp op);
3572 void emitLTLConcat(ValueRange inputs);
3575 ModuleEmitter &emitter;
3580 SmallPtrSetImpl<Operation *> &emittedOps;
3583 SmallVector<Token> localTokens;
3596void PropertyEmitter::emitAssertPropertyDisable(
3597 Value property, Value disable,
3598 PropertyPrecedence parenthesizeIfLooserThan) {
3601 ps <<
"disable iff" << PP::nbsp <<
"(";
3603 emitNestedProperty(disable, PropertyPrecedence::Unary);
3609 ps.scopedBox(PP::ibox0,
3610 [&] { emitNestedProperty(property, parenthesizeIfLooserThan); });
3616void PropertyEmitter::emitAssertPropertyBody(
3617 Value property, Value disable,
3618 PropertyPrecedence parenthesizeIfLooserThan) {
3619 assert(localTokens.empty());
3621 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3626 if (&buffer.tokens == &localTokens)
3627 buffer.flush(state.pp);
3630void PropertyEmitter::emitAssertPropertyBody(
3631 Value property, sv::EventControl event, Value clock, Value disable,
3632 PropertyPrecedence parenthesizeIfLooserThan) {
3633 assert(localTokens.empty());
3636 ps.scopedBox(PP::ibox2, [&] {
3637 ps <<
PPExtString(stringifyEventControl(event)) << PP::space;
3638 emitNestedProperty(clock, PropertyPrecedence::Lowest);
3644 emitAssertPropertyDisable(property, disable, parenthesizeIfLooserThan);
3649 if (&buffer.tokens == &localTokens)
3650 buffer.flush(state.pp);
3653EmittedProperty PropertyEmitter::emitNestedProperty(
3654 Value property, PropertyPrecedence parenthesizeIfLooserThan) {
3664 if (!isa<ltl::SequenceType, ltl::PropertyType>(property.getType())) {
3665 ExprEmitter(emitter, emittedOps, buffer.tokens)
3666 .emitExpression(property, LowestPrecedence,
3668 return {PropertyPrecedence::Symbol};
3671 unsigned startIndex = buffer.tokens.size();
3672 auto info = dispatchLTLVisitor(property.getDefiningOp());
3677 if (
info.precedence > parenthesizeIfLooserThan) {
3679 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
BeginToken(0));
3680 buffer.tokens.insert(buffer.tokens.begin() + startIndex,
StringToken(
"("));
3682 ps << PP::end <<
")";
3684 info.precedence = PropertyPrecedence::Symbol;
3688 emittedOps.insert(property.getDefiningOp());
3692EmittedProperty PropertyEmitter::visitUnhandledLTL(Operation *op) {
3693 emitOpError(op,
"emission as Verilog property or sequence not supported");
3694 ps <<
"<<unsupported: " <<
PPExtString(op->getName().getStringRef()) <<
">>";
3695 return {PropertyPrecedence::Symbol};
3698EmittedProperty PropertyEmitter::visitLTL(ltl::AndOp op) {
3701 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::And); },
3702 [&]() { ps << PP::space <<
"and" << PP::nbsp; });
3703 return {PropertyPrecedence::And};
3706EmittedProperty PropertyEmitter::visitLTL(ltl::OrOp op) {
3709 [&](
auto input) { emitNestedProperty(input, PropertyPrecedence::Or); },
3710 [&]() { ps << PP::space <<
"or" << PP::nbsp; });
3711 return {PropertyPrecedence::Or};
3714EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
3718 emitNestedProperty(input, PropertyPrecedence::Intersect);
3720 [&]() { ps << PP::space <<
"intersect" << PP::nbsp; });
3721 return {PropertyPrecedence::Intersect};
3724EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3726 if (
auto length = op.getLength()) {
3728 ps.addAsString(op.getDelay());
3731 ps.addAsString(op.getDelay());
3733 ps.addAsString(op.getDelay() + *length);
3737 if (op.getDelay() == 0) {
3739 }
else if (op.getDelay() == 1) {
3743 ps.addAsString(op.getDelay());
3748 emitNestedProperty(op.getInput(), PropertyPrecedence::Concat);
3749 return {PropertyPrecedence::Concat};
3752void PropertyEmitter::emitLTLConcat(ValueRange inputs) {
3753 bool addSeparator =
false;
3754 for (
auto input : inputs) {
3757 if (!input.getDefiningOp<ltl::DelayOp>())
3758 ps <<
"##0" << PP::space;
3760 addSeparator =
true;
3761 emitNestedProperty(input, PropertyPrecedence::Concat);
3765EmittedProperty PropertyEmitter::visitLTL(ltl::ConcatOp op) {
3766 emitLTLConcat(op.getInputs());
3767 return {PropertyPrecedence::Concat};
3770EmittedProperty PropertyEmitter::visitLTL(ltl::RepeatOp op) {
3771 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3772 if (
auto more = op.getMore()) {
3774 ps.addAsString(op.getBase());
3777 ps.addAsString(op.getBase() + *more);
3781 if (op.getBase() == 0) {
3783 }
else if (op.getBase() == 1) {
3787 ps.addAsString(op.getBase());
3791 return {PropertyPrecedence::Repeat};
3794EmittedProperty PropertyEmitter::visitLTL(ltl::GoToRepeatOp op) {
3795 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3797 auto more = op.getMore();
3799 ps.addAsString(op.getBase());
3802 ps.addAsString(op.getBase() + more);
3806 return {PropertyPrecedence::Repeat};
3809EmittedProperty PropertyEmitter::visitLTL(ltl::NonConsecutiveRepeatOp op) {
3810 emitNestedProperty(op.getInput(), PropertyPrecedence::Repeat);
3812 auto more = op.getMore();
3814 ps.addAsString(op.getBase());
3817 ps.addAsString(op.getBase() + more);
3821 return {PropertyPrecedence::Repeat};
3824EmittedProperty PropertyEmitter::visitLTL(ltl::NotOp op) {
3825 ps <<
"not" << PP::space;
3826 emitNestedProperty(op.getInput(), PropertyPrecedence::Unary);
3827 return {PropertyPrecedence::Unary};
3833 auto concatOp = value.getDefiningOp<ltl::ConcatOp>();
3834 if (!concatOp || concatOp.getInputs().size() < 2)
3836 auto delayOp = concatOp.getInputs().back().getDefiningOp<ltl::DelayOp>();
3837 if (!delayOp || delayOp.getDelay() != 1 || delayOp.getLength() != 0)
3839 auto constOp = delayOp.getInput().getDefiningOp<
ConstantOp>();
3840 if (!constOp || !constOp.getValue().isOne())
3842 return concatOp.getInputs().drop_back();
3845EmittedProperty PropertyEmitter::visitLTL(ltl::ImplicationOp op) {
3849 emitLTLConcat(range);
3850 ps << PP::space <<
"|=>" << PP::nbsp;
3852 emitNestedProperty(op.getAntecedent(), PropertyPrecedence::Implication);
3853 ps << PP::space <<
"|->" << PP::nbsp;
3855 emitNestedProperty(op.getConsequent(), PropertyPrecedence::Implication);
3856 return {PropertyPrecedence::Implication};
3859EmittedProperty PropertyEmitter::visitLTL(ltl::UntilOp op) {
3860 emitNestedProperty(op.getInput(), PropertyPrecedence::Until);
3861 ps << PP::space <<
"until" << PP::space;
3862 emitNestedProperty(op.getCondition(), PropertyPrecedence::Until);
3863 return {PropertyPrecedence::Until};
3866EmittedProperty PropertyEmitter::visitLTL(ltl::EventuallyOp op) {
3867 ps <<
"s_eventually" << PP::space;
3868 emitNestedProperty(op.getInput(), PropertyPrecedence::Qualifier);
3869 return {PropertyPrecedence::Qualifier};
3872EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
3874 ps.scopedBox(PP::ibox2, [&] {
3875 ps <<
PPExtString(stringifyClockEdge(op.getEdge())) << PP::space;
3876 emitNestedProperty(op.getClock(), PropertyPrecedence::Lowest);
3880 emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3881 return {PropertyPrecedence::Clocking};
3891class NameCollector {
3893 NameCollector(ModuleEmitter &moduleEmitter) : moduleEmitter(moduleEmitter) {}
3897 void collectNames(Block &block);
3899 size_t getMaxDeclNameWidth()
const {
return maxDeclNameWidth; }
3900 size_t getMaxTypeWidth()
const {
return maxTypeWidth; }
3903 size_t maxDeclNameWidth = 0, maxTypeWidth = 0;
3904 ModuleEmitter &moduleEmitter;
3909 static constexpr size_t maxTypeWidthBound = 32;
3914void NameCollector::collectNames(Block &block) {
3917 for (
auto &op : block) {
3921 if (isa<InstanceOp, InstanceChoiceOp, InterfaceInstanceOp,
3922 FuncCallProceduralOp, FuncCallOp>(op))
3924 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
3928 for (
auto result : op.getResults()) {
3930 maxDeclNameWidth = std::max(declName.size(), maxDeclNameWidth);
3931 SmallString<16> typeString;
3935 llvm::raw_svector_ostream stringStream(typeString);
3937 stringStream, op.getLoc());
3939 if (typeString.size() <= maxTypeWidthBound)
3940 maxTypeWidth = std::max(typeString.size(), maxTypeWidth);
3947 if (isa<IfDefProceduralOp, OrderedOutputOp>(op)) {
3948 for (
auto ®ion : op.getRegions()) {
3949 if (!region.empty())
3950 collectNames(region.front());
3964class StmtEmitter :
public EmitterBase,
3972 : EmitterBase(emitter.state), emitter(emitter), options(options) {}
3974 void emitStatement(Operation *op);
3975 void emitStatementBlock(Block &body);
3978 LogicalResult emitDeclaration(Operation *op);
3981 void collectNamesAndCalculateDeclarationWidths(Block &block);
3984 emitExpression(Value exp, SmallPtrSetImpl<Operation *> &emittedExprs,
3985 VerilogPrecedence parenthesizeIfLooserThan = LowestPrecedence,
3986 bool isAssignmentLikeContext =
false);
3987 void emitSVAttributes(Operation *op);
3990 using sv::Visitor<StmtEmitter, LogicalResult>::visitSV;
3993 friend class sv::Visitor<StmtEmitter, LogicalResult>;
3997 LogicalResult visitUnhandledStmt(Operation *op) {
return failure(); }
3998 LogicalResult visitInvalidStmt(Operation *op) {
return failure(); }
3999 LogicalResult visitUnhandledSV(Operation *op) {
return failure(); }
4000 LogicalResult visitInvalidSV(Operation *op) {
return failure(); }
4001 LogicalResult visitUnhandledVerif(Operation *op) {
return failure(); }
4002 LogicalResult visitInvalidVerif(Operation *op) {
return failure(); }
4004 LogicalResult visitSV(
sv::WireOp op) {
return emitDeclaration(op); }
4005 LogicalResult visitSV(
RegOp op) {
return emitDeclaration(op); }
4006 LogicalResult visitSV(LogicOp op) {
return emitDeclaration(op); }
4007 LogicalResult visitSV(LocalParamOp op) {
return emitDeclaration(op); }
4008 template <
typename Op>
4011 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4012 void emitAssignLike(llvm::function_ref<
void()> emitLHS,
4013 llvm::function_ref<
void()> emitRHS,
PPExtString syntax,
4015 std::optional<PPExtString> wordBeforeLHS = std::nullopt);
4016 LogicalResult visitSV(
AssignOp op);
4017 LogicalResult visitSV(BPAssignOp op);
4018 LogicalResult visitSV(PAssignOp op);
4019 LogicalResult visitSV(ForceOp op);
4020 LogicalResult visitSV(ReleaseOp op);
4021 LogicalResult visitSV(AliasOp op);
4022 LogicalResult visitSV(InterfaceInstanceOp op);
4023 LogicalResult emitOutputLikeOp(Operation *op,
const ModulePortInfo &ports);
4024 LogicalResult visitStmt(OutputOp op);
4026 LogicalResult visitStmt(InstanceOp op);
4027 LogicalResult visitStmt(InstanceChoiceOp op);
4028 void emitInstancePortList(Operation *op,
ModulePortInfo &modPortInfo,
4029 ArrayRef<Value> instPortValues);
4034 LogicalResult emitIfDef(Operation *op, MacroIdentAttr cond);
4035 LogicalResult visitSV(OrderedOutputOp op);
4036 LogicalResult visitSV(
IfDefOp op) {
return emitIfDef(op, op.getCond()); }
4037 LogicalResult visitSV(IfDefProceduralOp op) {
4038 return emitIfDef(op, op.getCond());
4040 LogicalResult visitSV(IfOp op);
4041 LogicalResult visitSV(AlwaysOp op);
4042 LogicalResult visitSV(AlwaysCombOp op);
4043 LogicalResult visitSV(AlwaysFFOp op);
4044 LogicalResult visitSV(InitialOp op);
4045 LogicalResult visitSV(CaseOp op);
4046 LogicalResult visitSV(FWriteOp op);
4047 LogicalResult visitSV(FFlushOp op);
4048 LogicalResult visitSV(VerbatimOp op);
4049 LogicalResult visitSV(MacroRefOp op);
4051 LogicalResult emitSimulationControlTask(Operation *op,
PPExtString taskName,
4052 std::optional<unsigned> verbosity);
4053 LogicalResult visitSV(StopOp op);
4054 LogicalResult visitSV(FinishOp op);
4055 LogicalResult visitSV(ExitOp op);
4057 LogicalResult emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4058 std::optional<unsigned> verbosity,
4060 ValueRange operands);
4061 LogicalResult visitSV(FatalOp op);
4062 LogicalResult visitSV(ErrorOp op);
4063 LogicalResult visitSV(WarningOp op);
4064 LogicalResult visitSV(InfoOp op);
4066 LogicalResult visitSV(ReadMemOp op);
4068 LogicalResult visitSV(GenerateOp op);
4069 LogicalResult visitSV(GenerateCaseOp op);
4071 LogicalResult visitSV(
ForOp op);
4073 void emitAssertionLabel(Operation *op);
4074 void emitAssertionMessage(StringAttr message, ValueRange args,
4075 SmallPtrSetImpl<Operation *> &ops,
4077 template <
typename Op>
4078 LogicalResult emitImmediateAssertion(Op op,
PPExtString opName);
4079 LogicalResult visitSV(AssertOp op);
4080 LogicalResult visitSV(AssumeOp op);
4081 LogicalResult visitSV(CoverOp op);
4082 template <
typename Op>
4083 LogicalResult emitConcurrentAssertion(Op op,
PPExtString opName);
4084 LogicalResult visitSV(AssertConcurrentOp op);
4085 LogicalResult visitSV(AssumeConcurrentOp op);
4086 LogicalResult visitSV(CoverConcurrentOp op);
4087 template <
typename Op>
4088 LogicalResult emitPropertyAssertion(Op op,
PPExtString opName);
4089 LogicalResult visitSV(AssertPropertyOp op);
4090 LogicalResult visitSV(AssumePropertyOp op);
4091 LogicalResult visitSV(CoverPropertyOp op);
4093 LogicalResult visitSV(BindOp op);
4094 LogicalResult visitSV(InterfaceOp op);
4095 LogicalResult visitSV(InterfaceSignalOp op);
4096 LogicalResult visitSV(InterfaceModportOp op);
4097 LogicalResult visitSV(AssignInterfaceSignalOp op);
4098 LogicalResult visitSV(MacroDefOp op);
4100 void emitBlockAsStatement(Block *block,
4101 const SmallPtrSetImpl<Operation *> &locationOps,
4102 StringRef multiLineComment = StringRef());
4104 LogicalResult visitSV(FuncDPIImportOp op);
4105 template <
typename CallOp>
4106 LogicalResult emitFunctionCall(CallOp callOp);
4107 LogicalResult visitSV(FuncCallProceduralOp op);
4108 LogicalResult visitSV(FuncCallOp op);
4109 LogicalResult visitSV(ReturnOp op);
4110 LogicalResult visitSV(IncludeOp op);
4113 ModuleEmitter &emitter;
4118 size_t maxDeclNameWidth = 0;
4119 size_t maxTypeWidth = 0;
4130void StmtEmitter::emitExpression(Value exp,
4131 SmallPtrSetImpl<Operation *> &emittedExprs,
4132 VerilogPrecedence parenthesizeIfLooserThan,
4133 bool isAssignmentLikeContext) {
4134 ExprEmitter(emitter, emittedExprs)
4135 .emitExpression(exp, parenthesizeIfLooserThan, isAssignmentLikeContext);
4140void StmtEmitter::emitSVAttributes(Operation *op) {
4148 setPendingNewline();
4151void StmtEmitter::emitAssignLike(llvm::function_ref<
void()> emitLHS,
4152 llvm::function_ref<
void()> emitRHS,
4154 std::optional<PPExtString> wordBeforeLHS) {
4156 ps.scopedBox(PP::ibox2, [&]() {
4157 if (wordBeforeLHS) {
4158 ps << *wordBeforeLHS << PP::space;
4162 ps << PP::space << syntax << PP::space;
4164 ps.scopedBox(PP::ibox0, [&]() {
4171template <
typename Op>
4173StmtEmitter::emitAssignLike(Op op,
PPExtString syntax,
4174 std::optional<PPExtString> wordBeforeLHS) {
4175 SmallPtrSet<Operation *, 8> ops;
4179 ps.addCallback({op,
true});
4180 emitAssignLike([&]() { emitExpression(op.getDest(), ops); },
4182 emitExpression(op.getSrc(), ops, LowestPrecedence,
4187 ps.addCallback({op,
false});
4188 emitLocationInfoAndNewLine(ops);
4192LogicalResult StmtEmitter::visitSV(
AssignOp op) {
4195 if (isa_and_nonnull<HWInstanceLike, FuncCallOp>(op.getSrc().getDefiningOp()))
4198 if (emitter.assignsInlined.count(op))
4202 emitSVAttributes(op);
4207LogicalResult StmtEmitter::visitSV(BPAssignOp op) {
4208 if (op.getSrc().getDefiningOp<FuncCallProceduralOp>())
4212 if (emitter.assignsInlined.count(op))
4216 emitSVAttributes(op);
4221LogicalResult StmtEmitter::visitSV(PAssignOp op) {
4223 emitSVAttributes(op);
4228LogicalResult StmtEmitter::visitSV(ForceOp op) {
4230 emitError(op,
"SV attributes emission is unimplemented for the op");
4235LogicalResult StmtEmitter::visitSV(ReleaseOp op) {
4237 emitError(op,
"SV attributes emission is unimplemented for the op");
4240 SmallPtrSet<Operation *, 8> ops;
4242 ps.addCallback({op,
true});
4243 ps.scopedBox(PP::ibox2, [&]() {
4244 ps <<
"release" << PP::space;
4245 emitExpression(op.getDest(), ops);
4248 ps.addCallback({op,
false});
4249 emitLocationInfoAndNewLine(ops);
4253LogicalResult StmtEmitter::visitSV(AliasOp op) {
4255 emitError(op,
"SV attributes emission is unimplemented for the op");
4258 SmallPtrSet<Operation *, 8> ops;
4260 ps.addCallback({op,
true});
4261 ps.scopedBox(PP::ibox2, [&]() {
4262 ps <<
"alias" << PP::space;
4263 ps.scopedBox(PP::cbox0, [&]() {
4265 op.getOperands(), [&](Value v) { emitExpression(v, ops); },
4266 [&]() { ps << PP::nbsp <<
"=" << PP::space; });
4270 ps.addCallback({op,
false});
4271 emitLocationInfoAndNewLine(ops);
4275LogicalResult StmtEmitter::visitSV(InterfaceInstanceOp op) {
4276 auto doNotPrint = op.getDoNotPrint();
4277 if (doNotPrint && !state.options.emitBindComments)
4281 emitError(op,
"SV attributes emission is unimplemented for the op");
4284 StringRef prefix =
"";
4285 ps.addCallback({op,
true});
4288 ps <<
"// This interface is elsewhere emitted as a bind statement."
4292 SmallPtrSet<Operation *, 8> ops;
4295 auto *interfaceOp = op.getReferencedInterface(&state.symbolCache);
4296 assert(interfaceOp &&
"InterfaceInstanceOp has invalid symbol that does not "
4297 "point to an interface");
4300 if (!prefix.empty())
4306 ps.addCallback({op,
false});
4307 emitLocationInfoAndNewLine(ops);
4315LogicalResult StmtEmitter::emitOutputLikeOp(Operation *op,
4317 SmallPtrSet<Operation *, 8> ops;
4318 size_t operandIndex = 0;
4319 bool isProcedural = op->getParentOp()->hasTrait<ProceduralRegion>();
4320 for (
PortInfo port : ports.getOutputs()) {
4321 auto operand = op->getOperand(operandIndex);
4325 if (operand.hasOneUse() && operand.getDefiningOp() &&
4326 isa<InstanceOp, InstanceChoiceOp>(operand.getDefiningOp())) {
4335 ps.addCallback({op,
true});
4337 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
4339 ps <<
"// Zero width: ";
4342 ps <<
"assign" << PP::space;
4344 ps << PP::space <<
"=" << PP::space;
4345 ps.scopedBox(PP::ibox0, [&]() {
4349 isa_and_nonnull<hw::ConstantOp>(operand.getDefiningOp()))
4350 ps <<
"/*Zero width*/";
4352 emitExpression(operand, ops, LowestPrecedence,
4357 ps.addCallback({op,
false});
4358 emitLocationInfoAndNewLine(ops);
4365LogicalResult StmtEmitter::visitStmt(OutputOp op) {
4366 auto parent = op->getParentOfType<PortList>();
4368 return emitOutputLikeOp(op, ports);
4371LogicalResult StmtEmitter::visitStmt(
TypeScopeOp op) {
4373 auto typescopeDef = (
"_TYPESCOPE_" + op.getSymName()).str();
4374 ps <<
"`ifndef " << typescopeDef << PP::newline;
4375 ps <<
"`define " << typescopeDef;
4376 setPendingNewline();
4377 emitStatementBlock(*op.getBodyBlock());
4379 ps <<
"`endif // " << typescopeDef;
4380 setPendingNewline();
4384LogicalResult StmtEmitter::visitStmt(
TypedeclOp op) {
4386 emitError(op,
"SV attributes emission is unimplemented for the op");
4391 ps << PP::neverbox <<
"// ";
4393 SmallPtrSet<Operation *, 8> ops;
4395 ps.scopedBox(PP::ibox2, [&]() {
4396 ps <<
"typedef" << PP::space;
4397 ps.invokeWithStringOS([&](
auto &os) {
4399 op.getAliasType(),
false);
4401 ps << PP::space <<
PPExtString(op.getPreferredName());
4402 ps.invokeWithStringOS(
4403 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
4408 emitLocationInfoAndNewLine(ops);
4412template <
typename CallOpTy>
4413LogicalResult StmtEmitter::emitFunctionCall(CallOpTy op) {
4417 dyn_cast<FuncOp>(state.symbolCache.getDefinition(op.getCalleeAttr()));
4419 SmallPtrSet<Operation *, 8> ops;
4423 auto explicitReturn = op.getExplicitlyReturnedValue(callee);
4424 if (explicitReturn) {
4425 assert(explicitReturn.hasOneUse());
4426 if (op->getParentOp()->template hasTrait<ProceduralRegion>()) {
4427 auto bpassignOp = cast<sv::BPAssignOp>(*explicitReturn.user_begin());
4428 emitExpression(bpassignOp.getDest(), ops);
4430 auto assignOp = cast<sv::AssignOp>(*explicitReturn.user_begin());
4431 ps <<
"assign" << PP::nbsp;
4432 emitExpression(assignOp.getDest(), ops);
4434 ps << PP::nbsp <<
"=" << PP::nbsp;
4437 auto arguments = callee.getPortList(
true);
4441 bool needsComma =
false;
4442 auto printArg = [&](Value value) {
4444 ps <<
"," << PP::space;
4445 emitExpression(value, ops);
4449 ps.scopedBox(PP::ibox0, [&] {
4450 unsigned inputIndex = 0, outputIndex = 0;
4451 for (
auto arg : arguments) {
4454 op.getResults()[outputIndex++].getUsers().begin()->getOperand(0));
4456 printArg(op.getInputs()[inputIndex++]);
4461 emitLocationInfoAndNewLine(ops);
4465LogicalResult StmtEmitter::visitSV(FuncCallProceduralOp op) {
4466 return emitFunctionCall(op);
4469LogicalResult StmtEmitter::visitSV(FuncCallOp op) {
4470 return emitFunctionCall(op);
4473template <
typename PPS>
4475 bool isAutomatic =
false,
4476 bool emitAsTwoStateType =
false) {
4477 ps <<
"function" << PP::nbsp;
4479 ps <<
"automatic" << PP::nbsp;
4480 auto retType = op.getExplicitlyReturnedType();
4482 ps.invokeWithStringOS([&](
auto &os) {
4483 emitter.printPackedType(retType, os, op->getLoc(), {},
false,
true,
4484 emitAsTwoStateType);
4490 emitter.emitPortList(
4494LogicalResult StmtEmitter::visitSV(ReturnOp op) {
4495 auto parent = op->getParentOfType<sv::FuncOp>();
4497 return emitOutputLikeOp(op, ports);
4500LogicalResult StmtEmitter::visitSV(IncludeOp op) {
4502 ps <<
"`include" << PP::nbsp;
4504 if (op.getStyle() == IncludeStyle::System)
4505 ps <<
"<" << op.getTarget() <<
">";
4507 ps <<
"\"" << op.getTarget() <<
"\"";
4509 emitLocationInfo(op.getLoc());
4510 setPendingNewline();
4514LogicalResult StmtEmitter::visitSV(FuncDPIImportOp importOp) {
4517 ps <<
"import" << PP::nbsp <<
"\"DPI-C\"" << PP::nbsp <<
"context"
4521 if (
auto linkageName = importOp.getLinkageName())
4522 ps << *linkageName << PP::nbsp <<
"=" << PP::nbsp;
4524 cast<FuncOp>(state.symbolCache.getDefinition(importOp.getCalleeAttr()));
4525 assert(op.isDeclaration() &&
"function must be a declaration");
4528 assert(state.pendingNewline);
4534LogicalResult StmtEmitter::visitSV(FFlushOp op) {
4536 emitError(op,
"SV attributes emission is unimplemented for the op");
4539 SmallPtrSet<Operation *, 8> ops;
4542 ps.addCallback({op,
true});
4544 if (
auto fd = op.getFd())
4545 ps.scopedBox(PP::ibox0, [&]() { emitExpression(op.getFd(), ops); });
4548 ps.addCallback({op,
false});
4549 emitLocationInfoAndNewLine(ops);
4553LogicalResult StmtEmitter::visitSV(FWriteOp op) {
4555 emitError(op,
"SV attributes emission is unimplemented for the op");
4558 SmallPtrSet<Operation *, 8> ops;
4561 ps.addCallback({op,
true});
4563 ps.scopedBox(PP::ibox0, [&]() {
4564 emitExpression(op.getFd(), ops);
4566 ps <<
"," << PP::space;
4567 ps.writeQuotedEscaped(op.getFormatString());
4575 for (
auto operand : op.getSubstitutions()) {
4576 ps <<
"," << PP::space;
4577 emitExpression(operand, ops);
4581 ps.addCallback({op,
false});
4582 emitLocationInfoAndNewLine(ops);
4586LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
4588 emitError(op,
"SV attributes emission is unimplemented for the op");
4591 SmallPtrSet<Operation *, 8> ops;
4596 StringRef
string = op.getFormatString();
4597 if (
string.ends_with(
"\n"))
4598 string =
string.drop_back();
4603 bool isFirst =
true;
4606 while (!
string.
empty()) {
4607 auto lhsRhs =
string.split(
'\n');
4611 ps << PP::end << PP::newline << PP::neverbox;
4615 emitTextWithSubstitutions(
4616 ps, lhsRhs.first, op,
4617 [&](Value operand) { emitExpression(operand, ops); }, op.getSymbols());
4618 string = lhsRhs.second;
4623 emitLocationInfoAndNewLine(ops);
4628LogicalResult StmtEmitter::visitSV(MacroRefOp op) {
4630 emitError(op,
"SV attributes emission is unimplemented for the op");
4634 SmallPtrSet<Operation *, 8> ops;
4639 auto macroOp = op.getReferencedMacro(&state.symbolCache);
4640 assert(macroOp &&
"Invalid IR");
4642 macroOp.getVerilogName() ? *macroOp.getVerilogName() : macroOp.getName();
4644 if (!op.getInputs().empty()) {
4646 llvm::interleaveComma(op.getInputs(), ps, [&](Value val) {
4647 emitExpression(val, ops, LowestPrecedence,
4653 emitLocationInfoAndNewLine(ops);
4659StmtEmitter::emitSimulationControlTask(Operation *op,
PPExtString taskName,
4660 std::optional<unsigned> verbosity) {
4662 emitError(op,
"SV attributes emission is unimplemented for the op");
4665 SmallPtrSet<Operation *, 8> ops;
4667 ps.addCallback({op,
true});
4669 if (verbosity && *verbosity != 1) {
4671 ps.addAsString(*verbosity);
4675 ps.addCallback({op,
false});
4676 emitLocationInfoAndNewLine(ops);
4680LogicalResult StmtEmitter::visitSV(StopOp op) {
4681 return emitSimulationControlTask(op,
PPExtString(
"$stop"), op.getVerbosity());
4684LogicalResult StmtEmitter::visitSV(FinishOp op) {
4685 return emitSimulationControlTask(op,
PPExtString(
"$finish"),
4689LogicalResult StmtEmitter::visitSV(ExitOp op) {
4690 return emitSimulationControlTask(op,
PPExtString(
"$exit"), {});
4696StmtEmitter::emitSeverityMessageTask(Operation *op,
PPExtString taskName,
4697 std::optional<unsigned> verbosity,
4698 StringAttr message, ValueRange operands) {
4700 emitError(op,
"SV attributes emission is unimplemented for the op");
4703 SmallPtrSet<Operation *, 8> ops;
4705 ps.addCallback({op,
true});
4711 if ((verbosity && *verbosity != 1) || message) {
4713 ps.scopedBox(PP::ibox0, [&]() {
4717 ps.addAsString(*verbosity);
4722 ps <<
"," << PP::space;
4723 ps.writeQuotedEscaped(message.getValue());
4725 for (
auto operand : operands) {
4726 ps <<
"," << PP::space;
4727 emitExpression(operand, ops);
4736 ps.addCallback({op,
false});
4737 emitLocationInfoAndNewLine(ops);
4741LogicalResult StmtEmitter::visitSV(FatalOp op) {
4742 return emitSeverityMessageTask(op,
PPExtString(
"$fatal"), op.getVerbosity(),
4743 op.getMessageAttr(), op.getSubstitutions());
4746LogicalResult StmtEmitter::visitSV(ErrorOp op) {
4747 return emitSeverityMessageTask(op,
PPExtString(
"$error"), {},
4748 op.getMessageAttr(), op.getSubstitutions());
4751LogicalResult StmtEmitter::visitSV(WarningOp op) {
4752 return emitSeverityMessageTask(op,
PPExtString(
"$warning"), {},
4753 op.getMessageAttr(), op.getSubstitutions());
4756LogicalResult StmtEmitter::visitSV(InfoOp op) {
4757 return emitSeverityMessageTask(op,
PPExtString(
"$info"), {},
4758 op.getMessageAttr(), op.getSubstitutions());
4761LogicalResult StmtEmitter::visitSV(ReadMemOp op) {
4762 SmallPtrSet<Operation *, 8> ops({op});
4765 ps.addCallback({op,
true});
4767 switch (op.getBaseAttr().getValue()) {
4768 case MemBaseTypeAttr::MemBaseBin:
4771 case MemBaseTypeAttr::MemBaseHex:
4776 ps.scopedBox(PP::ibox0, [&]() {
4777 ps.writeQuotedEscaped(op.getFilename());
4778 ps <<
"," << PP::space;
4779 emitExpression(op.getDest(), ops);
4783 ps.addCallback({op,
false});
4784 emitLocationInfoAndNewLine(ops);
4788LogicalResult StmtEmitter::visitSV(GenerateOp op) {
4789 emitSVAttributes(op);
4792 ps.addCallback({op,
true});
4793 ps <<
"generate" << PP::newline;
4795 setPendingNewline();
4796 emitStatementBlock(op.getBody().getBlocks().front());
4799 ps <<
"endgenerate";
4800 ps.addCallback({op,
false});
4801 setPendingNewline();
4805LogicalResult StmtEmitter::visitSV(GenerateCaseOp op) {
4806 emitSVAttributes(op);
4809 ps.addCallback({op,
true});
4811 ps.invokeWithStringOS([&](
auto &os) {
4812 emitter.printParamValue(
4813 op.getCond(), os, VerilogPrecedence::Selection,
4814 [&]() { return op->emitOpError(
"invalid case parameter"); });
4817 setPendingNewline();
4820 ArrayAttr
patterns = op.getCasePatterns();
4821 ArrayAttr caseNames = op.getCaseNames();
4822 MutableArrayRef<Region> regions = op.getCaseRegions();
4829 llvm::StringMap<size_t> nextGenIds;
4830 ps.scopedBox(PP::bbox2, [&]() {
4832 for (
size_t i = 0, e =
patterns.size(); i < e; ++i) {
4833 auto ®ion = regions[i];
4834 assert(region.hasOneBlock());
4835 Attribute patternAttr =
patterns[i];
4838 if (!isa<mlir::TypedAttr>(patternAttr))
4841 ps.invokeWithStringOS([&](
auto &os) {
4842 emitter.printParamValue(
4843 patternAttr, os, VerilogPrecedence::LowestPrecedence,
4844 [&]() {
return op->emitOpError(
"invalid case value"); });
4847 StringRef legalName =
4848 legalizeName(cast<StringAttr>(caseNames[i]).getValue(), nextGenIds,
4851 setPendingNewline();
4852 emitStatementBlock(region.getBlocks().front());
4855 setPendingNewline();
4861 ps.addCallback({op,
false});
4862 setPendingNewline();
4866LogicalResult StmtEmitter::visitSV(
ForOp op) {
4867 emitSVAttributes(op);
4868 llvm::SmallPtrSet<Operation *, 8> ops;
4869 ps.addCallback({op,
true});
4871 auto inductionVarName = op->getAttrOfType<StringAttr>(
"hw.verilogName");
4874 ps.scopedBox(PP::cbox0, [&]() {
4878 ps <<
"logic" << PP::nbsp;
4879 ps.invokeWithStringOS([&](
auto &os) {
4880 emitter.emitTypeDims(op.getInductionVar().getType(), op.getLoc(),
4885 [&]() { emitExpression(op.getLowerBound(), ops); },
PPExtString(
"="));
4890 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4891 [&]() { emitExpression(op.getUpperBound(), ops); },
4897 emitAssignLike([&]() { ps <<
PPExtString(inductionVarName); },
4898 [&]() { emitExpression(op.getStep(), ops); },
4902 ps << PP::neverbreak;
4903 setPendingNewline();
4904 emitStatementBlock(op.getBody().getBlocks().front());
4907 ps.addCallback({op,
false});
4908 emitLocationInfoAndNewLine(ops);
4913void StmtEmitter::emitAssertionLabel(Operation *op) {
4914 if (
auto label = op->getAttrOfType<StringAttr>(
"hw.verilogName"))
4920void StmtEmitter::emitAssertionMessage(StringAttr message, ValueRange args,
4921 SmallPtrSetImpl<Operation *> &ops,
4922 bool isConcurrent =
false) {
4925 ps << PP::space <<
"else" << PP::nbsp <<
"$error(";
4926 ps.scopedBox(PP::ibox0, [&]() {
4927 ps.writeQuotedEscaped(message.getValue());
4929 for (
auto arg : args) {
4930 ps <<
"," << PP::space;
4931 emitExpression(arg, ops);
4937template <
typename Op>
4938LogicalResult StmtEmitter::emitImmediateAssertion(Op op,
PPExtString opName) {
4940 emitError(op,
"SV attributes emission is unimplemented for the op");
4943 SmallPtrSet<Operation *, 8> ops;
4945 ps.addCallback({op,
true});
4946 ps.scopedBox(PP::ibox2, [&]() {
4947 emitAssertionLabel(op);
4948 ps.scopedBox(PP::cbox0, [&]() {
4950 switch (op.getDefer()) {
4951 case DeferAssert::Immediate:
4953 case DeferAssert::Observed:
4956 case DeferAssert::Final:
4961 ps.scopedBox(PP::ibox0, [&]() {
4962 emitExpression(op.getExpression(), ops);
4965 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops);
4969 ps.addCallback({op,
false});
4970 emitLocationInfoAndNewLine(ops);
4974LogicalResult StmtEmitter::visitSV(AssertOp op) {
4975 return emitImmediateAssertion(op,
PPExtString(
"assert"));
4978LogicalResult StmtEmitter::visitSV(AssumeOp op) {
4979 return emitImmediateAssertion(op,
PPExtString(
"assume"));
4982LogicalResult StmtEmitter::visitSV(CoverOp op) {
4983 return emitImmediateAssertion(op,
PPExtString(
"cover"));
4986template <
typename Op>
4987LogicalResult StmtEmitter::emitConcurrentAssertion(Op op,
PPExtString opName) {
4989 emitError(op,
"SV attributes emission is unimplemented for the op");
4992 SmallPtrSet<Operation *, 8> ops;
4994 ps.addCallback({op,
true});
4995 ps.scopedBox(PP::ibox2, [&]() {
4996 emitAssertionLabel(op);
4997 ps.scopedBox(PP::cbox0, [&]() {
4998 ps << opName << PP::nbsp <<
"property (";
4999 ps.scopedBox(PP::ibox0, [&]() {
5000 ps <<
"@(" <<
PPExtString(stringifyEventControl(op.getEvent()))
5002 emitExpression(op.getClock(), ops);
5003 ps <<
")" << PP::space;
5004 emitExpression(op.getProperty(), ops);
5007 emitAssertionMessage(op.getMessageAttr(), op.getSubstitutions(), ops,
5012 ps.addCallback({op,
false});
5013 emitLocationInfoAndNewLine(ops);
5017LogicalResult StmtEmitter::visitSV(AssertConcurrentOp op) {
5018 return emitConcurrentAssertion(op,
PPExtString(
"assert"));
5021LogicalResult StmtEmitter::visitSV(AssumeConcurrentOp op) {
5022 return emitConcurrentAssertion(op,
PPExtString(
"assume"));
5025LogicalResult StmtEmitter::visitSV(CoverConcurrentOp op) {
5026 return emitConcurrentAssertion(op,
PPExtString(
"cover"));
5031template <
typename Op>
5032LogicalResult StmtEmitter::emitPropertyAssertion(Op op,
PPExtString opName) {
5034 emitError(op,
"SV attributes emission is unimplemented for the op");
5044 Operation *parent = op->getParentOp();
5045 Value
property = op.getProperty();
5046 bool isTemporal = !
property.getType().isSignlessInteger(1);
5047 bool isProcedural = parent->hasTrait<ProceduralRegion>();
5048 bool emitAsImmediate = !isTemporal && isProcedural;
5051 SmallPtrSet<Operation *, 8> ops;
5053 ps.addCallback({op,
true});
5054 ps.scopedBox(PP::ibox2, [&]() {
5056 emitAssertionLabel(op);
5058 ps.scopedBox(PP::cbox0, [&]() {
5059 if (emitAsImmediate)
5060 ps << opName <<
"(";
5062 ps << opName << PP::nbsp <<
"property" << PP::nbsp <<
"(";
5064 Value clock = op.getClock();
5065 auto event = op.getEvent();
5067 ps.scopedBox(PP::ibox2, [&]() {
5068 PropertyEmitter(emitter, ops)
5069 .emitAssertPropertyBody(property, *event, clock, op.getDisable());
5072 ps.scopedBox(PP::ibox2, [&]() {
5073 PropertyEmitter(emitter, ops)
5074 .emitAssertPropertyBody(property, op.getDisable());
5079 ps.addCallback({op,
false});
5080 emitLocationInfoAndNewLine(ops);
5084LogicalResult StmtEmitter::visitSV(AssertPropertyOp op) {
5085 return emitPropertyAssertion(op,
PPExtString(
"assert"));
5088LogicalResult StmtEmitter::visitSV(AssumePropertyOp op) {
5089 return emitPropertyAssertion(op,
PPExtString(
"assume"));
5092LogicalResult StmtEmitter::visitSV(CoverPropertyOp op) {
5093 return emitPropertyAssertion(op,
PPExtString(
"cover"));
5096LogicalResult StmtEmitter::emitIfDef(Operation *op, MacroIdentAttr cond) {
5098 emitError(op,
"SV attributes emission is unimplemented for the op");
5101 cast<MacroDeclOp>(state.symbolCache.getDefinition(cond.getIdent()))
5102 .getMacroIdentifier());
5105 bool hasEmptyThen = op->getRegion(0).front().empty();
5107 ps <<
"`ifndef " << ident;
5109 ps <<
"`ifdef " << ident;
5111 SmallPtrSet<Operation *, 8> ops;
5113 emitLocationInfoAndNewLine(ops);
5116 emitStatementBlock(op->getRegion(0).front());
5118 if (!op->getRegion(1).empty()) {
5119 if (!hasEmptyThen) {
5121 ps <<
"`else // " << ident;
5122 setPendingNewline();
5124 emitStatementBlock(op->getRegion(1).front());
5131 setPendingNewline();
5139void StmtEmitter::emitBlockAsStatement(
5140 Block *block,
const SmallPtrSetImpl<Operation *> &locationOps,
5141 StringRef multiLineComment) {
5148 emitLocationInfoAndNewLine(locationOps);
5151 emitStatementBlock(*block);
5153 if (needsBeginEnd) {
5157 if (!multiLineComment.empty())
5158 ps <<
" // " << multiLineComment;
5159 setPendingNewline();
5163LogicalResult StmtEmitter::visitSV(OrderedOutputOp ooop) {
5165 for (
auto &op : ooop.getBody().front())
5170LogicalResult StmtEmitter::visitSV(IfOp op) {
5171 SmallPtrSet<Operation *, 8> ops;
5173 auto ifcondBox = PP::ibox2;
5175 emitSVAttributes(op);
5177 ps.addCallback({op,
true});
5178 ps <<
"if (" << ifcondBox;
5188 emitExpression(ifOp.getCond(), ops);
5189 ps << PP::end <<
")";
5190 emitBlockAsStatement(ifOp.getThenBlock(), ops);
5192 if (!ifOp.hasElse())
5196 Block *elseBlock = ifOp.getElseBlock();
5198 if (!nestedElseIfOp) {
5203 emitBlockAsStatement(elseBlock, ops);
5209 ifOp = nestedElseIfOp;
5210 ps <<
"else if (" << ifcondBox;
5212 ps.addCallback({op,
false});
5217LogicalResult StmtEmitter::visitSV(AlwaysOp op) {
5218 emitSVAttributes(op);
5219 SmallPtrSet<Operation *, 8> ops;
5223 auto printEvent = [&](AlwaysOp::Condition cond) {
5224 ps <<
PPExtString(stringifyEventControl(cond.event)) << PP::nbsp;
5225 ps.scopedBox(PP::cbox0, [&]() { emitExpression(cond.value, ops); });
5227 ps.addCallback({op,
true});
5229 switch (op.getNumConditions()) {
5235 printEvent(op.getCondition(0));
5240 ps.scopedBox(PP::cbox0, [&]() {
5241 printEvent(op.getCondition(0));
5242 for (
size_t i = 1, e = op.getNumConditions(); i != e; ++i) {
5243 ps << PP::space <<
"or" << PP::space;
5244 printEvent(op.getCondition(i));
5253 std::string comment;
5254 if (op.getNumConditions() == 0) {
5255 comment =
"always @*";
5257 comment =
"always @(";
5260 [&](Attribute eventAttr) {
5261 auto event = sv::EventControl(cast<IntegerAttr>(eventAttr).getInt());
5262 comment += stringifyEventControl(event);
5264 [&]() { comment +=
", "; });
5268 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5269 ps.addCallback({op,
false});
5273LogicalResult StmtEmitter::visitSV(AlwaysCombOp op) {
5274 emitSVAttributes(op);
5275 SmallPtrSet<Operation *, 8> ops;
5279 ps.addCallback({op,
true});
5280 StringRef opString =
"always_comb";
5281 if (state.options.noAlwaysComb)
5282 opString =
"always @(*)";
5285 emitBlockAsStatement(op.getBodyBlock(), ops, opString);
5286 ps.addCallback({op,
false});
5290LogicalResult StmtEmitter::visitSV(AlwaysFFOp op) {
5291 emitSVAttributes(op);
5293 SmallPtrSet<Operation *, 8> ops;
5297 ps.addCallback({op,
true});
5298 ps <<
"always_ff @(";
5299 ps.scopedBox(PP::cbox0, [&]() {
5300 ps <<
PPExtString(stringifyEventControl(op.getClockEdge())) << PP::nbsp;
5301 emitExpression(op.getClock(), ops);
5302 if (op.getResetStyle() == ResetType::AsyncReset) {
5303 ps << PP::nbsp <<
"or" << PP::space
5304 <<
PPExtString(stringifyEventControl(*op.getResetEdge())) << PP::nbsp;
5305 emitExpression(op.getReset(), ops);
5312 std::string comment;
5313 comment +=
"always_ff @(";
5314 comment += stringifyEventControl(op.getClockEdge());
5315 if (op.getResetStyle() == ResetType::AsyncReset) {
5317 comment += stringifyEventControl(*op.getResetEdge());
5321 if (op.getResetStyle() == ResetType::NoReset)
5322 emitBlockAsStatement(op.getBodyBlock(), ops, comment);
5325 emitLocationInfoAndNewLine(ops);
5326 ps.scopedBox(PP::bbox2, [&]() {
5332 if (op.getResetStyle() == ResetType::AsyncReset &&
5333 *op.getResetEdge() == sv::EventControl::AtNegEdge)
5335 emitExpression(op.getReset(), ops);
5337 emitBlockAsStatement(op.getResetBlock(), ops);
5340 emitBlockAsStatement(op.getBodyBlock(), ops);
5345 ps <<
" // " << comment;
5346 setPendingNewline();
5348 ps.addCallback({op,
false});
5352LogicalResult StmtEmitter::visitSV(InitialOp op) {
5353 emitSVAttributes(op);
5354 SmallPtrSet<Operation *, 8> ops;
5357 ps.addCallback({op,
true});
5359 emitBlockAsStatement(op.getBodyBlock(), ops,
"initial");
5360 ps.addCallback({op,
false});
5364LogicalResult StmtEmitter::visitSV(CaseOp op) {
5365 emitSVAttributes(op);
5366 SmallPtrSet<Operation *, 8> ops, emptyOps;
5369 ps.addCallback({op,
true});
5370 if (op.getValidationQualifier() !=
5371 ValidationQualifierTypeEnum::ValidationQualifierPlain)
5372 ps <<
PPExtString(circt::sv::stringifyValidationQualifierTypeEnum(
5373 op.getValidationQualifier()))
5375 const char *opname =
nullptr;
5376 switch (op.getCaseStyle()) {
5377 case CaseStmtType::CaseStmt:
5380 case CaseStmtType::CaseXStmt:
5383 case CaseStmtType::CaseZStmt:
5387 ps << opname <<
" (";
5388 ps.scopedBox(PP::ibox0, [&]() {
5389 emitExpression(op.getCond(), ops);
5392 emitLocationInfoAndNewLine(ops);
5394 ps.scopedBox(PP::bbox2, [&]() {
5395 for (
auto &caseInfo : op.getCases()) {
5397 auto &
pattern = caseInfo.pattern;
5399 llvm::TypeSwitch<CasePattern *>(
pattern.get())
5400 .Case<CaseBitPattern>([&](
auto bitPattern) {
5403 ps.invokeWithStringOS([&](
auto &os) {
5404 os << bitPattern->getWidth() <<
"'b";
5405 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
5406 os <<
getLetter(bitPattern->getBit(e - bit - 1));
5409 .Case<CaseEnumPattern>([&](
auto enumPattern) {
5410 ps <<
PPExtString(emitter.fieldNameResolver.getEnumFieldName(
5411 cast<hw::EnumFieldAttr>(enumPattern->attr())));
5413 .Case<CaseDefaultPattern>([&](
auto) { ps <<
"default"; })
5414 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
5417 emitBlockAsStatement(caseInfo.block, emptyOps);
5423 ps.addCallback({op,
false});
5424 emitLocationInfoAndNewLine(ops);
5428LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
5429 bool doNotPrint = op.getDoNotPrint();
5430 if (doNotPrint && !state.options.emitBindComments)
5435 emitSVAttributes(op);
5437 ps.addCallback({op,
true});
5440 <<
"/* This instance is elsewhere emitted as a bind statement."
5443 op->emitWarning() <<
"is emitted as a bind statement but has SV "
5444 "attributes. The attributes will not be emitted.";
5447 SmallPtrSet<Operation *, 8> ops;
5452 state.symbolCache.getDefinition(op.getReferencedModuleNameAttr());
5453 assert(moduleOp &&
"Invalid IR");
5457 if (!op.getParameters().empty()) {
5460 bool printed =
false;
5462 llvm::zip(op.getParameters(),
5463 moduleOp->getAttrOfType<ArrayAttr>(
"parameters"))) {
5464 auto param = cast<ParamDeclAttr>(std::get<0>(params));
5465 auto modParam = cast<ParamDeclAttr>(std::get<1>(params));
5467 if (param.getValue() == modParam.getValue())
5472 ps <<
" #(" << PP::bbox2 << PP::newline;
5475 ps <<
"," << PP::newline;
5479 state.globalNames.getParameterVerilogName(moduleOp, param.getName()));
5481 ps.invokeWithStringOS([&](
auto &os) {
5482 emitter.printParamValue(param.getValue(), os, [&]() {
5483 return op->emitOpError(
"invalid instance parameter '")
5484 << param.getName().getValue() <<
"' value";
5490 ps << PP::end << PP::newline <<
")";
5497 SmallVector<Value> instPortValues(modPortInfo.size());
5498 op.getValues(instPortValues, modPortInfo);
5499 emitInstancePortList(op, modPortInfo, instPortValues);
5501 ps.addCallback({op,
false});
5502 emitLocationInfoAndNewLine(ops);
5507 setPendingNewline();
5512LogicalResult StmtEmitter::visitStmt(InstanceChoiceOp op) {
5514 Operation *choiceMacroDeclOp = state.symbolCache.getDefinition(
5515 op->getAttrOfType<FlatSymbolRefAttr>(
"hw.choiceTarget"));
5520 Operation *defaultModuleOp =
5521 state.symbolCache.getDefinition(op.getDefaultModuleNameAttr());
5523 SmallVector<Value> instPortValues(modPortInfo.size());
5524 op.getValues(instPortValues, modPortInfo);
5525 emitInstancePortList(op, modPortInfo, instPortValues);
5527 SmallPtrSet<Operation *, 8> ops;
5529 ps.addCallback({op,
false});
5530 emitLocationInfoAndNewLine(ops);
5535void StmtEmitter::emitInstancePortList(Operation *op,
5537 ArrayRef<Value> instPortValues) {
5538 SmallPtrSet<Operation *, 8> ops;
5541 auto containingModule = cast<HWModuleOp>(emitter.currentModuleOp);
5542 ModulePortInfo containingPortList(containingModule.getPortList());
5547 size_t maxNameLength = 0;
5548 for (
auto &elt : modPortInfo) {
5549 maxNameLength = std::max(maxNameLength, elt.getVerilogName().size());
5552 auto getWireForValue = [&](Value result) {
5553 return result.getUsers().begin()->getOperand(0);
5557 bool isFirst =
true;
5558 bool isZeroWidth =
false;
5560 for (
size_t portNum = 0, portEnd = modPortInfo.
size(); portNum < portEnd;
5562 auto &modPort = modPortInfo.
at(portNum);
5564 Value portVal = instPortValues[portNum];
5569 bool shouldPrintComma =
true;
5571 shouldPrintComma =
false;
5572 for (
size_t i = portNum + 1, e = modPortInfo.
size(); i != e; ++i)
5574 shouldPrintComma =
true;
5579 if (shouldPrintComma)
5582 emitLocationInfoAndNewLine(ops);
5597 ps.scopedBox(isZeroWidth ? PP::neverbox :
PP::
ibox2, [&]() {
5598 auto modPortName = modPort.getVerilogName();
5600 ps.spaces(maxNameLength - modPortName.size() + 1);
5602 ps.scopedBox(PP::ibox0, [&]() {
5609 if (!modPort.isOutput()) {
5611 isa_and_nonnull<ConstantOp>(portVal.getDefiningOp()))
5612 ps <<
"/* Zero width */";
5614 emitExpression(portVal, ops, LowestPrecedence);
5615 }
else if (portVal.use_empty()) {
5616 ps <<
"/* unused */";
5617 }
else if (portVal.hasOneUse() &&
5618 (output = dyn_cast_or_null<OutputOp>(
5619 portVal.getUses().begin()->getOwner()))) {
5624 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
5626 containingPortList.atOutput(outputPortNo).getVerilogName());
5628 portVal = getWireForValue(portVal);
5629 emitExpression(portVal, ops);
5635 if (!isFirst || isZeroWidth) {
5636 emitLocationInfoAndNewLine(ops);
5649LogicalResult StmtEmitter::visitSV(BindOp op) {
5650 emitter.emitBind(op);
5651 assert(state.pendingNewline);
5655LogicalResult StmtEmitter::visitSV(InterfaceOp op) {
5656 emitComment(op.getCommentAttr());
5658 emitSVAttributes(op);
5661 ps.addCallback({op,
true});
5663 setPendingNewline();
5665 emitStatementBlock(*op.getBodyBlock());
5667 ps <<
"endinterface" << PP::newline;
5668 ps.addCallback({op,
false});
5669 setPendingNewline();
5673LogicalResult StmtEmitter::visitSV(InterfaceSignalOp op) {
5675 emitSVAttributes(op);
5677 ps.addCallback({op,
true});
5679 ps << PP::neverbox <<
"// ";
5680 ps.invokeWithStringOS([&](
auto &os) {
5685 ps.invokeWithStringOS(
5686 [&](
auto &os) { emitter.printUnpackedTypePostfix(op.getType(), os); });
5690 ps.addCallback({op,
false});
5691 setPendingNewline();
5695LogicalResult StmtEmitter::visitSV(InterfaceModportOp op) {
5697 ps.addCallback({op,
true});
5701 llvm::interleaveComma(op.getPorts(), ps, [&](
const Attribute &portAttr) {
5702 auto port = cast<ModportStructAttr>(portAttr);
5703 ps << PPExtString(stringifyEnum(port.getDirection().getValue())) <<
" ";
5704 auto *signalDecl = state.symbolCache.getDefinition(port.getSignal());
5705 ps << PPExtString(getSymOpName(signalDecl));
5709 ps.addCallback({op,
false});
5710 setPendingNewline();
5714LogicalResult StmtEmitter::visitSV(AssignInterfaceSignalOp op) {
5716 ps.addCallback({op,
true});
5717 SmallPtrSet<Operation *, 8> emitted;
5720 emitExpression(op.getIface(), emitted);
5721 ps <<
"." <<
PPExtString(op.getSignalName()) <<
" = ";
5722 emitExpression(op.getRhs(), emitted);
5724 ps.addCallback({op,
false});
5725 setPendingNewline();
5729LogicalResult StmtEmitter::visitSV(MacroDefOp op) {
5730 auto decl = op.getReferencedMacro(&state.symbolCache);
5733 ps.addCallback({op,
true});
5735 if (decl.getArgs()) {
5737 llvm::interleaveComma(*decl.getArgs(), ps, [&](
const Attribute &name) {
5738 ps << cast<StringAttr>(name);
5742 if (!op.getFormatString().empty()) {
5744 emitTextWithSubstitutions(ps, op.getFormatString(), op, {},
5747 ps.addCallback({op,
false});
5748 setPendingNewline();
5752void StmtEmitter::emitStatement(Operation *op) {
5759 if (isa<ltl::LTLDialect, debug::DebugDialect>(op->getDialect()))
5763 if (succeeded(dispatchStmtVisitor(op)) || succeeded(dispatchSVVisitor(op)) ||
5764 succeeded(dispatchVerifVisitor(op)))
5767 emitOpError(op,
"emission to Verilog not supported");
5768 emitPendingNewlineIfNeeded();
5769 ps <<
"unknown MLIR operation " <<
PPExtString(op->getName().getStringRef());
5770 setPendingNewline();
5781 StmtEmitter &stmtEmitter) {
5788 if (isa<IfDefProceduralOp>(op->getParentOp()))
5796 SmallVector<Value, 8> exprsToScan(op->getOperands());
5801 while (!exprsToScan.empty()) {
5802 Operation *expr = exprsToScan.pop_back_val().getDefiningOp();
5809 if (
auto readInout = dyn_cast<sv::ReadInOutOp>(expr)) {
5810 auto *defOp = readInout.getOperand().getDefiningOp();
5817 if (isa<sv::WireOp>(defOp))
5822 if (!isa<RegOp, LogicOp>(defOp))
5828 if (isa<LogicOp>(defOp) &&
5829 stmtEmitter.emitter.expressionsEmittedIntoDecl.count(defOp))
5833 if (llvm::all_of(defOp->getResult(0).getUsers(), [&](Operation *op) {
5834 return isa<ReadInOutOp, PAssignOp, AssignOp>(op);
5842 exprsToScan.append(expr->getOperands().begin(),
5843 expr->getOperands().end());
5849 if (expr->getBlock() != op->getBlock())
5854 if (!stmtEmitter.emitter.expressionsEmittedIntoDecl.count(expr))
5861template <
class AssignTy>
5863 AssignTy singleAssign;
5864 if (llvm::all_of(op->getUsers(), [&](Operation *user) {
5865 if (hasSVAttributes(user))
5868 if (auto assign = dyn_cast<AssignTy>(user)) {
5871 singleAssign = assign;
5875 return isa<ReadInOutOp>(user);
5877 return singleAssign;
5883 return llvm::all_of(op2->getUsers(), [&](Operation *user) {
5887 if (op1->getBlock() != user->getBlock())
5893 return op1->isBeforeInBlock(user);
5897LogicalResult StmtEmitter::emitDeclaration(Operation *op) {
5898 emitSVAttributes(op);
5899 auto value = op->getResult(0);
5900 SmallPtrSet<Operation *, 8> opsForLocation;
5901 opsForLocation.insert(op);
5903 ps.addCallback({op,
true});
5906 auto type = value.getType();
5909 ps.scopedBox(isZeroBit ? PP::neverbox :
PP::
ibox2, [&]() {
5910 unsigned targetColumn = 0;
5911 unsigned column = 0;
5914 if (maxDeclNameWidth > 0)
5915 targetColumn += maxDeclNameWidth + 1;
5918 ps <<
"// Zero width: " <<
PPExtString(word) << PP::space;
5919 }
else if (!word.empty()) {
5921 column += word.size();
5922 unsigned numSpaces = targetColumn > column ? targetColumn - column : 1;
5923 ps.spaces(numSpaces);
5924 column += numSpaces;
5927 SmallString<8> typeString;
5930 llvm::raw_svector_ostream stringStream(typeString);
5935 if (maxTypeWidth > 0)
5936 targetColumn += maxTypeWidth + 1;
5937 unsigned numSpaces = 0;
5938 if (!typeString.empty()) {
5940 column += typeString.size();
5943 if (targetColumn > column)
5944 numSpaces = targetColumn - column;
5945 ps.spaces(numSpaces);
5946 column += numSpaces;
5952 ps.invokeWithStringOS(
5953 [&](
auto &os) { emitter.printUnpackedTypePostfix(type, os); });
5956 if (state.options.printDebugInfo) {
5957 if (
auto innerSymOp = dyn_cast<hw::InnerSymbolOpInterface>(op)) {
5958 auto innerSym = innerSymOp.getInnerSymAttr();
5959 if (innerSym && !innerSym.empty()) {
5961 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
5967 if (
auto localparam = dyn_cast<LocalParamOp>(op)) {
5968 ps << PP::space <<
"=" << PP::space;
5969 ps.invokeWithStringOS([&](
auto &os) {
5970 emitter.printParamValue(localparam.getValue(), os, [&]() {
5971 return op->emitOpError(
"invalid localparam value");
5976 if (
auto regOp = dyn_cast<RegOp>(op)) {
5977 if (
auto initValue = regOp.getInit()) {
5978 ps << PP::space <<
"=" << PP::space;
5979 ps.scopedBox(PP::ibox0, [&]() {
5980 emitExpression(initValue, opsForLocation, LowestPrecedence,
5989 if (isa<sv::WireOp>(op) &&
5990 !op->getParentOp()->hasTrait<ProceduralRegion>() &&
5993 if (
auto singleAssign = getSingleAssignAndCheckUsers<AssignOp>(op)) {
5994 auto *source = singleAssign.getSrc().getDefiningOp();
5998 if (!source || isa<ConstantOp>(source) ||
5999 op->getNextNode() == singleAssign) {
6000 ps << PP::space <<
"=" << PP::space;
6001 ps.scopedBox(PP::ibox0, [&]() {
6002 emitExpression(singleAssign.getSrc(), opsForLocation,
6006 emitter.assignsInlined.insert(singleAssign);
6014 if (isa<LogicOp>(op) && op->getParentOp()->hasTrait<ProceduralRegion>() &&
6017 if (
auto singleAssign = getSingleAssignAndCheckUsers<BPAssignOp>(op)) {
6020 auto *source = singleAssign.getSrc().getDefiningOp();
6024 if (!source || isa<ConstantOp>(source) ||
6027 ps << PP::space <<
"=" << PP::space;
6028 ps.scopedBox(PP::ibox0, [&]() {
6029 emitExpression(singleAssign.getSrc(), opsForLocation,
6034 emitter.assignsInlined.insert(singleAssign);
6035 emitter.expressionsEmittedIntoDecl.insert(op);
6042 ps.addCallback({op,
false});
6043 emitLocationInfoAndNewLine(opsForLocation);
6047void StmtEmitter::collectNamesAndCalculateDeclarationWidths(Block &block) {
6050 NameCollector collector(emitter);
6051 collector.collectNames(block);
6054 maxDeclNameWidth = collector.getMaxDeclNameWidth();
6055 maxTypeWidth = collector.getMaxTypeWidth();
6058void StmtEmitter::emitStatementBlock(Block &body) {
6059 ps.scopedBox(PP::bbox2, [&]() {
6064 llvm::SaveAndRestore<size_t> x(maxDeclNameWidth);
6065 llvm::SaveAndRestore<size_t> x2(maxTypeWidth);
6070 if (!isa<IfDefProceduralOp>(body.getParentOp()))
6071 collectNamesAndCalculateDeclarationWidths(body);
6074 for (
auto &op : body) {
6081void ModuleEmitter::emitStatement(Operation *op) {
6082 StmtEmitter(*
this, state.options).emitStatement(op);
6087void ModuleEmitter::emitSVAttributes(Operation *op) {
6095 setPendingNewline();
6102void ModuleEmitter::emitHWGeneratedModule(HWModuleGeneratedOp module) {
6103 auto verilogName =
module.getVerilogModuleNameAttr();
6105 ps <<
"// external generated module " <<
PPExtString(verilogName.getValue())
6107 setPendingNewline();
6116void ModuleEmitter::emitBind(BindOp op) {
6118 emitError(op,
"SV attributes emission is unimplemented for the op");
6119 InstanceOp inst = op.getReferencedInstance(&state.symbolCache);
6125 Operation *childMod =
6126 state.symbolCache.getDefinition(inst.getReferencedModuleNameAttr());
6130 ps.addCallback({op,
true});
6131 ps <<
"bind " <<
PPExtString(parentVerilogName.getValue()) << PP::nbsp
6132 <<
PPExtString(childVerilogName.getValue()) << PP::nbsp
6134 bool isFirst =
true;
6135 ps.scopedBox(PP::bbox2, [&]() {
6136 auto parentPortInfo = parentMod.getPortList();
6140 size_t maxNameLength = 0;
6141 for (
auto &elt : childPortInfo) {
6142 auto portName = elt.getVerilogName();
6143 elt.name = Builder(inst.getContext()).getStringAttr(portName);
6144 maxNameLength = std::max(maxNameLength, elt.getName().size());
6147 SmallVector<Value> instPortValues(childPortInfo.size());
6148 inst.getValues(instPortValues, childPortInfo);
6150 for (
auto [idx, elt] :
llvm::enumerate(childPortInfo)) {
6152 Value portVal = instPortValues[idx];
6158 bool shouldPrintComma =
true;
6160 shouldPrintComma =
false;
6161 for (
size_t i = idx + 1, e = childPortInfo.size(); i != e; ++i)
6163 shouldPrintComma =
true;
6168 if (shouldPrintComma)
6181 ps << PP::neverbox <<
"//";
6185 ps.nbsp(maxNameLength - elt.getName().size());
6187 llvm::SmallPtrSet<Operation *, 4> ops;
6188 if (elt.isOutput()) {
6189 assert((portVal.hasOneUse() || portVal.use_empty()) &&
6190 "output port must have either single or no use");
6191 if (portVal.use_empty()) {
6192 ps <<
"/* unused */";
6193 }
else if (
auto output = dyn_cast_or_null<OutputOp>(
6194 portVal.getUses().begin()->getOwner())) {
6197 size_t outputPortNo = portVal.getUses().begin()->getOperandNumber();
6199 parentPortList.atOutput(outputPortNo).getVerilogName());
6201 portVal = portVal.getUsers().begin()->getOperand(0);
6202 ExprEmitter(*
this, ops)
6203 .emitExpression(portVal, LowestPrecedence,
6207 ExprEmitter(*
this, ops)
6208 .emitExpression(portVal, LowestPrecedence,
6221 ps.addCallback({op,
false});
6222 setPendingNewline();
6225void ModuleEmitter::emitBindInterface(BindInterfaceOp op) {
6227 emitError(op,
"SV attributes emission is unimplemented for the op");
6229 auto instance = op.getReferencedInstance(&state.symbolCache);
6231 auto *
interface = op->getParentOfType<ModuleOp>().lookupSymbol(
6232 instance.getInterfaceType().getInterface());
6234 ps.addCallback({op,
true});
6235 ps <<
"bind " <<
PPExtString(instantiator) << PP::nbsp
6236 <<
PPExtString(cast<InterfaceOp>(*interface).getSymName()) << PP::nbsp
6238 ps.addCallback({op,
false});
6239 setPendingNewline();
6242void ModuleEmitter::emitParameters(Operation *module, ArrayAttr params) {
6246 auto printParamType = [&](Type type, Attribute defaultValue,
6247 SmallString<8> &result) {
6249 llvm::raw_svector_ostream sstream(result);
6254 if (
auto intAttr = dyn_cast<IntegerAttr>(defaultValue))
6255 if (intAttr.getValue().getBitWidth() == 32)
6257 if (
auto fpAttr = dyn_cast<FloatAttr>(defaultValue))
6258 if (fpAttr.getType().isF64())
6261 if (isa<NoneType>(type))
6268 if (
auto intType = type_dyn_cast<IntegerType>(type))
6269 if (intType.getWidth() == 32) {
6270 sstream <<
"/*integer*/";
6274 printPackedType(type, sstream, module->getLoc(),
6282 size_t maxTypeWidth = 0;
6283 SmallString<8> scratch;
6284 for (
auto param : params) {
6285 auto paramAttr = cast<ParamDeclAttr>(param);
6287 printParamType(paramAttr.getType(), paramAttr.getValue(), scratch);
6288 maxTypeWidth = std::max(scratch.size(), maxTypeWidth);
6291 if (maxTypeWidth > 0)
6294 ps.scopedBox(PP::bbox2, [&]() {
6295 ps << PP::newline <<
"#(";
6296 ps.scopedBox(PP::cbox0, [&]() {
6299 [&](Attribute param) {
6300 auto paramAttr = cast<ParamDeclAttr>(param);
6301 auto defaultValue = paramAttr.getValue();
6303 printParamType(paramAttr.getType(), defaultValue, scratch);
6304 if (!scratch.empty())
6306 if (scratch.size() < maxTypeWidth)
6307 ps.nbsp(maxTypeWidth - scratch.size());
6309 ps <<
PPExtString(state.globalNames.getParameterVerilogName(
6310 module, paramAttr.getName()));
6314 ps.invokeWithStringOS([&](
auto &os) {
6316 return module->emitError("parameter '")
6317 << paramAttr.getName().getValue()
6318 << "' has invalid value";
6323 [&]() { ps <<
"," << PP::newline; });
6329void ModuleEmitter::emitPortList(Operation *module,
6331 bool emitAsTwoStateType) {
6333 if (portInfo.
size())
6334 emitLocationInfo(module->getLoc());
6338 bool hasOutputs =
false, hasZeroWidth =
false;
6339 size_t maxTypeWidth = 0, lastNonZeroPort = -1;
6340 SmallVector<SmallString<8>, 16> portTypeStrings;
6342 for (
size_t i = 0, e = portInfo.
size(); i < e; ++i) {
6343 auto port = portInfo.
at(i);
6347 lastNonZeroPort = i;
6350 portTypeStrings.push_back({});
6352 llvm::raw_svector_ostream stringStream(portTypeStrings.back());
6354 module->getLoc(), {},
true,
true, emitAsTwoStateType);
6357 maxTypeWidth = std::max(portTypeStrings.back().size(), maxTypeWidth);
6360 if (maxTypeWidth > 0)
6364 ps.scopedBox(PP::bbox2, [&]() {
6365 for (
size_t portIdx = 0, e = portInfo.
size(); portIdx != e;) {
6366 auto lastPort = e - 1;
6369 auto portType = portInfo.
at(portIdx).
type;
6373 bool isZeroWidth =
false;
6378 ps << (isZeroWidth ?
"// " :
" ");
6382 auto thisPortDirection = portInfo.
at(portIdx).
dir;
6383 switch (thisPortDirection) {
6384 case ModulePort::Direction::Output:
6387 case ModulePort::Direction::Input:
6388 ps << (hasOutputs ?
"input " :
"input ");
6390 case ModulePort::Direction::InOut:
6391 ps << (hasOutputs ?
"inout " :
"inout ");
6394 bool emitWireInPorts = state.options.emitWireInPorts;
6395 if (emitWireInPorts)
6399 if (!portTypeStrings[portIdx].
empty())
6400 ps << portTypeStrings[portIdx];
6401 if (portTypeStrings[portIdx].size() < maxTypeWidth)
6402 ps.nbsp(maxTypeWidth - portTypeStrings[portIdx].size());
6404 size_t startOfNamePos =
6405 (hasOutputs ? 7 : 6) + (emitWireInPorts ? 5 : 0) + maxTypeWidth;
6411 ps.invokeWithStringOS(
6412 [&](
auto &os) { printUnpackedTypePostfix(portType, os); });
6415 auto innerSym = portInfo.
at(portIdx).
getSym();
6416 if (state.options.printDebugInfo && innerSym && !innerSym.empty()) {
6418 ps.invokeWithStringOS([&](
auto &os) { os << innerSym; });
6423 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6427 if (
auto loc = portInfo.
at(portIdx).
loc)
6428 emitLocationInfo(loc);
6438 if (!state.options.disallowPortDeclSharing) {
6439 while (portIdx != e && portInfo.
at(portIdx).
dir == thisPortDirection &&
6442 auto port = portInfo.
at(portIdx);
6446 bool isZeroWidth =
false;
6451 ps << (isZeroWidth ?
"// " :
" ");
6454 ps.nbsp(startOfNamePos);
6457 StringRef name = port.getVerilogName();
6461 ps.invokeWithStringOS(
6462 [&](
auto &os) { printUnpackedTypePostfix(port.type, os); });
6465 auto sym = port.getSym();
6466 if (state.options.printDebugInfo && sym && !sym.empty())
6467 ps <<
" /* inner_sym: " <<
PPExtString(sym.getSymName().getValue())
6471 if (portIdx != lastNonZeroPort && portIdx != lastPort)
6475 if (
auto loc = port.loc)
6476 emitLocationInfo(loc);
6487 if (!portInfo.
size()) {
6489 SmallPtrSet<Operation *, 8> moduleOpSet;
6490 moduleOpSet.insert(module);
6491 emitLocationInfoAndNewLine(moduleOpSet);
6494 ps <<
");" << PP::newline;
6495 setPendingNewline();
6499void ModuleEmitter::emitHWModule(
HWModuleOp module) {
6500 currentModuleOp =
module;
6502 emitComment(module.getCommentAttr());
6503 emitSVAttributes(module);
6505 ps.addCallback({module,
true});
6509 emitParameters(module, module.getParameters());
6513 assert(state.pendingNewline);
6516 StmtEmitter(*
this, state.options).emitStatementBlock(*module.getBodyBlock());
6519 ps.addCallback({module,
false});
6521 setPendingNewline();
6523 currentModuleOp =
nullptr;
6526void ModuleEmitter::emitFunc(FuncOp func) {
6528 if (func.isDeclaration())
6531 currentModuleOp = func;
6533 ps.addCallback({func,
true});
6537 StmtEmitter(*
this, state.options).emitStatementBlock(*func.getBodyBlock());
6539 ps <<
"endfunction";
6541 currentModuleOp =
nullptr;
6550 explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}
6557 void emit(emit::FileListOp op);
6560 void emit(Block *block);
6562 void emitOp(emit::RefOp op);
6563 void emitOp(emit::VerbatimOp op);
6567 for (Operation &op : *block) {
6568 TypeSwitch<Operation *>(&op)
6569 .Case<emit::VerbatimOp, emit::RefOp>([&](
auto op) {
emitOp(op); })
6570 .Case<VerbatimOp, IfDefOp, MacroDefOp, sv::FuncDPIImportOp>(
6571 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6572 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6573 .Case<BindInterfaceOp>(
6574 [&](
auto op) { ModuleEmitter(state).emitBindInterface(op); })
6575 .Case<TypeScopeOp>([&](
auto typedecls) {
6576 ModuleEmitter(state).emitStatement(typedecls);
6579 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6585 for (
auto sym : op.getFiles()) {
6586 auto fileName = cast<FlatSymbolRefAttr>(sym).getAttr();
6588 auto it = state.fileMapping.find(fileName);
6589 if (it == state.fileMapping.end()) {
6590 emitOpError(op,
" references an invalid file: ") << sym;
6594 auto file = cast<emit::FileOp>(it->second);
6595 ps << PP::neverbox <<
PPExtString(file.getFileName()) << PP::end
6602 StringAttr target = op.getTargetAttr().getAttr();
6603 auto *targetOp = state.symbolCache.getDefinition(target);
6604 assert(isa<emit::Emittable>(targetOp) &&
"target must be emittable");
6606 TypeSwitch<Operation *>(targetOp)
6607 .Case<sv::FuncOp>([&](
auto func) { ModuleEmitter(state).emitFunc(func); })
6608 .Case<hw::HWModuleOp>(
6609 [&](
auto module) { ModuleEmitter(state).emitHWModule(module); })
6610 .Case<TypeScopeOp>([&](
auto typedecls) {
6611 ModuleEmitter(state).emitStatement(typedecls);
6614 [&](
auto op) { emitOpError(op,
"cannot be emitted to a file"); });
6620 SmallPtrSet<Operation *, 8> ops;
6625 StringRef text = op.getText();
6629 const auto &[lhs, rhs] = text.split(
'\n');
6633 ps << PP::end << PP::newline << PP::neverbox;
6635 }
while (!text.empty());
6638 emitLocationInfoAndNewLine(ops);
6656 auto collectInstanceSymbolsAndBinds = [&](Operation *moduleOp) {
6657 moduleOp->walk([&](Operation *op) {
6659 if (
auto name = op->getAttrOfType<InnerSymAttr>(
6662 SymbolTable::getSymbolAttrName()),
6663 name.getSymName(), op);
6664 if (isa<BindOp>(op))
6670 auto collectPorts = [&](
auto moduleOp) {
6671 auto portInfo = moduleOp.getPortList();
6672 for (
auto [i, p] : llvm::enumerate(portInfo)) {
6673 if (!p.attrs || p.attrs.empty())
6675 for (NamedAttribute portAttr : p.attrs) {
6676 if (
auto sym = dyn_cast<InnerSymAttr>(portAttr.getValue())) {
6685 DenseMap<StringAttr, SmallVector<emit::FileOp>> symbolsToFiles;
6686 for (
auto file :
designOp.getOps<emit::FileOp>())
6687 for (
auto refs : file.getOps<emit::RefOp>())
6688 symbolsToFiles[refs.getTargetAttr().getAttr()].push_back(file);
6690 SmallString<32> outputPath;
6691 for (
auto &op : *
designOp.getBody()) {
6694 bool isFileOp = isa<emit::FileOp, emit::FileListOp>(&op);
6696 bool hasFileName =
false;
6697 bool emitReplicatedOps = !isFileOp;
6698 bool addToFilelist = !isFileOp;
6704 auto attr = op.getAttrOfType<hw::OutputFileAttr>(
"output_file");
6706 LLVM_DEBUG(llvm::dbgs() <<
"Found output_file attribute " << attr
6707 <<
" on " << op <<
"\n";);
6708 if (!attr.isDirectory())
6711 emitReplicatedOps = attr.getIncludeReplicatedOps().getValue();
6712 addToFilelist = !attr.getExcludeFromFilelist().getValue();
6715 auto separateFile = [&](Operation *op, Twine defaultFileName =
"") {
6720 if (!defaultFileName.isTriviallyEmpty()) {
6721 llvm::sys::path::append(outputPath, defaultFileName);
6723 op->emitError(
"file name unspecified");
6725 llvm::sys::path::append(outputPath,
"error.out");
6729 auto destFile = StringAttr::get(op->getContext(), outputPath);
6730 auto &file =
files[destFile];
6731 file.ops.push_back(info);
6732 file.emitReplicatedOps = emitReplicatedOps;
6733 file.addToFilelist = addToFilelist;
6734 file.isVerilog = outputPath.ends_with(
".sv");
6739 if (!attr || attr.isDirectory()) {
6740 auto excludeFromFileListAttr =
6741 BoolAttr::get(op->getContext(), !addToFilelist);
6742 auto includeReplicatedOpsAttr =
6743 BoolAttr::get(op->getContext(), emitReplicatedOps);
6744 auto outputFileAttr = hw::OutputFileAttr::get(
6745 destFile, excludeFromFileListAttr, includeReplicatedOpsAttr);
6746 op->setAttr(
"output_file", outputFileAttr);
6752 TypeSwitch<Operation *>(&op)
6753 .Case<emit::FileOp, emit::FileListOp>([&](
auto file) {
6755 fileMapping.try_emplace(file.getSymNameAttr(), file);
6756 separateFile(file, file.getFileName());
6758 .Case<emit::FragmentOp>([&](
auto fragment) {
6761 .Case<HWModuleOp>([&](
auto mod) {
6763 auto sym = mod.getNameAttr();
6766 collectInstanceSymbolsAndBinds(mod);
6768 if (
auto it = symbolsToFiles.find(sym); it != symbolsToFiles.end()) {
6769 if (it->second.size() != 1 || attr) {
6772 op.emitError(
"modules can be emitted to a single file");
6780 if (attr || separateModules)
6786 .Case<InterfaceOp>([&](InterfaceOp intf) {
6791 for (
auto &op : *intf.getBodyBlock())
6792 if (
auto symOp = dyn_cast<mlir::SymbolOpInterface>(op))
6793 if (
auto name = symOp.getNameAttr())
6797 if (attr || separateModules)
6798 separateFile(intf, intf.getSymName() +
".sv");
6808 .Case<VerbatimOp, IfDefOp, MacroDefOp, IncludeOp, FuncDPIImportOp>(
6809 [&](Operation *op) {
6815 separateFile(op,
"");
6817 .Case<FuncOp>([&](
auto op) {
6823 separateFile(op,
"");
6827 .Case<HWGeneratorSchemaOp>([&](HWGeneratorSchemaOp schemaOp) {
6830 .Case<HierPathOp>([&](HierPathOp hierPathOp) {
6839 separateFile(op,
"");
6841 .Case<BindOp>([&](
auto op) {
6843 separateFile(op,
"bindfile.sv");
6848 .Case<MacroDeclOp>([&](
auto op) {
6851 .Case<sv::ReserveNamesOp>([](
auto op) {
6854 .Case<om::ClassLike>([&](
auto op) {
6857 .Case<om::ConstantOp>([&](
auto op) {
6860 .Default([&](
auto *) {
6861 op.emitError(
"unknown operation (SharedEmitterState::gatherFiles)");
6881 size_t lastReplicatedOp = 0;
6883 bool emitHeaderInclude =
6886 if (emitHeaderInclude)
6889 size_t numReplicatedOps =
6894 DenseSet<emit::FragmentOp> includedFragments;
6895 for (
const auto &opInfo : file.
ops) {
6896 Operation *op = opInfo.op;
6900 for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
6906 if (
auto fragments =
6908 for (
auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
6912 op->emitError(
"cannot find referenced fragment ") << sym;
6915 emit::FragmentOp fragment = it->second;
6916 if (includedFragments.insert(fragment).second) {
6917 thingsToEmit.emplace_back(it->second);
6923 thingsToEmit.emplace_back(op);
6928 for (; lastReplicatedOp < numReplicatedOps; lastReplicatedOp++)
6933 TypeSwitch<Operation *>(op)
6934 .Case<
HWModuleOp>([&](
auto op) { ModuleEmitter(state).emitHWModule(op); })
6935 .Case<HWModuleExternOp>([&](
auto op) {
6938 .Case<HWModuleGeneratedOp>(
6939 [&](
auto op) { ModuleEmitter(state).emitHWGeneratedModule(op); })
6940 .Case<HWGeneratorSchemaOp>([&](
auto op) { })
6941 .Case<BindOp>([&](
auto op) { ModuleEmitter(state).emitBind(op); })
6942 .Case<InterfaceOp, VerbatimOp, IfDefOp>(
6943 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6944 .Case<TypeScopeOp>([&](
auto typedecls) {
6945 ModuleEmitter(state).emitStatement(typedecls);
6947 .Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
6949 .Case<MacroDefOp, FuncDPIImportOp>(
6950 [&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6951 .Case<FuncOp>([&](
auto op) { ModuleEmitter(state).emitFunc(op); })
6952 .Case<IncludeOp>([&](
auto op) { ModuleEmitter(state).emitStatement(op); })
6953 .Default([&](
auto *op) {
6954 state.encounteredError =
true;
6955 op->emitError(
"unknown operation (ExportVerilog::emitOperation)");
6962 llvm::formatted_raw_ostream &os,
6963 StringAttr fileName,
bool parallelize) {
6964 MLIRContext *context =
designOp->getContext();
6968 parallelize &= context->isMultithreadingEnabled();
6979 size_t lineOffset = 0;
6980 for (
auto &entry : thingsToEmit) {
6981 entry.verilogLocs.setStream(os);
6982 if (
auto *op = entry.getOperation()) {
6987 state.addVerilogLocToOps(lineOffset, fileName);
6989 os << entry.getStringData();
6994 if (state.encounteredError)
7012 SmallString<256> buffer;
7013 llvm::raw_svector_ostream tmpStream(buffer);
7014 llvm::formatted_raw_ostream rs(tmpStream);
7025 for (
auto &entry : thingsToEmit) {
7028 auto *op = entry.getOperation();
7030 auto lineOffset = os.getLine() + 1;
7031 os << entry.getStringData();
7035 entry.verilogLocs.updateIRWithLoc(lineOffset, fileName, context);
7038 entry.verilogLocs.setStream(os);
7045 state.addVerilogLocToOps(0, fileName);
7061 module.emitWarning()
7062 << "`emitReplicatedOpsToHeader` option is enabled but an header is "
7063 "created only at SplitExportVerilog";
7072 for (
const auto &it : emitter.
files) {
7073 list.emplace_back(
"\n// ----- 8< ----- FILE \"" + it.first.str() +
7074 "\" ----- 8< -----\n\n");
7080 std::string contents(
"\n// ----- 8< ----- FILE \"" + it.first().str() +
7081 "\" ----- 8< -----\n\n");
7082 for (
auto &name : it.second)
7083 contents += name.str() +
"\n";
7084 list.emplace_back(contents);
7087 llvm::formatted_raw_ostream rs(os);
7091 emitter.
emitOps(list, rs, StringAttr::get(module.getContext(),
""),
7100 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7102 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7103 if (failed(failableParallelForEach(
7104 module->getContext(), modulesToPrepare,
7105 [&](
auto op) { return prepareHWModule(op, options); })))
7112struct ExportVerilogPass
7113 :
public circt::impl::ExportVerilogBase<ExportVerilogPass> {
7114 ExportVerilogPass(raw_ostream &os) : os(os) {}
7115 void runOnOperation()
override {
7117 mlir::OpPassManager preparePM(
"builtin.module");
7120 auto &modulePM = preparePM.nestAny();
7122 if (failed(runPipeline(preparePM, getOperation())))
7123 return signalPassFailure();
7126 return signalPassFailure();
7133struct ExportVerilogStreamOwnedPass :
public ExportVerilogPass {
7134 ExportVerilogStreamOwnedPass(std::unique_ptr<llvm::raw_ostream> os)
7135 : ExportVerilogPass{*os} {
7136 owned = std::move(os);
7140 std::unique_ptr<llvm::raw_ostream> owned;
7144std::unique_ptr<mlir::Pass>
7146 return std::make_unique<ExportVerilogStreamOwnedPass>(std::move(os));
7149std::unique_ptr<mlir::Pass>
7151 return std::make_unique<ExportVerilogPass>(os);
7162static std::unique_ptr<llvm::ToolOutputFile>
7166 SmallString<128> outputFilename(dirname);
7168 auto outputDir = llvm::sys::path::parent_path(outputFilename);
7171 std::error_code error = llvm::sys::fs::create_directories(outputDir);
7173 emitter.
designOp.emitError(
"cannot create output directory \"")
7174 << outputDir <<
"\": " << error.message();
7180 std::string errorMessage;
7181 auto output = mlir::openOutputFile(outputFilename, &errorMessage);
7183 emitter.
designOp.emitError(errorMessage);
7200 llvm::formatted_raw_ostream rs(output->os());
7206 StringAttr::get(fileName.getContext(), output->getFilename()),
7212 StringRef dirname) {
7223 bool insertSuccess =
7225 .insert({StringAttr::get(module.getContext(),
circtHeader),
7231 if (!insertSuccess) {
7232 module.emitError() << "tried to emit a heder to " << circtHeader
7233 << ", but the file is used as an output too.";
7239 parallelForEach(module->getContext(), emitter.
files.begin(),
7240 emitter.
files.end(), [&](
auto &it) {
7241 createSplitOutputFile(it.first, it.second, dirname,
7246 SmallString<128> filelistPath(dirname);
7247 llvm::sys::path::append(filelistPath,
"filelist.f");
7249 std::string errorMessage;
7250 auto output = mlir::openOutputFile(filelistPath, &errorMessage);
7252 module->emitError(errorMessage);
7256 for (
const auto &it : emitter.
files) {
7257 if (it.second.addToFilelist)
7258 output->os() << it.first.str() <<
"\n";
7267 for (
auto &name : it.second)
7268 output->os() << name.str() <<
"\n";
7279 SmallVector<HWEmittableModuleLike> modulesToPrepare;
7281 [&](HWEmittableModuleLike op) { modulesToPrepare.push_back(op); });
7282 if (failed(failableParallelForEach(
7283 module->getContext(), modulesToPrepare,
7284 [&](
auto op) { return prepareHWModule(op, options); })))
7292struct ExportSplitVerilogPass
7293 :
public circt::impl::ExportSplitVerilogBase<ExportSplitVerilogPass> {
7294 ExportSplitVerilogPass(StringRef directory) {
7295 directoryName = directory.str();
7297 void runOnOperation()
override {
7299 mlir::OpPassManager preparePM(
"builtin.module");
7304 if (failed(runPipeline(preparePM, getOperation())))
7305 return signalPassFailure();
7308 return signalPassFailure();
7313std::unique_ptr<mlir::Pass>
7315 return std::make_unique<ExportSplitVerilogPass>(directory);
assert(baseType &&"element must be base type")
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static bool hasSVAttributes(Operation *op)
static void emitOperation(VerilogEmitterState &state, Operation *op)
static LogicalResult exportVerilogImpl(ModuleOp module, llvm::raw_ostream &os)
static void emitDim(Attribute width, raw_ostream &os, Location loc, ModuleEmitter &emitter, bool downTo)
Emit a single dimension.
static int compareLocs(Location lhs, Location rhs)
static bool isDuplicatableExpression(Operation *op)
static TypedAttr getInt32Attr(MLIRContext *ctx, uint32_t value)
StringRef getVerilogValueName(Value val)
Retrieve value's verilog name from IR.
static void sortLocationVector(TVector &vec)
static bool hasStructType(Type type)
Return true if type has a struct type as a subtype.
static StringRef getVerilogDeclWord(Operation *op, const ModuleEmitter &emitter)
Return the word (e.g.
static bool isOkToBitSelectFrom(Value v)
Most expressions are invalid to bit-select from in Verilog, but some things are ok.
static LogicalResult exportSplitVerilogImpl(ModuleOp module, StringRef dirname)
static int compareLocsImpl(mlir::NameLoc lhs, mlir::NameLoc rhs)
static void emitZeroWidthIndexingValue(PPS &os)
Emits a known-safe token that is legal when indexing into singleton arrays.
static bool checkDominanceOfUsers(Operation *op1, Operation *op2)
Return true if op1 dominates users of op2.
static void emitDims(ArrayRef< Attribute > dims, raw_ostream &os, Location loc, ModuleEmitter &emitter)
Emit a list of packed dimensions.
static bool isExpressionEmittedInlineIntoProceduralDeclaration(Operation *op, StmtEmitter &stmtEmitter)
Given an operation corresponding to a VerilogExpression, determine whether it is safe to emit inline ...
static StringRef getPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef fileName, StringRef dirname, SharedEmitterState &emitter)
static void collectAndUniqueLocations(Location loc, SmallPtrSetImpl< Attribute > &locationSet)
Pull apart any fused locations into the location set, such that they are uniqued.
static Value isZeroExtension(Value value)
If the specified extension is a zero extended version of another value, return the shorter value,...
static void createSplitOutputFile(StringAttr fileName, FileInfo &file, StringRef dirname, SharedEmitterState &emitter)
static void getTypeDims(SmallVectorImpl< Attribute > &dims, Type type, Location loc)
Push this type's dimension into a vector.
static StringRef getInputPortVerilogName(Operation *module, size_t portArgNum)
Return the verilog name of the port for the module.
static StringRef getTwoStateIntegerAtomType(size_t width)
Return a 2-state integer atom type name if the width matches.
static TypedAttr getIntAttr(MLIRContext *ctx, Type t, const APInt &value)
static BlockStatementCount countStatements(Block &block)
Compute how many statements are within this block, for begin/end markers.
static bool haveMatchingDims(Type a, Type b, Location loc)
True iff 'a' and 'b' have the same wire dims.
static Type stripUnpackedTypes(Type type)
Given a set of known nested types (those supported by this pass), strip off leading unpacked types.
FailureOr< int > dispatchCompareLocations(Location lhs, Location rhs)
static bool isExpressionUnableToInline(Operation *op, const LoweringOptions &options)
Return true if we are unable to ever inline the specified operation.
void emitFunctionSignature(ModuleEmitter &emitter, PPS &ps, FuncOp op, bool isAutomatic=false, bool emitAsTwoStateType=false)
static AssignTy getSingleAssignAndCheckUsers(Operation *op)
static bool hasLeadingUnpackedType(Type type)
Return true if the type has a leading unpacked type.
static bool printPackedTypeImpl(Type type, raw_ostream &os, Location loc, SmallVectorImpl< Attribute > &dims, bool implicitIntType, bool singleBitDefaultType, ModuleEmitter &emitter, Type optionalAliasType={}, bool emitAsTwoStateType=false)
Output the basic type that consists of packed and primitive types.
static void emitSVAttributesImpl(PPS &ps, ArrayAttr attrs, bool mayBreak)
Emit SystemVerilog attributes.
static bool isDuplicatableNullaryExpression(Operation *op)
Return true for nullary operations that are better emitted multiple times as inline expression (when ...
static IfOp findNestedElseIf(Block *elseBlock)
Find a nested IfOp in an else block that can be printed as else if instead of nesting it into a new b...
StringRef circtHeaderInclude
static ValueRange getNonOverlappingConcatSubrange(Value value)
For a value concat(..., delay(const(true), 1, 0)), return ....
static StringRef legalizeName(StringRef name, llvm::StringMap< size_t > &nextGeneratedNameIDs)
Legalize the given name such that it only consists of valid identifier characters in Verilog and does...
static void printParamValue(OpAsmPrinter &p, Operation *, Attribute value, Type resultType)
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
static InstancePath empty
RewritePatternSet pattern
void emit(emit::FragmentOp op)
FileEmitter(VerilogEmitterState &state)
void emit(emit::FileOp op)
void emitOp(emit::RefOp op)
LocationEmitter(LoweringOptions::LocationInfoStyle style, Location loc)
void emitLocationSetInfo(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
LocationEmitter(LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Operation * > &ops)
Track the output verilog line,column number information for every op.
void setStream(llvm::formatted_raw_ostream &f)
Set the output stream.
void updateIRWithLoc(unsigned lineOffset, StringAttr fileName, MLIRContext *context)
Called after the verilog has been exported and the corresponding locations are recorded in the map.
This class wraps an operation or a fixed string that should be emitted.
Operation * getOperation() const
If the value is an Operation*, return it. Otherwise return null.
OpLocMap verilogLocs
Verilog output location information for entry.
void setString(StringRef value)
This method transforms the entry from an operation to a string value.
This stores lookup tables to make manipulating and working with the IR more efficient.
void freeze()
Mark the cache as frozen, which allows it to be shared across threads.
void addDefinition(mlir::StringAttr modSymbol, mlir::StringAttr name, mlir::Operation *op, size_t port=~0ULL)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
This helps visit TypeOp nodes.
This helps visit TypeOp nodes.
ResultType dispatchTypeOpVisitor(Operation *op, ExtraArgs... args)
ResultType visitUnhandledTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any combinational operations that are not handled by the concrete visitor...
ResultType visitInvalidTypeOp(Operation *op, ExtraArgs... args)
This callback is invoked on any non-expression operations.
Note: Callable class must implement a callable with signature: void (Data)
Wrap the TokenStream with a helper for CallbackTokens, to record the print events on the stream.
auto scopedBox(T &&t, Callable &&c, Token close=EndToken())
Open a box, invoke the lambda, and close it after.
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
GlobalNameTable legalizeGlobalNames(ModuleOp topLevel, const LoweringOptions &options)
Rewrite module names and interfaces to not conflict with each other or with Verilog keywords.
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
DenseMap< StringAttr, Operation * > FileMapping
Mapping from symbols to file operations.
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
StringRef getSymOpName(Operation *symOp)
Return the verilog name of the operations that can define a symbol.
LogicalResult lowerHWInstanceChoices(mlir::ModuleOp module)
Generates the macros used by instance choices.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
bool isCombinational(Operation *op)
Return true if the specified operation is a combinational logic op.
StringRef getVerilogModuleName(Operation *module)
StringAttr getVerilogModuleNameAttr(Operation *module)
Returns the verilog module name attribute or symbol name of any module-like operations.
mlir::Type getCanonicalType(mlir::Type type)
PP
Send one of these to TokenStream to add the corresponding token.
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
circt::hw::InOutType InOutType
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createExportSplitVerilogPass(llvm::StringRef directory="./")
std::unique_ptr< mlir::Pass > createHWLowerInstanceChoicesPass()
mlir::LogicalResult exportVerilog(mlir::ModuleOp module, llvm::raw_ostream &os)
Export a module containing HW, and SV dialect code.
mlir::LogicalResult exportSplitVerilog(mlir::ModuleOp module, llvm::StringRef dirname)
Export a module containing HW, and SV dialect code, as one file per SV module.
const char * getCirctVersionComment()
std::unique_ptr< mlir::Pass > createLegalizeAnonEnumsPass()
std::unique_ptr< mlir::Pass > createExportVerilogPass()
void appendPossiblyAbsolutePath(llvm::SmallVectorImpl< char > &base, const llvm::Twine &suffix)
Append a path to an existing path, replacing it if the other path is absolute.
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
llvm::raw_string_ostream & os
void emitLocationInfo(Location loc)
Return the location information in the specified style.
Impl(llvm::raw_string_ostream &os, LoweringOptions::LocationInfoStyle style, const SmallPtrSetImpl< Attribute > &locationSet)
void emitLocationInfo(FileLineColLoc loc)
void emitLocationSetInfoImpl(const SmallPtrSetImpl< Attribute > &locationSet)
Emit the location information of locationSet to sstr.
void emitLocationInfo(mlir::NameLoc loc)
LoweringOptions::LocationInfoStyle style
void emitLocationInfo(mlir::CallSiteLoc loc)
void printFileLineColSetInfo(llvm::SmallVector< FileLineColLoc, 8 > locVector)
Information to control the emission of a list of operations into a file.
bool isVerilog
If true, the file is known to be (system) verilog source code.
SmallVector< OpFileInfo, 1 > ops
The operations to be emitted into a separate file, and where among the replicated per-file operations...
bool isHeader
If true, the file is a header.
bool emitReplicatedOps
Whether to emit the replicated per-file operations.
Information to control the emission of a single operation into a file.
This class tracks the top-level state for the emitters, which is built and then shared across all per...
llvm::MapVector< StringAttr, FileInfo > files
The additional files to emit, with the output file name as the key into the map.
std::vector< StringOrOpToEmit > EmissionList
FileMapping fileMapping
Tracks the referenceable files through their symbol.
hw::HWSymbolCache symbolCache
A cache of symbol -> defining ops built once and used by each of the verilog module emitters.
void collectOpsForFile(const FileInfo &fileInfo, EmissionList &thingsToEmit, bool emitHeader=false)
Given a FileInfo, collect all the replicated and designated operations that go into it and append the...
ModuleOp designOp
The MLIR module to emit.
void emitOps(EmissionList &thingsToEmit, llvm::formatted_raw_ostream &os, StringAttr fileName, bool parallelize)
Actually emit the collected list of operations and strings to the specified file.
FileInfo rootFile
The main file that collects all operations that are neither replicated per-file ops nor specifically ...
llvm::StringMap< SmallVector< StringAttr > > fileLists
The various file lists and their contents to emit.
SmallPtrSet< Operation *, 8 > modulesContainingBinds
This is a set is populated at "gather" time, containing the hw.module operations that have a sv....
const LoweringOptions & options
std::atomic< bool > encounteredError
Whether any error has been encountered during emission.
FragmentMapping fragmentMapping
Tracks referenceable files through their symbol.
void gatherFiles(bool separateModules)
Organize the operations in the root MLIR module into output files to be generated.
SmallVector< Operation *, 0 > replicatedOps
A list of operations replicated in each output file (e.g., sv.verbatim or sv.ifdef without dedicated ...
const GlobalNameTable globalNames
Information about renamed global symbols, parameters, etc.
Options which control the emission from CIRCT to Verilog.
bool omitVersionComment
If true, do not emit a version comment at the top of each verilog file.
LocationInfoStyle
This option controls emitted location information style.
bool disallowMuxInlining
If true, every mux expression is spilled to a wire.
bool caseInsensitiveKeywords
If true, then unique names that collide with keywords case insensitively.
bool emitReplicatedOpsToHeader
If true, replicated ops are emitted to a header file.
bool allowExprInEventControl
If true, expressions are allowed in the sensitivity list of always statements, otherwise they are for...
This holds a decoded list of input/inout and output ports for a module or instance.
PortInfo & at(size_t idx)
This holds the name, type, direction of a module's ports.
StringRef getVerilogName() const
InnerSymAttr getSym() const
Struct defining a field. Used in structs.
Buffer tokens for clients that need to adjust things.
SmallVectorImpl< Token > BufferVec
String wrapper to indicate string has external storage.
String wrapper to indicate string needs to be saved.