35 #include "mlir/IR/BuiltinOps.h"
36 #include "mlir/IR/BuiltinTypes.h"
37 #include "mlir/IR/ImplicitLocOpBuilder.h"
38 #include "mlir/IR/Threading.h"
39 #include "mlir/Pass/Pass.h"
40 #include "llvm/Support/Debug.h"
41 #include "llvm/Support/Mutex.h"
43 #define DEBUG_TYPE "lower-to-hw"
46 #define GEN_PASS_DEF_LOWERFIRRTLTOHW
47 #include "circt/Conversion/Passes.h.inc"
50 using namespace circt;
51 using namespace firrtl;
52 using circt::comb::ICmpPredicate;
61 auto ftype = dyn_cast<FIRRTLBaseType>(type);
62 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
69 for (
auto operand : op.getAttached()) {
71 operand.getDefiningOp<InstanceOp>())
75 if (!operand.hasOneUse() || singleSource)
77 singleSource = operand;
86 auto checkTypes = [](Operation *op) -> WalkResult {
88 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
89 return op->emitError(
"Found unhandled FIRRTL operation '")
90 << op->getName() <<
"'";
93 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
94 if (llvm::any_of(types, [](Type type) {
95 return isa<FIRRTLDialect>(type.getDialect());
97 return op->emitOpError(
"found unhandled FIRRTL type");
102 if (failed(checkTypeRange(op->getOperandTypes())) ||
103 failed(checkTypeRange(op->getResultTypes())))
104 return WalkResult::interrupt();
107 for (
auto ®ion : op->getRegions())
108 for (
auto &block : region)
109 if (failed(checkTypeRange(block.getArgumentTypes())))
110 return WalkResult::interrupt();
113 return WalkResult::advance();
116 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
123 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
124 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
130 ImplicitLocOpBuilder &builder) {
132 if (BundleType bundle = dyn_cast<BundleType>(type))
133 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
135 if (type != val.getType())
136 val = builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
144 ImplicitLocOpBuilder &builder) {
146 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
150 .create<mlir::UnrealizedConversionCastOp>(
151 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
153 val = builder.createOrFold<HWStructCastOp>(type, val);
158 builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
165 StringRef annoClass, StringRef attrBase) {
167 auto *ctx = top.getContext();
170 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
171 SmallVector<NamedAttribute> old;
172 for (
auto i : top->getAttrs())
176 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
179 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
180 SmallVector<NamedAttribute> old;
181 for (
auto i : top->getAttrs())
184 hw::OutputFileAttr::getFromFilename(
185 ctx, file.getValue(),
true));
191 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
197 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
198 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
199 dst->setAttr(
"sv.namehint", attr);
207 struct FIRRTLModuleLowering;
210 struct CircuitLoweringState {
212 std::atomic<bool> usedPrintfCond{
false};
213 std::atomic<bool> usedAssertVerboseCond{
false};
214 std::atomic<bool> usedStopCond{
false};
216 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
219 : circuitOp(circuitOp), instanceGraph(instanceGraph),
220 enableAnnotationWarning(enableAnnotationWarning),
221 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
222 auto *context = circuitOp.getContext();
227 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
228 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
229 context, dirName.getValue(),
false,
true);
232 for (
auto &op : *circuitOp.getBodyBlock()) {
233 if (
auto module = dyn_cast<FModuleLike>(op))
244 testHarness =
nullptr;
245 }
else if (dut == testHarness) {
246 testHarness =
nullptr;
251 auto inDUT = [&](igraph::ModuleOpInterface child) {
253 auto inst = instRec->getInstance();
254 if (
auto *finst = dyn_cast<InstanceOp>(&inst))
255 return finst->getLowerToBind();
258 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
259 return getInstanceGraph().isAncestor(child, parent, isBind);
262 circuitOp->walk([&](FModuleLike moduleOp) {
264 dutModules.insert(moduleOp);
268 Operation *getNewModule(Operation *oldModule) {
269 auto it = oldToNewModuleMap.find(oldModule);
270 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
273 Operation *getOldModule(Operation *newModule) {
274 auto it = newToOldModuleMap.find(newModule);
275 return it != newToOldModuleMap.end() ? it->second :
nullptr;
278 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
279 oldToNewModuleMap[oldFMod] = newHWMod;
280 newToOldModuleMap[newHWMod] = oldFMod;
285 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
291 void addBind(sv::BindOp op) {
292 std::lock_guard<std::mutex> lock(bindsMutex);
298 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
301 auto hwAlias = typeAliases.getTypedecl(firAliasType);
304 assert(!typeAliases.isFrozen() &&
305 "type aliases cannot be generated after its frozen");
306 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
309 FModuleLike getDut() {
return dut; }
310 FModuleLike getTestHarness() {
return testHarness; }
316 bool isInDUT(igraph::ModuleOpInterface child) {
317 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
318 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
319 return dutModules.contains(child);
322 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
327 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
334 Type
lowerType(Type type, Location loc) {
336 [&](Type rawType, BaseTypeAliasType firrtlType,
337 Location typeLoc) -> hw::TypeAliasType {
338 return getTypeAlias(rawType, firrtlType, typeLoc);
343 friend struct FIRRTLModuleLowering;
344 friend struct FIRRTLLowering;
345 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
346 void operator=(
const CircuitLoweringState &) =
delete;
349 DenseMap<Operation *, Operation *> oldToNewModuleMap;
352 DenseMap<Operation *, Operation *> newToOldModuleMap;
363 DenseSet<igraph::ModuleOpInterface> dutModules;
367 StringSet<> pendingAnnotations;
368 const bool enableAnnotationWarning;
369 std::mutex annotationPrintingMtx;
375 SmallVector<sv::BindOp> binds;
378 std::mutex bindsMutex;
386 FModuleLike testHarness;
389 hw::OutputFileAttr testBenchDirectory;
393 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
396 SetVector<StringAttr> macroDeclNames;
397 std::mutex macroDeclMutex;
399 void addMacroDecl(StringAttr name) {
400 std::unique_lock<std::mutex> lock(macroDeclMutex);
401 macroDeclNames.insert(name);
406 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
407 llvm::sys::SmartMutex<true> fragmentsMutex;
410 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
411 fragments[module].insert(
427 struct RecordTypeAlias {
429 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
431 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
432 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
433 if (iter != firrtlTypeToAliasTypeMap.end())
438 bool isFrozen() {
return frozen; }
440 void freeze() { frozen =
true; }
442 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
444 assert(!frozen &&
"Record already frozen, cannot be updated");
447 auto b = ImplicitLocOpBuilder::atBlockBegin(
449 &circuitOp->getParentRegion()->getBlocks().back());
451 b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
452 typeScope.getBodyRegion().push_back(
new Block());
454 auto typeName = firAlias.getName();
460 typeDeclNamespace.newName(typeName.getValue()));
462 auto typeScopeBuilder =
463 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
464 auto typeDecl = typeScopeBuilder.create<
hw::TypedeclOp>(typeLoc, typeName,
468 {FlatSymbolRefAttr::get(typeDecl)}),
470 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
471 assert(insert.second &&
"Entry already exists, insert failed");
472 return insert.first->second;
481 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
489 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
492 void CircuitLoweringState::processRemainingAnnotations(
494 if (!enableAnnotationWarning || annoSet.
empty())
496 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
498 for (
auto a : annoSet) {
499 auto inserted = pendingAnnotations.insert(a.getClass());
500 if (!inserted.second)
539 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
540 "' still remaining after LowerToHW");
546 struct FIRRTLModuleLowering
547 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
549 void runOnOperation()
override;
550 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
552 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
555 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
556 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
557 SmallVectorImpl<hw::PortInfo> &ports,
558 Operation *moduleOp, StringRef moduleName,
560 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
562 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
565 Block *topLevelModule,
568 Block *topLevelModule,
572 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
576 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
584 bool enableAnnotationWarning,
586 auto pass = std::make_unique<FIRRTLModuleLowering>();
587 if (enableAnnotationWarning)
588 pass->setEnableAnnotationWarning();
589 pass->verificationFlavor = verificationFlavor;
595 void FIRRTLModuleLowering::runOnOperation() {
599 auto *topLevelModule = getOperation().getBody();
603 for (
auto &op : *topLevelModule) {
604 if ((circuit = dyn_cast<CircuitOp>(&op)))
611 auto *circuitBody = circuit.getBodyBlock();
615 CircuitLoweringState state(circuit, enableAnnotationWarning,
616 verificationFlavor, getAnalysis<InstanceGraph>(),
617 &getAnalysis<NLATable>());
619 SmallVector<hw::HWModuleOp, 32> modulesToProcess;
620 SmallVector<verif::FormalOp> formalOpsToProcess;
624 "firrtl.extract.assert");
626 "firrtl.extract.assume");
628 "firrtl.extract.cover");
629 circuitAnno.removeAnnotationsWithClass(
632 state.processRemainingAnnotations(circuit, circuitAnno);
635 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
637 TypeSwitch<Operation *, LogicalResult>(&op)
638 .Case<FModuleOp>([&](
auto module) {
639 auto loweredMod = lowerModule(module, topLevelModule, state);
643 state.recordModuleMapping(&op, loweredMod);
644 modulesToProcess.push_back(loweredMod);
646 module.walk([&](Operation *op) {
647 for (
auto res : op->getResults()) {
649 type_dyn_cast<BaseTypeAliasType>(res.getType()))
650 state.lowerType(aliasType, op->getLoc());
653 return lowerModulePortsAndMoveBody(module, loweredMod, state);
655 .Case<FExtModuleOp>([&](
auto extModule) {
657 lowerExtModule(extModule, topLevelModule, state);
660 state.recordModuleMapping(&op, loweredMod);
663 .Case<FMemModuleOp>([&](
auto memModule) {
665 lowerMemModule(memModule, topLevelModule, state);
668 state.recordModuleMapping(&op, loweredMod);
671 .Case<FormalOp>([&](
auto oldFormalOp) {
672 auto builder = OpBuilder::atBlockEnd(topLevelModule);
673 auto newFormalOp = builder.create<verif::FormalOp>(
674 oldFormalOp.getLoc(), oldFormalOp.getNameAttr(),
675 oldFormalOp.getParametersAttr());
676 newFormalOp.getBody().emplaceBlock();
677 state.recordModuleMapping(oldFormalOp, newFormalOp);
678 formalOpsToProcess.push_back(newFormalOp);
681 .Default([&](Operation *op) {
686 op->moveBefore(topLevelModule, topLevelModule->end());
692 return signalPassFailure();
695 state.typeAliases.freeze();
700 SmallVector<Attribute> dutHierarchyFiles;
701 SmallVector<Attribute> testHarnessHierarchyFiles;
702 circuitAnno.removeAnnotations([&](
Annotation annotation) {
704 auto file = hw::OutputFileAttr::getFromFilename(
706 annotation.
getMember<StringAttr>(
"filename").getValue(),
708 dutHierarchyFiles.push_back(file);
712 auto file = hw::OutputFileAttr::getFromFilename(
714 annotation.
getMember<StringAttr>(
"filename").getValue(),
718 if (state.getTestHarness())
719 testHarnessHierarchyFiles.push_back(file);
721 dutHierarchyFiles.push_back(file);
727 if (!dutHierarchyFiles.empty())
728 state.getNewModule(state.getDut())
731 if (!testHarnessHierarchyFiles.empty())
732 state.getNewModule(state.getTestHarness())
737 auto result = mlir::failableParallelForEachN(
738 &getContext(), 0, modulesToProcess.size(), [&](
auto index) {
739 return lowerModuleOperations(modulesToProcess[index], state);
742 return signalPassFailure();
745 result = mlir::failableParallelForEach(
746 &getContext(), formalOpsToProcess,
747 [&](
auto op) {
return lowerFormalBody(op, state); });
749 return signalPassFailure();
752 for (
auto bind : state.binds) {
757 for (
auto &[module, fragments] : state.fragments)
762 for (
auto oldNew : state.oldToNewModuleMap)
763 oldNew.first->erase();
765 if (!state.macroDeclNames.empty()) {
767 for (
auto name : state.macroDeclNames) {
768 b.create<sv::MacroDeclOp>(name);
773 lowerFileHeader(circuit, state);
780 void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
781 CircuitLoweringState &state) {
788 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
789 StringRef defineTrue =
"",
790 StringRef defineFalse = StringRef()) {
791 if (!defineFalse.data()) {
792 assert(defineTrue.data() &&
"didn't define anything");
794 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
799 if (defineTrue.data())
800 b.create<sv::MacroDefOp>(defName, defineTrue);
802 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
807 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
809 guard, []() {}, body);
812 if (state.usedPrintfCond) {
813 b.create<sv::MacroDeclOp>(
"PRINTF_COND");
814 b.create<sv::MacroDeclOp>(
"PRINTF_COND_");
815 b.create<emit::FragmentOp>(
"PRINTF_COND_FRAGMENT", [&] {
816 b.create<sv::VerbatimOp>(
817 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
819 emitGuard(
"PRINTF_COND_", [&]() {
820 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
825 if (state.usedAssertVerboseCond) {
826 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND");
827 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND_");
828 b.create<emit::FragmentOp>(
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
829 b.create<sv::VerbatimOp>(
830 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
831 "gate to assert error printing.");
832 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
833 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
834 "(`ASSERT_VERBOSE_COND)",
"1");
839 if (state.usedStopCond) {
840 b.create<sv::MacroDeclOp>(
"STOP_COND");
841 b.create<sv::MacroDeclOp>(
"STOP_COND_");
842 b.create<emit::FragmentOp>(
"STOP_COND_FRAGMENT", [&] {
843 b.create<sv::VerbatimOp>(
844 "\n// Users can define 'STOP_COND' to add an extra gate "
845 "to stop conditions.");
846 emitGuard(
"STOP_COND_", [&]() {
847 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
854 FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
855 SmallVectorImpl<hw::PortInfo> &ports,
856 Operation *moduleOp, StringRef moduleName,
858 ports.reserve(firrtlPorts.size());
860 size_t numResults = 0;
861 for (
auto e : llvm::enumerate(firrtlPorts)) {
863 size_t portNo = e.index();
868 if (firrtlPort.
sym.size() > 1 ||
869 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
870 return emitError(firrtlPort.
loc)
871 <<
"cannot lower aggregate port " << firrtlPort.
name
872 <<
" with field sensitive symbols, HW dialect does not support "
873 "per field symbols yet.";
874 hwPort.setSym(firrtlPort.
sym, moduleOp->getContext());
876 if (hadDontTouch && !hwPort.getSym()) {
877 if (hwPort.type.isInteger(0)) {
878 if (enableAnnotationWarning) {
879 mlir::emitWarning(firrtlPort.
loc)
880 <<
"zero width port " << hwPort.name
881 <<
" has dontTouch annotation, removing anyway";
888 moduleOp->getContext(),
889 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
890 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
891 moduleOp->getContext());
896 moduleOp->emitError(
"cannot lower this port type to HW");
902 if (hwPort.type.isInteger(0)) {
903 auto sym = hwPort.getSym();
904 if (sym && !sym.empty()) {
905 return mlir::emitError(firrtlPort.
loc)
906 <<
"zero width port " << hwPort.name
907 <<
" is referenced by name [" << sym
908 <<
"] (e.g. in an XMR) but must be removed";
916 hwPort.argNum = numResults++;
917 }
else if (firrtlPort.
isInput()) {
919 hwPort.argNum = numArgs++;
925 hwPort.argNum = numArgs++;
927 hwPort.loc = firrtlPort.
loc;
928 ports.push_back(hwPort);
938 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
939 return cast<ParamDeclAttr>(a);
944 Builder builder(module);
949 SmallVector<Attribute> newParams;
950 for (
const ParamDeclAttr &entry : params) {
951 auto name = entry.getName();
952 auto type = entry.getType();
953 auto value = ignoreValues ? Attribute() : entry.getValue();
956 newParams.push_back(paramAttr);
958 return builder.getArrayAttr(newParams);
961 bool FIRRTLModuleLowering::handleForceNameAnnos(
970 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
977 auto diag = oldModule.emitOpError()
979 <<
"' that is not a non-local annotation";
980 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
991 auto diag = oldModule.emitOpError()
993 <<
"' whose non-local symbol, '" << sym
994 <<
"' does not exist in the circuit";
995 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1008 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1010 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1011 if (!inserted.second &&
1012 (anno.
getMember(
"name") != (inserted.first->second))) {
1013 auto diag = oldModule.emitError()
1015 <<
"' with different names: " << inserted.first->second
1016 <<
" was not " << anno.
getMember(
"name");
1017 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1028 FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1029 Block *topLevelModule,
1032 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1033 SmallVector<hw::PortInfo, 8> ports;
1034 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1038 StringRef verilogName;
1039 if (
auto defName = oldModule.getDefname())
1040 verilogName = defName.value();
1043 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1044 auto nameAttr = builder.getStringAttr(oldModule.getName());
1050 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1051 SymbolTable::setSymbolVisibility(newModule,
1052 SymbolTable::getSymbolVisibility(oldModule));
1054 bool hasOutputPort =
1055 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1056 if (!hasOutputPort &&
1059 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1065 loweringState.processRemainingAnnotations(oldModule, annos);
1070 FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1071 Block *topLevelModule,
1074 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1075 SmallVector<hw::PortInfo, 8> ports;
1076 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1081 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1083 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1084 oldModule.getModuleNameAttr());
1092 FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1095 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1096 SmallVector<hw::PortInfo, 8> ports;
1097 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1102 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1103 auto nameAttr = builder.getStringAttr(oldModule.getName());
1105 builder.create<
hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1107 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1108 newModule.setCommentAttr(comment);
1111 SmallVector<StringRef, 12> attrNames = {
1112 "annotations",
"convention",
"layers",
1113 "portNames",
"sym_name",
"portDirections",
1114 "portTypes",
"portAnnotations",
"portSymbols",
1115 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName()};
1117 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1118 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1120 llvm::make_filter_range(oldModule->getAttrs(), [&](
auto namedAttr) {
1121 return !attrSet.count(namedAttr.getName()) &&
1122 !newModule->getAttrDictionary().contains(namedAttr.getName());
1124 newAttrs.push_back(i);
1126 newModule->setAttrs(newAttrs);
1130 SymbolTable::setSymbolVisibility(newModule,
1131 SymbolTable::getSymbolVisibility(oldModule));
1137 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1141 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1143 if (!newModule->hasAttr(
"output_file"))
1144 newModule->setAttr(
"output_file", testBenchDir);
1145 newModule->setAttr(
"firrtl.extract.do_not_extract",
1146 builder.getUnitAttr());
1147 newModule.setCommentAttr(
1148 builder.getStringAttr(
"VCS coverage exclude_file"));
1154 loweringState.processRemainingAnnotations(oldModule, annos);
1163 Operation *insertPoint) {
1164 if (!value.hasOneUse())
1167 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1168 if (!attach || attach.getNumOperands() != 2)
1172 auto loweredType =
lowerType(value.getType());
1173 if (loweredType.isInteger(0))
1178 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1179 auto *op = attachedValue.getDefiningOp();
1180 if (op && op->getBlock() == insertPoint->getBlock() &&
1181 !op->isBeforeInBlock(insertPoint))
1186 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1202 if (type_isa<AnalogType>(flipValue.getType()))
1205 Operation *connectOp =
nullptr;
1206 for (
auto &use : flipValue.getUses()) {
1209 if (use.getOperandNumber() != 0)
1211 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1217 connectOp = use.getOwner();
1227 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1228 if (loweredType.isInteger(0))
1233 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1235 auto connectSrc = connectOp->getOperand(1);
1238 if (!isa<FIRRTLType>(connectSrc.getType())) {
1244 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1245 connectSrc = builder
1246 .create<mlir::UnrealizedConversionCastOp>(
1247 type_cast<FIRRTLBaseType>(connectSrc.getType())
1254 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1256 if (destTy != connectSrc.getType() &&
1257 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1258 isa<BaseTypeAliasType>(destTy))) {
1260 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1262 if (!destTy.isGround()) {
1264 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1266 }
else if (destTy.getBitWidthOrSentinel() !=
1267 type_cast<FIRRTLBaseType>(connectSrc.getType())
1268 .getBitWidthOrSentinel()) {
1271 auto destWidth = destTy.getBitWidthOrSentinel();
1272 assert(destWidth != -1 &&
"must know integer widths");
1273 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1285 SmallVector<SubfieldOp> accesses;
1286 for (
auto *op : structValue.getUsers()) {
1287 assert(isa<SubfieldOp>(op));
1288 auto fieldAccess = cast<SubfieldOp>(op);
1290 fieldAccess.getInput().getType().base().getElementIndex(field);
1291 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1292 accesses.push_back(fieldAccess);
1300 LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1303 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1310 bodyBuilder.setInsertionPoint(cursor);
1313 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1314 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1315 "port count mismatch");
1317 SmallVector<Value, 4> outputs;
1320 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1321 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1323 unsigned nextHWInputArg = 0;
1324 int hwPortIndex = -1;
1325 for (
auto [firrtlPortIndex, port] : llvm::enumerate(firrtlPorts)) {
1327 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1330 type_isa<FIRRTLBaseType>(port.type) &&
1331 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1335 if (!port.isOutput() && !isZeroWidth) {
1338 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1344 oldArg.replaceAllUsesWith(newArg);
1350 if (isZeroWidth && port.isInput()) {
1351 Value newArg = bodyBuilder
1352 .create<WireOp>(port.type,
"." + port.getName().str() +
1355 oldArg.replaceAllUsesWith(newArg);
1363 outputs.push_back(value);
1364 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1370 auto newArg = bodyBuilder.create<WireOp>(
1371 port.type,
"." + port.getName().str() +
".output");
1374 oldArg.replaceAllUsesWith(newArg.getResult());
1377 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1378 if (!resultHWType.isInteger(0)) {
1381 outputs.push_back(output);
1384 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1385 newArg.setInnerSymAttr(sym);
1386 newModule.setPortSymbolAttr(hwPortIndex, {});
1392 outputOp->setOperands(outputs);
1395 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1396 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1397 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1398 oldBlockInstList.begin(), oldBlockInstList.end());
1409 FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp formalOp,
1411 auto builder = OpBuilder::atBlockEnd(&formalOp.getBody().front());
1416 auto oldFormalOp = cast<FormalOp>(
loweringState.getOldModule(formalOp));
1417 auto moduleName = oldFormalOp.getModuleNameAttr().getAttr();
1418 auto oldModule = cast<FModuleOp>(
1419 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1421 dyn_cast_or_null<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1423 return oldFormalOp->emitOpError()
1424 <<
"could not find module " << oldModule.getSymNameAttr();
1427 SmallVector<Value> symbolicInputs;
1428 for (
auto arg : newModule.getBody().getArguments())
1429 symbolicInputs.push_back(
1430 builder.create<verif::SymbolicValueOp>(arg.getLoc(), arg.getType()));
1433 builder.create<hw::InstanceOp>(formalOp.getLoc(), newModule,
1434 newModule.getNameAttr(), symbolicInputs);
1444 struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1446 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1447 : theModule(module), circuitState(circuitState),
1448 builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1449 backedgeBuilder(builder, module.getLoc()) {}
1451 LogicalResult
run();
1454 Value getOrCreateClockConstant(seq::ClockConst clock);
1455 Value getOrCreateIntConstant(
const APInt &value);
1456 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1457 bool isSigned =
false) {
1458 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1460 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1461 Value getOrCreateXConstant(
unsigned numBits);
1462 Value getOrCreateZConstant(Type type);
1463 Value getPossiblyInoutLoweredValue(Value value);
1464 Value getLoweredValue(Value value);
1465 Value getLoweredNonClockValue(Value value);
1466 Value getLoweredAndExtendedValue(Value value, Type destType);
1467 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1468 Value getLoweredFmtOperand(Value operand);
1469 LogicalResult setLowering(Value orig, Value result);
1470 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1471 template <
typename ResultOpType,
typename... CtorArgTypes>
1472 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1473 template <
typename ResultOpType,
typename... CtorArgTypes>
1474 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1475 Backedge createBackedge(Location loc, Type type);
1476 Backedge createBackedge(Value orig, Type type);
1477 bool updateIfBackedge(Value dest, Value src);
1480 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1485 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1486 if (forceable.isForceable())
1494 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1495 auto attr = op.getInnerSymAttr();
1499 if (requiresInnerSymbol(op))
1501 op.getContext(), attr, 0,
1502 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1506 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1510 Value getReadValue(Value v);
1512 Value getNonClockValue(Value v);
1514 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1515 sv::ResetType resetStyle, sv::EventControl resetEdge,
1516 Value reset,
const std::function<
void(
void)> &body = {},
1517 const std::function<void(
void)> &resetBody = {});
1518 void addToAlwaysBlock(Value clock,
1519 const std::function<
void(
void)> &body = {}) {
1520 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1521 sv::EventControl(), Value(), body,
1522 std::function<
void(
void)>());
1525 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1526 std::function<
void(
void)>
emit);
1527 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1528 std::function<
void(
void)> elseCtor = {});
1529 void addToInitialBlock(std::function<
void(
void)> body);
1530 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1531 std::function<
void(
void)> elseCtor = {});
1532 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1534 bool allowTruncate);
1535 Value createArrayIndexing(Value array, Value index);
1536 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1543 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1544 UnloweredOpResult handleUnloweredOp(Operation *op);
1545 LogicalResult visitExpr(ConstantOp op);
1546 LogicalResult visitExpr(SpecialConstantOp op);
1547 LogicalResult visitExpr(SubindexOp op);
1548 LogicalResult visitExpr(SubaccessOp op);
1549 LogicalResult visitExpr(SubfieldOp op);
1550 LogicalResult visitExpr(VectorCreateOp op);
1551 LogicalResult visitExpr(BundleCreateOp op);
1552 LogicalResult visitExpr(FEnumCreateOp op);
1553 LogicalResult visitExpr(AggregateConstantOp op);
1554 LogicalResult visitExpr(IsTagOp op);
1555 LogicalResult visitExpr(SubtagOp op);
1556 LogicalResult visitUnhandledOp(Operation *op) {
return failure(); }
1557 LogicalResult visitInvalidOp(Operation *op) {
1558 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1559 return visitUnrealizedConversionCast(castOp);
1564 LogicalResult visitDecl(WireOp op);
1565 LogicalResult visitDecl(NodeOp op);
1566 LogicalResult visitDecl(RegOp op);
1567 LogicalResult visitDecl(RegResetOp op);
1568 LogicalResult visitDecl(MemOp op);
1569 LogicalResult visitDecl(InstanceOp oldInstance);
1570 LogicalResult visitDecl(VerbatimWireOp op);
1573 LogicalResult lowerNoopCast(Operation *op);
1574 LogicalResult visitExpr(AsSIntPrimOp op);
1575 LogicalResult visitExpr(AsUIntPrimOp op);
1576 LogicalResult visitExpr(AsClockPrimOp op);
1577 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1579 LogicalResult visitExpr(HWStructCastOp op);
1580 LogicalResult visitExpr(BitCastOp op);
1582 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1583 LogicalResult visitExpr(CvtPrimOp op);
1584 LogicalResult visitExpr(NotPrimOp op);
1585 LogicalResult visitExpr(NegPrimOp op);
1586 LogicalResult visitExpr(PadPrimOp op);
1587 LogicalResult visitExpr(XorRPrimOp op);
1588 LogicalResult visitExpr(AndRPrimOp op);
1589 LogicalResult visitExpr(OrRPrimOp op);
1592 template <
typename ResultUnsignedOpType,
1593 typename ResultSignedOpType = ResultUnsignedOpType>
1594 LogicalResult lowerBinOp(Operation *op);
1595 template <
typename ResultOpType>
1596 LogicalResult lowerBinOpToVariadic(Operation *op);
1598 template <
typename ResultOpType>
1599 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1601 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1602 ICmpPredicate unsignedOp);
1603 template <
typename SignedOp,
typename Un
signedOp>
1604 LogicalResult lowerDivLikeOp(Operation *op);
1606 LogicalResult visitExpr(CatPrimOp op);
1608 LogicalResult visitExpr(AndPrimOp op) {
1609 return lowerBinOpToVariadic<comb::AndOp>(op);
1611 LogicalResult visitExpr(OrPrimOp op) {
1612 return lowerBinOpToVariadic<comb::OrOp>(op);
1614 LogicalResult visitExpr(XorPrimOp op) {
1615 return lowerBinOpToVariadic<comb::XorOp>(op);
1617 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1618 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1620 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1621 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1623 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1624 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1626 LogicalResult visitExpr(AddPrimOp op) {
1627 return lowerBinOpToVariadic<comb::AddOp>(op);
1629 LogicalResult visitExpr(EQPrimOp op) {
1630 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1632 LogicalResult visitExpr(NEQPrimOp op) {
1633 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1635 LogicalResult visitExpr(LTPrimOp op) {
1636 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1638 LogicalResult visitExpr(LEQPrimOp op) {
1639 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1641 LogicalResult visitExpr(GTPrimOp op) {
1642 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1644 LogicalResult visitExpr(GEQPrimOp op) {
1645 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1648 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1649 LogicalResult visitExpr(MulPrimOp op) {
1650 return lowerBinOpToVariadic<comb::MulOp>(op);
1652 LogicalResult visitExpr(DivPrimOp op) {
1653 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1655 LogicalResult visitExpr(RemPrimOp op) {
1656 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1660 LogicalResult visitExpr(IsXIntrinsicOp op);
1661 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1662 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1663 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1664 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1665 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1666 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1667 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1668 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1669 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1670 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1671 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1672 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1673 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1674 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1675 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1676 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1677 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1678 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1679 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1680 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1682 template <
typename TargetOp,
typename IntrinsicOp>
1683 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1684 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1685 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1686 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1687 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1688 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1691 LogicalResult visitExpr(BitsPrimOp op);
1692 LogicalResult visitExpr(InvalidValueOp op);
1693 LogicalResult visitExpr(HeadPrimOp op);
1694 LogicalResult visitExpr(ShlPrimOp op);
1695 LogicalResult visitExpr(ShrPrimOp op);
1696 LogicalResult visitExpr(DShlPrimOp op) {
1697 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1699 LogicalResult visitExpr(DShrPrimOp op) {
1700 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1702 LogicalResult visitExpr(DShlwPrimOp op) {
1703 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1705 LogicalResult visitExpr(TailPrimOp op);
1706 LogicalResult visitExpr(MuxPrimOp op);
1707 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1708 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1709 LogicalResult visitExpr(MultibitMuxOp op);
1710 LogicalResult visitExpr(VerbatimExprOp op);
1711 LogicalResult visitExpr(XMRRefOp op);
1712 LogicalResult visitExpr(XMRDerefOp op);
1715 LogicalResult lowerVerificationStatement(
1716 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1717 Value enable, StringAttr messageAttr, ValueRange operands,
1718 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1720 LogicalResult visitStmt(SkipOp op);
1722 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1723 LogicalResult visitStmt(ConnectOp op);
1724 LogicalResult visitStmt(MatchingConnectOp op);
1725 LogicalResult visitStmt(ForceOp op);
1726 LogicalResult visitStmt(PrintFOp op);
1727 LogicalResult visitStmt(StopOp op);
1728 LogicalResult visitStmt(AssertOp op);
1729 LogicalResult visitStmt(AssumeOp op);
1730 LogicalResult visitStmt(CoverOp op);
1731 LogicalResult visitStmt(AttachOp op);
1732 LogicalResult visitStmt(RefForceOp op);
1733 LogicalResult visitStmt(RefForceInitialOp op);
1734 LogicalResult visitStmt(RefReleaseOp op);
1735 LogicalResult visitStmt(RefReleaseInitialOp op);
1737 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1738 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1739 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1741 LogicalResult fixupLTLOps();
1744 return circuitState.lowerType(type, builder.getLoc());
1752 CircuitLoweringState &circuitState;
1755 ImplicitLocOpBuilder builder;
1760 DenseMap<Value, Value> valueMapping;
1764 DenseMap<Value, Value> fromClockMapping;
1768 DenseMap<Attribute, Value> hwConstantMap;
1769 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1773 DenseMap<unsigned, Value> hwConstantXMap;
1774 DenseMap<Type, Value> hwConstantZMap;
1780 DenseMap<Value, Value> readInOutCreated;
1784 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1785 sv::ResetType, sv::EventControl, Value>;
1793 hw::InnerSymbolNamespace moduleNamespace;
1802 llvm::MapVector<Value, Value> backedges;
1809 DenseSet<Operation *> maybeUnusedValues;
1811 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1812 void maybeUnused(Value value) {
1813 if (
auto *op = value.getDefiningOp())
1825 SetVector<Operation *> ltlOpFixupWorklist;
1829 LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1839 auto &body = theModule.getBody();
1841 SmallVector<Operation *, 16> opsToRemove;
1845 auto result = theModule.walk([&](Operation *op) {
1846 builder.setInsertionPoint(op);
1847 builder.setLoc(op->getLoc());
1848 auto done = succeeded(dispatchVisitor(op));
1849 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
1851 opsToRemove.push_back(op);
1853 switch (handleUnloweredOp(op)) {
1854 case AlreadyLowered:
1857 opsToRemove.push_back(op);
1859 case LoweringFailure:
1860 backedgeBuilder.abandon();
1861 return WalkResult::interrupt();
1864 return WalkResult::advance();
1867 if (result.wasInterrupted())
1875 for (
auto &[backedge, value] : backedges) {
1876 SmallVector<Location> driverLocs;
1882 if (backedge == value) {
1883 Location edgeLoc = backedge.getLoc();
1884 if (driverLocs.empty()) {
1885 mlir::emitError(edgeLoc,
"sink does not have a driver");
1887 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
1888 for (
auto loc : driverLocs)
1889 diag.attachNote(loc) <<
"through driver here";
1895 auto *it = backedges.find(value);
1896 if (it == backedges.end())
1899 driverLocs.push_back(value.getLoc());
1902 if (
auto *defOp = backedge.getDefiningOp())
1903 maybeUnusedValues.erase(defOp);
1904 backedge.replaceAllUsesWith(value);
1912 while (!opsToRemove.empty()) {
1913 auto *op = opsToRemove.pop_back_val();
1918 for (
auto result : op->getResults()) {
1922 auto builder = OpBuilder::atBlockBegin(&body.front());
1924 builder.getIntegerType(0), 0);
1925 maybeUnusedValues.insert(zeroI0);
1927 result.replaceAllUsesWith(zeroI0);
1930 if (!op->use_empty()) {
1931 auto d = op->emitOpError(
1932 "still has uses; should remove ops in reverse order of visitation");
1933 SmallPtrSet<Operation *, 2> visited;
1934 for (
auto *user : op->getUsers())
1935 if (visited.insert(user).second)
1936 d.attachNote(user->getLoc())
1937 <<
"used by " << user->getName() <<
" op";
1940 maybeUnusedValues.erase(op);
1945 while (!maybeUnusedValues.empty()) {
1946 auto it = maybeUnusedValues.begin();
1948 maybeUnusedValues.erase(it);
1949 if (!isOpTriviallyDead(op))
1951 for (
auto operand : op->getOperands())
1952 if (
auto *defOp = operand.getDefiningOp())
1953 maybeUnusedValues.insert(defOp);
1959 if (failed(fixupLTLOps()))
1970 Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
1973 auto &entry = hwConstantMap[attr];
1977 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1978 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
1984 Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
1985 auto attr = builder.getIntegerAttr(
1986 builder.getIntegerType(value.getBitWidth()), value);
1988 auto &entry = hwConstantMap[attr];
1992 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1993 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
1999 Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2002 if (hw::type_isa<IntegerType>(type))
2003 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2005 auto cache = hwAggregateConstantMap.lookup({value, type});
2010 SmallVector<Attribute> values;
2011 for (
auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
2013 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2014 subType = array.getElementType();
2015 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2016 subType = structType.getElements()[e.index()].type;
2018 assert(
false &&
"type must be either array or struct");
2020 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2024 if (hw::type_isa<hw::ArrayType>(type))
2025 std::reverse(values.begin(), values.end());
2027 auto &entry = hwAggregateConstantMap[{value, type}];
2028 entry = builder.getArrayAttr(values);
2036 const std::function<LogicalResult()> &fn) {
2037 assert(failedOperand &&
"Should be called on the failed operand");
2045 Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2047 auto &entry = hwConstantXMap[numBits];
2051 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2052 entry = entryBuilder.create<sv::ConstantXOp>(
2053 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2057 Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2058 auto &entry = hwConstantZMap[type];
2060 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2061 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2070 Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2072 if (isa<BlockArgument>(value))
2076 if (
auto lowering = valueMapping.lookup(value)) {
2077 assert(!isa<FIRRTLType>(lowering.getType()) &&
2078 "Lowered value should be a non-FIRRTL value");
2087 Value FIRRTLLowering::getLoweredValue(Value value) {
2088 auto result = getPossiblyInoutLoweredValue(value);
2094 if (isa<hw::InOutType>(result.getType()))
2095 return getReadValue(result);
2101 Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2102 auto result = getLoweredValue(value);
2106 if (hw::type_isa<seq::ClockType>(result.getType()))
2107 return getNonClockValue(result);
2115 Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2118 bool allowTruncate) {
2119 SmallVector<Value> resultBuffer;
2124 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2125 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2126 auto resultType = builder.getIntegerType(destWidth);
2128 if (srcWidth == destWidth)
2131 if (srcWidth > destWidth) {
2135 builder.emitError(
"operand should not be a truncation");
2139 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2141 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2149 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2150 .Case<FVectorType>([&](
auto srcVectorType) {
2151 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2152 unsigned size = resultBuffer.size();
2153 unsigned indexWidth =
2155 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2156 destVectorType.getNumElements());
2158 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2160 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2161 destVectorType.getElementType())))
2164 SmallVector<Value> temp(resultBuffer.begin() + size,
2165 resultBuffer.end());
2167 resultBuffer.resize(size);
2168 resultBuffer.push_back(array);
2171 .Case<BundleType>([&](BundleType srcStructType) {
2172 auto destStructType = firrtl::type_cast<BundleType>(destType);
2173 unsigned size = resultBuffer.size();
2176 if (destStructType.getNumElements() != srcStructType.getNumElements())
2179 for (
auto elem : llvm::enumerate(destStructType)) {
2180 auto structExtract =
2182 if (failed(recurse(structExtract,
2183 srcStructType.getElementType(elem.index()),
2184 destStructType.getElementType(elem.index()))))
2187 SmallVector<Value> temp(resultBuffer.begin() + size,
2188 resultBuffer.end());
2191 resultBuffer.resize(size);
2192 resultBuffer.push_back(newStruct);
2195 .Case<IntType>([&](
auto) {
2196 if (
auto result = cast(src, srcType, destType)) {
2197 resultBuffer.push_back(result);
2202 .Default([&](
auto) {
return failure(); });
2205 if (failed(recurse(array, sourceType, destType)))
2208 assert(resultBuffer.size() == 1 &&
2209 "resultBuffer must only contain a result array if `success` is true");
2210 return resultBuffer[0];
2218 Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2219 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2220 type_isa<FIRRTLBaseType>(destType) &&
2221 "input/output value should be FIRRTL");
2224 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2225 if (destWidth == -1)
2228 auto result = getLoweredValue(value);
2240 return getOrCreateIntConstant(destWidth, 0);
2244 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2246 auto loweredDstType =
lowerType(destType);
2247 if (result.getType() != loweredDstType &&
2248 (isa<hw::TypeAliasType>(result.getType()) ||
2249 isa<hw::TypeAliasType>(loweredDstType))) {
2250 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, result);
2254 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2256 if (destType == value.getType())
2259 return getExtOrTruncAggregateValue(
2260 result, type_cast<FIRRTLBaseType>(value.getType()),
2261 type_cast<FIRRTLBaseType>(destType),
2265 if (isa<seq::ClockType>(result.getType())) {
2267 if (destType == value.getType())
2269 builder.emitError(
"cannot use clock type as an integer");
2273 auto intResultType = dyn_cast<IntegerType>(result.getType());
2274 if (!intResultType) {
2275 builder.emitError(
"operand of type ")
2276 << result.getType() <<
" cannot be used as an integer";
2280 auto srcWidth = intResultType.getWidth();
2281 if (srcWidth ==
unsigned(destWidth))
2284 if (srcWidth >
unsigned(destWidth)) {
2285 builder.emitError(
"operand should not be a truncation");
2289 auto resultType = builder.getIntegerType(destWidth);
2293 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2294 if (type_cast<IntType>(valueFIRType).isSigned())
2297 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2306 Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2307 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2308 type_isa<FIRRTLBaseType>(destType) &&
2309 "input/output value should be FIRRTL");
2312 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2313 if (destWidth == -1)
2316 auto result = getLoweredValue(value);
2328 return getOrCreateIntConstant(destWidth, 0);
2332 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2334 if (destType == value.getType())
2337 return getExtOrTruncAggregateValue(
2338 result, type_cast<FIRRTLBaseType>(value.getType()),
2339 type_cast<FIRRTLBaseType>(destType),
2343 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2344 if (srcWidth ==
unsigned(destWidth))
2350 if (srcWidth >
unsigned(destWidth)) {
2351 auto resultType = builder.getIntegerType(destWidth);
2355 auto resultType = builder.getIntegerType(destWidth);
2359 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2360 if (type_cast<IntType>(valueFIRType).isSigned())
2363 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2370 Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2371 auto loweredValue = getLoweredValue(operand);
2372 if (!loweredValue) {
2376 loweredValue = getOrCreateIntConstant(1, 0);
2381 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2382 if (intTy.isSigned())
2383 loweredValue = builder.create<sv::SystemFunctionOp>(
2384 loweredValue.getType(),
"signed", loweredValue);
2386 return loweredValue;
2395 LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2396 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2397 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2398 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2402 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2405 if (srcWidth != -1) {
2407 assert((srcWidth != 0) &&
2408 "Lowering produced value for zero width source");
2410 assert((srcWidth == 0) &&
2411 "Lowering produced null value but source wasn't zero width");
2415 assert(result &&
"Lowering of foreign type produced null value");
2418 auto &slot = valueMapping[orig];
2419 assert(!slot &&
"value lowered multiple times");
2426 LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2430 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2431 auto &entry = hwConstantMap[cst.getValueAttr()];
2442 cst->moveBefore(&theModule.getBodyBlock()->front());
2446 return setLowering(orig, result);
2451 template <
typename ResultOpType,
typename... CtorArgTypes>
2452 LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2453 CtorArgTypes... args) {
2454 auto result = builder.createOrFold<ResultOpType>(args...);
2455 if (
auto *op = result.getDefiningOp())
2457 return setPossiblyFoldedLowering(orig->getResult(0), result);
2464 template <
typename ResultOpType,
typename... CtorArgTypes>
2465 LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2466 CtorArgTypes... args) {
2467 auto result = builder.createOrFold<ResultOpType>(args...);
2468 if (
auto *op = result.getDefiningOp())
2469 ltlOpFixupWorklist.insert(op);
2470 return setPossiblyFoldedLowering(orig->getResult(0), result);
2479 Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2480 auto backedge = backedgeBuilder.
get(type, loc);
2481 backedges.insert({backedge, backedge});
2489 Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2490 auto backedge = createBackedge(orig.getLoc(), type);
2491 (void)setLowering(orig, backedge);
2497 bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2498 auto backedgeIt = backedges.find(dest);
2499 if (backedgeIt == backedges.end())
2501 backedgeIt->second = src;
2509 void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2510 const std::function<
void(
void)> &fn, Region ®ion) {
2514 auto oldIP = builder.saveInsertionPoint();
2516 builder.setInsertionPointToEnd(®ion.front());
2518 builder.restoreInsertionPoint(oldIP);
2522 Value FIRRTLLowering::getReadValue(Value v) {
2523 Value result = readInOutCreated.lookup(v);
2529 auto oldIP = builder.saveInsertionPoint();
2530 if (
auto *vOp = v.getDefiningOp()) {
2531 builder.setInsertionPointAfter(vOp);
2535 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2540 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2541 result = getReadValue(arrayIndexInout.getInput());
2543 arrayIndexInout.getIndex());
2548 builder.restoreInsertionPoint(oldIP);
2549 readInOutCreated.insert({v, result});
2553 Value FIRRTLLowering::getNonClockValue(Value v) {
2554 auto it = fromClockMapping.try_emplace(v, Value{});
2556 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2557 builder.setInsertionPointAfterValue(v);
2558 it.first->second = builder.create<seq::FromClockOp>(v);
2560 return it.first->second;
2563 void FIRRTLLowering::addToAlwaysBlock(
2564 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2565 sv::EventControl resetEdge, Value reset,
2566 const std::function<
void(
void)> &body,
2567 const std::function<
void(
void)> &resetBody) {
2568 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2569 resetStyle, resetEdge, reset};
2570 sv::AlwaysOp alwaysOp;
2571 sv::IfOp insideIfOp;
2572 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2576 assert(resetStyle != sv::ResetType::NoReset);
2589 auto createIfOp = [&]() {
2592 insideIfOp = builder.create<sv::IfOp>(
2593 reset, []() {}, []() {});
2595 if (resetStyle == sv::ResetType::AsyncReset) {
2596 sv::EventControl events[] = {clockEdge, resetEdge};
2597 Value clocks[] = {clock, reset};
2599 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2600 if (resetEdge == sv::EventControl::AtNegEdge)
2601 llvm_unreachable(
"negative edge for reset is not expected");
2605 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2609 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2610 insideIfOp =
nullptr;
2612 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2616 assert(insideIfOp &&
"reset body must be initialized before");
2617 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2618 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2620 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2626 alwaysOp->moveBefore(builder.getInsertionBlock(),
2627 builder.getInsertionPoint());
2630 LogicalResult FIRRTLLowering::emitGuards(Location loc,
2631 ArrayRef<Attribute> guards,
2632 std::function<
void(
void)>
emit) {
2633 if (guards.empty()) {
2637 auto guard = dyn_cast<StringAttr>(guards[0]);
2639 return mlir::emitError(loc,
2640 "elements in `guards` array must be `StringAttr`");
2643 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2644 LogicalResult result = LogicalResult::failure();
2645 addToIfDefBlock(guard.getValue(), [&]() {
2646 result = emitGuards(loc, guards.drop_front(), emit);
2651 void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2652 std::function<
void(
void)> thenCtor,
2653 std::function<
void(
void)> elseCtor) {
2654 auto condAttr = builder.getStringAttr(cond);
2655 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2657 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2658 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2663 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2665 ifdefBlocks[{builder.getBlock(), condAttr}] =
2666 builder.create<
sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2670 void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2671 auto op = initialBlocks.lookup(builder.getBlock());
2673 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2678 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2680 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2684 void FIRRTLLowering::addIfProceduralBlock(Value cond,
2685 std::function<
void(
void)> thenCtor,
2686 std::function<
void(
void)> elseCtor) {
2689 auto insertIt = builder.getInsertionPoint();
2690 if (insertIt != builder.getBlock()->begin())
2691 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2692 if (ifOp.getCond() == cond) {
2693 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2694 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2699 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2711 FIRRTLLowering::UnloweredOpResult
2712 FIRRTLLowering::handleUnloweredOp(Operation *op) {
2717 if (!isa<FIRRTLDialect>(op->getDialect())) {
2718 for (
auto &operand : op->getOpOperands())
2719 if (
auto lowered = getPossiblyInoutLoweredValue(operand.get()))
2720 operand.set(lowered);
2721 for (
auto result : op->getResults())
2722 (void)setLowering(result, result);
2723 return AlreadyLowered;
2735 if (op->getNumResults() == 1) {
2736 auto resultType = op->getResult(0).getType();
2737 if (type_isa<FIRRTLBaseType>(resultType) &&
2739 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2741 (void)setLowering(op->getResult(0), Value());
2745 op->emitOpError(
"LowerToHW couldn't handle this operation");
2746 return LoweringFailure;
2749 LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2752 return setLowering(op, Value());
2754 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2757 LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2759 if (isa<ClockType>(op.getType())) {
2760 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2761 : seq::ClockConst::Low);
2763 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
2765 return setLowering(op, cst);
2768 FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2769 auto iIdx = getOrCreateIntConstant(
2771 firrtl::type_cast<FVectorType>(op.getInput().getType())
2778 if (isa<sv::InOutType>(input.getType()))
2779 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2782 if (
auto *definingOp = result.getDefiningOp())
2787 FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2788 Value valueIdx = getLoweredAndExtOrTruncValue(
2792 firrtl::type_cast<FVectorType>(op.getInput().getType())
2793 .getNumElements())));
2795 op->emitError() <<
"input lowering failed";
2802 if (isa<sv::InOutType>(input.getType()))
2803 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2805 result = createArrayIndexing(input, valueIdx);
2806 if (
auto *definingOp = result.getDefiningOp())
2811 FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2812 auto resultType =
lowerType(op->getResult(0).getType());
2813 if (!resultType || !input) {
2814 op->emitError() <<
"subfield type lowering failed";
2820 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2821 .getElementName(op.getFieldIndex());
2823 if (isa<sv::InOutType>(input.getType()))
2824 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2827 if (
auto *definingOp = result.getDefiningOp())
2832 LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2834 return setLowering(op, Value());
2836 auto input = getPossiblyInoutLoweredValue(op.getInput());
2838 return op.emitError() <<
"input lowering failed";
2840 auto result = lowerSubindex(op, input);
2843 return setLowering(op, *result);
2846 LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2848 return setLowering(op, Value());
2850 auto input = getPossiblyInoutLoweredValue(op.getInput());
2852 return op.emitError() <<
"input lowering failed";
2854 auto result = lowerSubaccess(op, input);
2857 return setLowering(op, *result);
2860 LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2863 if (getLoweredValue(op) || !op.getInput())
2867 return setLowering(op, Value());
2869 auto input = getPossiblyInoutLoweredValue(op.getInput());
2871 return op.emitError() <<
"input lowering failed";
2873 auto result = lowerSubfield(op, input);
2876 return setLowering(op, *result);
2879 LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2880 auto resultType =
lowerType(op.getResult().getType());
2881 SmallVector<Value> operands;
2883 for (
auto oper : llvm::reverse(op.getOperands())) {
2884 auto val = getLoweredValue(oper);
2887 operands.push_back(val);
2889 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2892 LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2893 auto resultType =
lowerType(op.getResult().getType());
2894 SmallVector<Value> operands;
2895 for (
auto oper : op.getOperands()) {
2896 auto val = getLoweredValue(oper);
2899 operands.push_back(val);
2901 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2904 LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2907 return setLowering(op, Value());
2909 auto input = getLoweredValue(op.getInput());
2910 auto tagName = op.getFieldNameAttr();
2913 if (
auto structType = dyn_cast<hw::StructType>(type)) {
2914 auto enumType = structType.getFieldType(
"tag");
2916 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
2917 auto unionType = structType.getFieldType(
"body");
2918 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2919 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2920 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2923 return setLoweringTo<hw::EnumConstantOp>(
2927 LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2928 auto resultType =
lowerType(op.getResult().getType());
2930 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2932 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2933 cast<ArrayAttr>(attr));
2936 LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2937 auto tagName = op.getFieldNameAttr();
2938 auto lhs = getLoweredValue(op.getInput());
2939 if (isa<hw::StructType>(lhs.getType()))
2942 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
2943 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2946 LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2949 return setLowering(op, Value());
2951 auto tagName = op.getFieldNameAttr();
2952 auto input = getLoweredValue(op.getInput());
2954 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2961 LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
2962 auto origResultType = op.getResult().getType();
2966 if (!type_isa<FIRRTLType>(origResultType)) {
2967 createBackedge(op.getResult(), origResultType);
2971 auto resultType =
lowerType(origResultType);
2975 if (resultType.isInteger(0)) {
2976 if (op.getInnerSym())
2977 return op.emitError(
"zero width wire is referenced by name [")
2978 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
2979 return setLowering(op.getResult(), Value());
2983 auto innerSym = lowerInnerSymbol(op);
2984 auto name = op.getNameAttr();
2987 auto wire = builder.create<hw::WireOp>(
2988 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
2993 return setLowering(op.getResult(), wire);
2996 LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
2997 auto resultTy =
lowerType(op.getType());
3002 SmallVector<Value, 4> operands;
3003 operands.reserve(op.getSubstitutions().size());
3004 for (
auto operand : op.getSubstitutions()) {
3005 auto lowered = getLoweredValue(operand);
3008 operands.push_back(lowered);
3011 ArrayAttr symbols = op.getSymbolsAttr();
3015 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3019 LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3020 auto operand = getLoweredValue(op.getInput());
3022 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3023 if (op.getInnerSym())
3024 return op.emitError(
"zero width node is referenced by name [")
3025 << *op.getInnerSym()
3026 <<
"] (e.g. in an XMR) but must be "
3028 return setLowering(op.getResult(), Value());
3034 auto name = op.getNameAttr();
3035 auto innerSym = lowerInnerSymbol(op);
3038 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3043 operand = builder.create<hw::WireOp>(operand, name);
3047 return setLowering(op.getResult(), operand);
3050 LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3051 auto resultType =
lowerType(op.getResult().getType());
3054 if (resultType.isInteger(0))
3055 return setLowering(op.getResult(), Value());
3057 Value clockVal = getLoweredValue(op.getClockVal());
3062 auto innerSym = lowerInnerSymbol(op);
3063 Backedge inputEdge = backedgeBuilder.
get(resultType);
3064 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3065 op.getNameAttr(), innerSym);
3068 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3069 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3070 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3071 reg->setAttr(
"firrtl.random_init_start", randomStart);
3072 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3073 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3079 inputEdge.setValue(
reg);
3080 (void)setLowering(op.getResult(),
reg);
3084 LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3085 auto resultType =
lowerType(op.getResult().getType());
3088 if (resultType.isInteger(0))
3089 return setLowering(op.getResult(), Value());
3091 Value clockVal = getLoweredValue(op.getClockVal());
3092 Value resetSignal = getLoweredValue(op.getResetSignal());
3094 Value resetValue = getLoweredAndExtOrTruncValue(
3095 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3097 if (!clockVal || !resetSignal || !resetValue)
3101 auto innerSym = lowerInnerSymbol(op);
3102 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3103 Backedge inputEdge = backedgeBuilder.
get(resultType);
3105 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3106 resetSignal, resetValue, innerSym, isAsync);
3109 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3110 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3111 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3112 reg->setAttr(
"firrtl.random_init_start", randomStart);
3113 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3114 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3120 inputEdge.setValue(
reg);
3121 (void)setLowering(op.getResult(),
reg);
3126 LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3129 if (type_isa<BundleType>(op.getDataType()))
3130 return op.emitOpError(
3131 "should have already been lowered from a ground type to an aggregate "
3132 "type using the LowerTypes pass. Use "
3133 "'firtool --lower-types' or 'circt-opt "
3134 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3143 : std::optional<uint32_t>());
3145 seq::FirMemInitAttr memInit;
3146 if (
auto init = op.getInitAttr())
3148 init.getIsBinary(), init.getIsInline());
3150 auto memDecl = builder.create<seq::FirMemOp>(
3153 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3157 if (!circuitState.isInDUT(theModule))
3158 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3159 memDecl.setOutputFileAttr(testBenchDir);
3163 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3165 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3168 (void)setLowering(a, value);
3174 auto addInput = [&](StringRef field, Value backedge) {
3176 if (cast<FIRRTLBaseType>(a.getType())
3178 .getBitWidthOrSentinel() > 0)
3179 (
void)setLowering(a, backedge);
3185 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3189 Value backedge, portValue;
3191 portValue = getOrCreateXConstant(1);
3194 backedge = portValue = createBackedge(builder.getLoc(), portType);
3196 addInput(field, backedge);
3200 auto addClock = [&](StringRef field) -> Value {
3202 Value portValue = createBackedge(builder.getLoc(), clockTy);
3203 addInput(field, portValue);
3207 auto memportKind = op.getPortKind(i);
3208 if (memportKind == MemOp::PortKind::Read) {
3209 auto addr = addInputPort(
"addr", op.getAddrBits());
3210 auto en = addInputPort(
"en", 1);
3211 auto clk = addClock(
"clk");
3212 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3213 addOutput(
"data", memSummary.
dataWidth, data);
3214 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3215 auto addr = addInputPort(
"addr", op.getAddrBits());
3216 auto en = addInputPort(
"en", 1);
3217 auto clk = addClock(
"clk");
3220 auto mode = addInputPort(
"wmode", 1);
3222 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3229 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3231 addOutput(
"rdata", memSummary.
dataWidth, rdata);
3233 auto addr = addInputPort(
"addr", op.getAddrBits());
3236 auto en = addInputPort(
"en", 1);
3240 auto clk = addClock(
"clk");
3253 LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3254 Operation *oldModule =
3255 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3257 auto *newModule = circuitState.getNewModule(oldModule);
3259 oldInstance->emitOpError(
"could not find module [")
3260 << oldInstance.getModuleName() <<
"] referenced by instance";
3266 ArrayAttr parameters;
3267 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3272 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3277 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3278 portIndicesByName[portInfo[portIdx].name] = portIdx;
3282 SmallVector<Value, 8> operands;
3283 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3284 auto &port = portInfo[portIndex];
3287 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3292 if (portType.isInteger(0))
3296 if (port.isOutput())
3299 auto portResult = oldInstance.getResult(portIndex);
3300 assert(portResult &&
"invalid IR, couldn't find port");
3304 if (port.isInput()) {
3305 operands.push_back(createBackedge(portResult, portType));
3311 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3312 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3314 auto loweredResult = getPossiblyInoutLoweredValue(source);
3315 operands.push_back(loweredResult);
3316 (void)setLowering(portResult, loweredResult);
3325 portType,
"." + port.getName().str() +
".wire");
3329 (void)setLowering(portResult, wire);
3331 operands.push_back(wire);
3338 auto innerSym = oldInstance.getInnerSymAttr();
3339 if (oldInstance.getLowerToBind()) {
3342 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3343 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3345 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3346 innerSym.getSymName());
3349 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3350 bindOp->setAttr(
"output_file", outputFile);
3353 circuitState.addBind(bindOp);
3357 auto newInstance = builder.create<hw::InstanceOp>(
3358 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3360 if (oldInstance.getLowerToBind())
3361 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3363 if (newInstance.getInnerSymAttr())
3364 if (
auto forceName = circuitState.instanceForceNames.lookup(
3365 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3366 newInstance.getInnerNameAttr()}))
3367 newInstance->setAttr(
"hw.verilogName", forceName);
3371 unsigned resultNo = 0;
3372 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3373 auto &port = portInfo[portIndex];
3377 Value resultVal = newInstance.getResult(resultNo);
3379 auto oldPortResult = oldInstance.getResult(portIndex);
3380 (void)setLowering(oldPortResult, resultVal);
3391 LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3392 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3397 return setLowering(op->getResult(0), operand);
3400 LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3401 if (isa<ClockType>(op.getInput().getType()))
3402 return setLowering(op->getResult(0),
3403 getLoweredNonClockValue(op.getInput()));
3404 return lowerNoopCast(op);
3407 LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3408 if (isa<ClockType>(op.getInput().getType()))
3409 return setLowering(op->getResult(0),
3410 getLoweredNonClockValue(op.getInput()));
3411 return lowerNoopCast(op);
3414 LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3415 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3418 LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3419 mlir::UnrealizedConversionCastOp op) {
3421 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3424 auto operand = op.getOperand(0);
3425 auto result = op.getResult(0);
3428 if (type_isa<FIRRTLType>(operand.getType()) &&
3429 type_isa<FIRRTLType>(result.getType()))
3430 return lowerNoopCast(op);
3434 if (!type_isa<FIRRTLType>(operand.getType())) {
3435 if (type_isa<FIRRTLType>(result.getType()))
3436 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3442 auto loweredResult = getLoweredValue(operand);
3443 if (!loweredResult) {
3446 if (operand.getType().isSignlessInteger(0)) {
3447 return setLowering(result, Value());
3454 result.replaceAllUsesWith(loweredResult);
3458 LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3461 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3462 return setLowering(op, op.getOperand());
3466 auto result = getLoweredValue(op.getOperand());
3472 op.replaceAllUsesWith(result);
3476 LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3477 auto operand = getLoweredValue(op.getOperand());
3480 auto resultType =
lowerType(op.getType());
3484 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3487 LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3488 auto operand = getLoweredValue(op.getOperand());
3492 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3493 return setLowering(op, getOrCreateIntConstant(1, 0));
3495 return setLowering(op, Value());
3500 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3501 return setLowering(op, operand);
3504 auto zero = getOrCreateIntConstant(1, 0);
3505 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3508 LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3509 auto operand = getLoweredValue(op.getInput());
3513 auto allOnes = getOrCreateIntConstant(
3514 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3515 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3518 LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3521 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3525 auto resultType =
lowerType(op.getType());
3527 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3528 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3532 LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3533 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3536 return setLowering(op, operand);
3539 LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3540 auto operand = getLoweredValue(op.getInput());
3543 return setLowering(op, getOrCreateIntConstant(1, 0));
3548 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3552 LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3553 auto operand = getLoweredValue(op.getInput());
3556 return setLowering(op, getOrCreateIntConstant(1, 1));
3561 return setLoweringTo<comb::ICmpOp>(
3562 op, ICmpPredicate::eq, operand,
3563 getOrCreateIntConstant(
3564 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3568 LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3569 auto operand = getLoweredValue(op.getInput());
3572 return setLowering(op, getOrCreateIntConstant(1, 0));
3578 return setLoweringTo<comb::ICmpOp>(
3579 op, ICmpPredicate::ne, operand,
3580 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3588 template <
typename ResultOpType>
3589 LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3590 auto resultType = op->getResult(0).getType();
3591 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3592 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3596 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3602 template <
typename ResultOpType>
3603 LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3604 auto resultType = op->getResult(0).getType();
3605 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3606 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3617 auto intType = builder.getIntegerType(*bitwidth);
3618 auto retType = lhs.getType();
3621 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3622 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3627 template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3628 LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3630 auto resultType = op->getResult(0).getType();
3631 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3632 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3637 if (type_cast<IntType>(resultType).isSigned())
3638 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3639 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3644 LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3645 ICmpPredicate unsignedOp) {
3647 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3648 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3649 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3653 if (cmpType.getWidth() == 0)
3655 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3656 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3661 Type resultType = builder.getIntegerType(1);
3662 return setLoweringTo<comb::ICmpOp>(
3663 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3669 template <
typename SignedOp,
typename Un
signedOp>
3670 LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3674 auto opType = type_cast<IntType>(op->getResult(0).getType());
3675 if (opType.getWidth() == 0)
3676 return setLowering(op->getResult(0), Value());
3680 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3681 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3686 if (opType.isSigned())
3687 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
3689 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
3691 if (
auto *definingOp = result.getDefiningOp())
3694 if (resultType == opType)
3695 return setLowering(op->getResult(0), result);
3696 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
3699 LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3700 auto lhs = getLoweredValue(op.getLhs());
3701 auto rhs = getLoweredValue(op.getRhs());
3705 return setLowering(op, rhs);
3707 return handleZeroBit(op.getRhs(),
3708 [&]() { return setLowering(op, Value()); });
3713 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3715 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3722 LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3723 auto input = getLoweredNonClockValue(op.getArg());
3727 if (!isa<IntType>(input.getType())) {
3728 auto srcType = op.getArg().getType();
3730 assert(bitwidth &&
"Unknown width");
3731 auto intType = builder.getIntegerType(*bitwidth);
3732 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
3735 return setLoweringTo<comb::ICmpOp>(
3736 op, ICmpPredicate::ceq, input,
3737 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
3740 LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
3741 auto operand = getLoweredValue(op.getInput());
3742 builder.create<hw::WireOp>(operand);
3746 LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3747 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
3748 op.getFormatStringAttr());
3751 LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3752 auto type =
lowerType(op.getResult().getType());
3756 auto valueOp = builder.create<sim::PlusArgsValueOp>(
3757 builder.getIntegerType(1), type, op.getFormatStringAttr());
3758 if (failed(setLowering(op.getResult(), valueOp.getResult())))
3760 if (failed(setLowering(op.getFound(), valueOp.getFound())))
3765 LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3766 op.emitError(
"SizeOf should have been resolved.");
3770 LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3772 if (op.getTestEnable())
3773 testEnable = getLoweredValue(op.getTestEnable());
3774 return setLoweringTo<seq::ClockGateOp>(
3775 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3776 testEnable, hw::InnerSymAttr{});
3779 LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
3780 auto operand = getLoweredValue(op.getInput());
3781 return setLoweringTo<seq::ClockInverterOp>(op, operand);
3784 LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
3785 auto operand = getLoweredValue(op.getInput());
3786 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
3789 LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3790 return setLoweringToLTL<ltl::AndOp>(
3792 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3795 LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3796 return setLoweringToLTL<ltl::OrOp>(
3798 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3801 LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
3802 return setLoweringToLTL<ltl::IntersectOp>(
3804 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3807 LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3808 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3809 op.getDelayAttr(), op.getLengthAttr());
3812 LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3813 return setLoweringToLTL<ltl::ConcatOp>(
3815 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3818 LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
3819 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
3820 op.getBaseAttr(), op.getMoreAttr());
3823 LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
3824 return setLoweringToLTL<ltl::GoToRepeatOp>(
3825 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3828 LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
3829 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
3830 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3833 LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3834 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3837 LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3838 return setLoweringToLTL<ltl::ImplicationOp>(
3840 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3843 LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
3844 return setLoweringToLTL<ltl::UntilOp>(
3846 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3849 LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3850 return setLoweringToLTL<ltl::EventuallyOp>(op,
3851 getLoweredValue(op.getInput()));
3854 LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3855 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3856 ltl::ClockEdge::Pos,
3857 getLoweredNonClockValue(op.getClock()));
3860 template <
typename TargetOp,
typename IntrinsicOp>
3861 LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
3862 auto property = getLoweredValue(op.getProperty());
3863 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
3864 builder.create<TargetOp>(property, enable, op.getLabelAttr());
3868 LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3869 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3872 LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3873 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
3876 LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3877 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
3880 LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3881 auto clock = getLoweredNonClockValue(op.getClock());
3882 auto reset = getLoweredValue(op.getReset());
3883 if (!clock || !reset)
3885 auto resetType = op.getReset().getType();
3886 auto uintResetType = dyn_cast<UIntType>(resetType);
3887 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3888 auto isAsync = isa<AsyncResetType>(resetType);
3889 if (!isAsync && !isSync) {
3890 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
3891 "requires sync or async reset");
3892 d.attachNote() <<
"reset is of type " << resetType
3893 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3896 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3903 LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3904 auto input = getLoweredValue(op.getInput());
3908 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
3909 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3912 LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3913 auto resultTy =
lowerType(op.getType());
3920 if (type_isa<AnalogType>(op.getType()))
3923 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
3926 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
3937 auto constant = getOrCreateIntConstant(*bitwidth, 0);
3939 if (!type_isa<IntegerType>(resultTy))
3940 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
3941 return setLowering(op, constant);
3945 op.emitOpError(
"unsupported type");
3949 LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
3950 auto input = getLoweredValue(op.getInput());
3953 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3954 if (op.getAmount() == 0)
3955 return setLowering(op, Value());
3956 Type resultType = builder.getIntegerType(op.getAmount());
3957 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
3958 inWidth - op.getAmount());
3961 LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
3962 auto input = getLoweredValue(op.getInput());
3965 if (op.getAmount() == 0)
3967 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
3972 if (op.getAmount() == 0)
3973 return setLowering(op, input);
3975 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
3976 return setLoweringTo<comb::ConcatOp>(op, input, zero);
3979 LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
3980 auto input = getLoweredValue(op.getInput());
3985 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3986 auto shiftAmount = op.getAmount();
3987 if (shiftAmount >= inWidth) {
3989 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
3990 return setLowering(op, {});
3993 shiftAmount = inWidth - 1;
3996 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
3997 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4000 LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4001 auto input = getLoweredValue(op.getInput());
4005 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4006 if (inWidth == op.getAmount())
4007 return setLowering(op, Value());
4008 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4009 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4012 LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4013 auto cond = getLoweredValue(op.getSel());
4014 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4015 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4016 if (!cond || !ifTrue || !ifFalse)
4019 if (isa<ClockType>(op.getType()))
4020 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4021 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4025 LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4026 auto cond = getLoweredValue(op.getSel());
4027 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4028 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4029 if (!cond || !ifTrue || !ifFalse)
4032 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4034 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4037 LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4038 auto sel = getLoweredValue(op.getSel());
4039 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4040 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4041 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4042 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4043 if (!sel || !v3 || !v2 || !v1 || !v0)
4045 Value array[] = {v3, v2, v1, v0};
4048 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4067 Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4068 assert(op->getNumResults() == 1 &&
"only expect a single result");
4069 auto val = op->getResult(0);
4080 OpBuilder::InsertionGuard guard(builder);
4081 builder.setInsertionPoint(op);
4082 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4083 for (
auto [idx, operand] : llvm::enumerate(op->getOperands())) {
4085 op->getContext(),
nullptr, 0,
4086 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4088 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4089 op->setOperand(idx, wire);
4093 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4096 "synopsys infer_mux_override",
4101 Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4103 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4108 if (!llvm::isPowerOf2_64(size)) {
4109 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4111 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4113 Value temp2[] = {ext.getResult(), array};
4119 return inBoundsRead;
4122 LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4124 auto index = getLoweredAndExtOrTruncValue(
4131 SmallVector<Value> loweredInputs;
4132 loweredInputs.reserve(op.getInputs().size());
4133 for (
auto input : op.getInputs()) {
4134 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4137 loweredInputs.push_back(lowered);
4141 return setLowering(op, createArrayIndexing(array, index));
4144 LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4145 auto resultTy =
lowerType(op.getType());
4149 SmallVector<Value, 4> operands;
4150 operands.reserve(op.getSubstitutions().size());
4151 for (
auto operand : op.getSubstitutions()) {
4152 auto lowered = getLoweredValue(operand);
4155 operands.push_back(lowered);
4158 ArrayAttr symbols = op.getSymbolsAttr();
4162 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4166 LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4170 Type baseType = op.getType().getType();
4173 if (isa<ClockType>(baseType))
4174 xmrType = builder.getIntegerType(1);
4179 op.getRef(), op.getVerbatimSuffixAttr());
4182 LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4186 if (isa<ClockType>(op.getType()))
4187 xmrType = builder.getIntegerType(1);
4191 auto xmr = builder.create<sv::XMRRefOp>(
4193 auto readXmr = getReadValue(xmr);
4194 if (!isa<ClockType>(op.getType()))
4195 return setLowering(op, readXmr);
4196 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4203 LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4215 FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4216 auto srcType = srcVal.getType();
4217 auto dstType = destVal.getType();
4218 if (srcType != dstType &&
4219 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4220 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4222 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4223 .Case<hw::WireOp>([&](
auto op) {
4224 maybeUnused(op.getInput());
4225 op.getInputMutable().assign(srcVal);
4228 .Case<seq::FirRegOp>([&](
auto op) {
4229 maybeUnused(op.getNext());
4230 op.getNextMutable().assign(srcVal);
4233 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4236 op.emitOpError(
"used as connect destination");
4239 .Default([](
auto) {
return false; });
4242 LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4243 auto dest = op.getDest();
4245 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4246 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4248 return handleZeroBit(op.getSrc(), []() { return success(); });
4250 auto destVal = getPossiblyInoutLoweredValue(dest);
4254 auto result = lowerConnect(destVal, srcVal);
4262 if (updateIfBackedge(destVal, srcVal))
4265 if (!isa<hw::InOutType>(destVal.getType()))
4266 return op.emitError(
"destination isn't an inout type");
4272 LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4273 auto dest = op.getDest();
4274 auto srcVal = getLoweredValue(op.getSrc());
4276 return handleZeroBit(op.getSrc(), []() { return success(); });
4278 auto destVal = getPossiblyInoutLoweredValue(dest);
4282 auto result = lowerConnect(destVal, srcVal);
4290 if (updateIfBackedge(destVal, srcVal))
4293 if (!isa<hw::InOutType>(destVal.getType()))
4294 return op.emitError(
"destination isn't an inout type");
4300 LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4301 auto srcVal = getLoweredValue(op.getSrc());
4305 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4309 if (!isa<hw::InOutType>(destVal.getType()))
4310 return op.emitError(
"destination isn't an inout type");
4313 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4314 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4315 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4320 LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4321 auto src = getLoweredNonClockValue(op.getSrc());
4322 auto clock = getLoweredNonClockValue(op.getClock());
4323 auto pred = getLoweredValue(op.getPredicate());
4324 if (!src || !clock || !pred)
4327 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4332 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4333 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4334 addToAlwaysBlock(clock, [&]() {
4335 addIfProceduralBlock(
4336 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4341 LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4342 auto src = getLoweredNonClockValue(op.getSrc());
4343 auto pred = getLoweredValue(op.getPredicate());
4347 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4352 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4353 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4354 addToInitialBlock([&]() {
4355 addIfProceduralBlock(
4356 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4361 LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4362 auto clock = getLoweredNonClockValue(op.getClock());
4363 auto pred = getLoweredValue(op.getPredicate());
4364 if (!clock || !pred)
4367 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4372 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4373 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4374 addToAlwaysBlock(clock, [&]() {
4375 addIfProceduralBlock(pred,
4376 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4381 LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4382 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4383 auto pred = getLoweredValue(op.getPredicate());
4384 if (!destVal || !pred)
4388 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4389 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4390 addToInitialBlock([&]() {
4391 addIfProceduralBlock(pred,
4392 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4400 LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4401 auto clock = getLoweredNonClockValue(op.getClock());
4402 auto cond = getLoweredValue(op.getCond());
4403 if (!clock || !cond)
4406 SmallVector<Value, 4> operands;
4407 operands.reserve(op.getSubstitutions().size());
4408 for (
auto operand : op.getSubstitutions()) {
4409 Value loweredValue = getLoweredFmtOperand(operand);
4412 operands.push_back(loweredValue);
4416 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4417 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4418 addToAlwaysBlock(clock, [&]() {
4419 circuitState.usedPrintfCond =
true;
4420 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
4424 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
4425 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
4427 addIfProceduralBlock(ifCond, [&]() {
4429 Value fdStderr = builder.create<
hw::ConstantOp>(APInt(32, 0x80000002));
4430 builder.
create<sv::FWriteOp>(fdStderr, op.getFormatString(), operands);
4440 LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4441 auto clock = getLoweredValue(op.getClock());
4442 auto cond = getLoweredValue(op.getCond());
4443 if (!clock || !cond)
4446 circuitState.usedStopCond =
true;
4447 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4450 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4451 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4453 if (op.getExitCode())
4454 builder.create<sim::FatalOp>(clock, exitCond);
4456 builder.create<sim::FinishOp>(clock, exitCond);
4464 template <
typename... Args>
4466 StringRef opName, Args &&...args) {
4467 if (opName ==
"assert")
4468 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4469 if (opName ==
"assume")
4470 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4471 if (opName ==
"cover")
4472 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4473 llvm_unreachable(
"unknown verification op");
4479 template <
typename... Args>
4481 StringRef opName, Args &&...args) {
4482 if (opName ==
"assert")
4483 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4484 if (opName ==
"assume")
4485 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4486 if (opName ==
"cover")
4487 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4488 llvm_unreachable(
"unknown verification op");
4509 LogicalResult FIRRTLLowering::lowerVerificationStatement(
4510 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4511 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4512 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
4513 StringRef opName = op->getName().stripDialect();
4516 ArrayRef<Attribute> guards{};
4517 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
4518 guards = guardsAttr.getValue();
4520 auto isCover = isa<CoverOp>(op);
4521 auto clock = getLoweredNonClockValue(opClock);
4522 auto enable = getLoweredValue(opEnable);
4523 auto predicate = getLoweredValue(opPredicate);
4524 if (!clock || !enable || !predicate)
4528 if (opNameAttr && !opNameAttr.getValue().empty())
4530 StringAttr prefixedLabel;
4533 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
4536 SmallVector<Value> messageOps;
4540 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
4541 flavor = VerificationFlavor::None;
4543 if (flavor == VerificationFlavor::None) {
4547 auto format = op->getAttrOfType<StringAttr>(
"format");
4549 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
4550 if (!isa<AssertOp>(op))
4551 return op->emitError()
4552 <<
"ifElseFatal format cannot be used for non-assertions";
4553 flavor = VerificationFlavor::IfElseFatal;
4554 }
else if (isConcurrent)
4555 flavor = VerificationFlavor::SVA;
4557 flavor = VerificationFlavor::Immediate;
4560 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4561 message = opMessageAttr;
4562 for (
auto operand : opOperands) {
4563 auto loweredValue = getLoweredFmtOperand(operand);
4571 if (flavor == VerificationFlavor::SVA)
4572 loweredValue = builder.create<sv::SampledOp>(loweredValue);
4573 messageOps.push_back(loweredValue);
4579 case VerificationFlavor::Immediate: {
4582 builder.getContext(), circt::sv::DeferAssert::Immediate);
4583 addToAlwaysBlock(clock, [&]() {
4584 addIfProceduralBlock(enable, [&]() {
4586 prefixedLabel, message, messageOps);
4591 case VerificationFlavor::IfElseFatal: {
4592 assert(isa<AssertOp>(op) &&
"only assert is expected");
4597 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4599 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4600 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
4601 addToAlwaysBlock(clock, [&]() {
4602 addIfProceduralBlock(predicate, [&]() {
4603 circuitState.usedStopCond =
true;
4604 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4606 circuitState.usedAssertVerboseCond =
true;
4607 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
4609 addIfProceduralBlock(
4610 builder.create<sv::MacroRefExprOp>(boolType,
4611 "ASSERT_VERBOSE_COND_"),
4612 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4613 addIfProceduralBlock(
4614 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
4615 [&]() { builder.create<sv::FatalOp>(); });
4621 case VerificationFlavor::SVA: {
4628 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4630 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4634 sv::EventControl event;
4635 switch (opEventControl) {
4636 case EventControl::AtPosEdge:
4637 event = circt::sv::EventControl::AtPosEdge;
4639 case EventControl::AtEdge:
4640 event = circt::sv::EventControl::AtEdge;
4642 case EventControl::AtNegEdge:
4643 event = circt::sv::EventControl::AtNegEdge;
4650 predicate, prefixedLabel, message, messageOps);
4653 case VerificationFlavor::None:
4655 "flavor `None` must be converted into one of concreate flavors");
4662 return emitGuards(op->getLoc(), guards,
emit);
4666 LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4667 return lowerVerificationStatement(
4668 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4669 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4670 op.getIsConcurrent(), op.getEventControl());
4674 LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4675 return lowerVerificationStatement(
4676 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4677 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4678 op.getIsConcurrent(), op.getEventControl());
4682 LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4683 return lowerVerificationStatement(
4684 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4685 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4686 op.getIsConcurrent(), op.getEventControl());
4690 LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
4695 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
4696 ArrayRef<Attribute> guards =
4697 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
4699 auto label = op.getNameAttr();
4700 StringAttr assumeLabel;
4701 if (label && !label.empty())
4704 auto predicate = getLoweredValue(op.getPredicate());
4705 auto enable = getLoweredValue(op.getEnable());
4707 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4709 SmallVector<Value> messageOps;
4710 for (
auto operand : op.getSubstitutions()) {
4711 auto loweredValue = getLoweredValue(operand);
4712 if (!loweredValue) {
4716 loweredValue = getOrCreateIntConstant(1, 0);
4718 messageOps.push_back(loweredValue);
4720 return emitGuards(op.getLoc(), guards, [&]() {
4721 builder.create<sv::AlwaysOp>(
4722 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4723 if (op.getMessageAttr().getValue().empty())
4724 buildImmediateVerifOp(
4725 builder,
"assume", predicate,
4726 circt::sv::DeferAssertAttr::get(
4727 builder.getContext(), circt::sv::DeferAssert::Immediate),
4730 buildImmediateVerifOp(
4731 builder,
"assume", predicate,
4732 circt::sv::DeferAssertAttr::get(
4733 builder.getContext(), circt::sv::DeferAssert::Immediate),
4734 assumeLabel, op.getMessageAttr(), messageOps);
4739 LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4741 if (op.getAttached().size() < 2)
4744 SmallVector<Value, 4> inoutValues;
4745 for (
auto v : op.getAttached()) {
4746 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4747 if (!inoutValues.back()) {
4751 inoutValues.pop_back();
4755 if (!isa<hw::InOutType>(inoutValues.back().getType()))
4756 return op.emitError(
"operand isn't an inout type");
4759 if (inoutValues.size() < 2)
4770 bool isAttachInternalOnly =
4771 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
4773 if (isAttachInternalOnly) {
4774 auto v0 = inoutValues.front();
4775 for (
auto v : inoutValues) {
4778 v.replaceAllUsesWith(v0);
4785 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4786 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
4791 SmallVector<Value, 4> values;
4792 for (
auto inoutValue : inoutValues)
4793 values.push_back(getReadValue(inoutValue));
4795 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4796 for (size_t i2 = 0; i2 != e; ++i2)
4798 builder.create<sv::AssignOp>(inoutValues[i1], values[i2]);
4807 builder.create<sv::VerbatimOp>(
4808 "`error \"Verilator does not support alias and thus "
4810 "arbitrarily connect bidirectional wires and ports\"");
4812 [&]() { builder.create<sv::AliasOp>(inoutValues); });
4818 LogicalResult FIRRTLLowering::fixupLTLOps() {
4819 if (ltlOpFixupWorklist.empty())
4821 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
4825 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4826 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
4827 if (isa<hw::WireOp>(user))
4828 ltlOpFixupWorklist.insert(user);
4831 while (!ltlOpFixupWorklist.empty()) {
4832 auto *op = ltlOpFixupWorklist.pop_back_val();
4835 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4836 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
4837 SmallVector<Type, 2> types;
4838 auto result = opIntf.inferReturnTypes(
4839 op->getContext(), op->getLoc(), op->getOperands(),
4840 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4844 assert(types.size() == op->getNumResults());
4848 for (
auto [result, type] : llvm::zip(op->getResults(), types)) {
4849 if (result.getType() == type)
4851 LLVM_DEBUG(llvm::dbgs()
4852 <<
" - Result #" << result.getResultNumber() <<
" from "
4853 << result.getType() <<
" to " << type <<
"\n");
4854 result.setType(type);
4855 for (
auto *user : result.getUsers())
4857 ltlOpFixupWorklist.insert(user);
4862 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
4863 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4864 wireOp.replaceAllUsesWith(wireOp.getInput());
4865 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
4866 if (wireOp.use_empty())
4873 SmallPtrSet<Operation *, 4> usersReported;
4874 for (
auto *user : op->getUsers()) {
4875 if (!usersReported.insert(user).second)
4877 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4879 if (isa<hw::WireOp>(user))
4881 auto d = op->emitError(
4882 "verification operation used in a non-verification context");
4883 d.attachNote(user->getLoc())
4884 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
static unsigned getBitWidthFromVectorSize(unsigned size)
static void moveVerifAnno(ModuleOp top, AnnotationSet &annos, StringRef annoClass, StringRef attrBase)
Move a ExtractTestCode related annotation from annotations to an attribute.
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
A namespace that is used to store existing names and generate new names in some scope within the IR.
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.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
This is an edge in the InstanceGraph.
def create(array_value, idx)
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Value createOrFoldSExt(Location loc, Value value, Type destTy, OpBuilder &builder)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
mlir::Type getPassiveType(mlir::Type anyBaseFIRRTLType)
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
constexpr const char * extractAssumeAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
AnnotationSet annotations
bool isInput() const
Return true if this is a simple input-only port.