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);
1585 LogicalResult lowerNoopCast(Operation *op);
1586 LogicalResult visitExpr(AsSIntPrimOp op);
1587 LogicalResult visitExpr(AsUIntPrimOp op);
1588 LogicalResult visitExpr(AsClockPrimOp op);
1589 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1591 LogicalResult visitExpr(HWStructCastOp op);
1592 LogicalResult visitExpr(BitCastOp op);
1594 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1595 LogicalResult visitExpr(CvtPrimOp op);
1596 LogicalResult visitExpr(NotPrimOp op);
1597 LogicalResult visitExpr(NegPrimOp op);
1598 LogicalResult visitExpr(PadPrimOp op);
1599 LogicalResult visitExpr(XorRPrimOp op);
1600 LogicalResult visitExpr(AndRPrimOp op);
1601 LogicalResult visitExpr(OrRPrimOp op);
1604 template <
typename ResultUnsignedOpType,
1605 typename ResultSignedOpType = ResultUnsignedOpType>
1606 LogicalResult lowerBinOp(Operation *op);
1607 template <
typename ResultOpType>
1608 LogicalResult lowerBinOpToVariadic(Operation *op);
1610 template <
typename ResultOpType>
1611 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1613 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1614 ICmpPredicate unsignedOp);
1615 template <
typename SignedOp,
typename Un
signedOp>
1616 LogicalResult lowerDivLikeOp(Operation *op);
1618 LogicalResult visitExpr(CatPrimOp op);
1620 LogicalResult visitExpr(AndPrimOp op) {
1621 return lowerBinOpToVariadic<comb::AndOp>(op);
1623 LogicalResult visitExpr(OrPrimOp op) {
1624 return lowerBinOpToVariadic<comb::OrOp>(op);
1626 LogicalResult visitExpr(XorPrimOp op) {
1627 return lowerBinOpToVariadic<comb::XorOp>(op);
1629 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1630 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1632 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1633 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1635 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1636 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1638 LogicalResult visitExpr(AddPrimOp op) {
1639 return lowerBinOpToVariadic<comb::AddOp>(op);
1641 LogicalResult visitExpr(EQPrimOp op) {
1642 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1644 LogicalResult visitExpr(NEQPrimOp op) {
1645 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1647 LogicalResult visitExpr(LTPrimOp op) {
1648 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1650 LogicalResult visitExpr(LEQPrimOp op) {
1651 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1653 LogicalResult visitExpr(GTPrimOp op) {
1654 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1656 LogicalResult visitExpr(GEQPrimOp op) {
1657 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1660 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1661 LogicalResult visitExpr(MulPrimOp op) {
1662 return lowerBinOpToVariadic<comb::MulOp>(op);
1664 LogicalResult visitExpr(DivPrimOp op) {
1665 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1667 LogicalResult visitExpr(RemPrimOp op) {
1668 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1672 LogicalResult visitExpr(IsXIntrinsicOp op);
1673 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1674 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1675 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1676 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1677 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1678 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1679 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1680 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1681 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1682 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1683 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1684 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1685 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1686 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1687 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1688 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1689 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1690 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1691 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1692 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1694 template <
typename TargetOp,
typename IntrinsicOp>
1695 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1696 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1697 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1698 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1699 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1700 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1703 LogicalResult visitExpr(BitsPrimOp op);
1704 LogicalResult visitExpr(InvalidValueOp op);
1705 LogicalResult visitExpr(HeadPrimOp op);
1706 LogicalResult visitExpr(ShlPrimOp op);
1707 LogicalResult visitExpr(ShrPrimOp op);
1708 LogicalResult visitExpr(DShlPrimOp op) {
1709 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1711 LogicalResult visitExpr(DShrPrimOp op) {
1712 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1714 LogicalResult visitExpr(DShlwPrimOp op) {
1715 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1717 LogicalResult visitExpr(TailPrimOp op);
1718 LogicalResult visitExpr(MuxPrimOp op);
1719 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
1720 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
1721 LogicalResult visitExpr(MultibitMuxOp op);
1722 LogicalResult visitExpr(VerbatimExprOp op);
1723 LogicalResult visitExpr(XMRRefOp op);
1724 LogicalResult visitExpr(XMRDerefOp op);
1727 LogicalResult lowerVerificationStatement(
1728 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
1729 Value enable, StringAttr messageAttr, ValueRange operands,
1730 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
1732 LogicalResult visitStmt(SkipOp op);
1734 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
1735 LogicalResult visitStmt(ConnectOp op);
1736 LogicalResult visitStmt(MatchingConnectOp op);
1737 LogicalResult visitStmt(ForceOp op);
1738 LogicalResult visitStmt(PrintFOp op);
1739 LogicalResult visitStmt(StopOp op);
1740 LogicalResult visitStmt(AssertOp op);
1741 LogicalResult visitStmt(AssumeOp op);
1742 LogicalResult visitStmt(CoverOp op);
1743 LogicalResult visitStmt(AttachOp op);
1744 LogicalResult visitStmt(RefForceOp op);
1745 LogicalResult visitStmt(RefForceInitialOp op);
1746 LogicalResult visitStmt(RefReleaseOp op);
1747 LogicalResult visitStmt(RefReleaseInitialOp op);
1749 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
1750 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
1751 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
1753 LogicalResult fixupLTLOps();
1756 return circuitState.lowerType(type, builder.getLoc());
1764 CircuitLoweringState &circuitState;
1767 ImplicitLocOpBuilder builder;
1772 DenseMap<Value, Value> valueMapping;
1776 DenseMap<Value, Value> fromClockMapping;
1780 DenseMap<Attribute, Value> hwConstantMap;
1781 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
1785 DenseMap<unsigned, Value> hwConstantXMap;
1786 DenseMap<Type, Value> hwConstantZMap;
1792 DenseMap<Value, Value> readInOutCreated;
1796 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
1797 sv::ResetType, sv::EventControl, Value>;
1814 llvm::MapVector<Value, Value> backedges;
1821 DenseSet<Operation *> maybeUnusedValues;
1823 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
1824 void maybeUnused(Value value) {
1825 if (
auto *op = value.getDefiningOp())
1837 SetVector<Operation *> ltlOpFixupWorklist;
1841LogicalResult FIRRTLModuleLowering::lowerModuleOperations(
1847LogicalResult FIRRTLLowering::run() {
1851 auto &body = theModule.getBody();
1853 SmallVector<Operation *, 16> opsToRemove;
1857 auto result = theModule.walk([&](Operation *op) {
1858 builder.setInsertionPoint(op);
1859 builder.setLoc(op->getLoc());
1860 auto done = succeeded(dispatchVisitor(op));
1861 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
1863 opsToRemove.push_back(op);
1865 switch (handleUnloweredOp(op)) {
1866 case AlreadyLowered:
1869 opsToRemove.push_back(op);
1871 case LoweringFailure:
1873 return WalkResult::interrupt();
1876 return WalkResult::advance();
1879 if (result.wasInterrupted())
1887 for (
auto &[backedge, value] : backedges) {
1888 SmallVector<Location> driverLocs;
1894 if (backedge == value) {
1895 Location edgeLoc = backedge.getLoc();
1896 if (driverLocs.empty()) {
1897 mlir::emitError(edgeLoc,
"sink does not have a driver");
1899 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
1900 for (
auto loc : driverLocs)
1901 diag.attachNote(loc) <<
"through driver here";
1907 auto *it = backedges.find(value);
1908 if (it == backedges.end())
1911 driverLocs.push_back(value.getLoc());
1914 if (
auto *defOp = backedge.getDefiningOp())
1915 maybeUnusedValues.erase(defOp);
1916 backedge.replaceAllUsesWith(value);
1924 while (!opsToRemove.empty()) {
1925 auto *op = opsToRemove.pop_back_val();
1930 for (
auto result : op->getResults()) {
1934 auto builder = OpBuilder::atBlockBegin(&body.front());
1936 builder.getIntegerType(0), 0);
1937 maybeUnusedValues.insert(zeroI0);
1939 result.replaceAllUsesWith(zeroI0);
1942 if (!op->use_empty()) {
1943 auto d = op->emitOpError(
1944 "still has uses; should remove ops in reverse order of visitation");
1945 SmallPtrSet<Operation *, 2> visited;
1946 for (
auto *user : op->getUsers())
1947 if (visited.insert(user).second)
1948 d.attachNote(user->getLoc())
1949 <<
"used by " << user->
getName() <<
" op";
1952 maybeUnusedValues.erase(op);
1957 while (!maybeUnusedValues.empty()) {
1958 auto it = maybeUnusedValues.begin();
1960 maybeUnusedValues.erase(it);
1961 if (!isOpTriviallyDead(op))
1963 for (
auto operand : op->getOperands())
1964 if (auto *defOp = operand.getDefiningOp())
1965 maybeUnusedValues.insert(defOp);
1971 if (failed(fixupLTLOps()))
1982Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
1983 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
1985 auto &entry = hwConstantMap[attr];
1989 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
1990 entry = entryBuilder.create<seq::ConstClockOp>(builder.getLoc(), attr);
1996Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
1997 auto attr = builder.getIntegerAttr(
1998 builder.getIntegerType(value.getBitWidth()), value);
2000 auto &entry = hwConstantMap[attr];
2004 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2005 entry = entryBuilder.create<
hw::ConstantOp>(builder.getLoc(), attr);
2011Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2014 if (hw::type_isa<IntegerType>(type))
2015 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2017 auto cache = hwAggregateConstantMap.lookup({value, type});
2022 SmallVector<Attribute> values;
2023 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2025 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2026 subType = array.getElementType();
2027 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2028 subType = structType.getElements()[e.index()].type;
2030 assert(
false &&
"type must be either array or struct");
2032 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2036 if (hw::type_isa<hw::ArrayType>(type))
2037 std::reverse(values.begin(), values.end());
2039 auto &entry = hwAggregateConstantMap[{value, type}];
2040 entry = builder.getArrayAttr(values);
2048 const std::function<LogicalResult()> &fn) {
2049 assert(failedOperand &&
"Should be called on the failed operand");
2057Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2059 auto &entry = hwConstantXMap[numBits];
2063 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2064 entry = entryBuilder.create<sv::ConstantXOp>(
2065 builder.getLoc(), entryBuilder.getIntegerType(numBits));
2069Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2070 auto &entry = hwConstantZMap[type];
2072 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2073 entry = entryBuilder.create<sv::ConstantZOp>(builder.getLoc(), type);
2082Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2084 if (isa<BlockArgument>(value))
2088 if (
auto lowering = valueMapping.lookup(value)) {
2089 assert(!isa<FIRRTLType>(lowering.getType()) &&
2090 "Lowered value should be a non-FIRRTL value");
2099Value FIRRTLLowering::getLoweredValue(Value value) {
2100 auto result = getPossiblyInoutLoweredValue(value);
2106 if (isa<hw::InOutType>(result.getType()))
2107 return getReadValue(result);
2113Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2114 auto result = getLoweredValue(value);
2118 if (hw::type_isa<seq::ClockType>(result.getType()))
2119 return getNonClockValue(result);
2127Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2130 bool allowTruncate) {
2131 SmallVector<Value> resultBuffer;
2136 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2137 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2138 auto resultType = builder.getIntegerType(destWidth);
2140 if (srcWidth == destWidth)
2143 if (srcWidth > destWidth) {
2147 builder.emitError(
"operand should not be a truncation");
2151 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2152 return comb::createOrFoldSExt(value, resultType, builder);
2153 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2161 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2162 .Case<FVectorType>([&](
auto srcVectorType) {
2163 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2164 unsigned size = resultBuffer.size();
2165 unsigned indexWidth =
2167 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2168 destVectorType.getNumElements());
2170 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2172 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2173 destVectorType.getElementType())))
2176 SmallVector<Value> temp(resultBuffer.begin() + size,
2177 resultBuffer.end());
2179 resultBuffer.resize(size);
2180 resultBuffer.push_back(array);
2183 .Case<BundleType>([&](BundleType srcStructType) {
2184 auto destStructType = firrtl::type_cast<BundleType>(destType);
2185 unsigned size = resultBuffer.size();
2188 if (destStructType.getNumElements() != srcStructType.getNumElements())
2191 for (
auto elem :
llvm::enumerate(destStructType)) {
2192 auto structExtract =
2194 if (failed(recurse(structExtract,
2195 srcStructType.getElementType(elem.index()),
2196 destStructType.getElementType(elem.index()))))
2199 SmallVector<Value> temp(resultBuffer.begin() + size,
2200 resultBuffer.end());
2203 resultBuffer.resize(size);
2204 resultBuffer.push_back(newStruct);
2207 .Case<IntType>([&](
auto) {
2208 if (
auto result = cast(src, srcType, destType)) {
2209 resultBuffer.push_back(result);
2214 .Default([&](
auto) {
return failure(); });
2217 if (failed(recurse(array, sourceType, destType)))
2220 assert(resultBuffer.size() == 1 &&
2221 "resultBuffer must only contain a result array if `success` is true");
2222 return resultBuffer[0];
2230Value FIRRTLLowering::getLoweredAndExtendedValue(Value value, Type destType) {
2231 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2232 type_isa<FIRRTLBaseType>(destType) &&
2233 "input/output value should be FIRRTL");
2236 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2237 if (destWidth == -1)
2240 auto result = getLoweredValue(value);
2252 return getOrCreateIntConstant(destWidth, 0);
2256 cast<FIRRTLBaseType>(value.getType()).getBitWidthOrSentinel()) {
2258 auto loweredDstType =
lowerType(destType);
2259 if (result.getType() != loweredDstType &&
2260 (isa<hw::TypeAliasType>(result.getType()) ||
2261 isa<hw::TypeAliasType>(loweredDstType))) {
2262 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, result);
2266 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2268 if (destType == value.getType())
2271 return getExtOrTruncAggregateValue(
2272 result, type_cast<FIRRTLBaseType>(value.getType()),
2273 type_cast<FIRRTLBaseType>(destType),
2277 if (isa<seq::ClockType>(result.getType())) {
2279 if (destType == value.getType())
2281 builder.emitError(
"cannot use clock type as an integer");
2285 auto intResultType = dyn_cast<IntegerType>(result.getType());
2286 if (!intResultType) {
2287 builder.emitError(
"operand of type ")
2288 << result.getType() <<
" cannot be used as an integer";
2292 auto srcWidth = intResultType.getWidth();
2293 if (srcWidth ==
unsigned(destWidth))
2296 if (srcWidth >
unsigned(destWidth)) {
2297 builder.emitError(
"operand should not be a truncation");
2301 auto resultType = builder.getIntegerType(destWidth);
2305 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2306 if (type_cast<IntType>(valueFIRType).isSigned())
2307 return comb::createOrFoldSExt(result, resultType, builder);
2309 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2318Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2319 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2320 type_isa<FIRRTLBaseType>(destType) &&
2321 "input/output value should be FIRRTL");
2324 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2325 if (destWidth == -1)
2328 auto result = getLoweredValue(value);
2340 return getOrCreateIntConstant(destWidth, 0);
2344 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2346 if (destType == value.getType())
2349 return getExtOrTruncAggregateValue(
2350 result, type_cast<FIRRTLBaseType>(value.getType()),
2351 type_cast<FIRRTLBaseType>(destType),
2355 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2356 if (srcWidth ==
unsigned(destWidth))
2362 if (srcWidth >
unsigned(destWidth)) {
2363 auto resultType = builder.getIntegerType(destWidth);
2367 auto resultType = builder.getIntegerType(destWidth);
2371 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2372 if (type_cast<IntType>(valueFIRType).isSigned())
2373 return comb::createOrFoldSExt(result, resultType, builder);
2375 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2382Value FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2383 auto loweredValue = getLoweredValue(operand);
2384 if (!loweredValue) {
2388 loweredValue = getOrCreateIntConstant(1, 0);
2393 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2394 if (intTy.isSigned())
2395 loweredValue = builder.create<sv::SystemFunctionOp>(
2396 loweredValue.getType(),
"signed", loweredValue);
2398 return loweredValue;
2407LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2408 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2409 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2410 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2414 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2417 if (srcWidth != -1) {
2419 assert((srcWidth != 0) &&
2420 "Lowering produced value for zero width source");
2422 assert((srcWidth == 0) &&
2423 "Lowering produced null value but source wasn't zero width");
2427 assert(result &&
"Lowering of foreign type produced null value");
2430 auto &slot = valueMapping[orig];
2431 assert(!slot &&
"value lowered multiple times");
2438LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2442 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2443 auto &entry = hwConstantMap[cst.getValueAttr()];
2454 cst->moveBefore(&theModule.getBodyBlock()->front());
2458 return setLowering(orig, result);
2463template <
typename ResultOpType,
typename... CtorArgTypes>
2464LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2465 CtorArgTypes... args) {
2466 auto result = builder.createOrFold<ResultOpType>(args...);
2467 if (
auto *op = result.getDefiningOp())
2469 return setPossiblyFoldedLowering(orig->getResult(0), result);
2476template <
typename ResultOpType,
typename... CtorArgTypes>
2477LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2478 CtorArgTypes... args) {
2479 auto result = builder.createOrFold<ResultOpType>(args...);
2480 if (
auto *op = result.getDefiningOp())
2481 ltlOpFixupWorklist.insert(op);
2482 return setPossiblyFoldedLowering(orig->getResult(0), result);
2491Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2492 auto backedge = backedgeBuilder.
get(type, loc);
2493 backedges.insert({backedge, backedge});
2501Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
2502 auto backedge = createBackedge(orig.getLoc(), type);
2503 (void)setLowering(orig, backedge);
2509bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
2510 auto backedgeIt = backedges.find(dest);
2511 if (backedgeIt == backedges.end())
2513 backedgeIt->second = src;
2521void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
2522 const std::function<
void(
void)> &fn, Region ®ion) {
2526 auto oldIP = builder.saveInsertionPoint();
2528 builder.setInsertionPointToEnd(®ion.front());
2530 builder.restoreInsertionPoint(oldIP);
2534Value FIRRTLLowering::getReadValue(Value v) {
2535 Value result = readInOutCreated.lookup(v);
2541 auto oldIP = builder.saveInsertionPoint();
2542 if (
auto *vOp = v.getDefiningOp()) {
2543 builder.setInsertionPointAfter(vOp);
2547 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
2552 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
2553 result = getReadValue(arrayIndexInout.getInput());
2555 arrayIndexInout.getIndex());
2560 builder.restoreInsertionPoint(oldIP);
2561 readInOutCreated.insert({v, result});
2565Value FIRRTLLowering::getNonClockValue(Value v) {
2566 auto it = fromClockMapping.try_emplace(v, Value{});
2568 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
2569 builder.setInsertionPointAfterValue(v);
2570 it.first->second = builder.create<seq::FromClockOp>(v);
2572 return it.first->second;
2575void FIRRTLLowering::addToAlwaysBlock(
2576 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
2577 sv::EventControl resetEdge, Value reset,
2578 const std::function<
void(
void)> &body,
2579 const std::function<
void(
void)> &resetBody) {
2580 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
2581 resetStyle, resetEdge, reset};
2582 sv::AlwaysOp alwaysOp;
2583 sv::IfOp insideIfOp;
2584 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
2588 assert(resetStyle != sv::ResetType::NoReset);
2601 auto createIfOp = [&]() {
2604 insideIfOp = builder.create<sv::IfOp>(
2605 reset, []() {}, []() {});
2607 if (resetStyle == sv::ResetType::AsyncReset) {
2608 sv::EventControl events[] = {clockEdge, resetEdge};
2609 Value clocks[] = {clock, reset};
2611 alwaysOp = builder.create<sv::AlwaysOp>(events, clocks, [&]() {
2612 if (resetEdge == sv::EventControl::AtNegEdge)
2613 llvm_unreachable(
"negative edge for reset is not expected");
2617 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock, createIfOp);
2621 alwaysOp = builder.create<sv::AlwaysOp>(clockEdge, clock);
2622 insideIfOp =
nullptr;
2624 alwaysBlocks[key] = {alwaysOp, insideIfOp};
2628 assert(insideIfOp &&
"reset body must be initialized before");
2629 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
2630 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
2632 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
2638 alwaysOp->moveBefore(builder.getInsertionBlock(),
2639 builder.getInsertionPoint());
2642LogicalResult FIRRTLLowering::emitGuards(Location loc,
2643 ArrayRef<Attribute> guards,
2644 std::function<
void(
void)>
emit) {
2645 if (guards.empty()) {
2649 auto guard = dyn_cast<StringAttr>(guards[0]);
2651 return mlir::emitError(loc,
2652 "elements in `guards` array must be `StringAttr`");
2655 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
2656 LogicalResult result = LogicalResult::failure();
2657 addToIfDefBlock(guard.getValue(), [&]() {
2658 result = emitGuards(loc, guards.drop_front(), emit);
2663void FIRRTLLowering::addToIfDefBlock(StringRef cond,
2664 std::function<
void(
void)> thenCtor,
2665 std::function<
void(
void)> elseCtor) {
2666 auto condAttr = builder.getStringAttr(cond);
2667 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
2669 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
2670 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
2675 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2677 ifdefBlocks[{builder.getBlock(), condAttr}] =
2678 builder.create<
sv::IfDefOp>(condAttr, thenCtor, elseCtor);
2682void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
2683 auto op = initialBlocks.lookup(builder.getBlock());
2685 runWithInsertionPointAtEndOfBlock(body, op.getBody());
2690 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
2692 initialBlocks[builder.getBlock()] = builder.create<sv::InitialOp>(body);
2696void FIRRTLLowering::addIfProceduralBlock(Value cond,
2697 std::function<
void(
void)> thenCtor,
2698 std::function<
void(
void)> elseCtor) {
2701 auto insertIt = builder.getInsertionPoint();
2702 if (insertIt != builder.getBlock()->begin())
2703 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
2704 if (ifOp.getCond() == cond) {
2705 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
2706 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
2711 builder.create<sv::IfOp>(cond, thenCtor, elseCtor);
2723FIRRTLLowering::UnloweredOpResult
2724FIRRTLLowering::handleUnloweredOp(Operation *op) {
2729 if (!isa<FIRRTLDialect>(op->getDialect())) {
2730 for (
auto &operand : op->getOpOperands())
2731 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
2732 operand.set(lowered);
2733 for (
auto result : op->getResults())
2734 (void)setLowering(result, result);
2735 return AlreadyLowered;
2747 if (op->getNumResults() == 1) {
2748 auto resultType = op->getResult(0).getType();
2749 if (type_isa<FIRRTLBaseType>(resultType) &&
2751 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
2753 (void)setLowering(op->getResult(0), Value());
2757 op->emitOpError(
"LowerToHW couldn't handle this operation");
2758 return LoweringFailure;
2761LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
2764 return setLowering(op, Value());
2766 return setLowering(op, getOrCreateIntConstant(op.getValue()));
2769LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
2771 if (isa<ClockType>(op.getType())) {
2772 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
2773 :
seq::ClockConst::Low);
2775 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
2777 return setLowering(op, cst);
2780FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
2781 auto iIdx = getOrCreateIntConstant(
2783 firrtl::type_cast<FVectorType>(op.getInput().getType())
2790 if (isa<sv::InOutType>(input.getType()))
2791 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
2794 if (
auto *definingOp = result.getDefiningOp())
2799FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
2800 Value valueIdx = getLoweredAndExtOrTruncValue(
2802 UIntType::get(op->getContext(),
2804 firrtl::type_cast<FVectorType>(op.getInput().getType())
2805 .getNumElements())));
2807 op->emitError() <<
"input lowering failed";
2814 if (isa<sv::InOutType>(input.getType()))
2815 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
2817 result = createArrayIndexing(input, valueIdx);
2818 if (
auto *definingOp = result.getDefiningOp())
2823FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
2824 auto resultType =
lowerType(op->getResult(0).getType());
2825 if (!resultType || !input) {
2826 op->emitError() <<
"subfield type lowering failed";
2832 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
2833 .getElementName(op.getFieldIndex());
2835 if (isa<sv::InOutType>(input.getType()))
2836 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
2839 if (
auto *definingOp = result.getDefiningOp())
2844LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
2846 return setLowering(op, Value());
2848 auto input = getPossiblyInoutLoweredValue(op.getInput());
2850 return op.emitError() <<
"input lowering failed";
2852 auto result = lowerSubindex(op, input);
2855 return setLowering(op, *result);
2858LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
2860 return setLowering(op, Value());
2862 auto input = getPossiblyInoutLoweredValue(op.getInput());
2864 return op.emitError() <<
"input lowering failed";
2866 auto result = lowerSubaccess(op, input);
2869 return setLowering(op, *result);
2872LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
2875 if (getLoweredValue(op) || !op.getInput())
2879 return setLowering(op, Value());
2881 auto input = getPossiblyInoutLoweredValue(op.getInput());
2883 return op.emitError() <<
"input lowering failed";
2885 auto result = lowerSubfield(op, input);
2888 return setLowering(op, *result);
2891LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
2892 auto resultType =
lowerType(op.getResult().getType());
2893 SmallVector<Value> operands;
2895 for (
auto oper :
llvm::reverse(op.getOperands())) {
2896 auto val = getLoweredValue(oper);
2899 operands.push_back(val);
2901 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
2904LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
2905 auto resultType =
lowerType(op.getResult().getType());
2906 SmallVector<Value> operands;
2907 for (
auto oper : op.getOperands()) {
2908 auto val = getLoweredValue(oper);
2911 operands.push_back(val);
2913 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
2916LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
2919 return setLowering(op, Value());
2921 auto input = getLoweredValue(op.getInput());
2922 auto tagName = op.getFieldNameAttr();
2925 if (
auto structType = dyn_cast<hw::StructType>(type)) {
2926 auto enumType = structType.getFieldType(
"tag");
2927 auto enumAttr = hw::EnumFieldAttr::get(op.getLoc(), tagName, enumType);
2928 auto enumOp = builder.create<hw::EnumConstantOp>(enumAttr);
2929 auto unionType = structType.getFieldType(
"body");
2930 auto unionOp = builder.create<hw::UnionCreateOp>(unionType, tagName, input);
2931 SmallVector<Value> operands = {enumOp.getResult(), unionOp.getResult()};
2932 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
2935 return setLoweringTo<hw::EnumConstantOp>(
2936 op, hw::EnumFieldAttr::get(op.getLoc(), tagName, type));
2939LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
2940 auto resultType =
lowerType(op.getResult().getType());
2942 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
2944 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
2945 cast<ArrayAttr>(attr));
2948LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
2949 auto tagName = op.getFieldNameAttr();
2950 auto lhs = getLoweredValue(op.getInput());
2951 if (isa<hw::StructType>(lhs.getType()))
2953 auto enumField = hw::EnumFieldAttr::get(op.getLoc(), tagName, lhs.getType());
2954 auto rhs = builder.create<hw::EnumConstantOp>(enumField);
2955 return setLoweringTo<hw::EnumCmpOp>(op, lhs, rhs);
2958LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
2961 return setLowering(op, Value());
2963 auto tagName = op.getFieldNameAttr();
2964 auto input = getLoweredValue(op.getInput());
2966 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
2973LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
2974 auto origResultType = op.getResult().getType();
2978 if (!type_isa<FIRRTLType>(origResultType)) {
2979 createBackedge(op.getResult(), origResultType);
2983 auto resultType =
lowerType(origResultType);
2987 if (resultType.isInteger(0)) {
2988 if (op.getInnerSym())
2989 return op.emitError(
"zero width wire is referenced by name [")
2990 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
2991 return setLowering(op.getResult(), Value());
2995 auto innerSym = lowerInnerSymbol(op);
2996 auto name = op.getNameAttr();
2999 auto wire = builder.create<hw::WireOp>(
3000 op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3002 if (
auto svAttrs = sv::getSVAttributes(op))
3003 sv::setSVAttributes(wire, svAttrs);
3005 return setLowering(op.getResult(), wire);
3008LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3009 auto resultTy =
lowerType(op.getType());
3012 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3014 SmallVector<Value, 4> operands;
3015 operands.reserve(op.getSubstitutions().size());
3016 for (
auto operand : op.getSubstitutions()) {
3017 auto lowered = getLoweredValue(operand);
3020 operands.push_back(lowered);
3023 ArrayAttr symbols = op.getSymbolsAttr();
3025 symbols = ArrayAttr::get(op.getContext(), {});
3027 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3031LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3032 auto operand = getLoweredValue(op.getInput());
3034 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3035 if (op.getInnerSym())
3036 return op.emitError(
"zero width node is referenced by name [")
3037 << *op.getInnerSym()
3038 <<
"] (e.g. in an XMR) but must be "
3040 return setLowering(op.getResult(), Value());
3046 auto name = op.getNameAttr();
3047 auto innerSym = lowerInnerSymbol(op);
3050 operand = builder.create<hw::WireOp>(operand, name, innerSym);
3053 if (
auto svAttrs = sv::getSVAttributes(op)) {
3055 operand = builder.create<hw::WireOp>(operand, name);
3056 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3059 return setLowering(op.getResult(), operand);
3062LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3063 auto resultType =
lowerType(op.getResult().getType());
3066 if (resultType.isInteger(0))
3067 return setLowering(op.getResult(), Value());
3069 Value clockVal = getLoweredValue(op.getClockVal());
3074 auto innerSym = lowerInnerSymbol(op);
3075 Backedge inputEdge = backedgeBuilder.
get(resultType);
3076 auto reg = builder.create<seq::FirRegOp>(inputEdge, clockVal,
3077 op.getNameAttr(), innerSym);
3080 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3081 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3082 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3083 reg->setAttr(
"firrtl.random_init_start", randomStart);
3084 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3085 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3088 if (
auto svAttrs = sv::getSVAttributes(op))
3089 sv::setSVAttributes(reg, svAttrs);
3091 inputEdge.setValue(reg);
3092 (void)setLowering(op.getResult(),
reg);
3096LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3097 auto resultType =
lowerType(op.getResult().getType());
3100 if (resultType.isInteger(0))
3101 return setLowering(op.getResult(), Value());
3103 Value clockVal = getLoweredValue(op.getClockVal());
3104 Value resetSignal = getLoweredValue(op.getResetSignal());
3106 Value resetValue = getLoweredAndExtOrTruncValue(
3107 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3109 if (!clockVal || !resetSignal || !resetValue)
3113 auto innerSym = lowerInnerSymbol(op);
3114 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3115 Backedge inputEdge = backedgeBuilder.
get(resultType);
3117 builder.create<seq::FirRegOp>(inputEdge, clockVal, op.getNameAttr(),
3118 resetSignal, resetValue, innerSym, isAsync);
3121 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3122 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3123 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3124 reg->setAttr(
"firrtl.random_init_start", randomStart);
3125 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3126 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3129 if (
auto svAttrs = sv::getSVAttributes(op))
3130 sv::setSVAttributes(reg, svAttrs);
3132 inputEdge.setValue(reg);
3133 (void)setLowering(op.getResult(),
reg);
3138LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3141 if (type_isa<BundleType>(op.getDataType()))
3142 return op.emitOpError(
3143 "should have already been lowered from a ground type to an aggregate "
3144 "type using the LowerTypes pass. Use "
3145 "'firtool --lower-types' or 'circt-opt "
3146 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3152 auto memType = seq::FirMemType::get(
3155 : std::optional<uint32_t>());
3157 seq::FirMemInitAttr memInit;
3158 if (
auto init = op.getInitAttr())
3159 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3160 init.getIsBinary(), init.getIsInline());
3162 auto memDecl = builder.create<seq::FirMemOp>(
3165 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3169 if (!circuitState.isInDUT(theModule))
3170 if (
auto testBenchDir = circuitState.getTestBenchDirectory())
3171 memDecl.setOutputFileAttr(testBenchDir);
3175 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3177 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3180 (void)setLowering(a, value);
3186 auto addInput = [&](StringRef field, Value backedge) {
3188 if (cast<FIRRTLBaseType>(a.getType())
3190 .getBitWidthOrSentinel() > 0)
3191 (void)setLowering(a, backedge);
3197 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3201 Value backedge, portValue;
3203 portValue = getOrCreateXConstant(1);
3205 auto portType = IntegerType::get(op.getContext(), width);
3206 backedge = portValue = createBackedge(builder.getLoc(), portType);
3208 addInput(field, backedge);
3212 auto addClock = [&](StringRef field) -> Value {
3213 Type clockTy = seq::ClockType::get(op.getContext());
3214 Value portValue = createBackedge(builder.getLoc(), clockTy);
3215 addInput(field, portValue);
3219 auto memportKind = op.getPortKind(i);
3220 if (memportKind == MemOp::PortKind::Read) {
3221 auto addr = addInputPort(
"addr", op.getAddrBits());
3222 auto en = addInputPort(
"en", 1);
3223 auto clk = addClock(
"clk");
3224 auto data = builder.create<seq::FirMemReadOp>(memDecl,
addr,
clk,
en);
3226 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3227 auto addr = addInputPort(
"addr", op.getAddrBits());
3228 auto en = addInputPort(
"en", 1);
3229 auto clk = addClock(
"clk");
3232 auto mode = addInputPort(
"wmode", 1);
3234 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3241 auto rdata = builder.create<seq::FirMemReadWriteOp>(
3245 auto addr = addInputPort(
"addr", op.getAddrBits());
3248 auto en = addInputPort(
"en", 1);
3252 auto clk = addClock(
"clk");
3265LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3266 Operation *oldModule =
3267 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3269 auto *newModule = circuitState.getNewModule(oldModule);
3271 oldInstance->emitOpError(
"could not find module [")
3272 << oldInstance.getModuleName() <<
"] referenced by instance";
3278 ArrayAttr parameters;
3279 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3284 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3289 for (
unsigned portIdx = 0, e = portInfo.size(); portIdx != e; ++portIdx)
3290 portIndicesByName[portInfo[portIdx].name] = portIdx;
3294 SmallVector<Value, 8> operands;
3295 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3296 auto &port = portInfo[portIndex];
3299 oldInstance->emitOpError(
"could not lower type of port ") << port.name;
3304 if (portType.isInteger(0))
3308 if (port.isOutput())
3311 auto portResult = oldInstance.getResult(portIndex);
3312 assert(portResult &&
"invalid IR, couldn't find port");
3316 if (port.isInput()) {
3317 operands.push_back(createBackedge(portResult, portType));
3323 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3324 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3326 auto loweredResult = getPossiblyInoutLoweredValue(source);
3327 operands.push_back(loweredResult);
3328 (void)setLowering(portResult, loweredResult);
3337 portType,
"." + port.getName().str() +
".wire");
3341 (void)setLowering(portResult, wire);
3343 operands.push_back(wire);
3350 auto innerSym = oldInstance.getInnerSymAttr();
3351 if (oldInstance.getLowerToBind()) {
3354 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3357 auto bindOp = builder.create<sv::BindOp>(theModule.getNameAttr(),
3358 innerSym.getSymName());
3361 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3362 bindOp->setAttr(
"output_file", outputFile);
3365 circuitState.addBind(bindOp);
3369 auto newInstance = builder.create<hw::InstanceOp>(
3370 newModule, oldInstance.getNameAttr(), operands, parameters, innerSym);
3372 if (oldInstance.getLowerToBind())
3373 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3375 if (newInstance.getInnerSymAttr())
3376 if (
auto forceName = circuitState.instanceForceNames.lookup(
3377 {cast<hw::HWModuleOp>(newInstance->getParentOp()).getNameAttr(),
3378 newInstance.getInnerNameAttr()}))
3379 newInstance->setAttr(
"hw.verilogName", forceName);
3383 unsigned resultNo = 0;
3384 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3385 auto &port = portInfo[portIndex];
3389 Value resultVal = newInstance.getResult(resultNo);
3391 auto oldPortResult = oldInstance.getResult(portIndex);
3392 (void)setLowering(oldPortResult, resultVal);
3403LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
3404 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
3409 return setLowering(op->getResult(0), operand);
3412LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
3413 if (isa<ClockType>(op.getInput().getType()))
3414 return setLowering(op->getResult(0),
3415 getLoweredNonClockValue(op.getInput()));
3416 return lowerNoopCast(op);
3419LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
3420 if (isa<ClockType>(op.getInput().getType()))
3421 return setLowering(op->getResult(0),
3422 getLoweredNonClockValue(op.getInput()));
3423 return lowerNoopCast(op);
3426LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
3427 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
3430LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
3431 mlir::UnrealizedConversionCastOp op) {
3433 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
3436 auto operand = op.getOperand(0);
3437 auto result = op.getResult(0);
3440 if (type_isa<FIRRTLType>(operand.getType()) &&
3441 type_isa<FIRRTLType>(result.getType()))
3442 return lowerNoopCast(op);
3446 if (!type_isa<FIRRTLType>(operand.getType())) {
3447 if (type_isa<FIRRTLType>(result.getType()))
3448 return setLowering(result, getPossiblyInoutLoweredValue(operand));
3454 auto loweredResult = getLoweredValue(operand);
3455 if (!loweredResult) {
3458 if (operand.getType().isSignlessInteger(0)) {
3459 return setLowering(result, Value());
3466 result.replaceAllUsesWith(loweredResult);
3470LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
3473 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
3474 return setLowering(op, op.getOperand());
3478 auto result = getLoweredValue(op.getOperand());
3484 op.replaceAllUsesWith(result);
3488LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
3489 auto operand = getLoweredValue(op.getOperand());
3492 auto resultType =
lowerType(op.getType());
3496 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
3499LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
3500 auto operand = getLoweredValue(op.getOperand());
3504 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
3505 return setLowering(op, getOrCreateIntConstant(1, 0));
3507 return setLowering(op, Value());
3512 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
3513 return setLowering(op, operand);
3516 auto zero = getOrCreateIntConstant(1, 0);
3517 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
3520LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
3521 auto operand = getLoweredValue(op.getInput());
3525 auto allOnes = getOrCreateIntConstant(
3526 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
3527 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
3530LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
3533 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3537 auto resultType =
lowerType(op.getType());
3539 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
3540 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
3544LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
3545 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
3548 return setLowering(op, operand);
3551LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
3552 auto operand = getLoweredValue(op.getInput());
3555 return setLowering(op, getOrCreateIntConstant(1, 0));
3560 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
3564LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
3565 auto operand = getLoweredValue(op.getInput());
3568 return setLowering(op, getOrCreateIntConstant(1, 1));
3573 return setLoweringTo<comb::ICmpOp>(
3574 op, ICmpPredicate::eq, operand,
3575 getOrCreateIntConstant(
3576 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
3580LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
3581 auto operand = getLoweredValue(op.getInput());
3584 return setLowering(op, getOrCreateIntConstant(1, 0));
3590 return setLoweringTo<comb::ICmpOp>(
3591 op, ICmpPredicate::ne, operand,
3592 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
3600template <
typename ResultOpType>
3601LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
3602 auto resultType = op->getResult(0).getType();
3603 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3604 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3608 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
3614template <
typename ResultOpType>
3615LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
3616 auto resultType = op->getResult(0).getType();
3617 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3618 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3629 auto intType = builder.getIntegerType(*bitwidth);
3630 auto retType = lhs.getType();
3633 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
3634 return setLoweringTo<hw::BitcastOp>(op, retType, result);
3639template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
3640LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
3642 auto resultType = op->getResult(0).getType();
3643 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3644 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3649 if (type_cast<IntType>(resultType).isSigned())
3650 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
3651 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
3656LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
3657 ICmpPredicate unsignedOp) {
3659 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
3660 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
3661 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
3665 if (cmpType.getWidth() == 0)
3666 cmpType = UIntType::get(builder.getContext(), 1);
3667 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
3668 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
3673 Type resultType = builder.getIntegerType(1);
3674 return setLoweringTo<comb::ICmpOp>(
3675 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
3681template <
typename SignedOp,
typename Un
signedOp>
3682LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
3686 auto opType = type_cast<IntType>(op->getResult(0).getType());
3687 if (opType.getWidth() == 0)
3688 return setLowering(op->getResult(0), Value());
3692 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
3693 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
3698 if (opType.isSigned())
3699 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
3701 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
3703 if (
auto *definingOp = result.getDefiningOp())
3706 if (resultType == opType)
3707 return setLowering(op->getResult(0), result);
3708 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
3711LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
3712 auto lhs = getLoweredValue(op.getLhs());
3713 auto rhs = getLoweredValue(op.getRhs());
3717 return setLowering(op, rhs);
3719 return handleZeroBit(op.getRhs(),
3720 [&]() { return setLowering(op, Value()); });
3725 return handleZeroBit(op.getRhs(), [&]() { return setLowering(op, lhs); });
3727 return setLoweringTo<comb::ConcatOp>(op, lhs, rhs);
3734LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
3735 auto input = getLoweredNonClockValue(op.getArg());
3739 if (!isa<IntType>(input.getType())) {
3740 auto srcType = op.getArg().getType();
3742 assert(bitwidth &&
"Unknown width");
3743 auto intType = builder.getIntegerType(*bitwidth);
3744 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
3747 return setLoweringTo<comb::ICmpOp>(
3748 op, ICmpPredicate::ceq, input,
3749 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
3752LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
3753 auto operand = getLoweredValue(op.getInput());
3754 builder.create<hw::WireOp>(operand);
3758LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
3759 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
3760 op.getFormatStringAttr());
3763LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
3764 auto type =
lowerType(op.getResult().getType());
3768 auto valueOp = builder.create<sim::PlusArgsValueOp>(
3769 builder.getIntegerType(1), type, op.getFormatStringAttr());
3770 if (failed(setLowering(op.getResult(), valueOp.getResult())))
3772 if (failed(setLowering(op.getFound(), valueOp.getFound())))
3777LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
3778 op.emitError(
"SizeOf should have been resolved.");
3782LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
3784 if (op.getTestEnable())
3785 testEnable = getLoweredValue(op.getTestEnable());
3786 return setLoweringTo<seq::ClockGateOp>(
3787 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
3788 testEnable, hw::InnerSymAttr{});
3791LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
3792 auto operand = getLoweredValue(op.getInput());
3793 return setLoweringTo<seq::ClockInverterOp>(op, operand);
3796LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
3797 auto operand = getLoweredValue(op.getInput());
3798 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
3801LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
3802 return setLoweringToLTL<ltl::AndOp>(
3804 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3807LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
3808 return setLoweringToLTL<ltl::OrOp>(
3810 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3813LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
3814 return setLoweringToLTL<ltl::IntersectOp>(
3816 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3819LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
3820 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
3821 op.getDelayAttr(), op.getLengthAttr());
3824LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
3825 return setLoweringToLTL<ltl::ConcatOp>(
3827 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3830LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
3831 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
3832 op.getBaseAttr(), op.getMoreAttr());
3835LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
3836 return setLoweringToLTL<ltl::GoToRepeatOp>(
3837 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3840LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
3841 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
3842 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
3845LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
3846 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
3849LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
3850 return setLoweringToLTL<ltl::ImplicationOp>(
3852 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3855LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
3856 return setLoweringToLTL<ltl::UntilOp>(
3858 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
3861LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
3862 return setLoweringToLTL<ltl::EventuallyOp>(op,
3863 getLoweredValue(op.getInput()));
3866LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
3867 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
3868 ltl::ClockEdge::Pos,
3869 getLoweredNonClockValue(op.getClock()));
3872template <
typename TargetOp,
typename IntrinsicOp>
3873LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
3874 auto property = getLoweredValue(op.getProperty());
3875 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
3876 builder.create<TargetOp>(property, enable, op.getLabelAttr());
3880LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
3881 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
3884LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
3885 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
3888LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
3889 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
3892LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
3893 auto clock = getLoweredNonClockValue(op.getClock());
3894 auto reset = getLoweredValue(op.getReset());
3895 if (!clock || !reset)
3897 auto resetType = op.getReset().getType();
3898 auto uintResetType = dyn_cast<UIntType>(resetType);
3899 auto isSync = uintResetType && uintResetType.getWidth() == 1;
3900 auto isAsync = isa<AsyncResetType>(resetType);
3901 if (!isAsync && !isSync) {
3902 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
3903 "requires sync or async reset");
3904 d.attachNote() <<
"reset is of type " << resetType
3905 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
3908 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
3915LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
3916 auto input = getLoweredValue(op.getInput());
3920 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
3921 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
3924LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
3925 auto resultTy =
lowerType(op.getType());
3932 if (type_isa<AnalogType>(op.getType()))
3935 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
3938 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
3949 auto constant = getOrCreateIntConstant(*bitwidth, 0);
3951 if (!type_isa<IntegerType>(resultTy))
3952 constant = builder.create<
hw::BitcastOp>(resultTy, constant);
3953 return setLowering(op, constant);
3957 op.emitOpError(
"unsupported type");
3961LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
3962 auto input = getLoweredValue(op.getInput());
3965 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3966 if (op.getAmount() == 0)
3967 return setLowering(op, Value());
3968 Type resultType = builder.getIntegerType(op.getAmount());
3969 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
3970 inWidth - op.getAmount());
3973LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
3974 auto input = getLoweredValue(op.getInput());
3977 if (op.getAmount() == 0)
3979 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
3984 if (op.getAmount() == 0)
3985 return setLowering(op, input);
3987 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
3988 return setLoweringTo<comb::ConcatOp>(op, input, zero);
3991LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
3992 auto input = getLoweredValue(op.getInput());
3997 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
3998 auto shiftAmount = op.getAmount();
3999 if (shiftAmount >= inWidth) {
4001 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4002 return setLowering(op, {});
4005 shiftAmount = inWidth - 1;
4008 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4009 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4012LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4013 auto input = getLoweredValue(op.getInput());
4017 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4018 if (inWidth == op.getAmount())
4019 return setLowering(op, Value());
4020 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4021 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4024LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4025 auto cond = getLoweredValue(op.getSel());
4026 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4027 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4028 if (!cond || !ifTrue || !ifFalse)
4031 if (isa<ClockType>(op.getType()))
4032 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4033 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4037LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4038 auto cond = getLoweredValue(op.getSel());
4039 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4040 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4041 if (!cond || !ifTrue || !ifFalse)
4044 auto val = builder.create<
comb::MuxOp>(ifTrue.getType(), cond, ifTrue,
4046 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4049LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4050 auto sel = getLoweredValue(op.getSel());
4051 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4052 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4053 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4054 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4055 if (!sel || !v3 || !v2 || !v1 || !v0)
4057 Value array[] = {v3, v2, v1, v0};
4060 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4079Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4080 assert(op->getNumResults() == 1 &&
"only expect a single result");
4081 auto val = op->getResult(0);
4085 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4092 OpBuilder::InsertionGuard guard(builder);
4093 builder.setInsertionPoint(op);
4094 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4095 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4097 op->getContext(),
nullptr, 0,
4100 builder.create<hw::WireOp>(operand, namehint + Twine(idx), innerSym);
4101 op->setOperand(idx, wire);
4105 auto assignOp = builder.create<
sv::AssignOp>(valWire, val);
4106 sv::setSVAttributes(assignOp,
4107 sv::SVAttributeAttr::get(builder.getContext(),
4108 "synopsys infer_mux_override",
4113Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4115 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4120 if (!llvm::isPowerOf2_64(size)) {
4121 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4123 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4125 Value temp2[] = {ext.getResult(), array};
4131 return inBoundsRead;
4134LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4136 auto index = getLoweredAndExtOrTruncValue(
4138 UIntType::get(op.getContext(),
4143 SmallVector<Value> loweredInputs;
4144 loweredInputs.reserve(op.getInputs().size());
4145 for (
auto input : op.getInputs()) {
4146 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4149 loweredInputs.push_back(lowered);
4153 return setLowering(op, createArrayIndexing(array, index));
4156LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4157 auto resultTy =
lowerType(op.getType());
4161 SmallVector<Value, 4> operands;
4162 operands.reserve(op.getSubstitutions().size());
4163 for (
auto operand : op.getSubstitutions()) {
4164 auto lowered = getLoweredValue(operand);
4167 operands.push_back(lowered);
4170 ArrayAttr symbols = op.getSymbolsAttr();
4172 symbols = ArrayAttr::get(op.getContext(), {});
4174 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4178LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4182 Type baseType = op.getType().getType();
4185 if (isa<ClockType>(baseType))
4186 xmrType = builder.getIntegerType(1);
4190 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4191 op.getRef(), op.getVerbatimSuffixAttr());
4194LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4198 if (isa<ClockType>(op.getType()))
4199 xmrType = builder.getIntegerType(1);
4203 auto xmr = builder.create<sv::XMRRefOp>(
4204 sv::InOutType::get(xmrType), op.getRef(), op.getVerbatimSuffixAttr());
4205 auto readXmr = getReadValue(xmr);
4206 if (!isa<ClockType>(op.getType()))
4207 return setLowering(op, readXmr);
4208 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4215LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
4227FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
4228 auto srcType = srcVal.getType();
4229 auto dstType = destVal.getType();
4230 if (srcType != dstType &&
4231 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
4232 srcVal = builder.create<
hw::BitcastOp>(destVal.getType(), srcVal);
4234 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
4235 .Case<hw::WireOp>([&](
auto op) {
4236 maybeUnused(op.getInput());
4237 op.getInputMutable().assign(srcVal);
4240 .Case<seq::FirRegOp>([&](
auto op) {
4241 maybeUnused(op.getNext());
4242 op.getNextMutable().assign(srcVal);
4245 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
4248 op.emitOpError(
"used as connect destination");
4251 .Default([](
auto) {
return false; });
4254LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
4255 auto dest = op.getDest();
4257 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
4258 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
4260 return handleZeroBit(op.getSrc(), []() { return success(); });
4262 auto destVal = getPossiblyInoutLoweredValue(dest);
4266 auto result = lowerConnect(destVal, srcVal);
4274 if (updateIfBackedge(destVal, srcVal))
4277 if (!isa<hw::InOutType>(destVal.getType()))
4278 return op.emitError(
"destination isn't an inout type");
4284LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
4285 auto dest = op.getDest();
4286 auto srcVal = getLoweredValue(op.getSrc());
4288 return handleZeroBit(op.getSrc(), []() { return success(); });
4290 auto destVal = getPossiblyInoutLoweredValue(dest);
4294 auto result = lowerConnect(destVal, srcVal);
4302 if (updateIfBackedge(destVal, srcVal))
4305 if (!isa<hw::InOutType>(destVal.getType()))
4306 return op.emitError(
"destination isn't an inout type");
4312LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
4313 auto srcVal = getLoweredValue(op.getSrc());
4317 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4321 if (!isa<hw::InOutType>(destVal.getType()))
4322 return op.emitError(
"destination isn't an inout type");
4325 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4326 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4327 addToInitialBlock([&]() { builder.create<sv::ForceOp>(destVal, srcVal); });
4332LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
4333 auto src = getLoweredNonClockValue(op.getSrc());
4334 auto clock = getLoweredNonClockValue(op.getClock());
4335 auto pred = getLoweredValue(op.getPredicate());
4336 if (!src || !clock || !pred)
4339 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4344 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4345 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4346 addToAlwaysBlock(clock, [&]() {
4347 addIfProceduralBlock(
4348 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4353LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
4354 auto src = getLoweredNonClockValue(op.getSrc());
4355 auto pred = getLoweredValue(op.getPredicate());
4359 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4364 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4365 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4366 addToInitialBlock([&]() {
4367 addIfProceduralBlock(
4368 pred, [&]() { builder.create<sv::ForceOp>(destVal, src); });
4373LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
4374 auto clock = getLoweredNonClockValue(op.getClock());
4375 auto pred = getLoweredValue(op.getPredicate());
4376 if (!clock || !pred)
4379 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4384 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4385 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4386 addToAlwaysBlock(clock, [&]() {
4387 addIfProceduralBlock(pred,
4388 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4393LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
4394 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
4395 auto pred = getLoweredValue(op.getPredicate());
4396 if (!destVal || !pred)
4400 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4401 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4402 addToInitialBlock([&]() {
4403 addIfProceduralBlock(pred,
4404 [&]() { builder.create<sv::ReleaseOp>(destVal); });
4412LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
4413 auto clock = getLoweredNonClockValue(op.getClock());
4414 auto cond = getLoweredValue(op.getCond());
4415 if (!clock || !cond)
4418 SmallVector<Value, 4> operands;
4419 operands.reserve(op.getSubstitutions().size());
4420 for (
auto operand : op.getSubstitutions()) {
4421 Value loweredValue = getLoweredFmtOperand(operand);
4424 operands.push_back(loweredValue);
4428 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4429 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
4430 addToAlwaysBlock(clock, [&]() {
4431 circuitState.usedPrintf =
true;
4432 circuitState.addFragment(theModule,
"PRINTF_FD_FRAGMENT");
4433 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
4437 builder.create<sv::MacroRefExprOp>(cond.getType(),
"PRINTF_COND_");
4438 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
4440 addIfProceduralBlock(ifCond, [&]() {
4442 Value fd = builder.create<sv::MacroRefExprOp>(
4443 builder.getIntegerType(32),
"PRINTF_FD_");
4444 builder.create<sv::FWriteOp>(fd, op.getFormatString(), operands);
4454LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
4455 auto clock = getLoweredValue(op.getClock());
4456 auto cond = getLoweredValue(op.getCond());
4457 if (!clock || !cond)
4460 circuitState.usedStopCond =
true;
4461 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4464 builder.create<sv::MacroRefExprOp>(cond.getType(),
"STOP_COND_");
4465 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
4467 if (op.getExitCode())
4468 builder.create<sim::FatalOp>(clock, exitCond);
4470 builder.create<sim::FinishOp>(clock, exitCond);
4478template <
typename... Args>
4480 StringRef opName, Args &&...args) {
4481 if (opName ==
"assert")
4482 return builder.create<sv::AssertOp>(std::forward<Args>(args)...);
4483 if (opName ==
"assume")
4484 return builder.create<sv::AssumeOp>(std::forward<Args>(args)...);
4485 if (opName ==
"cover")
4486 return builder.create<sv::CoverOp>(std::forward<Args>(args)...);
4487 llvm_unreachable(
"unknown verification op");
4493template <
typename... Args>
4495 StringRef opName, Args &&...args) {
4496 if (opName ==
"assert")
4497 return builder.create<sv::AssertConcurrentOp>(std::forward<Args>(args)...);
4498 if (opName ==
"assume")
4499 return builder.create<sv::AssumeConcurrentOp>(std::forward<Args>(args)...);
4500 if (opName ==
"cover")
4501 return builder.create<sv::CoverConcurrentOp>(std::forward<Args>(args)...);
4502 llvm_unreachable(
"unknown verification op");
4523LogicalResult FIRRTLLowering::lowerVerificationStatement(
4524 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
4525 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
4526 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
4527 StringRef opName = op->getName().stripDialect();
4530 ArrayRef<Attribute> guards{};
4531 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
4532 guards = guardsAttr.getValue();
4534 auto isCover = isa<CoverOp>(op);
4535 auto clock = getLoweredNonClockValue(opClock);
4536 auto enable = getLoweredValue(opEnable);
4537 auto predicate = getLoweredValue(opPredicate);
4538 if (!clock || !enable || !predicate)
4542 if (opNameAttr && !opNameAttr.getValue().empty())
4544 StringAttr prefixedLabel;
4547 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
4550 SmallVector<Value> messageOps;
4554 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
4555 flavor = VerificationFlavor::None;
4557 if (flavor == VerificationFlavor::None) {
4561 auto format = op->getAttrOfType<StringAttr>(
"format");
4563 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
4564 if (!isa<AssertOp>(op))
4565 return op->emitError()
4566 <<
"ifElseFatal format cannot be used for non-assertions";
4567 flavor = VerificationFlavor::IfElseFatal;
4568 }
else if (isConcurrent)
4569 flavor = VerificationFlavor::SVA;
4571 flavor = VerificationFlavor::Immediate;
4574 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
4575 message = opMessageAttr;
4576 for (
auto operand : opOperands) {
4577 auto loweredValue = getLoweredFmtOperand(operand);
4585 if (flavor == VerificationFlavor::SVA)
4586 loweredValue = builder.create<sv::SampledOp>(loweredValue);
4587 messageOps.push_back(loweredValue);
4593 case VerificationFlavor::Immediate: {
4595 auto deferImmediate = circt::sv::DeferAssertAttr::get(
4596 builder.getContext(), circt::sv::DeferAssert::Immediate);
4597 addToAlwaysBlock(clock, [&]() {
4598 addIfProceduralBlock(enable, [&]() {
4600 prefixedLabel, message, messageOps);
4605 case VerificationFlavor::IfElseFatal: {
4606 assert(isa<AssertOp>(op) &&
"only assert is expected");
4609 auto boolType = IntegerType::get(builder.getContext(), 1);
4610 predicate = comb::createOrFoldNot(predicate, builder,
true);
4611 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4613 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4614 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
4615 addToAlwaysBlock(clock, [&]() {
4616 addIfProceduralBlock(predicate, [&]() {
4617 circuitState.usedStopCond =
true;
4618 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
4620 circuitState.usedAssertVerboseCond =
true;
4621 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
4623 addIfProceduralBlock(
4624 builder.create<sv::MacroRefExprOp>(boolType,
4625 "ASSERT_VERBOSE_COND_"),
4626 [&]() { builder.create<sv::ErrorOp>(message, messageOps); });
4627 addIfProceduralBlock(
4628 builder.create<sv::MacroRefExprOp>(boolType,
"STOP_COND_"),
4629 [&]() { builder.create<sv::FatalOp>(); });
4635 case VerificationFlavor::SVA: {
4640 comb::createOrFoldNot(enable, builder,
true);
4642 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4644 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
4648 sv::EventControl event;
4649 switch (opEventControl) {
4650 case EventControl::AtPosEdge:
4651 event = circt::sv::EventControl::AtPosEdge;
4653 case EventControl::AtEdge:
4654 event = circt::sv::EventControl::AtEdge;
4656 case EventControl::AtNegEdge:
4657 event = circt::sv::EventControl::AtNegEdge;
4663 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
4664 predicate, prefixedLabel, message, messageOps);
4667 case VerificationFlavor::None:
4669 "flavor `None` must be converted into one of concreate flavors");
4676 return emitGuards(op->getLoc(), guards,
emit);
4680LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
4681 return lowerVerificationStatement(
4682 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
4683 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4684 op.getIsConcurrent(), op.getEventControl());
4688LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
4689 return lowerVerificationStatement(
4690 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
4691 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4692 op.getIsConcurrent(), op.getEventControl());
4696LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
4697 return lowerVerificationStatement(
4698 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
4699 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
4700 op.getIsConcurrent(), op.getEventControl());
4704LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
4709 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
4710 ArrayRef<Attribute> guards =
4711 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
4713 auto label = op.getNameAttr();
4714 StringAttr assumeLabel;
4715 if (label && !label.empty())
4717 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
4718 auto predicate = getLoweredValue(op.getPredicate());
4719 auto enable = getLoweredValue(op.getEnable());
4720 auto notEnable = comb::createOrFoldNot(enable, builder,
true);
4721 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
4723 SmallVector<Value> messageOps;
4724 for (
auto operand : op.getSubstitutions()) {
4725 auto loweredValue = getLoweredValue(operand);
4726 if (!loweredValue) {
4730 loweredValue = getOrCreateIntConstant(1, 0);
4732 messageOps.push_back(loweredValue);
4734 return emitGuards(op.getLoc(), guards, [&]() {
4735 builder.create<sv::AlwaysOp>(
4736 ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate), [&]() {
4737 if (op.getMessageAttr().getValue().empty())
4738 buildImmediateVerifOp(
4739 builder,
"assume", predicate,
4740 circt::sv::DeferAssertAttr::get(
4741 builder.getContext(), circt::sv::DeferAssert::Immediate),
4744 buildImmediateVerifOp(
4745 builder,
"assume", predicate,
4746 circt::sv::DeferAssertAttr::get(
4747 builder.getContext(), circt::sv::DeferAssert::Immediate),
4748 assumeLabel, op.getMessageAttr(), messageOps);
4753LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
4755 if (op.getAttached().size() < 2)
4758 SmallVector<Value, 4> inoutValues;
4759 for (
auto v : op.getAttached()) {
4760 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
4761 if (!inoutValues.back()) {
4765 inoutValues.pop_back();
4769 if (!isa<hw::InOutType>(inoutValues.back().getType()))
4770 return op.emitError(
"operand isn't an inout type");
4773 if (inoutValues.size() < 2)
4784 bool isAttachInternalOnly =
4785 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
4787 if (isAttachInternalOnly) {
4788 auto v0 = inoutValues.front();
4789 for (
auto v : inoutValues) {
4792 v.replaceAllUsesWith(v0);
4799 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
4800 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
4805 SmallVector<Value, 4> values;
4806 for (
auto inoutValue : inoutValues)
4807 values.push_back(getReadValue(inoutValue));
4809 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
4810 for (
size_t i2 = 0; i2 != e; ++i2)
4812 builder.create<
sv::AssignOp>(inoutValues[i1], values[i2]);
4821 builder.create<sv::VerbatimOp>(
4822 "`error \"Verilator does not support alias and thus "
4824 "arbitrarily connect bidirectional wires and ports\"");
4826 [&]() { builder.create<sv::AliasOp>(inoutValues); });
4832LogicalResult FIRRTLLowering::fixupLTLOps() {
4833 if (ltlOpFixupWorklist.empty())
4835 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
4839 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
4840 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
4841 if (isa<
hw::WireOp>(user))
4842 ltlOpFixupWorklist.insert(user);
4845 while (!ltlOpFixupWorklist.empty()) {
4846 auto *op = ltlOpFixupWorklist.pop_back_val();
4849 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
4850 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
4851 SmallVector<Type, 2> types;
4852 auto result = opIntf.inferReturnTypes(
4853 op->getContext(), op->getLoc(), op->getOperands(),
4854 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
4858 assert(types.size() == op->getNumResults());
4862 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
4863 if (result.getType() == type)
4865 LLVM_DEBUG(llvm::dbgs()
4866 <<
" - Result #" << result.getResultNumber() <<
" from "
4867 << result.getType() <<
" to " << type <<
"\n");
4868 result.setType(type);
4869 for (
auto *user : result.getUsers())
4871 ltlOpFixupWorklist.insert(user);
4876 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
4877 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
4878 wireOp.replaceAllUsesWith(wireOp.getInput());
4879 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
4880 if (wireOp.use_empty())
4887 SmallPtrSet<Operation *, 4> usersReported;
4888 for (
auto *user : op->getUsers()) {
4889 if (!usersReported.insert(user).second)
4891 if (isa<ltl::LTLDialect, verif::VerifDialect>(user->getDialect()))
4893 if (isa<hw::WireOp>(user))
4895 auto d = op->emitError(
4896 "verification operation used in a non-verification context");
4897 d.attachNote(user->getLoc())
4898 <<
"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