23#include "mlir/IR/Dominance.h"
24#include "mlir/IR/ImplicitLocOpBuilder.h"
25#include "mlir/IR/Threading.h"
26#include "mlir/Pass/Pass.h"
27#include "llvm/ADT/EquivalenceClasses.h"
28#include "llvm/ADT/SetVector.h"
29#include "llvm/ADT/TypeSwitch.h"
30#include "llvm/Support/Debug.h"
32#define DEBUG_TYPE "infer-resets"
36#define GEN_PASS_DEF_INFERRESETS
37#include "circt/Dialect/FIRRTL/Passes.h.inc"
41using circt::igraph::InstanceOpInterface;
44using llvm::BumpPtrAllocator;
46using llvm::SmallDenseSet;
47using llvm::SmallSetVector;
49using mlir::InferTypeOpInterface;
52using namespace firrtl;
61 if (
auto arg = dyn_cast<BlockArgument>(reset)) {
62 auto module = cast<FModuleOp>(arg.getParentRegion()->getParentOp());
63 return {
module.getPortNameAttr(arg.getArgNumber()), module};
65 auto *op = reset.getDefiningOp();
66 return {op->getAttrOfType<StringAttr>(
"name"),
67 op->getParentOfType<FModuleOp>()};
96 std::optional<unsigned> existingPort;
99 ResetDomain() =
default;
102 ResetDomain(Value rootReset)
103 : rootReset(rootReset), resetName(
getResetName(rootReset)),
104 resetType(rootReset.getType()) {}
107 explicit operator bool()
const {
return static_cast<bool>(rootReset); }
111inline bool operator==(
const ResetDomain &a,
const ResetDomain &b) {
112 return (a.isTop == b.isTop && a.resetName == b.resetName &&
113 a.resetType == b.resetType);
115inline bool operator!=(
const ResetDomain &a,
const ResetDomain &b) {
124 auto it = cache.find(type);
125 if (it != cache.end())
127 auto nullBit = [&]() {
129 builder, UIntType::get(builder.getContext(), 1,
true),
134 .
Case<ClockType>([&](
auto type) {
135 return builder.create<AsClockPrimOp>(nullBit());
137 .Case<AsyncResetType>([&](
auto type) {
138 return builder.create<AsAsyncResetPrimOp>(nullBit());
140 .Case<SIntType, UIntType>([&](
auto type) {
141 return builder.create<ConstantOp>(
142 type, APInt::getZero(type.getWidth().value_or(1)));
144 .Case<BundleType>([&](
auto type) {
145 auto wireOp = builder.create<WireOp>(type);
146 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
147 auto fieldType = type.getElementTypePreservingConst(i);
150 builder.create<SubfieldOp>(fieldType, wireOp.getResult(), i);
153 return wireOp.getResult();
155 .Case<FVectorType>([&](
auto type) {
156 auto wireOp = builder.create<WireOp>(type);
158 builder, type.getElementTypePreservingConst(), cache);
159 for (
unsigned i = 0, e = type.getNumElements(); i < e; ++i) {
160 auto acc = builder.create<SubindexOp>(zero.getType(),
161 wireOp.getResult(), i);
164 return wireOp.getResult();
166 .Case<ResetType, AnalogType>(
167 [&](
auto type) {
return builder.create<InvalidValueOp>(type); })
169 llvm_unreachable(
"switch handles all types");
172 cache.insert({type, value});
189 Value reset, Value resetValue) {
193 bool resetValueUsed =
false;
195 for (
auto &use : target.getUses()) {
196 Operation *useOp = use.getOwner();
197 builder.setInsertionPoint(useOp);
198 TypeSwitch<Operation *>(useOp)
201 .Case<ConnectOp, MatchingConnectOp>([&](
auto op) {
202 if (op.getDest() != target)
204 LLVM_DEBUG(llvm::dbgs() <<
" - Insert mux into " << op <<
"\n");
206 builder.create<MuxPrimOp>(reset, resetValue, op.getSrc());
207 op.getSrcMutable().assign(muxOp);
208 resetValueUsed =
true;
211 .Case<SubfieldOp>([&](
auto op) {
213 builder.create<SubfieldOp>(resetValue, op.getFieldIndexAttr());
215 resetValueUsed =
true;
217 resetSubValue.erase();
220 .Case<SubindexOp>([&](
auto op) {
222 builder.create<SubindexOp>(resetValue, op.getIndexAttr());
224 resetValueUsed =
true;
226 resetSubValue.erase();
229 .Case<SubaccessOp>([&](
auto op) {
230 if (op.getInput() != target)
233 builder.create<SubaccessOp>(resetValue, op.getIndex());
235 resetValueUsed =
true;
237 resetSubValue.erase();
240 return resetValueUsed;
255 bool operator<(
const ResetSignal &other)
const {
return field < other.field; }
256 bool operator==(
const ResetSignal &other)
const {
257 return field == other.field;
259 bool operator!=(
const ResetSignal &other)
const {
return !(*
this == other); }
279using ResetDrives = SmallVector<ResetDrive, 1>;
282using ResetNetwork = llvm::iterator_range<
283 llvm::EquivalenceClasses<ResetSignal>::member_iterator>;
286enum class ResetKind { Async, Sync };
288static StringRef resetKindToStringRef(
const ResetKind &kind) {
290 case ResetKind::Async:
292 case ResetKind::Sync:
295 llvm_unreachable(
"unhandled reset kind");
311 static bool isEqual(
const ResetSignal &lhs,
const ResetSignal &rhs) {
320 case ResetKind::Async:
321 return os <<
"async";
322 case ResetKind::Sync:
432struct InferResetsPass
433 :
public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
434 void runOnOperation()
override;
435 void runOnOperationInner();
438 using InferResetsBase::InferResetsBase;
439 InferResetsPass(
const InferResetsPass &other) : InferResetsBase(other) {}
444 void traceResets(CircuitOp circuit);
445 void traceResets(InstanceOp inst);
446 void traceResets(Value dst, Value src, Location loc);
447 void traceResets(Value value);
448 void traceResets(Type dstType, Value dst,
unsigned dstID, Type srcType,
449 Value src,
unsigned srcID, Location loc);
451 LogicalResult inferAndUpdateResets();
452 FailureOr<ResetKind> inferReset(ResetNetwork net);
453 LogicalResult updateReset(ResetNetwork net, ResetKind kind);
459 LogicalResult collectAnnos(CircuitOp circuit);
465 FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
467 LogicalResult buildDomains(CircuitOp circuit);
468 void buildDomains(FModuleOp module,
const InstancePath &instPath,
470 unsigned indent = 0);
472 LogicalResult determineImpl();
473 LogicalResult determineImpl(FModuleOp module, ResetDomain &domain);
475 LogicalResult implementFullReset();
476 LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
477 void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
479 LogicalResult verifyNoAbstractReset();
485 ResetNetwork getResetNetwork(ResetSignal signal) {
486 return llvm::make_range(resetClasses.findLeader(signal),
487 resetClasses.member_end());
491 ResetDrives &getResetDrives(ResetNetwork net) {
492 return resetDrives[*net.begin()];
497 ResetSignal guessRoot(ResetNetwork net);
498 ResetSignal guessRoot(ResetSignal signal) {
499 return guessRoot(getResetNetwork(signal));
506 llvm::EquivalenceClasses<ResetSignal> resetClasses;
509 DenseMap<ResetSignal, ResetDrives> resetDrives;
514 DenseMap<Operation *, Value> annotatedResets;
518 MapVector<FModuleOp, SmallVector<std::pair<ResetDomain, InstancePath>, 1>>
525 std::unique_ptr<InstancePathCache> instancePathCache;
529void InferResetsPass::runOnOperation() {
530 runOnOperationInner();
531 resetClasses = llvm::EquivalenceClasses<ResetSignal>();
533 annotatedResets.clear();
535 instancePathCache.reset(
nullptr);
536 markAnalysesPreserved<InstanceGraph>();
539void InferResetsPass::runOnOperationInner() {
540 instanceGraph = &getAnalysis<InstanceGraph>();
541 instancePathCache = std::make_unique<InstancePathCache>(*instanceGraph);
544 traceResets(getOperation());
547 if (failed(inferAndUpdateResets()))
548 return signalPassFailure();
551 if (failed(collectAnnos(getOperation())))
552 return signalPassFailure();
555 if (failed(buildDomains(getOperation())))
556 return signalPassFailure();
559 if (failed(determineImpl()))
560 return signalPassFailure();
563 if (failed(implementFullReset()))
564 return signalPassFailure();
567 if (failed(verifyNoAbstractReset()))
568 return signalPassFailure();
572 return std::make_unique<InferResetsPass>();
575ResetSignal InferResetsPass::guessRoot(ResetNetwork net) {
576 ResetDrives &drives = getResetDrives(net);
577 ResetSignal bestSignal = *net.begin();
578 unsigned bestNumDrives = -1;
580 for (
auto signal : net) {
582 if (isa_and_nonnull<InvalidValueOp>(
583 signal.field.getValue().getDefiningOp()))
588 unsigned numDrives = 0;
589 for (
auto &drive : drives)
590 if (drive.dst == signal)
596 if (numDrives < bestNumDrives) {
597 bestNumDrives = numDrives;
616 .
Case<BundleType>([](
auto type) {
618 for (
auto e : type.getElements())
623 [](
auto type) {
return getMaxFieldID(type.getElementType()) + 1; })
624 .Default([](
auto) {
return 0; });
628 assert(index < type.getNumElements());
630 for (
unsigned i = 0; i < index; ++i)
638 assert(type.getNumElements() &&
"Bundle must have >0 fields");
640 for (
const auto &e : llvm::enumerate(type.getElements())) {
642 if (fieldID < numSubfields)
644 fieldID -= numSubfields;
646 assert(
false &&
"field id outside bundle");
652 if (oldType.isGround()) {
658 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
666 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
667 if (vectorType.getNumElements() == 0)
684 if (
auto arg = dyn_cast<BlockArgument>(value)) {
685 auto module = cast<FModuleOp>(arg.getOwner()->getParentOp());
686 string +=
module.getPortName(arg.getArgNumber());
690 auto *op = value.getDefiningOp();
691 return TypeSwitch<Operation *, bool>(op)
692 .Case<InstanceOp, MemOp>([&](
auto op) {
693 string += op.getName();
696 op.getPortName(cast<OpResult>(value).getResultNumber()).getValue();
699 .Case<WireOp, NodeOp, RegOp, RegResetOp>([&](
auto op) {
700 string += op.getName();
703 .Default([](
auto) {
return false; });
707 SmallString<64> name;
712 auto type = value.getType();
715 if (
auto bundleType = type_dyn_cast<BundleType>(type)) {
718 auto &element = bundleType.getElements()[index];
721 string += element.name.getValue();
724 localID = localID -
getFieldID(bundleType, index);
725 }
else if (
auto vecType = type_dyn_cast<FVectorType>(type)) {
728 type = vecType.getElementType();
735 llvm_unreachable(
"unsupported type");
747 return TypeSwitch<Type, bool>(type)
749 return type.getRecursiveTypeProperties().hasUninferredReset;
751 .Default([](
auto) {
return false; });
758void InferResetsPass::traceResets(CircuitOp circuit) {
760 llvm::dbgs() <<
"\n";
761 debugHeader(
"Tracing uninferred resets") <<
"\n\n";
764 SmallVector<std::pair<FModuleOp, SmallVector<Operation *>>> moduleToOps;
766 for (
auto module : circuit.getOps<FModuleOp>())
767 moduleToOps.push_back({module, {}});
770 getAnalysis<hw::InnerSymbolTableCollection>()};
772 mlir::parallelForEach(circuit.getContext(), moduleToOps, [](
auto &e) {
773 e.first.walk([&](Operation *op) {
777 op->getResultTypes(),
778 [](mlir::Type type) { return typeContainsReset(type); }) ||
779 llvm::any_of(op->getOperandTypes(), typeContainsReset))
780 e.second.push_back(op);
784 for (
auto &[_, ops] : moduleToOps)
785 for (auto *op : ops) {
786 TypeSwitch<Operation *>(op)
787 .Case<FConnectLike>([&](
auto op) {
788 traceResets(op.getDest(), op.getSrc(), op.getLoc());
790 .Case<InstanceOp>([&](
auto op) { traceResets(op); })
791 .Case<RefSendOp>([&](
auto op) {
793 traceResets(op.getType().getType(), op.getResult(), 0,
794 op.getBase().getType().getPassiveType(), op.getBase(),
797 .Case<RefResolveOp>([&](
auto op) {
799 traceResets(op.getType(), op.getResult(), 0,
800 op.getRef().getType().getType(), op.getRef(), 0,
803 .Case<Forceable>([&](Forceable op) {
804 if (
auto node = dyn_cast<NodeOp>(op.getOperation()))
805 traceResets(node.getResult(), node.getInput(), node.getLoc());
807 if (op.isForceable())
808 traceResets(op.getDataType(), op.getData(), 0, op.getDataType(),
809 op.getDataRef(), 0, op.getLoc());
811 .Case<RWProbeOp>([&](RWProbeOp op) {
812 auto ist = irn.lookup(op.getTarget());
815 auto baseType = op.getType().getType();
816 traceResets(baseType, op.getResult(), 0, baseType.getPassiveType(),
817 ref.getValue(), ref.getFieldID(), op.getLoc());
819 .Case<UninferredResetCastOp, ConstCastOp, RefCastOp>([&](
auto op) {
820 traceResets(op.getResult(), op.getInput(), op.getLoc());
822 .Case<InvalidValueOp>([&](
auto op) {
831 auto type = op.getType();
834 LLVM_DEBUG(llvm::dbgs() <<
"Uniquify " << op <<
"\n");
835 ImplicitLocOpBuilder builder(op->getLoc(), op);
837 llvm::make_early_inc_range(
llvm::drop_begin(op->getUses()))) {
843 auto newOp = builder.create<InvalidValueOp>(type);
848 .Case<SubfieldOp>([&](
auto op) {
851 BundleType bundleType = op.getInput().getType();
852 auto index = op.getFieldIndex();
853 traceResets(op.getType(), op.getResult(), 0,
854 bundleType.getElements()[index].type, op.getInput(),
858 .Case<SubindexOp, SubaccessOp>([&](
auto op) {
871 FVectorType vectorType = op.getInput().getType();
872 traceResets(op.getType(), op.getResult(), 0,
873 vectorType.getElementType(), op.getInput(),
877 .Case<RefSubOp>([&](RefSubOp op) {
879 auto aggType = op.getInput().getType().getType();
880 uint64_t fieldID = TypeSwitch<FIRRTLBaseType, uint64_t>(aggType)
881 .Case<FVectorType>([](
auto type) {
884 .Case<BundleType>([&](
auto type) {
887 traceResets(op.getType(), op.getResult(), 0,
888 op.getResult().getType(), op.getInput(), fieldID,
896void InferResetsPass::traceResets(InstanceOp inst) {
898 auto module = inst.getReferencedModule<FModuleOp>(*instanceGraph);
901 LLVM_DEBUG(llvm::dbgs() <<
"Visiting instance " << inst.getName() <<
"\n");
904 for (
const auto &it :
llvm::enumerate(inst.getResults())) {
905 auto dir =
module.getPortDirection(it.index());
906 Value dstPort =
module.getArgument(it.index());
907 Value srcPort = it.value();
908 if (dir == Direction::Out)
909 std::swap(dstPort, srcPort);
910 traceResets(dstPort, srcPort, it.value().getLoc());
916void InferResetsPass::traceResets(Value dst, Value src, Location loc) {
918 traceResets(dst.getType(), dst, 0, src.getType(), src, 0, loc);
923void InferResetsPass::traceResets(Type dstType, Value dst,
unsigned dstID,
924 Type srcType, Value src,
unsigned srcID,
926 if (
auto dstBundle = type_dyn_cast<BundleType>(dstType)) {
927 auto srcBundle = type_cast<BundleType>(srcType);
928 for (
unsigned dstIdx = 0, e = dstBundle.getNumElements(); dstIdx < e;
930 auto dstField = dstBundle.getElements()[dstIdx].name;
931 auto srcIdx = srcBundle.getElementIndex(dstField);
934 auto &dstElt = dstBundle.getElements()[dstIdx];
935 auto &srcElt = srcBundle.getElements()[*srcIdx];
937 traceResets(srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
938 dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
941 traceResets(dstElt.type, dst, dstID +
getFieldID(dstBundle, dstIdx),
942 srcElt.type, src, srcID +
getFieldID(srcBundle, *srcIdx),
949 if (
auto dstVector = type_dyn_cast<FVectorType>(dstType)) {
950 auto srcVector = type_cast<FVectorType>(srcType);
951 auto srcElType = srcVector.getElementType();
952 auto dstElType = dstVector.getElementType();
965 traceResets(dstElType, dst, dstID +
getFieldID(dstVector), srcElType, src,
971 if (
auto dstRef = type_dyn_cast<RefType>(dstType)) {
972 auto srcRef = type_cast<RefType>(srcType);
973 return traceResets(dstRef.getType(), dst, dstID, srcRef.getType(), src,
978 auto dstBase = type_dyn_cast<FIRRTLBaseType>(dstType);
979 auto srcBase = type_dyn_cast<FIRRTLBaseType>(srcType);
980 if (!dstBase || !srcBase)
982 if (!type_isa<ResetType>(dstBase) && !type_isa<ResetType>(srcBase))
987 LLVM_DEBUG(llvm::dbgs() <<
"Visiting driver '" << dstField <<
"' = '"
988 << srcField <<
"' (" << dstType <<
" = " << srcType
994 ResetSignal dstLeader =
995 *resetClasses.findLeader(resetClasses.insert({dstField, dstBase}));
996 ResetSignal srcLeader =
997 *resetClasses.findLeader(resetClasses.insert({srcField, srcBase}));
1000 ResetSignal unionLeader = *resetClasses.unionSets(dstLeader, srcLeader);
1001 assert(unionLeader == dstLeader || unionLeader == srcLeader);
1006 if (dstLeader != srcLeader) {
1007 auto &unionDrives = resetDrives[unionLeader];
1008 auto mergedDrivesIt =
1009 resetDrives.find(unionLeader == dstLeader ? srcLeader : dstLeader);
1010 if (mergedDrivesIt != resetDrives.end()) {
1011 unionDrives.append(mergedDrivesIt->second);
1012 resetDrives.erase(mergedDrivesIt);
1018 resetDrives[unionLeader].push_back(
1019 {{dstField, dstBase}, {srcField, srcBase}, loc});
1026LogicalResult InferResetsPass::inferAndUpdateResets() {
1028 llvm::dbgs() <<
"\n";
1031 for (
auto it = resetClasses.begin(),
end = resetClasses.end(); it !=
end;
1033 if (!it->isLeader())
1035 ResetNetwork net = llvm::make_range(resetClasses.member_begin(it),
1036 resetClasses.member_end());
1039 auto kind = inferReset(net);
1044 if (failed(updateReset(net, *kind)))
1050FailureOr<ResetKind> InferResetsPass::inferReset(ResetNetwork net) {
1051 LLVM_DEBUG(llvm::dbgs() <<
"Inferring reset network with "
1052 << std::distance(net.begin(), net.end())
1056 unsigned asyncDrives = 0;
1057 unsigned syncDrives = 0;
1058 unsigned invalidDrives = 0;
1059 for (ResetSignal signal : net) {
1061 if (type_isa<AsyncResetType>(signal.type))
1063 else if (type_isa<UIntType>(signal.type))
1066 isa_and_nonnull<InvalidValueOp>(
1067 signal.field.getValue().getDefiningOp()))
1070 LLVM_DEBUG(llvm::dbgs() <<
"- Found " << asyncDrives <<
" async, "
1071 << syncDrives <<
" sync, " << invalidDrives
1072 <<
" invalid drives\n");
1075 if (asyncDrives == 0 && syncDrives == 0 && invalidDrives == 0) {
1076 ResetSignal root = guessRoot(net);
1077 auto diag = mlir::emitError(root.field.getValue().getLoc())
1078 <<
"reset network never driven with concrete type";
1079 for (ResetSignal signal : net)
1080 diag.attachNote(signal.field.
getLoc()) <<
"here: ";
1085 if (asyncDrives > 0 && syncDrives > 0) {
1086 ResetSignal root = guessRoot(net);
1087 bool majorityAsync = asyncDrives >= syncDrives;
1088 auto diag = mlir::emitError(root.field.getValue().getLoc())
1090 SmallString<32> fieldName;
1092 diag <<
" \"" << fieldName <<
"\"";
1093 diag <<
" simultaneously connected to async and sync resets";
1094 diag.attachNote(root.field.getValue().getLoc())
1095 <<
"majority of connections to this reset are "
1096 << (majorityAsync ?
"async" :
"sync");
1097 for (
auto &drive : getResetDrives(net)) {
1098 if ((type_isa<AsyncResetType>(drive.dst.type) && !majorityAsync) ||
1099 (type_isa<AsyncResetType>(drive.src.type) && !majorityAsync) ||
1100 (type_isa<UIntType>(drive.dst.type) && majorityAsync) ||
1101 (type_isa<UIntType>(drive.src.type) && majorityAsync))
1102 diag.attachNote(drive.loc)
1103 << (type_isa<AsyncResetType>(drive.src.type) ?
"async" :
"sync")
1112 auto kind = (asyncDrives ? ResetKind::Async : ResetKind::Sync);
1113 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred as " << kind <<
"\n");
1121LogicalResult InferResetsPass::updateReset(ResetNetwork net, ResetKind kind) {
1122 LLVM_DEBUG(llvm::dbgs() <<
"Updating reset network with "
1123 << std::distance(net.begin(), net.end())
1124 <<
" nodes to " << kind <<
"\n");
1128 if (kind == ResetKind::Async)
1129 resetType = AsyncResetType::get(&getContext());
1131 resetType = UIntType::get(&getContext(), 1);
1136 SmallSetVector<Operation *, 16> worklist;
1137 SmallDenseSet<Operation *> moduleWorklist;
1138 SmallDenseSet<std::pair<Operation *, Operation *>> extmoduleWorklist;
1139 for (
auto signal : net) {
1140 Value value = signal.field.getValue();
1141 if (!isa<BlockArgument>(value) &&
1142 !isa_and_nonnull<WireOp, RegOp, RegResetOp, InstanceOp, InvalidValueOp,
1143 ConstCastOp, RefCastOp, UninferredResetCastOp,
1144 RWProbeOp>(value.getDefiningOp()))
1146 if (updateReset(signal.field, resetType)) {
1147 for (
auto user : value.getUsers())
1148 worklist.insert(user);
1149 if (
auto blockArg = dyn_cast<BlockArgument>(value))
1150 moduleWorklist.insert(blockArg.getOwner()->getParentOp());
1151 else if (
auto instOp = value.getDefiningOp<InstanceOp>()) {
1152 if (
auto extmodule =
1153 instOp.getReferencedModule<FExtModuleOp>(*instanceGraph))
1154 extmoduleWorklist.insert({extmodule, instOp});
1155 }
else if (
auto uncast = value.getDefiningOp<UninferredResetCastOp>()) {
1156 uncast.replaceAllUsesWith(uncast.getInput());
1166 while (!worklist.empty()) {
1167 auto *wop = worklist.pop_back_val();
1168 SmallVector<Type, 2> types;
1169 if (
auto op = dyn_cast<InferTypeOpInterface>(wop)) {
1171 SmallVector<Type, 2> types;
1172 if (failed(op.inferReturnTypes(op->getContext(), op->getLoc(),
1173 op->getOperands(), op->getAttrDictionary(),
1174 op->getPropertiesStorage(),
1175 op->getRegions(), types)))
1180 for (
auto it :
llvm::zip(op->getResults(), types)) {
1181 auto newType = std::get<1>(it);
1182 if (std::get<0>(it).getType() == newType)
1184 std::get<0>(it).setType(newType);
1185 for (
auto *user : std::
get<0>(it).getUsers())
1186 worklist.insert(user);
1188 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << *op <<
"\n");
1189 }
else if (
auto uop = dyn_cast<UninferredResetCastOp>(wop)) {
1190 for (
auto *user : uop.getResult().getUsers())
1191 worklist.insert(user);
1192 uop.replaceAllUsesWith(uop.getInput());
1193 LLVM_DEBUG(llvm::dbgs() <<
"- Inferred " << uop <<
"\n");
1199 for (
auto *op : moduleWorklist) {
1200 auto module = dyn_cast<FModuleOp>(op);
1204 SmallVector<Attribute> argTypes;
1205 argTypes.reserve(module.getNumPorts());
1206 for (
auto arg : module.getArguments())
1207 argTypes.push_back(TypeAttr::
get(arg.getType()));
1209 module.setPortTypesAttr(ArrayAttr::get(op->getContext(), argTypes));
1210 LLVM_DEBUG(llvm::dbgs()
1211 <<
"- Updated type of module '" << module.getName() <<
"'\n");
1215 for (
auto pair : extmoduleWorklist) {
1216 auto module = cast<FExtModuleOp>(pair.first);
1217 auto instOp = cast<InstanceOp>(pair.second);
1219 SmallVector<Attribute> types;
1220 for (
auto type : instOp.getResultTypes())
1221 types.push_back(TypeAttr::
get(type));
1223 module.setPortTypesAttr(ArrayAttr::get(module->getContext(), types));
1224 LLVM_DEBUG(llvm::dbgs()
1225 <<
"- Updated type of extmodule '" << module.getName() <<
"'\n");
1235 if (oldType.isGround()) {
1241 if (
auto bundleType = type_dyn_cast<BundleType>(oldType)) {
1243 SmallVector<BundleType::BundleElement> fields(bundleType.begin(),
1246 fields[index].type, fieldID -
getFieldID(bundleType, index), fieldType);
1247 return BundleType::get(oldType.getContext(), fields, bundleType.
isConst());
1251 if (
auto vectorType = type_dyn_cast<FVectorType>(oldType)) {
1252 auto newType =
updateType(vectorType.getElementType(),
1253 fieldID -
getFieldID(vectorType), fieldType);
1254 return FVectorType::get(newType, vectorType.getNumElements(),
1255 vectorType.isConst());
1258 llvm_unreachable(
"unknown aggregate type");
1265 auto oldType = type_cast<FIRRTLType>(field.
getValue().getType());
1271 if (oldType == newType)
1273 LLVM_DEBUG(llvm::dbgs() <<
"- Updating '" << field <<
"' from " << oldType
1274 <<
" to " << newType <<
"\n");
1283LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
1285 llvm::dbgs() <<
"\n";
1286 debugHeader(
"Gather reset annotations") <<
"\n\n";
1288 SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
1289 for (
auto module : circuit.getOps<FModuleOp>())
1290 results.push_back({module, {}});
1292 if (failed(mlir::failableParallelForEach(
1293 circuit.getContext(), results, [&](
auto &moduleAndResult) {
1294 auto result = collectAnnos(moduleAndResult.first);
1297 moduleAndResult.second = *result;
1302 for (
auto [module, reset] : results)
1303 if (reset.has_value())
1304 annotatedResets.insert({module, *reset});
1308FailureOr<std::optional<Value>>
1309InferResetsPass::collectAnnos(FModuleOp module) {
1310 bool anyFailed =
false;
1311 SmallSetVector<std::pair<Annotation, Location>, 4> conflictingAnnos;
1315 bool ignore =
false;
1319 conflictingAnnos.insert({anno, module.getLoc()});
1324 module.emitError("''FullResetAnnotation' cannot target module; must
"
1325 "target port or wire/node instead
");
1333 // Consume any reset annotations on module ports.
1335 // Helper for checking annotations and determining the reset
1336 auto checkAnnotations = [&](Annotation anno, Value arg) {
1337 if (anno.isClass(fullResetAnnoClass)) {
1338 ResetKind expectedResetKind;
1339 if (auto rt = anno.getMember<StringAttr>("resetType
")) {
1341 expectedResetKind = ResetKind::Sync;
1342 } else if (rt == "async
") {
1343 expectedResetKind = ResetKind::Async;
1345 mlir::emitError(arg.getLoc(),
1346 "'FullResetAnnotation' requires resetType ==
'sync' "
1347 "|
'async', but got resetType ==
")
1353 mlir::emitError(arg.getLoc(),
1354 "'FullResetAnnotation' requires resetType ==
"
1355 "'sync' |
'async', but got no resetType
");
1359 // Check that the type is well-formed
1360 bool isAsync = expectedResetKind == ResetKind::Async;
1361 bool validUint = false;
1362 if (auto uintT = dyn_cast<UIntType>(arg.getType()))
1363 validUint = uintT.getWidth() == 1;
1364 if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
1365 (!isAsync && !validUint)) {
1366 auto kind = resetKindToStringRef(expectedResetKind);
1367 mlir::emitError(arg.getLoc(),
1368 "'FullResetAnnotation' with resetType ==
'")
1369 << kind << "' must target
" << kind << " reset, but targets
"
1376 conflictingAnnos.insert({anno, reset.getLoc()});
1380 if (anno.isClass(excludeFromFullResetAnnoClass)) {
1382 mlir::emitError(arg.getLoc(),
1383 "'ExcludeFromFullResetAnnotation' cannot
"
1384 "target port/wire/node; must target
module instead");
1392 Value arg =
module.getArgument(argNum);
1393 return checkAnnotations(anno, arg);
1399 module.getBody().walk([&](Operation *op) {
1401 if (!isa<WireOp, NodeOp>(op)) {
1402 if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
1403 excludeFromFullResetAnnoClass)) {
1406 "reset annotations must target module, port, or wire/node");
1414 auto arg = op->getResult(0);
1415 return checkAnnotations(anno, arg);
1424 if (!ignore && !reset) {
1425 LLVM_DEBUG(llvm::dbgs()
1426 <<
"No reset annotation for " << module.getName() <<
"\n");
1427 return std::optional<Value>();
1431 if (conflictingAnnos.size() > 1) {
1432 auto diag =
module.emitError("multiple reset annotations on module '")
1433 << module.getName() << "'";
1434 for (
auto &annoAndLoc : conflictingAnnos)
1435 diag.attachNote(annoAndLoc.second)
1436 <<
"conflicting " << annoAndLoc.first.getClassAttr() <<
":";
1442 llvm::dbgs() <<
"Annotated reset for " <<
module.getName() << ": ";
1444 llvm::dbgs() <<
"no domain\n";
1445 else if (
auto arg = dyn_cast<BlockArgument>(reset))
1446 llvm::dbgs() <<
"port " <<
module.getPortName(arg.getArgNumber()) << "\n";
1448 llvm::dbgs() <<
"wire "
1449 << reset.getDefiningOp()->getAttrOfType<StringAttr>(
"name")
1455 return std::optional<Value>(reset);
1467LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
1469 llvm::dbgs() <<
"\n";
1470 debugHeader(
"Build full reset domains") <<
"\n\n";
1474 auto &instGraph = getAnalysis<InstanceGraph>();
1475 auto module = dyn_cast<FModuleOp>(*instGraph.getTopLevelNode()->getModule());
1477 LLVM_DEBUG(llvm::dbgs()
1478 <<
"Skipping circuit because main module is no `firrtl.module`");
1481 buildDomains(module,
InstancePath{}, Value{}, instGraph);
1484 bool anyFailed =
false;
1485 for (
auto &it : domains) {
1486 auto module = cast<FModuleOp>(it.first);
1487 auto &domainConflicts = it.second;
1488 if (domainConflicts.size() <= 1)
1492 SmallDenseSet<Value> printedDomainResets;
1493 auto diag =
module.emitError("module '")
1495 << "' instantiated in different reset domains";
1496 for (
auto &it : domainConflicts) {
1497 ResetDomain &domain = it.first;
1498 const auto &path = it.second;
1499 auto inst = path.leaf();
1500 auto loc = path.empty() ?
module.getLoc() : inst.getLoc();
1501 auto ¬e = diag.attachNote(loc);
1505 note <<
"root instance";
1507 note <<
"instance '";
1510 [&](InstanceOpInterface inst) { note << inst.getInstanceName(); },
1511 [&]() { note <<
"/"; });
1517 if (domain.rootReset) {
1519 note <<
" reset domain rooted at '" << nameAndModule.first.getValue()
1520 <<
"' of module '" << nameAndModule.second.getName() <<
"'";
1523 if (printedDomainResets.insert(domain.rootReset).second) {
1524 diag.attachNote(domain.rootReset.getLoc())
1525 <<
"reset domain '" << nameAndModule.first.getValue()
1526 <<
"' of module '" << nameAndModule.second.getName()
1527 <<
"' declared here:";
1530 note <<
" no reset domain";
1533 return failure(anyFailed);
1536void InferResetsPass::buildDomains(FModuleOp module,
1541 llvm::dbgs().indent(indent * 2) <<
"Visiting ";
1542 if (instPath.
empty())
1543 llvm::dbgs() <<
"$root";
1545 llvm::dbgs() << instPath.
leaf().getInstanceName();
1546 llvm::dbgs() <<
" (" <<
module.getName() << ")\n";
1551 auto it = annotatedResets.find(module);
1552 if (it != annotatedResets.end()) {
1555 if (
auto localReset = it->second)
1556 domain = ResetDomain(localReset);
1557 domain.isTop =
true;
1558 }
else if (parentReset) {
1560 domain = ResetDomain(parentReset);
1566 auto &entries = domains[module];
1567 if (llvm::all_of(entries,
1568 [&](
const auto &entry) {
return entry.first != domain; }))
1569 entries.push_back({domain, instPath});
1572 for (
auto *record : *instGraph[module]) {
1573 auto submodule = dyn_cast<FModuleOp>(*record->getTarget()->getModule());
1577 instancePathCache->appendInstance(instPath, record->getInstance());
1578 buildDomains(submodule, childPath, domain.rootReset, instGraph, indent + 1);
1583LogicalResult InferResetsPass::determineImpl() {
1584 auto anyFailed =
false;
1586 llvm::dbgs() <<
"\n";
1587 debugHeader(
"Determine implementation") <<
"\n\n";
1589 for (
auto &it : domains) {
1590 auto module = cast<FModuleOp>(it.first);
1591 auto &domain = it.second.back().first;
1592 if (failed(determineImpl(module, domain)))
1595 return failure(anyFailed);
1613LogicalResult InferResetsPass::determineImpl(FModuleOp module,
1614 ResetDomain &domain) {
1618 LLVM_DEBUG(llvm::dbgs() <<
"Planning reset for " << module.getName() <<
"\n");
1623 LLVM_DEBUG(llvm::dbgs()
1624 <<
"- Rooting at local value " << domain.resetName <<
"\n");
1625 domain.localReset = domain.rootReset;
1626 if (
auto blockArg = dyn_cast<BlockArgument>(domain.rootReset))
1627 domain.existingPort = blockArg.getArgNumber();
1633 auto neededName = domain.resetName;
1634 auto neededType = domain.resetType;
1635 LLVM_DEBUG(llvm::dbgs() <<
"- Looking for existing port " << neededName
1637 auto portNames =
module.getPortNames();
1638 auto *portIt = llvm::find(portNames, neededName);
1641 if (portIt == portNames.end()) {
1642 LLVM_DEBUG(llvm::dbgs() <<
"- Creating new port " << neededName <<
"\n");
1643 domain.resetName = neededName;
1647 LLVM_DEBUG(llvm::dbgs() <<
"- Reusing existing port " << neededName <<
"\n");
1650 auto portNo = std::distance(portNames.begin(), portIt);
1651 auto portType =
module.getPortType(portNo);
1652 if (portType != neededType) {
1653 auto diag = emitError(module.getPortLocation(portNo),
"module '")
1654 <<
module.getName() << "' is in reset domain requiring port '"
1655 << domain.resetName.getValue() << "' to have type "
1656 << domain.resetType << ", but has type " << portType;
1657 diag.attachNote(domain.rootReset.getLoc()) <<
"reset domain rooted here";
1662 domain.existingPort = portNo;
1663 domain.localReset =
module.getArgument(portNo);
1672LogicalResult InferResetsPass::implementFullReset() {
1674 llvm::dbgs() <<
"\n";
1677 for (
auto &it : domains)
1678 if (failed(implementFullReset(cast<FModuleOp>(it.first),
1679 it.second.back().first)))
1689LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
1690 ResetDomain &domain) {
1691 LLVM_DEBUG(llvm::dbgs() <<
"Implementing full reset for " << module.getName()
1696 LLVM_DEBUG(llvm::dbgs()
1697 <<
"- Skipping because module explicitly has no domain\n");
1702 auto *context =
module.getContext();
1704 annotations.addAnnotations(DictionaryAttr::get(
1705 context, NamedAttribute(StringAttr::get(context,
"class"),
1707 annotations.applyToOperation(module);
1710 auto actualReset = domain.localReset;
1711 if (!domain.localReset) {
1712 PortInfo portInfo{domain.resetName,
1716 domain.rootReset.getLoc()};
1717 module.insertPorts({{0, portInfo}});
1718 actualReset =
module.getArgument(0);
1719 LLVM_DEBUG(llvm::dbgs() <<
"- Inserted port " << domain.resetName <<
"\n");
1723 llvm::dbgs() <<
"- Using ";
1724 if (
auto blockArg = dyn_cast<BlockArgument>(actualReset))
1725 llvm::dbgs() <<
"port #" << blockArg.getArgNumber() <<
" ";
1727 llvm::dbgs() <<
"wire/node ";
1733 SmallVector<Operation *> opsToUpdate;
1734 module.walk([&](Operation *op) {
1735 if (isa<InstanceOp, RegOp, RegResetOp>(op))
1736 opsToUpdate.push_back(op);
1743 if (!isa<BlockArgument>(actualReset)) {
1744 mlir::DominanceInfo dom(module);
1749 auto *resetOp = actualReset.getDefiningOp();
1750 if (!opsToUpdate.empty() && !dom.dominates(resetOp, opsToUpdate[0])) {
1751 LLVM_DEBUG(llvm::dbgs()
1752 <<
"- Reset doesn't dominate all uses, needs to be moved\n");
1756 auto nodeOp = dyn_cast<NodeOp>(resetOp);
1757 if (nodeOp && !dom.dominates(nodeOp.getInput(), opsToUpdate[0])) {
1758 LLVM_DEBUG(llvm::dbgs()
1759 <<
"- Promoting node to wire for move: " << nodeOp <<
"\n");
1760 auto builder = ImplicitLocOpBuilder::atBlockBegin(nodeOp.getLoc(),
1761 nodeOp->getBlock());
1762 auto wireOp = builder.create<WireOp>(
1763 nodeOp.getResult().getType(), nodeOp.getNameAttr(),
1764 nodeOp.getNameKindAttr(), nodeOp.getAnnotationsAttr(),
1765 nodeOp.getInnerSymAttr(), nodeOp.getForceableAttr());
1767 nodeOp->replaceAllUsesWith(wireOp);
1768 nodeOp->removeAttr(nodeOp.getInnerSymAttrName());
1772 nodeOp.setNameKind(NameKindEnum::DroppableName);
1773 nodeOp.setAnnotationsAttr(ArrayAttr::get(builder.getContext(), {}));
1774 builder.setInsertionPointAfter(nodeOp);
1775 emitConnect(builder, wireOp.getResult(), nodeOp.getResult());
1777 actualReset = wireOp.getResult();
1778 domain.localReset = wireOp.getResult();
1783 Block *targetBlock = dom.findNearestCommonDominator(
1784 resetOp->getBlock(), opsToUpdate[0]->getBlock());
1786 if (targetBlock != resetOp->getBlock())
1787 llvm::dbgs() <<
"- Needs to be moved to different block\n";
1796 auto getParentInBlock = [](Operation *op,
Block *block) {
1797 while (op && op->getBlock() != block)
1798 op = op->getParentOp();
1801 auto *resetOpInTarget = getParentInBlock(resetOp, targetBlock);
1802 auto *firstOpInTarget = getParentInBlock(opsToUpdate[0], targetBlock);
1808 if (resetOpInTarget->isBeforeInBlock(firstOpInTarget))
1809 resetOp->moveBefore(resetOpInTarget);
1811 resetOp->moveBefore(firstOpInTarget);
1816 for (
auto *op : opsToUpdate)
1817 implementFullReset(op, module, actualReset);
1824void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
1825 Value actualReset) {
1826 ImplicitLocOpBuilder builder(op->getLoc(), op);
1829 if (
auto instOp = dyn_cast<InstanceOp>(op)) {
1833 auto refModule = instOp.getReferencedModule<FModuleOp>(*instanceGraph);
1836 auto domainIt = domains.find(refModule);
1837 if (domainIt == domains.end())
1839 auto &domain = domainIt->second.back().first;
1842 LLVM_DEBUG(llvm::dbgs()
1843 <<
"- Update instance '" << instOp.getName() <<
"'\n");
1847 if (!domain.localReset) {
1848 LLVM_DEBUG(llvm::dbgs() <<
" - Adding new result as reset\n");
1850 auto newInstOp = instOp.cloneAndInsertPorts(
1852 {domain.resetName, type_cast<FIRRTLBaseType>(actualReset.getType()),
1854 instReset = newInstOp.getResult(0);
1857 instOp.replaceAllUsesWith(newInstOp.getResults().drop_front());
1858 instanceGraph->replaceInstance(instOp, newInstOp);
1861 }
else if (domain.existingPort.has_value()) {
1862 auto idx = *domain.existingPort;
1863 instReset = instOp.getResult(idx);
1864 LLVM_DEBUG(llvm::dbgs() <<
" - Using result #" << idx <<
" as reset\n");
1874 assert(instReset && actualReset);
1875 builder.setInsertionPointAfter(instOp);
1881 if (
auto regOp = dyn_cast<RegOp>(op)) {
1885 LLVM_DEBUG(llvm::dbgs() <<
"- Adding full reset to " << regOp <<
"\n");
1887 auto newRegOp = builder.create<RegResetOp>(
1888 regOp.getResult().getType(), regOp.getClockVal(), actualReset, zero,
1889 regOp.getNameAttr(), regOp.getNameKindAttr(), regOp.getAnnotations(),
1890 regOp.getInnerSymAttr(), regOp.getForceableAttr());
1891 regOp.getResult().replaceAllUsesWith(newRegOp.getResult());
1892 if (regOp.getForceable())
1893 regOp.getRef().replaceAllUsesWith(newRegOp.getRef());
1899 if (
auto regOp = dyn_cast<RegResetOp>(op)) {
1902 if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
1903 type_isa<UIntType>(actualReset.getType())) {
1904 LLVM_DEBUG(llvm::dbgs() <<
"- Skipping (has reset) " << regOp <<
"\n");
1907 if (failed(regOp.verifyInvariants()))
1908 signalPassFailure();
1911 LLVM_DEBUG(llvm::dbgs() <<
"- Updating reset of " << regOp <<
"\n");
1913 auto reset = regOp.getResetSignal();
1914 auto value = regOp.getResetValue();
1920 builder.setInsertionPointAfterValue(regOp.getResult());
1921 auto mux = builder.create<MuxPrimOp>(reset, value, regOp.getResult());
1925 builder.setInsertionPoint(regOp);
1927 regOp.getResetSignalMutable().assign(actualReset);
1928 regOp.getResetValueMutable().assign(zero);
1932LogicalResult InferResetsPass::verifyNoAbstractReset() {
1933 bool hasAbstractResetPorts =
false;
1934 for (FModuleLike module :
1935 getOperation().
getBodyBlock()->getOps<FModuleLike>()) {
1936 for (
PortInfo port : module.getPorts()) {
1937 if (getBaseOfType<ResetType>(port.type)) {
1938 auto diag = emitError(port.loc)
1939 <<
"a port \"" << port.getName()
1940 <<
"\" with abstract reset type was unable to be "
1941 "inferred by InferResets (is this a top-level port?)";
1942 diag.attachNote(module->getLoc())
1943 <<
"the module with this uninferred reset port was defined here";
1944 hasAbstractResetPorts =
true;
1949 if (hasAbstractResetPorts)
assert(baseType &&"element must be base type")
static Value createZeroValue(ImplicitLocOpBuilder &builder, FIRRTLBaseType type, SmallDenseMap< FIRRTLBaseType, Value > &cache)
Construct a zero value of the given type using the given builder.
static unsigned getFieldID(BundleType type, unsigned index)
static unsigned getIndexForFieldID(BundleType type, unsigned fieldID)
static FIRRTLBaseType updateType(FIRRTLBaseType oldType, unsigned fieldID, FIRRTLBaseType fieldType)
Update the type of a single field within a type.
static bool isUselessVec(FIRRTLBaseType oldType, unsigned fieldID)
static StringAttr getResetName(Value reset)
Return the name of a reset.
static bool insertResetMux(ImplicitLocOpBuilder &builder, Value target, Value reset, Value resetValue)
Helper function that inserts reset multiplexer into all ConnectOps with the given target.
static bool typeContainsReset(Type type)
Check whether a type contains a ResetType.
static bool getDeclName(Value value, SmallString< 32 > &string)
static unsigned getMaxFieldID(FIRRTLBaseType type)
static std::pair< StringAttr, FModuleOp > getResetNameAndModule(Value reset)
Return the name and parent module of a reset.
static InstancePath empty
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
This class represents a reference to a specific field or element of an aggregate value.
unsigned getFieldID() const
Get the field ID of this FieldRef, which is a unique identifier mapped to a specific field in a bundl...
Value getValue() const
Get the Value which created this location.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
This class provides a read-only projection of an annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
bool isConst()
Returns true if this is a 'const' type that can only hold compile-time constant values.
FIRRTLBaseType getConstType(bool isConst)
Return a 'const' or non-'const' version of this type.
This class implements the same functionality as TypeSwitch except that it uses firrtl::type_dyn_cast ...
FIRRTLTypeSwitch< T, ResultT > & Case(CallableT &&caseFn)
Add a case on the given type.
This graph tracks modules and where they are instantiated.
An instance path composed of a series of instances.
InstanceOpInterface leaf() const
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr const char * excludeFromFullResetAnnoClass
Annotation that marks a module as not belonging to any reset domain.
FieldRef getFieldRefForTarget(const hw::InnerSymTarget &ist)
Get FieldRef pointing to the specified inner symbol target, which must be valid.
constexpr const char * excludeMemToRegAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
FIRRTLType mapBaseType(FIRRTLType type, function_ref< FIRRTLBaseType(FIRRTLBaseType)> fn)
Return a FIRRTLType with its base type component mutated by the given function.
constexpr const char * fullResetAnnoClass
Annotation that marks a reset (port or wire) and domain.
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const InstanceInfo::LatticeValue &value)
std::pair< std::string, bool > getFieldName(const FieldRef &fieldRef, bool nameSafe=false)
Get a string identifier representing the FieldRef.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
std::unique_ptr< mlir::Pass > createInferResetsPass()
static bool operator==(const ModulePort &a, const ModulePort &b)
static llvm::hash_code hash_value(const ModulePort &port)
bool operator<(const DictEntry &entry, const DictEntry &other)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugHeader(llvm::StringRef str, int width=80)
Write a "header"-like string to the debug stream with a certain width.
bool operator!=(uint64_t a, const FVInt &b)
This holds the name and type that describes the module's ports.
This class represents the namespace in which InnerRef's can be resolved.
A data structure that caches and provides absolute paths to module instances in the IR.
static ResetSignal getEmptyKey()
static ResetSignal getTombstoneKey()
static bool isEqual(const ResetSignal &lhs, const ResetSignal &rhs)
static unsigned getHashValue(const ResetSignal &x)