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 Value getOrCreateXConstant(
unsigned numBits);
1743 Value getOrCreateZConstant(Type type);
1744 Value getPossiblyInoutLoweredValue(Value value);
1745 Value getLoweredValue(Value value);
1746 Value getLoweredNonClockValue(Value value);
1747 Value getLoweredAndExtendedValue(Value value, Type destType);
1748 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1749 LogicalResult setLowering(Value orig, Value result);
1750 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1751 template <
typename ResultOpType,
typename... CtorArgTypes>
1752 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1753 template <
typename ResultOpType,
typename... CtorArgTypes>
1754 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1755 Backedge createBackedge(Location loc, Type type);
1756 Backedge createBackedge(Value orig, Type type);
1757 bool updateIfBackedge(Value dest, Value src);
1760 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1765 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1766 if (forceable.isForceable())
1774 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1775 auto attr = op.getInnerSymAttr();
1779 if (requiresInnerSymbol(op))
1781 op.getContext(), attr, 0,
1789 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1790 Operation *instanceOp,
1791 SmallVectorImpl<Value> &inputOperands);
1793 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1797 Value getReadValue(Value v);
1799 Value getNonClockValue(Value v);
1801 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1802 sv::ResetType resetStyle, sv::EventControl resetEdge,
1803 Value reset,
const std::function<
void(
void)> &body = {},
1804 const std::function<void(
void)> &resetBody = {});
1805 void addToAlwaysBlock(Value clock,
1806 const std::function<
void(
void)> &body = {}) {
1807 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1808 sv::EventControl(), Value(), body,
1809 std::function<
void(
void)>());
1812 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1813 std::function<
void(
void)>
emit);
1814 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1815 std::function<
void(
void)> elseCtor = {});
1816 void addToInitialBlock(std::function<
void(
void)> body);
1817 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1818 std::function<
void(
void)> elseCtor = {});
1819 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1821 bool allowTruncate);
1822 Value createArrayIndexing(Value array, Value index);
1823 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1825 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1826 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1827 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1830 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1831 UnloweredOpResult handleUnloweredOp(Operation *op);
1832 LogicalResult visitExpr(ConstantOp op);
1833 LogicalResult visitExpr(SpecialConstantOp op);
1834 LogicalResult visitExpr(SubindexOp op);
1835 LogicalResult visitExpr(SubaccessOp op);
1836 LogicalResult visitExpr(SubfieldOp op);
1837 LogicalResult visitExpr(VectorCreateOp op);
1838 LogicalResult visitExpr(BundleCreateOp op);
1839 LogicalResult visitExpr(FEnumCreateOp op);
1840 LogicalResult visitExpr(AggregateConstantOp op);
1841 LogicalResult visitExpr(IsTagOp op);
1842 LogicalResult visitExpr(SubtagOp op);
1843 LogicalResult visitExpr(TagExtractOp op);
1846 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1847 return visitUnrealizedConversionCast(castOp);
1852 LogicalResult visitDecl(WireOp op);
1853 LogicalResult visitDecl(NodeOp op);
1854 LogicalResult visitDecl(RegOp op);
1855 LogicalResult visitDecl(RegResetOp op);
1856 LogicalResult visitDecl(MemOp op);
1857 LogicalResult visitDecl(InstanceOp oldInstance);
1858 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1859 LogicalResult visitDecl(VerbatimWireOp op);
1860 LogicalResult visitDecl(ContractOp op);
1863 LogicalResult lowerNoopCast(Operation *op);
1864 LogicalResult visitExpr(AsSIntPrimOp op);
1865 LogicalResult visitExpr(AsUIntPrimOp op);
1866 LogicalResult visitExpr(AsClockPrimOp op);
1867 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1869 LogicalResult visitExpr(HWStructCastOp op);
1870 LogicalResult visitExpr(BitCastOp op);
1872 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1873 LogicalResult visitExpr(CvtPrimOp op);
1874 LogicalResult visitExpr(NotPrimOp op);
1875 LogicalResult visitExpr(NegPrimOp op);
1876 LogicalResult visitExpr(PadPrimOp op);
1877 LogicalResult visitExpr(XorRPrimOp op);
1878 LogicalResult visitExpr(AndRPrimOp op);
1879 LogicalResult visitExpr(OrRPrimOp op);
1882 template <
typename ResultUnsignedOpType,
1883 typename ResultSignedOpType = ResultUnsignedOpType>
1884 LogicalResult lowerBinOp(Operation *op);
1885 template <
typename ResultOpType>
1886 LogicalResult lowerBinOpToVariadic(Operation *op);
1888 template <
typename ResultOpType>
1889 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1891 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1892 ICmpPredicate unsignedOp);
1893 template <
typename SignedOp,
typename Un
signedOp>
1894 LogicalResult lowerDivLikeOp(Operation *op);
1896 LogicalResult visitExpr(CatPrimOp op);
1898 LogicalResult visitExpr(AndPrimOp op) {
1899 return lowerBinOpToVariadic<comb::AndOp>(op);
1901 LogicalResult visitExpr(OrPrimOp op) {
1902 return lowerBinOpToVariadic<comb::OrOp>(op);
1904 LogicalResult visitExpr(XorPrimOp op) {
1905 return lowerBinOpToVariadic<comb::XorOp>(op);
1907 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1908 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1910 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1911 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1913 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1914 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1916 LogicalResult visitExpr(AddPrimOp op) {
1917 return lowerBinOpToVariadic<comb::AddOp>(op);
1919 LogicalResult visitExpr(EQPrimOp op) {
1920 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1922 LogicalResult visitExpr(NEQPrimOp op) {
1923 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1925 LogicalResult visitExpr(LTPrimOp op) {
1926 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1928 LogicalResult visitExpr(LEQPrimOp op) {
1929 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1931 LogicalResult visitExpr(GTPrimOp op) {
1932 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1934 LogicalResult visitExpr(GEQPrimOp op) {
1935 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1938 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1939 LogicalResult visitExpr(MulPrimOp op) {
1940 return lowerBinOpToVariadic<comb::MulOp>(op);
1942 LogicalResult visitExpr(DivPrimOp op) {
1943 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1945 LogicalResult visitExpr(RemPrimOp op) {
1946 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1950 LogicalResult visitExpr(IsXIntrinsicOp op);
1951 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1952 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1953 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1954 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1955 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1956 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1957 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1958 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1959 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1960 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1961 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1962 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1963 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1964 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1965 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1966 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1967 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1968 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1969 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1970 LogicalResult visitExpr(LTLPastIntrinsicOp op);
1971 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1973 template <
typename TargetOp,
typename IntrinsicOp>
1974 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1975 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1976 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1977 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1978 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1979 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1980 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1981 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1984 LogicalResult visitExpr(BitsPrimOp op);
1985 LogicalResult visitExpr(InvalidValueOp op);
1986 LogicalResult visitExpr(HeadPrimOp op);
1987 LogicalResult visitExpr(ShlPrimOp op);
1988 LogicalResult visitExpr(ShrPrimOp op);
1989 LogicalResult visitExpr(DShlPrimOp op) {
1990 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1992 LogicalResult visitExpr(DShrPrimOp op) {
1993 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1995 LogicalResult visitExpr(DShlwPrimOp op) {
1996 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1998 LogicalResult visitExpr(TailPrimOp op);
1999 LogicalResult visitExpr(MuxPrimOp op);
2000 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2001 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2002 LogicalResult visitExpr(MultibitMuxOp op);
2003 LogicalResult visitExpr(VerbatimExprOp op);
2004 LogicalResult visitExpr(XMRRefOp op);
2005 LogicalResult visitExpr(XMRDerefOp op);
2008 LogicalResult visitExpr(TimeOp op);
2009 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2012 LogicalResult lowerVerificationStatement(
2013 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2014 Value enable, StringAttr messageAttr, ValueRange operands,
2015 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2016 LogicalResult lowerVerificationStatementToCore(
2017 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2018 Value enable, StringAttr nameAttr, EventControl eventControl);
2020 LogicalResult visitStmt(SkipOp op);
2022 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2023 LogicalResult visitStmt(ConnectOp op);
2024 LogicalResult visitStmt(MatchingConnectOp op);
2025 LogicalResult visitStmt(ForceOp op);
2027 std::optional<Value> getLoweredFmtOperand(Value operand);
2028 LogicalResult loweredFmtOperands(ValueRange operands,
2029 SmallVectorImpl<Value> &loweredOperands);
2030 FailureOr<Value> lowerSimFormatString(StringRef originalFormatString,
2031 ValueRange operands);
2032 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2036 LogicalResult lowerStatementWithFd(
2037 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2038 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2042 LogicalResult visitPrintfLike(T op,
2043 const FileDescriptorInfo &fileDescriptorInfo,
2044 bool usePrintfCond);
2045 LogicalResult visitStmt(PrintFOp op);
2046 LogicalResult visitStmt(FPrintFOp op);
2047 LogicalResult visitStmt(FFlushOp op);
2048 LogicalResult visitStmt(StopOp op);
2049 LogicalResult visitStmt(AssertOp op);
2050 LogicalResult visitStmt(AssumeOp op);
2051 LogicalResult visitStmt(CoverOp op);
2052 LogicalResult visitStmt(AttachOp op);
2053 LogicalResult visitStmt(RefForceOp op);
2054 LogicalResult visitStmt(RefForceInitialOp op);
2055 LogicalResult visitStmt(RefReleaseOp op);
2056 LogicalResult visitStmt(RefReleaseInitialOp op);
2057 LogicalResult visitStmt(BindOp op);
2059 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2060 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2061 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2063 LogicalResult fixupLTLOps();
2066 return circuitState.lowerType(type, builder.getLoc());
2074 CircuitLoweringState &circuitState;
2077 ImplicitLocOpBuilder builder;
2082 DenseMap<Value, Value> valueMapping;
2086 DenseMap<Value, Value> fromClockMapping;
2090 DenseMap<Attribute, Value> hwConstantMap;
2091 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2095 DenseMap<unsigned, Value> hwConstantXMap;
2096 DenseMap<Type, Value> hwConstantZMap;
2102 DenseMap<Value, Value> readInOutCreated;
2106 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2107 sv::ResetType, sv::EventControl, Value>;
2131 DenseSet<Operation *> maybeUnusedValues;
2133 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2134 void maybeUnused(Value value) {
2135 if (
auto *op = value.getDefiningOp())
2147 SetVector<Operation *> ltlOpFixupWorklist;
2152 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2154 void addToWorklist(Block &block) {
2155 worklist.push_back({block.begin(), block.end()});
2157 void addToWorklist(Region ®ion) {
2158 for (
auto &block :
llvm::reverse(region))
2159 addToWorklist(block);
2170LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2171 OpBuilder b(&getContext());
2172 fileOp->walk([&](Operation *op) {
2173 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2174 b.setInsertionPointAfter(bindOp);
2175 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2183FIRRTLModuleLowering::lowerBody(Operation *op,
2185 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2187 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2189 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2191 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2192 return lowerFileBody(fileOp);
2197LogicalResult FIRRTLLowering::run() {
2200 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2201 if (failed(setLowering(arg, arg)))
2208 addToWorklist(theModule.getBody());
2209 SmallVector<Operation *, 16> opsToRemove;
2211 while (!worklist.empty()) {
2212 auto &[opsIt, opsEnd] = worklist.back();
2213 if (opsIt == opsEnd) {
2214 worklist.pop_back();
2217 Operation *op = &*opsIt++;
2219 builder.setInsertionPoint(op);
2220 builder.setLoc(op->getLoc());
2221 auto done = succeeded(dispatchVisitor(op));
2222 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2224 opsToRemove.push_back(op);
2226 switch (handleUnloweredOp(op)) {
2227 case AlreadyLowered:
2230 opsToRemove.push_back(op);
2232 case LoweringFailure:
2244 for (
auto &[backedge, value] : backedges) {
2245 SmallVector<Location> driverLocs;
2251 if (backedge == value) {
2252 Location edgeLoc = backedge.getLoc();
2253 if (driverLocs.empty()) {
2254 mlir::emitError(edgeLoc,
"sink does not have a driver");
2256 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2257 for (
auto loc : driverLocs)
2258 diag.attachNote(loc) <<
"through driver here";
2264 auto *it = backedges.find(value);
2265 if (it == backedges.end())
2268 driverLocs.push_back(value.getLoc());
2271 if (
auto *defOp = backedge.getDefiningOp())
2272 maybeUnusedValues.erase(defOp);
2273 backedge.replaceAllUsesWith(value);
2281 while (!opsToRemove.empty()) {
2282 auto *op = opsToRemove.pop_back_val();
2287 for (
auto result : op->getResults()) {
2291 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2293 builder.getIntegerType(0), 0);
2294 maybeUnusedValues.insert(zeroI0);
2296 result.replaceAllUsesWith(zeroI0);
2299 if (!op->use_empty()) {
2300 auto d = op->emitOpError(
2301 "still has uses; should remove ops in reverse order of visitation");
2302 SmallPtrSet<Operation *, 2> visited;
2303 for (
auto *user : op->getUsers())
2304 if (visited.insert(user).second)
2306 <<
"used by " << user->
getName() <<
" op";
2309 maybeUnusedValues.erase(op);
2315 SmallVector<Operation *> worklist(maybeUnusedValues.begin(),
2316 maybeUnusedValues.end());
2317 while (!worklist.empty()) {
2318 auto *op = worklist.pop_back_val();
2319 maybeUnusedValues.erase(op);
2320 if (!isOpTriviallyDead(op))
2322 for (
auto operand : op->getOperands())
2323 if (auto *defOp = operand.getDefiningOp())
2324 if (maybeUnusedValues.insert(defOp).second)
2325 worklist.push_back(defOp);
2331 if (failed(fixupLTLOps()))
2342Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2343 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2345 auto &entry = hwConstantMap[attr];
2349 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2350 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2356Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2357 auto attr = builder.getIntegerAttr(
2358 builder.getIntegerType(value.getBitWidth()), value);
2360 auto &entry = hwConstantMap[attr];
2364 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2371Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2374 if (hw::type_isa<IntegerType>(type))
2375 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2377 auto cache = hwAggregateConstantMap.lookup({value, type});
2382 SmallVector<Attribute> values;
2383 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2385 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2386 subType = array.getElementType();
2387 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2388 subType = structType.getElements()[e.index()].type;
2390 assert(
false &&
"type must be either array or struct");
2392 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2396 if (hw::type_isa<hw::ArrayType>(type))
2397 std::reverse(values.begin(), values.end());
2399 auto &entry = hwAggregateConstantMap[{value, type}];
2400 entry = builder.getArrayAttr(values);
2408 const std::function<LogicalResult()> &fn) {
2409 assert(failedOperand &&
"Should be called on the failed operand");
2417Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2419 auto &entry = hwConstantXMap[numBits];
2423 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2424 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2425 entryBuilder.getIntegerType(numBits));
2429Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2430 auto &entry = hwConstantZMap[type];
2432 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2433 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2442Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2444 if (
auto lowering = valueMapping.lookup(value)) {
2445 assert(!isa<FIRRTLType>(lowering.getType()) &&
2446 "Lowered value should be a non-FIRRTL value");
2455Value FIRRTLLowering::getLoweredValue(Value value) {
2456 auto result = getPossiblyInoutLoweredValue(value);
2462 if (isa<hw::InOutType>(result.getType()))
2463 return getReadValue(result);
2469Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2470 auto result = getLoweredValue(value);
2474 if (hw::type_isa<seq::ClockType>(result.getType()))
2475 return getNonClockValue(result);
2483Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2486 bool allowTruncate) {
2487 SmallVector<Value> resultBuffer;
2492 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2493 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2494 auto resultType = builder.getIntegerType(destWidth);
2496 if (srcWidth == destWidth)
2499 if (srcWidth > destWidth) {
2503 builder.emitError(
"operand should not be a truncation");
2507 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2508 return comb::createOrFoldSExt(builder, value, resultType);
2509 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2517 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2518 .Case<FVectorType>([&](
auto srcVectorType) {
2519 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2520 unsigned size = resultBuffer.size();
2521 unsigned indexWidth =
2523 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2524 destVectorType.getNumElements());
2526 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2528 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2529 destVectorType.getElementType())))
2532 SmallVector<Value> temp(resultBuffer.begin() + size,
2533 resultBuffer.end());
2535 resultBuffer.resize(size);
2536 resultBuffer.push_back(array);
2539 .Case<BundleType>([&](BundleType srcStructType) {
2540 auto destStructType = firrtl::type_cast<BundleType>(destType);
2541 unsigned size = resultBuffer.size();
2544 if (destStructType.getNumElements() != srcStructType.getNumElements())
2547 for (
auto elem :
llvm::enumerate(destStructType)) {
2548 auto structExtract =
2550 if (failed(recurse(structExtract,
2551 srcStructType.getElementType(elem.index()),
2552 destStructType.getElementType(elem.index()))))
2555 SmallVector<Value> temp(resultBuffer.begin() + size,
2556 resultBuffer.end());
2559 resultBuffer.resize(size);
2560 resultBuffer.push_back(newStruct);
2563 .Case<IntType>([&](
auto) {
2564 if (
auto result = cast(src, srcType, destType)) {
2565 resultBuffer.push_back(result);
2570 .Default([&](
auto) {
return failure(); });
2573 if (failed(recurse(array, sourceType, destType)))
2576 assert(resultBuffer.size() == 1 &&
2577 "resultBuffer must only contain a result array if `success` is true");
2578 return resultBuffer[0];
2586Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2587 auto srcType = cast<FIRRTLBaseType>(src.getType());
2588 auto dstType = cast<FIRRTLBaseType>(target);
2589 auto loweredSrc = getLoweredValue(src);
2592 auto dstWidth = dstType.getBitWidthOrSentinel();
2608 return getOrCreateIntConstant(dstWidth, 0);
2611 auto loweredSrcType = loweredSrc.getType();
2612 auto loweredDstType =
lowerType(dstType);
2615 if (loweredSrcType == loweredDstType)
2619 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2621 if (loweredSrcType != loweredDstType &&
2622 (isa<hw::TypeAliasType>(loweredSrcType) ||
2623 isa<hw::TypeAliasType>(loweredDstType))) {
2624 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2629 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2630 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2633 if (isa<seq::ClockType>(loweredSrcType)) {
2634 builder.emitError(
"cannot use clock type as an integer");
2638 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2639 if (!intSourceType) {
2640 builder.emitError(
"operand of type ")
2641 << loweredSrcType <<
" cannot be used as an integer";
2645 auto loweredSrcWidth = intSourceType.getWidth();
2646 if (loweredSrcWidth ==
unsigned(dstWidth))
2649 if (loweredSrcWidth >
unsigned(dstWidth)) {
2650 builder.emitError(
"operand should not be a truncation");
2655 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2656 if (type_cast<IntType>(valueFIRType).isSigned())
2657 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2659 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2668Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2669 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2670 type_isa<FIRRTLBaseType>(destType) &&
2671 "input/output value should be FIRRTL");
2674 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2675 if (destWidth == -1)
2678 auto result = getLoweredValue(value);
2690 return getOrCreateIntConstant(destWidth, 0);
2694 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2696 if (destType == value.getType())
2699 return getExtOrTruncAggregateValue(
2700 result, type_cast<FIRRTLBaseType>(value.getType()),
2701 type_cast<FIRRTLBaseType>(destType),
2705 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2706 if (srcWidth ==
unsigned(destWidth))
2712 if (srcWidth >
unsigned(destWidth)) {
2713 auto resultType = builder.getIntegerType(destWidth);
2717 auto resultType = builder.getIntegerType(destWidth);
2721 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2722 if (type_cast<IntType>(valueFIRType).isSigned())
2723 return comb::createOrFoldSExt(builder, result, resultType);
2725 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2739std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2741 if (type_isa<FStringType>(operand.getType())) {
2742 if (isa<TimeOp>(operand.getDefiningOp()))
2743 return sv::TimeOp::create(builder);
2744 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2748 auto loweredValue = getLoweredValue(operand);
2749 if (!loweredValue) {
2753 loweredValue = getOrCreateIntConstant(1, 0);
2758 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2759 if (intTy.isSigned())
2760 loweredValue = sv::SystemFunctionOp::create(
2761 builder, loweredValue.getType(),
"signed", loweredValue);
2763 return loweredValue;
2767FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2768 SmallVectorImpl<Value> &loweredOperands) {
2769 for (
auto operand : operands) {
2770 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2775 loweredOperands.push_back(*loweredValue);
2781FIRRTLLowering::lowerSimFormatString(StringRef originalFormatString,
2782 ValueRange operands) {
2783 SmallVector<Value> fragments;
2785 auto emitLiteral = [&](StringRef text) {
2787 fragments.push_back(sim::FormatLiteralOp::create(builder, text));
2790 auto emitIntFormat = [&](Value operand,
char specifier,
2791 IntegerAttr widthAttr) -> FailureOr<Value> {
2793 if (type_isa<ClockType>(operand.getType()))
2794 loweredValue = getLoweredNonClockValue(operand);
2796 loweredValue = getLoweredValue(operand);
2797 if (!loweredValue) {
2800 loweredValue = getOrCreateIntConstant(1, 0);
2803 if (!mlir::isa<IntegerType>(loweredValue.getType())) {
2804 emitError(builder.getLoc(),
"lower-to-core requires integer printf "
2806 << specifier <<
"'";
2810 switch (specifier) {
2812 return sim::FormatBinOp::create(builder, loweredValue,
2813 builder.getBoolAttr(
false),
2814 builder.getI8IntegerAttr(
'0'), widthAttr)
2817 UnitAttr signedAttr;
2818 if (
auto intTy = dyn_cast<IntType>(operand.getType());
2819 intTy && intTy.isSigned())
2820 signedAttr = builder.getUnitAttr();
2821 return sim::FormatDecOp::create(
2822 builder, loweredValue, builder.getBoolAttr(
false),
2823 builder.getI8IntegerAttr(
' '), widthAttr, signedAttr)
2827 return sim::FormatHexOp::create(builder, loweredValue,
2828 builder.getBoolAttr(
false),
2829 builder.getBoolAttr(
false),
2830 builder.getI8IntegerAttr(
'0'), widthAttr)
2833 return sim::FormatCharOp::create(builder, loweredValue).getResult();
2835 llvm_unreachable(
"unsupported FIRRTL format specifier");
2839 SmallString<32> literal;
2840 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
2841 char c = originalFormatString[i];
2844 emitLiteral(literal);
2847 SmallString<6> width;
2848 c = originalFormatString[++i];
2851 c = originalFormatString[++i];
2854 IntegerAttr widthAttr;
2855 if (!width.empty()) {
2856 unsigned widthValue;
2857 if (StringRef(width).getAsInteger(10, widthValue)) {
2858 emitError(builder.getLoc(),
"invalid FIRRTL printf width");
2861 widthAttr = builder.getI32IntegerAttr(widthValue);
2865 if (!width.empty()) {
2866 emitError(builder.getLoc(),
2867 "literal percents ('%%') may not specify a width");
2870 literal.push_back(
'%');
2874 if (operands.size() <= subIdx) {
2875 emitError(builder.getLoc(),
"not enough operands for printf format");
2879 if (c ==
'c' && widthAttr) {
2880 emitError(builder.getLoc(),
"ASCII character format specifiers ('%c') "
2881 "may not specify a width");
2890 auto fragment = emitIntFormat(operands[subIdx++], c, widthAttr);
2891 if (failed(fragment))
2893 fragments.push_back(*fragment);
2897 emitError(builder.getLoc(),
"unknown printf substitution '%")
2898 << width << c <<
"'";
2904 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
2905 literal.push_back(c);
2909 emitLiteral(literal);
2912 if (operands.size() <= subIdx) {
2913 emitError(builder.getLoc(),
"not enough operands for printf format");
2917 auto substitution = operands[subIdx++];
2918 if (!type_isa<FStringType>(substitution.getType())) {
2919 emitError(builder.getLoc(),
"expected fstring operand for '{{}}' "
2925 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
2926 .template Case<HierarchicalModuleNameOp>([&](
auto) {
2927 fragments.push_back(sim::FormatHierPathOp::create(
2931 .
template Case<TimeOp>([&](
auto) {
2932 fragments.push_back(sim::FormatCurrentTimeOp::create(builder));
2935 .Default([&](
auto) {
2936 emitError(builder.getLoc(),
"has a substitution with "
2939 .attachNote(substitution.getLoc())
2940 <<
"op with an unimplemented lowering is here";
2949 literal.push_back(c);
2954 emitLiteral(literal);
2955 if (fragments.empty())
2956 return sim::FormatLiteralOp::create(builder,
"").getResult();
2957 if (fragments.size() == 1)
2958 return fragments.front();
2959 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
2962LogicalResult FIRRTLLowering::lowerStatementWithFd(
2963 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2964 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2966 bool failed =
false;
2967 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2968 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2969 addToAlwaysBlock(clock, [&]() {
2972 circuitState.usedPrintf =
true;
2974 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2977 Value ifCond = cond;
2978 if (usePrintfCond) {
2980 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2981 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2984 addIfProceduralBlock(ifCond, [&]() {
2988 if (fileDescriptor.isDefaultFd()) {
2990 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2993 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2994 if (llvm::failed(fdOrError)) {
3000 failed = llvm::failed(fn(fd));
3004 return failure(failed);
3008FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3009 circuitState.usedFileDescriptorLib =
true;
3010 circuitState.addFragment(
3011 theModule, sv::getFileDescriptorFragmentRef(builder.getContext()));
3014 if (
info.isSubstitutionRequired()) {
3015 SmallVector<Value> fileNameOperands;
3016 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3019 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3024 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3028 return sv::createProceduralFileDescriptorGetterCall(builder, builder.getLoc(),
3038LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3039 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3040 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3041 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3045 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3048 if (srcWidth != -1) {
3050 assert((srcWidth != 0) &&
3051 "Lowering produced value for zero width source");
3053 assert((srcWidth == 0) &&
3054 "Lowering produced null value but source wasn't zero width");
3058 assert(result &&
"Lowering of foreign type produced null value");
3061 auto &slot = valueMapping[orig];
3062 assert(!slot &&
"value lowered multiple times");
3069LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3073 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3074 auto &entry = hwConstantMap[cst.getValueAttr()];
3085 cst->moveBefore(&theModule.getBodyBlock()->front());
3089 return setLowering(orig, result);
3094template <
typename ResultOpType,
typename... CtorArgTypes>
3095LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3096 CtorArgTypes... args) {
3097 auto result = builder.createOrFold<ResultOpType>(args...);
3098 if (
auto *op = result.getDefiningOp())
3100 return setPossiblyFoldedLowering(orig->getResult(0), result);
3107template <
typename ResultOpType,
typename... CtorArgTypes>
3108LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3109 CtorArgTypes... args) {
3110 auto result = builder.createOrFold<ResultOpType>(args...);
3111 if (
auto *op = result.getDefiningOp())
3112 ltlOpFixupWorklist.insert(op);
3113 return setPossiblyFoldedLowering(orig->getResult(0), result);
3122Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3123 auto backedge = backedgeBuilder.
get(type, loc);
3124 backedges.insert({backedge, backedge});
3132Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3133 auto backedge = createBackedge(orig.getLoc(), type);
3134 (void)setLowering(orig, backedge);
3140bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3141 auto backedgeIt = backedges.find(dest);
3142 if (backedgeIt == backedges.end())
3144 backedgeIt->second = src;
3152void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3153 const std::function<
void(
void)> &fn, Region ®ion) {
3157 auto oldIP = builder.saveInsertionPoint();
3159 builder.setInsertionPointToEnd(®ion.front());
3161 builder.restoreInsertionPoint(oldIP);
3165Value FIRRTLLowering::getReadValue(Value v) {
3166 Value result = readInOutCreated.lookup(v);
3172 auto oldIP = builder.saveInsertionPoint();
3173 if (
auto *vOp = v.getDefiningOp()) {
3174 builder.setInsertionPointAfter(vOp);
3178 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3183 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3184 result = getReadValue(arrayIndexInout.getInput());
3186 arrayIndexInout.getIndex());
3191 builder.restoreInsertionPoint(oldIP);
3192 readInOutCreated.insert({v, result});
3196Value FIRRTLLowering::getNonClockValue(Value v) {
3197 auto it = fromClockMapping.try_emplace(v, Value{});
3199 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3200 builder.setInsertionPointAfterValue(v);
3201 it.first->second = seq::FromClockOp::create(builder, v);
3203 return it.first->second;
3206void FIRRTLLowering::addToAlwaysBlock(
3207 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3208 sv::EventControl resetEdge, Value reset,
3209 const std::function<
void(
void)> &body,
3210 const std::function<
void(
void)> &resetBody) {
3211 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3212 resetStyle, resetEdge, reset};
3213 sv::AlwaysOp alwaysOp;
3214 sv::IfOp insideIfOp;
3215 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3219 assert(resetStyle != sv::ResetType::NoReset);
3232 auto createIfOp = [&]() {
3235 insideIfOp = sv::IfOp::create(
3236 builder, reset, [] {}, [] {});
3238 if (resetStyle == sv::ResetType::AsyncReset) {
3239 sv::EventControl events[] = {clockEdge, resetEdge};
3240 Value clocks[] = {clock, reset};
3242 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3243 if (resetEdge == sv::EventControl::AtNegEdge)
3244 llvm_unreachable(
"negative edge for reset is not expected");
3248 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3252 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3253 insideIfOp =
nullptr;
3255 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3259 assert(insideIfOp &&
"reset body must be initialized before");
3260 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3261 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3263 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3269 alwaysOp->moveBefore(builder.getInsertionBlock(),
3270 builder.getInsertionPoint());
3273LogicalResult FIRRTLLowering::emitGuards(Location loc,
3274 ArrayRef<Attribute> guards,
3275 std::function<
void(
void)>
emit) {
3276 if (guards.empty()) {
3280 auto guard = dyn_cast<StringAttr>(guards[0]);
3282 return mlir::emitError(loc,
3283 "elements in `guards` array must be `StringAttr`");
3286 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3287 LogicalResult result = LogicalResult::failure();
3288 addToIfDefBlock(guard.getValue(), [&]() {
3289 result = emitGuards(loc, guards.drop_front(), emit);
3294void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3295 std::function<
void(
void)> thenCtor,
3296 std::function<
void(
void)> elseCtor) {
3297 auto condAttr = builder.getStringAttr(cond);
3298 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3300 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3301 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3306 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3308 ifdefBlocks[{builder.getBlock(), condAttr}] =
3309 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3313void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3314 auto op = initialBlocks.lookup(builder.getBlock());
3316 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3321 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3323 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3327void FIRRTLLowering::addIfProceduralBlock(Value cond,
3328 std::function<
void(
void)> thenCtor,
3329 std::function<
void(
void)> elseCtor) {
3332 auto insertIt = builder.getInsertionPoint();
3333 if (insertIt != builder.getBlock()->begin())
3334 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3335 if (ifOp.getCond() == cond) {
3336 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3337 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3342 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3354FIRRTLLowering::UnloweredOpResult
3355FIRRTLLowering::handleUnloweredOp(Operation *op) {
3357 if (!op->getRegions().empty() &&
3358 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3359 op->emitOpError(
"must explicitly handle its regions");
3360 return LoweringFailure;
3367 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3369 for (
auto ®ion : op->getRegions())
3370 addToWorklist(region);
3371 for (
auto &operand : op->getOpOperands())
3372 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3373 operand.set(lowered);
3374 for (
auto result : op->getResults())
3375 (void)setLowering(result, result);
3376 return AlreadyLowered;
3388 if (op->getNumResults() == 1) {
3389 auto resultType = op->getResult(0).getType();
3390 if (type_isa<FIRRTLBaseType>(resultType) &&
3392 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3394 (void)setLowering(op->getResult(0), Value());
3398 op->emitOpError(
"LowerToHW couldn't handle this operation");
3399 return LoweringFailure;
3402LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3405 return setLowering(op, Value());
3407 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3410LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3412 if (isa<ClockType>(op.getType())) {
3413 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3414 :
seq::ClockConst::Low);
3416 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3418 return setLowering(op, cst);
3421FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3422 auto iIdx = getOrCreateIntConstant(
3424 firrtl::type_cast<FVectorType>(op.getInput().getType())
3431 if (isa<sv::InOutType>(input.getType()))
3432 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3435 if (
auto *definingOp = result.getDefiningOp())
3440FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3441 Value valueIdx = getLoweredAndExtOrTruncValue(
3443 UIntType::get(op->getContext(),
3445 firrtl::type_cast<FVectorType>(op.getInput().getType())
3446 .getNumElements())));
3448 op->emitError() <<
"input lowering failed";
3455 if (isa<sv::InOutType>(input.getType()))
3456 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3458 result = createArrayIndexing(input, valueIdx);
3459 if (
auto *definingOp = result.getDefiningOp())
3464FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3465 auto resultType =
lowerType(op->getResult(0).getType());
3466 if (!resultType || !input) {
3467 op->emitError() <<
"subfield type lowering failed";
3473 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3474 .getElementName(op.getFieldIndex());
3476 if (isa<sv::InOutType>(input.getType()))
3477 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3480 if (
auto *definingOp = result.getDefiningOp())
3485LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3487 return setLowering(op, Value());
3489 auto input = getPossiblyInoutLoweredValue(op.getInput());
3491 return op.emitError() <<
"input lowering failed";
3493 auto result = lowerSubindex(op, input);
3496 return setLowering(op, *result);
3499LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3501 return setLowering(op, Value());
3503 auto input = getPossiblyInoutLoweredValue(op.getInput());
3505 return op.emitError() <<
"input lowering failed";
3507 auto result = lowerSubaccess(op, input);
3510 return setLowering(op, *result);
3513LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3516 if (getLoweredValue(op) || !op.getInput())
3520 return setLowering(op, Value());
3522 auto input = getPossiblyInoutLoweredValue(op.getInput());
3524 return op.emitError() <<
"input lowering failed";
3526 auto result = lowerSubfield(op, input);
3529 return setLowering(op, *result);
3532LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3533 auto resultType =
lowerType(op.getResult().getType());
3534 SmallVector<Value> operands;
3536 for (
auto oper :
llvm::reverse(op.getOperands())) {
3537 auto val = getLoweredValue(oper);
3540 operands.push_back(val);
3542 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3545LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3546 auto resultType =
lowerType(op.getResult().getType());
3547 SmallVector<Value> operands;
3548 for (
auto oper : op.getOperands()) {
3549 auto val = getLoweredValue(oper);
3552 operands.push_back(val);
3554 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3557LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3560 return setLowering(op, Value());
3562 auto input = getLoweredValue(op.getInput());
3563 auto tagName = op.getFieldNameAttr();
3564 auto oldType = op.getType().base();
3566 auto element = *oldType.getElement(op.getFieldNameAttr());
3568 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3569 auto tagType = structType.getFieldType(
"tag");
3570 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3571 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3573 auto bodyType = structType.getFieldType(
"body");
3574 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3575 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3576 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3578 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3579 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3582LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3583 auto resultType =
lowerType(op.getResult().getType());
3585 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3587 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3588 cast<ArrayAttr>(attr));
3591LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3593 auto tagName = op.getFieldNameAttr();
3594 auto lhs = getLoweredValue(op.getInput());
3595 if (isa<hw::StructType>(lhs.getType()))
3598 auto index = op.getFieldIndex();
3599 auto enumType = op.getInput().getType().base();
3600 auto tagValue = enumType.getElementValueAttr(index);
3601 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3602 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3603 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3604 loweredTagValue, tagName);
3606 Type resultType = builder.getIntegerType(1);
3607 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3611LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3614 return setLowering(op, Value());
3616 auto tagName = op.getFieldNameAttr();
3617 auto input = getLoweredValue(op.getInput());
3619 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3622LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3625 return setLowering(op, Value());
3627 auto input = getLoweredValue(op.getInput());
3633 if (isa<hw::StructType>(input.getType())) {
3634 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3639 return setLowering(op, input);
3646LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3647 auto origResultType = op.getResult().getType();
3651 if (!type_isa<FIRRTLType>(origResultType)) {
3652 createBackedge(op.getResult(), origResultType);
3656 auto resultType =
lowerType(origResultType);
3660 if (resultType.isInteger(0)) {
3661 if (op.getInnerSym())
3662 return op.emitError(
"zero width wire is referenced by name [")
3663 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3664 return setLowering(op.getResult(), Value());
3668 auto innerSym = lowerInnerSymbol(op);
3669 auto name = op.getNameAttr();
3672 auto wire = hw::WireOp::create(
3673 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3675 if (
auto svAttrs = sv::getSVAttributes(op))
3676 sv::setSVAttributes(wire, svAttrs);
3678 return setLowering(op.getResult(), wire);
3681LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3682 auto resultTy =
lowerType(op.getType());
3685 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3687 SmallVector<Value, 4> operands;
3688 operands.reserve(op.getSubstitutions().size());
3689 for (
auto operand : op.getSubstitutions()) {
3690 auto lowered = getLoweredValue(operand);
3693 operands.push_back(lowered);
3696 ArrayAttr symbols = op.getSymbolsAttr();
3698 symbols = ArrayAttr::get(op.getContext(), {});
3700 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3704LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3705 auto operand = getLoweredValue(op.getInput());
3707 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3708 if (op.getInnerSym())
3709 return op.emitError(
"zero width node is referenced by name [")
3710 << *op.getInnerSym()
3711 <<
"] (e.g. in an XMR) but must be "
3713 return setLowering(op.getResult(), Value());
3719 auto name = op.getNameAttr();
3720 auto innerSym = lowerInnerSymbol(op);
3723 operand = hw::WireOp::create(builder, operand, name, innerSym);
3726 if (
auto svAttrs = sv::getSVAttributes(op)) {
3728 operand = hw::WireOp::create(builder, operand, name);
3729 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3732 return setLowering(op.getResult(), operand);
3735LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3736 auto resultType =
lowerType(op.getResult().getType());
3739 if (resultType.isInteger(0))
3740 return setLowering(op.getResult(), Value());
3742 Value clockVal = getLoweredValue(op.getClockVal());
3747 auto innerSym = lowerInnerSymbol(op);
3748 Backedge inputEdge = backedgeBuilder.
get(resultType);
3749 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3750 op.getNameAttr(), innerSym);
3753 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3754 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3755 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3756 reg->setAttr(
"firrtl.random_init_start", randomStart);
3757 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3758 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3761 if (
auto svAttrs = sv::getSVAttributes(op))
3762 sv::setSVAttributes(reg, svAttrs);
3765 (void)setLowering(op.getResult(),
reg);
3769LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3770 auto resultType =
lowerType(op.getResult().getType());
3773 if (resultType.isInteger(0))
3774 return setLowering(op.getResult(), Value());
3776 Value clockVal = getLoweredValue(op.getClockVal());
3777 Value resetSignal = getLoweredValue(op.getResetSignal());
3779 Value resetValue = getLoweredAndExtOrTruncValue(
3780 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3782 if (!clockVal || !resetSignal || !resetValue)
3786 auto innerSym = lowerInnerSymbol(op);
3787 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3788 Backedge inputEdge = backedgeBuilder.
get(resultType);
3790 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3791 resetSignal, resetValue, innerSym, isAsync);
3794 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3795 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3796 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3797 reg->setAttr(
"firrtl.random_init_start", randomStart);
3798 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3799 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3802 if (
auto svAttrs = sv::getSVAttributes(op))
3803 sv::setSVAttributes(reg, svAttrs);
3806 (void)setLowering(op.getResult(),
reg);
3811LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3814 if (type_isa<BundleType>(op.getDataType()))
3815 return op.emitOpError(
3816 "should have already been lowered from a ground type to an aggregate "
3817 "type using the LowerTypes pass. Use "
3818 "'firtool --lower-types' or 'circt-opt "
3819 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3825 auto memType = seq::FirMemType::get(
3828 : std::optional<uint32_t>());
3830 seq::FirMemInitAttr memInit;
3831 if (
auto init = op.getInitAttr())
3832 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3833 init.getIsBinary(), init.getIsInline());
3835 auto memDecl = seq::FirMemOp::create(
3838 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3841 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3843 if (!file.isDirectory())
3844 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3845 file.getDirectory());
3846 memDecl.setOutputFileAttr(dir);
3852 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3854 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3857 (void)setLowering(a, value);
3863 auto addInput = [&](StringRef field, Value backedge) {
3865 if (cast<FIRRTLBaseType>(a.getType())
3867 .getBitWidthOrSentinel() > 0)
3868 (void)setLowering(a, backedge);
3874 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3878 Value backedge, portValue;
3880 portValue = getOrCreateXConstant(1);
3882 auto portType = IntegerType::get(op.getContext(), width);
3883 backedge = portValue = createBackedge(builder.getLoc(), portType);
3885 addInput(field, backedge);
3889 auto addClock = [&](StringRef field) -> Value {
3890 Type clockTy = seq::ClockType::get(op.getContext());
3891 Value portValue = createBackedge(builder.getLoc(), clockTy);
3892 addInput(field, portValue);
3896 auto memportKind = op.getPortKind(i);
3897 if (memportKind == MemOp::PortKind::Read) {
3898 auto addr = addInputPort(
"addr", op.getAddrBits());
3899 auto en = addInputPort(
"en", 1);
3900 auto clk = addClock(
"clk");
3901 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3903 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3904 auto addr = addInputPort(
"addr", op.getAddrBits());
3905 auto en = addInputPort(
"en", 1);
3906 auto clk = addClock(
"clk");
3909 auto mode = addInputPort(
"wmode", 1);
3911 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3918 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3922 auto addr = addInputPort(
"addr", op.getAddrBits());
3925 auto en = addInputPort(
"en", 1);
3929 auto clk = addClock(
"clk");
3943FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
3944 Operation *instanceOp,
3945 SmallVectorImpl<Value> &inputOperands) {
3947 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3948 auto &port = portInfo[portIndex];
3951 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
3956 if (portType.isInteger(0))
3960 if (port.isOutput())
3963 auto portResult = instanceOp->getResult(portIndex);
3964 assert(portResult &&
"invalid IR, couldn't find port");
3968 if (port.isInput()) {
3969 inputOperands.push_back(createBackedge(portResult, portType));
3975 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3976 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3978 auto loweredResult = getPossiblyInoutLoweredValue(source);
3979 inputOperands.push_back(loweredResult);
3980 (void)setLowering(portResult, loweredResult);
3989 "." + port.getName().str() +
".wire");
3993 (void)setLowering(portResult, wire);
3994 inputOperands.push_back(wire);
4000LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4001 Operation *oldModule =
4002 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4004 auto *newModule = circuitState.getNewModule(oldModule);
4006 oldInstance->emitOpError(
"could not find module [")
4007 << oldInstance.getModuleName() <<
"] referenced by instance";
4013 ArrayAttr parameters;
4014 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4019 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4023 SmallVector<Value, 8> operands;
4024 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4031 auto innerSym = oldInstance.getInnerSymAttr();
4032 if (oldInstance.getLowerToBind()) {
4035 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4038 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4039 innerSym.getSymName());
4042 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4043 bindOp->setAttr(
"output_file", outputFile);
4046 circuitState.addBind(bindOp);
4051 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4052 operands, parameters, innerSym);
4054 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4055 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4057 if (newInstance.getInnerSymAttr())
4058 if (
auto forceName = circuitState.instanceForceNames.lookup(
4059 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4060 newInstance.getInnerNameAttr()}))
4061 newInstance->setAttr(
"hw.verilogName", forceName);
4065 unsigned resultNo = 0;
4066 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4067 auto &port = portInfo[portIndex];
4071 Value resultVal = newInstance.getResult(resultNo);
4073 auto oldPortResult = oldInstance.getResult(portIndex);
4074 (void)setLowering(oldPortResult, resultVal);
4080LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4081 if (oldInstanceChoice.getInnerSymAttr()) {
4082 oldInstanceChoice->emitOpError(
4083 "instance choice with inner sym cannot be lowered");
4088 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4090 return oldInstanceChoice->emitOpError(
4091 "must have instance_macro attribute set before "
4095 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4096 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4099 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4100 auto *defaultModuleNode =
4101 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4103 Operation *defaultModule = defaultModuleNode->getModule();
4107 SmallVector<PortInfo, 8> portInfo =
4108 cast<FModuleLike>(defaultModule).getPorts();
4111 SmallVector<Value, 8> inputOperands;
4113 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4117 SmallVector<sv::WireOp, 8> outputWires;
4118 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4119 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4120 auto &port = portInfo[portIndex];
4124 if (!portType || portType.isInteger(0))
4127 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4128 outputWires.push_back(wire);
4129 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4133 auto optionName = oldInstanceChoice.getOptionNameAttr();
4136 auto createInstanceAndAssign = [&](Operation *oldMod,
4137 StringRef suffix) -> hw::InstanceOp {
4138 auto *newMod = circuitState.getNewModule(oldMod);
4140 ArrayAttr parameters;
4141 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4145 SmallString<64> instName;
4146 instName = oldInstanceChoice.getInstanceName();
4147 if (!suffix.empty()) {
4153 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4154 inputOperands, parameters,
nullptr);
4160 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4167 SmallVector<StringAttr> macroNames;
4168 SmallVector<Operation *> altModules;
4169 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4170 altModules.push_back(
4171 circuitState.getInstanceGraph()
4172 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4176 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4177 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4178 if (!optionCaseMacroRef)
4179 return oldInstanceChoice->emitOpError(
4180 "failed to get macro for option case");
4181 macroNames.push_back(optionCaseMacroRef.getAttr());
4185 sv::createNestedIfDefs(
4188 [&](StringRef macro, std::function<
void()> thenCtor,
4189 std::function<
void()> elseCtor) {
4190 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4194 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4195 sv::IfDefOp::create(
4196 builder, oldInstanceChoice.getLoc(), macroNames[i],
4198 SmallString<256> errorMessage;
4199 llvm::raw_svector_ostream os(errorMessage);
4200 os <<
"Multiple instance choice options defined for option '"
4201 << optionName.getValue() <<
"': '"
4202 << macroNames[index].getValue() <<
"' and '"
4203 << macroNames[i].getValue() <<
"'";
4204 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4205 builder.getStringAttr(errorMessage));
4211 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4213 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4215 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4216 builder.getStringAttr(
"{{0}}"),
4217 builder.getArrayAttr({hw::InnerRefAttr::get(
4218 theModule.getNameAttr(),
4219 inst.getInnerSymAttr().getSymName())}));
4223 SmallString<256> errorMessage;
4224 llvm::raw_svector_ostream os(errorMessage);
4225 os <<
"Required instance choice option '" << optionName.getValue()
4226 <<
"' not selected, must define one of: ";
4227 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4228 os <<
"'" << macro.getValue() <<
"'";
4230 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4231 builder.getStringAttr(errorMessage));
4237LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4238 SmallVector<Value> inputs;
4239 SmallVector<Type> types;
4240 for (
auto input : oldOp.getInputs()) {
4241 auto lowered = getLoweredValue(input);
4244 inputs.push_back(lowered);
4245 types.push_back(lowered.getType());
4248 auto newOp = verif::ContractOp::create(builder, types, inputs);
4249 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4250 auto &body = newOp.getBody().emplaceBlock();
4252 for (
auto [newResult, oldResult, oldArg] :
4253 llvm::zip(newOp.getResults(), oldOp.getResults(),
4254 oldOp.getBody().getArguments())) {
4255 if (failed(setLowering(oldResult, newResult)))
4257 if (failed(setLowering(oldArg, newResult)))
4261 body.getOperations().splice(body.end(),
4262 oldOp.getBody().front().getOperations());
4263 addToWorklist(body);
4273LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4274 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4279 return setLowering(op->getResult(0), operand);
4282LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4283 if (isa<ClockType>(op.getInput().getType()))
4284 return setLowering(op->getResult(0),
4285 getLoweredNonClockValue(op.getInput()));
4286 return lowerNoopCast(op);
4289LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4290 if (isa<ClockType>(op.getInput().getType()))
4291 return setLowering(op->getResult(0),
4292 getLoweredNonClockValue(op.getInput()));
4293 return lowerNoopCast(op);
4296LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4297 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4300LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4301 mlir::UnrealizedConversionCastOp op) {
4303 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4306 auto operand = op.getOperand(0);
4307 auto result = op.getResult(0);
4310 if (type_isa<FIRRTLType>(operand.getType()) &&
4311 type_isa<FIRRTLType>(result.getType()))
4312 return lowerNoopCast(op);
4316 if (!type_isa<FIRRTLType>(operand.getType())) {
4317 if (type_isa<FIRRTLType>(result.getType()))
4318 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4324 auto loweredResult = getLoweredValue(operand);
4325 if (!loweredResult) {
4328 if (operand.getType().isSignlessInteger(0)) {
4329 return setLowering(result, Value());
4336 result.replaceAllUsesWith(loweredResult);
4340LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4343 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4344 return setLowering(op, op.getOperand());
4348 auto result = getLoweredValue(op.getOperand());
4354 op.replaceAllUsesWith(result);
4358LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4359 auto operand = getLoweredValue(op.getOperand());
4362 auto resultType =
lowerType(op.getType());
4366 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4369LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4370 auto operand = getLoweredValue(op.getOperand());
4374 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4375 return setLowering(op, getOrCreateIntConstant(1, 0));
4377 return setLowering(op, Value());
4382 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4383 return setLowering(op, operand);
4386 auto zero = getOrCreateIntConstant(1, 0);
4387 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4390LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4391 auto operand = getLoweredValue(op.getInput());
4395 auto allOnes = getOrCreateIntConstant(
4396 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4397 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4400LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4403 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4407 auto resultType =
lowerType(op.getType());
4409 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4410 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4414LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4415 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4418 return setLowering(op, operand);
4421LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4422 auto operand = getLoweredValue(op.getInput());
4425 return setLowering(op, getOrCreateIntConstant(1, 0));
4430 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4434LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4435 auto operand = getLoweredValue(op.getInput());
4438 return setLowering(op, getOrCreateIntConstant(1, 1));
4443 return setLoweringTo<comb::ICmpOp>(
4444 op, ICmpPredicate::eq, operand,
4445 getOrCreateIntConstant(
4446 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4450LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4451 auto operand = getLoweredValue(op.getInput());
4454 return setLowering(op, getOrCreateIntConstant(1, 0));
4460 return setLoweringTo<comb::ICmpOp>(
4461 op, ICmpPredicate::ne, operand,
4462 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4470template <
typename ResultOpType>
4471LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4472 auto resultType = op->getResult(0).getType();
4473 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4474 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4478 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4484template <
typename ResultOpType>
4485LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4486 auto resultType = op->getResult(0).getType();
4487 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4488 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4499 auto intType = builder.getIntegerType(*bitwidth);
4500 auto retType = lhs.getType();
4503 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4504 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4509template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4510LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4512 auto resultType = op->getResult(0).getType();
4513 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4514 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4519 if (type_cast<IntType>(resultType).isSigned())
4520 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4521 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4526LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4527 ICmpPredicate unsignedOp) {
4529 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4530 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4531 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4535 if (cmpType.getWidth() == 0)
4536 cmpType = UIntType::get(builder.getContext(), 1);
4537 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4538 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4543 Type resultType = builder.getIntegerType(1);
4544 return setLoweringTo<comb::ICmpOp>(
4545 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4551template <
typename SignedOp,
typename Un
signedOp>
4552LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4556 auto opType = type_cast<IntType>(op->getResult(0).getType());
4557 if (opType.getWidth() == 0)
4558 return setLowering(op->getResult(0), Value());
4562 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4563 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4568 if (opType.isSigned())
4569 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4571 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4573 if (
auto *definingOp = result.getDefiningOp())
4576 if (resultType == opType)
4577 return setLowering(op->getResult(0), result);
4578 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4581LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4583 if (op.getInputs().empty())
4584 return setLowering(op, Value());
4586 SmallVector<Value> loweredOperands;
4589 for (
auto operand : op.getInputs()) {
4590 auto loweredOperand = getLoweredValue(operand);
4591 if (loweredOperand) {
4592 loweredOperands.push_back(loweredOperand);
4595 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4603 if (loweredOperands.empty())
4604 return setLowering(op, Value());
4607 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4614LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4615 auto input = getLoweredNonClockValue(op.getArg());
4619 if (!isa<IntType>(input.getType())) {
4620 auto srcType = op.getArg().getType();
4622 assert(bitwidth &&
"Unknown width");
4623 auto intType = builder.getIntegerType(*bitwidth);
4624 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4627 return setLoweringTo<comb::ICmpOp>(
4628 op, ICmpPredicate::ceq, input,
4629 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4632LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4633 auto operand = getLoweredValue(op.getInput());
4634 hw::WireOp::create(builder, operand);
4638LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4639 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4640 op.getFormatStringAttr());
4643LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4644 auto type =
lowerType(op.getResult().getType());
4648 auto valueOp = sim::PlusArgsValueOp::create(
4649 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4650 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4652 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4657LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4658 op.emitError(
"SizeOf should have been resolved.");
4662LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4664 if (op.getTestEnable())
4665 testEnable = getLoweredValue(op.getTestEnable());
4666 return setLoweringTo<seq::ClockGateOp>(
4667 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4668 testEnable, hw::InnerSymAttr{});
4671LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4672 auto operand = getLoweredValue(op.getInput());
4673 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4676LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4677 auto operand = getLoweredValue(op.getInput());
4678 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4681LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4682 return setLoweringToLTL<ltl::AndOp>(
4684 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4687LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4688 return setLoweringToLTL<ltl::OrOp>(
4690 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4693LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4694 return setLoweringToLTL<ltl::IntersectOp>(
4696 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4699LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4700 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4701 op.getDelayAttr(), op.getLengthAttr());
4704LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4705 return setLoweringToLTL<ltl::ConcatOp>(
4707 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4710LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4711 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4712 op.getBaseAttr(), op.getMoreAttr());
4715LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4716 return setLoweringToLTL<ltl::GoToRepeatOp>(
4717 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4720LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4721 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4722 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4725LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4726 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4729LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4730 return setLoweringToLTL<ltl::ImplicationOp>(
4732 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4735LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4736 return setLoweringToLTL<ltl::UntilOp>(
4738 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4741LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4742 return setLoweringToLTL<ltl::EventuallyOp>(op,
4743 getLoweredValue(op.getInput()));
4746LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4747 Value
clk = getLoweredNonClockValue(op.getClock());
4748 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4749 op.getDelayAttr(),
clk);
4752LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4753 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4754 ltl::ClockEdge::Pos,
4755 getLoweredNonClockValue(op.getClock()));
4758template <
typename TargetOp,
typename IntrinsicOp>
4759LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4760 auto property = getLoweredValue(op.getProperty());
4761 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4762 TargetOp::create(builder, property, enable, op.getLabelAttr());
4766LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4767 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4770LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4771 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4774LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4775 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4778LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4779 if (!isa<verif::ContractOp>(op->getParentOp()))
4780 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4781 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4784LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4785 if (!isa<verif::ContractOp>(op->getParentOp()))
4786 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4787 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4790LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4791 auto clock = getLoweredNonClockValue(op.getClock());
4792 auto reset = getLoweredValue(op.getReset());
4793 if (!clock || !reset)
4795 auto resetType = op.getReset().getType();
4796 auto uintResetType = dyn_cast<UIntType>(resetType);
4797 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4798 auto isAsync = isa<AsyncResetType>(resetType);
4799 if (!isAsync && !isSync) {
4800 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4801 "requires sync or async reset");
4802 d.attachNote() <<
"reset is of type " << resetType
4803 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4806 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4813LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4814 auto input = getLoweredValue(op.getInput());
4818 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4819 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4822LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4823 auto resultTy =
lowerType(op.getType());
4830 if (type_isa<AnalogType>(op.getType()))
4833 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4836 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4847 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4849 if (!type_isa<IntegerType>(resultTy))
4851 return setLowering(op, constant);
4855 op.emitOpError(
"unsupported type");
4859LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4860 auto input = getLoweredValue(op.getInput());
4863 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4864 if (op.getAmount() == 0)
4865 return setLowering(op, Value());
4866 Type resultType = builder.getIntegerType(op.getAmount());
4867 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4868 inWidth - op.getAmount());
4871LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4872 auto input = getLoweredValue(op.getInput());
4875 if (op.getAmount() == 0)
4877 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4882 if (op.getAmount() == 0)
4883 return setLowering(op, input);
4885 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4886 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4889LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4890 auto input = getLoweredValue(op.getInput());
4895 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4896 auto shiftAmount = op.getAmount();
4897 if (shiftAmount >= inWidth) {
4899 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4900 return setLowering(op, {});
4903 shiftAmount = inWidth - 1;
4906 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4907 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4910LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4911 auto input = getLoweredValue(op.getInput());
4915 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4916 if (inWidth == op.getAmount())
4917 return setLowering(op, Value());
4918 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4919 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4922LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4923 auto cond = getLoweredValue(op.getSel());
4924 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4925 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4926 if (!cond || !ifTrue || !ifFalse)
4929 if (isa<ClockType>(op.getType()))
4930 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4931 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4935LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4936 auto cond = getLoweredValue(op.getSel());
4937 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4938 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4939 if (!cond || !ifTrue || !ifFalse)
4942 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4944 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4947LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4948 auto sel = getLoweredValue(op.getSel());
4949 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4950 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4951 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4952 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4953 if (!sel || !v3 || !v2 || !v1 || !v0)
4955 Value array[] = {v3, v2, v1, v0};
4958 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4977Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4978 assert(op->getNumResults() == 1 &&
"only expect a single result");
4979 auto val = op->getResult(0);
4983 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4990 OpBuilder::InsertionGuard guard(builder);
4991 builder.setInsertionPoint(op);
4992 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4993 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4995 op->getContext(),
nullptr, 0,
4998 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
4999 op->setOperand(idx, wire);
5004 sv::setSVAttributes(assignOp,
5005 sv::SVAttributeAttr::get(builder.getContext(),
5006 "synopsys infer_mux_override",
5011Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5013 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5018 if (!llvm::isPowerOf2_64(size)) {
5019 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5021 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5023 Value temp2[] = {ext.getResult(), array};
5029 return inBoundsRead;
5032LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5034 auto index = getLoweredAndExtOrTruncValue(
5036 UIntType::get(op.getContext(),
5041 SmallVector<Value> loweredInputs;
5042 loweredInputs.reserve(op.getInputs().size());
5043 for (
auto input : op.getInputs()) {
5044 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5047 loweredInputs.push_back(lowered);
5051 return setLowering(op, createArrayIndexing(array, index));
5054LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5055 auto resultTy =
lowerType(op.getType());
5059 SmallVector<Value, 4> operands;
5060 operands.reserve(op.getSubstitutions().size());
5061 for (
auto operand : op.getSubstitutions()) {
5062 auto lowered = getLoweredValue(operand);
5065 operands.push_back(lowered);
5068 ArrayAttr symbols = op.getSymbolsAttr();
5070 symbols = ArrayAttr::get(op.getContext(), {});
5072 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5076LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5080 Type baseType = op.getType().getType();
5083 if (isa<ClockType>(baseType))
5084 xmrType = builder.getIntegerType(1);
5088 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5089 op.getRef(), op.getVerbatimSuffixAttr());
5092LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5096 if (isa<ClockType>(op.getType()))
5097 xmrType = builder.getIntegerType(1);
5101 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5102 op.getRef(), op.getVerbatimSuffixAttr());
5103 auto readXmr = getReadValue(xmr);
5104 if (!isa<ClockType>(op.getType()))
5105 return setLowering(op, readXmr);
5106 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5111LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5112LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5120LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5132FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5133 auto srcType = srcVal.getType();
5134 auto dstType = destVal.getType();
5135 if (srcType != dstType &&
5136 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5139 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5140 .Case<hw::WireOp>([&](
auto op) {
5141 maybeUnused(op.getInput());
5142 op.getInputMutable().assign(srcVal);
5145 .Case<seq::FirRegOp>([&](
auto op) {
5146 maybeUnused(op.getNext());
5147 op.getNextMutable().assign(srcVal);
5150 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5153 op.emitOpError(
"used as connect destination");
5156 .Default([](
auto) {
return false; });
5159LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5160 auto dest = op.getDest();
5162 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5163 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5165 return handleZeroBit(op.getSrc(), []() { return success(); });
5167 auto destVal = getPossiblyInoutLoweredValue(dest);
5171 auto result = lowerConnect(destVal, srcVal);
5179 if (updateIfBackedge(destVal, srcVal))
5182 if (!isa<hw::InOutType>(destVal.getType()))
5183 return op.emitError(
"destination isn't an inout type");
5189LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5190 auto dest = op.getDest();
5191 auto srcVal = getLoweredValue(op.getSrc());
5193 return handleZeroBit(op.getSrc(), []() { return success(); });
5195 auto destVal = getPossiblyInoutLoweredValue(dest);
5199 auto result = lowerConnect(destVal, srcVal);
5207 if (updateIfBackedge(destVal, srcVal))
5210 if (!isa<hw::InOutType>(destVal.getType()))
5211 return op.emitError(
"destination isn't an inout type");
5217LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5218 if (circuitState.lowerToCore)
5219 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5221 auto srcVal = getLoweredValue(op.getSrc());
5225 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5229 if (!isa<hw::InOutType>(destVal.getType()))
5230 return op.emitError(
"destination isn't an inout type");
5233 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5234 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5235 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5240LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5241 if (circuitState.lowerToCore)
5242 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5244 auto src = getLoweredNonClockValue(op.getSrc());
5245 auto clock = getLoweredNonClockValue(op.getClock());
5246 auto pred = getLoweredValue(op.getPredicate());
5247 if (!src || !clock || !pred)
5250 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5255 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5256 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5257 addToAlwaysBlock(clock, [&]() {
5258 addIfProceduralBlock(
5259 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5264LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5265 if (circuitState.lowerToCore)
5266 return op.emitOpError(
5267 "lower-to-core does not support firrtl.ref.force_initial");
5269 auto src = getLoweredNonClockValue(op.getSrc());
5270 auto pred = getLoweredValue(op.getPredicate());
5274 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5279 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5280 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5281 addToInitialBlock([&]() {
5282 addIfProceduralBlock(
5283 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5288LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5289 if (circuitState.lowerToCore)
5290 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5292 auto clock = getLoweredNonClockValue(op.getClock());
5293 auto pred = getLoweredValue(op.getPredicate());
5294 if (!clock || !pred)
5297 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5302 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5303 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5304 addToAlwaysBlock(clock, [&]() {
5305 addIfProceduralBlock(pred,
5306 [&]() { sv::ReleaseOp::create(builder, destVal); });
5311LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5312 if (circuitState.lowerToCore)
5313 return op.emitOpError(
5314 "lower-to-core does not support firrtl.ref.release_initial");
5316 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5317 auto pred = getLoweredValue(op.getPredicate());
5318 if (!destVal || !pred)
5322 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5323 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5324 addToInitialBlock([&]() {
5325 addIfProceduralBlock(pred,
5326 [&]() { sv::ReleaseOp::create(builder, destVal); });
5334 StringRef originalFormatString,
5335 ValueRange operands,
5336 StringAttr &result) {
5339 SmallString<32> formatString;
5340 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5341 char c = originalFormatString[i];
5345 formatString.push_back(c);
5348 SmallString<6> width;
5349 c = originalFormatString[++i];
5352 c = originalFormatString[++i];
5363 formatString.append(width);
5369 formatString.push_back(c);
5376 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5377 formatString.push_back(c);
5381 auto substitution = operands[subIdx++];
5382 assert(type_isa<FStringType>(substitution.getType()) &&
5383 "the operand for a '{{}}' substitution must be an 'fstring' type");
5385 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5386 .template Case<TimeOp>([&](
auto) {
5387 formatString.append(
"%0t");
5390 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5391 formatString.append(
"%m");
5394 .Default([&](
auto) {
5395 emitError(loc,
"has a substitution with an unimplemented "
5397 .attachNote(substitution.getLoc())
5398 <<
"op with an unimplemented lowering is here";
5408 formatString.push_back(c);
5412 result = StringAttr::get(loc->getContext(), formatString);
5419LogicalResult FIRRTLLowering::visitPrintfLike(
5420 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5421 auto clock = getLoweredNonClockValue(op.getClock());
5422 auto cond = getLoweredValue(op.getCond());
5423 if (!clock || !cond)
5426 StringAttr formatString;
5428 op.getSubstitutions(), formatString)))
5431 auto fn = [&](Value fd) {
5432 SmallVector<Value> operands;
5433 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5435 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5439 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5443LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5444 if (!circuitState.lowerToCore)
5445 return visitPrintfLike(op, {},
true);
5447 auto clock = getLoweredValue(op.getClock());
5448 auto cond = getLoweredValue(op.getCond());
5449 if (!clock || !cond)
5453 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5454 if (failed(formatString))
5457 auto stderrOp = sim::StderrStreamOp::create(builder);
5458 sim::TriggeredOp::create(builder, clock, cond, [&] {
5459 sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
5464LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5465 if (circuitState.lowerToCore) {
5466 auto clock = getLoweredValue(op.getClock());
5467 auto cond = getLoweredValue(op.getCond());
5468 if (!clock || !cond)
5471 auto fileFormatString = lowerSimFormatString(
5472 op.getOutputFileAttr(), op.getOutputFileSubstitutions());
5473 if (failed(fileFormatString))
5477 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5478 if (failed(formatString))
5481 sim::TriggeredOp::create(builder, clock, cond, [&] {
5482 auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
5483 sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
5488 StringAttr outputFileAttr;
5490 op.getOutputFileSubstitutions(),
5494 FileDescriptorInfo outputFile(outputFileAttr,
5495 op.getOutputFileSubstitutions());
5496 return visitPrintfLike(op, outputFile,
false);
5500LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5501 if (circuitState.lowerToCore)
5502 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5504 auto clock = getLoweredNonClockValue(op.getClock());
5505 auto cond = getLoweredValue(op.getCond());
5506 if (!clock || !cond)
5509 auto fn = [&](Value fd) {
5510 sv::FFlushOp::create(builder, op.getLoc(), fd);
5514 if (!op.getOutputFileAttr())
5515 return lowerStatementWithFd({}, clock, cond, fn,
false);
5519 StringAttr outputFileAttr;
5521 op.getOutputFileSubstitutions(),
5525 return lowerStatementWithFd(
5526 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5527 clock, cond, fn,
false);
5532LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5533 auto clock = getLoweredValue(op.getClock());
5534 auto cond = getLoweredValue(op.getCond());
5535 if (!clock || !cond)
5538 circuitState.usedStopCond =
true;
5539 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5542 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5543 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5545 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5546 op.getExitCode() == 0,
5555template <
typename... Args>
5557 StringRef opName, Args &&...args) {
5558 if (opName ==
"assert")
5559 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5560 if (opName ==
"assume")
5561 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5562 if (opName ==
"cover")
5563 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5564 llvm_unreachable(
"unknown verification op");
5570template <
typename... Args>
5572 StringRef opName, Args &&...args) {
5573 if (opName ==
"assert")
5574 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5575 if (opName ==
"assume")
5576 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5577 if (opName ==
"cover")
5578 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5579 llvm_unreachable(
"unknown verification op");
5583 switch (eventControl) {
5584 case EventControl::AtPosEdge:
5585 return verif::ClockEdge::Pos;
5586 case EventControl::AtEdge:
5587 return verif::ClockEdge::Both;
5588 case EventControl::AtNegEdge:
5589 return verif::ClockEdge::Neg;
5591 llvm_unreachable(
"unknown FIRRTL event control");
5594LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5595 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5596 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5597 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5598 if (guardsAttr && !guardsAttr.empty())
5599 return op->emitOpError(
5600 "lower-to-core does not support guarded verification statements");
5602 auto clock = getLoweredNonClockValue(opClock);
5603 auto enable = getLoweredValue(opEnable);
5604 auto predicate = getLoweredValue(opPredicate);
5605 if (!clock || !enable || !predicate)
5609 if (opNameAttr && !opNameAttr.getValue().empty())
5610 label = StringAttr::get(builder.getContext(),
5611 labelPrefix + opNameAttr.getValue());
5614 auto opName = op->getName().stripDialect();
5615 if (opName ==
"assert") {
5616 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5620 if (opName ==
"assume") {
5621 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5625 if (opName ==
"cover") {
5626 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5630 llvm_unreachable(
"unknown verification op");
5651LogicalResult FIRRTLLowering::lowerVerificationStatement(
5652 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5653 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5654 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5655 if (circuitState.lowerToCore)
5656 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5657 opPredicate, opEnable, opNameAttr,
5660 StringRef opName = op->getName().stripDialect();
5663 ArrayRef<Attribute> guards{};
5664 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5665 guards = guardsAttr.getValue();
5667 auto isCover = isa<CoverOp>(op);
5668 auto clock = getLoweredNonClockValue(opClock);
5669 auto enable = getLoweredValue(opEnable);
5670 auto predicate = getLoweredValue(opPredicate);
5671 if (!clock || !enable || !predicate)
5675 if (opNameAttr && !opNameAttr.getValue().empty())
5677 StringAttr prefixedLabel;
5680 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5683 SmallVector<Value> messageOps;
5687 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5688 flavor = VerificationFlavor::None;
5690 if (flavor == VerificationFlavor::None) {
5694 auto format = op->getAttrOfType<StringAttr>(
"format");
5696 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5697 if (!isa<AssertOp>(op))
5698 return op->emitError()
5699 <<
"ifElseFatal format cannot be used for non-assertions";
5700 flavor = VerificationFlavor::IfElseFatal;
5701 }
else if (isConcurrent)
5702 flavor = VerificationFlavor::SVA;
5704 flavor = VerificationFlavor::Immediate;
5707 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5711 opOperands, message)))
5714 if (failed(loweredFmtOperands(opOperands, messageOps)))
5717 if (flavor == VerificationFlavor::SVA) {
5722 for (
auto &loweredValue : messageOps)
5723 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5729 case VerificationFlavor::Immediate: {
5731 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5732 builder.getContext(), circt::sv::DeferAssert::Immediate);
5733 addToAlwaysBlock(clock, [&]() {
5734 addIfProceduralBlock(enable, [&]() {
5736 prefixedLabel, message, messageOps);
5741 case VerificationFlavor::IfElseFatal: {
5742 assert(isa<AssertOp>(op) &&
"only assert is expected");
5745 auto boolType = IntegerType::get(builder.getContext(), 1);
5746 predicate = comb::createOrFoldNot(builder, predicate,
true);
5747 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5749 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5750 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5751 addToAlwaysBlock(clock, [&]() {
5752 addIfProceduralBlock(predicate, [&]() {
5753 circuitState.usedStopCond =
true;
5754 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5756 circuitState.usedAssertVerboseCond =
true;
5757 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5759 addIfProceduralBlock(
5760 sv::MacroRefExprOp::create(builder, boolType,
5761 "ASSERT_VERBOSE_COND_"),
5763 sv::ErrorProceduralOp::create(builder, message, messageOps);
5765 addIfProceduralBlock(
5766 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5767 [&]() { sv::FatalProceduralOp::create(builder); });
5773 case VerificationFlavor::SVA: {
5778 comb::createOrFoldNot(builder, enable,
true);
5780 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5782 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5786 sv::EventControl event;
5787 switch (opEventControl) {
5788 case EventControl::AtPosEdge:
5789 event = circt::sv::EventControl::AtPosEdge;
5791 case EventControl::AtEdge:
5792 event = circt::sv::EventControl::AtEdge;
5794 case EventControl::AtNegEdge:
5795 event = circt::sv::EventControl::AtNegEdge;
5801 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5802 predicate, prefixedLabel, message, messageOps);
5805 case VerificationFlavor::None:
5807 "flavor `None` must be converted into one of concreate flavors");
5814 return emitGuards(op->getLoc(), guards,
emit);
5818LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5819 return lowerVerificationStatement(
5820 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5821 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5822 op.getIsConcurrent(), op.getEventControl());
5826LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5827 return lowerVerificationStatement(
5828 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5829 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5830 op.getIsConcurrent(), op.getEventControl());
5834LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5835 return lowerVerificationStatement(
5836 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5837 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5838 op.getIsConcurrent(), op.getEventControl());
5842LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5843 if (circuitState.lowerToCore) {
5844 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5845 if (guardsAttr && !guardsAttr.empty())
5846 return op.emitOpError(
5847 "lower-to-core does not support guarded verification statements");
5849 auto predicate = getLoweredValue(op.getPredicate());
5850 auto enable = getLoweredValue(op.getEnable());
5851 if (!predicate || !enable)
5854 auto label = op.getNameAttr();
5855 StringAttr assumeLabel;
5856 if (label && !label.empty())
5858 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5859 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5867 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5868 ArrayRef<Attribute> guards =
5869 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5871 auto label = op.getNameAttr();
5872 StringAttr assumeLabel;
5873 if (label && !label.empty())
5875 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5876 auto predicate = getLoweredValue(op.getPredicate());
5877 auto enable = getLoweredValue(op.getEnable());
5878 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5879 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5881 SmallVector<Value> messageOps;
5882 for (
auto operand : op.getSubstitutions()) {
5883 auto loweredValue = getLoweredValue(operand);
5884 if (!loweredValue) {
5888 loweredValue = getOrCreateIntConstant(1, 0);
5890 messageOps.push_back(loweredValue);
5892 return emitGuards(op.getLoc(), guards, [&]() {
5893 sv::AlwaysOp::create(
5894 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5896 if (op.getMessageAttr().getValue().empty())
5897 buildImmediateVerifOp(
5898 builder,
"assume", predicate,
5899 circt::sv::DeferAssertAttr::get(
5900 builder.getContext(), circt::sv::DeferAssert::Immediate),
5903 buildImmediateVerifOp(
5904 builder,
"assume", predicate,
5905 circt::sv::DeferAssertAttr::get(
5906 builder.getContext(), circt::sv::DeferAssert::Immediate),
5907 assumeLabel, op.getMessageAttr(), messageOps);
5912LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5914 if (op.getAttached().size() < 2)
5917 SmallVector<Value, 4> inoutValues;
5918 for (
auto v : op.getAttached()) {
5919 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5920 if (!inoutValues.back()) {
5924 inoutValues.pop_back();
5928 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5929 return op.emitError(
"operand isn't an inout type");
5932 if (inoutValues.size() < 2)
5940 if (circuitState.lowerToCore)
5941 return op.emitOpError(
5942 "lower-to-core does not support firrtl.attach that requires SV "
5948 bool isAttachInternalOnly =
5949 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5951 if (isAttachInternalOnly) {
5952 auto v0 = inoutValues.front();
5953 for (
auto v : inoutValues) {
5956 v.replaceAllUsesWith(v0);
5963 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5964 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5969 SmallVector<Value, 4> values;
5970 for (
auto inoutValue : inoutValues)
5971 values.push_back(getReadValue(inoutValue));
5973 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5974 for (
size_t i2 = 0; i2 != e; ++i2)
5982 sv::IfDefOp::create(
5983 builder,
"VERILATOR",
5985 sv::VerbatimOp::create(
5987 "`error \"Verilator does not support alias and thus "
5989 "arbitrarily connect bidirectional wires and ports\"");
5991 [&]() { sv::AliasOp::create(builder, inoutValues); });
5997LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5998 sv::BindOp::create(builder, op.getInstanceAttr());
6002LogicalResult FIRRTLLowering::fixupLTLOps() {
6003 if (ltlOpFixupWorklist.empty())
6005 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6009 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6010 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6011 if (isa<
hw::WireOp>(user))
6012 ltlOpFixupWorklist.insert(user);
6015 while (!ltlOpFixupWorklist.empty()) {
6016 auto *op = ltlOpFixupWorklist.pop_back_val();
6019 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6020 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6021 SmallVector<Type, 2> types;
6022 auto result = opIntf.inferReturnTypes(
6023 op->getContext(), op->getLoc(), op->getOperands(),
6024 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6028 assert(types.size() == op->getNumResults());
6032 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6033 if (result.getType() == type)
6035 LLVM_DEBUG(llvm::dbgs()
6036 <<
" - Result #" << result.getResultNumber() <<
" from "
6037 << result.getType() <<
" to " << type <<
"\n");
6038 result.setType(type);
6039 for (
auto *user : result.getUsers())
6041 ltlOpFixupWorklist.insert(user);
6046 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6047 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6048 wireOp.replaceAllUsesWith(wireOp.getInput());
6049 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6050 if (wireOp.use_empty())
6057 SmallPtrSet<Operation *, 4> usersReported;
6058 for (
auto *user : op->getUsers()) {
6059 if (!usersReported.insert(user).second)
6061 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6062 user->getDialect()))
6064 if (isa<hw::WireOp>(user))
6066 auto d = op->emitError(
6067 "verification operation used in a non-verification context");
6068 d.attachNote(user->getLoc())
6069 <<
"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, 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