36 #include "mlir/IR/BuiltinOps.h"
37 #include "mlir/IR/BuiltinTypes.h"
38 #include "mlir/IR/ImplicitLocOpBuilder.h"
39 #include "mlir/IR/Threading.h"
40 #include "mlir/Pass/Pass.h"
41 #include "llvm/ADT/StringSet.h"
42 #include "llvm/ADT/TinyPtrVector.h"
43 #include "llvm/Support/Debug.h"
44 #include "llvm/Support/FormatVariadic.h"
45 #include "llvm/Support/Mutex.h"
46 #include "llvm/Support/Parallel.h"
48 #define DEBUG_TYPE "lower-to-hw"
51 #define GEN_PASS_DEF_LOWERFIRRTLTOHW
52 #include "circt/Conversion/Passes.h.inc"
55 using namespace circt;
56 using namespace firrtl;
57 using circt::comb::ICmpPredicate;
66 auto ftype = dyn_cast<FIRRTLBaseType>(type);
67 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
74 for (
auto operand : op.getAttached()) {
76 operand.getDefiningOp<InstanceOp>())
80 if (!operand.hasOneUse() || singleSource)
82 singleSource = operand;
91 auto checkTypes = [](Operation *op) -> WalkResult {
93 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
94 return op->emitError(
"Found unhandled FIRRTL operation '")
95 << op->getName() <<
"'";
98 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
99 if (llvm::any_of(types, [](Type type) {
100 return isa<FIRRTLDialect>(type.getDialect());
102 return op->emitOpError(
"found unhandled FIRRTL type");
107 if (failed(checkTypeRange(op->getOperandTypes())) ||
108 failed(checkTypeRange(op->getResultTypes())))
109 return WalkResult::interrupt();
112 for (
auto ®ion : op->getRegions())
113 for (
auto &block : region)
114 if (failed(checkTypeRange(block.getArgumentTypes())))
115 return WalkResult::interrupt();
118 return WalkResult::advance();
121 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
128 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
129 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
135 ImplicitLocOpBuilder &builder) {
137 if (BundleType bundle = dyn_cast<BundleType>(type))
138 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
140 if (type != val.getType())
141 val = builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
149 ImplicitLocOpBuilder &builder) {
151 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
155 .create<mlir::UnrealizedConversionCastOp>(
156 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
158 val = builder.createOrFold<HWStructCastOp>(type, val);
163 builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
170 StringRef annoClass, StringRef attrBase) {
172 auto ctx = top.getContext();
175 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
176 SmallVector<NamedAttribute> old;
177 for (
auto i : top->getAttrs())
181 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
184 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
185 SmallVector<NamedAttribute> old;
186 for (
auto i : top->getAttrs())
189 hw::OutputFileAttr::getFromFilename(
190 ctx, file.getValue(),
true));
196 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
202 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
203 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
204 dst->setAttr(
"sv.namehint", attr);
212 struct FIRRTLModuleLowering;
215 struct CircuitLoweringState {
217 std::atomic<bool> usedPrintfCond{
false};
218 std::atomic<bool> usedAssertVerboseCond{
false};
219 std::atomic<bool> usedStopCond{
false};
221 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
224 : circuitOp(circuitOp), instanceGraph(instanceGraph),
225 enableAnnotationWarning(enableAnnotationWarning),
226 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
227 auto *context = circuitOp.getContext();
232 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
233 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
234 context, dirName.getValue(),
false,
true);
237 for (
auto &op : *circuitOp.getBodyBlock()) {
238 if (
auto module = dyn_cast<FModuleLike>(op))
249 testHarness =
nullptr;
250 }
else if (dut == testHarness) {
251 testHarness =
nullptr;
256 auto inDUT = [&](igraph::ModuleOpInterface child) {
258 auto inst = instRec->getInstance();
259 if (
auto *finst = dyn_cast<InstanceOp>(&inst))
260 return finst->getLowerToBind();
263 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
264 return getInstanceGraph().isAncestor(child, parent, isBind);
267 circuitOp->walk([&](FModuleLike moduleOp) {
269 dutModules.insert(moduleOp);
273 Operation *getNewModule(Operation *oldModule) {
274 auto it = oldToNewModuleMap.find(oldModule);
275 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
278 Operation *getOldModule(Operation *newModule) {
279 auto it = newToOldModuleMap.find(newModule);
280 return it != newToOldModuleMap.end() ? it->second :
nullptr;
283 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
284 oldToNewModuleMap[oldFMod] = newHWMod;
285 newToOldModuleMap[newHWMod] = oldFMod;
290 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
296 void addBind(sv::BindOp op) {
297 std::lock_guard<std::mutex> lock(bindsMutex);
303 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
306 auto hwAlias = typeAliases.getTypedecl(firAliasType);
309 assert(!typeAliases.isFrozen() &&
310 "type aliases cannot be generated after its frozen");
311 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
314 FModuleLike getDut() {
return dut; }
315 FModuleLike getTestHarness() {
return testHarness; }
321 bool isInDUT(igraph::ModuleOpInterface child) {
322 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
323 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
324 return dutModules.contains(child);
327 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
332 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
339 Type
lowerType(Type type, Location loc) {
341 [&](Type rawType, BaseTypeAliasType firrtlType,
342 Location typeLoc) -> hw::TypeAliasType {
343 return getTypeAlias(rawType, firrtlType, typeLoc);
348 friend struct FIRRTLModuleLowering;
349 friend struct FIRRTLLowering;
350 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
351 void operator=(
const CircuitLoweringState &) =
delete;
354 DenseMap<Operation *, Operation *> oldToNewModuleMap;
357 DenseMap<Operation *, Operation *> newToOldModuleMap;
368 DenseSet<igraph::ModuleOpInterface> dutModules;
372 StringSet<> pendingAnnotations;
373 const bool enableAnnotationWarning;
374 std::mutex annotationPrintingMtx;
380 SmallVector<sv::BindOp> binds;
383 std::mutex bindsMutex;
391 FModuleLike testHarness;
394 hw::OutputFileAttr testBenchDirectory;
398 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
401 SetVector<StringAttr> macroDeclNames;
402 std::mutex macroDeclMutex;
404 void addMacroDecl(StringAttr name) {
405 std::unique_lock<std::mutex> lock(macroDeclMutex);
406 macroDeclNames.insert(name);
411 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
412 llvm::sys::SmartMutex<true> fragmentsMutex;
415 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
416 fragments[module].insert(
432 struct RecordTypeAlias {
434 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
436 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
437 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
438 if (iter != firrtlTypeToAliasTypeMap.end())
443 bool isFrozen() {
return frozen; }
445 void freeze() { frozen =
true; }
447 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
449 assert(!frozen &&
"Record already frozen, cannot be updated");
452 auto b = ImplicitLocOpBuilder::atBlockBegin(
454 &circuitOp->getParentRegion()->getBlocks().back());
456 b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
457 typeScope.getBodyRegion().push_back(
new Block());
459 auto typeName = firAlias.getName();
465 typeDeclNamespace.newName(typeName.getValue()));
467 auto typeScopeBuilder =
468 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
469 auto typeDecl = typeScopeBuilder.create<
hw::TypedeclOp>(typeLoc, typeName,
473 {FlatSymbolRefAttr::get(typeDecl)}),
475 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
476 assert(insert.second &&
"Entry already exists, insert failed");
477 return insert.first->second;
486 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
494 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
497 void CircuitLoweringState::processRemainingAnnotations(
499 if (!enableAnnotationWarning || annoSet.
empty())
501 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
503 for (
auto a : annoSet) {
504 auto inserted = pendingAnnotations.insert(a.getClass());
505 if (!inserted.second)
544 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
545 "' still remaining after LowerToHW");
551 struct FIRRTLModuleLowering
552 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
554 void runOnOperation()
override;
555 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
557 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
560 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
561 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
562 SmallVectorImpl<hw::PortInfo> &ports,
563 Operation *moduleOp, StringRef moduleName,
565 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
567 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
570 Block *topLevelModule,
573 Block *topLevelModule,
577 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
587 bool enableAnnotationWarning,
589 auto pass = std::make_unique<FIRRTLModuleLowering>();
590 if (enableAnnotationWarning)
591 pass->setEnableAnnotationWarning();
592 pass->verificationFlavor = verificationFlavor;
598 void FIRRTLModuleLowering::runOnOperation() {
602 auto *topLevelModule = getOperation().getBody();
606 for (
auto &op : *topLevelModule) {
607 if ((circuit = dyn_cast<CircuitOp>(&op)))
614 auto *circuitBody = circuit.getBodyBlock();
618 CircuitLoweringState state(circuit, enableAnnotationWarning,
619 verificationFlavor, getAnalysis<InstanceGraph>(),
620 &getAnalysis<NLATable>());
622 SmallVector<hw::HWModuleOp, 32> modulesToProcess;
626 "firrtl.extract.assert");
628 "firrtl.extract.assume");
630 "firrtl.extract.cover");
631 circuitAnno.removeAnnotationsWithClass(
634 state.processRemainingAnnotations(circuit, circuitAnno);
637 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
639 TypeSwitch<Operation *, LogicalResult>(&op)
640 .Case<FModuleOp>([&](
auto module) {
641 auto loweredMod = lowerModule(module, topLevelModule, state);
645 state.recordModuleMapping(&op, loweredMod);
646 modulesToProcess.push_back(loweredMod);
648 module.walk([&](Operation *op) {
649 for (
auto res : op->getResults()) {
651 type_dyn_cast<BaseTypeAliasType>(res.getType()))
652 state.lowerType(aliasType, op->getLoc());
655 return lowerModulePortsAndMoveBody(module, loweredMod, state);
657 .Case<FExtModuleOp>([&](
auto extModule) {
659 lowerExtModule(extModule, topLevelModule, state);
662 state.recordModuleMapping(&op, loweredMod);
665 .Case<FMemModuleOp>([&](
auto memModule) {
667 lowerMemModule(memModule, topLevelModule, state);
670 state.recordModuleMapping(&op, loweredMod);
673 .Default([&](Operation *op) {
678 op->moveBefore(topLevelModule, topLevelModule->end());
684 return signalPassFailure();
687 state.typeAliases.freeze();
692 SmallVector<Attribute> dutHierarchyFiles;
693 SmallVector<Attribute> testHarnessHierarchyFiles;
694 circuitAnno.removeAnnotations([&](
Annotation annotation) {
696 auto file = hw::OutputFileAttr::getFromFilename(
698 annotation.
getMember<StringAttr>(
"filename").getValue(),
700 dutHierarchyFiles.push_back(file);
704 auto file = hw::OutputFileAttr::getFromFilename(
706 annotation.
getMember<StringAttr>(
"filename").getValue(),
710 if (state.getTestHarness())
711 testHarnessHierarchyFiles.push_back(file);
713 dutHierarchyFiles.push_back(file);
719 if (!dutHierarchyFiles.empty())
720 state.getNewModule(state.getDut())
723 if (!testHarnessHierarchyFiles.empty())
724 state.getNewModule(state.getTestHarness())
729 auto result = mlir::failableParallelForEachN(
730 &getContext(), 0, modulesToProcess.size(), [&](
auto index) {
731 return lowerModuleOperations(modulesToProcess[index], state);
736 return signalPassFailure();
739 for (
auto bind : state.binds) {
744 for (
auto &[module, fragments] : state.fragments)
749 for (
auto oldNew : state.oldToNewModuleMap)
750 oldNew.first->erase();
752 if (!state.macroDeclNames.empty()) {
754 for (
auto name : state.macroDeclNames) {
755 b.create<sv::MacroDeclOp>(name);
760 lowerFileHeader(circuit, state);
767 void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
768 CircuitLoweringState &state) {
775 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
776 StringRef defineTrue =
"",
777 StringRef defineFalse = StringRef()) {
778 if (!defineFalse.data()) {
779 assert(defineTrue.data() &&
"didn't define anything");
781 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
786 if (defineTrue.data())
787 b.create<sv::MacroDefOp>(defName, defineTrue);
789 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
794 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
796 guard, []() {}, body);
799 if (state.usedPrintfCond) {
800 b.create<sv::MacroDeclOp>(
"PRINTF_COND");
801 b.create<sv::MacroDeclOp>(
"PRINTF_COND_");
802 b.create<emit::FragmentOp>(
"PRINTF_COND_FRAGMENT", [&] {
803 b.create<sv::VerbatimOp>(
804 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
806 emitGuard(
"PRINTF_COND_", [&]() {
807 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
812 if (state.usedAssertVerboseCond) {
813 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND");
814 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND_");
815 b.create<emit::FragmentOp>(
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
816 b.create<sv::VerbatimOp>(
817 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
818 "gate to assert error printing.");
819 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
820 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
821 "(`ASSERT_VERBOSE_COND)",
"1");
826 if (state.usedStopCond) {
827 b.create<sv::MacroDeclOp>(
"STOP_COND");
828 b.create<sv::MacroDeclOp>(
"STOP_COND_");
829 b.create<emit::FragmentOp>(
"STOP_COND_FRAGMENT", [&] {
830 b.create<sv::VerbatimOp>(
831 "\n// Users can define 'STOP_COND' to add an extra gate "
832 "to stop conditions.");
833 emitGuard(
"STOP_COND_", [&]() {
834 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
841 FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
842 SmallVectorImpl<hw::PortInfo> &ports,
843 Operation *moduleOp, StringRef moduleName,
845 ports.reserve(firrtlPorts.size());
847 size_t numResults = 0;
848 for (
auto e : llvm::enumerate(firrtlPorts)) {
850 size_t portNo = e.index();
855 if (firrtlPort.
sym.size() > 1 ||
856 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
857 return emitError(firrtlPort.
loc)
858 <<
"cannot lower aggregate port " << firrtlPort.
name
859 <<
" with field sensitive symbols, HW dialect does not support "
860 "per field symbols yet.";
861 hwPort.setSym(firrtlPort.
sym, moduleOp->getContext());
863 if (hadDontTouch && !hwPort.getSym()) {
864 if (hwPort.type.isInteger(0)) {
865 if (enableAnnotationWarning) {
866 mlir::emitWarning(firrtlPort.
loc)
867 <<
"zero width port " << hwPort.name
868 <<
" has dontTouch annotation, removing anyway";
875 moduleOp->getContext(),
876 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
877 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
878 moduleOp->getContext());
883 moduleOp->emitError(
"cannot lower this port type to HW");
889 if (hwPort.type.isInteger(0)) {
890 auto sym = hwPort.getSym();
891 if (sym && !sym.empty()) {
892 return mlir::emitError(firrtlPort.
loc)
893 <<
"zero width port " << hwPort.name
894 <<
" is referenced by name [" << sym
895 <<
"] (e.g. in an XMR) but must be removed";
903 hwPort.argNum = numResults++;
904 }
else if (firrtlPort.
isInput()) {
906 hwPort.argNum = numArgs++;
912 hwPort.argNum = numArgs++;
914 hwPort.loc = firrtlPort.
loc;
915 ports.push_back(hwPort);
925 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
926 return cast<ParamDeclAttr>(a);
931 Builder builder(module);
936 SmallVector<Attribute> newParams;
937 for (
const ParamDeclAttr &entry : params) {
938 auto name = entry.getName();
939 auto type = entry.getType();
940 auto value = ignoreValues ? Attribute() : entry.getValue();
943 newParams.push_back(paramAttr);
945 return builder.getArrayAttr(newParams);
948 bool FIRRTLModuleLowering::handleForceNameAnnos(
957 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
964 auto diag = oldModule.emitOpError()
966 <<
"' that is not a non-local annotation";
967 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
978 auto diag = oldModule.emitOpError()
980 <<
"' whose non-local symbol, '" << sym
981 <<
"' does not exist in the circuit";
982 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
995 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
997 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
998 if (!inserted.second &&
999 (anno.
getMember(
"name") != (inserted.first->second))) {
1000 auto diag = oldModule.emitError()
1002 <<
"' with different names: " << inserted.first->second
1003 <<
" was not " << anno.
getMember(
"name");
1004 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1015 FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1016 Block *topLevelModule,
1019 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1020 SmallVector<hw::PortInfo, 8> ports;
1021 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1025 StringRef verilogName;
1026 if (
auto defName = oldModule.getDefname())
1027 verilogName = defName.value();
1030 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1031 auto nameAttr = builder.getStringAttr(oldModule.getName());
1037 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1038 SymbolTable::setSymbolVisibility(newModule,
1039 SymbolTable::getSymbolVisibility(oldModule));
1041 bool hasOutputPort =
1042 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1043 if (!hasOutputPort &&
1046 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1052 loweringState.processRemainingAnnotations(oldModule, annos);
1057 FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1058 Block *topLevelModule,
1061 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1062 SmallVector<hw::PortInfo, 8> ports;
1063 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1068 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1070 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1071 oldModule.getModuleNameAttr());
1079 FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1082 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1083 SmallVector<hw::PortInfo, 8> ports;
1084 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1089 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1090 auto nameAttr = builder.getStringAttr(oldModule.getName());
1092 builder.create<
hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1094 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1095 newModule.setCommentAttr(comment);
1098 SmallVector<StringRef, 12> attrNames = {
1099 "annotations",
"convention",
"layers",
1100 "portNames",
"sym_name",
"portDirections",
1101 "portTypes",
"portAnnotations",
"portSyms",
1102 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName()};
1104 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1105 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1107 llvm::make_filter_range(oldModule->getAttrs(), [&](
auto namedAttr) {
1108 return !attrSet.count(namedAttr.getName()) &&
1109 !newModule->getAttrDictionary().contains(namedAttr.getName());
1111 newAttrs.push_back(i);
1113 newModule->setAttrs(newAttrs);
1117 SymbolTable::setSymbolVisibility(newModule,
1118 SymbolTable::getSymbolVisibility(oldModule));
1124 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1128 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1130 if (!newModule->hasAttr(
"output_file"))
1131 newModule->setAttr(
"output_file", testBenchDir);
1132 newModule->setAttr(
"firrtl.extract.do_not_extract",
1133 builder.getUnitAttr());
1134 newModule.setCommentAttr(
1135 builder.getStringAttr(
"VCS coverage exclude_file"));
1141 loweringState.processRemainingAnnotations(oldModule, annos);
1150 Operation *insertPoint) {
1151 if (!value.hasOneUse())
1154 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1155 if (!attach || attach.getNumOperands() != 2)
1159 auto loweredType =
lowerType(value.getType());
1160 if (loweredType.isInteger(0))
1165 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1166 auto *op = attachedValue.getDefiningOp();
1167 if (op && op->getBlock() == insertPoint->getBlock() &&
1168 !op->isBeforeInBlock(insertPoint))
1173 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1189 if (type_isa<AnalogType>(flipValue.getType()))
1192 Operation *connectOp =
nullptr;
1193 for (
auto &use : flipValue.getUses()) {
1196 if (use.getOperandNumber() != 0)
1198 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1204 connectOp = use.getOwner();
1214 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1215 if (loweredType.isInteger(0))
1220 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1222 auto connectSrc = connectOp->getOperand(1);
1225 if (!isa<FIRRTLType>(connectSrc.getType())) {
1231 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1232 connectSrc = builder
1233 .create<mlir::UnrealizedConversionCastOp>(
1234 type_cast<FIRRTLBaseType>(connectSrc.getType())
1241 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1243 if (destTy != connectSrc.getType() &&
1244 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1245 isa<BaseTypeAliasType>(destTy))) {
1247 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1249 if (!destTy.isGround()) {
1251 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1253 }
else if (destTy.getBitWidthOrSentinel() !=
1254 type_cast<FIRRTLBaseType>(connectSrc.getType())
1255 .getBitWidthOrSentinel()) {
1258 auto destWidth = destTy.getBitWidthOrSentinel();
1259 assert(destWidth != -1 &&
"must know integer widths");
1260 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1272 SmallVector<SubfieldOp> accesses;
1273 for (
auto *op : structValue.getUsers()) {
1274 assert(isa<SubfieldOp>(op));
1275 auto fieldAccess = cast<SubfieldOp>(op);
1277 fieldAccess.getInput().getType().base().getElementIndex(field);
1278 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1279 accesses.push_back(fieldAccess);
1287 LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1290 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1297 bodyBuilder.setInsertionPoint(cursor);
1300 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1301 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1302 "port count mismatch");
1304 SmallVector<Value, 4> outputs;
1307 auto outputOp = newModule.getBodyBlock()->getTerminator();
1308 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1310 unsigned nextHWInputArg = 0;
1311 int hwPortIndex = -1;
1312 for (
auto [firrtlPortIndex, port] : llvm::enumerate(firrtlPorts)) {
1314 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1317 type_isa<FIRRTLBaseType>(port.type) &&
1318 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1322 if (!port.isOutput() && !isZeroWidth) {
1325 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1331 oldArg.replaceAllUsesWith(newArg);
1337 if (isZeroWidth && port.isInput()) {
1338 Value newArg = bodyBuilder
1339 .create<WireOp>(port.type,
"." + port.getName().str() +
1342 oldArg.replaceAllUsesWith(newArg);
1350 outputs.push_back(value);
1351 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1357 auto newArg = bodyBuilder.create<WireOp>(
1358 port.type,
"." + port.getName().str() +
".output");
1361 oldArg.replaceAllUsesWith(newArg.getResult());
1364 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1365 if (!resultHWType.isInteger(0)) {
1368 outputs.push_back(output);
1371 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1372 newArg.setInnerSymAttr(sym);
1373 newModule.setPortSymbolAttr(hwPortIndex, {});
1379 outputOp->setOperands(outputs);
1382 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1383 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1384 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1385 oldBlockInstList.begin(), oldBlockInstList.end());
1399 struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1401 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1402 : theModule(module), circuitState(circuitState),
1403 builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1404 backedgeBuilder(builder, module.getLoc()) {}
1406 LogicalResult
run();
1409 Value getOrCreateClockConstant(seq::ClockConst clock);
1410 Value getOrCreateIntConstant(
const APInt &value);
1411 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1412 bool isSigned =
false) {
1413 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1415 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1416 Value getOrCreateXConstant(
unsigned numBits);
1417 Value getOrCreateZConstant(Type type);
1418 Value getPossiblyInoutLoweredValue(Value value);
1419 Value getLoweredValue(Value value);
1420 Value getLoweredNonClockValue(Value value);
1421 Value getLoweredAndExtendedValue(Value value, Type destType);
1422 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1423 Value getLoweredFmtOperand(Value operand);
1424 LogicalResult setLowering(Value orig, Value result);
1425 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1426 template <
typename ResultOpType,
typename... CtorArgTypes>
1427 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1428 template <
typename ResultOpType,
typename... CtorArgTypes>
1429 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1430 Backedge createBackedge(Location loc, Type type);
1431 Backedge createBackedge(Value orig, Type type);
1432 bool updateIfBackedge(Value dest, Value src);
1435 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1440 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1441 if (forceable.isForceable())
1449 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1450 auto attr = op.getInnerSymAttr();
1454 if (requiresInnerSymbol(op))
1456 op.getContext(), attr, 0,
1457 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1461 void runWithInsertionPointAtEndOfBlock(std::function<
void(
void)> fn,
1465 Value getReadValue(Value v);
1467 Value getNonClockValue(Value v);
1469 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1470 sv::ResetType resetStyle, sv::EventControl resetEdge,
1471 Value reset, std::function<
void(
void)> body = {},
1472 std::function<void(
void)> resetBody = {});
1473 void addToAlwaysBlock(Value clock, std::function<
void(
void)> body = {}) {
1474 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1475 sv::EventControl(), Value(), body,
1476 std::function<
void(
void)>());
1479 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1480 std::function<
void(
void)>
emit);
1481 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1482 std::function<
void(
void)> elseCtor = {});
1483 void addToInitialBlock(std::function<
void(
void)> body);
1484 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1485 std::function<
void(
void)> elseCtor = {});
1486 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1488 bool allowTruncate);
1489 Value createArrayIndexing(Value array, Value index);
1490 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1497 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1498 UnloweredOpResult handleUnloweredOp(Operation *op);
1499 LogicalResult visitExpr(ConstantOp op);
1500 LogicalResult visitExpr(SpecialConstantOp op);
1501 LogicalResult visitExpr(SubindexOp op);
1502 LogicalResult visitExpr(SubaccessOp op);
1503 LogicalResult visitExpr(SubfieldOp op);
1504 LogicalResult visitExpr(VectorCreateOp op);
1505 LogicalResult visitExpr(BundleCreateOp op);
1506 LogicalResult visitExpr(FEnumCreateOp op);
1507 LogicalResult visitExpr(AggregateConstantOp op);
1508 LogicalResult visitExpr(IsTagOp op);
1509 LogicalResult visitExpr(SubtagOp op);
1510 LogicalResult visitUnhandledOp(Operation *op) {
return failure(); }
1511 LogicalResult visitInvalidOp(Operation *op) {
1512 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1513 return visitUnrealizedConversionCast(castOp);
1518 LogicalResult visitDecl(WireOp op);
1519 LogicalResult visitDecl(NodeOp op);
1520 LogicalResult visitDecl(RegOp op);
1521 LogicalResult visitDecl(RegResetOp op);
1522 LogicalResult visitDecl(MemOp op);
1523 LogicalResult visitDecl(InstanceOp op);
1524 LogicalResult visitDecl(VerbatimWireOp op);
1527 LogicalResult lowerNoopCast(Operation *op);
1528 LogicalResult visitExpr(AsSIntPrimOp op);
1529 LogicalResult visitExpr(AsUIntPrimOp op);
1530 LogicalResult visitExpr(AsClockPrimOp op);
1531 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1533 LogicalResult visitExpr(HWStructCastOp op);
1534 LogicalResult visitExpr(BitCastOp op);
1536 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1537 LogicalResult visitExpr(CvtPrimOp op);
1538 LogicalResult visitExpr(NotPrimOp op);
1539 LogicalResult visitExpr(NegPrimOp op);
1540 LogicalResult visitExpr(PadPrimOp op);
1541 LogicalResult visitExpr(XorRPrimOp op);
1542 LogicalResult visitExpr(AndRPrimOp op);
1543 LogicalResult visitExpr(OrRPrimOp op);
1546 template <
typename ResultUnsignedOpType,
1547 typename ResultSignedOpType = ResultUnsignedOpType>
1548 LogicalResult lowerBinOp(Operation *op);
1549 template <
typename ResultOpType>
1550 LogicalResult lowerBinOpToVariadic(Operation *op);
1552 template <
typename ResultOpType>
1553 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1555 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1556 ICmpPredicate unsignedOp);
1557 template <
typename SignedOp,
typename Un
signedOp>
1558 LogicalResult lowerDivLikeOp(Operation *op);
1560 LogicalResult visitExpr(CatPrimOp op);
1562 LogicalResult visitExpr(AndPrimOp op) {
1563 return lowerBinOpToVariadic<comb::AndOp>(op);
1565 LogicalResult visitExpr(OrPrimOp op) {
1566 return lowerBinOpToVariadic<comb::OrOp>(op);
1568 LogicalResult visitExpr(XorPrimOp op) {
1569 return lowerBinOpToVariadic<comb::XorOp>(op);
1571 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1572 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1574 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1575 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1577 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1578 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1580 LogicalResult visitExpr(AddPrimOp op) {
1581 return lowerBinOpToVariadic<comb::AddOp>(op);
1583 LogicalResult visitExpr(EQPrimOp op) {
1584 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1586 LogicalResult visitExpr(NEQPrimOp op) {
1587 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1589 LogicalResult visitExpr(LTPrimOp op) {
1590 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1592 LogicalResult visitExpr(LEQPrimOp op) {
1593 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1595 LogicalResult visitExpr(GTPrimOp op) {
1596 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1598 LogicalResult visitExpr(GEQPrimOp op) {
1599 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1602 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1603 LogicalResult visitExpr(MulPrimOp op) {
1604 return lowerBinOpToVariadic<comb::MulOp>(op);
1606 LogicalResult visitExpr(DivPrimOp op) {
1607 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1609 LogicalResult visitExpr(RemPrimOp op) {
1610 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1614 LogicalResult visitExpr(IsXIntrinsicOp op);
1615 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1616 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1617 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1618 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1619 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1620 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1621 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1622 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1623 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1624 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1625 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1626 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1627 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1628 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1629 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1630 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1631 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1632 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1633 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1634 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1636 template <
typename TargetOp,
typename IntrinsicOp>
1637 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1638 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1639 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1640 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1641 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1642 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1645 LogicalResult visitExpr(BitsPrimOp op);
1646 LogicalResult visitExpr(InvalidValueOp op);
1647 LogicalResult visitExpr(HeadPrimOp op);
1648 LogicalResult visitExpr(ShlPrimOp op);
1649 LogicalResult visitExpr(ShrPrimOp op);
1650 LogicalResult visitExpr(DShlPrimOp op) {
1651 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1653 LogicalResult visitExpr(DShrPrimOp op) {
1654 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1656 LogicalResult visitExpr(DShlwPrimOp op) {
1657 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1659 LogicalResult visitExpr(TailPrimOp op);
1660 LogicalResult visitExpr(MuxPrimOp op);
1661 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1662 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1663 LogicalResult visitExpr(MultibitMuxOp op);
1664 LogicalResult visitExpr(VerbatimExprOp op);
1665 LogicalResult visitExpr(XMRRefOp op);
1666 LogicalResult visitExpr(XMRDerefOp op);
1669 LogicalResult lowerVerificationStatement(
1670 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1671 Value enable, StringAttr messageAttr, ValueRange operands,
1672 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1674 LogicalResult visitStmt(SkipOp op);
1676 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1677 LogicalResult visitStmt(ConnectOp op);
1678 LogicalResult visitStmt(MatchingConnectOp op);
1679 LogicalResult visitStmt(ForceOp op);
1680 LogicalResult visitStmt(PrintFOp op);
1681 LogicalResult visitStmt(StopOp op);
1682 LogicalResult visitStmt(AssertOp op);
1683 LogicalResult visitStmt(AssumeOp op);
1684 LogicalResult visitStmt(CoverOp op);
1685 LogicalResult visitStmt(AttachOp op);
1686 LogicalResult visitStmt(RefForceOp op);
1687 LogicalResult visitStmt(RefForceInitialOp op);
1688 LogicalResult visitStmt(RefReleaseOp op);
1689 LogicalResult visitStmt(RefReleaseInitialOp op);
1691 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1692 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1693 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1695 LogicalResult fixupLTLOps();
1698 return circuitState.lowerType(type, builder.getLoc());
1706 CircuitLoweringState &circuitState;
1709 ImplicitLocOpBuilder builder;
1714 DenseMap<Value, Value> valueMapping;
1718 DenseMap<Value, Value> fromClockMapping;
1722 DenseMap<Attribute, Value> hwConstantMap;
1723 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1727 DenseMap<unsigned, Value> hwConstantXMap;
1728 DenseMap<Type, Value> hwConstantZMap;
1734 DenseMap<Value, Value> readInOutCreated;
1738 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1739 sv::ResetType, sv::EventControl, Value>;
1747 hw::InnerSymbolNamespace moduleNamespace;
1756 llvm::MapVector<Value, Value> backedges;
1763 DenseSet<Operation *> maybeUnusedValues;
1765 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1766 void maybeUnused(Value value) {
1767 if (
auto *op = value.getDefiningOp())
1779 SetVector<Operation *> ltlOpFixupWorklist;
1783 LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1793 auto &body = theModule.getBody();
1795 SmallVector<Operation *, 16> opsToRemove;
1799 for (
auto &op : body.front().getOperations()) {
1800 builder.setInsertionPoint(&op);
1801 builder.setLoc(op.getLoc());
1802 auto done = succeeded(dispatchVisitor(&op));
1803 circuitState.processRemainingAnnotations(&op,
AnnotationSet(&op));
1805 opsToRemove.push_back(&op);
1807 switch (handleUnloweredOp(&op)) {
1808 case AlreadyLowered:
1811 opsToRemove.push_back(&op);
1813 case LoweringFailure:
1825 for (
auto &[backedge, value] : backedges) {
1826 SmallVector<Location> driverLocs;
1832 if (backedge == value) {
1833 Location edgeLoc = backedge.getLoc();
1834 if (driverLocs.empty()) {
1835 mlir::emitError(edgeLoc,
"sink does not have a driver");
1837 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
1838 for (
auto loc : driverLocs)
1839 diag.attachNote(loc) <<
"through driver here";
1845 auto it = backedges.find(value);
1846 if (it == backedges.end())
1849 driverLocs.push_back(value.getLoc());
1852 if (
auto *defOp = backedge.getDefiningOp())
1853 maybeUnusedValues.erase(defOp);
1854 backedge.replaceAllUsesWith(value);
1862 while (!opsToRemove.empty()) {
1863 auto *op = opsToRemove.pop_back_val();
1868 for (
auto result : op->getResults()) {
1872 auto builder = OpBuilder::atBlockBegin(&body.front());
1874 builder.getIntegerType(0), 0);
1875 maybeUnusedValues.insert(zeroI0);
1877 result.replaceAllUsesWith(zeroI0);
1880 if (!op->use_empty()) {
1881 auto d = op->emitOpError(
1882 "still has uses; should remove ops in reverse order of visitation");
1883 SmallPtrSet<Operation *, 2> visited;
1884 for (
auto *user : op->getUsers())
1885 if (visited.insert(user).second)
1886 d.attachNote(user->getLoc())
1887 <<
"used by " << user->getName() <<
" op";
1890 maybeUnusedValues.erase(op);
1895 while (!maybeUnusedValues.empty()) {
1896 auto it = maybeUnusedValues.begin();
1898 maybeUnusedValues.erase(it);
1899 if (!isOpTriviallyDead(op))
1901 for (
auto operand : op->getOperands())
1902 if (
auto *defOp = operand.getDefiningOp())
1903 maybeUnusedValues.insert(defOp);
1909 if (failed(fixupLTLOps()))
1920 Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
1923 auto &entry = hwConstantMap[attr];
1927 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1928 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
1934 Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
1935 auto attr = builder.getIntegerAttr(
1936 builder.getIntegerType(value.getBitWidth()), value);
1938 auto &entry = hwConstantMap[attr];
1942 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1943 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
1949 Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
1952 if (hw::type_isa<IntegerType>(type))
1953 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
1955 auto cache = hwAggregateConstantMap.lookup({value, type});
1960 SmallVector<Attribute> values;
1961 for (
auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
1963 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
1964 subType = array.getElementType();
1965 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
1966 subType = structType.getElements()[e.index()].type;
1968 assert(
false &&
"type must be either array or struct");
1970 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
1974 if (hw::type_isa<hw::ArrayType>(type))
1975 std::reverse(values.begin(), values.end());
1977 auto &entry = hwAggregateConstantMap[{value, type}];
1978 entry = builder.getArrayAttr(values);
1986 std::function<LogicalResult()> fn) {
1987 assert(failedOperand &&
"Should be called on the failed operand");
1995 Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
1997 auto &entry = hwConstantXMap[numBits];
2001 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2002 entry = entryBuilder.create<sv::ConstantXOp>(
2003 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2007 Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2008 auto &entry = hwConstantZMap[type];
2010 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2011 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2020 Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2022 if (isa<BlockArgument>(value))
2026 if (
auto lowering = valueMapping.lookup(value)) {
2027 assert(!isa<FIRRTLType>(lowering.getType()) &&
2028 "Lowered value should be a non-FIRRTL value");
2037 Value FIRRTLLowering::getLoweredValue(Value value) {
2038 auto result = getPossiblyInoutLoweredValue(value);
2044 if (isa<hw::InOutType>(result.getType()))
2045 return getReadValue(result);
2051 Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2052 auto result = getLoweredValue(value);
2056 if (hw::type_isa<seq::ClockType>(result.getType()))
2057 return getNonClockValue(result);
2065 Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2068 bool allowTruncate) {
2069 SmallVector<Value> resultBuffer;
2074 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2075 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2076 auto resultType = builder.getIntegerType(destWidth);
2078 if (srcWidth == destWidth)
2081 if (srcWidth > destWidth) {
2085 builder.emitError(
"operand should not be a truncation");
2089 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2091 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2099 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2100 .Case<FVectorType>([&](
auto srcVectorType) {
2101 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2102 unsigned size = resultBuffer.size();
2103 unsigned indexWidth =
2105 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2106 destVectorType.getNumElements());
2108 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2110 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2111 destVectorType.getElementType())))
2114 SmallVector<Value> temp(resultBuffer.begin() + size,
2115 resultBuffer.end());
2117 resultBuffer.resize(size);
2118 resultBuffer.push_back(array);
2121 .Case<BundleType>([&](BundleType srcStructType) {
2122 auto destStructType = firrtl::type_cast<BundleType>(destType);
2123 unsigned size = resultBuffer.size();
2126 if (destStructType.getNumElements() != srcStructType.getNumElements())
2129 for (
auto elem : llvm::enumerate(destStructType)) {
2130 auto structExtract =
2132 if (failed(recurse(structExtract,
2133 srcStructType.getElementType(elem.index()),
2134 destStructType.getElementType(elem.index()))))
2137 SmallVector<Value> temp(resultBuffer.begin() + size,
2138 resultBuffer.end());
2141 resultBuffer.resize(size);
2142 resultBuffer.push_back(newStruct);
2145 .Case<IntType>([&](
auto) {
2146 if (
auto result = cast(src, srcType, destType)) {
2147 resultBuffer.push_back(result);
2152 .Default([&](
auto) {
return failure(); });
2155 if (failed(recurse(array, sourceType, destType)))
2158 assert(resultBuffer.size() == 1 &&
2159 "resultBuffer must only contain a result array if `success` is true");
2160 return resultBuffer[0];
2168 Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2169 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2170 type_isa<FIRRTLBaseType>(destType) &&
2171 "input/output value should be FIRRTL");
2174 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2175 if (destWidth == -1)
2178 auto result = getLoweredValue(value);
2190 return getOrCreateIntConstant(destWidth, 0);
2194 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2196 auto loweredDstType =
lowerType(destType);
2197 if (result.getType() != loweredDstType &&
2198 (isa<hw::TypeAliasType>(result.getType()) ||
2199 isa<hw::TypeAliasType>(loweredDstType))) {
2200 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, result);
2204 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2206 if (destType == value.getType())
2209 return getExtOrTruncAggregateValue(
2210 result, type_cast<FIRRTLBaseType>(value.getType()),
2211 type_cast<FIRRTLBaseType>(destType),
2215 if (isa<seq::ClockType>(result.getType())) {
2217 if (destType == value.getType())
2219 builder.emitError(
"cannot use clock type as an integer");
2223 auto intResultType = dyn_cast<IntegerType>(result.getType());
2224 if (!intResultType) {
2225 builder.emitError(
"operand of type ")
2226 << result.getType() <<
" cannot be used as an integer";
2230 auto srcWidth = intResultType.getWidth();
2231 if (srcWidth ==
unsigned(destWidth))
2234 if (srcWidth >
unsigned(destWidth)) {
2235 builder.emitError(
"operand should not be a truncation");
2239 auto resultType = builder.getIntegerType(destWidth);
2243 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2244 if (type_cast<IntType>(valueFIRType).isSigned())
2247 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2256 Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2257 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2258 type_isa<FIRRTLBaseType>(destType) &&
2259 "input/output value should be FIRRTL");
2262 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2263 if (destWidth == -1)
2266 auto result = getLoweredValue(value);
2278 return getOrCreateIntConstant(destWidth, 0);
2282 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2284 if (destType == value.getType())
2287 return getExtOrTruncAggregateValue(
2288 result, type_cast<FIRRTLBaseType>(value.getType()),
2289 type_cast<FIRRTLBaseType>(destType),
2293 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2294 if (srcWidth ==
unsigned(destWidth))
2300 if (srcWidth >
unsigned(destWidth)) {
2301 auto resultType = builder.getIntegerType(destWidth);
2305 auto resultType = builder.getIntegerType(destWidth);
2309 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2310 if (type_cast<IntType>(valueFIRType).isSigned())
2313 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2320 Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2321 auto loweredValue = getLoweredValue(operand);
2322 if (!loweredValue) {
2326 loweredValue = getOrCreateIntConstant(1, 0);
2331 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2332 if (intTy.isSigned())
2333 loweredValue = builder.create<sv::SystemFunctionOp>(
2334 loweredValue.getType(),
"signed", loweredValue);
2336 return loweredValue;
2345 LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2346 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2347 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2348 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2352 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2355 if (srcWidth != -1) {
2357 assert((srcWidth != 0) &&
2358 "Lowering produced value for zero width source");
2360 assert((srcWidth == 0) &&
2361 "Lowering produced null value but source wasn't zero width");
2365 assert(result &&
"Lowering of foreign type produced null value");
2368 auto &slot = valueMapping[orig];
2369 assert(!slot &&
"value lowered multiple times");
2376 LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2380 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2381 auto &entry = hwConstantMap[cst.getValueAttr()];
2392 cst->moveBefore(&theModule.getBodyBlock()->front());
2396 return setLowering(orig, result);
2401 template <
typename ResultOpType,
typename... CtorArgTypes>
2402 LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2403 CtorArgTypes... args) {
2404 auto result = builder.createOrFold<ResultOpType>(args...);
2405 if (
auto *op = result.getDefiningOp())
2407 return setPossiblyFoldedLowering(orig->getResult(0), result);
2414 template <
typename ResultOpType,
typename... CtorArgTypes>
2415 LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2416 CtorArgTypes... args) {
2417 auto result = builder.createOrFold<ResultOpType>(args...);
2418 if (
auto *op = result.getDefiningOp())
2419 ltlOpFixupWorklist.insert(op);
2420 return setPossiblyFoldedLowering(orig->getResult(0), result);
2429 Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2430 auto backedge = backedgeBuilder.
get(type, loc);
2431 backedges.insert({backedge, backedge});
2439 Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2440 auto backedge = createBackedge(orig.getLoc(), type);
2441 (void)setLowering(orig, backedge);
2447 bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2448 auto backedgeIt = backedges.find(dest);
2449 if (backedgeIt == backedges.end())
2451 backedgeIt->second = src;
2459 void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2460 std::function<
void(
void)> fn, Region ®ion) {
2464 auto oldIP = builder.saveInsertionPoint();
2466 builder.setInsertionPointToEnd(®ion.front());
2468 builder.restoreInsertionPoint(oldIP);
2472 Value FIRRTLLowering::getReadValue(Value v) {
2473 Value result = readInOutCreated.lookup(v);
2479 auto oldIP = builder.saveInsertionPoint();
2480 if (
auto *vOp = v.getDefiningOp()) {
2481 builder.setInsertionPointAfter(vOp);
2485 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2490 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2491 result = getReadValue(arrayIndexInout.getInput());
2493 arrayIndexInout.getIndex());
2498 builder.restoreInsertionPoint(oldIP);
2499 readInOutCreated.insert({v, result});
2503 Value FIRRTLLowering::getNonClockValue(Value v) {
2504 auto it = fromClockMapping.try_emplace(v, Value{});
2506 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2507 builder.setInsertionPointAfterValue(v);
2508 it.first->second = builder.create<seq::FromClockOp>(v);
2510 return it.first->second;
2513 void FIRRTLLowering::addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
2514 sv::ResetType resetStyle,
2515 sv::EventControl resetEdge, Value reset,
2516 std::function<
void(
void)> body,
2517 std::function<
void(
void)> resetBody) {
2518 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2519 resetStyle, resetEdge, reset};
2520 sv::AlwaysOp alwaysOp;
2521 sv::IfOp insideIfOp;
2522 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2526 assert(resetStyle != sv::ResetType::NoReset);
2539 auto createIfOp = [&]() {
2542 insideIfOp = builder.create<sv::IfOp>(
2543 reset, []() {}, []() {});
2545 if (resetStyle == sv::ResetType::AsyncReset) {
2546 sv::EventControl events[] = {clockEdge, resetEdge};
2547 Value clocks[] = {clock, reset};
2549 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2550 if (resetEdge == sv::EventControl::AtNegEdge)
2551 llvm_unreachable(
"negative edge for reset is not expected");
2555 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2559 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2560 insideIfOp =
nullptr;
2562 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2566 assert(insideIfOp &&
"reset body must be initialized before");
2567 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2568 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2570 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2576 alwaysOp->moveBefore(builder.getInsertionBlock(),
2577 builder.getInsertionPoint());
2580 LogicalResult FIRRTLLowering::emitGuards(Location loc,
2581 ArrayRef<Attribute> guards,
2582 std::function<
void(
void)>
emit) {
2583 if (guards.empty()) {
2587 auto guard = dyn_cast<StringAttr>(guards[0]);
2589 return mlir::emitError(loc,
2590 "elements in `guards` array must be `StringAttr`");
2593 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2594 LogicalResult result = LogicalResult::failure();
2595 addToIfDefBlock(guard.getValue(), [&]() {
2596 result = emitGuards(loc, guards.drop_front(), emit);
2601 void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2602 std::function<
void(
void)> thenCtor,
2603 std::function<
void(
void)> elseCtor) {
2604 auto condAttr = builder.getStringAttr(cond);
2605 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2607 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2608 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2613 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2615 ifdefBlocks[{builder.getBlock(), condAttr}] =
2616 builder.create<
sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2620 void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2621 auto op = initialBlocks.lookup(builder.getBlock());
2623 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2628 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2630 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2634 void FIRRTLLowering::addIfProceduralBlock(Value cond,
2635 std::function<
void(
void)> thenCtor,
2636 std::function<
void(
void)> elseCtor) {
2639 auto insertIt = builder.getInsertionPoint();
2640 if (insertIt != builder.getBlock()->begin())
2641 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2642 if (ifOp.getCond() == cond) {
2643 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2644 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2649 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2661 FIRRTLLowering::UnloweredOpResult
2662 FIRRTLLowering::handleUnloweredOp(Operation *op) {
2667 if (!isa<FIRRTLDialect>(op->getDialect())) {
2668 for (
auto &operand : op->getOpOperands())
2669 if (
auto lowered = getPossiblyInoutLoweredValue(operand.get()))
2670 operand.set(lowered);
2671 for (
auto result : op->getResults())
2672 (void)setLowering(result, result);
2673 return AlreadyLowered;
2685 if (op->getNumResults() == 1) {
2686 auto resultType = op->getResult(0).getType();
2687 if (type_isa<FIRRTLBaseType>(resultType) &&
2689 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2691 (void)setLowering(op->getResult(0), Value());
2695 op->emitOpError(
"LowerToHW couldn't handle this operation");
2696 return LoweringFailure;
2699 LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2702 return setLowering(op, Value());
2704 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2707 LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2709 if (isa<ClockType>(op.getType())) {
2710 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2711 : seq::ClockConst::Low);
2713 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
2715 return setLowering(op, cst);
2718 FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2719 auto iIdx = getOrCreateIntConstant(
2721 firrtl::type_cast<FVectorType>(op.getInput().getType())
2728 if (isa<sv::InOutType>(input.getType()))
2729 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2732 if (
auto *definingOp = result.getDefiningOp())
2737 FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2738 Value valueIdx = getLoweredAndExtOrTruncValue(
2742 firrtl::type_cast<FVectorType>(op.getInput().getType())
2743 .getNumElements())));
2745 op->emitError() <<
"input lowering failed";
2752 if (isa<sv::InOutType>(input.getType()))
2753 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2755 result = createArrayIndexing(input, valueIdx);
2756 if (
auto *definingOp = result.getDefiningOp())
2761 FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2762 auto resultType =
lowerType(op->getResult(0).getType());
2763 if (!resultType || !input) {
2764 op->emitError() <<
"subfield type lowering failed";
2770 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2771 .getElementName(op.getFieldIndex());
2773 if (isa<sv::InOutType>(input.getType()))
2774 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2777 if (
auto *definingOp = result.getDefiningOp())
2782 LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2784 return setLowering(op, Value());
2786 auto input = getPossiblyInoutLoweredValue(op.getInput());
2788 return op.emitError() <<
"input lowering failed";
2790 auto result = lowerSubindex(op, input);
2793 return setLowering(op, *result);
2796 LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2798 return setLowering(op, Value());
2800 auto input = getPossiblyInoutLoweredValue(op.getInput());
2802 return op.emitError() <<
"input lowering failed";
2804 auto result = lowerSubaccess(op, input);
2807 return setLowering(op, *result);
2810 LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2813 if (getLoweredValue(op) || !op.getInput())
2817 return setLowering(op, Value());
2819 auto input = getPossiblyInoutLoweredValue(op.getInput());
2821 return op.emitError() <<
"input lowering failed";
2823 auto result = lowerSubfield(op, input);
2826 return setLowering(op, *result);
2829 LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2830 auto resultType =
lowerType(op.getResult().getType());
2831 SmallVector<Value> operands;
2833 for (
auto oper : llvm::reverse(op.getOperands())) {
2834 auto val = getLoweredValue(oper);
2837 operands.push_back(val);
2839 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2842 LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2843 auto resultType =
lowerType(op.getResult().getType());
2844 SmallVector<Value> operands;
2845 for (
auto oper : op.getOperands()) {
2846 auto val = getLoweredValue(oper);
2849 operands.push_back(val);
2851 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2854 LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2857 return setLowering(op, Value());
2859 auto input = getLoweredValue(op.getInput());
2860 auto tagName = op.getFieldNameAttr();
2863 if (
auto structType = dyn_cast<hw::StructType>(type)) {
2864 auto enumType = structType.getFieldType(
"tag");
2866 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
2867 auto unionType = structType.getFieldType(
"body");
2868 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2869 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2870 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2873 return setLoweringTo<hw::EnumConstantOp>(
2877 LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2878 auto resultType =
lowerType(op.getResult().getType());
2880 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2882 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2883 cast<ArrayAttr>(attr));
2886 LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2887 auto tagName = op.getFieldNameAttr();
2888 auto lhs = getLoweredValue(op.getInput());
2889 if (isa<hw::StructType>(lhs.getType()))
2892 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
2893 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2896 LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2899 return setLowering(op, Value());
2901 auto tagName = op.getFieldNameAttr();
2902 auto input = getLoweredValue(op.getInput());
2904 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2911 LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
2912 auto origResultType = op.getResult().getType();
2916 if (!type_isa<FIRRTLType>(origResultType)) {
2917 createBackedge(op.getResult(), origResultType);
2921 auto resultType =
lowerType(origResultType);
2925 if (resultType.isInteger(0)) {
2926 if (op.getInnerSym())
2927 return op.emitError(
"zero width wire is referenced by name [")
2928 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
2929 return setLowering(op.getResult(), Value());
2933 auto innerSym = lowerInnerSymbol(op);
2934 auto name = op.getNameAttr();
2937 auto wire = builder.create<hw::WireOp>(
2938 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
2943 return setLowering(op.getResult(), wire);
2946 LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
2947 auto resultTy =
lowerType(op.getType());
2952 SmallVector<Value, 4> operands;
2953 operands.reserve(op.getSubstitutions().size());
2954 for (
auto operand : op.getSubstitutions()) {
2955 auto lowered = getLoweredValue(operand);
2958 operands.push_back(lowered);
2961 ArrayAttr symbols = op.getSymbolsAttr();
2965 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
2969 LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
2970 auto operand = getLoweredValue(op.getInput());
2972 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
2973 if (op.getInnerSym())
2974 return op.emitError(
"zero width node is referenced by name [")
2975 << *op.getInnerSym()
2976 <<
"] (e.g. in an XMR) but must be "
2978 return setLowering(op.getResult(), Value());
2984 auto name = op.getNameAttr();
2985 auto innerSym = lowerInnerSymbol(op);
2988 operand = builder.create<hw::WireOp>(operand, name, innerSym);
2993 operand = builder.create<hw::WireOp>(operand, name);
2997 return setLowering(op.getResult(), operand);
3000 LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3001 auto resultType =
lowerType(op.getResult().getType());
3004 if (resultType.isInteger(0))
3005 return setLowering(op.getResult(), Value());
3007 Value clockVal = getLoweredValue(op.getClockVal());
3012 auto innerSym = lowerInnerSymbol(op);
3013 Backedge inputEdge = backedgeBuilder.
get(resultType);
3014 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3015 op.getNameAttr(), innerSym);
3018 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3019 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3020 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3021 reg->setAttr(
"firrtl.random_init_start", randomStart);
3022 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3023 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3029 inputEdge.setValue(
reg);
3030 (void)setLowering(op.getResult(),
reg);
3034 LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3035 auto resultType =
lowerType(op.getResult().getType());
3038 if (resultType.isInteger(0))
3039 return setLowering(op.getResult(), Value());
3041 Value clockVal = getLoweredValue(op.getClockVal());
3042 Value resetSignal = getLoweredValue(op.getResetSignal());
3044 Value resetValue = getLoweredAndExtOrTruncValue(
3045 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3047 if (!clockVal || !resetSignal || !resetValue)
3051 auto innerSym = lowerInnerSymbol(op);
3052 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3053 Backedge inputEdge = backedgeBuilder.
get(resultType);
3055 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3056 resetSignal, resetValue, innerSym, isAsync);
3059 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3060 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3061 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3062 reg->setAttr(
"firrtl.random_init_start", randomStart);
3063 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3064 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3070 inputEdge.setValue(
reg);
3071 (void)setLowering(op.getResult(),
reg);
3076 LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3079 if (type_isa<BundleType>(op.getDataType()))
3080 return op.emitOpError(
3081 "should have already been lowered from a ground type to an aggregate "
3082 "type using the LowerTypes pass. Use "
3083 "'firtool --lower-types' or 'circt-opt "
3084 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3093 : std::optional<uint32_t>());
3095 seq::FirMemInitAttr memInit;
3096 if (
auto init = op.getInitAttr())
3098 init.getIsBinary(), init.getIsInline());
3100 auto memDecl = builder.create<seq::FirMemOp>(
3103 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3107 if (!circuitState.isInDUT(theModule))
3108 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3109 memDecl.setOutputFileAttr(testBenchDir);
3113 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3115 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3118 (void)setLowering(a, value);
3124 auto addInput = [&](StringRef field, Value backedge) {
3126 if (cast<FIRRTLBaseType>(a.getType())
3128 .getBitWidthOrSentinel() > 0)
3129 (
void)setLowering(a, backedge);
3135 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3139 Value backedge, portValue;
3141 portValue = getOrCreateXConstant(1);
3144 backedge = portValue = createBackedge(builder.getLoc(), portType);
3146 addInput(field, backedge);
3150 auto addClock = [&](StringRef field) -> Value {
3152 Value portValue = createBackedge(builder.getLoc(), clockTy);
3153 addInput(field, portValue);
3157 auto memportKind = op.getPortKind(i);
3158 if (memportKind == MemOp::PortKind::Read) {
3159 auto addr = addInputPort(
"addr", op.getAddrBits());
3160 auto en = addInputPort(
"en", 1);
3161 auto clk = addClock(
"clk");
3162 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3163 addOutput(
"data", memSummary.
dataWidth, data);
3164 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3165 auto addr = addInputPort(
"addr", op.getAddrBits());
3166 auto en = addInputPort(
"en", 1);
3167 auto clk = addClock(
"clk");
3170 auto mode = addInputPort(
"wmode", 1);
3172 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3179 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3181 addOutput(
"rdata", memSummary.
dataWidth, rdata);
3183 auto addr = addInputPort(
"addr", op.getAddrBits());
3186 auto en = addInputPort(
"en", 1);
3190 auto clk = addClock(
"clk");
3203 LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3204 Operation *oldModule =
3205 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3207 auto newModule = circuitState.getNewModule(oldModule);
3209 oldInstance->emitOpError(
"could not find module [")
3210 << oldInstance.getModuleName() <<
"] referenced by instance";
3216 ArrayAttr parameters;
3217 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3222 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3227 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3228 portIndicesByName[portInfo[portIdx].name] = portIdx;
3232 SmallVector<Value, 8> operands;
3233 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3234 auto &port = portInfo[portIndex];
3237 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3242 if (portType.isInteger(0))
3246 if (port.isOutput())
3249 auto portResult = oldInstance.getResult(portIndex);
3250 assert(portResult &&
"invalid IR, couldn't find port");
3254 if (port.isInput()) {
3255 operands.push_back(createBackedge(portResult, portType));
3261 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3262 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3264 auto loweredResult = getPossiblyInoutLoweredValue(source);
3265 operands.push_back(loweredResult);
3266 (void)setLowering(portResult, loweredResult);
3275 portType,
"." + port.getName().str() +
".wire");
3279 (void)setLowering(portResult, wire);
3281 operands.push_back(wire);
3288 auto innerSym = oldInstance.getInnerSymAttr();
3289 if (oldInstance.getLowerToBind()) {
3292 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3293 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
3295 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3296 innerSym.getSymName());
3299 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3300 bindOp->setAttr(
"output_file", outputFile);
3303 circuitState.addBind(bindOp);
3307 auto newInstance = builder.create<hw::InstanceOp>(
3308 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3310 if (oldInstance.getLowerToBind())
3311 newInstance->setAttr(
"doNotPrint", builder.getBoolAttr(
true));
3313 if (newInstance.getInnerSymAttr())
3314 if (
auto forceName = circuitState.instanceForceNames.lookup(
3315 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3316 newInstance.getInnerNameAttr()}))
3317 newInstance->setAttr(
"hw.verilogName", forceName);
3321 unsigned resultNo = 0;
3322 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3323 auto &port = portInfo[portIndex];
3327 Value resultVal = newInstance.getResult(resultNo);
3329 auto oldPortResult = oldInstance.getResult(portIndex);
3330 (void)setLowering(oldPortResult, resultVal);
3341 LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3342 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3347 return setLowering(op->getResult(0), operand);
3350 LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3351 if (isa<ClockType>(op.getInput().getType()))
3352 return setLowering(op->getResult(0),
3353 getLoweredNonClockValue(op.getInput()));
3354 return lowerNoopCast(op);
3357 LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3358 if (isa<ClockType>(op.getInput().getType()))
3359 return setLowering(op->getResult(0),
3360 getLoweredNonClockValue(op.getInput()));
3361 return lowerNoopCast(op);
3364 LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3365 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3368 LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3369 mlir::UnrealizedConversionCastOp op) {
3371 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3374 auto operand = op.getOperand(0);
3375 auto result = op.getResult(0);
3378 if (type_isa<FIRRTLType>(operand.getType()) &&
3379 type_isa<FIRRTLType>(result.getType()))
3380 return lowerNoopCast(op);
3384 if (!type_isa<FIRRTLType>(operand.getType())) {
3385 if (type_isa<FIRRTLType>(result.getType()))
3386 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3392 auto lowered_result = getLoweredValue(operand);
3393 if (!lowered_result) {
3396 if (operand.getType().isSignlessInteger(0)) {
3397 return setLowering(result, Value());
3404 result.replaceAllUsesWith(lowered_result);
3408 LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3411 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3412 return setLowering(op, op.getOperand());
3416 auto result = getLoweredValue(op.getOperand());
3422 op.replaceAllUsesWith(result);
3426 LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3427 auto operand = getLoweredValue(op.getOperand());
3430 auto resultType =
lowerType(op.getType());
3434 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3437 LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3438 auto operand = getLoweredValue(op.getOperand());
3442 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3443 return setLowering(op, getOrCreateIntConstant(1, 0));
3445 return setLowering(op, Value());
3450 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3451 return setLowering(op, operand);
3454 auto zero = getOrCreateIntConstant(1, 0);
3455 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3458 LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3459 auto operand = getLoweredValue(op.getInput());
3463 auto allOnes = getOrCreateIntConstant(
3464 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3465 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3468 LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3471 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3475 auto resultType =
lowerType(op.getType());
3477 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3478 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3482 LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3483 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3486 return setLowering(op, operand);
3489 LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3490 auto operand = getLoweredValue(op.getInput());
3493 return setLowering(op, getOrCreateIntConstant(1, 0));
3498 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3502 LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3503 auto operand = getLoweredValue(op.getInput());
3506 return setLowering(op, getOrCreateIntConstant(1, 1));
3511 return setLoweringTo<comb::ICmpOp>(
3512 op, ICmpPredicate::eq, operand,
3513 getOrCreateIntConstant(
3514 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3518 LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3519 auto operand = getLoweredValue(op.getInput());
3522 return setLowering(op, getOrCreateIntConstant(1, 0));
3528 return setLoweringTo<comb::ICmpOp>(
3529 op, ICmpPredicate::ne, operand,
3530 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3538 template <
typename ResultOpType>
3539 LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3540 auto resultType = op->getResult(0).getType();
3541 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3542 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3546 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3552 template <
typename ResultOpType>
3553 LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3554 auto resultType = op->getResult(0).getType();
3555 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3556 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3567 auto intType = builder.getIntegerType(*bitwidth);
3568 auto retType = lhs.getType();
3571 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3572 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3577 template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3578 LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3580 auto resultType = op->getResult(0).getType();
3581 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3582 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3587 if (type_cast<IntType>(resultType).isSigned())
3588 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3589 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3594 LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3595 ICmpPredicate unsignedOp) {
3597 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3598 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3599 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3603 if (cmpType.getWidth() == 0)
3605 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3606 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3611 Type resultType = builder.getIntegerType(1);
3612 return setLoweringTo<comb::ICmpOp>(
3613 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3619 template <
typename SignedOp,
typename Un
signedOp>
3620 LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3624 auto opType = type_cast<IntType>(op->getResult(0).getType());
3625 if (opType.getWidth() == 0)
3626 return setLowering(op->getResult(0), Value());
3630 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3631 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3636 if (opType.isSigned())
3637 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
3639 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
3641 if (
auto *definingOp = result.getDefiningOp())
3644 if (resultType == opType)
3645 return setLowering(op->getResult(0), result);
3646 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
3649 LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3650 auto lhs = getLoweredValue(op.getLhs());
3651 auto rhs = getLoweredValue(op.getRhs());
3655 return setLowering(op, rhs);
3657 return handleZeroBit(op.getRhs(),
3658 [&]() { return setLowering(op, Value()); });
3663 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3665 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3672 LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3673 auto input = getLoweredNonClockValue(op.getArg());
3677 if (!isa<IntType>(input.getType())) {
3678 auto srcType = op.getArg().getType();
3680 assert(bitwidth &&
"Unknown width");
3681 auto intType = builder.getIntegerType(*bitwidth);
3682 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
3685 return setLoweringTo<comb::ICmpOp>(
3686 op, ICmpPredicate::ceq, input,
3687 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
3690 LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
3691 auto operand = getLoweredValue(op.getInput());
3692 builder.create<hw::WireOp>(operand);
3696 LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3697 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
3698 op.getFormatStringAttr());
3701 LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3702 auto type =
lowerType(op.getResult().getType());
3706 auto valueOp = builder.create<sim::PlusArgsValueOp>(
3707 builder.getIntegerType(1), type, op.getFormatStringAttr());
3708 if (failed(setLowering(op.getResult(), valueOp.getResult())))
3710 if (failed(setLowering(op.getFound(), valueOp.getFound())))
3715 LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3716 op.emitError(
"SizeOf should have been resolved.");
3720 LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3722 if (op.getTestEnable())
3723 testEnable = getLoweredValue(op.getTestEnable());
3724 return setLoweringTo<seq::ClockGateOp>(
3725 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3726 testEnable, hw::InnerSymAttr{});
3729 LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
3730 auto operand = getLoweredValue(op.getInput());
3731 return setLoweringTo<seq::ClockInverterOp>(op, operand);
3734 LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
3735 auto operand = getLoweredValue(op.getInput());
3736 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
3739 LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3740 return setLoweringToLTL<ltl::AndOp>(
3742 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3745 LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3746 return setLoweringToLTL<ltl::OrOp>(
3748 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3751 LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
3752 return setLoweringToLTL<ltl::IntersectOp>(
3754 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3757 LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3758 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3759 op.getDelayAttr(), op.getLengthAttr());
3762 LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3763 return setLoweringToLTL<ltl::ConcatOp>(
3765 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3768 LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
3769 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
3770 op.getBaseAttr(), op.getMoreAttr());
3773 LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
3774 return setLoweringToLTL<ltl::GoToRepeatOp>(
3775 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3778 LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
3779 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
3780 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3783 LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3784 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3787 LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3788 return setLoweringToLTL<ltl::ImplicationOp>(
3790 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3793 LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
3794 return setLoweringToLTL<ltl::UntilOp>(
3796 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3799 LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3800 return setLoweringToLTL<ltl::EventuallyOp>(op,
3801 getLoweredValue(op.getInput()));
3804 LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3805 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3806 ltl::ClockEdge::Pos,
3807 getLoweredNonClockValue(op.getClock()));
3810 template <
typename TargetOp,
typename IntrinsicOp>
3811 LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
3812 auto property = getLoweredValue(op.getProperty());
3813 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
3814 builder.create<TargetOp>(property, enable, op.getLabelAttr());
3818 LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3819 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3822 LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3823 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
3826 LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3827 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
3830 LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3831 auto clock = getLoweredNonClockValue(op.getClock());
3832 auto reset = getLoweredValue(op.getReset());
3833 if (!clock || !reset)
3835 auto resetType = op.getReset().getType();
3836 auto uintResetType = dyn_cast<UIntType>(resetType);
3837 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3838 auto isAsync = isa<AsyncResetType>(resetType);
3839 if (!isAsync && !isSync) {
3840 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
3841 "requires sync or async reset");
3842 d.attachNote() <<
"reset is of type " << resetType
3843 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3846 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3853 LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3854 auto input = getLoweredValue(op.getInput());
3858 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
3859 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3862 LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3863 auto resultTy =
lowerType(op.getType());
3870 if (type_isa<AnalogType>(op.getType()))
3873 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
3876 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
3887 auto constant = getOrCreateIntConstant(*bitwidth, 0);
3889 if (!type_isa<IntegerType>(resultTy))
3890 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
3891 return setLowering(op, constant);
3895 op.emitOpError(
"unsupported type");
3899 LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
3900 auto input = getLoweredValue(op.getInput());
3903 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3904 if (op.getAmount() == 0)
3905 return setLowering(op, Value());
3906 Type resultType = builder.getIntegerType(op.getAmount());
3907 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
3908 inWidth - op.getAmount());
3911 LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
3912 auto input = getLoweredValue(op.getInput());
3915 if (op.getAmount() == 0)
3917 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
3922 if (op.getAmount() == 0)
3923 return setLowering(op, input);
3925 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
3926 return setLoweringTo<comb::ConcatOp>(op, input, zero);
3929 LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
3930 auto input = getLoweredValue(op.getInput());
3935 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3936 auto shiftAmount = op.getAmount();
3937 if (shiftAmount >= inWidth) {
3939 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
3940 return setLowering(op, {});
3943 shiftAmount = inWidth - 1;
3946 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
3947 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
3950 LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
3951 auto input = getLoweredValue(op.getInput());
3955 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3956 if (inWidth == op.getAmount())
3957 return setLowering(op, Value());
3958 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
3959 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
3962 LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
3963 auto cond = getLoweredValue(op.getSel());
3964 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3965 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3966 if (!cond || !ifTrue || !ifFalse)
3969 if (isa<ClockType>(op.getType()))
3970 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
3971 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
3975 LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
3976 auto cond = getLoweredValue(op.getSel());
3977 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
3978 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
3979 if (!cond || !ifTrue || !ifFalse)
3982 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
3984 return setLowering(op, createValueWithMuxAnnotation(val,
true));
3987 LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
3988 auto sel = getLoweredValue(op.getSel());
3989 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
3990 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
3991 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
3992 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
3993 if (!sel || !v3 || !v2 || !v1 || !v0)
3995 Value array[] = {v3, v2, v1, v0};
3998 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4017 Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4018 assert(op->getNumResults() == 1 &&
"only expect a single result");
4019 auto val = op->getResult(0);
4030 OpBuilder::InsertionGuard guard(builder);
4031 builder.setInsertionPoint(op);
4032 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4033 for (
auto [idx, operand] : llvm::enumerate(op->getOperands())) {
4035 op->getContext(),
nullptr, 0,
4036 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4038 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4039 op->setOperand(idx, wire);
4043 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4046 "synopsys infer_mux_override",
4051 Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4053 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4058 if (!llvm::isPowerOf2_64(size)) {
4059 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4061 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4063 Value temp2[] = {ext.getResult(), array};
4069 return inBoundsRead;
4072 LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4074 auto index = getLoweredAndExtOrTruncValue(
4081 SmallVector<Value> loweredInputs;
4082 loweredInputs.reserve(op.getInputs().size());
4083 for (
auto input : op.getInputs()) {
4084 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4087 loweredInputs.push_back(lowered);
4091 return setLowering(op, createArrayIndexing(array, index));
4094 LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4095 auto resultTy =
lowerType(op.getType());
4099 SmallVector<Value, 4> operands;
4100 operands.reserve(op.getSubstitutions().size());
4101 for (
auto operand : op.getSubstitutions()) {
4102 auto lowered = getLoweredValue(operand);
4105 operands.push_back(lowered);
4108 ArrayAttr symbols = op.getSymbolsAttr();
4112 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4116 LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4120 Type baseType = op.getType().getType();
4123 if (isa<ClockType>(baseType))
4124 xmrType = builder.getIntegerType(1);
4129 op.getRef(), op.getVerbatimSuffixAttr());
4132 LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4136 if (isa<ClockType>(op.getType()))
4137 xmrType = builder.getIntegerType(1);
4141 auto xmr = builder.create<sv::XMRRefOp>(
4143 auto readXmr = getReadValue(xmr);
4144 if (!isa<ClockType>(op.getType()))
4145 return setLowering(op, readXmr);
4146 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4153 LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4165 FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4166 auto srcType = srcVal.getType();
4167 auto dstType = destVal.getType();
4168 if (srcType != dstType &&
4169 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4170 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4172 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4173 .Case<hw::WireOp>([&](
auto op) {
4174 maybeUnused(op.getInput());
4175 op.getInputMutable().assign(srcVal);
4178 .Case<seq::FirRegOp>([&](
auto op) {
4179 maybeUnused(op.getNext());
4180 op.getNextMutable().assign(srcVal);
4183 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4186 op.emitOpError(
"used as connect destination");
4189 .Default([](
auto) {
return false; });
4192 LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4193 auto dest = op.getDest();
4195 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4196 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4198 return handleZeroBit(op.getSrc(), []() { return success(); });
4200 auto destVal = getPossiblyInoutLoweredValue(dest);
4204 auto result = lowerConnect(destVal, srcVal);
4212 if (updateIfBackedge(destVal, srcVal))
4215 if (!isa<hw::InOutType>(destVal.getType()))
4216 return op.emitError(
"destination isn't an inout type");
4222 LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4223 auto dest = op.getDest();
4224 auto srcVal = getLoweredValue(op.getSrc());
4226 return handleZeroBit(op.getSrc(), []() { return success(); });
4228 auto destVal = getPossiblyInoutLoweredValue(dest);
4232 auto result = lowerConnect(destVal, srcVal);
4240 if (updateIfBackedge(destVal, srcVal))
4243 if (!isa<hw::InOutType>(destVal.getType()))
4244 return op.emitError(
"destination isn't an inout type");
4250 LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4251 auto srcVal = getLoweredValue(op.getSrc());
4255 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4259 if (!isa<hw::InOutType>(destVal.getType()))
4260 return op.emitError(
"destination isn't an inout type");
4263 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4264 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4265 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4270 LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4271 auto src = getLoweredNonClockValue(op.getSrc());
4272 auto clock = getLoweredNonClockValue(op.getClock());
4273 auto pred = getLoweredValue(op.getPredicate());
4274 if (!src || !clock || !pred)
4277 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4282 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4283 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4284 addToAlwaysBlock(clock, [&]() {
4285 addIfProceduralBlock(
4286 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4291 LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4292 auto src = getLoweredNonClockValue(op.getSrc());
4293 auto pred = getLoweredValue(op.getPredicate());
4297 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4302 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4303 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4304 addToInitialBlock([&]() {
4305 addIfProceduralBlock(
4306 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4311 LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4312 auto clock = getLoweredNonClockValue(op.getClock());
4313 auto pred = getLoweredValue(op.getPredicate());
4314 if (!clock || !pred)
4317 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4322 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4323 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4324 addToAlwaysBlock(clock, [&]() {
4325 addIfProceduralBlock(pred,
4326 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4331 LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4332 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4333 auto pred = getLoweredValue(op.getPredicate());
4334 if (!destVal || !pred)
4338 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4339 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4340 addToInitialBlock([&]() {
4341 addIfProceduralBlock(pred,
4342 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4350 LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4351 auto clock = getLoweredNonClockValue(op.getClock());
4352 auto cond = getLoweredValue(op.getCond());
4353 if (!clock || !cond)
4356 SmallVector<Value, 4> operands;
4357 operands.reserve(op.getSubstitutions().size());
4358 for (
auto operand : op.getSubstitutions()) {
4359 Value loweredValue = getLoweredFmtOperand(operand);
4362 operands.push_back(loweredValue);
4366 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4367 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4368 addToAlwaysBlock(clock, [&]() {
4369 circuitState.usedPrintfCond =
true;
4370 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
4374 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
4375 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
4377 addIfProceduralBlock(ifCond, [&]() {
4379 Value fdStderr = builder.create<
hw::ConstantOp>(APInt(32, 0x80000002));
4380 builder.
create<sv::FWriteOp>(fdStderr, op.getFormatString(), operands);
4390 LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4391 auto clock = getLoweredValue(op.getClock());
4392 auto cond = getLoweredValue(op.getCond());
4393 if (!clock || !cond)
4396 circuitState.usedStopCond =
true;
4397 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4400 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4401 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4403 if (op.getExitCode())
4404 builder.create<sim::FatalOp>(clock, exitCond);
4406 builder.create<sim::FinishOp>(clock, exitCond);
4414 template <
typename... Args>
4416 StringRef opName, Args &&...args) {
4417 if (opName ==
"assert")
4418 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4419 if (opName ==
"assume")
4420 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4421 if (opName ==
"cover")
4422 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4423 llvm_unreachable(
"unknown verification op");
4429 template <
typename... Args>
4431 StringRef opName, Args &&...args) {
4432 if (opName ==
"assert")
4433 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4434 if (opName ==
"assume")
4435 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4436 if (opName ==
"cover")
4437 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4438 llvm_unreachable(
"unknown verification op");
4459 LogicalResult FIRRTLLowering::lowerVerificationStatement(
4460 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4461 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4462 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
4463 StringRef opName = op->getName().stripDialect();
4466 ArrayRef<Attribute> guards{};
4467 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
4468 guards = guardsAttr.getValue();
4470 auto isCover = isa<CoverOp>(op);
4471 auto clock = getLoweredNonClockValue(opClock);
4472 auto enable = getLoweredValue(opEnable);
4473 auto predicate = getLoweredValue(opPredicate);
4474 if (!clock || !enable || !predicate)
4478 if (opNameAttr && !opNameAttr.getValue().empty())
4480 StringAttr prefixedLabel;
4483 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
4486 SmallVector<Value> messageOps;
4490 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
4491 flavor = VerificationFlavor::None;
4493 if (flavor == VerificationFlavor::None) {
4497 auto format = op->getAttrOfType<StringAttr>(
"format");
4499 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
4500 if (!isa<AssertOp>(op))
4501 return op->emitError()
4502 <<
"ifElseFatal format cannot be used for non-assertions";
4503 flavor = VerificationFlavor::IfElseFatal;
4504 }
else if (isConcurrent)
4505 flavor = VerificationFlavor::SVA;
4507 flavor = VerificationFlavor::Immediate;
4510 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4511 message = opMessageAttr;
4512 for (
auto operand : opOperands) {
4513 auto loweredValue = getLoweredFmtOperand(operand);
4521 if (flavor == VerificationFlavor::SVA)
4522 loweredValue = builder.create<sv::SampledOp>(loweredValue);
4523 messageOps.push_back(loweredValue);
4529 case VerificationFlavor::Immediate: {
4532 builder.getContext(), circt::sv::DeferAssert::Immediate);
4533 addToAlwaysBlock(clock, [&]() {
4534 addIfProceduralBlock(enable, [&]() {
4536 prefixedLabel, message, messageOps);
4541 case VerificationFlavor::IfElseFatal: {
4542 assert(isa<AssertOp>(op) &&
"only assert is expected");
4547 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4549 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4550 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
4551 addToAlwaysBlock(clock, [&]() {
4552 addIfProceduralBlock(predicate, [&]() {
4553 circuitState.usedStopCond =
true;
4554 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4556 circuitState.usedAssertVerboseCond =
true;
4557 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
4559 addIfProceduralBlock(
4560 builder.create<sv::MacroRefExprOp>(boolType,
4561 "ASSERT_VERBOSE_COND_"),
4562 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4563 addIfProceduralBlock(
4564 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
4565 [&]() { builder.create<sv::FatalOp>(); });
4571 case VerificationFlavor::SVA: {
4578 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4580 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4584 sv::EventControl event;
4585 switch (opEventControl) {
4586 case EventControl::AtPosEdge:
4587 event = circt::sv::EventControl::AtPosEdge;
4589 case EventControl::AtEdge:
4590 event = circt::sv::EventControl::AtEdge;
4592 case EventControl::AtNegEdge:
4593 event = circt::sv::EventControl::AtNegEdge;
4600 predicate, prefixedLabel, message, messageOps);
4603 case VerificationFlavor::None:
4605 "flavor `None` must be converted into one of concreate flavors");
4612 return emitGuards(op->getLoc(), guards,
emit);
4616 LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4617 return lowerVerificationStatement(
4618 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4619 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4620 op.getIsConcurrent(), op.getEventControl());
4624 LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4625 return lowerVerificationStatement(
4626 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4627 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4628 op.getIsConcurrent(), op.getEventControl());
4632 LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4633 return lowerVerificationStatement(
4634 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4635 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4636 op.getIsConcurrent(), op.getEventControl());
4640 LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
4645 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
4646 ArrayRef<Attribute> guards =
4647 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
4649 auto label = op.getNameAttr();
4650 StringAttr assumeLabel;
4651 if (label && !label.empty())
4654 auto predicate = getLoweredValue(op.getPredicate());
4655 auto enable = getLoweredValue(op.getEnable());
4657 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4659 SmallVector<Value> messageOps;
4660 for (
auto operand : op.getSubstitutions()) {
4661 auto loweredValue = getLoweredValue(operand);
4662 if (!loweredValue) {
4666 loweredValue = getOrCreateIntConstant(1, 0);
4668 messageOps.push_back(loweredValue);
4670 return emitGuards(op.getLoc(), guards, [&]() {
4671 builder.create<sv::AlwaysOp>(
4672 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4673 if (op.getMessageAttr().getValue().empty())
4674 buildImmediateVerifOp(
4675 builder,
"assume", predicate,
4676 circt::sv::DeferAssertAttr::get(
4677 builder.getContext(), circt::sv::DeferAssert::Immediate),
4680 buildImmediateVerifOp(
4681 builder,
"assume", predicate,
4682 circt::sv::DeferAssertAttr::get(
4683 builder.getContext(), circt::sv::DeferAssert::Immediate),
4684 assumeLabel, op.getMessageAttr(), messageOps);
4689 LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4691 if (op.getAttached().size() < 2)
4694 SmallVector<Value, 4> inoutValues;
4695 for (
auto v : op.getAttached()) {
4696 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4697 if (!inoutValues.back()) {
4701 inoutValues.pop_back();
4705 if (!isa<hw::InOutType>(inoutValues.back().getType()))
4706 return op.emitError(
"operand isn't an inout type");
4709 if (inoutValues.size() < 2)
4720 bool isAttachInternalOnly =
4721 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
4723 if (isAttachInternalOnly) {
4724 auto v0 = inoutValues.front();
4725 for (
auto v : inoutValues) {
4728 v.replaceAllUsesWith(v0);
4735 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4736 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
4741 SmallVector<Value, 4> values;
4742 for (
size_t i = 0, e = inoutValues.size(); i != e; ++i)
4743 values.push_back(getReadValue(inoutValues[i]));
4745 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4746 for (size_t i2 = 0; i2 != e; ++i2)
4748 builder.create<sv::AssignOp>(inoutValues[i1], values[i2]);
4757 builder.create<sv::VerbatimOp>(
4758 "`error \"Verilator does not support alias and thus "
4760 "arbitrarily connect bidirectional wires and ports\"");
4762 [&]() { builder.create<sv::AliasOp>(inoutValues); });
4768 LogicalResult FIRRTLLowering::fixupLTLOps() {
4769 if (ltlOpFixupWorklist.empty())
4771 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
4775 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4776 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
4777 if (isa<hw::WireOp>(user))
4778 ltlOpFixupWorklist.insert(user);
4781 while (!ltlOpFixupWorklist.empty()) {
4782 auto *op = ltlOpFixupWorklist.pop_back_val();
4785 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4786 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
4787 SmallVector<Type, 2> types;
4788 auto result = opIntf.inferReturnTypes(
4789 op->getContext(), op->getLoc(), op->getOperands(),
4790 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4794 assert(types.size() == op->getNumResults());
4798 for (
auto [result, type] : llvm::zip(op->getResults(), types)) {
4799 if (result.getType() == type)
4801 LLVM_DEBUG(llvm::dbgs()
4802 <<
" - Result #" << result.getResultNumber() <<
" from "
4803 << result.getType() <<
" to " << type <<
"\n");
4804 result.setType(type);
4805 for (
auto *user : result.getUsers())
4807 ltlOpFixupWorklist.insert(user);
4812 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
4813 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4814 wireOp.replaceAllUsesWith(wireOp.getInput());
4815 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
4816 if (wireOp.use_empty())
4823 SmallPtrSet<Operation *, 4> usersReported;
4824 for (
auto *user : op->getUsers()) {
4825 if (!usersReported.insert(user).second)
4827 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4829 if (isa<hw::WireOp>(user))
4831 auto d = op->emitError(
4832 "verification operation used in a non-verification context");
4833 d.attachNote(user->getLoc())
4834 <<
"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.
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
This is an edge in the InstanceGraph.
def create(array_value, idx)
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Value createOrFoldSExt(Location loc, Value value, Type destTy, OpBuilder &builder)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
constexpr const char * extractCoverageAnnoClass
constexpr const char * elaborationArtefactsDirectoryAnnoClass
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
constexpr const char * extractGrandCentralClass
constexpr const char * blackBoxAnnoClass
constexpr const char * dontObfuscateModuleAnnoClass
constexpr const char * metadataDirectoryAttrName
constexpr const char * testBenchDirAnnoClass
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
constexpr const char * dutAnnoClass
constexpr const char * forceNameAnnoClass
constexpr const char * noDedupAnnoClass
mlir::Type getPassiveType(mlir::Type anyBaseFIRRTLType)
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
constexpr const char * extractAssertAnnoClass
constexpr const char * verifBlackBoxAnnoClass
constexpr const char * blackBoxTargetDirAnnoClass
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
constexpr const char * extractAssumeAnnoClass
constexpr const char * testHarnessHierAnnoClass
constexpr const char * moduleHierAnnoClass
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
mlir::ArrayAttr getSVAttributes(mlir::Operation *op)
Return all the SV attributes of an operation, or null if there are none.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
AnnotationSet annotations
bool isInput() const
Return true if this is a simple input-only port.