37#include "mlir/IR/BuiltinOps.h"
38#include "mlir/IR/BuiltinTypes.h"
39#include "mlir/IR/ImplicitLocOpBuilder.h"
40#include "mlir/IR/Threading.h"
41#include "mlir/Pass/Pass.h"
42#include "llvm/ADT/DenseMap.h"
43#include "llvm/Support/Debug.h"
44#include "llvm/Support/Mutex.h"
45#include "llvm/Support/Path.h"
47#define DEBUG_TYPE "lower-to-hw"
50#define GEN_PASS_DEF_LOWERFIRRTLTOHW
51#include "circt/Conversion/Passes.h.inc"
55using namespace firrtl;
56using circt::comb::ICmpPredicate;
65 auto ftype = dyn_cast<FIRRTLBaseType>(type);
66 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
73 for (
auto operand : op.getAttached()) {
75 operand.getDefiningOp<InstanceOp>())
79 if (!operand.hasOneUse() || singleSource)
81 singleSource = operand;
90 auto checkTypes = [](Operation *op) -> WalkResult {
92 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
93 return op->emitError(
"Found unhandled FIRRTL operation '")
94 << op->getName() <<
"'";
97 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
98 if (llvm::any_of(types, [](Type type) {
99 return isa<FIRRTLDialect>(type.getDialect());
101 return op->emitOpError(
"found unhandled FIRRTL type");
106 if (failed(checkTypeRange(op->getOperandTypes())) ||
107 failed(checkTypeRange(op->getResultTypes())))
108 return WalkResult::interrupt();
111 for (
auto ®ion : op->getRegions())
112 for (
auto &block : region)
113 if (failed(checkTypeRange(block.getArgumentTypes())))
114 return WalkResult::interrupt();
117 return WalkResult::advance();
120 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
127 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
128 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
134 ImplicitLocOpBuilder &builder) {
136 if (BundleType bundle = dyn_cast<BundleType>(type))
137 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
139 if (type != val.getType())
140 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
148 ImplicitLocOpBuilder &builder) {
150 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
152 val = mlir::UnrealizedConversionCastOp::create(
154 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
156 val = builder.createOrFold<HWStructCastOp>(type, val);
161 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
167 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
173 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
174 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
175 dst->setAttr(
"sv.namehint", attr);
181class FileDescriptorInfo {
183 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
184 : outputFileFormat(outputFileName), substitutions(substitutions) {
186 substitutions.empty() &&
187 "substitutions must be empty when output file name is empty");
190 FileDescriptorInfo() =
default;
193 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
196 bool isDefaultFd()
const {
return !outputFileFormat; }
198 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
199 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
203 StringAttr outputFileFormat = {};
206 mlir::ValueRange substitutions;
216struct FIRRTLModuleLowering;
219struct CircuitLoweringState {
221 std::atomic<bool> usedPrintf{
false};
222 std::atomic<bool> usedAssertVerboseCond{
false};
223 std::atomic<bool> usedStopCond{
false};
224 std::atomic<bool> usedFileDescriptorLib{
false};
226 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
231 : circuitOp(circuitOp), instanceGraph(instanceGraph),
232 enableAnnotationWarning(enableAnnotationWarning),
233 lowerToCore(lowerToCore), verificationFlavor(verificationFlavor),
234 nlaTable(nlaTable), macroTable(macroTable) {
235 auto *
context = circuitOp.getContext();
239 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
240 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
241 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
242 context, dirName.getValue(),
false,
true);
246 if (
auto module = dyn_cast<FModuleLike>(op)) {
258 testHarness =
nullptr;
259 }
else if (dut == testHarness) {
260 testHarness =
nullptr;
265 auto inDUT = [&](igraph::ModuleOpInterface child) {
267 if (
auto inst = instRec->getInstance<InstanceOp>())
268 return inst.getLowerToBind() || inst.getDoNotPrint();
271 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
272 return getInstanceGraph().isAncestor(child, parent, isPhony);
275 circuitOp->walk([&](FModuleLike moduleOp) {
277 dutModules.insert(moduleOp);
281 Operation *getNewModule(Operation *oldModule) {
282 auto it = oldToNewModuleMap.find(oldModule);
283 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
286 Operation *getOldModule(Operation *newModule) {
287 auto it = newToOldModuleMap.find(newModule);
288 return it != newToOldModuleMap.end() ? it->second :
nullptr;
291 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
292 oldToNewModuleMap[oldFMod] = newHWMod;
293 newToOldModuleMap[newHWMod] = oldFMod;
298 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
304 void addBind(sv::BindOp op) {
305 std::lock_guard<std::mutex> lock(bindsMutex);
311 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
314 auto hwAlias = typeAliases.getTypedecl(firAliasType);
317 assert(!typeAliases.isFrozen() &&
318 "type aliases cannot be generated after its frozen");
319 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
322 FModuleLike getDut() {
return dut; }
323 FModuleLike getTestHarness() {
return testHarness; }
329 bool isInDUT(igraph::ModuleOpInterface child) {
330 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
331 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
332 return dutModules.contains(child);
335 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
340 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
347 Type
lowerType(Type type, Location loc) {
348 return ::lowerType(type, loc,
349 [&](Type rawType, BaseTypeAliasType firrtlType,
350 Location typeLoc) -> hw::TypeAliasType {
351 return getTypeAlias(rawType, firrtlType, typeLoc);
357 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
358 auto it = verbatimSourcesByFileName.find(fileName);
359 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
364 void registerVerbatimSource(StringRef fileName,
366 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
367 verbatimSourcesByFileName[fileName] = verbatimOp;
371 emit::FileOp getEmitFileForFile(StringRef fileName) {
372 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
373 auto it = emitFilesByFileName.find(fileName);
374 return it != emitFilesByFileName.end() ? it->second :
nullptr;
379 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
380 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
381 emitFilesByFileName[fileName] = fileOp;
385 friend struct FIRRTLModuleLowering;
386 friend struct FIRRTLLowering;
387 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
388 void operator=(
const CircuitLoweringState &) =
delete;
391 DenseMap<Operation *, Operation *> oldToNewModuleMap;
394 DenseMap<Operation *, Operation *> newToOldModuleMap;
405 DenseSet<igraph::ModuleOpInterface> dutModules;
409 StringSet<> pendingAnnotations;
410 const bool enableAnnotationWarning;
411 std::mutex annotationPrintingMtx;
413 const bool lowerToCore;
418 SmallVector<sv::BindOp> binds;
421 std::mutex bindsMutex;
429 FModuleLike testHarness;
432 hw::OutputFileAttr testBenchDirectory;
436 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
439 SetVector<StringAttr> macroDeclNames;
440 std::mutex macroDeclMutex;
442 void addMacroDecl(StringAttr name) {
443 std::unique_lock<std::mutex> lock(macroDeclMutex);
444 macroDeclNames.insert(name);
449 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
450 llvm::sys::SmartMutex<true> fragmentsMutex;
454 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
457 void addFragment(
hw::HWModuleOp module, FlatSymbolRefAttr fragment) {
458 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
459 fragments[module].insert(fragment);
474 struct RecordTypeAlias {
476 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
478 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
479 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
480 if (iter != firrtlTypeToAliasTypeMap.end())
485 bool isFrozen() {
return frozen; }
487 void freeze() { frozen =
true; }
489 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
491 assert(!frozen &&
"Record already frozen, cannot be updated");
494 auto b = ImplicitLocOpBuilder::atBlockBegin(
496 &circuitOp->getParentRegion()->getBlocks().back());
498 b, b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
499 typeScope.getBodyRegion().push_back(
new Block());
501 auto typeName = firAlias.getName();
506 StringAttr::get(typeName.getContext(),
507 typeDeclNamespace.newName(typeName.getValue()));
509 auto typeScopeBuilder =
510 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
512 typeName, rawType,
nullptr);
513 auto hwAlias = hw::TypeAliasType::get(
514 SymbolRefAttr::get(typeScope.getSymNameAttr(),
515 {FlatSymbolRefAttr::get(typeDecl)}),
517 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
518 assert(insert.second &&
"Entry already exists, insert failed");
519 return insert.first->second;
528 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
536 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
539 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
540 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
543 llvm::StringMap<emit::FileOp> emitFilesByFileName;
544 llvm::sys::SmartMutex<true> emitFilesMutex;
550void CircuitLoweringState::processRemainingAnnotations(
552 if (!enableAnnotationWarning || annoSet.
empty())
554 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
556 for (
auto a : annoSet) {
557 auto inserted = pendingAnnotations.insert(a.getClass());
558 if (!inserted.second)
579 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
584 extractGrandCentralAnnoClass,
587 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
588 extractCoverageAnnoClass,
592 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
593 blackBoxTargetDirAnnoClass))
596 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
597 "' still remaining after LowerToHW");
603struct FIRRTLModuleLowering
604 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
606 void runOnOperation()
override;
607 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
608 void setLowerToCore() { lowerToCore =
true; }
610 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
613 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
615 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
616 SmallVectorImpl<hw::PortInfo> &ports,
617 Operation *moduleOp, StringRef moduleName,
619 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
621 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
624 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
626 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
629 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
632 Block *topLevelModule,
636 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
640 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
642 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
644 LogicalResult lowerFileBody(emit::FileOp op);
651std::unique_ptr<mlir::Pass>
655 auto pass = std::make_unique<FIRRTLModuleLowering>();
656 if (enableAnnotationWarning)
657 pass->setEnableAnnotationWarning();
659 pass->setLowerToCore();
660 pass->verificationFlavor = verificationFlavor;
666void FIRRTLModuleLowering::runOnOperation() {
670 auto *topLevelModule = getOperation().getBody();
674 for (
auto &op : *topLevelModule) {
675 if ((circuit = dyn_cast<CircuitOp>(&op)))
682 auto *circuitBody = circuit.getBodyBlock();
686 CircuitLoweringState state(circuit, enableAnnotationWarning, lowerToCore,
687 verificationFlavor, getAnalysis<InstanceGraph>(),
688 &getAnalysis<NLATable>(),
689 getAnalysis<InstanceChoiceMacroTable>());
691 SmallVector<Operation *, 32> opsToProcess;
694 state.processRemainingAnnotations(circuit, circuitAnno);
697 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
699 TypeSwitch<Operation *, LogicalResult>(&op)
700 .Case<FModuleOp>([&](
auto module) {
701 auto loweredMod = lowerModule(module, topLevelModule, state);
705 state.recordModuleMapping(&op, loweredMod);
706 opsToProcess.push_back(loweredMod);
708 module.walk([&](Operation *op) {
709 for (auto res : op->getResults()) {
711 type_dyn_cast<BaseTypeAliasType>(res.getType()))
712 state.lowerType(aliasType, op->getLoc());
715 return lowerModulePortsAndMoveBody(module, loweredMod, state);
717 .Case<FExtModuleOp>([&](
auto extModule) {
719 lowerExtModule(extModule, topLevelModule, state);
722 state.recordModuleMapping(&op, loweredMod);
725 .Case<FMemModuleOp>([&](
auto memModule) {
727 lowerMemModule(memModule, topLevelModule, state);
730 state.recordModuleMapping(&op, loweredMod);
733 .Case<FormalOp>([&](
auto oldOp) {
734 auto builder = OpBuilder::atBlockEnd(topLevelModule);
735 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
737 oldOp.getParametersAttr());
738 newOp.getBody().emplaceBlock();
739 state.recordModuleMapping(oldOp, newOp);
740 opsToProcess.push_back(newOp);
743 .Case<SimulationOp>([&](
auto oldOp) {
744 auto loc = oldOp.getLoc();
745 auto builder = OpBuilder::atBlockEnd(topLevelModule);
746 auto newOp = verif::SimulationOp::create(
747 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
748 auto &body = newOp.getRegion().emplaceBlock();
749 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
750 body.addArgument(builder.getI1Type(), loc);
751 state.recordModuleMapping(oldOp, newOp);
752 opsToProcess.push_back(newOp);
755 .Case<emit::FileOp>([&](
auto fileOp) {
756 fileOp->moveBefore(topLevelModule, topLevelModule->end());
757 opsToProcess.push_back(fileOp);
760 .Case<OptionOp, OptionCaseOp>([&](
auto) {
764 .Default([&](Operation *op) {
769 op->moveBefore(topLevelModule, topLevelModule->end());
775 return signalPassFailure();
778 state.typeAliases.freeze();
783 SmallVector<Attribute> dutHierarchyFiles;
784 SmallVector<Attribute> testHarnessHierarchyFiles;
785 circuitAnno.removeAnnotations([&](
Annotation annotation) {
786 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
787 auto file = hw::OutputFileAttr::getFromFilename(
789 annotation.
getMember<StringAttr>(
"filename").getValue(),
791 dutHierarchyFiles.push_back(file);
794 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
795 auto file = hw::OutputFileAttr::getFromFilename(
797 annotation.
getMember<StringAttr>(
"filename").getValue(),
801 if (state.getTestHarness())
802 testHarnessHierarchyFiles.push_back(file);
804 dutHierarchyFiles.push_back(file);
810 if (!dutHierarchyFiles.empty())
811 state.getNewModule(state.getDut())
813 ArrayAttr::get(&getContext(), dutHierarchyFiles));
814 if (!testHarnessHierarchyFiles.empty())
815 state.getNewModule(state.getTestHarness())
817 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
821 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
825 return signalPassFailure();
828 for (
auto bind : state.binds) {
833 for (
auto &[module, fragments] : state.fragments)
835 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
838 for (
auto oldNew : state.oldToNewModuleMap)
839 oldNew.first->erase();
841 if (!state.macroDeclNames.empty()) {
842 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
843 for (
auto name : state.macroDeclNames) {
844 sv::MacroDeclOp::create(b, name);
849 lowerFileHeader(circuit, state);
856void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
857 CircuitLoweringState &state) {
860 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
864 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
865 StringRef defineTrue =
"",
866 StringRef defineFalse = StringRef()) {
867 if (!defineFalse.data()) {
868 assert(defineTrue.data() &&
"didn't define anything");
870 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
875 if (defineTrue.data())
876 sv::MacroDefOp::create(b, defName, defineTrue);
878 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
883 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
885 b, guard, [] {}, body);
888 if (state.usedFileDescriptorLib)
889 sv::emitFileDescriptorRuntime(op->getParentOp(), b);
891 if (state.usedPrintf) {
892 sv::MacroDeclOp::create(b,
"PRINTF_COND");
893 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
894 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
895 sv::VerbatimOp::create(
896 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
898 emitGuard(
"PRINTF_COND_", [&]() {
899 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
904 if (state.usedAssertVerboseCond) {
905 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
906 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
907 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
908 sv::VerbatimOp::create(
909 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
910 "gate to assert error printing.");
911 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
912 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
913 "(`ASSERT_VERBOSE_COND)",
"1");
918 if (state.usedStopCond) {
919 sv::MacroDeclOp::create(b,
"STOP_COND");
920 sv::MacroDeclOp::create(b,
"STOP_COND_");
921 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
922 sv::VerbatimOp::create(
923 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
924 "to stop conditions.");
925 emitGuard(
"STOP_COND_", [&]() {
926 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
933FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
934 SmallVectorImpl<hw::PortInfo> &ports,
935 Operation *moduleOp, StringRef moduleName,
937 ports.reserve(firrtlPorts.size());
939 size_t numResults = 0;
940 for (
auto e :
llvm::enumerate(firrtlPorts)) {
942 size_t portNo = e.index();
947 if (firrtlPort.
sym.size() > 1 ||
948 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
949 return emitError(firrtlPort.
loc)
950 <<
"cannot lower aggregate port " << firrtlPort.
name
951 <<
" with field sensitive symbols, HW dialect does not support "
952 "per field symbols yet.";
953 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
955 if (hadDontTouch && !hwPort.
getSym()) {
956 if (hwPort.
type.isInteger(0)) {
957 if (enableAnnotationWarning) {
958 mlir::emitWarning(firrtlPort.
loc)
959 <<
"zero width port " << hwPort.
name
960 <<
" has dontTouch annotation, removing anyway";
966 hw::InnerSymAttr::get(StringAttr::get(
967 moduleOp->getContext(),
968 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
969 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
970 moduleOp->getContext());
975 moduleOp->emitError(
"cannot lower this port type to HW");
981 if (hwPort.
type.isInteger(0)) {
982 auto sym = hwPort.
getSym();
983 if (sym && !sym.empty()) {
984 return mlir::emitError(firrtlPort.
loc)
985 <<
"zero width port " << hwPort.
name
986 <<
" is referenced by name [" << sym
987 <<
"] (e.g. in an XMR) but must be removed";
994 hwPort.
dir = hw::ModulePort::Direction::Output;
995 hwPort.
argNum = numResults++;
996 }
else if (firrtlPort.
isInput()) {
997 hwPort.
dir = hw::ModulePort::Direction::Input;
998 hwPort.
argNum = numArgs++;
1002 hwPort.
type = hw::InOutType::get(hwPort.
type);
1003 hwPort.
dir = hw::ModulePort::Direction::InOut;
1004 hwPort.
argNum = numArgs++;
1006 hwPort.
loc = firrtlPort.
loc;
1007 ports.push_back(hwPort);
1017 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1018 return cast<ParamDeclAttr>(a);
1023 Builder builder(module);
1028 SmallVector<Attribute> newParams;
1029 for (
const ParamDeclAttr &entry : params) {
1030 auto name = entry.getName();
1031 auto type = entry.getType();
1032 auto value = ignoreValues ? Attribute() : entry.getValue();
1034 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1035 newParams.push_back(paramAttr);
1037 return builder.getArrayAttr(newParams);
1040bool FIRRTLModuleLowering::handleForceNameAnnos(
1043 bool failed =
false;
1046 if (!anno.
isClass(forceNameAnnoClass))
1049 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1056 auto diag = oldModule.emitOpError()
1057 <<
"contains a '" << forceNameAnnoClass
1058 <<
"' that is not a non-local annotation";
1059 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1070 auto diag = oldModule.emitOpError()
1071 <<
"contains a '" << forceNameAnnoClass
1072 <<
"' whose non-local symbol, '" << sym
1073 <<
"' does not exist in the circuit";
1074 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1087 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1089 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1090 if (!inserted.second &&
1091 (anno.
getMember(
"name") != (inserted.first->second))) {
1092 auto diag = oldModule.emitError()
1093 <<
"contained multiple '" << forceNameAnnoClass
1094 <<
"' with different names: " << inserted.first->second
1095 <<
" was not " << anno.
getMember(
"name");
1096 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1107 FExtModuleOp oldModule, Block *topLevelModule,
1118 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1119 SmallVector<hw::PortInfo, 8> ports;
1120 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1125 StringRef verilogName;
1126 if (
auto defName = oldModule.getDefname())
1127 verilogName = defName.value();
1129 verilogName = oldModule.getName();
1131 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1133 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1134 if (!filesAttr || filesAttr.empty()) {
1135 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1140 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1141 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1142 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1144 if (!primaryFileContent || !primaryOutputFile) {
1145 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1149 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1150 builder.getContext(), primaryOutputFile.getValue());
1152 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1153 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1156 SmallVector<Attribute> additionalFiles;
1160 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1161 auto file = cast<DictionaryAttr>(filesAttr[i]);
1162 auto content = file.getAs<StringAttr>(
"content");
1163 auto outputFile = file.getAs<StringAttr>(
"output_file");
1164 auto fileName = llvm::sys::path::filename(outputFile);
1166 if (!(content && outputFile)) {
1167 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1175 auto fileSymbolName = circuitNamespace.newName(fileName);
1176 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1177 outputFile.getValue(), fileSymbolName);
1178 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1179 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1180 builder.setInsertionPointAfter(
emitFile);
1183 auto ext = llvm::sys::path::extension(outputFile.getValue());
1184 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1185 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1186 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1187 emitFile->setAttr(
"output_file", outputFileAttr);
1191 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1197 parameters = builder.getArrayAttr({});
1199 if (!verbatimSource) {
1200 verbatimSource = sv::SVVerbatimSourceOp::create(
1201 builder, oldModule.getLoc(),
1202 circuitNamespace.newName(primaryFileName.str()),
1203 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1204 additionalFiles.empty() ?
nullptr
1205 : builder.getArrayAttr(additionalFiles),
1206 builder.getStringAttr(verilogName));
1208 SymbolTable::setSymbolVisibility(
1209 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1211 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1214 return verbatimSource;
1218FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1219 Block *topLevelModule,
1221 if (
auto verbatimMod =
1222 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1228 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1229 SmallVector<hw::PortInfo, 8> ports;
1230 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1234 StringRef verilogName;
1235 if (
auto defName = oldModule.getDefname())
1236 verilogName = defName.value();
1239 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1240 auto nameAttr = builder.getStringAttr(oldModule.getName());
1245 auto newModule = hw::HWModuleExternOp::create(
1246 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1247 SymbolTable::setSymbolVisibility(newModule,
1248 SymbolTable::getSymbolVisibility(oldModule));
1250 bool hasOutputPort =
1251 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1252 if (!hasOutputPort &&
1254 internalVerifBlackBoxAnnoClass) &&
1256 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1259 if (
auto extReqs = oldModule.getExternalRequirements();
1260 extReqs && !extReqs.empty())
1261 newModule->setAttr(
"circt.external_requirements", extReqs);
1266 loweringState.processRemainingAnnotations(oldModule, annos);
1271 FExtModuleOp oldModule, Block *topLevelModule,
1276 auto verbatimSource =
1277 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1279 if (!verbatimSource)
1282 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1283 SmallVector<hw::PortInfo, 8> ports;
1284 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1288 StringRef verilogName;
1289 if (
auto defName = oldModule.getDefname())
1290 verilogName = defName.value();
1292 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1294 auto newModule = sv::SVVerbatimModuleOp::create(
1297 builder.getStringAttr(oldModule.getName()),
1299 FlatSymbolRefAttr::get(verbatimSource),
1300 parameters ? parameters : builder.getArrayAttr({}),
1301 verilogName.empty() ? StringAttr{}
1302 : builder.getStringAttr(verilogName));
1304 SymbolTable::setSymbolVisibility(newModule,
1305 SymbolTable::getSymbolVisibility(oldModule));
1307 bool hasOutputPort =
1308 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1309 if (!hasOutputPort &&
1311 internalVerifBlackBoxAnnoClass) &&
1313 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1316 if (
auto extReqs = oldModule.getExternalRequirements();
1317 extReqs && !extReqs.empty())
1318 newModule->setAttr(
"circt.external_requirements", extReqs);
1323 loweringState.processRemainingAnnotations(oldModule, annos);
1328FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1329 Block *topLevelModule,
1332 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1333 SmallVector<hw::PortInfo, 8> ports;
1334 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1339 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1340 auto newModule = hw::HWModuleExternOp::create(
1341 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1342 oldModule.getModuleNameAttr());
1350FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1353 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1354 SmallVector<hw::PortInfo, 8> ports;
1355 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1360 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1361 auto nameAttr = builder.getStringAttr(oldModule.getName());
1363 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1365 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1366 newModule.setCommentAttr(comment);
1369 SmallVector<StringRef, 13> attrNames = {
1370 "annotations",
"convention",
"layers",
1371 "portNames",
"sym_name",
"portDirections",
1372 "portTypes",
"portAnnotations",
"portSymbols",
1373 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1376 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1377 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1379 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1380 return !attrSet.count(namedAttr.getName()) &&
1381 !newModule->getAttrDictionary().contains(namedAttr.getName());
1383 newAttrs.push_back(i);
1385 newModule->setAttrs(newAttrs);
1389 SymbolTable::setSymbolVisibility(newModule,
1390 SymbolTable::getSymbolVisibility(oldModule));
1396 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1400 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1402 if (!newModule->hasAttr(
"output_file"))
1403 newModule->setAttr(
"output_file", testBenchDir);
1404 newModule->setAttr(
"firrtl.extract.do_not_extract",
1405 builder.getUnitAttr());
1406 newModule.setCommentAttr(
1407 builder.getStringAttr(
"VCS coverage exclude_file"));
1413 loweringState.processRemainingAnnotations(oldModule, annos);
1422 Operation *insertPoint) {
1423 if (!value.hasOneUse())
1426 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1427 if (!attach || attach.getNumOperands() != 2)
1431 auto loweredType =
lowerType(value.getType());
1432 if (loweredType.isInteger(0))
1437 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1438 auto *op = attachedValue.getDefiningOp();
1439 if (op && op->getBlock() == insertPoint->getBlock() &&
1440 !op->isBeforeInBlock(insertPoint))
1445 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1461 if (type_isa<AnalogType>(flipValue.getType()))
1464 Operation *connectOp =
nullptr;
1465 for (
auto &use : flipValue.getUses()) {
1468 if (use.getOperandNumber() != 0)
1470 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1476 connectOp = use.getOwner();
1486 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1487 if (loweredType.isInteger(0))
1492 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1494 auto connectSrc = connectOp->getOperand(1);
1497 if (!isa<FIRRTLType>(connectSrc.getType())) {
1503 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1505 mlir::UnrealizedConversionCastOp::create(
1507 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1513 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1515 if (destTy != connectSrc.getType() &&
1516 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1517 isa<BaseTypeAliasType>(destTy))) {
1519 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1521 if (!destTy.isGround()) {
1523 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1525 }
else if (destTy.getBitWidthOrSentinel() !=
1526 type_cast<FIRRTLBaseType>(connectSrc.getType())
1527 .getBitWidthOrSentinel()) {
1530 auto destWidth = destTy.getBitWidthOrSentinel();
1531 assert(destWidth != -1 &&
"must know integer widths");
1532 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1544 SmallVector<SubfieldOp> accesses;
1545 for (
auto *op : structValue.getUsers()) {
1546 assert(isa<SubfieldOp>(op));
1547 auto fieldAccess = cast<SubfieldOp>(op);
1549 fieldAccess.getInput().getType().base().getElementIndex(field);
1550 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1551 accesses.push_back(fieldAccess);
1559LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1562 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1569 bodyBuilder.setInsertionPoint(cursor);
1572 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1573 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1574 "port count mismatch");
1576 SmallVector<Value, 4> outputs;
1579 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1580 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1582 unsigned nextHWInputArg = 0;
1583 int hwPortIndex = -1;
1584 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1586 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1589 type_isa<FIRRTLBaseType>(port.type) &&
1590 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1594 if (!port.isOutput() && !isZeroWidth) {
1597 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1603 oldArg.replaceAllUsesWith(newArg);
1609 if (isZeroWidth && port.isInput()) {
1611 WireOp::create(bodyBuilder, port.type,
1612 "." + port.getName().str() +
".0width_input")
1614 oldArg.replaceAllUsesWith(newArg);
1622 outputs.push_back(value);
1623 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1629 auto newArg = WireOp::create(bodyBuilder, port.type,
1630 "." + port.getName().str() +
".output");
1633 oldArg.replaceAllUsesWith(newArg.getResult());
1636 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1637 if (!resultHWType.isInteger(0)) {
1640 outputs.push_back(output);
1643 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1644 newArg.setInnerSymAttr(sym);
1645 newModule.setPortSymbolAttr(hwPortIndex, {});
1651 outputOp->setOperands(outputs);
1654 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1655 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1656 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1657 oldBlockInstList.begin(), oldBlockInstList.end());
1668FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1670 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1675 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1676 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1677 auto oldModule = cast<FModuleOp>(
1678 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1679 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1682 SmallVector<Value> symbolicInputs;
1683 for (
auto arg : newModule.getBody().getArguments())
1684 symbolicInputs.push_back(
verif::SymbolicValueOp::create(
1685 builder, arg.
getLoc(), arg.getType(),
1689 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1690 newModule.getNameAttr(), symbolicInputs);
1697FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1699 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1702 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1703 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1704 auto oldModule = cast<FModuleLike>(
1705 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1707 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1711 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1712 newOp.getBody()->args_end());
1713 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1714 newModule.getNameAttr(), inputs);
1715 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1725struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1727 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1728 : theModule(module), circuitState(circuitState),
1729 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1730 backedgeBuilder(builder, module.
getLoc()) {}
1732 LogicalResult
run();
1735 Value getOrCreateClockConstant(seq::ClockConst clock);
1736 Value getOrCreateIntConstant(
const APInt &value);
1737 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1738 bool isSigned =
false) {
1739 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1741 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1742 Attribute getZeroAttributeForType(Type type);
1743 Value getZeroValueForType(Type type);
1744 Value getOrCreateXConstant(
unsigned numBits);
1745 Value getOrCreateZConstant(Type type);
1746 Value getPossiblyInoutLoweredValue(Value value);
1747 Value getLoweredValue(Value value);
1748 Value getLoweredNonClockValue(Value value);
1749 Value getLoweredAndExtendedValue(Value value, Type destType);
1750 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1751 LogicalResult setLowering(Value orig, Value result);
1752 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1753 template <
typename ResultOpType,
typename... CtorArgTypes>
1754 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1755 template <
typename ResultOpType,
typename... CtorArgTypes>
1756 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1757 Backedge createBackedge(Location loc, Type type);
1758 Backedge createBackedge(Value orig, Type type);
1759 bool updateIfBackedge(Value dest, Value src);
1762 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1767 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1768 if (forceable.isForceable())
1776 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1777 auto attr = op.getInnerSymAttr();
1781 if (requiresInnerSymbol(op))
1783 op.getContext(), attr, 0,
1791 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1792 Operation *instanceOp,
1793 SmallVectorImpl<Value> &inputOperands);
1795 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1799 Value getReadValue(Value v);
1801 Value getNonClockValue(Value v);
1803 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1804 sv::ResetType resetStyle, sv::EventControl resetEdge,
1805 Value reset,
const std::function<
void(
void)> &body = {},
1806 const std::function<void(
void)> &resetBody = {});
1807 void addToAlwaysBlock(Value clock,
1808 const std::function<
void(
void)> &body = {}) {
1809 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1810 sv::EventControl(), Value(), body,
1811 std::function<
void(
void)>());
1814 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1815 std::function<
void(
void)>
emit);
1816 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1817 std::function<
void(
void)> elseCtor = {});
1818 void addToInitialBlock(std::function<
void(
void)> body);
1819 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1820 std::function<
void(
void)> elseCtor = {});
1821 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1823 bool allowTruncate);
1824 Value createArrayIndexing(Value array, Value index);
1825 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1827 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1828 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1829 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1832 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1833 UnloweredOpResult handleUnloweredOp(Operation *op);
1834 LogicalResult visitExpr(ConstantOp op);
1835 LogicalResult visitExpr(SpecialConstantOp op);
1836 LogicalResult visitExpr(SubindexOp op);
1837 LogicalResult visitExpr(SubaccessOp op);
1838 LogicalResult visitExpr(SubfieldOp op);
1839 LogicalResult visitExpr(VectorCreateOp op);
1840 LogicalResult visitExpr(BundleCreateOp op);
1841 LogicalResult visitExpr(FEnumCreateOp op);
1842 LogicalResult visitExpr(AggregateConstantOp op);
1843 LogicalResult visitExpr(IsTagOp op);
1844 LogicalResult visitExpr(SubtagOp op);
1845 LogicalResult visitExpr(TagExtractOp op);
1848 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1849 return visitUnrealizedConversionCast(castOp);
1854 LogicalResult visitDecl(WireOp op);
1855 LogicalResult visitDecl(NodeOp op);
1856 LogicalResult visitDecl(RegOp op);
1857 LogicalResult visitDecl(RegResetOp op);
1858 LogicalResult visitDecl(MemOp op);
1859 LogicalResult visitDecl(InstanceOp oldInstance);
1860 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1861 LogicalResult visitDecl(VerbatimWireOp op);
1862 LogicalResult visitDecl(ContractOp op);
1865 LogicalResult lowerNoopCast(Operation *op);
1866 LogicalResult visitExpr(AsSIntPrimOp op);
1867 LogicalResult visitExpr(AsUIntPrimOp op);
1868 LogicalResult visitExpr(AsClockPrimOp op);
1869 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1871 LogicalResult visitExpr(HWStructCastOp op);
1872 LogicalResult visitExpr(BitCastOp op);
1874 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1875 LogicalResult visitExpr(CvtPrimOp op);
1876 LogicalResult visitExpr(NotPrimOp op);
1877 LogicalResult visitExpr(NegPrimOp op);
1878 LogicalResult visitExpr(PadPrimOp op);
1879 LogicalResult visitExpr(XorRPrimOp op);
1880 LogicalResult visitExpr(AndRPrimOp op);
1881 LogicalResult visitExpr(OrRPrimOp op);
1884 template <
typename ResultUnsignedOpType,
1885 typename ResultSignedOpType = ResultUnsignedOpType>
1886 LogicalResult lowerBinOp(Operation *op);
1887 template <
typename ResultOpType>
1888 LogicalResult lowerBinOpToVariadic(Operation *op);
1890 template <
typename ResultOpType>
1891 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1893 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1894 ICmpPredicate unsignedOp);
1895 template <
typename SignedOp,
typename Un
signedOp>
1896 LogicalResult lowerDivLikeOp(Operation *op);
1898 LogicalResult visitExpr(CatPrimOp op);
1900 LogicalResult visitExpr(AndPrimOp op) {
1901 return lowerBinOpToVariadic<comb::AndOp>(op);
1903 LogicalResult visitExpr(OrPrimOp op) {
1904 return lowerBinOpToVariadic<comb::OrOp>(op);
1906 LogicalResult visitExpr(XorPrimOp op) {
1907 return lowerBinOpToVariadic<comb::XorOp>(op);
1909 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1910 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1912 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1913 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1915 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1916 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1918 LogicalResult visitExpr(AddPrimOp op) {
1919 return lowerBinOpToVariadic<comb::AddOp>(op);
1921 LogicalResult visitExpr(EQPrimOp op) {
1922 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1924 LogicalResult visitExpr(NEQPrimOp op) {
1925 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1927 LogicalResult visitExpr(LTPrimOp op) {
1928 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1930 LogicalResult visitExpr(LEQPrimOp op) {
1931 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1933 LogicalResult visitExpr(GTPrimOp op) {
1934 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1936 LogicalResult visitExpr(GEQPrimOp op) {
1937 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1940 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1941 LogicalResult visitExpr(MulPrimOp op) {
1942 return lowerBinOpToVariadic<comb::MulOp>(op);
1944 LogicalResult visitExpr(DivPrimOp op) {
1945 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1947 LogicalResult visitExpr(RemPrimOp op) {
1948 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1952 LogicalResult visitExpr(IsXIntrinsicOp op);
1953 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1954 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1955 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1956 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1957 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1958 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1959 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1960 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1961 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1962 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1963 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1964 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1965 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1966 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1967 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1968 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1969 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1970 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1971 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1972 LogicalResult visitExpr(LTLPastIntrinsicOp op);
1973 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1975 template <
typename TargetOp,
typename IntrinsicOp>
1976 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1977 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1978 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1979 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1980 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1981 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1982 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1983 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1986 LogicalResult visitExpr(BitsPrimOp op);
1987 LogicalResult visitExpr(InvalidValueOp op);
1988 LogicalResult visitExpr(HeadPrimOp op);
1989 LogicalResult visitExpr(ShlPrimOp op);
1990 LogicalResult visitExpr(ShrPrimOp op);
1991 LogicalResult visitExpr(DShlPrimOp op) {
1992 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1994 LogicalResult visitExpr(DShrPrimOp op) {
1995 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1997 LogicalResult visitExpr(DShlwPrimOp op) {
1998 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2000 LogicalResult visitExpr(TailPrimOp op);
2001 LogicalResult visitExpr(MuxPrimOp op);
2002 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2003 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2004 LogicalResult visitExpr(MultibitMuxOp op);
2005 LogicalResult visitExpr(VerbatimExprOp op);
2006 LogicalResult visitExpr(XMRRefOp op);
2007 LogicalResult visitExpr(XMRDerefOp op);
2010 LogicalResult visitExpr(TimeOp op);
2011 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2014 LogicalResult lowerVerificationStatement(
2015 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2016 Value enable, StringAttr messageAttr, ValueRange operands,
2017 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2018 LogicalResult lowerVerificationStatementToCore(
2019 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2020 Value enable, StringAttr nameAttr, EventControl eventControl);
2022 LogicalResult visitStmt(SkipOp op);
2024 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2025 LogicalResult visitStmt(ConnectOp op);
2026 LogicalResult visitStmt(MatchingConnectOp op);
2027 LogicalResult visitStmt(ForceOp op);
2029 std::optional<Value> getLoweredFmtOperand(Value operand);
2030 LogicalResult loweredFmtOperands(ValueRange operands,
2031 SmallVectorImpl<Value> &loweredOperands);
2032 FailureOr<Value> lowerSimFormatString(StringRef originalFormatString,
2033 ValueRange operands);
2034 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2038 LogicalResult lowerStatementWithFd(
2039 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2040 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2044 LogicalResult visitPrintfLike(T op,
2045 const FileDescriptorInfo &fileDescriptorInfo,
2046 bool usePrintfCond);
2047 LogicalResult visitStmt(PrintFOp op);
2048 LogicalResult visitStmt(FPrintFOp op);
2049 LogicalResult visitStmt(FFlushOp op);
2050 LogicalResult visitStmt(StopOp op);
2051 LogicalResult visitStmt(AssertOp op);
2052 LogicalResult visitStmt(AssumeOp op);
2053 LogicalResult visitStmt(CoverOp op);
2054 LogicalResult visitStmt(AttachOp op);
2055 LogicalResult visitStmt(RefForceOp op);
2056 LogicalResult visitStmt(RefForceInitialOp op);
2057 LogicalResult visitStmt(RefReleaseOp op);
2058 LogicalResult visitStmt(RefReleaseInitialOp op);
2059 LogicalResult visitStmt(BindOp op);
2061 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2062 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2063 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2065 LogicalResult fixupLTLOps();
2068 return circuitState.lowerType(type, builder.getLoc());
2076 CircuitLoweringState &circuitState;
2079 ImplicitLocOpBuilder builder;
2084 DenseMap<Value, Value> valueMapping;
2088 DenseMap<Value, Value> fromClockMapping;
2092 DenseMap<Attribute, Value> hwConstantMap;
2093 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2097 DenseMap<unsigned, Value> hwConstantXMap;
2098 DenseMap<Type, Value> hwConstantZMap;
2104 DenseMap<Value, Value> readInOutCreated;
2108 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2109 sv::ResetType, sv::EventControl, Value>;
2133 DenseSet<Operation *> maybeUnusedValues;
2135 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2136 void maybeUnused(Value value) {
2137 if (
auto *op = value.getDefiningOp())
2149 SetVector<Operation *> ltlOpFixupWorklist;
2154 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2156 void addToWorklist(Block &block) {
2157 worklist.push_back({block.begin(), block.end()});
2159 void addToWorklist(Region ®ion) {
2160 for (
auto &block :
llvm::reverse(region))
2161 addToWorklist(block);
2172LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2173 OpBuilder b(&getContext());
2174 fileOp->walk([&](Operation *op) {
2175 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2176 b.setInsertionPointAfter(bindOp);
2177 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2185FIRRTLModuleLowering::lowerBody(Operation *op,
2187 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2189 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2191 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2193 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2194 return lowerFileBody(fileOp);
2199LogicalResult FIRRTLLowering::run() {
2202 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2203 if (failed(setLowering(arg, arg)))
2210 addToWorklist(theModule.getBody());
2211 SmallVector<Operation *, 16> opsToRemove;
2213 while (!worklist.empty()) {
2214 auto &[opsIt, opsEnd] = worklist.back();
2215 if (opsIt == opsEnd) {
2216 worklist.pop_back();
2219 Operation *op = &*opsIt++;
2221 builder.setInsertionPoint(op);
2222 builder.setLoc(op->getLoc());
2223 auto done = succeeded(dispatchVisitor(op));
2224 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2226 opsToRemove.push_back(op);
2228 switch (handleUnloweredOp(op)) {
2229 case AlreadyLowered:
2232 opsToRemove.push_back(op);
2234 case LoweringFailure:
2246 for (
auto &[backedge, value] : backedges) {
2247 SmallVector<Location> driverLocs;
2253 if (backedge == value) {
2254 Location edgeLoc = backedge.getLoc();
2255 if (driverLocs.empty()) {
2256 mlir::emitError(edgeLoc,
"sink does not have a driver");
2258 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2259 for (
auto loc : driverLocs)
2260 diag.attachNote(loc) <<
"through driver here";
2266 auto *it = backedges.find(value);
2267 if (it == backedges.end())
2270 driverLocs.push_back(value.getLoc());
2273 if (
auto *defOp = backedge.getDefiningOp())
2274 maybeUnusedValues.erase(defOp);
2275 backedge.replaceAllUsesWith(value);
2283 while (!opsToRemove.empty()) {
2284 auto *op = opsToRemove.pop_back_val();
2289 for (
auto result : op->getResults()) {
2293 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2295 builder.getIntegerType(0), 0);
2296 maybeUnusedValues.insert(zeroI0);
2298 result.replaceAllUsesWith(zeroI0);
2301 if (!op->use_empty()) {
2302 auto d = op->emitOpError(
2303 "still has uses; should remove ops in reverse order of visitation");
2304 SmallPtrSet<Operation *, 2> visited;
2305 for (
auto *user : op->getUsers())
2306 if (visited.insert(user).second)
2308 <<
"used by " << user->
getName() <<
" op";
2311 maybeUnusedValues.erase(op);
2317 SmallVector<Operation *> worklist(maybeUnusedValues.begin(),
2318 maybeUnusedValues.end());
2319 while (!worklist.empty()) {
2320 auto *op = worklist.pop_back_val();
2321 maybeUnusedValues.erase(op);
2322 if (!isOpTriviallyDead(op))
2324 for (
auto operand : op->getOperands())
2325 if (auto *defOp = operand.getDefiningOp())
2326 if (maybeUnusedValues.insert(defOp).second)
2327 worklist.push_back(defOp);
2333 if (failed(fixupLTLOps()))
2344Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2345 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2347 auto &entry = hwConstantMap[attr];
2351 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2352 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2358Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2359 auto attr = builder.getIntegerAttr(
2360 builder.getIntegerType(value.getBitWidth()), value);
2362 auto &entry = hwConstantMap[attr];
2366 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2373Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2376 if (hw::type_isa<IntegerType>(type))
2377 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2379 auto cache = hwAggregateConstantMap.lookup({value, type});
2384 SmallVector<Attribute> values;
2385 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2387 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2388 subType = array.getElementType();
2389 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2390 subType = structType.getElements()[e.index()].type;
2392 assert(
false &&
"type must be either array or struct");
2394 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2398 if (hw::type_isa<hw::ArrayType>(type))
2399 std::reverse(values.begin(), values.end());
2401 auto &entry = hwAggregateConstantMap[{value, type}];
2402 entry = builder.getArrayAttr(values);
2410 const std::function<LogicalResult()> &fn) {
2411 assert(failedOperand &&
"Should be called on the failed operand");
2419Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2421 auto &entry = hwConstantXMap[numBits];
2425 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2426 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2427 entryBuilder.getIntegerType(numBits));
2431Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2432 auto &entry = hwConstantZMap[type];
2434 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2435 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2449Attribute FIRRTLLowering::getZeroAttributeForType(Type type) {
2450 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2451 return builder.getIntegerAttr(intType, 0);
2452 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type)) {
2456 auto element = getZeroAttributeForType(array.getElementType());
2457 SmallVector<Attribute> values(array.getNumElements(), element);
2458 return builder.getArrayAttr(values);
2460 if (
auto structType = hw::type_dyn_cast<hw::StructType>(type)) {
2461 SmallVector<Attribute> values;
2462 values.reserve(structType.getElements().size());
2463 for (
auto &field : structType.getElements())
2464 values.push_back(getZeroAttributeForType(field.type));
2465 return builder.getArrayAttr(values);
2467 llvm_unreachable(
"unsupported lowered type for zero attribute");
2473Value FIRRTLLowering::getZeroValueForType(Type type) {
2474 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2475 return getOrCreateIntConstant(intType.getWidth(), 0);
2476 return hw::AggregateConstantOp::create(
2477 builder, type, cast<ArrayAttr>(getZeroAttributeForType(type)));
2484Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2486 if (
auto lowering = valueMapping.lookup(value)) {
2487 assert(!isa<FIRRTLType>(lowering.getType()) &&
2488 "Lowered value should be a non-FIRRTL value");
2497Value FIRRTLLowering::getLoweredValue(Value value) {
2498 auto result = getPossiblyInoutLoweredValue(value);
2504 if (isa<hw::InOutType>(result.getType()))
2505 return getReadValue(result);
2511Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2512 auto result = getLoweredValue(value);
2516 if (hw::type_isa<seq::ClockType>(result.getType()))
2517 return getNonClockValue(result);
2525Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2528 bool allowTruncate) {
2529 SmallVector<Value> resultBuffer;
2534 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2535 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2536 auto resultType = builder.getIntegerType(destWidth);
2538 if (srcWidth == destWidth)
2541 if (srcWidth > destWidth) {
2545 builder.emitError(
"operand should not be a truncation");
2549 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2550 return comb::createOrFoldSExt(builder, value, resultType);
2551 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2559 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2560 .Case<FVectorType>([&](
auto srcVectorType) {
2561 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2562 unsigned size = resultBuffer.size();
2563 unsigned indexWidth =
2565 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2566 destVectorType.getNumElements());
2568 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2570 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2571 destVectorType.getElementType())))
2574 SmallVector<Value> temp(resultBuffer.begin() + size,
2575 resultBuffer.end());
2577 resultBuffer.resize(size);
2578 resultBuffer.push_back(array);
2581 .Case<BundleType>([&](BundleType srcStructType) {
2582 auto destStructType = firrtl::type_cast<BundleType>(destType);
2583 unsigned size = resultBuffer.size();
2586 if (destStructType.getNumElements() != srcStructType.getNumElements())
2589 for (
auto elem :
llvm::enumerate(destStructType)) {
2590 auto structExtract =
2592 if (failed(recurse(structExtract,
2593 srcStructType.getElementType(elem.index()),
2594 destStructType.getElementType(elem.index()))))
2597 SmallVector<Value> temp(resultBuffer.begin() + size,
2598 resultBuffer.end());
2601 resultBuffer.resize(size);
2602 resultBuffer.push_back(newStruct);
2605 .Case<IntType>([&](
auto) {
2606 if (
auto result = cast(src, srcType, destType)) {
2607 resultBuffer.push_back(result);
2612 .Default([&](
auto) {
return failure(); });
2615 if (failed(recurse(array, sourceType, destType)))
2618 assert(resultBuffer.size() == 1 &&
2619 "resultBuffer must only contain a result array if `success` is true");
2620 return resultBuffer[0];
2628Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2629 auto srcType = cast<FIRRTLBaseType>(src.getType());
2630 auto dstType = cast<FIRRTLBaseType>(target);
2631 auto loweredSrc = getLoweredValue(src);
2634 auto dstWidth = dstType.getBitWidthOrSentinel();
2650 return getOrCreateIntConstant(dstWidth, 0);
2653 auto loweredSrcType = loweredSrc.getType();
2654 auto loweredDstType =
lowerType(dstType);
2657 if (loweredSrcType == loweredDstType)
2661 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2663 if (loweredSrcType != loweredDstType &&
2664 (isa<hw::TypeAliasType>(loweredSrcType) ||
2665 isa<hw::TypeAliasType>(loweredDstType))) {
2666 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2671 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2672 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2675 if (isa<seq::ClockType>(loweredSrcType)) {
2676 builder.emitError(
"cannot use clock type as an integer");
2680 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2681 if (!intSourceType) {
2682 builder.emitError(
"operand of type ")
2683 << loweredSrcType <<
" cannot be used as an integer";
2687 auto loweredSrcWidth = intSourceType.getWidth();
2688 if (loweredSrcWidth ==
unsigned(dstWidth))
2691 if (loweredSrcWidth >
unsigned(dstWidth)) {
2692 builder.emitError(
"operand should not be a truncation");
2697 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2698 if (type_cast<IntType>(valueFIRType).isSigned())
2699 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2701 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2710Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2711 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2712 type_isa<FIRRTLBaseType>(destType) &&
2713 "input/output value should be FIRRTL");
2716 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2717 if (destWidth == -1)
2720 auto result = getLoweredValue(value);
2732 return getOrCreateIntConstant(destWidth, 0);
2736 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2738 if (destType == value.getType())
2741 return getExtOrTruncAggregateValue(
2742 result, type_cast<FIRRTLBaseType>(value.getType()),
2743 type_cast<FIRRTLBaseType>(destType),
2747 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2748 if (srcWidth ==
unsigned(destWidth))
2754 if (srcWidth >
unsigned(destWidth)) {
2755 auto resultType = builder.getIntegerType(destWidth);
2759 auto resultType = builder.getIntegerType(destWidth);
2763 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2764 if (type_cast<IntType>(valueFIRType).isSigned())
2765 return comb::createOrFoldSExt(builder, result, resultType);
2767 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2781std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2783 if (type_isa<FStringType>(operand.getType())) {
2784 if (isa<TimeOp>(operand.getDefiningOp()))
2785 return sv::TimeOp::create(builder);
2786 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2790 auto loweredValue = getLoweredValue(operand);
2791 if (!loweredValue) {
2795 loweredValue = getOrCreateIntConstant(1, 0);
2800 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2801 if (intTy.isSigned())
2802 loweredValue = sv::SystemFunctionOp::create(
2803 builder, loweredValue.getType(),
"signed", loweredValue);
2805 return loweredValue;
2809FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2810 SmallVectorImpl<Value> &loweredOperands) {
2811 for (
auto operand : operands) {
2812 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2817 loweredOperands.push_back(*loweredValue);
2823FIRRTLLowering::lowerSimFormatString(StringRef originalFormatString,
2824 ValueRange operands) {
2825 SmallVector<Value> fragments;
2827 auto emitLiteral = [&](StringRef text) {
2829 fragments.push_back(sim::FormatLiteralOp::create(builder, text));
2832 auto emitIntFormat = [&](Value operand,
char specifier,
2833 IntegerAttr widthAttr) -> FailureOr<Value> {
2835 if (type_isa<ClockType>(operand.getType()))
2836 loweredValue = getLoweredNonClockValue(operand);
2838 loweredValue = getLoweredValue(operand);
2839 if (!loweredValue) {
2842 loweredValue = getOrCreateIntConstant(1, 0);
2845 if (!mlir::isa<IntegerType>(loweredValue.getType())) {
2846 emitError(builder.getLoc(),
"lower-to-core requires integer printf "
2848 << specifier <<
"'";
2852 switch (specifier) {
2854 return sim::FormatBinOp::create(builder, loweredValue,
2855 builder.getBoolAttr(
false),
2856 builder.getI8IntegerAttr(
'0'), widthAttr)
2859 UnitAttr signedAttr;
2860 if (
auto intTy = dyn_cast<IntType>(operand.getType());
2861 intTy && intTy.isSigned())
2862 signedAttr = builder.getUnitAttr();
2863 return sim::FormatDecOp::create(
2864 builder, loweredValue, builder.getBoolAttr(
false),
2865 builder.getI8IntegerAttr(
' '), widthAttr, signedAttr)
2869 return sim::FormatHexOp::create(builder, loweredValue,
2870 builder.getBoolAttr(
false),
2871 builder.getBoolAttr(
false),
2872 builder.getI8IntegerAttr(
'0'), widthAttr)
2875 return sim::FormatCharOp::create(builder, loweredValue).getResult();
2877 llvm_unreachable(
"unsupported FIRRTL format specifier");
2881 SmallString<32> literal;
2882 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
2883 char c = originalFormatString[i];
2886 emitLiteral(literal);
2889 SmallString<6> width;
2890 c = originalFormatString[++i];
2893 c = originalFormatString[++i];
2896 IntegerAttr widthAttr;
2897 if (!width.empty()) {
2898 unsigned widthValue;
2899 if (StringRef(width).getAsInteger(10, widthValue)) {
2900 emitError(builder.getLoc(),
"invalid FIRRTL printf width");
2903 widthAttr = builder.getI32IntegerAttr(widthValue);
2907 if (!width.empty()) {
2908 emitError(builder.getLoc(),
2909 "literal percents ('%%') may not specify a width");
2912 literal.push_back(
'%');
2916 if (operands.size() <= subIdx) {
2917 emitError(builder.getLoc(),
"not enough operands for printf format");
2921 if (c ==
'c' && widthAttr) {
2922 emitError(builder.getLoc(),
"ASCII character format specifiers ('%c') "
2923 "may not specify a width");
2932 auto fragment = emitIntFormat(operands[subIdx++], c, widthAttr);
2933 if (failed(fragment))
2935 fragments.push_back(*fragment);
2939 emitError(builder.getLoc(),
"unknown printf substitution '%")
2940 << width << c <<
"'";
2946 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
2947 literal.push_back(c);
2951 emitLiteral(literal);
2954 if (operands.size() <= subIdx) {
2955 emitError(builder.getLoc(),
"not enough operands for printf format");
2959 auto substitution = operands[subIdx++];
2960 if (!type_isa<FStringType>(substitution.getType())) {
2961 emitError(builder.getLoc(),
"expected fstring operand for '{{}}' "
2967 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
2968 .template Case<HierarchicalModuleNameOp>([&](
auto) {
2969 fragments.push_back(sim::FormatHierPathOp::create(
2973 .
template Case<TimeOp>([&](
auto) {
2974 fragments.push_back(sim::FormatCurrentTimeOp::create(builder));
2977 .Default([&](
auto) {
2978 emitError(builder.getLoc(),
"has a substitution with "
2981 .attachNote(substitution.getLoc())
2982 <<
"op with an unimplemented lowering is here";
2991 literal.push_back(c);
2996 emitLiteral(literal);
2997 if (fragments.empty())
2998 return sim::FormatLiteralOp::create(builder,
"").getResult();
2999 if (fragments.size() == 1)
3000 return fragments.front();
3001 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
3004LogicalResult FIRRTLLowering::lowerStatementWithFd(
3005 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
3006 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
3008 bool failed =
false;
3009 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
3010 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
3011 addToAlwaysBlock(clock, [&]() {
3014 circuitState.usedPrintf =
true;
3016 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
3019 Value ifCond = cond;
3020 if (usePrintfCond) {
3022 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
3023 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
3026 addIfProceduralBlock(ifCond, [&]() {
3030 if (fileDescriptor.isDefaultFd()) {
3032 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
3035 auto fdOrError = callFileDescriptorLib(fileDescriptor);
3036 if (llvm::failed(fdOrError)) {
3042 failed = llvm::failed(fn(fd));
3046 return failure(failed);
3050FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3051 circuitState.usedFileDescriptorLib =
true;
3052 circuitState.addFragment(
3053 theModule, sv::getFileDescriptorFragmentRef(builder.getContext()));
3056 if (
info.isSubstitutionRequired()) {
3057 SmallVector<Value> fileNameOperands;
3058 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3061 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3066 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3070 return sv::createProceduralFileDescriptorGetterCall(builder, builder.getLoc(),
3080LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3081 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3082 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3083 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3087 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3090 if (srcWidth != -1) {
3092 assert((srcWidth != 0) &&
3093 "Lowering produced value for zero width source");
3095 assert((srcWidth == 0) &&
3096 "Lowering produced null value but source wasn't zero width");
3100 assert(result &&
"Lowering of foreign type produced null value");
3103 auto &slot = valueMapping[orig];
3104 assert(!slot &&
"value lowered multiple times");
3111LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3115 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3116 auto &entry = hwConstantMap[cst.getValueAttr()];
3127 cst->moveBefore(&theModule.getBodyBlock()->front());
3131 return setLowering(orig, result);
3136template <
typename ResultOpType,
typename... CtorArgTypes>
3137LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3138 CtorArgTypes... args) {
3139 auto result = builder.createOrFold<ResultOpType>(args...);
3140 if (
auto *op = result.getDefiningOp())
3142 return setPossiblyFoldedLowering(orig->getResult(0), result);
3149template <
typename ResultOpType,
typename... CtorArgTypes>
3150LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3151 CtorArgTypes... args) {
3152 auto result = builder.createOrFold<ResultOpType>(args...);
3153 if (
auto *op = result.getDefiningOp())
3154 ltlOpFixupWorklist.insert(op);
3155 return setPossiblyFoldedLowering(orig->getResult(0), result);
3164Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3165 auto backedge = backedgeBuilder.
get(type, loc);
3166 backedges.insert({backedge, backedge});
3174Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3175 auto backedge = createBackedge(orig.getLoc(), type);
3176 (void)setLowering(orig, backedge);
3182bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3183 auto backedgeIt = backedges.find(dest);
3184 if (backedgeIt == backedges.end())
3186 backedgeIt->second = src;
3194void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3195 const std::function<
void(
void)> &fn, Region ®ion) {
3199 auto oldIP = builder.saveInsertionPoint();
3201 builder.setInsertionPointToEnd(®ion.front());
3203 builder.restoreInsertionPoint(oldIP);
3207Value FIRRTLLowering::getReadValue(Value v) {
3208 Value result = readInOutCreated.lookup(v);
3214 auto oldIP = builder.saveInsertionPoint();
3215 if (
auto *vOp = v.getDefiningOp()) {
3216 builder.setInsertionPointAfter(vOp);
3220 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3225 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3226 result = getReadValue(arrayIndexInout.getInput());
3228 arrayIndexInout.getIndex());
3233 builder.restoreInsertionPoint(oldIP);
3234 readInOutCreated.insert({v, result});
3238Value FIRRTLLowering::getNonClockValue(Value v) {
3239 auto it = fromClockMapping.try_emplace(v, Value{});
3241 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3242 builder.setInsertionPointAfterValue(v);
3243 it.first->second = seq::FromClockOp::create(builder, v);
3245 return it.first->second;
3248void FIRRTLLowering::addToAlwaysBlock(
3249 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3250 sv::EventControl resetEdge, Value reset,
3251 const std::function<
void(
void)> &body,
3252 const std::function<
void(
void)> &resetBody) {
3253 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3254 resetStyle, resetEdge, reset};
3255 sv::AlwaysOp alwaysOp;
3256 sv::IfOp insideIfOp;
3257 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3261 assert(resetStyle != sv::ResetType::NoReset);
3274 auto createIfOp = [&]() {
3277 insideIfOp = sv::IfOp::create(
3278 builder, reset, [] {}, [] {});
3280 if (resetStyle == sv::ResetType::AsyncReset) {
3281 sv::EventControl events[] = {clockEdge, resetEdge};
3282 Value clocks[] = {clock, reset};
3284 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3285 if (resetEdge == sv::EventControl::AtNegEdge)
3286 llvm_unreachable(
"negative edge for reset is not expected");
3290 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3294 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3295 insideIfOp =
nullptr;
3297 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3301 assert(insideIfOp &&
"reset body must be initialized before");
3302 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3303 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3305 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3311 alwaysOp->moveBefore(builder.getInsertionBlock(),
3312 builder.getInsertionPoint());
3315LogicalResult FIRRTLLowering::emitGuards(Location loc,
3316 ArrayRef<Attribute> guards,
3317 std::function<
void(
void)>
emit) {
3318 if (guards.empty()) {
3322 auto guard = dyn_cast<StringAttr>(guards[0]);
3324 return mlir::emitError(loc,
3325 "elements in `guards` array must be `StringAttr`");
3328 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3329 LogicalResult result = LogicalResult::failure();
3330 addToIfDefBlock(guard.getValue(), [&]() {
3331 result = emitGuards(loc, guards.drop_front(), emit);
3336void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3337 std::function<
void(
void)> thenCtor,
3338 std::function<
void(
void)> elseCtor) {
3339 auto condAttr = builder.getStringAttr(cond);
3340 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3342 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3343 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3348 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3350 ifdefBlocks[{builder.getBlock(), condAttr}] =
3351 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3355void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3356 auto op = initialBlocks.lookup(builder.getBlock());
3358 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3363 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3365 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3369void FIRRTLLowering::addIfProceduralBlock(Value cond,
3370 std::function<
void(
void)> thenCtor,
3371 std::function<
void(
void)> elseCtor) {
3374 auto insertIt = builder.getInsertionPoint();
3375 if (insertIt != builder.getBlock()->begin())
3376 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3377 if (ifOp.getCond() == cond) {
3378 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3379 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3384 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3396FIRRTLLowering::UnloweredOpResult
3397FIRRTLLowering::handleUnloweredOp(Operation *op) {
3399 if (!op->getRegions().empty() &&
3400 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3401 op->emitOpError(
"must explicitly handle its regions");
3402 return LoweringFailure;
3409 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3411 for (
auto ®ion : op->getRegions())
3412 addToWorklist(region);
3413 for (
auto &operand : op->getOpOperands())
3414 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3415 operand.set(lowered);
3416 for (
auto result : op->getResults())
3417 (void)setLowering(result, result);
3418 return AlreadyLowered;
3430 if (op->getNumResults() == 1) {
3431 auto resultType = op->getResult(0).getType();
3432 if (type_isa<FIRRTLBaseType>(resultType) &&
3434 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3436 (void)setLowering(op->getResult(0), Value());
3440 op->emitOpError(
"LowerToHW couldn't handle this operation");
3441 return LoweringFailure;
3444LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3447 return setLowering(op, Value());
3449 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3452LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3454 if (isa<ClockType>(op.getType())) {
3455 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3456 :
seq::ClockConst::Low);
3458 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3460 return setLowering(op, cst);
3463FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3464 auto iIdx = getOrCreateIntConstant(
3466 firrtl::type_cast<FVectorType>(op.getInput().getType())
3473 if (isa<sv::InOutType>(input.getType()))
3474 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3477 if (
auto *definingOp = result.getDefiningOp())
3482FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3483 Value valueIdx = getLoweredAndExtOrTruncValue(
3485 UIntType::get(op->getContext(),
3487 firrtl::type_cast<FVectorType>(op.getInput().getType())
3488 .getNumElements())));
3490 op->emitError() <<
"input lowering failed";
3497 if (isa<sv::InOutType>(input.getType()))
3498 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3500 result = createArrayIndexing(input, valueIdx);
3501 if (
auto *definingOp = result.getDefiningOp())
3506FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3507 auto resultType =
lowerType(op->getResult(0).getType());
3508 if (!resultType || !input) {
3509 op->emitError() <<
"subfield type lowering failed";
3515 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3516 .getElementName(op.getFieldIndex());
3518 if (isa<sv::InOutType>(input.getType()))
3519 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3522 if (
auto *definingOp = result.getDefiningOp())
3527LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3529 return setLowering(op, Value());
3531 auto input = getPossiblyInoutLoweredValue(op.getInput());
3533 return op.emitError() <<
"input lowering failed";
3535 auto result = lowerSubindex(op, input);
3538 return setLowering(op, *result);
3541LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3543 return setLowering(op, Value());
3545 auto input = getPossiblyInoutLoweredValue(op.getInput());
3547 return op.emitError() <<
"input lowering failed";
3549 auto result = lowerSubaccess(op, input);
3552 return setLowering(op, *result);
3555LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3558 if (getLoweredValue(op) || !op.getInput())
3562 return setLowering(op, Value());
3564 auto input = getPossiblyInoutLoweredValue(op.getInput());
3566 return op.emitError() <<
"input lowering failed";
3568 auto result = lowerSubfield(op, input);
3571 return setLowering(op, *result);
3574LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3575 auto resultType =
lowerType(op.getResult().getType());
3576 auto arrayType = cast<hw::ArrayType>(resultType);
3577 SmallVector<Value> operands;
3579 for (
auto oper :
llvm::reverse(op.getOperands())) {
3580 auto val = getLoweredValue(oper);
3585 val = getZeroValueForType(arrayType.getElementType());
3587 operands.push_back(val);
3589 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3592LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3593 auto resultType =
lowerType(op.getResult().getType());
3594 auto structType = cast<hw::StructType>(resultType);
3595 SmallVector<Value> operands;
3596 for (
auto [oper, field] :
3597 llvm::zip_equal(op.getOperands(), structType.getElements())) {
3598 auto val = getLoweredValue(oper);
3603 val = getZeroValueForType(field.type);
3605 operands.push_back(val);
3607 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3610LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3613 return setLowering(op, Value());
3615 auto input = getLoweredValue(op.getInput());
3616 auto tagName = op.getFieldNameAttr();
3617 auto oldType = op.getType().base();
3619 auto element = *oldType.getElement(op.getFieldNameAttr());
3621 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3627 input = getOrCreateIntConstant(0, 0);
3629 auto tagType = structType.getFieldType(
"tag");
3630 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3631 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3633 auto bodyType = structType.getFieldType(
"body");
3634 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3635 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3636 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3638 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3639 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3642LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3643 auto resultType =
lowerType(op.getResult().getType());
3645 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3647 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3648 cast<ArrayAttr>(attr));
3651LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3655 return setLowering(op, getOrCreateIntConstant(1, 1));
3657 auto tagName = op.getFieldNameAttr();
3658 auto lhs = getLoweredValue(op.getInput());
3659 if (isa<hw::StructType>(lhs.getType()))
3662 auto index = op.getFieldIndex();
3663 auto enumType = op.getInput().getType().base();
3664 auto tagValue = enumType.getElementValueAttr(index);
3665 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3666 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3667 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3668 loweredTagValue, tagName);
3670 Type resultType = builder.getIntegerType(1);
3671 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3675LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3678 return setLowering(op, Value());
3680 auto tagName = op.getFieldNameAttr();
3681 auto input = getLoweredValue(op.getInput());
3683 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3686LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3689 return setLowering(op, Value());
3691 auto input = getLoweredValue(op.getInput());
3697 if (isa<hw::StructType>(input.getType())) {
3698 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3703 return setLowering(op, input);
3710LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3711 auto origResultType = op.getResult().getType();
3715 if (!type_isa<FIRRTLType>(origResultType)) {
3716 createBackedge(op.getResult(), origResultType);
3720 auto resultType =
lowerType(origResultType);
3724 if (resultType.isInteger(0)) {
3725 if (op.getInnerSym())
3726 return op.emitError(
"zero width wire is referenced by name [")
3727 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3728 return setLowering(op.getResult(), Value());
3732 auto innerSym = lowerInnerSymbol(op);
3733 auto name = op.getNameAttr();
3736 auto wire = hw::WireOp::create(
3737 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3739 if (
auto svAttrs = sv::getSVAttributes(op))
3740 sv::setSVAttributes(wire, svAttrs);
3742 return setLowering(op.getResult(), wire);
3745LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3746 auto resultTy =
lowerType(op.getType());
3749 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3751 SmallVector<Value, 4> operands;
3752 operands.reserve(op.getSubstitutions().size());
3753 for (
auto operand : op.getSubstitutions()) {
3754 auto lowered = getLoweredValue(operand);
3757 operands.push_back(lowered);
3760 ArrayAttr symbols = op.getSymbolsAttr();
3762 symbols = ArrayAttr::get(op.getContext(), {});
3764 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3768LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3769 auto operand = getLoweredValue(op.getInput());
3771 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3772 if (op.getInnerSym())
3773 return op.emitError(
"zero width node is referenced by name [")
3774 << *op.getInnerSym()
3775 <<
"] (e.g. in an XMR) but must be "
3777 return setLowering(op.getResult(), Value());
3783 auto name = op.getNameAttr();
3784 auto innerSym = lowerInnerSymbol(op);
3787 operand = hw::WireOp::create(builder, operand, name, innerSym);
3790 if (
auto svAttrs = sv::getSVAttributes(op)) {
3792 operand = hw::WireOp::create(builder, operand, name);
3793 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3796 return setLowering(op.getResult(), operand);
3799LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3800 auto resultType =
lowerType(op.getResult().getType());
3803 if (resultType.isInteger(0))
3804 return setLowering(op.getResult(), Value());
3806 Value clockVal = getLoweredValue(op.getClockVal());
3811 auto innerSym = lowerInnerSymbol(op);
3812 Backedge inputEdge = backedgeBuilder.
get(resultType);
3813 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3814 op.getNameAttr(), innerSym);
3817 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3818 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3819 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3820 reg->setAttr(
"firrtl.random_init_start", randomStart);
3821 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3822 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3825 if (
auto svAttrs = sv::getSVAttributes(op))
3826 sv::setSVAttributes(reg, svAttrs);
3829 (void)setLowering(op.getResult(),
reg);
3833LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3834 auto resultType =
lowerType(op.getResult().getType());
3837 if (resultType.isInteger(0))
3838 return setLowering(op.getResult(), Value());
3840 Value clockVal = getLoweredValue(op.getClockVal());
3841 Value resetSignal = getLoweredValue(op.getResetSignal());
3843 Value resetValue = getLoweredAndExtOrTruncValue(
3844 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3846 if (!clockVal || !resetSignal || !resetValue)
3850 auto innerSym = lowerInnerSymbol(op);
3851 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3852 Backedge inputEdge = backedgeBuilder.
get(resultType);
3854 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3855 resetSignal, resetValue, innerSym, isAsync);
3858 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3859 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3860 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3861 reg->setAttr(
"firrtl.random_init_start", randomStart);
3862 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3863 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3866 if (
auto svAttrs = sv::getSVAttributes(op))
3867 sv::setSVAttributes(reg, svAttrs);
3870 (void)setLowering(op.getResult(),
reg);
3875LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3878 if (type_isa<BundleType>(op.getDataType()))
3879 return op.emitOpError(
3880 "should have already been lowered from a ground type to an aggregate "
3881 "type using the LowerTypes pass. Use "
3882 "'firtool --lower-types' or 'circt-opt "
3883 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3889 auto memType = seq::FirMemType::get(
3892 : std::optional<uint32_t>());
3894 seq::FirMemInitAttr memInit;
3895 if (
auto init = op.getInitAttr())
3896 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3897 init.getIsBinary(), init.getIsInline());
3899 auto memDecl = seq::FirMemOp::create(
3902 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3905 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3907 if (!file.isDirectory())
3908 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3909 file.getDirectory());
3910 memDecl.setOutputFileAttr(dir);
3916 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3918 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3921 (void)setLowering(a, value);
3927 auto addInput = [&](StringRef field, Value backedge) {
3929 if (cast<FIRRTLBaseType>(a.getType())
3931 .getBitWidthOrSentinel() > 0)
3932 (void)setLowering(a, backedge);
3938 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3942 Value backedge, portValue;
3944 portValue = getOrCreateXConstant(1);
3946 auto portType = IntegerType::get(op.getContext(), width);
3947 backedge = portValue = createBackedge(builder.getLoc(), portType);
3949 addInput(field, backedge);
3953 auto addClock = [&](StringRef field) -> Value {
3954 Type clockTy = seq::ClockType::get(op.getContext());
3955 Value portValue = createBackedge(builder.getLoc(), clockTy);
3956 addInput(field, portValue);
3960 auto memportKind = op.getPortKind(i);
3961 if (memportKind == MemOp::PortKind::Read) {
3962 auto addr = addInputPort(
"addr", op.getAddrBits());
3963 auto en = addInputPort(
"en", 1);
3964 auto clk = addClock(
"clk");
3965 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3967 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3968 auto addr = addInputPort(
"addr", op.getAddrBits());
3969 auto en = addInputPort(
"en", 1);
3970 auto clk = addClock(
"clk");
3973 auto mode = addInputPort(
"wmode", 1);
3975 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3982 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3986 auto addr = addInputPort(
"addr", op.getAddrBits());
3989 auto en = addInputPort(
"en", 1);
3993 auto clk = addClock(
"clk");
4007FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
4008 Operation *instanceOp,
4009 SmallVectorImpl<Value> &inputOperands) {
4011 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4012 auto &port = portInfo[portIndex];
4015 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
4020 if (portType.isInteger(0))
4024 if (port.isOutput())
4027 auto portResult = instanceOp->getResult(portIndex);
4028 assert(portResult &&
"invalid IR, couldn't find port");
4032 if (port.isInput()) {
4033 inputOperands.push_back(createBackedge(portResult, portType));
4039 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
4040 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
4042 auto loweredResult = getPossiblyInoutLoweredValue(source);
4043 inputOperands.push_back(loweredResult);
4044 (void)setLowering(portResult, loweredResult);
4053 "." + port.getName().str() +
".wire");
4057 (void)setLowering(portResult, wire);
4058 inputOperands.push_back(wire);
4064LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4065 Operation *oldModule =
4066 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4068 auto *newModule = circuitState.getNewModule(oldModule);
4070 oldInstance->emitOpError(
"could not find module [")
4071 << oldInstance.getModuleName() <<
"] referenced by instance";
4077 ArrayAttr parameters;
4078 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4083 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4087 SmallVector<Value, 8> operands;
4088 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4095 auto innerSym = oldInstance.getInnerSymAttr();
4096 if (oldInstance.getLowerToBind()) {
4099 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4102 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4103 innerSym.getSymName());
4106 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4107 bindOp->setAttr(
"output_file", outputFile);
4110 circuitState.addBind(bindOp);
4115 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4116 operands, parameters, innerSym);
4118 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4119 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4121 if (newInstance.getInnerSymAttr())
4122 if (
auto forceName = circuitState.instanceForceNames.lookup(
4123 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4124 newInstance.getInnerNameAttr()}))
4125 newInstance->setAttr(
"hw.verilogName", forceName);
4129 unsigned resultNo = 0;
4130 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4131 auto &port = portInfo[portIndex];
4135 Value resultVal = newInstance.getResult(resultNo);
4137 auto oldPortResult = oldInstance.getResult(portIndex);
4138 (void)setLowering(oldPortResult, resultVal);
4144LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4145 if (oldInstanceChoice.getInnerSymAttr()) {
4146 oldInstanceChoice->emitOpError(
4147 "instance choice with inner sym cannot be lowered");
4152 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4154 return oldInstanceChoice->emitOpError(
4155 "must have instance_macro attribute set before "
4159 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4160 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4163 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4164 auto *defaultModuleNode =
4165 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4167 Operation *defaultModule = defaultModuleNode->getModule();
4171 SmallVector<PortInfo, 8> portInfo =
4172 cast<FModuleLike>(defaultModule).getPorts();
4175 SmallVector<Value, 8> inputOperands;
4177 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4181 SmallVector<sv::WireOp, 8> outputWires;
4182 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4183 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4184 auto &port = portInfo[portIndex];
4188 if (!portType || portType.isInteger(0))
4191 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4192 outputWires.push_back(wire);
4193 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4197 auto optionName = oldInstanceChoice.getOptionNameAttr();
4200 auto createInstanceAndAssign = [&](Operation *oldMod,
4201 StringRef suffix) -> hw::InstanceOp {
4202 auto *newMod = circuitState.getNewModule(oldMod);
4204 ArrayAttr parameters;
4205 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4209 SmallString<64> instName;
4210 instName = oldInstanceChoice.getInstanceName();
4211 if (!suffix.empty()) {
4217 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4218 inputOperands, parameters,
nullptr);
4224 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4231 SmallVector<StringAttr> macroNames;
4232 SmallVector<Operation *> altModules;
4233 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4234 altModules.push_back(
4235 circuitState.getInstanceGraph()
4236 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4240 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4241 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4242 if (!optionCaseMacroRef)
4243 return oldInstanceChoice->emitOpError(
4244 "failed to get macro for option case");
4245 macroNames.push_back(optionCaseMacroRef.getAttr());
4249 sv::createNestedIfDefs(
4252 [&](StringRef macro, std::function<
void()> thenCtor,
4253 std::function<
void()> elseCtor) {
4254 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4258 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4259 sv::IfDefOp::create(
4260 builder, oldInstanceChoice.getLoc(), macroNames[i],
4262 SmallString<256> errorMessage;
4263 llvm::raw_svector_ostream os(errorMessage);
4264 os <<
"Multiple instance choice options defined for option '"
4265 << optionName.getValue() <<
"': '"
4266 << macroNames[index].getValue() <<
"' and '"
4267 << macroNames[i].getValue() <<
"'";
4268 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4269 builder.getStringAttr(errorMessage));
4275 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4277 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4279 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4280 builder.getStringAttr(
"{{0}}"),
4281 builder.getArrayAttr({hw::InnerRefAttr::get(
4282 theModule.getNameAttr(),
4283 inst.getInnerSymAttr().getSymName())}));
4287 SmallString<256> errorMessage;
4288 llvm::raw_svector_ostream os(errorMessage);
4289 os <<
"Required instance choice option '" << optionName.getValue()
4290 <<
"' not selected, must define one of: ";
4291 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4292 os <<
"'" << macro.getValue() <<
"'";
4294 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4295 builder.getStringAttr(errorMessage));
4301LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4302 SmallVector<Value> inputs;
4303 SmallVector<Type> types;
4304 for (
auto input : oldOp.getInputs()) {
4305 auto lowered = getLoweredValue(input);
4308 inputs.push_back(lowered);
4309 types.push_back(lowered.getType());
4312 auto newOp = verif::ContractOp::create(builder, types, inputs);
4313 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4314 auto &body = newOp.getBody().emplaceBlock();
4316 for (
auto [newResult, oldResult, oldArg] :
4317 llvm::zip(newOp.getResults(), oldOp.getResults(),
4318 oldOp.getBody().getArguments())) {
4319 if (failed(setLowering(oldResult, newResult)))
4321 if (failed(setLowering(oldArg, newResult)))
4325 body.getOperations().splice(body.end(),
4326 oldOp.getBody().front().getOperations());
4327 addToWorklist(body);
4337LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4338 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4343 return setLowering(op->getResult(0), operand);
4346LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4347 if (isa<ClockType>(op.getInput().getType()))
4348 return setLowering(op->getResult(0),
4349 getLoweredNonClockValue(op.getInput()));
4350 return lowerNoopCast(op);
4353LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4354 if (isa<ClockType>(op.getInput().getType()))
4355 return setLowering(op->getResult(0),
4356 getLoweredNonClockValue(op.getInput()));
4357 return lowerNoopCast(op);
4360LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4361 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4364LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4365 mlir::UnrealizedConversionCastOp op) {
4367 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4370 auto operand = op.getOperand(0);
4371 auto result = op.getResult(0);
4374 if (type_isa<FIRRTLType>(operand.getType()) &&
4375 type_isa<FIRRTLType>(result.getType()))
4376 return lowerNoopCast(op);
4380 if (!type_isa<FIRRTLType>(operand.getType())) {
4381 if (type_isa<FIRRTLType>(result.getType()))
4382 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4388 auto loweredResult = getLoweredValue(operand);
4389 if (!loweredResult) {
4392 if (operand.getType().isSignlessInteger(0)) {
4393 return setLowering(result, Value());
4400 result.replaceAllUsesWith(loweredResult);
4404LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4407 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4408 return setLowering(op, op.getOperand());
4412 auto result = getLoweredValue(op.getOperand());
4418 op.replaceAllUsesWith(result);
4422LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4423 auto operand = getLoweredValue(op.getOperand());
4426 auto resultType =
lowerType(op.getType());
4430 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4433LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4434 auto operand = getLoweredValue(op.getOperand());
4438 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4439 return setLowering(op, getOrCreateIntConstant(1, 0));
4441 return setLowering(op, Value());
4446 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4447 return setLowering(op, operand);
4450 auto zero = getOrCreateIntConstant(1, 0);
4451 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4454LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4455 auto operand = getLoweredValue(op.getInput());
4459 auto allOnes = getOrCreateIntConstant(
4460 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4461 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4464LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4467 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4471 auto resultType =
lowerType(op.getType());
4473 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4474 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4478LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4479 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4482 return setLowering(op, operand);
4485LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4486 auto operand = getLoweredValue(op.getInput());
4489 return setLowering(op, getOrCreateIntConstant(1, 0));
4494 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4498LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4499 auto operand = getLoweredValue(op.getInput());
4502 return setLowering(op, getOrCreateIntConstant(1, 1));
4507 return setLoweringTo<comb::ICmpOp>(
4508 op, ICmpPredicate::eq, operand,
4509 getOrCreateIntConstant(
4510 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4514LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4515 auto operand = getLoweredValue(op.getInput());
4518 return setLowering(op, getOrCreateIntConstant(1, 0));
4524 return setLoweringTo<comb::ICmpOp>(
4525 op, ICmpPredicate::ne, operand,
4526 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4534template <
typename ResultOpType>
4535LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4536 auto resultType = op->getResult(0).getType();
4537 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4538 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4542 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4548template <
typename ResultOpType>
4549LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4550 auto resultType = op->getResult(0).getType();
4551 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4552 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4563 auto intType = builder.getIntegerType(*bitwidth);
4564 auto retType = lhs.getType();
4567 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4568 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4573template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4574LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4576 auto resultType = op->getResult(0).getType();
4577 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4578 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4583 if (type_cast<IntType>(resultType).isSigned())
4584 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4585 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4590LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4591 ICmpPredicate unsignedOp) {
4593 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4594 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4595 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4599 if (cmpType.getWidth() == 0)
4600 cmpType = UIntType::get(builder.getContext(), 1);
4601 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4602 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4607 Type resultType = builder.getIntegerType(1);
4608 return setLoweringTo<comb::ICmpOp>(
4609 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4615template <
typename SignedOp,
typename Un
signedOp>
4616LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4620 auto opType = type_cast<IntType>(op->getResult(0).getType());
4621 if (opType.getWidth() == 0)
4622 return setLowering(op->getResult(0), Value());
4626 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4627 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4632 if (opType.isSigned())
4633 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4635 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4637 if (
auto *definingOp = result.getDefiningOp())
4640 if (resultType == opType)
4641 return setLowering(op->getResult(0), result);
4642 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4645LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4647 if (op.getInputs().empty())
4648 return setLowering(op, Value());
4650 SmallVector<Value> loweredOperands;
4653 for (
auto operand : op.getInputs()) {
4654 auto loweredOperand = getLoweredValue(operand);
4655 if (loweredOperand) {
4656 loweredOperands.push_back(loweredOperand);
4659 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4667 if (loweredOperands.empty())
4668 return setLowering(op, Value());
4671 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4678LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4679 auto input = getLoweredNonClockValue(op.getArg());
4683 if (!isa<IntType>(input.getType())) {
4684 auto srcType = op.getArg().getType();
4686 assert(bitwidth &&
"Unknown width");
4687 auto intType = builder.getIntegerType(*bitwidth);
4688 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4691 return setLoweringTo<comb::ICmpOp>(
4692 op, ICmpPredicate::ceq, input,
4693 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4696LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4697 auto operand = getLoweredValue(op.getInput());
4698 hw::WireOp::create(builder, operand);
4702LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4703 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4704 op.getFormatStringAttr());
4707LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4708 auto type =
lowerType(op.getResult().getType());
4712 auto valueOp = sim::PlusArgsValueOp::create(
4713 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4714 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4716 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4721LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4722 op.emitError(
"SizeOf should have been resolved.");
4726LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4728 if (op.getTestEnable())
4729 testEnable = getLoweredValue(op.getTestEnable());
4730 return setLoweringTo<seq::ClockGateOp>(
4731 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4732 testEnable, hw::InnerSymAttr{});
4735LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4736 auto operand = getLoweredValue(op.getInput());
4737 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4740LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4741 auto operand = getLoweredValue(op.getInput());
4742 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4745LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4746 return setLoweringToLTL<ltl::AndOp>(
4748 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4751LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4752 return setLoweringToLTL<ltl::OrOp>(
4754 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4757LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4758 return setLoweringToLTL<ltl::IntersectOp>(
4760 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4763LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4764 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4765 op.getDelayAttr(), op.getLengthAttr());
4768LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4769 return setLoweringToLTL<ltl::ConcatOp>(
4771 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4774LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4775 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4776 op.getBaseAttr(), op.getMoreAttr());
4779LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4780 return setLoweringToLTL<ltl::GoToRepeatOp>(
4781 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4784LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4785 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4786 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4789LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4790 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4793LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4794 return setLoweringToLTL<ltl::ImplicationOp>(
4796 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4799LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4800 return setLoweringToLTL<ltl::UntilOp>(
4802 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4805LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4806 return setLoweringToLTL<ltl::EventuallyOp>(op,
4807 getLoweredValue(op.getInput()));
4810LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4811 Value
clk = getLoweredNonClockValue(op.getClock());
4812 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4813 op.getDelayAttr(),
clk);
4817 switch (eventControl) {
4818 case EventControl::AtPosEdge:
4819 return ltl::ClockEdge::Pos;
4820 case EventControl::AtEdge:
4821 return ltl::ClockEdge::Both;
4822 case EventControl::AtNegEdge:
4823 return ltl::ClockEdge::Neg;
4825 llvm_unreachable(
"unknown event control");
4828LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4829 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4831 getLoweredNonClockValue(op.getClock()));
4834template <
typename TargetOp,
typename IntrinsicOp>
4835LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4836 auto property = getLoweredValue(op.getProperty());
4837 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4838 TargetOp::create(builder, property, enable, op.getLabelAttr());
4842LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4843 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4846LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4847 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4850LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4851 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4854LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4855 if (!isa<verif::ContractOp>(op->getParentOp()))
4856 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4857 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4860LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4861 if (!isa<verif::ContractOp>(op->getParentOp()))
4862 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4863 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4866LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4867 auto clock = getLoweredNonClockValue(op.getClock());
4868 auto reset = getLoweredValue(op.getReset());
4869 if (!clock || !reset)
4871 auto resetType = op.getReset().getType();
4872 auto uintResetType = dyn_cast<UIntType>(resetType);
4873 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4874 auto isAsync = isa<AsyncResetType>(resetType);
4875 if (!isAsync && !isSync) {
4876 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4877 "requires sync or async reset");
4878 d.attachNote() <<
"reset is of type " << resetType
4879 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4882 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4889LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4890 auto input = getLoweredValue(op.getInput());
4894 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4895 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4898LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4899 auto resultTy =
lowerType(op.getType());
4906 if (type_isa<AnalogType>(op.getType()))
4909 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4912 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4923 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4925 if (!type_isa<IntegerType>(resultTy))
4927 return setLowering(op, constant);
4931 op.emitOpError(
"unsupported type");
4935LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4936 auto input = getLoweredValue(op.getInput());
4939 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4940 if (op.getAmount() == 0)
4941 return setLowering(op, Value());
4942 Type resultType = builder.getIntegerType(op.getAmount());
4943 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4944 inWidth - op.getAmount());
4947LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4948 auto input = getLoweredValue(op.getInput());
4951 if (op.getAmount() == 0)
4953 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4958 if (op.getAmount() == 0)
4959 return setLowering(op, input);
4961 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4962 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4965LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4966 auto input = getLoweredValue(op.getInput());
4971 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4972 auto shiftAmount = op.getAmount();
4973 if (shiftAmount >= inWidth) {
4975 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4976 return setLowering(op, {});
4979 shiftAmount = inWidth - 1;
4982 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4983 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4986LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4987 auto input = getLoweredValue(op.getInput());
4991 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4992 if (inWidth == op.getAmount())
4993 return setLowering(op, Value());
4994 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4995 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4998LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4999 auto cond = getLoweredValue(op.getSel());
5000 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5001 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5002 if (!cond || !ifTrue || !ifFalse)
5005 if (isa<ClockType>(op.getType()))
5006 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
5007 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
5011LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
5012 auto cond = getLoweredValue(op.getSel());
5013 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5014 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5015 if (!cond || !ifTrue || !ifFalse)
5018 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
5020 return setLowering(op, createValueWithMuxAnnotation(val,
true));
5023LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
5024 auto sel = getLoweredValue(op.getSel());
5025 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
5026 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
5027 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
5028 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
5029 if (!sel || !v3 || !v2 || !v1 || !v0)
5031 Value array[] = {v3, v2, v1, v0};
5034 return setLowering(op, createValueWithMuxAnnotation(val,
false));
5053Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
5054 assert(op->getNumResults() == 1 &&
"only expect a single result");
5055 auto val = op->getResult(0);
5059 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5066 OpBuilder::InsertionGuard guard(builder);
5067 builder.setInsertionPoint(op);
5068 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5069 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
5071 op->getContext(),
nullptr, 0,
5074 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5075 op->setOperand(idx, wire);
5080 sv::setSVAttributes(assignOp,
5081 sv::SVAttributeAttr::get(builder.getContext(),
5082 "synopsys infer_mux_override",
5087Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5089 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5094 if (!llvm::isPowerOf2_64(size)) {
5095 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5097 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5099 Value temp2[] = {ext.getResult(), array};
5105 return inBoundsRead;
5108LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5110 auto index = getLoweredAndExtOrTruncValue(
5112 UIntType::get(op.getContext(),
5117 SmallVector<Value> loweredInputs;
5118 loweredInputs.reserve(op.getInputs().size());
5119 for (
auto input : op.getInputs()) {
5120 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5123 loweredInputs.push_back(lowered);
5127 return setLowering(op, createArrayIndexing(array, index));
5130LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5131 auto resultTy =
lowerType(op.getType());
5135 SmallVector<Value, 4> operands;
5136 operands.reserve(op.getSubstitutions().size());
5137 for (
auto operand : op.getSubstitutions()) {
5138 auto lowered = getLoweredValue(operand);
5141 operands.push_back(lowered);
5144 ArrayAttr symbols = op.getSymbolsAttr();
5146 symbols = ArrayAttr::get(op.getContext(), {});
5148 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5152LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5156 Type baseType = op.getType().getType();
5159 if (isa<ClockType>(baseType))
5160 xmrType = builder.getIntegerType(1);
5164 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5165 op.getRef(), op.getVerbatimSuffixAttr());
5168LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5172 if (isa<ClockType>(op.getType()))
5173 xmrType = builder.getIntegerType(1);
5177 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5178 op.getRef(), op.getVerbatimSuffixAttr());
5179 auto readXmr = getReadValue(xmr);
5180 if (!isa<ClockType>(op.getType()))
5181 return setLowering(op, readXmr);
5182 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5187LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5188LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5196LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5208FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5209 auto srcType = srcVal.getType();
5210 auto dstType = destVal.getType();
5211 if (srcType != dstType &&
5212 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5215 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5216 .Case<hw::WireOp>([&](
auto op) {
5217 maybeUnused(op.getInput());
5218 op.getInputMutable().assign(srcVal);
5221 .Case<seq::FirRegOp>([&](
auto op) {
5222 maybeUnused(op.getNext());
5223 op.getNextMutable().assign(srcVal);
5226 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5229 op.emitOpError(
"used as connect destination");
5232 .Default([](
auto) {
return false; });
5235LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5236 auto dest = op.getDest();
5238 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5239 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5241 return handleZeroBit(op.getSrc(), []() { return success(); });
5243 auto destVal = getPossiblyInoutLoweredValue(dest);
5247 auto result = lowerConnect(destVal, srcVal);
5255 if (updateIfBackedge(destVal, srcVal))
5258 if (!isa<hw::InOutType>(destVal.getType()))
5259 return op.emitError(
"destination isn't an inout type");
5265LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5266 auto dest = op.getDest();
5267 auto srcVal = getLoweredValue(op.getSrc());
5269 return handleZeroBit(op.getSrc(), []() { return success(); });
5271 auto destVal = getPossiblyInoutLoweredValue(dest);
5275 auto result = lowerConnect(destVal, srcVal);
5283 if (updateIfBackedge(destVal, srcVal))
5286 if (!isa<hw::InOutType>(destVal.getType()))
5287 return op.emitError(
"destination isn't an inout type");
5293LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5294 if (circuitState.lowerToCore)
5295 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5297 auto srcVal = getLoweredValue(op.getSrc());
5301 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5305 if (!isa<hw::InOutType>(destVal.getType()))
5306 return op.emitError(
"destination isn't an inout type");
5309 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5310 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5311 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5316LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5317 if (circuitState.lowerToCore)
5318 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5320 auto src = getLoweredNonClockValue(op.getSrc());
5321 auto clock = getLoweredNonClockValue(op.getClock());
5322 auto pred = getLoweredValue(op.getPredicate());
5323 if (!src || !clock || !pred)
5326 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5331 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5332 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5333 addToAlwaysBlock(clock, [&]() {
5334 addIfProceduralBlock(
5335 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5340LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5341 if (circuitState.lowerToCore)
5342 return op.emitOpError(
5343 "lower-to-core does not support firrtl.ref.force_initial");
5345 auto src = getLoweredNonClockValue(op.getSrc());
5346 auto pred = getLoweredValue(op.getPredicate());
5350 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5355 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5356 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5357 addToInitialBlock([&]() {
5358 addIfProceduralBlock(
5359 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5364LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5365 if (circuitState.lowerToCore)
5366 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5368 auto clock = getLoweredNonClockValue(op.getClock());
5369 auto pred = getLoweredValue(op.getPredicate());
5370 if (!clock || !pred)
5373 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5378 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5379 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5380 addToAlwaysBlock(clock, [&]() {
5381 addIfProceduralBlock(pred,
5382 [&]() { sv::ReleaseOp::create(builder, destVal); });
5387LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5388 if (circuitState.lowerToCore)
5389 return op.emitOpError(
5390 "lower-to-core does not support firrtl.ref.release_initial");
5392 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5393 auto pred = getLoweredValue(op.getPredicate());
5394 if (!destVal || !pred)
5398 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5399 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5400 addToInitialBlock([&]() {
5401 addIfProceduralBlock(pred,
5402 [&]() { sv::ReleaseOp::create(builder, destVal); });
5410 StringRef originalFormatString,
5411 ValueRange operands,
5412 StringAttr &result) {
5415 SmallString<32> formatString;
5416 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5417 char c = originalFormatString[i];
5421 formatString.push_back(c);
5424 SmallString<6> width;
5425 c = originalFormatString[++i];
5428 c = originalFormatString[++i];
5439 formatString.append(width);
5445 formatString.push_back(c);
5452 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5453 formatString.push_back(c);
5457 auto substitution = operands[subIdx++];
5458 assert(type_isa<FStringType>(substitution.getType()) &&
5459 "the operand for a '{{}}' substitution must be an 'fstring' type");
5461 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5462 .template Case<TimeOp>([&](
auto) {
5463 formatString.append(
"%0t");
5466 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5467 formatString.append(
"%m");
5470 .Default([&](
auto) {
5471 emitError(loc,
"has a substitution with an unimplemented "
5473 .attachNote(substitution.getLoc())
5474 <<
"op with an unimplemented lowering is here";
5484 formatString.push_back(c);
5488 result = StringAttr::get(loc->getContext(), formatString);
5495LogicalResult FIRRTLLowering::visitPrintfLike(
5496 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5497 auto clock = getLoweredNonClockValue(op.getClock());
5498 auto cond = getLoweredValue(op.getCond());
5499 if (!clock || !cond)
5502 StringAttr formatString;
5504 op.getSubstitutions(), formatString)))
5507 auto fn = [&](Value fd) {
5508 SmallVector<Value> operands;
5509 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5511 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5515 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5519LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5520 if (!circuitState.lowerToCore)
5521 return visitPrintfLike(op, {},
true);
5523 auto clock = getLoweredValue(op.getClock());
5524 auto cond = getLoweredValue(op.getCond());
5525 if (!clock || !cond)
5529 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5530 if (failed(formatString))
5533 auto stderrOp = sim::StderrStreamOp::create(builder);
5534 sim::TriggeredOp::create(builder, clock, cond, [&] {
5535 sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
5540LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5541 if (circuitState.lowerToCore) {
5542 auto clock = getLoweredValue(op.getClock());
5543 auto cond = getLoweredValue(op.getCond());
5544 if (!clock || !cond)
5547 auto fileFormatString = lowerSimFormatString(
5548 op.getOutputFileAttr(), op.getOutputFileSubstitutions());
5549 if (failed(fileFormatString))
5553 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5554 if (failed(formatString))
5557 sim::TriggeredOp::create(builder, clock, cond, [&] {
5558 auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
5559 sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
5564 StringAttr outputFileAttr;
5566 op.getOutputFileSubstitutions(),
5570 FileDescriptorInfo outputFile(outputFileAttr,
5571 op.getOutputFileSubstitutions());
5572 return visitPrintfLike(op, outputFile,
false);
5576LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5577 if (circuitState.lowerToCore)
5578 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5580 auto clock = getLoweredNonClockValue(op.getClock());
5581 auto cond = getLoweredValue(op.getCond());
5582 if (!clock || !cond)
5585 auto fn = [&](Value fd) {
5586 sv::FFlushOp::create(builder, op.getLoc(), fd);
5590 if (!op.getOutputFileAttr())
5591 return lowerStatementWithFd({}, clock, cond, fn,
false);
5595 StringAttr outputFileAttr;
5597 op.getOutputFileSubstitutions(),
5601 return lowerStatementWithFd(
5602 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5603 clock, cond, fn,
false);
5608LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5609 auto clock = getLoweredValue(op.getClock());
5610 auto cond = getLoweredValue(op.getCond());
5611 if (!clock || !cond)
5614 circuitState.usedStopCond =
true;
5615 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5618 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5619 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5621 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5622 op.getExitCode() == 0,
5631template <
typename... Args>
5633 StringRef opName, Args &&...args) {
5634 if (opName ==
"assert")
5635 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5636 if (opName ==
"assume")
5637 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5638 if (opName ==
"cover")
5639 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5640 llvm_unreachable(
"unknown verification op");
5646template <
typename... Args>
5648 StringRef opName, Args &&...args) {
5649 if (opName ==
"assert")
5650 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5651 if (opName ==
"assume")
5652 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5653 if (opName ==
"cover")
5654 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5655 llvm_unreachable(
"unknown verification op");
5659 switch (eventControl) {
5660 case EventControl::AtPosEdge:
5661 return verif::ClockEdge::Pos;
5662 case EventControl::AtEdge:
5663 return verif::ClockEdge::Both;
5664 case EventControl::AtNegEdge:
5665 return verif::ClockEdge::Neg;
5667 llvm_unreachable(
"unknown FIRRTL event control");
5670LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5671 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5672 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5673 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5674 if (guardsAttr && !guardsAttr.empty())
5675 return op->emitOpError(
5676 "lower-to-core does not support guarded verification statements");
5678 auto clock = getLoweredNonClockValue(opClock);
5679 auto enable = getLoweredValue(opEnable);
5680 auto predicate = getLoweredValue(opPredicate);
5681 if (!clock || !enable || !predicate)
5685 if (opNameAttr && !opNameAttr.getValue().empty())
5686 label = StringAttr::get(builder.getContext(),
5687 labelPrefix + opNameAttr.getValue());
5690 auto opName = op->getName().stripDialect();
5691 if (opName ==
"assert") {
5692 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5696 if (opName ==
"assume") {
5697 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5701 if (opName ==
"cover") {
5702 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5706 llvm_unreachable(
"unknown verification op");
5727LogicalResult FIRRTLLowering::lowerVerificationStatement(
5728 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5729 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5730 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5731 if (circuitState.lowerToCore)
5732 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5733 opPredicate, opEnable, opNameAttr,
5736 StringRef opName = op->getName().stripDialect();
5739 ArrayRef<Attribute> guards{};
5740 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5741 guards = guardsAttr.getValue();
5743 auto isCover = isa<CoverOp>(op);
5744 auto clock = getLoweredNonClockValue(opClock);
5745 auto enable = getLoweredValue(opEnable);
5746 auto predicate = getLoweredValue(opPredicate);
5747 if (!clock || !enable || !predicate)
5751 if (opNameAttr && !opNameAttr.getValue().empty())
5753 StringAttr prefixedLabel;
5756 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5759 SmallVector<Value> messageOps;
5763 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5764 flavor = VerificationFlavor::None;
5766 if (flavor == VerificationFlavor::None) {
5770 auto format = op->getAttrOfType<StringAttr>(
"format");
5772 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5773 if (!isa<AssertOp>(op))
5774 return op->emitError()
5775 <<
"ifElseFatal format cannot be used for non-assertions";
5776 flavor = VerificationFlavor::IfElseFatal;
5777 }
else if (isConcurrent)
5778 flavor = VerificationFlavor::SVA;
5780 flavor = VerificationFlavor::Immediate;
5783 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5787 opOperands, message)))
5790 if (failed(loweredFmtOperands(opOperands, messageOps)))
5793 if (flavor == VerificationFlavor::SVA) {
5798 for (
auto &loweredValue : messageOps)
5799 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5805 case VerificationFlavor::Immediate: {
5807 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5808 builder.getContext(), circt::sv::DeferAssert::Immediate);
5809 addToAlwaysBlock(clock, [&]() {
5810 addIfProceduralBlock(enable, [&]() {
5812 prefixedLabel, message, messageOps);
5817 case VerificationFlavor::IfElseFatal: {
5818 assert(isa<AssertOp>(op) &&
"only assert is expected");
5821 auto boolType = IntegerType::get(builder.getContext(), 1);
5822 predicate = comb::createOrFoldNot(builder, predicate,
true);
5823 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5825 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5826 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5827 addToAlwaysBlock(clock, [&]() {
5828 addIfProceduralBlock(predicate, [&]() {
5829 circuitState.usedStopCond =
true;
5830 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5832 circuitState.usedAssertVerboseCond =
true;
5833 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5835 addIfProceduralBlock(
5836 sv::MacroRefExprOp::create(builder, boolType,
5837 "ASSERT_VERBOSE_COND_"),
5839 sv::ErrorProceduralOp::create(builder, message, messageOps);
5841 addIfProceduralBlock(
5842 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5843 [&]() { sv::FatalProceduralOp::create(builder); });
5849 case VerificationFlavor::SVA: {
5854 comb::createOrFoldNot(builder, enable,
true);
5856 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5858 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5862 sv::EventControl event;
5863 switch (opEventControl) {
5864 case EventControl::AtPosEdge:
5865 event = circt::sv::EventControl::AtPosEdge;
5867 case EventControl::AtEdge:
5868 event = circt::sv::EventControl::AtEdge;
5870 case EventControl::AtNegEdge:
5871 event = circt::sv::EventControl::AtNegEdge;
5877 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5878 predicate, prefixedLabel, message, messageOps);
5881 case VerificationFlavor::None:
5883 "flavor `None` must be converted into one of concreate flavors");
5890 return emitGuards(op->getLoc(), guards,
emit);
5894LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5895 return lowerVerificationStatement(
5896 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5897 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5898 op.getIsConcurrent(), op.getEventControl());
5902LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5903 return lowerVerificationStatement(
5904 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5905 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5906 op.getIsConcurrent(), op.getEventControl());
5910LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5911 return lowerVerificationStatement(
5912 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5913 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5914 op.getIsConcurrent(), op.getEventControl());
5918LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5919 if (circuitState.lowerToCore) {
5920 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5921 if (guardsAttr && !guardsAttr.empty())
5922 return op.emitOpError(
5923 "lower-to-core does not support guarded verification statements");
5925 auto predicate = getLoweredValue(op.getPredicate());
5926 auto enable = getLoweredValue(op.getEnable());
5927 if (!predicate || !enable)
5930 auto label = op.getNameAttr();
5931 StringAttr assumeLabel;
5932 if (label && !label.empty())
5934 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5935 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5943 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5944 ArrayRef<Attribute> guards =
5945 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5947 auto label = op.getNameAttr();
5948 StringAttr assumeLabel;
5949 if (label && !label.empty())
5951 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5952 auto predicate = getLoweredValue(op.getPredicate());
5953 auto enable = getLoweredValue(op.getEnable());
5954 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5955 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5957 SmallVector<Value> messageOps;
5958 for (
auto operand : op.getSubstitutions()) {
5959 auto loweredValue = getLoweredValue(operand);
5960 if (!loweredValue) {
5964 loweredValue = getOrCreateIntConstant(1, 0);
5966 messageOps.push_back(loweredValue);
5968 return emitGuards(op.getLoc(), guards, [&]() {
5969 sv::AlwaysOp::create(
5970 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5972 if (op.getMessageAttr().getValue().empty())
5973 buildImmediateVerifOp(
5974 builder,
"assume", predicate,
5975 circt::sv::DeferAssertAttr::get(
5976 builder.getContext(), circt::sv::DeferAssert::Immediate),
5979 buildImmediateVerifOp(
5980 builder,
"assume", predicate,
5981 circt::sv::DeferAssertAttr::get(
5982 builder.getContext(), circt::sv::DeferAssert::Immediate),
5983 assumeLabel, op.getMessageAttr(), messageOps);
5988LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5990 if (op.getAttached().size() < 2)
5993 SmallVector<Value, 4> inoutValues;
5994 for (
auto v : op.getAttached()) {
5995 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5996 if (!inoutValues.back()) {
6000 inoutValues.pop_back();
6004 if (!isa<hw::InOutType>(inoutValues.back().getType()))
6005 return op.emitError(
"operand isn't an inout type");
6008 if (inoutValues.size() < 2)
6016 if (circuitState.lowerToCore)
6017 return op.emitOpError(
6018 "lower-to-core does not support firrtl.attach that requires SV "
6024 bool isAttachInternalOnly =
6025 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
6027 if (isAttachInternalOnly) {
6028 auto v0 = inoutValues.front();
6029 for (
auto v : inoutValues) {
6032 v.replaceAllUsesWith(v0);
6039 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
6040 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
6045 SmallVector<Value, 4> values;
6046 for (
auto inoutValue : inoutValues)
6047 values.push_back(getReadValue(inoutValue));
6049 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
6050 for (
size_t i2 = 0; i2 != e; ++i2)
6058 sv::IfDefOp::create(
6059 builder,
"VERILATOR",
6061 sv::VerbatimOp::create(
6063 "`error \"Verilator does not support alias and thus "
6065 "arbitrarily connect bidirectional wires and ports\"");
6067 [&]() { sv::AliasOp::create(builder, inoutValues); });
6073LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
6074 sv::BindOp::create(builder, op.getInstanceAttr());
6078LogicalResult FIRRTLLowering::fixupLTLOps() {
6079 if (ltlOpFixupWorklist.empty())
6081 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6085 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6086 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6087 if (isa<
hw::WireOp>(user))
6088 ltlOpFixupWorklist.insert(user);
6091 while (!ltlOpFixupWorklist.empty()) {
6092 auto *op = ltlOpFixupWorklist.pop_back_val();
6095 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6096 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6097 SmallVector<Type, 2> types;
6098 auto result = opIntf.inferReturnTypes(
6099 op->getContext(), op->getLoc(), op->getOperands(),
6100 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6104 assert(types.size() == op->getNumResults());
6108 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6109 if (result.getType() == type)
6111 LLVM_DEBUG(llvm::dbgs()
6112 <<
" - Result #" << result.getResultNumber() <<
" from "
6113 << result.getType() <<
" to " << type <<
"\n");
6114 result.setType(type);
6115 for (
auto *user : result.getUsers())
6117 ltlOpFixupWorklist.insert(user);
6122 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6123 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6124 wireOp.replaceAllUsesWith(wireOp.getInput());
6125 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6126 if (wireOp.use_empty())
6133 SmallPtrSet<Operation *, 4> usersReported;
6134 for (
auto *user : op->getUsers()) {
6135 if (!usersReported.insert(user).second)
6137 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6138 user->getDialect()))
6140 if (isa<hw::WireOp>(user))
6142 auto d = op->emitError(
6143 "verification operation used in a non-verification context");
6144 d.attachNote(user->getLoc())
6145 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult emitFile(ArrayRef< Operation * > operations, StringRef filePath, raw_ostream &os)
Emits the given operation to a file represented by the passed ostream and file-path.
static void lowerModuleBody(FModuleOp mod, const DenseMap< StringAttr, PortConversion > &ports)
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 ltl::ClockEdge firrtlToLTLClockEdge(EventControl eventControl)
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 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 verif::ClockEdge firrtlToVerifClockEdge(EventControl eventControl)
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 LogicalResult resolveFormatString(Location loc, StringRef originalFormatString, ValueRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static StringAttr getArgName(Operation *op, size_t idx)
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.
void setValue(mlir::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.
The target of an inner symbol, the entity the symbol is a handle for.
This is an edge in the InstanceGraph.
create(elements, Type result_type=None)
create(str sym_name, Type type, str verilog_name=None)
create(data_type, name=None, sym_name=None)
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.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
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.
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
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.
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, bool lowerToCore=false)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
The namespace of a CircuitOp, generally inhabited by modules.
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