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);
4816LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4817 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4818 ltl::ClockEdge::Pos,
4819 getLoweredNonClockValue(op.getClock()));
4822template <
typename TargetOp,
typename IntrinsicOp>
4823LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4824 auto property = getLoweredValue(op.getProperty());
4825 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4826 TargetOp::create(builder, property, enable, op.getLabelAttr());
4830LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4831 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4834LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4835 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4838LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4839 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4842LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4843 if (!isa<verif::ContractOp>(op->getParentOp()))
4844 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4845 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4848LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4849 if (!isa<verif::ContractOp>(op->getParentOp()))
4850 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4851 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4854LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4855 auto clock = getLoweredNonClockValue(op.getClock());
4856 auto reset = getLoweredValue(op.getReset());
4857 if (!clock || !reset)
4859 auto resetType = op.getReset().getType();
4860 auto uintResetType = dyn_cast<UIntType>(resetType);
4861 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4862 auto isAsync = isa<AsyncResetType>(resetType);
4863 if (!isAsync && !isSync) {
4864 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4865 "requires sync or async reset");
4866 d.attachNote() <<
"reset is of type " << resetType
4867 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4870 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4877LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4878 auto input = getLoweredValue(op.getInput());
4882 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4883 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4886LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4887 auto resultTy =
lowerType(op.getType());
4894 if (type_isa<AnalogType>(op.getType()))
4897 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4900 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4911 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4913 if (!type_isa<IntegerType>(resultTy))
4915 return setLowering(op, constant);
4919 op.emitOpError(
"unsupported type");
4923LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4924 auto input = getLoweredValue(op.getInput());
4927 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4928 if (op.getAmount() == 0)
4929 return setLowering(op, Value());
4930 Type resultType = builder.getIntegerType(op.getAmount());
4931 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4932 inWidth - op.getAmount());
4935LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4936 auto input = getLoweredValue(op.getInput());
4939 if (op.getAmount() == 0)
4941 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4946 if (op.getAmount() == 0)
4947 return setLowering(op, input);
4949 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4950 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4953LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4954 auto input = getLoweredValue(op.getInput());
4959 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4960 auto shiftAmount = op.getAmount();
4961 if (shiftAmount >= inWidth) {
4963 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4964 return setLowering(op, {});
4967 shiftAmount = inWidth - 1;
4970 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4971 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4974LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4975 auto input = getLoweredValue(op.getInput());
4979 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4980 if (inWidth == op.getAmount())
4981 return setLowering(op, Value());
4982 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4983 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4986LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4987 auto cond = getLoweredValue(op.getSel());
4988 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4989 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4990 if (!cond || !ifTrue || !ifFalse)
4993 if (isa<ClockType>(op.getType()))
4994 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4995 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4999LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
5000 auto cond = getLoweredValue(op.getSel());
5001 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5002 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5003 if (!cond || !ifTrue || !ifFalse)
5006 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
5008 return setLowering(op, createValueWithMuxAnnotation(val,
true));
5011LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
5012 auto sel = getLoweredValue(op.getSel());
5013 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
5014 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
5015 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
5016 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
5017 if (!sel || !v3 || !v2 || !v1 || !v0)
5019 Value array[] = {v3, v2, v1, v0};
5022 return setLowering(op, createValueWithMuxAnnotation(val,
false));
5041Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
5042 assert(op->getNumResults() == 1 &&
"only expect a single result");
5043 auto val = op->getResult(0);
5047 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5054 OpBuilder::InsertionGuard guard(builder);
5055 builder.setInsertionPoint(op);
5056 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5057 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
5059 op->getContext(),
nullptr, 0,
5062 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5063 op->setOperand(idx, wire);
5068 sv::setSVAttributes(assignOp,
5069 sv::SVAttributeAttr::get(builder.getContext(),
5070 "synopsys infer_mux_override",
5075Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5077 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5082 if (!llvm::isPowerOf2_64(size)) {
5083 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5085 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5087 Value temp2[] = {ext.getResult(), array};
5093 return inBoundsRead;
5096LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5098 auto index = getLoweredAndExtOrTruncValue(
5100 UIntType::get(op.getContext(),
5105 SmallVector<Value> loweredInputs;
5106 loweredInputs.reserve(op.getInputs().size());
5107 for (
auto input : op.getInputs()) {
5108 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5111 loweredInputs.push_back(lowered);
5115 return setLowering(op, createArrayIndexing(array, index));
5118LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5119 auto resultTy =
lowerType(op.getType());
5123 SmallVector<Value, 4> operands;
5124 operands.reserve(op.getSubstitutions().size());
5125 for (
auto operand : op.getSubstitutions()) {
5126 auto lowered = getLoweredValue(operand);
5129 operands.push_back(lowered);
5132 ArrayAttr symbols = op.getSymbolsAttr();
5134 symbols = ArrayAttr::get(op.getContext(), {});
5136 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5140LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5144 Type baseType = op.getType().getType();
5147 if (isa<ClockType>(baseType))
5148 xmrType = builder.getIntegerType(1);
5152 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5153 op.getRef(), op.getVerbatimSuffixAttr());
5156LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5160 if (isa<ClockType>(op.getType()))
5161 xmrType = builder.getIntegerType(1);
5165 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5166 op.getRef(), op.getVerbatimSuffixAttr());
5167 auto readXmr = getReadValue(xmr);
5168 if (!isa<ClockType>(op.getType()))
5169 return setLowering(op, readXmr);
5170 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5175LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5176LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5184LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5196FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5197 auto srcType = srcVal.getType();
5198 auto dstType = destVal.getType();
5199 if (srcType != dstType &&
5200 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5203 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5204 .Case<hw::WireOp>([&](
auto op) {
5205 maybeUnused(op.getInput());
5206 op.getInputMutable().assign(srcVal);
5209 .Case<seq::FirRegOp>([&](
auto op) {
5210 maybeUnused(op.getNext());
5211 op.getNextMutable().assign(srcVal);
5214 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5217 op.emitOpError(
"used as connect destination");
5220 .Default([](
auto) {
return false; });
5223LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5224 auto dest = op.getDest();
5226 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5227 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5229 return handleZeroBit(op.getSrc(), []() { return success(); });
5231 auto destVal = getPossiblyInoutLoweredValue(dest);
5235 auto result = lowerConnect(destVal, srcVal);
5243 if (updateIfBackedge(destVal, srcVal))
5246 if (!isa<hw::InOutType>(destVal.getType()))
5247 return op.emitError(
"destination isn't an inout type");
5253LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5254 auto dest = op.getDest();
5255 auto srcVal = getLoweredValue(op.getSrc());
5257 return handleZeroBit(op.getSrc(), []() { return success(); });
5259 auto destVal = getPossiblyInoutLoweredValue(dest);
5263 auto result = lowerConnect(destVal, srcVal);
5271 if (updateIfBackedge(destVal, srcVal))
5274 if (!isa<hw::InOutType>(destVal.getType()))
5275 return op.emitError(
"destination isn't an inout type");
5281LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5282 if (circuitState.lowerToCore)
5283 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5285 auto srcVal = getLoweredValue(op.getSrc());
5289 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5293 if (!isa<hw::InOutType>(destVal.getType()))
5294 return op.emitError(
"destination isn't an inout type");
5297 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5298 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5299 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5304LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5305 if (circuitState.lowerToCore)
5306 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5308 auto src = getLoweredNonClockValue(op.getSrc());
5309 auto clock = getLoweredNonClockValue(op.getClock());
5310 auto pred = getLoweredValue(op.getPredicate());
5311 if (!src || !clock || !pred)
5314 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5319 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5320 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5321 addToAlwaysBlock(clock, [&]() {
5322 addIfProceduralBlock(
5323 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5328LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5329 if (circuitState.lowerToCore)
5330 return op.emitOpError(
5331 "lower-to-core does not support firrtl.ref.force_initial");
5333 auto src = getLoweredNonClockValue(op.getSrc());
5334 auto pred = getLoweredValue(op.getPredicate());
5338 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5343 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5344 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5345 addToInitialBlock([&]() {
5346 addIfProceduralBlock(
5347 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5352LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5353 if (circuitState.lowerToCore)
5354 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5356 auto clock = getLoweredNonClockValue(op.getClock());
5357 auto pred = getLoweredValue(op.getPredicate());
5358 if (!clock || !pred)
5361 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5366 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5367 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5368 addToAlwaysBlock(clock, [&]() {
5369 addIfProceduralBlock(pred,
5370 [&]() { sv::ReleaseOp::create(builder, destVal); });
5375LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5376 if (circuitState.lowerToCore)
5377 return op.emitOpError(
5378 "lower-to-core does not support firrtl.ref.release_initial");
5380 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5381 auto pred = getLoweredValue(op.getPredicate());
5382 if (!destVal || !pred)
5386 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5387 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5388 addToInitialBlock([&]() {
5389 addIfProceduralBlock(pred,
5390 [&]() { sv::ReleaseOp::create(builder, destVal); });
5398 StringRef originalFormatString,
5399 ValueRange operands,
5400 StringAttr &result) {
5403 SmallString<32> formatString;
5404 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5405 char c = originalFormatString[i];
5409 formatString.push_back(c);
5412 SmallString<6> width;
5413 c = originalFormatString[++i];
5416 c = originalFormatString[++i];
5427 formatString.append(width);
5433 formatString.push_back(c);
5440 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5441 formatString.push_back(c);
5445 auto substitution = operands[subIdx++];
5446 assert(type_isa<FStringType>(substitution.getType()) &&
5447 "the operand for a '{{}}' substitution must be an 'fstring' type");
5449 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5450 .template Case<TimeOp>([&](
auto) {
5451 formatString.append(
"%0t");
5454 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5455 formatString.append(
"%m");
5458 .Default([&](
auto) {
5459 emitError(loc,
"has a substitution with an unimplemented "
5461 .attachNote(substitution.getLoc())
5462 <<
"op with an unimplemented lowering is here";
5472 formatString.push_back(c);
5476 result = StringAttr::get(loc->getContext(), formatString);
5483LogicalResult FIRRTLLowering::visitPrintfLike(
5484 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5485 auto clock = getLoweredNonClockValue(op.getClock());
5486 auto cond = getLoweredValue(op.getCond());
5487 if (!clock || !cond)
5490 StringAttr formatString;
5492 op.getSubstitutions(), formatString)))
5495 auto fn = [&](Value fd) {
5496 SmallVector<Value> operands;
5497 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5499 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5503 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5507LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5508 if (!circuitState.lowerToCore)
5509 return visitPrintfLike(op, {},
true);
5511 auto clock = getLoweredValue(op.getClock());
5512 auto cond = getLoweredValue(op.getCond());
5513 if (!clock || !cond)
5517 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5518 if (failed(formatString))
5521 auto stderrOp = sim::StderrStreamOp::create(builder);
5522 sim::TriggeredOp::create(builder, clock, cond, [&] {
5523 sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
5528LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5529 if (circuitState.lowerToCore) {
5530 auto clock = getLoweredValue(op.getClock());
5531 auto cond = getLoweredValue(op.getCond());
5532 if (!clock || !cond)
5535 auto fileFormatString = lowerSimFormatString(
5536 op.getOutputFileAttr(), op.getOutputFileSubstitutions());
5537 if (failed(fileFormatString))
5541 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5542 if (failed(formatString))
5545 sim::TriggeredOp::create(builder, clock, cond, [&] {
5546 auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
5547 sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
5552 StringAttr outputFileAttr;
5554 op.getOutputFileSubstitutions(),
5558 FileDescriptorInfo outputFile(outputFileAttr,
5559 op.getOutputFileSubstitutions());
5560 return visitPrintfLike(op, outputFile,
false);
5564LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5565 if (circuitState.lowerToCore)
5566 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5568 auto clock = getLoweredNonClockValue(op.getClock());
5569 auto cond = getLoweredValue(op.getCond());
5570 if (!clock || !cond)
5573 auto fn = [&](Value fd) {
5574 sv::FFlushOp::create(builder, op.getLoc(), fd);
5578 if (!op.getOutputFileAttr())
5579 return lowerStatementWithFd({}, clock, cond, fn,
false);
5583 StringAttr outputFileAttr;
5585 op.getOutputFileSubstitutions(),
5589 return lowerStatementWithFd(
5590 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5591 clock, cond, fn,
false);
5596LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5597 auto clock = getLoweredValue(op.getClock());
5598 auto cond = getLoweredValue(op.getCond());
5599 if (!clock || !cond)
5602 circuitState.usedStopCond =
true;
5603 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5606 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5607 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5609 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5610 op.getExitCode() == 0,
5619template <
typename... Args>
5621 StringRef opName, Args &&...args) {
5622 if (opName ==
"assert")
5623 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5624 if (opName ==
"assume")
5625 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5626 if (opName ==
"cover")
5627 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5628 llvm_unreachable(
"unknown verification op");
5634template <
typename... Args>
5636 StringRef opName, Args &&...args) {
5637 if (opName ==
"assert")
5638 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5639 if (opName ==
"assume")
5640 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5641 if (opName ==
"cover")
5642 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5643 llvm_unreachable(
"unknown verification op");
5647 switch (eventControl) {
5648 case EventControl::AtPosEdge:
5649 return verif::ClockEdge::Pos;
5650 case EventControl::AtEdge:
5651 return verif::ClockEdge::Both;
5652 case EventControl::AtNegEdge:
5653 return verif::ClockEdge::Neg;
5655 llvm_unreachable(
"unknown FIRRTL event control");
5658LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5659 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5660 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5661 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5662 if (guardsAttr && !guardsAttr.empty())
5663 return op->emitOpError(
5664 "lower-to-core does not support guarded verification statements");
5666 auto clock = getLoweredNonClockValue(opClock);
5667 auto enable = getLoweredValue(opEnable);
5668 auto predicate = getLoweredValue(opPredicate);
5669 if (!clock || !enable || !predicate)
5673 if (opNameAttr && !opNameAttr.getValue().empty())
5674 label = StringAttr::get(builder.getContext(),
5675 labelPrefix + opNameAttr.getValue());
5678 auto opName = op->getName().stripDialect();
5679 if (opName ==
"assert") {
5680 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5684 if (opName ==
"assume") {
5685 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5689 if (opName ==
"cover") {
5690 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5694 llvm_unreachable(
"unknown verification op");
5715LogicalResult FIRRTLLowering::lowerVerificationStatement(
5716 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5717 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5718 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5719 if (circuitState.lowerToCore)
5720 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5721 opPredicate, opEnable, opNameAttr,
5724 StringRef opName = op->getName().stripDialect();
5727 ArrayRef<Attribute> guards{};
5728 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5729 guards = guardsAttr.getValue();
5731 auto isCover = isa<CoverOp>(op);
5732 auto clock = getLoweredNonClockValue(opClock);
5733 auto enable = getLoweredValue(opEnable);
5734 auto predicate = getLoweredValue(opPredicate);
5735 if (!clock || !enable || !predicate)
5739 if (opNameAttr && !opNameAttr.getValue().empty())
5741 StringAttr prefixedLabel;
5744 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5747 SmallVector<Value> messageOps;
5751 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5752 flavor = VerificationFlavor::None;
5754 if (flavor == VerificationFlavor::None) {
5758 auto format = op->getAttrOfType<StringAttr>(
"format");
5760 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5761 if (!isa<AssertOp>(op))
5762 return op->emitError()
5763 <<
"ifElseFatal format cannot be used for non-assertions";
5764 flavor = VerificationFlavor::IfElseFatal;
5765 }
else if (isConcurrent)
5766 flavor = VerificationFlavor::SVA;
5768 flavor = VerificationFlavor::Immediate;
5771 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5775 opOperands, message)))
5778 if (failed(loweredFmtOperands(opOperands, messageOps)))
5781 if (flavor == VerificationFlavor::SVA) {
5786 for (
auto &loweredValue : messageOps)
5787 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5793 case VerificationFlavor::Immediate: {
5795 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5796 builder.getContext(), circt::sv::DeferAssert::Immediate);
5797 addToAlwaysBlock(clock, [&]() {
5798 addIfProceduralBlock(enable, [&]() {
5800 prefixedLabel, message, messageOps);
5805 case VerificationFlavor::IfElseFatal: {
5806 assert(isa<AssertOp>(op) &&
"only assert is expected");
5809 auto boolType = IntegerType::get(builder.getContext(), 1);
5810 predicate = comb::createOrFoldNot(builder, predicate,
true);
5811 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5813 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5814 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5815 addToAlwaysBlock(clock, [&]() {
5816 addIfProceduralBlock(predicate, [&]() {
5817 circuitState.usedStopCond =
true;
5818 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5820 circuitState.usedAssertVerboseCond =
true;
5821 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5823 addIfProceduralBlock(
5824 sv::MacroRefExprOp::create(builder, boolType,
5825 "ASSERT_VERBOSE_COND_"),
5827 sv::ErrorProceduralOp::create(builder, message, messageOps);
5829 addIfProceduralBlock(
5830 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5831 [&]() { sv::FatalProceduralOp::create(builder); });
5837 case VerificationFlavor::SVA: {
5842 comb::createOrFoldNot(builder, enable,
true);
5844 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5846 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5850 sv::EventControl event;
5851 switch (opEventControl) {
5852 case EventControl::AtPosEdge:
5853 event = circt::sv::EventControl::AtPosEdge;
5855 case EventControl::AtEdge:
5856 event = circt::sv::EventControl::AtEdge;
5858 case EventControl::AtNegEdge:
5859 event = circt::sv::EventControl::AtNegEdge;
5865 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5866 predicate, prefixedLabel, message, messageOps);
5869 case VerificationFlavor::None:
5871 "flavor `None` must be converted into one of concreate flavors");
5878 return emitGuards(op->getLoc(), guards,
emit);
5882LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5883 return lowerVerificationStatement(
5884 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5885 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5886 op.getIsConcurrent(), op.getEventControl());
5890LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5891 return lowerVerificationStatement(
5892 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5893 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5894 op.getIsConcurrent(), op.getEventControl());
5898LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5899 return lowerVerificationStatement(
5900 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5901 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5902 op.getIsConcurrent(), op.getEventControl());
5906LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5907 if (circuitState.lowerToCore) {
5908 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5909 if (guardsAttr && !guardsAttr.empty())
5910 return op.emitOpError(
5911 "lower-to-core does not support guarded verification statements");
5913 auto predicate = getLoweredValue(op.getPredicate());
5914 auto enable = getLoweredValue(op.getEnable());
5915 if (!predicate || !enable)
5918 auto label = op.getNameAttr();
5919 StringAttr assumeLabel;
5920 if (label && !label.empty())
5922 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5923 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5931 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5932 ArrayRef<Attribute> guards =
5933 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5935 auto label = op.getNameAttr();
5936 StringAttr assumeLabel;
5937 if (label && !label.empty())
5939 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5940 auto predicate = getLoweredValue(op.getPredicate());
5941 auto enable = getLoweredValue(op.getEnable());
5942 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5943 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5945 SmallVector<Value> messageOps;
5946 for (
auto operand : op.getSubstitutions()) {
5947 auto loweredValue = getLoweredValue(operand);
5948 if (!loweredValue) {
5952 loweredValue = getOrCreateIntConstant(1, 0);
5954 messageOps.push_back(loweredValue);
5956 return emitGuards(op.getLoc(), guards, [&]() {
5957 sv::AlwaysOp::create(
5958 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5960 if (op.getMessageAttr().getValue().empty())
5961 buildImmediateVerifOp(
5962 builder,
"assume", predicate,
5963 circt::sv::DeferAssertAttr::get(
5964 builder.getContext(), circt::sv::DeferAssert::Immediate),
5967 buildImmediateVerifOp(
5968 builder,
"assume", predicate,
5969 circt::sv::DeferAssertAttr::get(
5970 builder.getContext(), circt::sv::DeferAssert::Immediate),
5971 assumeLabel, op.getMessageAttr(), messageOps);
5976LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5978 if (op.getAttached().size() < 2)
5981 SmallVector<Value, 4> inoutValues;
5982 for (
auto v : op.getAttached()) {
5983 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5984 if (!inoutValues.back()) {
5988 inoutValues.pop_back();
5992 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5993 return op.emitError(
"operand isn't an inout type");
5996 if (inoutValues.size() < 2)
6004 if (circuitState.lowerToCore)
6005 return op.emitOpError(
6006 "lower-to-core does not support firrtl.attach that requires SV "
6012 bool isAttachInternalOnly =
6013 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
6015 if (isAttachInternalOnly) {
6016 auto v0 = inoutValues.front();
6017 for (
auto v : inoutValues) {
6020 v.replaceAllUsesWith(v0);
6027 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
6028 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
6033 SmallVector<Value, 4> values;
6034 for (
auto inoutValue : inoutValues)
6035 values.push_back(getReadValue(inoutValue));
6037 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
6038 for (
size_t i2 = 0; i2 != e; ++i2)
6046 sv::IfDefOp::create(
6047 builder,
"VERILATOR",
6049 sv::VerbatimOp::create(
6051 "`error \"Verilator does not support alias and thus "
6053 "arbitrarily connect bidirectional wires and ports\"");
6055 [&]() { sv::AliasOp::create(builder, inoutValues); });
6061LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
6062 sv::BindOp::create(builder, op.getInstanceAttr());
6066LogicalResult FIRRTLLowering::fixupLTLOps() {
6067 if (ltlOpFixupWorklist.empty())
6069 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6073 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6074 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6075 if (isa<
hw::WireOp>(user))
6076 ltlOpFixupWorklist.insert(user);
6079 while (!ltlOpFixupWorklist.empty()) {
6080 auto *op = ltlOpFixupWorklist.pop_back_val();
6083 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6084 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6085 SmallVector<Type, 2> types;
6086 auto result = opIntf.inferReturnTypes(
6087 op->getContext(), op->getLoc(), op->getOperands(),
6088 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6092 assert(types.size() == op->getNumResults());
6096 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6097 if (result.getType() == type)
6099 LLVM_DEBUG(llvm::dbgs()
6100 <<
" - Result #" << result.getResultNumber() <<
" from "
6101 << result.getType() <<
" to " << type <<
"\n");
6102 result.setType(type);
6103 for (
auto *user : result.getUsers())
6105 ltlOpFixupWorklist.insert(user);
6110 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6111 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6112 wireOp.replaceAllUsesWith(wireOp.getInput());
6113 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6114 if (wireOp.use_empty())
6121 SmallPtrSet<Operation *, 4> usersReported;
6122 for (
auto *user : op->getUsers()) {
6123 if (!usersReported.insert(user).second)
6125 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6126 user->getDialect()))
6128 if (isa<hw::WireOp>(user))
6130 auto d = op->emitError(
6131 "verification operation used in a non-verification context");
6132 d.attachNote(user->getLoc())
6133 <<
"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 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