35#include "mlir/IR/BuiltinOps.h"
36#include "mlir/IR/BuiltinTypes.h"
37#include "mlir/IR/ImplicitLocOpBuilder.h"
38#include "mlir/IR/Threading.h"
39#include "mlir/Pass/Pass.h"
40#include "llvm/Support/Debug.h"
41#include "llvm/Support/Mutex.h"
43#define DEBUG_TYPE "lower-to-hw"
46#define GEN_PASS_DEF_LOWERFIRRTLTOHW
47#include "circt/Conversion/Passes.h.inc"
51using namespace firrtl;
52using circt::comb::ICmpPredicate;
61 auto ftype = dyn_cast<FIRRTLBaseType>(type);
62 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
69 for (
auto operand : op.getAttached()) {
71 operand.getDefiningOp<InstanceOp>())
75 if (!operand.hasOneUse() || singleSource)
77 singleSource = operand;
86 auto checkTypes = [](Operation *op) -> WalkResult {
88 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
89 return op->emitError(
"Found unhandled FIRRTL operation '")
90 << op->getName() <<
"'";
93 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
94 if (llvm::any_of(types, [](Type type) {
95 return isa<FIRRTLDialect>(type.getDialect());
97 return op->emitOpError(
"found unhandled FIRRTL type");
102 if (failed(checkTypeRange(op->getOperandTypes())) ||
103 failed(checkTypeRange(op->getResultTypes())))
104 return WalkResult::interrupt();
107 for (
auto ®ion : op->getRegions())
108 for (
auto &block : region)
109 if (failed(checkTypeRange(block.getArgumentTypes())))
110 return WalkResult::interrupt();
113 return WalkResult::advance();
116 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
123 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
124 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
130 ImplicitLocOpBuilder &builder) {
132 if (BundleType bundle = dyn_cast<BundleType>(type))
133 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
135 if (type != val.getType())
136 val = builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(
144 ImplicitLocOpBuilder &builder) {
146 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
150 .create<mlir::UnrealizedConversionCastOp>(
151 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
153 val = builder.createOrFold<HWStructCastOp>(type, val);
158 builder.create<mlir::UnrealizedConversionCastOp>(type, val).getResult(0);
165 StringRef annoClass, StringRef attrBase) {
167 auto *ctx = top.getContext();
170 if (
auto dir = anno.getMember<StringAttr>(
"directory")) {
171 SmallVector<NamedAttribute> old;
172 for (
auto i : top->getAttrs())
175 StringAttr::get(ctx, attrBase),
176 hw::OutputFileAttr::getAsDirectory(ctx, dir.getValue(),
true,
true));
179 if (
auto file = anno.getMember<StringAttr>(
"filename")) {
180 SmallVector<NamedAttribute> old;
181 for (
auto i : top->getAttrs())
183 old.emplace_back(StringAttr::get(ctx, attrBase +
".bindfile"),
184 hw::OutputFileAttr::getFromFilename(
185 ctx, file.getValue(),
true));
191 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
197 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
198 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
199 dst->setAttr(
"sv.namehint", attr);
207struct FIRRTLModuleLowering;
210struct CircuitLoweringState {
212 std::atomic<bool> usedPrintf{
false};
213 std::atomic<bool> usedAssertVerboseCond{
false};
214 std::atomic<bool> usedStopCond{
false};
216 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
219 : circuitOp(circuitOp), instanceGraph(instanceGraph),
220 enableAnnotationWarning(enableAnnotationWarning),
221 verificationFlavor(verificationFlavor), nlaTable(nlaTable) {
222 auto *context = circuitOp.getContext();
227 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
228 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
229 context, dirName.getValue(),
false,
true);
233 if (
auto module = dyn_cast<FModuleLike>(op))
244 testHarness =
nullptr;
245 }
else if (dut == testHarness) {
246 testHarness =
nullptr;
251 auto inDUT = [&](igraph::ModuleOpInterface child) {
253 auto inst = instRec->getInstance();
254 if (
auto *finst = dyn_cast<InstanceOp>(&inst))
255 return finst->getLowerToBind();
258 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
259 return getInstanceGraph().isAncestor(child, parent, isBind);
262 circuitOp->walk([&](FModuleLike moduleOp) {
264 dutModules.insert(moduleOp);
268 Operation *getNewModule(Operation *oldModule) {
269 auto it = oldToNewModuleMap.find(oldModule);
270 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
273 Operation *getOldModule(Operation *newModule) {
274 auto it = newToOldModuleMap.find(newModule);
275 return it != newToOldModuleMap.end() ? it->second :
nullptr;
278 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
279 oldToNewModuleMap[oldFMod] = newHWMod;
280 newToOldModuleMap[newHWMod] = oldFMod;
285 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
291 void addBind(sv::BindOp op) {
292 std::lock_guard<std::mutex> lock(bindsMutex);
298 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
301 auto hwAlias = typeAliases.getTypedecl(firAliasType);
304 assert(!typeAliases.isFrozen() &&
305 "type aliases cannot be generated after its frozen");
306 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
309 FModuleLike getDut() {
return dut; }
310 FModuleLike getTestHarness() {
return testHarness; }
316 bool isInDUT(igraph::ModuleOpInterface child) {
317 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
318 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
319 return dutModules.contains(child);
322 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
327 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
334 Type
lowerType(Type type, Location loc) {
335 return ::lowerType(type, loc,
336 [&](Type rawType, BaseTypeAliasType firrtlType,
337 Location typeLoc) -> hw::TypeAliasType {
338 return getTypeAlias(rawType, firrtlType, typeLoc);
343 friend struct FIRRTLModuleLowering;
344 friend struct FIRRTLLowering;
345 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
346 void operator=(
const CircuitLoweringState &) =
delete;
349 DenseMap<Operation *, Operation *> oldToNewModuleMap;
352 DenseMap<Operation *, Operation *> newToOldModuleMap;
363 DenseSet<igraph::ModuleOpInterface> dutModules;
367 StringSet<> pendingAnnotations;
368 const bool enableAnnotationWarning;
369 std::mutex annotationPrintingMtx;
375 SmallVector<sv::BindOp> binds;
378 std::mutex bindsMutex;
386 FModuleLike testHarness;
389 hw::OutputFileAttr testBenchDirectory;
393 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
396 SetVector<StringAttr> macroDeclNames;
397 std::mutex macroDeclMutex;
399 void addMacroDecl(StringAttr name) {
400 std::unique_lock<std::mutex> lock(macroDeclMutex);
401 macroDeclNames.insert(name);
406 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
407 llvm::sys::SmartMutex<true> fragmentsMutex;
410 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
411 fragments[module].insert(
412 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
427 struct RecordTypeAlias {
429 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
431 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
432 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
433 if (iter != firrtlTypeToAliasTypeMap.end())
438 bool isFrozen() {
return frozen; }
440 void freeze() { frozen =
true; }
442 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
444 assert(!frozen &&
"Record already frozen, cannot be updated");
447 auto b = ImplicitLocOpBuilder::atBlockBegin(
449 &circuitOp->getParentRegion()->getBlocks().back());
451 b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
452 typeScope.getBodyRegion().push_back(
new Block());
454 auto typeName = firAlias.getName();
459 StringAttr::get(typeName.getContext(),
460 typeDeclNamespace.newName(typeName.getValue()));
462 auto typeScopeBuilder =
463 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
464 auto typeDecl = typeScopeBuilder.create<
hw::TypedeclOp>(typeLoc, typeName,
466 auto hwAlias = hw::TypeAliasType::get(
467 SymbolRefAttr::get(typeScope.getSymNameAttr(),
468 {FlatSymbolRefAttr::get(typeDecl)}),
470 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
471 assert(insert.second &&
"Entry already exists, insert failed");
472 return insert.first->second;
481 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
489 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
492void CircuitLoweringState::processRemainingAnnotations(
494 if (!enableAnnotationWarning || annoSet.
empty())
496 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
498 for (
auto a : annoSet) {
499 auto inserted = pendingAnnotations.insert(a.getClass());
500 if (!inserted.second)
539 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
540 "' still remaining after LowerToHW");
546struct FIRRTLModuleLowering
547 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
549 void runOnOperation()
override;
550 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
552 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
555 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
556 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
557 SmallVectorImpl<hw::PortInfo> &ports,
558 Operation *moduleOp, StringRef moduleName,
560 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
562 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
565 Block *topLevelModule,
568 Block *topLevelModule,
572 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
576 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
584 bool enableAnnotationWarning,
586 auto pass = std::make_unique<FIRRTLModuleLowering>();
587 if (enableAnnotationWarning)
588 pass->setEnableAnnotationWarning();
589 pass->verificationFlavor = verificationFlavor;
595void FIRRTLModuleLowering::runOnOperation() {
599 auto *topLevelModule = getOperation().getBody();
603 for (
auto &op : *topLevelModule) {
604 if ((circuit = dyn_cast<CircuitOp>(&op)))
611 auto *circuitBody = circuit.getBodyBlock();
615 CircuitLoweringState state(circuit, enableAnnotationWarning,
616 verificationFlavor, getAnalysis<InstanceGraph>(),
617 &getAnalysis<NLATable>());
619 SmallVector<hw::HWModuleOp, 32> modulesToProcess;
620 SmallVector<verif::FormalOp> formalOpsToProcess;
624 "firrtl.extract.assert");
626 "firrtl.extract.assume");
628 "firrtl.extract.cover");
629 circuitAnno.removeAnnotationsWithClass(
632 state.processRemainingAnnotations(circuit, circuitAnno);
635 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
637 TypeSwitch<Operation *, LogicalResult>(&op)
638 .Case<FModuleOp>([&](
auto module) {
639 auto loweredMod = lowerModule(module, topLevelModule, state);
643 state.recordModuleMapping(&op, loweredMod);
644 modulesToProcess.push_back(loweredMod);
646 module.walk([&](Operation *op) {
647 for (auto res : op->getResults()) {
649 type_dyn_cast<BaseTypeAliasType>(res.getType()))
650 state.lowerType(aliasType, op->getLoc());
653 return lowerModulePortsAndMoveBody(module, loweredMod, state);
655 .Case<FExtModuleOp>([&](
auto extModule) {
657 lowerExtModule(extModule, topLevelModule, state);
660 state.recordModuleMapping(&op, loweredMod);
663 .Case<FMemModuleOp>([&](
auto memModule) {
665 lowerMemModule(memModule, topLevelModule, state);
668 state.recordModuleMapping(&op, loweredMod);
671 .Case<FormalOp>([&](
auto oldFormalOp) {
672 auto builder = OpBuilder::atBlockEnd(topLevelModule);
673 auto newFormalOp = builder.create<verif::FormalOp>(
674 oldFormalOp.getLoc(), oldFormalOp.getNameAttr(),
675 oldFormalOp.getParametersAttr());
676 newFormalOp.getBody().emplaceBlock();
677 state.recordModuleMapping(oldFormalOp, newFormalOp);
678 formalOpsToProcess.push_back(newFormalOp);
681 .Default([&](Operation *op) {
686 op->moveBefore(topLevelModule, topLevelModule->end());
692 return signalPassFailure();
695 state.typeAliases.freeze();
700 SmallVector<Attribute> dutHierarchyFiles;
701 SmallVector<Attribute> testHarnessHierarchyFiles;
702 circuitAnno.removeAnnotations([&](
Annotation annotation) {
704 auto file = hw::OutputFileAttr::getFromFilename(
706 annotation.
getMember<StringAttr>(
"filename").getValue(),
708 dutHierarchyFiles.push_back(file);
712 auto file = hw::OutputFileAttr::getFromFilename(
714 annotation.
getMember<StringAttr>(
"filename").getValue(),
718 if (state.getTestHarness())
719 testHarnessHierarchyFiles.push_back(file);
721 dutHierarchyFiles.push_back(file);
727 if (!dutHierarchyFiles.empty())
728 state.getNewModule(state.getDut())
730 ArrayAttr::get(&getContext(), dutHierarchyFiles));
731 if (!testHarnessHierarchyFiles.empty())
732 state.getNewModule(state.getTestHarness())
734 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
737 auto result = mlir::failableParallelForEachN(
738 &getContext(), 0, modulesToProcess.size(), [&](
auto index) {
739 return lowerModuleOperations(modulesToProcess[index], state);
742 return signalPassFailure();
745 result = mlir::failableParallelForEach(
746 &getContext(), formalOpsToProcess,
747 [&](
auto op) {
return lowerFormalBody(op, state); });
749 return signalPassFailure();
752 for (
auto bind : state.binds) {
757 for (
auto &[module, fragments] : state.fragments)
759 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
762 for (
auto oldNew : state.oldToNewModuleMap)
763 oldNew.first->erase();
765 if (!state.macroDeclNames.empty()) {
766 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
767 for (
auto name : state.macroDeclNames) {
768 b.create<sv::MacroDeclOp>(name);
773 lowerFileHeader(circuit, state);
780void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
781 CircuitLoweringState &state) {
784 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
788 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
789 StringRef defineTrue =
"",
790 StringRef defineFalse = StringRef()) {
791 if (!defineFalse.data()) {
792 assert(defineTrue.data() &&
"didn't define anything");
794 guard, [&]() { b.create<sv::MacroDefOp>(defName, defineTrue); });
799 if (defineTrue.data())
800 b.create<sv::MacroDefOp>(defName, defineTrue);
802 [&]() { b.create<sv::MacroDefOp>(defName, defineFalse); });
807 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
809 guard, []() {}, body);
812 if (state.usedPrintf) {
813 b.create<sv::MacroDeclOp>(
"PRINTF_FD");
814 b.create<sv::MacroDeclOp>(
"PRINTF_FD_");
815 b.create<emit::FragmentOp>(
"PRINTF_FD_FRAGMENT", [&] {
816 b.create<sv::VerbatimOp>(
817 "\n// Users can define 'PRINTF_FD' to add a specified fd to "
819 emitGuard(
"PRINTF_FD_", [&]() {
820 emitGuardedDefine(
"PRINTF_FD",
"PRINTF_FD_",
"(`PRINTF_FD)",
825 b.create<sv::MacroDeclOp>(
"PRINTF_COND");
826 b.create<sv::MacroDeclOp>(
"PRINTF_COND_");
827 b.create<emit::FragmentOp>(
"PRINTF_COND_FRAGMENT", [&] {
828 b.create<sv::VerbatimOp>(
829 "\n// Users can define 'PRINTF_COND' to add an extra gate to "
831 emitGuard(
"PRINTF_COND_", [&]() {
832 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
837 if (state.usedAssertVerboseCond) {
838 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND");
839 b.create<sv::MacroDeclOp>(
"ASSERT_VERBOSE_COND_");
840 b.create<emit::FragmentOp>(
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
841 b.create<sv::VerbatimOp>(
842 "\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
843 "gate to assert error printing.");
844 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
845 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
846 "(`ASSERT_VERBOSE_COND)",
"1");
851 if (state.usedStopCond) {
852 b.create<sv::MacroDeclOp>(
"STOP_COND");
853 b.create<sv::MacroDeclOp>(
"STOP_COND_");
854 b.create<emit::FragmentOp>(
"STOP_COND_FRAGMENT", [&] {
855 b.create<sv::VerbatimOp>(
856 "\n// Users can define 'STOP_COND' to add an extra gate "
857 "to stop conditions.");
858 emitGuard(
"STOP_COND_", [&]() {
859 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
866FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
867 SmallVectorImpl<hw::PortInfo> &ports,
868 Operation *moduleOp, StringRef moduleName,
870 ports.reserve(firrtlPorts.size());
872 size_t numResults = 0;
873 for (
auto e :
llvm::enumerate(firrtlPorts)) {
875 size_t portNo = e.index();
880 if (firrtlPort.
sym.size() > 1 ||
881 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
882 return emitError(firrtlPort.
loc)
883 <<
"cannot lower aggregate port " << firrtlPort.
name
884 <<
" with field sensitive symbols, HW dialect does not support "
885 "per field symbols yet.";
886 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
888 if (hadDontTouch && !hwPort.
getSym()) {
889 if (hwPort.
type.isInteger(0)) {
890 if (enableAnnotationWarning) {
891 mlir::emitWarning(firrtlPort.
loc)
892 <<
"zero width port " << hwPort.
name
893 <<
" has dontTouch annotation, removing anyway";
899 hw::InnerSymAttr::get(StringAttr::get(
900 moduleOp->getContext(),
901 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
902 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
903 moduleOp->getContext());
908 moduleOp->emitError(
"cannot lower this port type to HW");
914 if (hwPort.
type.isInteger(0)) {
915 auto sym = hwPort.
getSym();
916 if (sym && !sym.empty()) {
917 return mlir::emitError(firrtlPort.
loc)
918 <<
"zero width port " << hwPort.
name
919 <<
" is referenced by name [" << sym
920 <<
"] (e.g. in an XMR) but must be removed";
927 hwPort.
dir = hw::ModulePort::Direction::Output;
928 hwPort.
argNum = numResults++;
929 }
else if (firrtlPort.
isInput()) {
930 hwPort.
dir = hw::ModulePort::Direction::Input;
931 hwPort.
argNum = numArgs++;
935 hwPort.
type = hw::InOutType::get(hwPort.
type);
936 hwPort.
dir = hw::ModulePort::Direction::InOut;
937 hwPort.
argNum = numArgs++;
939 hwPort.
loc = firrtlPort.
loc;
940 ports.push_back(hwPort);
950 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
951 return cast<ParamDeclAttr>(a);
956 Builder builder(module);
961 SmallVector<Attribute> newParams;
962 for (
const ParamDeclAttr &entry : params) {
963 auto name = entry.getName();
964 auto type = entry.getType();
965 auto value = ignoreValues ? Attribute() : entry.getValue();
967 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
968 newParams.push_back(paramAttr);
970 return builder.getArrayAttr(newParams);
973bool FIRRTLModuleLowering::handleForceNameAnnos(
982 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
989 auto diag = oldModule.emitOpError()
991 <<
"' that is not a non-local annotation";
992 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1003 auto diag = oldModule.emitOpError()
1005 <<
"' whose non-local symbol, '" << sym
1006 <<
"' does not exist in the circuit";
1007 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1020 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1022 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1023 if (!inserted.second &&
1024 (anno.
getMember(
"name") != (inserted.first->second))) {
1025 auto diag = oldModule.emitError()
1027 <<
"' with different names: " << inserted.first->second
1028 <<
" was not " << anno.
getMember(
"name");
1029 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1040FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1041 Block *topLevelModule,
1044 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1045 SmallVector<hw::PortInfo, 8> ports;
1046 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1050 StringRef verilogName;
1051 if (
auto defName = oldModule.getDefname())
1052 verilogName = defName.value();
1055 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1056 auto nameAttr = builder.getStringAttr(oldModule.getName());
1062 oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1063 SymbolTable::setSymbolVisibility(newModule,
1064 SymbolTable::getSymbolVisibility(oldModule));
1066 bool hasOutputPort =
1067 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1068 if (!hasOutputPort &&
1071 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1077 loweringState.processRemainingAnnotations(oldModule, annos);
1082FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1083 Block *topLevelModule,
1086 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1087 SmallVector<hw::PortInfo, 8> ports;
1088 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1093 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1095 oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1096 oldModule.getModuleNameAttr());
1104FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1107 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1108 SmallVector<hw::PortInfo, 8> ports;
1109 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1114 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1115 auto nameAttr = builder.getStringAttr(oldModule.getName());
1117 builder.create<
hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
1119 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1120 newModule.setCommentAttr(comment);
1123 SmallVector<StringRef, 12> attrNames = {
1124 "annotations",
"convention",
"layers",
1125 "portNames",
"sym_name",
"portDirections",
1126 "portTypes",
"portAnnotations",
"portSymbols",
1127 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName()};
1129 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1130 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1132 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1133 return !attrSet.count(namedAttr.getName()) &&
1134 !newModule->getAttrDictionary().contains(namedAttr.getName());
1136 newAttrs.push_back(i);
1138 newModule->setAttrs(newAttrs);
1142 SymbolTable::setSymbolVisibility(newModule,
1143 SymbolTable::getSymbolVisibility(oldModule));
1149 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1153 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1155 if (!newModule->hasAttr(
"output_file"))
1156 newModule->setAttr(
"output_file", testBenchDir);
1157 newModule->setAttr(
"firrtl.extract.do_not_extract",
1158 builder.getUnitAttr());
1159 newModule.setCommentAttr(
1160 builder.getStringAttr(
"VCS coverage exclude_file"));
1166 loweringState.processRemainingAnnotations(oldModule, annos);
1175 Operation *insertPoint) {
1176 if (!value.hasOneUse())
1179 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1180 if (!attach || attach.getNumOperands() != 2)
1184 auto loweredType =
lowerType(value.getType());
1185 if (loweredType.isInteger(0))
1190 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1191 auto *op = attachedValue.getDefiningOp();
1192 if (op && op->getBlock() == insertPoint->getBlock() &&
1193 !op->isBeforeInBlock(insertPoint))
1198 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1214 if (type_isa<AnalogType>(flipValue.getType()))
1217 Operation *connectOp =
nullptr;
1218 for (
auto &use : flipValue.getUses()) {
1221 if (use.getOperandNumber() != 0)
1223 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1229 connectOp = use.getOwner();
1239 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1240 if (loweredType.isInteger(0))
1245 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1247 auto connectSrc = connectOp->getOperand(1);
1250 if (!isa<FIRRTLType>(connectSrc.getType())) {
1256 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1257 connectSrc = builder
1258 .create<mlir::UnrealizedConversionCastOp>(
1259 type_cast<FIRRTLBaseType>(connectSrc.getType())
1266 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1268 if (destTy != connectSrc.getType() &&
1269 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1270 isa<BaseTypeAliasType>(destTy))) {
1272 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1274 if (!destTy.isGround()) {
1276 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1278 }
else if (destTy.getBitWidthOrSentinel() !=
1279 type_cast<FIRRTLBaseType>(connectSrc.getType())
1280 .getBitWidthOrSentinel()) {
1283 auto destWidth = destTy.getBitWidthOrSentinel();
1284 assert(destWidth != -1 &&
"must know integer widths");
1285 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1297 SmallVector<SubfieldOp> accesses;
1298 for (
auto *op : structValue.getUsers()) {
1299 assert(isa<SubfieldOp>(op));
1300 auto fieldAccess = cast<SubfieldOp>(op);
1302 fieldAccess.getInput().getType().base().getElementIndex(field);
1303 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1304 accesses.push_back(fieldAccess);
1312LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1315 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1322 bodyBuilder.setInsertionPoint(cursor);
1325 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1326 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1327 "port count mismatch");
1329 SmallVector<Value, 4> outputs;
1332 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1333 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1335 unsigned nextHWInputArg = 0;
1336 int hwPortIndex = -1;
1337 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1339 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1342 type_isa<FIRRTLBaseType>(port.type) &&
1343 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1347 if (!port.isOutput() && !isZeroWidth) {
1350 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1356 oldArg.replaceAllUsesWith(newArg);
1362 if (isZeroWidth && port.isInput()) {
1363 Value newArg = bodyBuilder
1364 .create<WireOp>(port.type,
"." + port.getName().str() +
1367 oldArg.replaceAllUsesWith(newArg);
1375 outputs.push_back(value);
1376 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1382 auto newArg = bodyBuilder.create<WireOp>(
1383 port.type,
"." + port.getName().str() +
".output");
1386 oldArg.replaceAllUsesWith(newArg.getResult());
1389 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1390 if (!resultHWType.isInteger(0)) {
1393 outputs.push_back(output);
1396 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1397 newArg.setInnerSymAttr(sym);
1398 newModule.setPortSymbolAttr(hwPortIndex, {});
1404 outputOp->setOperands(outputs);
1407 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1408 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1409 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1410 oldBlockInstList.begin(), oldBlockInstList.end());
1421FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp formalOp,
1423 auto builder = OpBuilder::atBlockEnd(&formalOp.getBody().front());
1428 auto oldFormalOp = cast<FormalOp>(
loweringState.getOldModule(formalOp));
1429 auto moduleName = oldFormalOp.getModuleNameAttr().getAttr();
1430 auto oldModule = cast<FModuleOp>(
1431 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1433 dyn_cast_or_null<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1435 return oldFormalOp->emitOpError()
1436 <<
"could not find module " << oldModule.getSymNameAttr();
1439 SmallVector<Value> symbolicInputs;
1440 for (
auto arg : newModule.getBody().getArguments())
1441 symbolicInputs.push_back(
1442 builder.create<
verif::SymbolicValueOp>(arg.getLoc(), arg.getType()));
1445 builder.create<hw::InstanceOp>(formalOp.getLoc(), newModule,
1446 newModule.getNameAttr(), symbolicInputs);
1456struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1458 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1459 : theModule(module), circuitState(circuitState),
1460 builder(module.getLoc(), module.getContext()), moduleNamespace(module),
1461 backedgeBuilder(builder, module.getLoc()) {}
1463 LogicalResult
run();
1466 Value getOrCreateClockConstant(seq::ClockConst clock);
1467 Value getOrCreateIntConstant(
const APInt &value);
1468 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1469 bool isSigned =
false) {
1470 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1472 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1473 Value getOrCreateXConstant(
unsigned numBits);
1474 Value getOrCreateZConstant(Type type);
1475 Value getPossiblyInoutLoweredValue(Value value);
1476 Value getLoweredValue(Value value);
1477 Value getLoweredNonClockValue(Value value);
1478 Value getLoweredAndExtendedValue(Value value, Type destType);
1479 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1480 Value getLoweredFmtOperand(Value operand);
1481 LogicalResult setLowering(Value orig, Value result);
1482 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1483 template <
typename ResultOpType,
typename... CtorArgTypes>
1484 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1485 template <
typename ResultOpType,
typename... CtorArgTypes>
1486 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1487 Backedge createBackedge(Location loc, Type type);
1488 Backedge createBackedge(Value orig, Type type);
1489 bool updateIfBackedge(Value dest, Value src);
1492 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1497 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1498 if (forceable.isForceable())
1506 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1507 auto attr = op.getInnerSymAttr();
1511 if (requiresInnerSymbol(op))
1513 op.getContext(), attr, 0,
1518 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1522 Value getReadValue(Value v);
1524 Value getNonClockValue(Value v);
1526 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1527 sv::ResetType resetStyle, sv::EventControl resetEdge,
1528 Value reset,
const std::function<
void(
void)> &body = {},
1529 const std::function<void(
void)> &resetBody = {});
1530 void addToAlwaysBlock(Value clock,
1531 const std::function<
void(
void)> &body = {}) {
1532 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1533 sv::EventControl(), Value(), body,
1534 std::function<
void(
void)>());
1537 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1538 std::function<
void(
void)>
emit);
1539 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1540 std::function<
void(
void)> elseCtor = {});
1541 void addToInitialBlock(std::function<
void(
void)> body);
1542 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1543 std::function<
void(
void)> elseCtor = {});
1544 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1546 bool allowTruncate);
1547 Value createArrayIndexing(Value array, Value index);
1548 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1550 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1551 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1552 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1555 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1556 UnloweredOpResult handleUnloweredOp(Operation *op);
1557 LogicalResult visitExpr(ConstantOp op);
1558 LogicalResult visitExpr(SpecialConstantOp op);
1559 LogicalResult visitExpr(SubindexOp op);
1560 LogicalResult visitExpr(SubaccessOp op);
1561 LogicalResult visitExpr(SubfieldOp op);
1562 LogicalResult visitExpr(VectorCreateOp op);
1563 LogicalResult visitExpr(BundleCreateOp op);
1564 LogicalResult visitExpr(FEnumCreateOp op);
1565 LogicalResult visitExpr(AggregateConstantOp op);
1566 LogicalResult visitExpr(IsTagOp op);
1567 LogicalResult visitExpr(SubtagOp op);
1570 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1571 return visitUnrealizedConversionCast(castOp);
1576 LogicalResult visitDecl(WireOp op);
1577 LogicalResult visitDecl(NodeOp op);
1578 LogicalResult visitDecl(RegOp op);
1579 LogicalResult visitDecl(RegResetOp op);
1580 LogicalResult visitDecl(MemOp op);
1581 LogicalResult visitDecl(InstanceOp oldInstance);
1582 LogicalResult visitDecl(VerbatimWireOp op);
1583 LogicalResult visitDecl(ContractOp op);
1586 LogicalResult lowerNoopCast(Operation *op);
1587 LogicalResult visitExpr(AsSIntPrimOp op);
1588 LogicalResult visitExpr(AsUIntPrimOp op);
1589 LogicalResult visitExpr(AsClockPrimOp op);
1590 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1592 LogicalResult visitExpr(HWStructCastOp op);
1593 LogicalResult visitExpr(BitCastOp op);
1595 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1596 LogicalResult visitExpr(CvtPrimOp op);
1597 LogicalResult visitExpr(NotPrimOp op);
1598 LogicalResult visitExpr(NegPrimOp op);
1599 LogicalResult visitExpr(PadPrimOp op);
1600 LogicalResult visitExpr(XorRPrimOp op);
1601 LogicalResult visitExpr(AndRPrimOp op);
1602 LogicalResult visitExpr(OrRPrimOp op);
1605 template <
typename ResultUnsignedOpType,
1606 typename ResultSignedOpType = ResultUnsignedOpType>
1607 LogicalResult lowerBinOp(Operation *op);
1608 template <
typename ResultOpType>
1609 LogicalResult lowerBinOpToVariadic(Operation *op);
1611 template <
typename ResultOpType>
1612 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1614 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1615 ICmpPredicate unsignedOp);
1616 template <
typename SignedOp,
typename Un
signedOp>
1617 LogicalResult lowerDivLikeOp(Operation *op);
1619 LogicalResult visitExpr(CatPrimOp op);
1621 LogicalResult visitExpr(AndPrimOp op) {
1622 return lowerBinOpToVariadic<comb::AndOp>(op);
1624 LogicalResult visitExpr(OrPrimOp op) {
1625 return lowerBinOpToVariadic<comb::OrOp>(op);
1627 LogicalResult visitExpr(XorPrimOp op) {
1628 return lowerBinOpToVariadic<comb::XorOp>(op);
1630 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1631 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1633 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1634 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1636 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1637 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1639 LogicalResult visitExpr(AddPrimOp op) {
1640 return lowerBinOpToVariadic<comb::AddOp>(op);
1642 LogicalResult visitExpr(EQPrimOp op) {
1643 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1645 LogicalResult visitExpr(NEQPrimOp op) {
1646 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1648 LogicalResult visitExpr(LTPrimOp op) {
1649 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1651 LogicalResult visitExpr(LEQPrimOp op) {
1652 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1654 LogicalResult visitExpr(GTPrimOp op) {
1655 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1657 LogicalResult visitExpr(GEQPrimOp op) {
1658 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1661 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1662 LogicalResult visitExpr(MulPrimOp op) {
1663 return lowerBinOpToVariadic<comb::MulOp>(op);
1665 LogicalResult visitExpr(DivPrimOp op) {
1666 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1668 LogicalResult visitExpr(RemPrimOp op) {
1669 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1673 LogicalResult visitExpr(IsXIntrinsicOp op);
1674 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1675 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1676 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1677 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1678 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1679 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1680 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1681 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1682 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1683 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1684 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1685 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1686 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1687 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1688 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1689 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1690 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1691 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1692 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1693 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1695 template <
typename TargetOp,
typename IntrinsicOp>
1696 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1697 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1698 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1699 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1700 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1701 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1702 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1703 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1706 LogicalResult visitExpr(BitsPrimOp op);
1707 LogicalResult visitExpr(InvalidValueOp op);
1708 LogicalResult visitExpr(HeadPrimOp op);
1709 LogicalResult visitExpr(ShlPrimOp op);
1710 LogicalResult visitExpr(ShrPrimOp op);
1711 LogicalResult visitExpr(DShlPrimOp op) {
1712 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1714 LogicalResult visitExpr(DShrPrimOp op) {
1715 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1717 LogicalResult visitExpr(DShlwPrimOp op) {
1718 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1720 LogicalResult visitExpr(TailPrimOp op);
1721 LogicalResult visitExpr(MuxPrimOp op);
1722 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1723 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1724 LogicalResult visitExpr(MultibitMuxOp op);
1725 LogicalResult visitExpr(VerbatimExprOp op);
1726 LogicalResult visitExpr(XMRRefOp op);
1727 LogicalResult visitExpr(XMRDerefOp op);
1730 LogicalResult lowerVerificationStatement(
1731 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1732 Value enable, StringAttr messageAttr, ValueRange operands,
1733 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1735 LogicalResult visitStmt(SkipOp op);
1737 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1738 LogicalResult visitStmt(ConnectOp op);
1739 LogicalResult visitStmt(MatchingConnectOp op);
1740 LogicalResult visitStmt(ForceOp op);
1741 LogicalResult visitStmt(PrintFOp op);
1742 LogicalResult visitStmt(StopOp op);
1743 LogicalResult visitStmt(AssertOp op);
1744 LogicalResult visitStmt(AssumeOp op);
1745 LogicalResult visitStmt(CoverOp op);
1746 LogicalResult visitStmt(AttachOp op);
1747 LogicalResult visitStmt(RefForceOp op);
1748 LogicalResult visitStmt(RefForceInitialOp op);
1749 LogicalResult visitStmt(RefReleaseOp op);
1750 LogicalResult visitStmt(RefReleaseInitialOp op);
1752 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1753 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1754 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1756 LogicalResult fixupLTLOps();
1759 return circuitState.lowerType(type, builder.getLoc());
1767 CircuitLoweringState &circuitState;
1770 ImplicitLocOpBuilder builder;
1775 DenseMap<Value, Value> valueMapping;
1779 DenseMap<Value, Value> fromClockMapping;
1783 DenseMap<Attribute, Value> hwConstantMap;
1784 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1788 DenseMap<unsigned, Value> hwConstantXMap;
1789 DenseMap<Type, Value> hwConstantZMap;
1795 DenseMap<Value, Value> readInOutCreated;
1799 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1800 sv::ResetType, sv::EventControl, Value>;
1817 llvm::MapVector<Value, Value> backedges;
1824 DenseSet<Operation *> maybeUnusedValues;
1826 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1827 void maybeUnused(Value value) {
1828 if (
auto *op = value.getDefiningOp())
1840 SetVector<Operation *> ltlOpFixupWorklist;
1845 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
1847 void addToWorklist(Block &block) {
1848 worklist.push_back({block.begin(), block.end()});
1850 void addToWorklist(Region ®ion) {
1851 for (
auto &block :
llvm::reverse(region))
1852 addToWorklist(block);
1857LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1863LogicalResult FIRRTLLowering::run() {
1866 for (
auto arg : theModule.
getBodyBlock()->getArguments())
1867 if (failed(setLowering(arg, arg)))
1874 addToWorklist(theModule.getBody());
1875 SmallVector<Operation *, 16> opsToRemove;
1877 while (!worklist.empty()) {
1878 auto &[opsIt, opsEnd] = worklist.back();
1879 if (opsIt == opsEnd) {
1880 worklist.pop_back();
1883 Operation *op = &*opsIt++;
1885 builder.setInsertionPoint(op);
1886 builder.setLoc(op->getLoc());
1887 auto done = succeeded(dispatchVisitor(op));
1888 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
1890 opsToRemove.push_back(op);
1892 switch (handleUnloweredOp(op)) {
1893 case AlreadyLowered:
1896 opsToRemove.push_back(op);
1898 case LoweringFailure:
1910 for (
auto &[backedge, value] : backedges) {
1911 SmallVector<Location> driverLocs;
1917 if (backedge == value) {
1918 Location edgeLoc = backedge.getLoc();
1919 if (driverLocs.empty()) {
1920 mlir::emitError(edgeLoc,
"sink does not have a driver");
1922 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
1923 for (
auto loc : driverLocs)
1924 diag.attachNote(loc) <<
"through driver here";
1930 auto *it = backedges.find(value);
1931 if (it == backedges.end())
1934 driverLocs.push_back(value.getLoc());
1937 if (
auto *defOp = backedge.getDefiningOp())
1938 maybeUnusedValues.erase(defOp);
1939 backedge.replaceAllUsesWith(value);
1947 while (!opsToRemove.empty()) {
1948 auto *op = opsToRemove.pop_back_val();
1953 for (
auto result : op->getResults()) {
1957 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
1959 builder.getIntegerType(0), 0);
1960 maybeUnusedValues.insert(zeroI0);
1962 result.replaceAllUsesWith(zeroI0);
1965 if (!op->use_empty()) {
1966 auto d = op->emitOpError(
1967 "still has uses; should remove ops in reverse order of visitation");
1968 SmallPtrSet<Operation *, 2> visited;
1969 for (
auto *user : op->getUsers())
1970 if (visited.insert(user).second)
1971 d.attachNote(user->getLoc())
1972 <<
"used by " << user->
getName() <<
" op";
1975 maybeUnusedValues.erase(op);
1980 while (!maybeUnusedValues.empty()) {
1981 auto it = maybeUnusedValues.begin();
1983 maybeUnusedValues.erase(it);
1984 if (!isOpTriviallyDead(op))
1986 for (
auto operand : op->getOperands())
1987 if (auto *defOp = operand.getDefiningOp())
1988 maybeUnusedValues.insert(defOp);
1994 if (failed(fixupLTLOps()))
2005Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2006 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2008 auto &entry = hwConstantMap[attr];
2012 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2013 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
2019Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2020 auto attr = builder.getIntegerAttr(
2021 builder.getIntegerType(value.getBitWidth()), value);
2023 auto &entry = hwConstantMap[attr];
2027 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2028 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
2034Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2037 if (hw::type_isa<IntegerType>(type))
2038 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2040 auto cache = hwAggregateConstantMap.lookup({value, type});
2045 SmallVector<Attribute> values;
2046 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2048 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2049 subType = array.getElementType();
2050 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2051 subType = structType.getElements()[e.index()].type;
2053 assert(
false &&
"type must be either array or struct");
2055 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2059 if (hw::type_isa<hw::ArrayType>(type))
2060 std::reverse(values.begin(), values.end());
2062 auto &entry = hwAggregateConstantMap[{value, type}];
2063 entry = builder.getArrayAttr(values);
2071 const std::function<LogicalResult()> &fn) {
2072 assert(failedOperand &&
"Should be called on the failed operand");
2080Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2082 auto &entry = hwConstantXMap[numBits];
2086 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2087 entry = entryBuilder.create<sv::ConstantXOp>(
2088 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2092Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2093 auto &entry = hwConstantZMap[type];
2095 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2096 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2105Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2107 if (
auto lowering = valueMapping.lookup(value)) {
2108 assert(!isa<FIRRTLType>(lowering.getType()) &&
2109 "Lowered value should be a non-FIRRTL value");
2118Value FIRRTLLowering::getLoweredValue(Value value) {
2119 auto result = getPossiblyInoutLoweredValue(value);
2125 if (isa<hw::InOutType>(result.getType()))
2126 return getReadValue(result);
2132Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2133 auto result = getLoweredValue(value);
2137 if (hw::type_isa<seq::ClockType>(result.getType()))
2138 return getNonClockValue(result);
2146Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2149 bool allowTruncate) {
2150 SmallVector<Value> resultBuffer;
2155 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2156 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2157 auto resultType = builder.getIntegerType(destWidth);
2159 if (srcWidth == destWidth)
2162 if (srcWidth > destWidth) {
2166 builder.emitError(
"operand should not be a truncation");
2170 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2171 return comb::createOrFoldSExt(value, resultType, builder);
2172 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2180 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2181 .Case<FVectorType>([&](
auto srcVectorType) {
2182 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2183 unsigned size = resultBuffer.size();
2184 unsigned indexWidth =
2186 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2187 destVectorType.getNumElements());
2189 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2191 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2192 destVectorType.getElementType())))
2195 SmallVector<Value> temp(resultBuffer.begin() + size,
2196 resultBuffer.end());
2198 resultBuffer.resize(size);
2199 resultBuffer.push_back(array);
2202 .Case<BundleType>([&](BundleType srcStructType) {
2203 auto destStructType = firrtl::type_cast<BundleType>(destType);
2204 unsigned size = resultBuffer.size();
2207 if (destStructType.getNumElements() != srcStructType.getNumElements())
2210 for (
auto elem :
llvm::enumerate(destStructType)) {
2211 auto structExtract =
2213 if (failed(recurse(structExtract,
2214 srcStructType.getElementType(elem.index()),
2215 destStructType.getElementType(elem.index()))))
2218 SmallVector<Value> temp(resultBuffer.begin() + size,
2219 resultBuffer.end());
2222 resultBuffer.resize(size);
2223 resultBuffer.push_back(newStruct);
2226 .Case<IntType>([&](
auto) {
2227 if (
auto result = cast(src, srcType, destType)) {
2228 resultBuffer.push_back(result);
2233 .Default([&](
auto) {
return failure(); });
2236 if (failed(recurse(array, sourceType, destType)))
2239 assert(resultBuffer.size() == 1 &&
2240 "resultBuffer must only contain a result array if `success` is true");
2241 return resultBuffer[0];
2249Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2250 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2251 type_isa<FIRRTLBaseType>(destType) &&
2252 "input/output value should be FIRRTL");
2255 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2256 if (destWidth == -1)
2259 auto result = getLoweredValue(value);
2271 return getOrCreateIntConstant(destWidth, 0);
2275 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2277 auto loweredDstType =
lowerType(destType);
2278 if (result.getType() != loweredDstType &&
2279 (isa<hw::TypeAliasType>(result.getType()) ||
2280 isa<hw::TypeAliasType>(loweredDstType))) {
2281 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, result);
2285 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2287 if (destType == value.getType())
2290 return getExtOrTruncAggregateValue(
2291 result, type_cast<FIRRTLBaseType>(value.getType()),
2292 type_cast<FIRRTLBaseType>(destType),
2296 if (isa<seq::ClockType>(result.getType())) {
2298 if (destType == value.getType())
2300 builder.emitError(
"cannot use clock type as an integer");
2304 auto intResultType = dyn_cast<IntegerType>(result.getType());
2305 if (!intResultType) {
2306 builder.emitError(
"operand of type ")
2307 << result.getType() <<
" cannot be used as an integer";
2311 auto srcWidth = intResultType.getWidth();
2312 if (srcWidth ==
unsigned(destWidth))
2315 if (srcWidth >
unsigned(destWidth)) {
2316 builder.emitError(
"operand should not be a truncation");
2320 auto resultType = builder.getIntegerType(destWidth);
2324 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2325 if (type_cast<IntType>(valueFIRType).isSigned())
2326 return comb::createOrFoldSExt(result, resultType, builder);
2328 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2337Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2338 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2339 type_isa<FIRRTLBaseType>(destType) &&
2340 "input/output value should be FIRRTL");
2343 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2344 if (destWidth == -1)
2347 auto result = getLoweredValue(value);
2359 return getOrCreateIntConstant(destWidth, 0);
2363 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2365 if (destType == value.getType())
2368 return getExtOrTruncAggregateValue(
2369 result, type_cast<FIRRTLBaseType>(value.getType()),
2370 type_cast<FIRRTLBaseType>(destType),
2374 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2375 if (srcWidth ==
unsigned(destWidth))
2381 if (srcWidth >
unsigned(destWidth)) {
2382 auto resultType = builder.getIntegerType(destWidth);
2386 auto resultType = builder.getIntegerType(destWidth);
2390 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2391 if (type_cast<IntType>(valueFIRType).isSigned())
2392 return comb::createOrFoldSExt(result, resultType, builder);
2394 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2401Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2402 auto loweredValue = getLoweredValue(operand);
2403 if (!loweredValue) {
2407 loweredValue = getOrCreateIntConstant(1, 0);
2412 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2413 if (intTy.isSigned())
2414 loweredValue = builder.create<sv::SystemFunctionOp>(
2415 loweredValue.getType(),
"signed", loweredValue);
2417 return loweredValue;
2426LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2427 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2428 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2429 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2433 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2436 if (srcWidth != -1) {
2438 assert((srcWidth != 0) &&
2439 "Lowering produced value for zero width source");
2441 assert((srcWidth == 0) &&
2442 "Lowering produced null value but source wasn't zero width");
2446 assert(result &&
"Lowering of foreign type produced null value");
2449 auto &slot = valueMapping[orig];
2450 assert(!slot &&
"value lowered multiple times");
2457LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2461 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2462 auto &entry = hwConstantMap[cst.getValueAttr()];
2473 cst->moveBefore(&theModule.getBodyBlock()->front());
2477 return setLowering(orig, result);
2482template <
typename ResultOpType,
typename... CtorArgTypes>
2483LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2484 CtorArgTypes... args) {
2485 auto result = builder.createOrFold<ResultOpType>(args...);
2486 if (
auto *op = result.getDefiningOp())
2488 return setPossiblyFoldedLowering(orig->getResult(0), result);
2495template <
typename ResultOpType,
typename... CtorArgTypes>
2496LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2497 CtorArgTypes... args) {
2498 auto result = builder.createOrFold<ResultOpType>(args...);
2499 if (
auto *op = result.getDefiningOp())
2500 ltlOpFixupWorklist.insert(op);
2501 return setPossiblyFoldedLowering(orig->getResult(0), result);
2510Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2511 auto backedge = backedgeBuilder.
get(type, loc);
2512 backedges.insert({backedge, backedge});
2520Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2521 auto backedge = createBackedge(orig.getLoc(), type);
2522 (void)setLowering(orig, backedge);
2528bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2529 auto backedgeIt = backedges.find(dest);
2530 if (backedgeIt == backedges.end())
2532 backedgeIt->second = src;
2540void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2541 const std::function<
void(
void)> &fn, Region ®ion) {
2545 auto oldIP = builder.saveInsertionPoint();
2547 builder.setInsertionPointToEnd(®ion.front());
2549 builder.restoreInsertionPoint(oldIP);
2553Value FIRRTLLowering::getReadValue(Value v) {
2554 Value result = readInOutCreated.lookup(v);
2560 auto oldIP = builder.saveInsertionPoint();
2561 if (
auto *vOp = v.getDefiningOp()) {
2562 builder.setInsertionPointAfter(vOp);
2566 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2571 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2572 result = getReadValue(arrayIndexInout.getInput());
2574 arrayIndexInout.getIndex());
2579 builder.restoreInsertionPoint(oldIP);
2580 readInOutCreated.insert({v, result});
2584Value FIRRTLLowering::getNonClockValue(Value v) {
2585 auto it = fromClockMapping.try_emplace(v, Value{});
2587 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2588 builder.setInsertionPointAfterValue(v);
2589 it.first->second = builder.create<seq::FromClockOp>(v);
2591 return it.first->second;
2594void FIRRTLLowering::addToAlwaysBlock(
2595 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2596 sv::EventControl resetEdge, Value reset,
2597 const std::function<
void(
void)> &body,
2598 const std::function<
void(
void)> &resetBody) {
2599 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2600 resetStyle, resetEdge, reset};
2601 sv::AlwaysOp alwaysOp;
2602 sv::IfOp insideIfOp;
2603 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2607 assert(resetStyle != sv::ResetType::NoReset);
2620 auto createIfOp = [&]() {
2623 insideIfOp = builder.create<sv::IfOp>(
2624 reset, []() {}, []() {});
2626 if (resetStyle == sv::ResetType::AsyncReset) {
2627 sv::EventControl events[] = {clockEdge, resetEdge};
2628 Value clocks[] = {clock, reset};
2630 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2631 if (resetEdge == sv::EventControl::AtNegEdge)
2632 llvm_unreachable(
"negative edge for reset is not expected");
2636 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2640 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2641 insideIfOp =
nullptr;
2643 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2647 assert(insideIfOp &&
"reset body must be initialized before");
2648 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2649 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2651 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2657 alwaysOp->moveBefore(builder.getInsertionBlock(),
2658 builder.getInsertionPoint());
2661LogicalResult FIRRTLLowering::emitGuards(Location loc,
2662 ArrayRef<Attribute> guards,
2663 std::function<
void(
void)>
emit) {
2664 if (guards.empty()) {
2668 auto guard = dyn_cast<StringAttr>(guards[0]);
2670 return mlir::emitError(loc,
2671 "elements in `guards` array must be `StringAttr`");
2674 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2675 LogicalResult result = LogicalResult::failure();
2676 addToIfDefBlock(guard.getValue(), [&]() {
2677 result = emitGuards(loc, guards.drop_front(), emit);
2682void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2683 std::function<
void(
void)> thenCtor,
2684 std::function<
void(
void)> elseCtor) {
2685 auto condAttr = builder.getStringAttr(cond);
2686 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2688 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2689 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2694 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2696 ifdefBlocks[{builder.getBlock(), condAttr}] =
2697 builder.create<
sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2701void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2702 auto op = initialBlocks.lookup(builder.getBlock());
2704 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2709 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2711 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2715void FIRRTLLowering::addIfProceduralBlock(Value cond,
2716 std::function<
void(
void)> thenCtor,
2717 std::function<
void(
void)> elseCtor) {
2720 auto insertIt = builder.getInsertionPoint();
2721 if (insertIt != builder.getBlock()->begin())
2722 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2723 if (ifOp.getCond() == cond) {
2724 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2725 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2730 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2742FIRRTLLowering::UnloweredOpResult
2743FIRRTLLowering::handleUnloweredOp(Operation *op) {
2745 if (!op->getRegions().empty() && isa<FIRRTLDialect>(op->getDialect())) {
2746 op->emitOpError(
"must explicitly handle its regions");
2747 return LoweringFailure;
2754 if (!isa<FIRRTLDialect>(op->getDialect())) {
2756 for (
auto ®ion : op->getRegions())
2757 addToWorklist(region);
2758 for (
auto &operand : op->getOpOperands())
2759 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
2760 operand.set(lowered);
2761 for (
auto result : op->getResults())
2762 (void)setLowering(result, result);
2763 return AlreadyLowered;
2775 if (op->getNumResults() == 1) {
2776 auto resultType = op->getResult(0).getType();
2777 if (type_isa<FIRRTLBaseType>(resultType) &&
2779 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2781 (void)setLowering(op->getResult(0), Value());
2785 op->emitOpError(
"LowerToHW couldn't handle this operation");
2786 return LoweringFailure;
2789LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2792 return setLowering(op, Value());
2794 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2797LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2799 if (isa<ClockType>(op.getType())) {
2800 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2801 :
seq::ClockConst::Low);
2803 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
2805 return setLowering(op, cst);
2808FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2809 auto iIdx = getOrCreateIntConstant(
2811 firrtl::type_cast<FVectorType>(op.getInput().getType())
2818 if (isa<sv::InOutType>(input.getType()))
2819 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2822 if (
auto *definingOp = result.getDefiningOp())
2827FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2828 Value valueIdx = getLoweredAndExtOrTruncValue(
2830 UIntType::get(op->getContext(),
2832 firrtl::type_cast<FVectorType>(op.getInput().getType())
2833 .getNumElements())));
2835 op->emitError() <<
"input lowering failed";
2842 if (isa<sv::InOutType>(input.getType()))
2843 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2845 result = createArrayIndexing(input, valueIdx);
2846 if (
auto *definingOp = result.getDefiningOp())
2851FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2852 auto resultType =
lowerType(op->getResult(0).getType());
2853 if (!resultType || !input) {
2854 op->emitError() <<
"subfield type lowering failed";
2860 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2861 .getElementName(op.getFieldIndex());
2863 if (isa<sv::InOutType>(input.getType()))
2864 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2867 if (
auto *definingOp = result.getDefiningOp())
2872LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2874 return setLowering(op, Value());
2876 auto input = getPossiblyInoutLoweredValue(op.getInput());
2878 return op.emitError() <<
"input lowering failed";
2880 auto result = lowerSubindex(op, input);
2883 return setLowering(op, *result);
2886LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2888 return setLowering(op, Value());
2890 auto input = getPossiblyInoutLoweredValue(op.getInput());
2892 return op.emitError() <<
"input lowering failed";
2894 auto result = lowerSubaccess(op, input);
2897 return setLowering(op, *result);
2900LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2903 if (getLoweredValue(op) || !op.getInput())
2907 return setLowering(op, Value());
2909 auto input = getPossiblyInoutLoweredValue(op.getInput());
2911 return op.emitError() <<
"input lowering failed";
2913 auto result = lowerSubfield(op, input);
2916 return setLowering(op, *result);
2919LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2920 auto resultType =
lowerType(op.getResult().getType());
2921 SmallVector<Value> operands;
2923 for (
auto oper :
llvm::reverse(op.getOperands())) {
2924 auto val = getLoweredValue(oper);
2927 operands.push_back(val);
2929 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2932LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2933 auto resultType =
lowerType(op.getResult().getType());
2934 SmallVector<Value> operands;
2935 for (
auto oper : op.getOperands()) {
2936 auto val = getLoweredValue(oper);
2939 operands.push_back(val);
2941 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2944LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2947 return setLowering(op, Value());
2949 auto input = getLoweredValue(op.getInput());
2950 auto tagName = op.getFieldNameAttr();
2953 if (
auto structType = dyn_cast<hw::StructType>(type)) {
2954 auto enumType = structType.getFieldType(
"tag");
2955 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
2956 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
2957 auto unionType = structType.getFieldType(
"body");
2958 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2959 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2960 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2963 return setLoweringTo<hw::EnumConstantOp>(
2964 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
2967LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2968 auto resultType =
lowerType(op.getResult().getType());
2970 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2972 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2973 cast<ArrayAttr>(attr));
2976LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2977 auto tagName = op.getFieldNameAttr();
2978 auto lhs = getLoweredValue(op.getInput());
2979 if (isa<hw::StructType>(lhs.getType()))
2981 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
2982 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
2983 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2986LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2989 return setLowering(op, Value());
2991 auto tagName = op.getFieldNameAttr();
2992 auto input = getLoweredValue(op.getInput());
2994 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3001LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3002 auto origResultType = op.getResult().getType();
3006 if (!type_isa<FIRRTLType>(origResultType)) {
3007 createBackedge(op.getResult(), origResultType);
3011 auto resultType =
lowerType(origResultType);
3015 if (resultType.isInteger(0)) {
3016 if (op.getInnerSym())
3017 return op.emitError(
"zero width wire is referenced by name [")
3018 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3019 return setLowering(op.getResult(), Value());
3023 auto innerSym = lowerInnerSymbol(op);
3024 auto name = op.getNameAttr();
3027 auto wire = builder.create<hw::WireOp>(
3028 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3030 if (
auto svAttrs = sv::getSVAttributes(op))
3031 sv::setSVAttributes(wire, svAttrs);
3033 return setLowering(op.getResult(), wire);
3036LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3037 auto resultTy =
lowerType(op.getType());
3040 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3042 SmallVector<Value, 4> operands;
3043 operands.reserve(op.getSubstitutions().size());
3044 for (
auto operand : op.getSubstitutions()) {
3045 auto lowered = getLoweredValue(operand);
3048 operands.push_back(lowered);
3051 ArrayAttr symbols = op.getSymbolsAttr();
3053 symbols = ArrayAttr::get(op.getContext(), {});
3055 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3059LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3060 auto operand = getLoweredValue(op.getInput());
3062 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3063 if (op.getInnerSym())
3064 return op.emitError(
"zero width node is referenced by name [")
3065 << *op.getInnerSym()
3066 <<
"] (e.g. in an XMR) but must be "
3068 return setLowering(op.getResult(), Value());
3074 auto name = op.getNameAttr();
3075 auto innerSym = lowerInnerSymbol(op);
3078 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3081 if (
auto svAttrs = sv::getSVAttributes(op)) {
3083 operand = builder.create<hw::WireOp>(operand, name);
3084 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3087 return setLowering(op.getResult(), operand);
3090LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3091 auto resultType =
lowerType(op.getResult().getType());
3094 if (resultType.isInteger(0))
3095 return setLowering(op.getResult(), Value());
3097 Value clockVal = getLoweredValue(op.getClockVal());
3102 auto innerSym = lowerInnerSymbol(op);
3103 Backedge inputEdge = backedgeBuilder.
get(resultType);
3104 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3105 op.getNameAttr(), innerSym);
3108 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3109 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3110 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3111 reg->setAttr(
"firrtl.random_init_start", randomStart);
3112 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3113 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3116 if (
auto svAttrs = sv::getSVAttributes(op))
3117 sv::setSVAttributes(reg, svAttrs);
3119 inputEdge.setValue(reg);
3120 (void)setLowering(op.getResult(),
reg);
3124LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3125 auto resultType =
lowerType(op.getResult().getType());
3128 if (resultType.isInteger(0))
3129 return setLowering(op.getResult(), Value());
3131 Value clockVal = getLoweredValue(op.getClockVal());
3132 Value resetSignal = getLoweredValue(op.getResetSignal());
3134 Value resetValue = getLoweredAndExtOrTruncValue(
3135 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3137 if (!clockVal || !resetSignal || !resetValue)
3141 auto innerSym = lowerInnerSymbol(op);
3142 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3143 Backedge inputEdge = backedgeBuilder.
get(resultType);
3145 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3146 resetSignal, resetValue, innerSym, isAsync);
3149 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3150 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3151 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3152 reg->setAttr(
"firrtl.random_init_start", randomStart);
3153 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3154 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3157 if (
auto svAttrs = sv::getSVAttributes(op))
3158 sv::setSVAttributes(reg, svAttrs);
3160 inputEdge.setValue(reg);
3161 (void)setLowering(op.getResult(),
reg);
3166LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3169 if (type_isa<BundleType>(op.getDataType()))
3170 return op.emitOpError(
3171 "should have already been lowered from a ground type to an aggregate "
3172 "type using the LowerTypes pass. Use "
3173 "'firtool --lower-types' or 'circt-opt "
3174 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3180 auto memType = seq::FirMemType::get(
3183 : std::optional<uint32_t>());
3185 seq::FirMemInitAttr memInit;
3186 if (
auto init = op.getInitAttr())
3187 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3188 init.getIsBinary(), init.getIsInline());
3190 auto memDecl = builder.create<seq::FirMemOp>(
3193 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3197 if (!circuitState.isInDUT(theModule))
3198 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3199 memDecl.setOutputFileAttr(testBenchDir);
3203 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3205 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3208 (void)setLowering(a, value);
3214 auto addInput = [&](StringRef field, Value backedge) {
3216 if (cast<FIRRTLBaseType>(a.getType())
3218 .getBitWidthOrSentinel() > 0)
3219 (void)setLowering(a, backedge);
3225 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3229 Value backedge, portValue;
3231 portValue = getOrCreateXConstant(1);
3233 auto portType = IntegerType::get(op.getContext(), width);
3234 backedge = portValue = createBackedge(builder.getLoc(), portType);
3236 addInput(field, backedge);
3240 auto addClock = [&](StringRef field) -> Value {
3241 Type clockTy = seq::ClockType::get(op.getContext());
3242 Value portValue = createBackedge(builder.getLoc(), clockTy);
3243 addInput(field, portValue);
3247 auto memportKind = op.getPortKind(i);
3248 if (memportKind == MemOp::PortKind::Read) {
3249 auto addr = addInputPort(
"addr", op.getAddrBits());
3250 auto en = addInputPort(
"en", 1);
3251 auto clk = addClock(
"clk");
3252 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3254 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3255 auto addr = addInputPort(
"addr", op.getAddrBits());
3256 auto en = addInputPort(
"en", 1);
3257 auto clk = addClock(
"clk");
3260 auto mode = addInputPort(
"wmode", 1);
3262 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3269 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3273 auto addr = addInputPort(
"addr", op.getAddrBits());
3276 auto en = addInputPort(
"en", 1);
3280 auto clk = addClock(
"clk");
3293LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3294 Operation *oldModule =
3295 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3297 auto *newModule = circuitState.getNewModule(oldModule);
3299 oldInstance->emitOpError(
"could not find module [")
3300 << oldInstance.getModuleName() <<
"] referenced by instance";
3306 ArrayAttr parameters;
3307 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3312 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3317 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3318 portIndicesByName[portInfo[portIdx].name] = portIdx;
3322 SmallVector<Value, 8> operands;
3323 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3324 auto &port = portInfo[portIndex];
3327 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3332 if (portType.isInteger(0))
3336 if (port.isOutput())
3339 auto portResult = oldInstance.getResult(portIndex);
3340 assert(portResult &&
"invalid IR, couldn't find port");
3344 if (port.isInput()) {
3345 operands.push_back(createBackedge(portResult, portType));
3351 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3352 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3354 auto loweredResult = getPossiblyInoutLoweredValue(source);
3355 operands.push_back(loweredResult);
3356 (void)setLowering(portResult, loweredResult);
3365 portType,
"." + port.getName().str() +
".wire");
3369 (void)setLowering(portResult, wire);
3371 operands.push_back(wire);
3378 auto innerSym = oldInstance.getInnerSymAttr();
3379 if (oldInstance.getLowerToBind()) {
3382 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3385 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3386 innerSym.getSymName());
3389 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3390 bindOp->setAttr(
"output_file", outputFile);
3393 circuitState.addBind(bindOp);
3397 auto newInstance = builder.create<hw::InstanceOp>(
3398 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3400 if (oldInstance.getLowerToBind())
3401 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3403 if (newInstance.getInnerSymAttr())
3404 if (
auto forceName = circuitState.instanceForceNames.lookup(
3405 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3406 newInstance.getInnerNameAttr()}))
3407 newInstance->setAttr(
"hw.verilogName", forceName);
3411 unsigned resultNo = 0;
3412 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3413 auto &port = portInfo[portIndex];
3417 Value resultVal = newInstance.getResult(resultNo);
3419 auto oldPortResult = oldInstance.getResult(portIndex);
3420 (void)setLowering(oldPortResult, resultVal);
3426LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
3427 SmallVector<Value> inputs;
3428 SmallVector<Type> types;
3429 for (
auto input : oldOp.getInputs()) {
3430 auto lowered = getLoweredValue(input);
3433 inputs.push_back(lowered);
3434 types.push_back(lowered.getType());
3437 auto newOp = builder.create<verif::ContractOp>(types, inputs);
3438 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
3439 auto &body = newOp.getBody().emplaceBlock();
3441 for (
auto [newResult, oldResult, oldArg] :
3442 llvm::zip(newOp.getResults(), oldOp.getResults(),
3443 oldOp.getBody().getArguments())) {
3444 if (failed(setLowering(oldResult, newResult)))
3446 if (failed(setLowering(oldArg, newResult)))
3450 body.getOperations().splice(body.end(),
3451 oldOp.getBody().front().getOperations());
3452 addToWorklist(body);
3462LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3463 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3468 return setLowering(op->getResult(0), operand);
3471LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3472 if (isa<ClockType>(op.getInput().getType()))
3473 return setLowering(op->getResult(0),
3474 getLoweredNonClockValue(op.getInput()));
3475 return lowerNoopCast(op);
3478LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3479 if (isa<ClockType>(op.getInput().getType()))
3480 return setLowering(op->getResult(0),
3481 getLoweredNonClockValue(op.getInput()));
3482 return lowerNoopCast(op);
3485LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3486 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3489LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3490 mlir::UnrealizedConversionCastOp op) {
3492 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3495 auto operand = op.getOperand(0);
3496 auto result = op.getResult(0);
3499 if (type_isa<FIRRTLType>(operand.getType()) &&
3500 type_isa<FIRRTLType>(result.getType()))
3501 return lowerNoopCast(op);
3505 if (!type_isa<FIRRTLType>(operand.getType())) {
3506 if (type_isa<FIRRTLType>(result.getType()))
3507 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3513 auto loweredResult = getLoweredValue(operand);
3514 if (!loweredResult) {
3517 if (operand.getType().isSignlessInteger(0)) {
3518 return setLowering(result, Value());
3525 result.replaceAllUsesWith(loweredResult);
3529LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3532 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3533 return setLowering(op, op.getOperand());
3537 auto result = getLoweredValue(op.getOperand());
3543 op.replaceAllUsesWith(result);
3547LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3548 auto operand = getLoweredValue(op.getOperand());
3551 auto resultType =
lowerType(op.getType());
3555 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3558LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3559 auto operand = getLoweredValue(op.getOperand());
3563 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3564 return setLowering(op, getOrCreateIntConstant(1, 0));
3566 return setLowering(op, Value());
3571 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3572 return setLowering(op, operand);
3575 auto zero = getOrCreateIntConstant(1, 0);
3576 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3579LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3580 auto operand = getLoweredValue(op.getInput());
3584 auto allOnes = getOrCreateIntConstant(
3585 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3586 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3589LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3592 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3596 auto resultType =
lowerType(op.getType());
3598 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3599 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3603LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3604 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3607 return setLowering(op, operand);
3610LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3611 auto operand = getLoweredValue(op.getInput());
3614 return setLowering(op, getOrCreateIntConstant(1, 0));
3619 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3623LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3624 auto operand = getLoweredValue(op.getInput());
3627 return setLowering(op, getOrCreateIntConstant(1, 1));
3632 return setLoweringTo<comb::ICmpOp>(
3633 op, ICmpPredicate::eq, operand,
3634 getOrCreateIntConstant(
3635 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3639LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3640 auto operand = getLoweredValue(op.getInput());
3643 return setLowering(op, getOrCreateIntConstant(1, 0));
3649 return setLoweringTo<comb::ICmpOp>(
3650 op, ICmpPredicate::ne, operand,
3651 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3659template <
typename ResultOpType>
3660LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3661 auto resultType = op->getResult(0).getType();
3662 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3663 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3667 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3673template <
typename ResultOpType>
3674LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3675 auto resultType = op->getResult(0).getType();
3676 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3677 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3688 auto intType = builder.getIntegerType(*bitwidth);
3689 auto retType = lhs.getType();
3692 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3693 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3698template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3699LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3701 auto resultType = op->getResult(0).getType();
3702 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3703 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3708 if (type_cast<IntType>(resultType).isSigned())
3709 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3710 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3715LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3716 ICmpPredicate unsignedOp) {
3718 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3719 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3720 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3724 if (cmpType.getWidth() == 0)
3725 cmpType = UIntType::get(builder.getContext(), 1);
3726 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3727 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3732 Type resultType = builder.getIntegerType(1);
3733 return setLoweringTo<comb::ICmpOp>(
3734 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3740template <
typename SignedOp,
typename Un
signedOp>
3741LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3745 auto opType = type_cast<IntType>(op->getResult(0).getType());
3746 if (opType.getWidth() == 0)
3747 return setLowering(op->getResult(0), Value());
3751 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3752 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3757 if (opType.isSigned())
3758 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
3760 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
3762 if (
auto *definingOp = result.getDefiningOp())
3765 if (resultType == opType)
3766 return setLowering(op->getResult(0), result);
3767 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
3770LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3771 auto lhs = getLoweredValue(op.getLhs());
3772 auto rhs = getLoweredValue(op.getRhs());
3776 return setLowering(op, rhs);
3778 return handleZeroBit(op.getRhs(),
3779 [&]() { return setLowering(op, Value()); });
3784 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3786 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3793LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3794 auto input = getLoweredNonClockValue(op.getArg());
3798 if (!isa<IntType>(input.getType())) {
3799 auto srcType = op.getArg().getType();
3801 assert(bitwidth &&
"Unknown width");
3802 auto intType = builder.getIntegerType(*bitwidth);
3803 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
3806 return setLoweringTo<comb::ICmpOp>(
3807 op, ICmpPredicate::ceq, input,
3808 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
3811LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
3812 auto operand = getLoweredValue(op.getInput());
3813 builder.create<hw::WireOp>(operand);
3817LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3818 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
3819 op.getFormatStringAttr());
3822LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3823 auto type =
lowerType(op.getResult().getType());
3827 auto valueOp = builder.create<sim::PlusArgsValueOp>(
3828 builder.getIntegerType(1), type, op.getFormatStringAttr());
3829 if (failed(setLowering(op.getResult(), valueOp.getResult())))
3831 if (failed(setLowering(op.getFound(), valueOp.getFound())))
3836LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3837 op.emitError(
"SizeOf should have been resolved.");
3841LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3843 if (op.getTestEnable())
3844 testEnable = getLoweredValue(op.getTestEnable());
3845 return setLoweringTo<seq::ClockGateOp>(
3846 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3847 testEnable, hw::InnerSymAttr{});
3850LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
3851 auto operand = getLoweredValue(op.getInput());
3852 return setLoweringTo<seq::ClockInverterOp>(op, operand);
3855LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
3856 auto operand = getLoweredValue(op.getInput());
3857 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
3860LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3861 return setLoweringToLTL<ltl::AndOp>(
3863 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3866LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3867 return setLoweringToLTL<ltl::OrOp>(
3869 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3872LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
3873 return setLoweringToLTL<ltl::IntersectOp>(
3875 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3878LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3879 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3880 op.getDelayAttr(), op.getLengthAttr());
3883LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3884 return setLoweringToLTL<ltl::ConcatOp>(
3886 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3889LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
3890 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
3891 op.getBaseAttr(), op.getMoreAttr());
3894LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
3895 return setLoweringToLTL<ltl::GoToRepeatOp>(
3896 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3899LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
3900 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
3901 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3904LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3905 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3908LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3909 return setLoweringToLTL<ltl::ImplicationOp>(
3911 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3914LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
3915 return setLoweringToLTL<ltl::UntilOp>(
3917 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3920LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3921 return setLoweringToLTL<ltl::EventuallyOp>(op,
3922 getLoweredValue(op.getInput()));
3925LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3926 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3927 ltl::ClockEdge::Pos,
3928 getLoweredNonClockValue(op.getClock()));
3931template <
typename TargetOp,
typename IntrinsicOp>
3932LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
3933 auto property = getLoweredValue(op.getProperty());
3934 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
3935 builder.create<TargetOp>(property, enable, op.getLabelAttr());
3939LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3940 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3943LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3944 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
3947LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3948 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
3951LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
3952 if (!isa<verif::ContractOp>(op->getParentOp()))
3953 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3954 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
3957LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
3958 if (!isa<verif::ContractOp>(op->getParentOp()))
3959 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3960 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
3963LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3964 auto clock = getLoweredNonClockValue(op.getClock());
3965 auto reset = getLoweredValue(op.getReset());
3966 if (!clock || !reset)
3968 auto resetType = op.getReset().getType();
3969 auto uintResetType = dyn_cast<UIntType>(resetType);
3970 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3971 auto isAsync = isa<AsyncResetType>(resetType);
3972 if (!isAsync && !isSync) {
3973 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
3974 "requires sync or async reset");
3975 d.attachNote() <<
"reset is of type " << resetType
3976 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3979 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3986LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3987 auto input = getLoweredValue(op.getInput());
3991 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
3992 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3995LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3996 auto resultTy =
lowerType(op.getType());
4003 if (type_isa<AnalogType>(op.getType()))
4006 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4009 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4020 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4022 if (!type_isa<IntegerType>(resultTy))
4023 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
4024 return setLowering(op, constant);
4028 op.emitOpError(
"unsupported type");
4032LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4033 auto input = getLoweredValue(op.getInput());
4036 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4037 if (op.getAmount() == 0)
4038 return setLowering(op, Value());
4039 Type resultType = builder.getIntegerType(op.getAmount());
4040 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4041 inWidth - op.getAmount());
4044LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4045 auto input = getLoweredValue(op.getInput());
4048 if (op.getAmount() == 0)
4050 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4055 if (op.getAmount() == 0)
4056 return setLowering(op, input);
4058 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4059 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4062LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4063 auto input = getLoweredValue(op.getInput());
4068 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4069 auto shiftAmount = op.getAmount();
4070 if (shiftAmount >= inWidth) {
4072 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4073 return setLowering(op, {});
4076 shiftAmount = inWidth - 1;
4079 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4080 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4083LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4084 auto input = getLoweredValue(op.getInput());
4088 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4089 if (inWidth == op.getAmount())
4090 return setLowering(op, Value());
4091 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4092 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4095LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4096 auto cond = getLoweredValue(op.getSel());
4097 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4098 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4099 if (!cond || !ifTrue || !ifFalse)
4102 if (isa<ClockType>(op.getType()))
4103 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4104 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4108LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4109 auto cond = getLoweredValue(op.getSel());
4110 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4111 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4112 if (!cond || !ifTrue || !ifFalse)
4115 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4117 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4120LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4121 auto sel = getLoweredValue(op.getSel());
4122 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4123 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4124 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4125 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4126 if (!sel || !v3 || !v2 || !v1 || !v0)
4128 Value array[] = {v3, v2, v1, v0};
4131 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4150Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4151 assert(op->getNumResults() == 1 &&
"only expect a single result");
4152 auto val = op->getResult(0);
4156 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4163 OpBuilder::InsertionGuard guard(builder);
4164 builder.setInsertionPoint(op);
4165 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4166 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4168 op->getContext(),
nullptr, 0,
4171 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4172 op->setOperand(idx, wire);
4176 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4177 sv::setSVAttributes(assignOp,
4178 sv::SVAttributeAttr::get(builder.getContext(),
4179 "synopsys infer_mux_override",
4184Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4186 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4191 if (!llvm::isPowerOf2_64(size)) {
4192 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4194 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4196 Value temp2[] = {ext.getResult(), array};
4202 return inBoundsRead;
4205LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4207 auto index = getLoweredAndExtOrTruncValue(
4209 UIntType::get(op.getContext(),
4214 SmallVector<Value> loweredInputs;
4215 loweredInputs.reserve(op.getInputs().size());
4216 for (
auto input : op.getInputs()) {
4217 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4220 loweredInputs.push_back(lowered);
4224 return setLowering(op, createArrayIndexing(array, index));
4227LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4228 auto resultTy =
lowerType(op.getType());
4232 SmallVector<Value, 4> operands;
4233 operands.reserve(op.getSubstitutions().size());
4234 for (
auto operand : op.getSubstitutions()) {
4235 auto lowered = getLoweredValue(operand);
4238 operands.push_back(lowered);
4241 ArrayAttr symbols = op.getSymbolsAttr();
4243 symbols = ArrayAttr::get(op.getContext(), {});
4245 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4249LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4253 Type baseType = op.getType().getType();
4256 if (isa<ClockType>(baseType))
4257 xmrType = builder.getIntegerType(1);
4261 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4262 op.getRef(), op.getVerbatimSuffixAttr());
4265LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4269 if (isa<ClockType>(op.getType()))
4270 xmrType = builder.getIntegerType(1);
4274 auto xmr = builder.create<sv::XMRRefOp>(
4275 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4276 auto readXmr = getReadValue(xmr);
4277 if (!isa<ClockType>(op.getType()))
4278 return setLowering(op, readXmr);
4279 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4286LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4298FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4299 auto srcType = srcVal.getType();
4300 auto dstType = destVal.getType();
4301 if (srcType != dstType &&
4302 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4303 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4305 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4306 .Case<hw::WireOp>([&](
auto op) {
4307 maybeUnused(op.getInput());
4308 op.getInputMutable().assign(srcVal);
4311 .Case<seq::FirRegOp>([&](
auto op) {
4312 maybeUnused(op.getNext());
4313 op.getNextMutable().assign(srcVal);
4316 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4319 op.emitOpError(
"used as connect destination");
4322 .Default([](
auto) {
return false; });
4325LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4326 auto dest = op.getDest();
4328 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4329 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4331 return handleZeroBit(op.getSrc(), []() { return success(); });
4333 auto destVal = getPossiblyInoutLoweredValue(dest);
4337 auto result = lowerConnect(destVal, srcVal);
4345 if (updateIfBackedge(destVal, srcVal))
4348 if (!isa<hw::InOutType>(destVal.getType()))
4349 return op.emitError(
"destination isn't an inout type");
4355LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4356 auto dest = op.getDest();
4357 auto srcVal = getLoweredValue(op.getSrc());
4359 return handleZeroBit(op.getSrc(), []() { return success(); });
4361 auto destVal = getPossiblyInoutLoweredValue(dest);
4365 auto result = lowerConnect(destVal, srcVal);
4373 if (updateIfBackedge(destVal, srcVal))
4376 if (!isa<hw::InOutType>(destVal.getType()))
4377 return op.emitError(
"destination isn't an inout type");
4383LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4384 auto srcVal = getLoweredValue(op.getSrc());
4388 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4392 if (!isa<hw::InOutType>(destVal.getType()))
4393 return op.emitError(
"destination isn't an inout type");
4396 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4397 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4398 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4403LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4404 auto src = getLoweredNonClockValue(op.getSrc());
4405 auto clock = getLoweredNonClockValue(op.getClock());
4406 auto pred = getLoweredValue(op.getPredicate());
4407 if (!src || !clock || !pred)
4410 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4415 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4416 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4417 addToAlwaysBlock(clock, [&]() {
4418 addIfProceduralBlock(
4419 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4424LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4425 auto src = getLoweredNonClockValue(op.getSrc());
4426 auto pred = getLoweredValue(op.getPredicate());
4430 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4435 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4436 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4437 addToInitialBlock([&]() {
4438 addIfProceduralBlock(
4439 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4444LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4445 auto clock = getLoweredNonClockValue(op.getClock());
4446 auto pred = getLoweredValue(op.getPredicate());
4447 if (!clock || !pred)
4450 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4455 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4456 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4457 addToAlwaysBlock(clock, [&]() {
4458 addIfProceduralBlock(pred,
4459 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4464LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4465 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4466 auto pred = getLoweredValue(op.getPredicate());
4467 if (!destVal || !pred)
4471 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4472 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4473 addToInitialBlock([&]() {
4474 addIfProceduralBlock(pred,
4475 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4483LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4484 auto clock = getLoweredNonClockValue(op.getClock());
4485 auto cond = getLoweredValue(op.getCond());
4486 if (!clock || !cond)
4489 SmallVector<Value, 4> operands;
4490 operands.reserve(op.getSubstitutions().size());
4491 for (
auto operand : op.getSubstitutions()) {
4492 Value loweredValue = getLoweredFmtOperand(operand);
4495 operands.push_back(loweredValue);
4499 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4500 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4501 addToAlwaysBlock(clock, [&]() {
4502 circuitState.usedPrintf =
true;
4503 circuitState.addFragment(theModule,
"PRINTF_FD_FRAGMENT");
4504 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
4508 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
4509 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
4511 addIfProceduralBlock(ifCond, [&]() {
4513 Value fd = builder.create<sv::MacroRefExprOp>(
4514 builder.getIntegerType(32),
"PRINTF_FD_");
4515 builder.create<sv::FWriteOp>(fd, op.getFormatString(), operands);
4525LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4526 auto clock = getLoweredValue(op.getClock());
4527 auto cond = getLoweredValue(op.getCond());
4528 if (!clock || !cond)
4531 circuitState.usedStopCond =
true;
4532 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4535 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4536 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4538 if (op.getExitCode())
4539 builder.create<sim::FatalOp>(clock, exitCond);
4541 builder.create<sim::FinishOp>(clock, exitCond);
4549template <
typename... Args>
4551 StringRef opName, Args &&...args) {
4552 if (opName ==
"assert")
4553 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4554 if (opName ==
"assume")
4555 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4556 if (opName ==
"cover")
4557 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4558 llvm_unreachable(
"unknown verification op");
4564template <
typename... Args>
4566 StringRef opName, Args &&...args) {
4567 if (opName ==
"assert")
4568 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4569 if (opName ==
"assume")
4570 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4571 if (opName ==
"cover")
4572 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4573 llvm_unreachable(
"unknown verification op");
4594LogicalResult FIRRTLLowering::lowerVerificationStatement(
4595 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4596 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4597 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
4598 StringRef opName = op->getName().stripDialect();
4601 ArrayRef<Attribute> guards{};
4602 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
4603 guards = guardsAttr.getValue();
4605 auto isCover = isa<CoverOp>(op);
4606 auto clock = getLoweredNonClockValue(opClock);
4607 auto enable = getLoweredValue(opEnable);
4608 auto predicate = getLoweredValue(opPredicate);
4609 if (!clock || !enable || !predicate)
4613 if (opNameAttr && !opNameAttr.getValue().empty())
4615 StringAttr prefixedLabel;
4618 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
4621 SmallVector<Value> messageOps;
4625 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
4626 flavor = VerificationFlavor::None;
4628 if (flavor == VerificationFlavor::None) {
4632 auto format = op->getAttrOfType<StringAttr>(
"format");
4634 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
4635 if (!isa<AssertOp>(op))
4636 return op->emitError()
4637 <<
"ifElseFatal format cannot be used for non-assertions";
4638 flavor = VerificationFlavor::IfElseFatal;
4639 }
else if (isConcurrent)
4640 flavor = VerificationFlavor::SVA;
4642 flavor = VerificationFlavor::Immediate;
4645 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4646 message = opMessageAttr;
4647 for (
auto operand : opOperands) {
4648 auto loweredValue = getLoweredFmtOperand(operand);
4656 if (flavor == VerificationFlavor::SVA)
4657 loweredValue = builder.create<sv::SampledOp>(loweredValue);
4658 messageOps.push_back(loweredValue);
4664 case VerificationFlavor::Immediate: {
4666 auto deferImmediate = circt::sv::DeferAssertAttr::get(
4667 builder.getContext(), circt::sv::DeferAssert::Immediate);
4668 addToAlwaysBlock(clock, [&]() {
4669 addIfProceduralBlock(enable, [&]() {
4671 prefixedLabel, message, messageOps);
4676 case VerificationFlavor::IfElseFatal: {
4677 assert(isa<AssertOp>(op) &&
"only assert is expected");
4680 auto boolType = IntegerType::get(builder.getContext(), 1);
4681 predicate = comb::createOrFoldNot(predicate, builder,
true);
4682 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4684 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4685 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
4686 addToAlwaysBlock(clock, [&]() {
4687 addIfProceduralBlock(predicate, [&]() {
4688 circuitState.usedStopCond =
true;
4689 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4691 circuitState.usedAssertVerboseCond =
true;
4692 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
4694 addIfProceduralBlock(
4695 builder.create<sv::MacroRefExprOp>(boolType,
4696 "ASSERT_VERBOSE_COND_"),
4697 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4698 addIfProceduralBlock(
4699 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
4700 [&]() { builder.create<sv::FatalOp>(); });
4706 case VerificationFlavor::SVA: {
4711 comb::createOrFoldNot(enable, builder,
true);
4713 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4715 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4719 sv::EventControl event;
4720 switch (opEventControl) {
4721 case EventControl::AtPosEdge:
4722 event = circt::sv::EventControl::AtPosEdge;
4724 case EventControl::AtEdge:
4725 event = circt::sv::EventControl::AtEdge;
4727 case EventControl::AtNegEdge:
4728 event = circt::sv::EventControl::AtNegEdge;
4734 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
4735 predicate, prefixedLabel, message, messageOps);
4738 case VerificationFlavor::None:
4740 "flavor `None` must be converted into one of concreate flavors");
4747 return emitGuards(op->getLoc(), guards,
emit);
4751LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4752 return lowerVerificationStatement(
4753 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4754 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4755 op.getIsConcurrent(), op.getEventControl());
4759LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4760 return lowerVerificationStatement(
4761 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4762 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4763 op.getIsConcurrent(), op.getEventControl());
4767LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4768 return lowerVerificationStatement(
4769 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4770 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4771 op.getIsConcurrent(), op.getEventControl());
4775LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
4780 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
4781 ArrayRef<Attribute> guards =
4782 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
4784 auto label = op.getNameAttr();
4785 StringAttr assumeLabel;
4786 if (label && !label.empty())
4788 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
4789 auto predicate = getLoweredValue(op.getPredicate());
4790 auto enable = getLoweredValue(op.getEnable());
4791 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
4792 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4794 SmallVector<Value> messageOps;
4795 for (
auto operand : op.getSubstitutions()) {
4796 auto loweredValue = getLoweredValue(operand);
4797 if (!loweredValue) {
4801 loweredValue = getOrCreateIntConstant(1, 0);
4803 messageOps.push_back(loweredValue);
4805 return emitGuards(op.getLoc(), guards, [&]() {
4806 builder.create<sv::AlwaysOp>(
4807 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4808 if (op.getMessageAttr().getValue().empty())
4809 buildImmediateVerifOp(
4810 builder,
"assume", predicate,
4811 circt::sv::DeferAssertAttr::get(
4812 builder.getContext(), circt::sv::DeferAssert::Immediate),
4815 buildImmediateVerifOp(
4816 builder,
"assume", predicate,
4817 circt::sv::DeferAssertAttr::get(
4818 builder.getContext(), circt::sv::DeferAssert::Immediate),
4819 assumeLabel, op.getMessageAttr(), messageOps);
4824LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4826 if (op.getAttached().size() < 2)
4829 SmallVector<Value, 4> inoutValues;
4830 for (
auto v : op.getAttached()) {
4831 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4832 if (!inoutValues.back()) {
4836 inoutValues.pop_back();
4840 if (!isa<hw::InOutType>(inoutValues.back().getType()))
4841 return op.emitError(
"operand isn't an inout type");
4844 if (inoutValues.size() < 2)
4855 bool isAttachInternalOnly =
4856 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
4858 if (isAttachInternalOnly) {
4859 auto v0 = inoutValues.front();
4860 for (
auto v : inoutValues) {
4863 v.replaceAllUsesWith(v0);
4870 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4871 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
4876 SmallVector<Value, 4> values;
4877 for (
auto inoutValue : inoutValues)
4878 values.push_back(getReadValue(inoutValue));
4880 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4881 for (
size_t i2 = 0; i2 != e; ++i2)
4883 builder.create<
sv::AssignOp>(inoutValues[i1], values[i2]);
4892 builder.create<sv::VerbatimOp>(
4893 "`error \"Verilator does not support alias and thus "
4895 "arbitrarily connect bidirectional wires and ports\"");
4897 [&]() { builder.create<sv::AliasOp>(inoutValues); });
4903LogicalResult FIRRTLLowering::fixupLTLOps() {
4904 if (ltlOpFixupWorklist.empty())
4906 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
4910 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4911 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
4912 if (isa<
hw::WireOp>(user))
4913 ltlOpFixupWorklist.insert(user);
4916 while (!ltlOpFixupWorklist.empty()) {
4917 auto *op = ltlOpFixupWorklist.pop_back_val();
4920 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4921 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
4922 SmallVector<Type, 2> types;
4923 auto result = opIntf.inferReturnTypes(
4924 op->getContext(), op->getLoc(), op->getOperands(),
4925 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4929 assert(types.size() == op->getNumResults());
4933 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
4934 if (result.getType() == type)
4936 LLVM_DEBUG(llvm::dbgs()
4937 <<
" - Result #" << result.getResultNumber() <<
" from "
4938 << result.getType() <<
" to " << type <<
"\n");
4939 result.setType(type);
4940 for (
auto *user : result.getUsers())
4942 ltlOpFixupWorklist.insert(user);
4947 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
4948 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4949 wireOp.replaceAllUsesWith(wireOp.getInput());
4950 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
4951 if (wireOp.use_empty())
4958 SmallPtrSet<Operation *, 4> usersReported;
4959 for (
auto *user : op->getUsers()) {
4960 if (!usersReported.insert(user).second)
4962 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4964 if (isa<hw::WireOp>(user))
4966 auto d = op->emitError(
4967 "verification operation used in a non-verification context");
4968 d.attachNote(user->getLoc())
4969 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
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 Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
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 Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static 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 SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
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.
static Block * getBodyBlock(FModuleLike mod)
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.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
This is an edge in the InstanceGraph.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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
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)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
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)
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.
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const