13 #include "../PassDetail.h"
37 #include "mlir/IR/BuiltinOps.h"
38 #include "mlir/IR/BuiltinTypes.h"
39 #include "mlir/IR/ImplicitLocOpBuilder.h"
40 #include "mlir/IR/Threading.h"
41 #include "mlir/Pass/Pass.h"
42 #include "llvm/ADT/StringSet.h"
43 #include "llvm/ADT/TinyPtrVector.h"
44 #include "llvm/Support/Debug.h"
45 #include "llvm/Support/FormatVariadic.h"
46 #include "llvm/Support/Mutex.h"
47 #include "llvm/Support/Parallel.h"
49 #define DEBUG_TYPE "lower-to-hw"
51 using namespace circt;
52 using namespace firrtl;
53 using circt::comb::ICmpPredicate;
62 auto ftype = dyn_cast<FIRRTLBaseType>(type);
63 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
70 for (
auto operand : op.getAttached()) {
72 operand.getDefiningOp<InstanceOp>())
76 if (!operand.hasOneUse() || singleSource)
78 singleSource = operand;
87 auto checkTypes = [](Operation *op) -> WalkResult {
89 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
90 return op->emitError(
"Found unhandled FIRRTL operation '")
91 << op->getName() <<
"'";
94 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
95 if (llvm::any_of(types, [](Type type) {
96 return isa<FIRRTLDialect>(type.getDialect());
98 return op->emitOpError(
"found unhandled FIRRTL type");
103 if (failed(checkTypeRange(op->getOperandTypes())) ||
104 failed(checkTypeRange(op->getResultTypes())))
105 return WalkResult::interrupt();
108 for (
auto ®ion : op->getRegions())
109 for (
auto &block : region)
110 if (failed(checkTypeRange(block.getArgumentTypes())))
111 return WalkResult::interrupt();
114 return WalkResult::advance();
117 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
124 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
125 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
131 ImplicitLocOpBuilder &
builder) {
133 if (BundleType bundle = dyn_cast<BundleType>(type))
134 val =
builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
136 if (type != val.getType())
137 val =
builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
145 ImplicitLocOpBuilder &
builder) {
147 if (hw::StructType structTy = type.dyn_cast<hw::StructType>()) {
151 .create<mlir::UnrealizedConversionCastOp>(
152 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
154 val =
builder.createOrFold<HWStructCastOp>(type, val);
159 builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
166 StringRef annoClass, StringRef attrBase) {
168 auto ctx = top.getContext();
171 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
172 SmallVector<NamedAttribute> old;
173 for (
auto i : top->getAttrs())
177 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
180 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
181 SmallVector<NamedAttribute> old;
182 for (
auto i : top->getAttrs())
185 hw::OutputFileAttr::getFromFilename(
186 ctx, file.getValue(),
true));
192 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
198 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
199 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
200 dst->setAttr(
"sv.namehint", attr);
208 struct FIRRTLModuleLowering;
211 struct CircuitLoweringState {
213 std::atomic<bool> usedPrintfCond{
false};
214 std::atomic<bool> usedAssertVerboseCond{
false};
215 std::atomic<bool> usedStopCond{
false};
217 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
220 : circuitOp(circuitOp), instanceGraph(instanceGraph),
221 enableAnnotationWarning(enableAnnotationWarning),
222 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
223 auto *context = circuitOp.getContext();
228 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
229 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
230 context, dirName.getValue(),
false,
true);
233 for (
auto &op : *circuitOp.getBodyBlock()) {
234 if (
auto module = dyn_cast<FModuleLike>(op))
245 testHarness =
nullptr;
246 }
else if (dut == testHarness) {
247 testHarness =
nullptr;
252 auto inDUT = [&](igraph::ModuleOpInterface child) {
254 auto inst = instRec->getInstance();
255 if (
auto *finst = dyn_cast<InstanceOp>(&inst))
256 return finst->getLowerToBind();
259 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
260 return getInstanceGraph().isAncestor(child, parent, isBind);
263 circuitOp->walk([&](FModuleLike moduleOp) {
265 dutModules.insert(moduleOp);
269 Operation *getNewModule(Operation *oldModule) {
270 auto it = oldToNewModuleMap.find(oldModule);
271 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
274 Operation *getOldModule(Operation *newModule) {
275 auto it = newToOldModuleMap.find(newModule);
276 return it != newToOldModuleMap.end() ? it->second :
nullptr;
279 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
280 oldToNewModuleMap[oldFMod] = newHWMod;
281 newToOldModuleMap[newHWMod] = oldFMod;
286 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
292 void addBind(sv::BindOp op) {
293 std::lock_guard<std::mutex> lock(bindsMutex);
299 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
302 auto hwAlias = typeAliases.getTypedecl(firAliasType);
305 assert(!typeAliases.isFrozen() &&
306 "type aliases cannot be generated after its frozen");
307 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
310 FModuleLike getDut() {
return dut; }
311 FModuleLike getTestHarness() {
return testHarness; }
317 bool isInDUT(igraph::ModuleOpInterface child) {
318 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
319 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
320 return dutModules.contains(child);
323 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
328 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
335 Type
lowerType(Type type, Location loc) {
337 [&](Type rawType, BaseTypeAliasType firrtlType,
338 Location typeLoc) -> hw::TypeAliasType {
339 return getTypeAlias(rawType, firrtlType, typeLoc);
344 friend struct FIRRTLModuleLowering;
345 friend struct FIRRTLLowering;
346 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
347 void operator=(
const CircuitLoweringState &) =
delete;
350 DenseMap<Operation *, Operation *> oldToNewModuleMap;
353 DenseMap<Operation *, Operation *> newToOldModuleMap;
364 DenseSet<igraph::ModuleOpInterface> dutModules;
368 StringSet<> pendingAnnotations;
369 const bool enableAnnotationWarning;
370 std::mutex annotationPrintingMtx;
376 SmallVector<sv::BindOp> binds;
379 std::mutex bindsMutex;
387 FModuleLike testHarness;
390 hw::OutputFileAttr testBenchDirectory;
394 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
397 SetVector<StringAttr> macroDeclNames;
398 std::mutex macroDeclMutex;
400 void addMacroDecl(StringAttr name) {
401 std::unique_lock<std::mutex> lock(macroDeclMutex);
402 macroDeclNames.insert(name);
407 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
408 llvm::sys::SmartMutex<true> fragmentsMutex;
411 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
412 fragments[module].insert(
428 struct RecordTypeAlias {
430 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
432 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
433 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
434 if (iter != firrtlTypeToAliasTypeMap.end())
439 bool isFrozen() {
return frozen; }
441 void freeze() { frozen =
true; }
443 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
445 assert(!frozen &&
"Record already frozen, cannot be updated");
448 auto b = ImplicitLocOpBuilder::atBlockBegin(
450 &circuitOp->getParentRegion()->getBlocks().back());
452 b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
453 typeScope.getBodyRegion().push_back(
new Block());
455 auto typeName = firAlias.getName();
461 typeDeclNamespace.newName(typeName.getValue()));
463 auto typeScopeBuilder =
464 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
465 auto typeDecl = typeScopeBuilder.create<
hw::TypedeclOp>(typeLoc, typeName,
469 {FlatSymbolRefAttr::get(typeDecl)}),
471 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
472 assert(insert.second &&
"Entry already exists, insert failed");
473 return insert.first->second;
482 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
490 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
493 void CircuitLoweringState::processRemainingAnnotations(
495 if (!enableAnnotationWarning || annoSet.
empty())
497 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
499 for (
auto a : annoSet) {
500 auto inserted = pendingAnnotations.insert(a.getClass());
501 if (!inserted.second)
540 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
541 "' still remaining after LowerToHW");
547 struct FIRRTLModuleLowering :
public 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,
559 CircuitLoweringState &loweringState);
560 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
561 CircuitLoweringState &loweringState);
562 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
563 CircuitLoweringState &loweringState);
565 Block *topLevelModule,
566 CircuitLoweringState &loweringState);
568 Block *topLevelModule,
569 CircuitLoweringState &loweringState);
572 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
573 CircuitLoweringState &loweringState);
575 CircuitLoweringState &loweringState);
582 bool enableAnnotationWarning,
584 auto pass = std::make_unique<FIRRTLModuleLowering>();
585 if (enableAnnotationWarning)
586 pass->setEnableAnnotationWarning();
587 pass->verificationFlavor = verificationFlavor;
593 void FIRRTLModuleLowering::runOnOperation() {
597 auto *topLevelModule = getOperation().getBody();
601 for (
auto &op : *topLevelModule) {
602 if ((circuit = dyn_cast<CircuitOp>(&op)))
609 auto *circuitBody = circuit.getBodyBlock();
613 CircuitLoweringState state(circuit, enableAnnotationWarning,
614 verificationFlavor, getAnalysis<InstanceGraph>(),
615 &getAnalysis<NLATable>());
617 SmallVector<hw::HWModuleOp, 32> modulesToProcess;
621 "firrtl.extract.assert");
623 "firrtl.extract.assume");
625 "firrtl.extract.cover");
626 circuitAnno.removeAnnotationsWithClass(
629 state.processRemainingAnnotations(circuit, circuitAnno);
632 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
634 TypeSwitch<Operation *, LogicalResult>(&op)
635 .Case<FModuleOp>([&](
auto module) {
636 auto loweredMod = lowerModule(module, topLevelModule, state);
640 state.recordModuleMapping(&op, loweredMod);
641 modulesToProcess.push_back(loweredMod);
643 module.walk([&](Operation *op) {
644 for (
auto res : op->getResults()) {
646 type_dyn_cast<BaseTypeAliasType>(res.getType()))
647 state.lowerType(aliasType, op->getLoc());
650 return lowerModulePortsAndMoveBody(module, loweredMod, state);
652 .Case<FExtModuleOp>([&](
auto extModule) {
654 lowerExtModule(extModule, topLevelModule, state);
657 state.recordModuleMapping(&op, loweredMod);
660 .Case<FMemModuleOp>([&](
auto memModule) {
662 lowerMemModule(memModule, topLevelModule, state);
665 state.recordModuleMapping(&op, loweredMod);
668 .Default([&](Operation *op) {
673 op->moveBefore(topLevelModule, topLevelModule->end());
679 return signalPassFailure();
682 state.typeAliases.freeze();
687 SmallVector<Attribute> dutHierarchyFiles;
688 SmallVector<Attribute> testHarnessHierarchyFiles;
689 circuitAnno.removeAnnotations([&](
Annotation annotation) {
691 auto file = hw::OutputFileAttr::getFromFilename(
693 annotation.
getMember<StringAttr>(
"filename").getValue(),
695 dutHierarchyFiles.push_back(file);
699 auto file = hw::OutputFileAttr::getFromFilename(
701 annotation.
getMember<StringAttr>(
"filename").getValue(),
705 if (state.getTestHarness())
706 testHarnessHierarchyFiles.push_back(file);
708 dutHierarchyFiles.push_back(file);
714 if (!dutHierarchyFiles.empty())
715 state.getNewModule(state.getDut())
718 if (!testHarnessHierarchyFiles.empty())
719 state.getNewModule(state.getTestHarness())
724 auto result = mlir::failableParallelForEachN(
725 &getContext(), 0, modulesToProcess.size(), [&](
auto index) {
726 return lowerModuleOperations(modulesToProcess[index], state);
731 return signalPassFailure();
734 for (
auto bind : state.binds) {
739 for (
auto &[module, fragments] : state.fragments)
744 for (
auto oldNew : state.oldToNewModuleMap)
745 oldNew.first->erase();
747 if (!state.macroDeclNames.empty()) {
749 for (
auto name : state.macroDeclNames) {
750 b.create<sv::MacroDeclOp>(name);
755 lowerFileHeader(circuit, state);
762 void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
763 CircuitLoweringState &state) {
770 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
771 StringRef defineTrue =
"",
772 StringRef defineFalse = StringRef()) {
773 if (!defineFalse.data()) {
774 assert(defineTrue.data() &&
"didn't define anything");
776 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
781 if (defineTrue.data())
782 b.create<sv::MacroDefOp>(defName, defineTrue);
784 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
789 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
791 guard, []() {}, body);
794 if (state.usedPrintfCond) {
795 b.create<sv::MacroDeclOp>(
"PRINTF_COND");
796 b.create<sv::MacroDeclOp>(
"PRINTF_COND_");
797 b.create<emit::FragmentOp>(
"PRINTF_COND_FRAGMENT", [&] {
798 b.create<sv::VerbatimOp>(
799 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
801 emitGuard(
"PRINTF_COND_", [&]() {
802 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
807 if (state.usedAssertVerboseCond) {
808 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND");
809 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND_");
810 b.create<emit::FragmentOp>(
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
811 b.create<sv::VerbatimOp>(
812 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
813 "gate to assert error printing.");
814 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
815 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
816 "(`ASSERT_VERBOSE_COND)",
"1");
821 if (state.usedStopCond) {
822 b.create<sv::MacroDeclOp>(
"STOP_COND");
823 b.create<sv::MacroDeclOp>(
"STOP_COND_");
824 b.create<emit::FragmentOp>(
"STOP_COND_FRAGMENT", [&] {
825 b.create<sv::VerbatimOp>(
826 "\n// Users can define 'STOP_COND' to add an extra gate "
827 "to stop conditions.");
828 emitGuard(
"STOP_COND_", [&]() {
829 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
836 FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
837 SmallVectorImpl<hw::PortInfo> &ports,
838 Operation *moduleOp, StringRef moduleName,
839 CircuitLoweringState &loweringState) {
840 ports.reserve(firrtlPorts.size());
842 size_t numResults = 0;
843 for (
auto e : llvm::enumerate(firrtlPorts)) {
845 size_t portNo = e.index();
848 hwPort.type = loweringState.lowerType(firrtlPort.
type, firrtlPort.
loc);
850 if (firrtlPort.
sym.size() > 1 ||
851 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
852 return emitError(firrtlPort.
loc)
853 <<
"cannot lower aggregate port " << firrtlPort.
name
854 <<
" with field sensitive symbols, HW dialect does not support "
855 "per field symbols yet.";
856 hwPort.setSym(firrtlPort.
sym, moduleOp->getContext());
858 if (hadDontTouch && !hwPort.getSym()) {
859 if (hwPort.type.isInteger(0)) {
860 if (enableAnnotationWarning) {
861 mlir::emitWarning(firrtlPort.
loc)
862 <<
"zero width port " << hwPort.name
863 <<
" has dontTouch annotation, removing anyway";
870 moduleOp->getContext(),
871 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
872 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
873 moduleOp->getContext());
878 moduleOp->emitError(
"cannot lower this port type to HW");
884 if (hwPort.type.isInteger(0)) {
885 auto sym = hwPort.getSym();
886 if (sym && !sym.empty()) {
887 return mlir::emitError(firrtlPort.
loc)
888 <<
"zero width port " << hwPort.name
889 <<
" is referenced by name [" << sym
890 <<
"] (e.g. in an XMR) but must be removed";
898 hwPort.argNum = numResults++;
899 }
else if (firrtlPort.
isInput()) {
901 hwPort.argNum = numArgs++;
907 hwPort.argNum = numArgs++;
909 hwPort.loc = firrtlPort.
loc;
910 ports.push_back(hwPort);
911 loweringState.processRemainingAnnotations(moduleOp, firrtlPort.
annotations);
920 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
921 return cast<ParamDeclAttr>(a);
931 SmallVector<Attribute> newParams;
932 for (
const ParamDeclAttr &entry : params) {
933 auto name = entry.getName();
934 auto type = entry.getType();
935 auto value = ignoreValues ? Attribute() : entry.getValue();
938 newParams.push_back(paramAttr);
940 return builder.getArrayAttr(newParams);
943 bool FIRRTLModuleLowering::handleForceNameAnnos(
945 CircuitLoweringState &loweringState) {
952 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
959 auto diag = oldModule.emitOpError()
961 <<
"' that is not a non-local annotation";
962 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
968 auto nla = loweringState.nlaTable->getNLA(sym.getAttr());
973 auto diag = oldModule.emitOpError()
975 <<
"' whose non-local symbol, '" << sym
976 <<
"' does not exist in the circuit";
977 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
990 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
991 auto inserted = loweringState.instanceForceNames.insert(
992 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
993 if (!inserted.second &&
994 (anno.
getMember(
"name") != (inserted.first->second))) {
995 auto diag = oldModule.emitError()
997 <<
"' with different names: " << inserted.first->second
998 <<
" was not " << anno.
getMember(
"name");
999 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1010 FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1011 Block *topLevelModule,
1012 CircuitLoweringState &loweringState) {
1014 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1015 SmallVector<hw::PortInfo, 8> ports;
1016 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1020 StringRef verilogName;
1021 if (
auto defName = oldModule.getDefname())
1022 verilogName = defName.value();
1025 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1026 auto nameAttr =
builder.getStringAttr(oldModule.getName());
1032 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1033 SymbolTable::setSymbolVisibility(newModule,
1034 SymbolTable::getSymbolVisibility(oldModule));
1036 bool hasOutputPort =
1037 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1038 if (!hasOutputPort &&
1040 loweringState.isInDUT(oldModule))
1041 newModule->setAttr(
"firrtl.extract.cover.extra",
builder.getUnitAttr());
1044 if (handleForceNameAnnos(oldModule, annos, loweringState))
1047 loweringState.processRemainingAnnotations(oldModule, annos);
1052 FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1053 Block *topLevelModule,
1054 CircuitLoweringState &loweringState) {
1056 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1057 SmallVector<hw::PortInfo, 8> ports;
1058 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1063 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1065 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1066 oldModule.getModuleNameAttr());
1067 loweringState.processRemainingAnnotations(oldModule,
1074 FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1075 CircuitLoweringState &loweringState) {
1077 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1078 SmallVector<hw::PortInfo, 8> ports;
1079 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1084 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1085 auto nameAttr =
builder.getStringAttr(oldModule.getName());
1089 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1090 newModule.setCommentAttr(comment);
1093 SmallVector<StringRef, 12> attrNames = {
1094 "annotations",
"convention",
"layers",
1095 "portNames",
"sym_name",
"portDirections",
1096 "portTypes",
"portAnnotations",
"portSyms",
1097 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName()};
1099 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1100 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1102 llvm::make_filter_range(oldModule->getAttrs(), [&](
auto namedAttr) {
1103 return !attrSet.count(namedAttr.getName()) &&
1104 !newModule->getAttrDictionary().contains(namedAttr.getName());
1106 newAttrs.push_back(i);
1108 newModule->setAttrs(newAttrs);
1112 SymbolTable::setSymbolVisibility(newModule,
1113 SymbolTable::getSymbolVisibility(oldModule));
1119 newModule->setAttr(
"firrtl.extract.cover.extra",
builder.getUnitAttr());
1123 if (
auto testBenchDir = loweringState.getTestBenchDirectory())
1124 if (loweringState.isInTestHarness(oldModule)) {
1125 if (!newModule->hasAttr(
"output_file"))
1126 newModule->setAttr(
"output_file", testBenchDir);
1127 newModule->setAttr(
"firrtl.extract.do_not_extract",
1129 newModule.setCommentAttr(
1130 builder.getStringAttr(
"VCS coverage exclude_file"));
1133 if (handleForceNameAnnos(oldModule, annos, loweringState))
1136 loweringState.processRemainingAnnotations(oldModule, annos);
1145 Operation *insertPoint) {
1146 if (!value.hasOneUse())
1149 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1150 if (!attach || attach.getNumOperands() != 2)
1154 auto loweredType =
lowerType(value.getType());
1155 if (loweredType.isInteger(0))
1160 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1161 auto *op = attachedValue.getDefiningOp();
1162 if (op && op->getBlock() == insertPoint->getBlock() &&
1163 !op->isBeforeInBlock(insertPoint))
1168 ImplicitLocOpBuilder
builder(insertPoint->getLoc(), insertPoint);
1182 CircuitLoweringState &loweringState) {
1184 if (type_isa<AnalogType>(flipValue.getType()))
1187 Operation *connectOp =
nullptr;
1188 for (
auto &use : flipValue.getUses()) {
1191 if (use.getOperandNumber() != 0)
1193 if (!isa<ConnectOp, StrictConnectOp>(use.getOwner()))
1199 connectOp = use.getOwner();
1209 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1210 if (loweredType.isInteger(0))
1215 ImplicitLocOpBuilder
builder(insertPoint->getLoc(), insertPoint);
1217 auto connectSrc = connectOp->getOperand(1);
1220 if (!connectSrc.getType().isa<
FIRRTLType>()) {
1226 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1228 .create<mlir::UnrealizedConversionCastOp>(
1229 type_cast<FIRRTLBaseType>(connectSrc.getType())
1236 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1238 if (destTy != connectSrc.getType() &&
1239 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1240 isa<BaseTypeAliasType>(destTy))) {
1242 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1244 if (!destTy.isGround()) {
1246 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1248 }
else if (destTy.getBitWidthOrSentinel() !=
1249 type_cast<FIRRTLBaseType>(connectSrc.getType())
1250 .getBitWidthOrSentinel()) {
1253 auto destWidth = destTy.getBitWidthOrSentinel();
1254 assert(destWidth != -1 &&
"must know integer widths");
1255 connectSrc =
builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1267 SmallVector<SubfieldOp> accesses;
1268 for (
auto *op : structValue.getUsers()) {
1269 assert(isa<SubfieldOp>(op));
1270 auto fieldAccess = cast<SubfieldOp>(op);
1272 fieldAccess.getInput().getType().base().getElementIndex(field);
1273 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1274 accesses.push_back(fieldAccess);
1282 LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1284 CircuitLoweringState &loweringState) {
1285 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1292 bodyBuilder.setInsertionPoint(cursor);
1295 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1296 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1297 "port count mismatch");
1299 SmallVector<Value, 4>
outputs;
1302 auto outputOp = newModule.getBodyBlock()->getTerminator();
1303 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1305 unsigned nextHWInputArg = 0;
1306 int hwPortIndex = -1;
1307 for (
auto [firrtlPortIndex, port] : llvm::enumerate(firrtlPorts)) {
1309 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1312 type_isa<FIRRTLBaseType>(port.type) &&
1313 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1317 if (!port.isOutput() && !isZeroWidth) {
1320 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1326 oldArg.replaceAllUsesWith(newArg);
1332 if (isZeroWidth && port.isInput()) {
1333 Value newArg = bodyBuilder
1334 .create<WireOp>(port.type,
"." + port.getName().str() +
1337 oldArg.replaceAllUsesWith(newArg);
1346 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1352 auto newArg = bodyBuilder.create<WireOp>(
1353 port.type,
"." + port.getName().str() +
".output");
1356 oldArg.replaceAllUsesWith(newArg.getResult());
1359 auto resultHWType = loweringState.lowerType(port.type, port.loc);
1360 if (!resultHWType.isInteger(0)) {
1366 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1367 newArg.setInnerSymAttr(sym);
1368 newModule.setPortSymbolAttr(hwPortIndex, {});
1374 outputOp->setOperands(
outputs);
1377 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1378 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1379 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1380 oldBlockInstList.begin(), oldBlockInstList.end());
1394 struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1396 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1397 : theModule(module), circuitState(circuitState),
1398 builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1399 backedgeBuilder(
builder, module.getLoc()) {}
1401 LogicalResult run();
1404 Value getOrCreateClockConstant(seq::ClockConst clock);
1405 Value getOrCreateIntConstant(
const APInt &value);
1406 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1407 bool isSigned =
false) {
1408 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1410 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1411 Value getOrCreateXConstant(
unsigned numBits);
1412 Value getOrCreateZConstant(Type type);
1413 Value getPossiblyInoutLoweredValue(Value value);
1414 Value getLoweredValue(Value value);
1415 Value getLoweredNonClockValue(Value value);
1416 Value getLoweredAndExtendedValue(Value value, Type destType);
1417 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1418 Value getLoweredFmtOperand(Value operand);
1419 LogicalResult setLowering(Value orig, Value result);
1420 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1421 template <
typename ResultOpType,
typename... CtorArgTypes>
1422 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1423 template <
typename ResultOpType,
typename... CtorArgTypes>
1424 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1425 Backedge createBackedge(Location loc, Type type);
1426 Backedge createBackedge(Value orig, Type type);
1427 bool updateIfBackedge(Value dest, Value src);
1430 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1435 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1436 if (forceable.isForceable())
1444 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1445 auto attr = op.getInnerSymAttr();
1449 if (requiresInnerSymbol(op))
1451 op.getContext(), attr, 0,
1452 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1456 void runWithInsertionPointAtEndOfBlock(std::function<
void(
void)> fn,
1460 Value getReadValue(Value v);
1462 Value getNonClockValue(Value v);
1464 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1465 ::ResetType resetStyle, sv::EventControl resetEdge,
1466 Value reset, std::function<
void(
void)> body = {},
1467 std::function<void(
void)> resetBody = {});
1468 void addToAlwaysBlock(Value clock, std::function<
void(
void)> body = {}) {
1469 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, ::ResetType(),
1470 sv::EventControl(), Value(), body,
1471 std::function<
void(
void)>());
1474 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1475 std::function<
void(
void)>
emit);
1476 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1477 std::function<
void(
void)> elseCtor = {});
1478 void addToInitialBlock(std::function<
void(
void)> body);
1479 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1480 std::function<
void(
void)> elseCtor = {});
1481 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1483 bool allowTruncate);
1484 Value createArrayIndexing(Value array, Value index);
1485 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1492 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1493 UnloweredOpResult handleUnloweredOp(Operation *op);
1494 LogicalResult visitExpr(ConstantOp op);
1495 LogicalResult visitExpr(SpecialConstantOp op);
1496 LogicalResult visitExpr(SubindexOp op);
1497 LogicalResult visitExpr(SubaccessOp op);
1498 LogicalResult visitExpr(SubfieldOp op);
1499 LogicalResult visitExpr(VectorCreateOp op);
1500 LogicalResult visitExpr(BundleCreateOp op);
1501 LogicalResult visitExpr(FEnumCreateOp op);
1502 LogicalResult visitExpr(AggregateConstantOp op);
1503 LogicalResult visitExpr(IsTagOp op);
1504 LogicalResult visitExpr(SubtagOp op);
1505 LogicalResult visitUnhandledOp(Operation *op) {
return failure(); }
1506 LogicalResult visitInvalidOp(Operation *op) {
1507 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1508 return visitUnrealizedConversionCast(castOp);
1513 LogicalResult visitDecl(WireOp op);
1514 LogicalResult visitDecl(NodeOp op);
1515 LogicalResult visitDecl(RegOp op);
1516 LogicalResult visitDecl(RegResetOp op);
1517 LogicalResult visitDecl(MemOp op);
1518 LogicalResult visitDecl(InstanceOp op);
1519 LogicalResult visitDecl(VerbatimWireOp op);
1522 LogicalResult lowerNoopCast(Operation *op);
1523 LogicalResult visitExpr(AsSIntPrimOp op);
1524 LogicalResult visitExpr(AsUIntPrimOp op);
1525 LogicalResult visitExpr(AsClockPrimOp op);
1526 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1528 LogicalResult visitExpr(HWStructCastOp op);
1529 LogicalResult visitExpr(BitCastOp op);
1531 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1532 LogicalResult visitExpr(CvtPrimOp op);
1533 LogicalResult visitExpr(NotPrimOp op);
1534 LogicalResult visitExpr(NegPrimOp op);
1535 LogicalResult visitExpr(PadPrimOp op);
1536 LogicalResult visitExpr(XorRPrimOp op);
1537 LogicalResult visitExpr(AndRPrimOp op);
1538 LogicalResult visitExpr(OrRPrimOp op);
1541 template <
typename ResultUnsignedOpType,
1542 typename ResultSignedOpType = ResultUnsignedOpType>
1543 LogicalResult lowerBinOp(Operation *op);
1544 template <
typename ResultOpType>
1545 LogicalResult lowerBinOpToVariadic(Operation *op);
1547 template <
typename ResultOpType>
1548 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1550 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1551 ICmpPredicate unsignedOp);
1552 template <
typename SignedOp,
typename Un
signedOp>
1553 LogicalResult lowerDivLikeOp(Operation *op);
1555 LogicalResult visitExpr(CatPrimOp op);
1557 LogicalResult visitExpr(AndPrimOp op) {
1558 return lowerBinOpToVariadic<comb::AndOp>(op);
1560 LogicalResult visitExpr(OrPrimOp op) {
1561 return lowerBinOpToVariadic<comb::OrOp>(op);
1563 LogicalResult visitExpr(XorPrimOp op) {
1564 return lowerBinOpToVariadic<comb::XorOp>(op);
1566 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1567 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1569 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1570 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1572 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1573 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1575 LogicalResult visitExpr(AddPrimOp op) {
1576 return lowerBinOpToVariadic<comb::AddOp>(op);
1578 LogicalResult visitExpr(EQPrimOp op) {
1579 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1581 LogicalResult visitExpr(NEQPrimOp op) {
1582 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1584 LogicalResult visitExpr(LTPrimOp op) {
1585 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1587 LogicalResult visitExpr(LEQPrimOp op) {
1588 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1590 LogicalResult visitExpr(GTPrimOp op) {
1591 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1593 LogicalResult visitExpr(GEQPrimOp op) {
1594 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1597 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1598 LogicalResult visitExpr(MulPrimOp op) {
1599 return lowerBinOpToVariadic<comb::MulOp>(op);
1601 LogicalResult visitExpr(DivPrimOp op) {
1602 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1604 LogicalResult visitExpr(RemPrimOp op) {
1605 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1609 LogicalResult visitExpr(IsXIntrinsicOp op);
1610 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1611 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1612 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1613 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1614 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1615 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1616 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1617 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1618 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1619 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1620 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1621 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1622 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1623 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1624 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1625 LogicalResult visitExpr(LTLDisableIntrinsicOp op);
1626 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1627 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1628 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1629 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1630 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1633 LogicalResult visitExpr(BitsPrimOp op);
1634 LogicalResult visitExpr(InvalidValueOp op);
1635 LogicalResult visitExpr(HeadPrimOp op);
1636 LogicalResult visitExpr(ShlPrimOp op);
1637 LogicalResult visitExpr(ShrPrimOp op);
1638 LogicalResult visitExpr(DShlPrimOp op) {
1639 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1641 LogicalResult visitExpr(DShrPrimOp op) {
1642 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1644 LogicalResult visitExpr(DShlwPrimOp op) {
1645 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1647 LogicalResult visitExpr(TailPrimOp op);
1648 LogicalResult visitExpr(MuxPrimOp op);
1649 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1650 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1651 LogicalResult visitExpr(MultibitMuxOp op);
1652 LogicalResult visitExpr(VerbatimExprOp op);
1653 LogicalResult visitExpr(XMRRefOp op);
1654 LogicalResult visitExpr(XMRDerefOp op);
1657 LogicalResult lowerVerificationStatement(
1658 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1659 Value enable, StringAttr messageAttr, ValueRange operands,
1660 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1662 LogicalResult visitStmt(SkipOp op);
1665 LogicalResult visitStmt(ConnectOp op);
1666 LogicalResult visitStmt(StrictConnectOp op);
1667 LogicalResult visitStmt(ForceOp op);
1668 LogicalResult visitStmt(PrintFOp op);
1669 LogicalResult visitStmt(StopOp op);
1670 LogicalResult visitStmt(AssertOp op);
1671 LogicalResult visitStmt(AssumeOp op);
1672 LogicalResult visitStmt(CoverOp op);
1673 LogicalResult visitStmt(AttachOp op);
1674 LogicalResult visitStmt(RefForceOp op);
1675 LogicalResult visitStmt(RefForceInitialOp op);
1676 LogicalResult visitStmt(RefReleaseOp op);
1677 LogicalResult visitStmt(RefReleaseInitialOp op);
1683 LogicalResult fixupLTLOps();
1686 return circuitState.lowerType(type,
builder.getLoc());
1694 CircuitLoweringState &circuitState;
1702 DenseMap<Value, Value> valueMapping;
1706 DenseMap<Value, Value> fromClockMapping;
1710 DenseMap<Attribute, Value> hwConstantMap;
1711 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1715 DenseMap<unsigned, Value> hwConstantXMap;
1716 DenseMap<Type, Value> hwConstantZMap;
1722 DenseMap<Value, Value> readInOutCreated;
1726 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1727 ::ResetType, sv::EventControl, Value>;
1735 hw::InnerSymbolNamespace moduleNamespace;
1744 llvm::MapVector<Value, Value> backedges;
1751 DenseSet<Operation *> maybeUnusedValues;
1753 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1754 void maybeUnused(Value value) {
1755 if (
auto *op = value.getDefiningOp())
1767 SetVector<Operation *> ltlOpFixupWorklist;
1771 LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1773 return FIRRTLLowering(module, loweringState).run();
1777 LogicalResult FIRRTLLowering::run() {
1781 auto &body = theModule.getBody();
1783 SmallVector<Operation *, 16> opsToRemove;
1787 for (
auto &op : body.front().getOperations()) {
1788 builder.setInsertionPoint(&op);
1790 auto done = succeeded(dispatchVisitor(&op));
1791 circuitState.processRemainingAnnotations(&op,
AnnotationSet(&op));
1793 opsToRemove.push_back(&op);
1795 switch (handleUnloweredOp(&op)) {
1796 case AlreadyLowered:
1799 opsToRemove.push_back(&op);
1801 case LoweringFailure:
1813 for (
auto &[backedge, value] : backedges) {
1814 SmallVector<Location> driverLocs;
1820 if (backedge == value) {
1821 Location edgeLoc = backedge.getLoc();
1822 if (driverLocs.empty()) {
1823 mlir::emitError(edgeLoc,
"sink does not have a driver");
1825 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
1826 for (
auto loc : driverLocs)
1827 diag.attachNote(loc) <<
"through driver here";
1833 auto it = backedges.find(value);
1834 if (it == backedges.end())
1837 driverLocs.push_back(value.getLoc());
1840 if (
auto *defOp = backedge.getDefiningOp())
1841 maybeUnusedValues.erase(defOp);
1842 backedge.replaceAllUsesWith(value);
1850 while (!opsToRemove.empty()) {
1851 auto *op = opsToRemove.pop_back_val();
1856 for (
auto result : op->getResults()) {
1860 auto builder = OpBuilder::atBlockBegin(&body.front());
1862 builder.getIntegerType(0), 0);
1863 maybeUnusedValues.insert(zeroI0);
1865 result.replaceAllUsesWith(zeroI0);
1868 if (!op->use_empty()) {
1869 auto d = op->emitOpError(
1870 "still has uses; should remove ops in reverse order of visitation");
1871 SmallPtrSet<Operation *, 2> visited;
1872 for (
auto *user : op->getUsers())
1873 if (visited.insert(user).second)
1874 d.attachNote(user->getLoc())
1875 <<
"used by " << user->getName() <<
" op";
1878 maybeUnusedValues.erase(op);
1883 while (!maybeUnusedValues.empty()) {
1884 auto it = maybeUnusedValues.begin();
1886 maybeUnusedValues.erase(it);
1887 if (!isOpTriviallyDead(op))
1889 for (
auto operand : op->getOperands())
1890 if (
auto *defOp = operand.getDefiningOp())
1891 maybeUnusedValues.insert(defOp);
1897 if (failed(fixupLTLOps()))
1908 Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
1911 auto &entry = hwConstantMap[attr];
1915 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1916 entry = entryBuilder.create<seq::ConstClockOp>(
builder.getLoc(), attr);
1922 Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
1923 auto attr =
builder.getIntegerAttr(
1924 builder.getIntegerType(value.getBitWidth()), value);
1926 auto &entry = hwConstantMap[attr];
1930 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1937 Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
1940 if (hw::type_isa<IntegerType>(type))
1941 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
1943 auto cache = hwAggregateConstantMap.lookup({value, type});
1948 SmallVector<Attribute> values;
1949 for (
auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
1951 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
1952 subType = array.getElementType();
1953 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
1954 subType = structType.getElements()[e.index()].type;
1956 assert(
false &&
"type must be either array or struct");
1958 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
1962 if (hw::type_isa<hw::ArrayType>(type))
1963 std::reverse(values.begin(), values.end());
1965 auto &entry = hwAggregateConstantMap[{value, type}];
1966 entry =
builder.getArrayAttr(values);
1974 std::function<LogicalResult()> fn) {
1975 assert(failedOperand &&
"Should be called on the failed operand");
1983 Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
1985 auto &entry = hwConstantXMap[numBits];
1989 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1990 entry = entryBuilder.create<sv::ConstantXOp>(
1991 builder.getLoc(), entryBuilder.getIntegerType(numBits));
1995 Value FIRRTLLowering::getOrCreateZConstant(Type type) {
1996 auto &entry = hwConstantZMap[type];
1998 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1999 entry = entryBuilder.create<sv::ConstantZOp>(
builder.getLoc(), type);
2008 Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2010 if (value.isa<BlockArgument>())
2014 if (
auto lowering = valueMapping.lookup(value)) {
2016 "Lowered value should be a non-FIRRTL value");
2025 Value FIRRTLLowering::getLoweredValue(Value value) {
2026 auto result = getPossiblyInoutLoweredValue(value);
2033 return getReadValue(result);
2039 Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2040 auto result = getLoweredValue(value);
2044 if (hw::type_isa<seq::ClockType>(result.getType()))
2045 return getNonClockValue(result);
2053 Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2056 bool allowTruncate) {
2057 SmallVector<Value> resultBuffer;
2062 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2063 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2064 auto resultType =
builder.getIntegerType(destWidth);
2066 if (srcWidth == destWidth)
2069 if (srcWidth > destWidth) {
2073 builder.emitError(
"operand should not be a truncation");
2077 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2079 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2087 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2088 .Case<FVectorType>([&](
auto srcVectorType) {
2089 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2090 unsigned size = resultBuffer.size();
2091 unsigned indexWidth =
2093 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2094 destVectorType.getNumElements());
2096 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2098 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2099 destVectorType.getElementType())))
2102 SmallVector<Value> temp(resultBuffer.begin() + size,
2103 resultBuffer.end());
2105 resultBuffer.resize(size);
2106 resultBuffer.push_back(array);
2109 .Case<BundleType>([&](BundleType srcStructType) {
2110 auto destStructType = firrtl::type_cast<BundleType>(destType);
2111 unsigned size = resultBuffer.size();
2114 if (destStructType.getNumElements() != srcStructType.getNumElements())
2117 for (
auto elem : llvm::enumerate(destStructType)) {
2118 auto structExtract =
2120 if (failed(recurse(structExtract,
2121 srcStructType.getElementType(elem.index()),
2122 destStructType.getElementType(elem.index()))))
2125 SmallVector<Value> temp(resultBuffer.begin() + size,
2126 resultBuffer.end());
2129 resultBuffer.resize(size);
2130 resultBuffer.push_back(newStruct);
2133 .Case<IntType>([&](
auto) {
2134 if (
auto result = cast(src, srcType, destType)) {
2135 resultBuffer.push_back(result);
2140 .Default([&](
auto) {
return failure(); });
2143 if (failed(recurse(array, sourceType, destType)))
2146 assert(resultBuffer.size() == 1 &&
2147 "resultBuffer must only contain a result array if `success` is true");
2148 return resultBuffer[0];
2156 Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2157 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2158 type_isa<FIRRTLBaseType>(destType) &&
2159 "input/output value should be FIRRTL");
2162 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2163 if (destWidth == -1)
2166 auto result = getLoweredValue(value);
2178 return getOrCreateIntConstant(destWidth, 0);
2182 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2184 auto loweredDstType =
lowerType(destType);
2185 if (result.getType() != loweredDstType &&
2186 (isa<hw::TypeAliasType>(result.getType()) ||
2187 isa<hw::TypeAliasType>(loweredDstType))) {
2192 if (result.getType().isa<hw::ArrayType, hw::StructType>()) {
2194 if (destType == value.getType())
2197 return getExtOrTruncAggregateValue(
2198 result, type_cast<FIRRTLBaseType>(value.getType()),
2199 type_cast<FIRRTLBaseType>(destType),
2203 if (result.getType().isa<seq::ClockType>()) {
2205 if (destType == value.getType())
2207 builder.emitError(
"cannot use clock type as an integer");
2211 auto intResultType = dyn_cast<IntegerType>(result.getType());
2212 if (!intResultType) {
2213 builder.emitError(
"operand of type ")
2214 << result.getType() <<
" cannot be used as an integer";
2218 auto srcWidth = intResultType.getWidth();
2219 if (srcWidth ==
unsigned(destWidth))
2222 if (srcWidth >
unsigned(destWidth)) {
2223 builder.emitError(
"operand should not be a truncation");
2227 auto resultType =
builder.getIntegerType(destWidth);
2231 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2232 if (type_cast<IntType>(valueFIRType).isSigned())
2235 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2244 Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2245 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2246 type_isa<FIRRTLBaseType>(destType) &&
2247 "input/output value should be FIRRTL");
2250 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2251 if (destWidth == -1)
2254 auto result = getLoweredValue(value);
2266 return getOrCreateIntConstant(destWidth, 0);
2270 if (result.getType().isa<hw::ArrayType, hw::StructType>()) {
2272 if (destType == value.getType())
2275 return getExtOrTruncAggregateValue(
2276 result, type_cast<FIRRTLBaseType>(value.getType()),
2277 type_cast<FIRRTLBaseType>(destType),
2281 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2282 if (srcWidth ==
unsigned(destWidth))
2288 if (srcWidth >
unsigned(destWidth)) {
2289 auto resultType =
builder.getIntegerType(destWidth);
2293 auto resultType =
builder.getIntegerType(destWidth);
2297 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2298 if (type_cast<IntType>(valueFIRType).isSigned())
2301 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2308 Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2309 auto loweredValue = getLoweredValue(operand);
2310 if (!loweredValue) {
2314 loweredValue = getOrCreateIntConstant(1, 0);
2319 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2320 if (intTy.isSigned())
2321 loweredValue =
builder.create<sv::SystemFunctionOp>(
2322 loweredValue.getType(),
"signed", loweredValue);
2324 return loweredValue;
2333 LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2334 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2335 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2336 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2340 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2343 if (srcWidth != -1) {
2345 assert((srcWidth != 0) &&
2346 "Lowering produced value for zero width source");
2348 assert((srcWidth == 0) &&
2349 "Lowering produced null value but source wasn't zero width");
2353 assert(result &&
"Lowering of foreign type produced null value");
2356 auto &slot = valueMapping[orig];
2357 assert(!slot &&
"value lowered multiple times");
2364 LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2368 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2369 auto &entry = hwConstantMap[cst.getValueAttr()];
2380 cst->moveBefore(&theModule.getBodyBlock()->front());
2384 return setLowering(orig, result);
2389 template <
typename ResultOpType,
typename... CtorArgTypes>
2390 LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2391 CtorArgTypes... args) {
2392 auto result =
builder.createOrFold<ResultOpType>(args...);
2393 if (
auto *op = result.getDefiningOp())
2395 return setPossiblyFoldedLowering(orig->getResult(0), result);
2402 template <
typename ResultOpType,
typename... CtorArgTypes>
2403 LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2404 CtorArgTypes... args) {
2405 auto result =
builder.createOrFold<ResultOpType>(args...);
2406 if (
auto *op = result.getDefiningOp())
2407 ltlOpFixupWorklist.insert(op);
2408 return setPossiblyFoldedLowering(orig->getResult(0), result);
2417 Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2418 auto backedge = backedgeBuilder.
get(type, loc);
2419 backedges.insert({backedge, backedge});
2427 Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2428 auto backedge = createBackedge(orig.getLoc(), type);
2429 (void)setLowering(orig, backedge);
2435 bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2436 auto backedgeIt = backedges.find(dest);
2437 if (backedgeIt == backedges.end())
2439 backedgeIt->second = src;
2447 void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2448 std::function<
void(
void)> fn, Region ®ion) {
2452 auto oldIP =
builder.saveInsertionPoint();
2454 builder.setInsertionPointToEnd(®ion.front());
2456 builder.restoreInsertionPoint(oldIP);
2460 Value FIRRTLLowering::getReadValue(Value v) {
2461 Value result = readInOutCreated.lookup(v);
2467 auto oldIP =
builder.saveInsertionPoint();
2468 if (
auto *vOp = v.getDefiningOp()) {
2469 builder.setInsertionPointAfter(vOp);
2473 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2478 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2479 result = getReadValue(arrayIndexInout.getInput());
2481 arrayIndexInout.getIndex());
2486 builder.restoreInsertionPoint(oldIP);
2487 readInOutCreated.insert({v, result});
2491 Value FIRRTLLowering::getNonClockValue(Value v) {
2492 auto it = fromClockMapping.try_emplace(v, Value{});
2494 ImplicitLocOpBuilder
builder(v.getLoc(), v.getContext());
2495 builder.setInsertionPointAfterValue(v);
2496 it.first->second =
builder.create<seq::FromClockOp>(v);
2498 return it.first->second;
2501 void FIRRTLLowering::addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
2502 ::ResetType resetStyle,
2503 sv::EventControl resetEdge, Value reset,
2504 std::function<
void(
void)> body,
2505 std::function<
void(
void)> resetBody) {
2506 AlwaysKeyType key{
builder.getBlock(), clockEdge, clock,
2507 resetStyle, resetEdge, reset};
2508 sv::AlwaysOp alwaysOp;
2509 sv::IfOp insideIfOp;
2510 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2514 assert(resetStyle != ::ResetType::NoReset);
2527 auto createIfOp = [&]() {
2530 insideIfOp =
builder.create<sv::IfOp>(
2531 reset, []() {}, []() {});
2533 if (resetStyle == ::ResetType::AsyncReset) {
2534 sv::EventControl events[] = {clockEdge, resetEdge};
2535 Value clocks[] = {clock, reset};
2537 alwaysOp =
builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2538 if (resetEdge == sv::EventControl::AtNegEdge)
2539 llvm_unreachable(
"negative edge for reset is not expected");
2543 alwaysOp =
builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2547 alwaysOp =
builder.create<sv::AlwaysOp>(clockEdge, clock);
2548 insideIfOp =
nullptr;
2550 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2554 assert(insideIfOp &&
"reset body must be initialized before");
2555 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2556 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2558 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2564 alwaysOp->moveBefore(
builder.getInsertionBlock(),
2568 LogicalResult FIRRTLLowering::emitGuards(Location loc,
2569 ArrayRef<Attribute> guards,
2570 std::function<
void(
void)>
emit) {
2571 if (guards.empty()) {
2575 auto guard = guards[0].dyn_cast<StringAttr>();
2577 return mlir::emitError(loc,
2578 "elements in `guards` array must be `StringAttr`");
2581 circuitState.addMacroDecl(
builder.getStringAttr(guard.getValue()));
2582 LogicalResult result = LogicalResult::failure();
2583 addToIfDefBlock(guard.getValue(), [&]() {
2584 result = emitGuards(loc, guards.drop_front(), emit);
2589 void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2590 std::function<
void(
void)> thenCtor,
2591 std::function<
void(
void)> elseCtor) {
2592 auto condAttr =
builder.getStringAttr(cond);
2593 auto op = ifdefBlocks.lookup({
builder.getBlock(), condAttr});
2595 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2596 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2601 op->moveBefore(
builder.getInsertionBlock(),
builder.getInsertionPoint());
2603 ifdefBlocks[{
builder.getBlock(), condAttr}] =
2608 void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2609 auto op = initialBlocks.lookup(
builder.getBlock());
2611 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2616 op->moveBefore(
builder.getInsertionBlock(),
builder.getInsertionPoint());
2618 initialBlocks[
builder.getBlock()] =
builder.create<sv::InitialOp>(body);
2622 void FIRRTLLowering::addIfProceduralBlock(Value cond,
2623 std::function<
void(
void)> thenCtor,
2624 std::function<
void(
void)> elseCtor) {
2627 auto insertIt =
builder.getInsertionPoint();
2628 if (insertIt !=
builder.getBlock()->begin())
2629 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2630 if (ifOp.getCond() == cond) {
2631 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2632 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2637 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2649 FIRRTLLowering::UnloweredOpResult
2650 FIRRTLLowering::handleUnloweredOp(Operation *op) {
2655 if (!isa<FIRRTLDialect>(op->getDialect())) {
2656 for (
auto &operand : op->getOpOperands())
2657 if (
auto lowered = getPossiblyInoutLoweredValue(operand.get()))
2658 operand.set(lowered);
2659 for (
auto result : op->getResults())
2660 (void)setLowering(result, result);
2661 return AlreadyLowered;
2673 if (op->getNumResults() == 1) {
2674 auto resultType = op->getResult(0).getType();
2675 if (type_isa<FIRRTLBaseType>(resultType) &&
2677 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2679 (void)setLowering(op->getResult(0), Value());
2683 op->emitOpError(
"LowerToHW couldn't handle this operation");
2684 return LoweringFailure;
2687 LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2690 return setLowering(op, Value());
2692 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2695 LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2697 if (op.getType().isa<ClockType>()) {
2698 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2699 : seq::ClockConst::Low);
2701 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
2703 return setLowering(op, cst);
2706 FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2707 auto iIdx = getOrCreateIntConstant(
2709 firrtl::type_cast<FVectorType>(op.getInput().getType())
2717 result =
builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2724 FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2725 Value valueIdx = getLoweredAndExtOrTruncValue(
2729 firrtl::type_cast<FVectorType>(op.getInput().getType())
2730 .getNumElements())));
2732 op->emitError() <<
"input lowering failed";
2740 result =
builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2742 result = createArrayIndexing(input, valueIdx);
2747 FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2748 auto resultType =
lowerType(op->getResult(0).getType());
2749 if (!resultType || !input) {
2750 op->emitError() <<
"subfield type lowering failed";
2756 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2757 .getElementName(op.getFieldIndex());
2760 result =
builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2767 LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2769 return setLowering(op, Value());
2771 auto input = getPossiblyInoutLoweredValue(op.getInput());
2773 return op.emitError() <<
"input lowering failed";
2775 auto result = lowerSubindex(op, input);
2778 return setLowering(op, *result);
2781 LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2783 return setLowering(op, Value());
2785 auto input = getPossiblyInoutLoweredValue(op.getInput());
2787 return op.emitError() <<
"input lowering failed";
2789 auto result = lowerSubaccess(op, input);
2792 return setLowering(op, *result);
2795 LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2798 if (getLoweredValue(op) || !op.getInput())
2802 return setLowering(op, Value());
2804 auto input = getPossiblyInoutLoweredValue(op.getInput());
2806 return op.emitError() <<
"input lowering failed";
2808 auto result = lowerSubfield(op, input);
2811 return setLowering(op, *result);
2814 LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2815 auto resultType =
lowerType(op.getResult().getType());
2816 SmallVector<Value> operands;
2818 for (
auto oper : llvm::reverse(op.getOperands())) {
2819 auto val = getLoweredValue(oper);
2822 operands.push_back(val);
2824 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2827 LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2828 auto resultType =
lowerType(op.getResult().getType());
2829 SmallVector<Value> operands;
2830 for (
auto oper : op.getOperands()) {
2831 auto val = getLoweredValue(oper);
2834 operands.push_back(val);
2836 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2839 LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2842 return setLowering(op, Value());
2844 auto input = getLoweredValue(op.getInput());
2845 auto tagName = op.getFieldNameAttr();
2848 if (
auto structType = dyn_cast<hw::StructType>(type)) {
2849 auto enumType = structType.getFieldType(
"tag");
2851 auto enumOp =
builder.create<hw::EnumConstantOp>(enumAttr);
2852 auto unionType = structType.getFieldType(
"body");
2853 auto unionOp =
builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2854 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2855 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2858 return setLoweringTo<hw::EnumConstantOp>(
2862 LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2863 auto resultType =
lowerType(op.getResult().getType());
2865 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2867 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2868 cast<ArrayAttr>(attr));
2871 LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2872 auto tagName = op.getFieldNameAttr();
2873 auto lhs = getLoweredValue(op.getInput());
2874 if (isa<hw::StructType>(lhs.getType()))
2877 auto rhs =
builder.create<hw::EnumConstantOp>(enumField);
2878 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2881 LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2884 return setLowering(op, Value());
2886 auto tagName = op.getFieldNameAttr();
2887 auto input = getLoweredValue(op.getInput());
2889 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2896 LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
2897 auto origResultType = op.getResult().getType();
2901 if (!type_isa<FIRRTLType>(origResultType)) {
2902 createBackedge(op.getResult(), origResultType);
2906 auto resultType =
lowerType(origResultType);
2910 if (resultType.isInteger(0))
2911 return setLowering(op.getResult(), Value());
2914 auto innerSym = lowerInnerSymbol(op);
2915 auto name = op.getNameAttr();
2918 auto wire =
builder.create<hw::WireOp>(
2919 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
2924 return setLowering(op.getResult(), wire);
2927 LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
2928 auto resultTy =
lowerType(op.getType());
2933 SmallVector<Value, 4> operands;
2934 operands.reserve(op.getSubstitutions().size());
2935 for (
auto operand : op.getSubstitutions()) {
2936 auto lowered = getLoweredValue(operand);
2939 operands.push_back(lowered);
2942 ArrayAttr symbols = op.getSymbolsAttr();
2946 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
2950 LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
2951 auto operand = getLoweredValue(op.getInput());
2954 op.getInput(), [&]() { return setLowering(op.getResult(), Value()); });
2959 auto name = op.getNameAttr();
2960 auto innerSym = lowerInnerSymbol(op);
2963 operand =
builder.create<hw::WireOp>(operand, name, innerSym);
2968 operand =
builder.create<hw::WireOp>(operand, name);
2972 return setLowering(op.getResult(), operand);
2975 LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
2976 auto resultType =
lowerType(op.getResult().getType());
2979 if (resultType.isInteger(0))
2980 return setLowering(op.getResult(), Value());
2982 Value clockVal = getLoweredValue(op.getClockVal());
2987 auto innerSym = lowerInnerSymbol(op);
2988 Backedge inputEdge = backedgeBuilder.
get(resultType);
2989 auto reg =
builder.create<seq::FirRegOp>(inputEdge, clockVal,
2990 op.getNameAttr(), innerSym);
2993 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
2994 reg->setAttr(
"firrtl.random_init_register", randomRegister);
2995 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
2996 reg->setAttr(
"firrtl.random_init_start", randomStart);
2997 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
2998 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3004 inputEdge.setValue(
reg);
3005 (void)setLowering(op.getResult(),
reg);
3009 LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3010 auto resultType =
lowerType(op.getResult().getType());
3013 if (resultType.isInteger(0))
3014 return setLowering(op.getResult(), Value());
3016 Value clockVal = getLoweredValue(op.getClockVal());
3017 Value resetSignal = getLoweredValue(op.getResetSignal());
3019 Value resetValue = getLoweredAndExtOrTruncValue(
3020 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3022 if (!clockVal || !resetSignal || !resetValue)
3026 auto innerSym = lowerInnerSymbol(op);
3027 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3028 Backedge inputEdge = backedgeBuilder.
get(resultType);
3030 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3031 resetSignal, resetValue, innerSym, isAsync);
3034 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3035 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3036 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3037 reg->setAttr(
"firrtl.random_init_start", randomStart);
3038 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3039 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3045 inputEdge.setValue(
reg);
3046 (void)setLowering(op.getResult(),
reg);
3051 LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3054 if (type_isa<BundleType>(op.getDataType()))
3055 return op.emitOpError(
3056 "should have already been lowered from a ground type to an aggregate "
3057 "type using the LowerTypes pass. Use "
3058 "'firtool --lower-types' or 'circt-opt "
3059 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3068 : std::optional<uint32_t>());
3070 seq::FirMemInitAttr memInit;
3071 if (
auto init = op.getInitAttr())
3073 init.getIsBinary(), init.getIsInline());
3075 auto memDecl =
builder.create<seq::FirMemOp>(
3078 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3082 if (!circuitState.isInDUT(theModule))
3083 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3084 memDecl.setOutputFileAttr(testBenchDir);
3088 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3090 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3093 (void)setLowering(a, value);
3099 auto addInput = [&](StringRef field, Value backedge) {
3104 .getBitWidthOrSentinel() > 0)
3105 (
void)setLowering(a, backedge);
3111 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3115 Value backedge, portValue;
3117 portValue = getOrCreateXConstant(1);
3120 backedge = portValue = createBackedge(
builder.getLoc(), portType);
3122 addInput(field, backedge);
3126 auto addClock = [&](StringRef field) -> Value {
3128 Value portValue = createBackedge(
builder.getLoc(), clockTy);
3129 addInput(field, portValue);
3133 auto memportKind = op.getPortKind(i);
3134 if (memportKind == MemOp::PortKind::Read) {
3135 auto addr = addInputPort(
"addr", op.getAddrBits());
3136 auto en = addInputPort(
"en", 1);
3137 auto clk = addClock(
"clk");
3139 addOutput(
"data", memSummary.
dataWidth, data);
3140 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3141 auto addr = addInputPort(
"addr", op.getAddrBits());
3142 auto en = addInputPort(
"en", 1);
3143 auto clk = addClock(
"clk");
3146 auto mode = addInputPort(
"wmode", 1);
3157 addOutput(
"rdata", memSummary.
dataWidth, rdata);
3159 auto addr = addInputPort(
"addr", op.getAddrBits());
3162 auto en = addInputPort(
"en", 1);
3166 auto clk = addClock(
"clk");
3179 LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3180 Operation *oldModule =
3181 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3183 auto newModule = circuitState.getNewModule(oldModule);
3185 oldInstance->emitOpError(
"could not find module [")
3186 << oldInstance.getModuleName() <<
"] referenced by instance";
3192 ArrayAttr parameters;
3193 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3198 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3203 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3204 portIndicesByName[portInfo[portIdx].name] = portIdx;
3208 SmallVector<Value, 8> operands;
3209 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3210 auto &port = portInfo[portIndex];
3213 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3218 if (portType.isInteger(0))
3222 if (port.isOutput())
3225 auto portResult = oldInstance.getResult(portIndex);
3226 assert(portResult &&
"invalid IR, couldn't find port");
3230 if (port.isInput()) {
3231 operands.push_back(createBackedge(portResult, portType));
3237 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3238 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3240 auto loweredResult = getPossiblyInoutLoweredValue(source);
3241 operands.push_back(loweredResult);
3242 (void)setLowering(portResult, loweredResult);
3251 portType,
"." + port.getName().str() +
".wire");
3255 (void)setLowering(portResult, wire);
3257 operands.push_back(wire);
3264 auto innerSym = oldInstance.getInnerSymAttr();
3265 if (oldInstance.getLowerToBind()) {
3268 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3269 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3271 auto bindOp =
builder.create<sv::BindOp>(theModule.getNameAttr(),
3272 innerSym.getSymName());
3275 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3276 bindOp->setAttr(
"output_file", outputFile);
3279 circuitState.addBind(bindOp);
3283 auto newInstance =
builder.create<hw::InstanceOp>(
3284 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3286 if (oldInstance.getLowerToBind())
3287 newInstance->setAttr(
"doNotPrint",
builder.getBoolAttr(
true));
3289 if (newInstance.getInnerSymAttr())
3290 if (
auto forceName = circuitState.instanceForceNames.lookup(
3291 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3292 newInstance.getInnerNameAttr()}))
3293 newInstance->setAttr(
"hw.verilogName", forceName);
3297 unsigned resultNo = 0;
3298 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3299 auto &port = portInfo[portIndex];
3303 Value resultVal = newInstance.getResult(resultNo);
3305 auto oldPortResult = oldInstance.getResult(portIndex);
3306 (void)setLowering(oldPortResult, resultVal);
3317 LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3318 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3323 return setLowering(op->getResult(0), operand);
3326 LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3327 if (isa<ClockType>(op.getInput().getType()))
3328 return setLowering(op->getResult(0),
3329 getLoweredNonClockValue(op.getInput()));
3330 return lowerNoopCast(op);
3333 LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3334 if (isa<ClockType>(op.getInput().getType()))
3335 return setLowering(op->getResult(0),
3336 getLoweredNonClockValue(op.getInput()));
3337 return lowerNoopCast(op);
3340 LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3341 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3344 LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3345 mlir::UnrealizedConversionCastOp op) {
3347 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3350 auto operand = op.getOperand(0);
3351 auto result = op.getResult(0);
3354 if (type_isa<FIRRTLType>(operand.getType()) &&
3355 type_isa<FIRRTLType>(result.getType()))
3356 return lowerNoopCast(op);
3360 if (!type_isa<FIRRTLType>(operand.getType())) {
3361 if (type_isa<FIRRTLType>(result.getType()))
3362 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3368 auto lowered_result = getLoweredValue(operand);
3369 if (!lowered_result) {
3372 if (operand.getType().isSignlessInteger(0)) {
3373 return setLowering(result, Value());
3380 result.replaceAllUsesWith(lowered_result);
3384 LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3387 if (
auto opStructType = op.getOperand().getType().dyn_cast<hw::StructType>())
3388 return setLowering(op, op.getOperand());
3392 auto result = getLoweredValue(op.getOperand());
3398 op.replaceAllUsesWith(result);
3402 LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3403 auto operand = getLoweredValue(op.getOperand());
3406 auto resultType =
lowerType(op.getType());
3410 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3413 LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3414 auto operand = getLoweredValue(op.getOperand());
3418 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3419 return setLowering(op, getOrCreateIntConstant(1, 0));
3421 return setLowering(op, Value());
3426 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3427 return setLowering(op, operand);
3430 auto zero = getOrCreateIntConstant(1, 0);
3431 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3434 LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3435 auto operand = getLoweredValue(op.getInput());
3439 auto allOnes = getOrCreateIntConstant(
3440 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3441 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3444 LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3447 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3451 auto resultType =
lowerType(op.getType());
3453 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3454 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3458 LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3459 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3462 return setLowering(op, operand);
3465 LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3466 auto operand = getLoweredValue(op.getInput());
3469 return setLowering(op, getOrCreateIntConstant(1, 0));
3474 return setLoweringTo<comb::ParityOp>(op,
builder.getIntegerType(1), operand,
3478 LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3479 auto operand = getLoweredValue(op.getInput());
3482 return setLowering(op, getOrCreateIntConstant(1, 1));
3487 return setLoweringTo<comb::ICmpOp>(
3488 op, ICmpPredicate::eq, operand,
3489 getOrCreateIntConstant(
3490 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3494 LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3495 auto operand = getLoweredValue(op.getInput());
3498 return setLowering(op, getOrCreateIntConstant(1, 0));
3504 return setLoweringTo<comb::ICmpOp>(
3505 op, ICmpPredicate::ne, operand,
3506 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3514 template <
typename ResultOpType>
3515 LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3516 auto resultType = op->getResult(0).getType();
3517 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3518 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3522 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3528 template <
typename ResultOpType>
3529 LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3530 auto resultType = op->getResult(0).getType();
3531 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3532 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3543 auto intType =
builder.getIntegerType(*bitwidth);
3544 auto retType = lhs.getType();
3547 auto result =
builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3548 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3553 template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3554 LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3556 auto resultType = op->getResult(0).getType();
3557 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3558 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3563 if (type_cast<IntType>(resultType).isSigned())
3564 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3565 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3570 LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3571 ICmpPredicate unsignedOp) {
3573 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3574 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3575 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3579 if (cmpType.getWidth() == 0)
3581 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3582 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3587 Type resultType =
builder.getIntegerType(1);
3588 return setLoweringTo<comb::ICmpOp>(
3589 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3595 template <
typename SignedOp,
typename Un
signedOp>
3596 LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3600 auto opType = type_cast<IntType>(op->getResult(0).getType());
3601 if (opType.getWidth() == 0)
3602 return setLowering(op->getResult(0), Value());
3606 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3607 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3612 if (opType.isSigned())
3613 result =
builder.createOrFold<SignedOp>(lhs, rhs,
true);
3615 result =
builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
3619 if (resultType == opType)
3620 return setLowering(op->getResult(0), result);
3621 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
3624 LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3625 auto lhs = getLoweredValue(op.getLhs());
3626 auto rhs = getLoweredValue(op.getRhs());
3630 return setLowering(op, rhs);
3632 return handleZeroBit(op.getRhs(),
3633 [&]() { return setLowering(op, Value()); });
3638 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3640 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3647 LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3648 auto input = getLoweredNonClockValue(op.getArg());
3652 return setLoweringTo<comb::ICmpOp>(
3653 op, ICmpPredicate::ceq, input,
3654 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
3657 LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
3658 auto operand = getLoweredValue(op.getInput());
3659 builder.create<hw::WireOp>(operand);
3663 LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3664 return setLoweringTo<sim::PlusArgsTestOp>(op,
builder.getIntegerType(1),
3665 op.getFormatStringAttr());
3668 LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3669 auto type =
lowerType(op.getResult().getType());
3673 auto valueOp =
builder.create<sim::PlusArgsValueOp>(
3674 builder.getIntegerType(1), type, op.getFormatStringAttr());
3675 if (failed(setLowering(op.getResult(), valueOp.getResult())))
3677 if (failed(setLowering(op.getFound(), valueOp.getFound())))
3682 LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3683 op.emitError(
"SizeOf should have been resolved.");
3687 LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3689 if (op.getTestEnable())
3690 testEnable = getLoweredValue(op.getTestEnable());
3691 return setLoweringTo<seq::ClockGateOp>(
3692 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3693 testEnable, hw::InnerSymAttr{});
3696 LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
3697 auto operand = getLoweredValue(op.getInput());
3698 return setLoweringTo<seq::ClockInverterOp>(op, operand);
3701 LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
3702 auto operand = getLoweredValue(op.getInput());
3703 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
3706 LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3707 return setLoweringToLTL<ltl::AndOp>(
3709 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3712 LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3713 return setLoweringToLTL<ltl::OrOp>(
3715 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3718 LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3719 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3720 op.getDelayAttr(), op.getLengthAttr());
3723 LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3724 return setLoweringToLTL<ltl::ConcatOp>(
3726 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3729 LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3730 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3733 LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3734 return setLoweringToLTL<ltl::ImplicationOp>(
3736 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3739 LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3740 return setLoweringToLTL<ltl::EventuallyOp>(op,
3741 getLoweredValue(op.getInput()));
3744 LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3745 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3746 ltl::ClockEdge::Pos,
3747 getLoweredNonClockValue(op.getClock()));
3750 LogicalResult FIRRTLLowering::visitExpr(LTLDisableIntrinsicOp op) {
3751 return setLoweringToLTL<ltl::DisableOp>(
3753 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3756 LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3757 builder.create<verif::AssertOp>(getLoweredValue(op.getProperty()),
3762 LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3763 builder.create<verif::AssumeOp>(getLoweredValue(op.getProperty()),
3768 LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3769 builder.create<verif::CoverOp>(getLoweredValue(op.getProperty()),
3774 LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3775 auto clock = getLoweredNonClockValue(op.getClock());
3776 auto reset = getLoweredValue(op.getReset());
3777 if (!clock || !reset)
3779 auto resetType = op.getReset().getType();
3780 auto uintResetType = dyn_cast<UIntType>(resetType);
3781 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3782 auto isAsync = isa<AsyncResetType>(resetType);
3783 if (!isAsync && !isSync) {
3784 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
3785 "requires sync or async reset");
3786 d.attachNote() <<
"reset is of type " << resetType
3787 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3790 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3797 LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3798 auto input = getLoweredValue(op.getInput());
3802 Type resultType =
builder.getIntegerType(op.getHi() - op.getLo() + 1);
3803 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3806 LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3807 auto resultTy =
lowerType(op.getType());
3814 if (type_isa<AnalogType>(op.getType()))
3817 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
3820 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
3831 auto constant = getOrCreateIntConstant(*bitwidth, 0);
3833 if (!type_isa<IntegerType>(resultTy))
3835 return setLowering(op, constant);
3839 op.emitOpError(
"unsupported type");
3843 LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
3844 auto input = getLoweredValue(op.getInput());
3847 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3848 if (op.getAmount() == 0)
3849 return setLowering(op, Value());
3850 Type resultType =
builder.getIntegerType(op.getAmount());
3851 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
3852 inWidth - op.getAmount());
3855 LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
3856 auto input = getLoweredValue(op.getInput());
3859 if (op.getAmount() == 0)
3861 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
3866 if (op.getAmount() == 0)
3867 return setLowering(op, input);
3869 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
3870 return setLoweringTo<comb::ConcatOp>(op, input, zero);
3873 LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
3874 auto input = getLoweredValue(op.getInput());
3879 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3880 auto shiftAmount = op.getAmount();
3881 if (shiftAmount >= inWidth) {
3883 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
3884 return setLowering(op, {});
3887 shiftAmount = inWidth - 1;
3890 Type resultType =
builder.getIntegerType(inWidth - shiftAmount);
3891 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
3894 LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
3895 auto input = getLoweredValue(op.getInput());
3899 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3900 if (inWidth == op.getAmount())
3901 return setLowering(op, Value());
3902 Type resultType =
builder.getIntegerType(inWidth - op.getAmount());
3903 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
3906 LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
3907 auto cond = getLoweredValue(op.getSel());
3908 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3909 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3910 if (!cond || !ifTrue || !ifFalse)
3913 if (op.getType().isa<ClockType>())
3914 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
3915 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
3919 LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
3920 auto cond = getLoweredValue(op.getSel());
3921 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3922 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3923 if (!cond || !ifTrue || !ifFalse)
3928 return setLowering(op, createValueWithMuxAnnotation(val,
true));
3931 LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
3932 auto sel = getLoweredValue(op.getSel());
3933 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
3934 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
3935 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
3936 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
3937 if (!sel || !v3 || !v2 || !v1 || !v0)
3939 Value array[] = {v3, v2, v1, v0};
3942 return setLowering(op, createValueWithMuxAnnotation(val,
false));
3961 Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
3962 assert(op->getNumResults() == 1 &&
"only expect a single result");
3963 auto val = op->getResult(0);
3974 OpBuilder::InsertionGuard guard(
builder);
3975 builder.setInsertionPoint(op);
3976 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
3977 for (
auto [idx, operand] : llvm::enumerate(op->getOperands())) {
3979 op->getContext(),
nullptr, 0,
3980 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3982 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
3983 op->setOperand(idx, wire);
3990 "synopsys infer_mux_override",
3995 Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
3997 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4002 if (!llvm::isPowerOf2_64(size)) {
4003 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4005 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4007 Value temp2[] = {ext.getResult(), array};
4013 return inBoundsRead;
4016 LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4018 auto index = getLoweredAndExtOrTruncValue(
4025 SmallVector<Value> loweredInputs;
4026 loweredInputs.reserve(op.getInputs().size());
4027 for (
auto input : op.getInputs()) {
4028 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4031 loweredInputs.push_back(lowered);
4035 return setLowering(op, createArrayIndexing(array, index));
4038 LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4039 auto resultTy =
lowerType(op.getType());
4043 SmallVector<Value, 4> operands;
4044 operands.reserve(op.getSubstitutions().size());
4045 for (
auto operand : op.getSubstitutions()) {
4046 auto lowered = getLoweredValue(operand);
4049 operands.push_back(lowered);
4052 ArrayAttr symbols = op.getSymbolsAttr();
4056 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4060 LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4064 Type baseType = op.getType().getType();
4067 if (isa<ClockType>(baseType))
4068 xmrType =
builder.getIntegerType(1);
4073 op.getRef(), op.getVerbatimSuffixAttr());
4076 LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4080 if (isa<ClockType>(op.getType()))
4081 xmrType =
builder.getIntegerType(1);
4085 auto xmr =
builder.create<sv::XMRRefOp>(
4087 auto readXmr = getReadValue(xmr);
4088 if (!isa<ClockType>(op.getType()))
4089 return setLowering(op, readXmr);
4090 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4097 LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4109 FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4110 auto srcType = srcVal.getType();
4111 auto dstType = destVal.getType();
4112 if (srcType != dstType &&
4113 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4116 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4117 .Case<hw::WireOp>([&](
auto op) {
4118 maybeUnused(op.getInput());
4119 op.getInputMutable().assign(srcVal);
4122 .Case<seq::FirRegOp>([&](
auto op) {
4123 maybeUnused(op.getNext());
4124 op.getNextMutable().assign(srcVal);
4127 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4130 op.emitOpError(
"used as connect destination");
4133 .Default([](
auto) {
return false; });
4136 LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4137 auto dest = op.getDest();
4139 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4140 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4142 return handleZeroBit(op.getSrc(), []() { return success(); });
4144 auto destVal = getPossiblyInoutLoweredValue(dest);
4148 auto result = lowerConnect(destVal, srcVal);
4156 if (updateIfBackedge(destVal, srcVal))
4160 return op.emitError(
"destination isn't an inout type");
4166 LogicalResult FIRRTLLowering::visitStmt(StrictConnectOp op) {
4167 auto dest = op.getDest();
4168 auto srcVal = getLoweredValue(op.getSrc());
4170 return handleZeroBit(op.getSrc(), []() { return success(); });
4172 auto destVal = getPossiblyInoutLoweredValue(dest);
4176 auto result = lowerConnect(destVal, srcVal);
4184 if (updateIfBackedge(destVal, srcVal))
4188 return op.emitError(
"destination isn't an inout type");
4194 LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4195 auto srcVal = getLoweredValue(op.getSrc());
4199 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4204 return op.emitError(
"destination isn't an inout type");
4207 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4208 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4209 addToInitialBlock([&]() {
builder.create<sv::ForceOp>(destVal, srcVal); });
4214 LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4215 auto src = getLoweredNonClockValue(op.getSrc());
4216 auto clock = getLoweredNonClockValue(op.getClock());
4217 auto pred = getLoweredValue(op.getPredicate());
4218 if (!src || !clock || !pred)
4221 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4226 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4227 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4228 addToAlwaysBlock(clock, [&]() {
4229 addIfProceduralBlock(
4230 pred, [&]() {
builder.create<sv::ForceOp>(destVal, src); });
4235 LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4236 auto src = getLoweredNonClockValue(op.getSrc());
4237 auto pred = getLoweredValue(op.getPredicate());
4241 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4246 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4247 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4248 addToInitialBlock([&]() {
4249 addIfProceduralBlock(
4250 pred, [&]() {
builder.create<sv::ForceOp>(destVal, src); });
4255 LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4256 auto clock = getLoweredNonClockValue(op.getClock());
4257 auto pred = getLoweredValue(op.getPredicate());
4258 if (!clock || !pred)
4261 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4266 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4267 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4268 addToAlwaysBlock(clock, [&]() {
4269 addIfProceduralBlock(pred,
4270 [&]() {
builder.create<sv::ReleaseOp>(destVal); });
4275 LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4276 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4277 auto pred = getLoweredValue(op.getPredicate());
4278 if (!destVal || !pred)
4282 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4283 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4284 addToInitialBlock([&]() {
4285 addIfProceduralBlock(pred,
4286 [&]() {
builder.create<sv::ReleaseOp>(destVal); });
4294 LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4295 auto clock = getLoweredNonClockValue(op.getClock());
4296 auto cond = getLoweredValue(op.getCond());
4297 if (!clock || !cond)
4300 SmallVector<Value, 4> operands;
4301 operands.reserve(op.getSubstitutions().size());
4302 for (
auto operand : op.getSubstitutions()) {
4303 Value loweredValue = getLoweredFmtOperand(operand);
4306 operands.push_back(loweredValue);
4310 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4311 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4312 addToAlwaysBlock(clock, [&]() {
4313 circuitState.usedPrintfCond =
true;
4314 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
4318 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
4321 addIfProceduralBlock(ifCond, [&]() {
4324 builder.create<sv::FWriteOp>(fdStderr, op.getFormatString(), operands);
4334 LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4335 auto clock = getLoweredValue(op.getClock());
4336 auto cond = getLoweredValue(op.getCond());
4337 if (!clock || !cond)
4340 circuitState.usedStopCond =
true;
4341 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4344 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4347 if (op.getExitCode())
4348 builder.create<sim::FatalOp>(clock, exitCond);
4350 builder.create<sim::FinishOp>(clock, exitCond);
4358 template <
typename... Args>
4360 StringRef opName, Args &&...args) {
4361 if (opName ==
"assert")
4362 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4363 if (opName ==
"assume")
4364 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4365 if (opName ==
"cover")
4366 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4367 llvm_unreachable(
"unknown verification op");
4373 template <
typename... Args>
4375 StringRef opName, Args &&...args) {
4376 if (opName ==
"assert")
4377 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4378 if (opName ==
"assume")
4379 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4380 if (opName ==
"cover")
4381 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4382 llvm_unreachable(
"unknown verification op");
4403 LogicalResult FIRRTLLowering::lowerVerificationStatement(
4404 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4405 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4406 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
4407 StringRef opName = op->getName().stripDialect();
4410 ArrayRef<Attribute> guards{};
4411 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
4412 guards = guardsAttr.getValue();
4414 auto isCover = isa<CoverOp>(op);
4415 auto clock = getLoweredNonClockValue(opClock);
4416 auto enable = getLoweredValue(opEnable);
4417 auto predicate = getLoweredValue(opPredicate);
4418 if (!clock || !enable || !predicate)
4422 if (opNameAttr && !opNameAttr.getValue().empty())
4424 StringAttr prefixedLabel;
4430 SmallVector<Value> messageOps;
4434 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
4435 flavor = VerificationFlavor::None;
4437 if (flavor == VerificationFlavor::None) {
4441 auto format = op->getAttrOfType<StringAttr>(
"format");
4443 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
4444 if (!isa<AssertOp>(op))
4445 return op->emitError()
4446 <<
"ifElseFatal format cannot be used for non-assertions";
4447 flavor = VerificationFlavor::IfElseFatal;
4448 }
else if (isConcurrent)
4449 flavor = VerificationFlavor::SVA;
4451 flavor = VerificationFlavor::Immediate;
4454 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4455 message = opMessageAttr;
4456 for (
auto operand : opOperands) {
4457 auto loweredValue = getLoweredFmtOperand(operand);
4465 if (flavor == VerificationFlavor::SVA)
4466 loweredValue =
builder.create<sv::SampledOp>(loweredValue);
4467 messageOps.push_back(loweredValue);
4473 case VerificationFlavor::Immediate: {
4476 builder.getContext(), circt::sv::DeferAssert::Immediate);
4477 addToAlwaysBlock(clock, [&]() {
4478 addIfProceduralBlock(enable, [&]() {
4480 prefixedLabel, message, messageOps);
4485 case VerificationFlavor::IfElseFatal: {
4486 assert(isa<AssertOp>(op) &&
"only assert is expected");
4493 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4494 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
4495 addToAlwaysBlock(clock, [&]() {
4496 addIfProceduralBlock(predicate, [&]() {
4497 circuitState.usedStopCond =
true;
4498 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4500 circuitState.usedAssertVerboseCond =
true;
4501 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
4503 addIfProceduralBlock(
4504 builder.create<sv::MacroRefExprOp>(boolType,
4505 "ASSERT_VERBOSE_COND_"),
4506 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4507 addIfProceduralBlock(
4508 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
4509 [&]() { builder.create<sv::FatalOp>(); });
4515 case VerificationFlavor::SVA: {
4528 sv::EventControl event;
4529 switch (opEventControl) {
4530 case EventControl::AtPosEdge:
4531 event = circt::sv::EventControl::AtPosEdge;
4533 case EventControl::AtEdge:
4534 event = circt::sv::EventControl::AtEdge;
4536 case EventControl::AtNegEdge:
4537 event = circt::sv::EventControl::AtNegEdge;
4544 predicate, prefixedLabel, message, messageOps);
4547 case VerificationFlavor::None:
4549 "flavor `None` must be converted into one of concreate flavors");
4556 return emitGuards(op->getLoc(), guards,
emit);
4560 LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4561 return lowerVerificationStatement(
4562 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4563 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4564 op.getIsConcurrent(), op.getEventControl());
4568 LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4569 return lowerVerificationStatement(
4570 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4571 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4572 op.getIsConcurrent(), op.getEventControl());
4576 LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4577 return lowerVerificationStatement(
4578 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4579 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4580 op.getIsConcurrent(), op.getEventControl());
4584 LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
4589 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
4590 ArrayRef<Attribute> guards =
4591 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
4593 auto label = op.getNameAttr();
4594 StringAttr assumeLabel;
4598 auto predicate = getLoweredValue(op.getPredicate());
4599 auto enable = getLoweredValue(op.getEnable());
4603 SmallVector<Value> messageOps;
4604 for (
auto operand : op.getSubstitutions()) {
4605 auto loweredValue = getLoweredValue(operand);
4606 if (!loweredValue) {
4610 loweredValue = getOrCreateIntConstant(1, 0);
4612 messageOps.push_back(loweredValue);
4614 return emitGuards(op.getLoc(), guards, [&]() {
4615 builder.create<sv::AlwaysOp>(
4616 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4617 if (op.getMessageAttr().getValue().empty())
4618 buildImmediateVerifOp(
4619 builder,
"assume", predicate,
4620 circt::sv::DeferAssertAttr::get(
4621 builder.getContext(), circt::sv::DeferAssert::Immediate),
4624 buildImmediateVerifOp(
4625 builder,
"assume", predicate,
4626 circt::sv::DeferAssertAttr::get(
4627 builder.getContext(), circt::sv::DeferAssert::Immediate),
4628 assumeLabel, op.getMessageAttr(), messageOps);
4633 LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4635 if (op.getAttached().size() < 2)
4638 SmallVector<Value, 4> inoutValues;
4639 for (
auto v : op.getAttached()) {
4640 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4641 if (!inoutValues.back()) {
4645 inoutValues.pop_back();
4650 return op.emitError(
"operand isn't an inout type");
4653 if (inoutValues.size() < 2)
4664 bool isAttachInternalOnly =
4665 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
4667 if (isAttachInternalOnly) {
4668 auto v0 = inoutValues.front();
4669 for (
auto v : inoutValues) {
4672 v.replaceAllUsesWith(v0);
4679 circuitState.addMacroDecl(
builder.getStringAttr(
"SYNTHESIS"));
4680 circuitState.addMacroDecl(
builder.getStringAttr(
"VERILATOR"));
4685 SmallVector<Value, 4> values;
4686 for (
size_t i = 0, e = inoutValues.size(); i != e; ++i)
4687 values.push_back(getReadValue(inoutValues[i]));
4689 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4690 for (size_t i2 = 0; i2 != e; ++i2)
4692 builder.create<sv::AssignOp>(inoutValues[i1], values[i2]);
4701 builder.create<sv::VerbatimOp>(
4702 "`error \"Verilator does not support alias and thus "
4704 "arbitrarily connect bidirectional wires and ports\"");
4706 [&]() {
builder.create<sv::AliasOp>(inoutValues); });
4712 LogicalResult FIRRTLLowering::fixupLTLOps() {
4713 if (ltlOpFixupWorklist.empty())
4715 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
4719 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4720 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
4721 if (isa<hw::WireOp>(user))
4722 ltlOpFixupWorklist.insert(user);
4725 while (!ltlOpFixupWorklist.empty()) {
4726 auto *op = ltlOpFixupWorklist.pop_back_val();
4729 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4730 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
4731 SmallVector<Type, 2> types;
4732 auto result = opIntf.inferReturnTypes(
4733 op->getContext(), op->getLoc(), op->getOperands(),
4734 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4738 assert(types.size() == op->getNumResults());
4742 for (
auto [result, type] : llvm::zip(op->getResults(), types)) {
4743 if (result.getType() == type)
4745 LLVM_DEBUG(llvm::dbgs()
4746 <<
" - Result #" << result.getResultNumber() <<
" from "
4747 << result.getType() <<
" to " << type <<
"\n");
4748 result.setType(type);
4749 for (
auto *user : result.getUsers())
4751 ltlOpFixupWorklist.insert(user);
4756 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
4757 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4758 wireOp.replaceAllUsesWith(wireOp.getInput());
4759 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
4760 if (wireOp.use_empty())
4767 SmallPtrSet<Operation *, 4> usersReported;
4768 for (
auto *user : op->getUsers()) {
4769 if (!usersReported.insert(user).second)
4771 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4773 if (isa<hw::WireOp>(user))
4775 auto d = op->emitError(
4776 "verification operation used in a non-verification context");
4777 d.attachNote(user->getLoc())
4778 <<
"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 LogicalResult handleZeroBit(Value failedOperand, std::function< LogicalResult()> fn)
Zero bit operands end up looking like failures from getLoweredValue.
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 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.
llvm::SmallVector< StringAttr > outputs
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.
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 * scalaClassAnnoClass
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.
circt::hw::InOutType InOutType
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.
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.