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 emitError(builder.getLoc(),
"lower-to-core does not support "
2933 "{{SimulationTime}} in printf");
2936 .Default([&](
auto) {
2937 emitError(builder.getLoc(),
"has a substitution with "
2940 .attachNote(substitution.getLoc())
2941 <<
"op with an unimplemented lowering is here";
2950 literal.push_back(c);
2955 emitLiteral(literal);
2956 if (fragments.empty())
2957 return sim::FormatLiteralOp::create(builder,
"").getResult();
2958 if (fragments.size() == 1)
2959 return fragments.front();
2960 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
2963LogicalResult FIRRTLLowering::lowerStatementWithFd(
2964 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2965 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2967 bool failed =
false;
2968 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2969 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2970 addToAlwaysBlock(clock, [&]() {
2973 circuitState.usedPrintf =
true;
2975 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2978 Value ifCond = cond;
2979 if (usePrintfCond) {
2981 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2982 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2985 addIfProceduralBlock(ifCond, [&]() {
2989 if (fileDescriptor.isDefaultFd()) {
2991 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2994 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2995 if (llvm::failed(fdOrError)) {
3001 failed = llvm::failed(fn(fd));
3005 return failure(failed);
3009FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3010 circuitState.usedFileDescriptorLib =
true;
3011 circuitState.addFragment(
3012 theModule, sv::getFileDescriptorFragmentRef(builder.getContext()));
3015 if (
info.isSubstitutionRequired()) {
3016 SmallVector<Value> fileNameOperands;
3017 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3020 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3025 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3029 return sv::createProceduralFileDescriptorGetterCall(builder, builder.getLoc(),
3039LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3040 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3041 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3042 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3046 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3049 if (srcWidth != -1) {
3051 assert((srcWidth != 0) &&
3052 "Lowering produced value for zero width source");
3054 assert((srcWidth == 0) &&
3055 "Lowering produced null value but source wasn't zero width");
3059 assert(result &&
"Lowering of foreign type produced null value");
3062 auto &slot = valueMapping[orig];
3063 assert(!slot &&
"value lowered multiple times");
3070LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3074 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3075 auto &entry = hwConstantMap[cst.getValueAttr()];
3086 cst->moveBefore(&theModule.getBodyBlock()->front());
3090 return setLowering(orig, result);
3095template <
typename ResultOpType,
typename... CtorArgTypes>
3096LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3097 CtorArgTypes... args) {
3098 auto result = builder.createOrFold<ResultOpType>(args...);
3099 if (
auto *op = result.getDefiningOp())
3101 return setPossiblyFoldedLowering(orig->getResult(0), result);
3108template <
typename ResultOpType,
typename... CtorArgTypes>
3109LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3110 CtorArgTypes... args) {
3111 auto result = builder.createOrFold<ResultOpType>(args...);
3112 if (
auto *op = result.getDefiningOp())
3113 ltlOpFixupWorklist.insert(op);
3114 return setPossiblyFoldedLowering(orig->getResult(0), result);
3123Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3124 auto backedge = backedgeBuilder.
get(type, loc);
3125 backedges.insert({backedge, backedge});
3133Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3134 auto backedge = createBackedge(orig.getLoc(), type);
3135 (void)setLowering(orig, backedge);
3141bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3142 auto backedgeIt = backedges.find(dest);
3143 if (backedgeIt == backedges.end())
3145 backedgeIt->second = src;
3153void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3154 const std::function<
void(
void)> &fn, Region ®ion) {
3158 auto oldIP = builder.saveInsertionPoint();
3160 builder.setInsertionPointToEnd(®ion.front());
3162 builder.restoreInsertionPoint(oldIP);
3166Value FIRRTLLowering::getReadValue(Value v) {
3167 Value result = readInOutCreated.lookup(v);
3173 auto oldIP = builder.saveInsertionPoint();
3174 if (
auto *vOp = v.getDefiningOp()) {
3175 builder.setInsertionPointAfter(vOp);
3179 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3184 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3185 result = getReadValue(arrayIndexInout.getInput());
3187 arrayIndexInout.getIndex());
3192 builder.restoreInsertionPoint(oldIP);
3193 readInOutCreated.insert({v, result});
3197Value FIRRTLLowering::getNonClockValue(Value v) {
3198 auto it = fromClockMapping.try_emplace(v, Value{});
3200 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3201 builder.setInsertionPointAfterValue(v);
3202 it.first->second = seq::FromClockOp::create(builder, v);
3204 return it.first->second;
3207void FIRRTLLowering::addToAlwaysBlock(
3208 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3209 sv::EventControl resetEdge, Value reset,
3210 const std::function<
void(
void)> &body,
3211 const std::function<
void(
void)> &resetBody) {
3212 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3213 resetStyle, resetEdge, reset};
3214 sv::AlwaysOp alwaysOp;
3215 sv::IfOp insideIfOp;
3216 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3220 assert(resetStyle != sv::ResetType::NoReset);
3233 auto createIfOp = [&]() {
3236 insideIfOp = sv::IfOp::create(
3237 builder, reset, [] {}, [] {});
3239 if (resetStyle == sv::ResetType::AsyncReset) {
3240 sv::EventControl events[] = {clockEdge, resetEdge};
3241 Value clocks[] = {clock, reset};
3243 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3244 if (resetEdge == sv::EventControl::AtNegEdge)
3245 llvm_unreachable(
"negative edge for reset is not expected");
3249 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3253 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3254 insideIfOp =
nullptr;
3256 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3260 assert(insideIfOp &&
"reset body must be initialized before");
3261 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3262 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3264 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3270 alwaysOp->moveBefore(builder.getInsertionBlock(),
3271 builder.getInsertionPoint());
3274LogicalResult FIRRTLLowering::emitGuards(Location loc,
3275 ArrayRef<Attribute> guards,
3276 std::function<
void(
void)>
emit) {
3277 if (guards.empty()) {
3281 auto guard = dyn_cast<StringAttr>(guards[0]);
3283 return mlir::emitError(loc,
3284 "elements in `guards` array must be `StringAttr`");
3287 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3288 LogicalResult result = LogicalResult::failure();
3289 addToIfDefBlock(guard.getValue(), [&]() {
3290 result = emitGuards(loc, guards.drop_front(), emit);
3295void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3296 std::function<
void(
void)> thenCtor,
3297 std::function<
void(
void)> elseCtor) {
3298 auto condAttr = builder.getStringAttr(cond);
3299 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3301 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3302 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3307 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3309 ifdefBlocks[{builder.getBlock(), condAttr}] =
3310 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3314void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3315 auto op = initialBlocks.lookup(builder.getBlock());
3317 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3322 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3324 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3328void FIRRTLLowering::addIfProceduralBlock(Value cond,
3329 std::function<
void(
void)> thenCtor,
3330 std::function<
void(
void)> elseCtor) {
3333 auto insertIt = builder.getInsertionPoint();
3334 if (insertIt != builder.getBlock()->begin())
3335 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3336 if (ifOp.getCond() == cond) {
3337 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3338 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3343 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3355FIRRTLLowering::UnloweredOpResult
3356FIRRTLLowering::handleUnloweredOp(Operation *op) {
3358 if (!op->getRegions().empty() &&
3359 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3360 op->emitOpError(
"must explicitly handle its regions");
3361 return LoweringFailure;
3368 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3370 for (
auto ®ion : op->getRegions())
3371 addToWorklist(region);
3372 for (
auto &operand : op->getOpOperands())
3373 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3374 operand.set(lowered);
3375 for (
auto result : op->getResults())
3376 (void)setLowering(result, result);
3377 return AlreadyLowered;
3389 if (op->getNumResults() == 1) {
3390 auto resultType = op->getResult(0).getType();
3391 if (type_isa<FIRRTLBaseType>(resultType) &&
3393 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3395 (void)setLowering(op->getResult(0), Value());
3399 op->emitOpError(
"LowerToHW couldn't handle this operation");
3400 return LoweringFailure;
3403LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3406 return setLowering(op, Value());
3408 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3411LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3413 if (isa<ClockType>(op.getType())) {
3414 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3415 :
seq::ClockConst::Low);
3417 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3419 return setLowering(op, cst);
3422FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3423 auto iIdx = getOrCreateIntConstant(
3425 firrtl::type_cast<FVectorType>(op.getInput().getType())
3432 if (isa<sv::InOutType>(input.getType()))
3433 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3436 if (
auto *definingOp = result.getDefiningOp())
3441FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3442 Value valueIdx = getLoweredAndExtOrTruncValue(
3444 UIntType::get(op->getContext(),
3446 firrtl::type_cast<FVectorType>(op.getInput().getType())
3447 .getNumElements())));
3449 op->emitError() <<
"input lowering failed";
3456 if (isa<sv::InOutType>(input.getType()))
3457 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3459 result = createArrayIndexing(input, valueIdx);
3460 if (
auto *definingOp = result.getDefiningOp())
3465FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3466 auto resultType =
lowerType(op->getResult(0).getType());
3467 if (!resultType || !input) {
3468 op->emitError() <<
"subfield type lowering failed";
3474 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3475 .getElementName(op.getFieldIndex());
3477 if (isa<sv::InOutType>(input.getType()))
3478 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3481 if (
auto *definingOp = result.getDefiningOp())
3486LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3488 return setLowering(op, Value());
3490 auto input = getPossiblyInoutLoweredValue(op.getInput());
3492 return op.emitError() <<
"input lowering failed";
3494 auto result = lowerSubindex(op, input);
3497 return setLowering(op, *result);
3500LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3502 return setLowering(op, Value());
3504 auto input = getPossiblyInoutLoweredValue(op.getInput());
3506 return op.emitError() <<
"input lowering failed";
3508 auto result = lowerSubaccess(op, input);
3511 return setLowering(op, *result);
3514LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3517 if (getLoweredValue(op) || !op.getInput())
3521 return setLowering(op, Value());
3523 auto input = getPossiblyInoutLoweredValue(op.getInput());
3525 return op.emitError() <<
"input lowering failed";
3527 auto result = lowerSubfield(op, input);
3530 return setLowering(op, *result);
3533LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3534 auto resultType =
lowerType(op.getResult().getType());
3535 SmallVector<Value> operands;
3537 for (
auto oper :
llvm::reverse(op.getOperands())) {
3538 auto val = getLoweredValue(oper);
3541 operands.push_back(val);
3543 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3546LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3547 auto resultType =
lowerType(op.getResult().getType());
3548 SmallVector<Value> operands;
3549 for (
auto oper : op.getOperands()) {
3550 auto val = getLoweredValue(oper);
3553 operands.push_back(val);
3555 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3558LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3561 return setLowering(op, Value());
3563 auto input = getLoweredValue(op.getInput());
3564 auto tagName = op.getFieldNameAttr();
3565 auto oldType = op.getType().base();
3567 auto element = *oldType.getElement(op.getFieldNameAttr());
3569 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3570 auto tagType = structType.getFieldType(
"tag");
3571 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3572 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3574 auto bodyType = structType.getFieldType(
"body");
3575 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3576 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3577 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3579 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3580 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3583LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3584 auto resultType =
lowerType(op.getResult().getType());
3586 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3588 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3589 cast<ArrayAttr>(attr));
3592LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3594 auto tagName = op.getFieldNameAttr();
3595 auto lhs = getLoweredValue(op.getInput());
3596 if (isa<hw::StructType>(lhs.getType()))
3599 auto index = op.getFieldIndex();
3600 auto enumType = op.getInput().getType().base();
3601 auto tagValue = enumType.getElementValueAttr(index);
3602 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3603 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3604 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3605 loweredTagValue, tagName);
3607 Type resultType = builder.getIntegerType(1);
3608 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3612LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3615 return setLowering(op, Value());
3617 auto tagName = op.getFieldNameAttr();
3618 auto input = getLoweredValue(op.getInput());
3620 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3623LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3626 return setLowering(op, Value());
3628 auto input = getLoweredValue(op.getInput());
3634 if (isa<hw::StructType>(input.getType())) {
3635 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3640 return setLowering(op, input);
3647LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3648 auto origResultType = op.getResult().getType();
3652 if (!type_isa<FIRRTLType>(origResultType)) {
3653 createBackedge(op.getResult(), origResultType);
3657 auto resultType =
lowerType(origResultType);
3661 if (resultType.isInteger(0)) {
3662 if (op.getInnerSym())
3663 return op.emitError(
"zero width wire is referenced by name [")
3664 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3665 return setLowering(op.getResult(), Value());
3669 auto innerSym = lowerInnerSymbol(op);
3670 auto name = op.getNameAttr();
3673 auto wire = hw::WireOp::create(
3674 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3676 if (
auto svAttrs = sv::getSVAttributes(op))
3677 sv::setSVAttributes(wire, svAttrs);
3679 return setLowering(op.getResult(), wire);
3682LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3683 auto resultTy =
lowerType(op.getType());
3686 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3688 SmallVector<Value, 4> operands;
3689 operands.reserve(op.getSubstitutions().size());
3690 for (
auto operand : op.getSubstitutions()) {
3691 auto lowered = getLoweredValue(operand);
3694 operands.push_back(lowered);
3697 ArrayAttr symbols = op.getSymbolsAttr();
3699 symbols = ArrayAttr::get(op.getContext(), {});
3701 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3705LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3706 auto operand = getLoweredValue(op.getInput());
3708 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3709 if (op.getInnerSym())
3710 return op.emitError(
"zero width node is referenced by name [")
3711 << *op.getInnerSym()
3712 <<
"] (e.g. in an XMR) but must be "
3714 return setLowering(op.getResult(), Value());
3720 auto name = op.getNameAttr();
3721 auto innerSym = lowerInnerSymbol(op);
3724 operand = hw::WireOp::create(builder, operand, name, innerSym);
3727 if (
auto svAttrs = sv::getSVAttributes(op)) {
3729 operand = hw::WireOp::create(builder, operand, name);
3730 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3733 return setLowering(op.getResult(), operand);
3736LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3737 auto resultType =
lowerType(op.getResult().getType());
3740 if (resultType.isInteger(0))
3741 return setLowering(op.getResult(), Value());
3743 Value clockVal = getLoweredValue(op.getClockVal());
3748 auto innerSym = lowerInnerSymbol(op);
3749 Backedge inputEdge = backedgeBuilder.
get(resultType);
3750 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3751 op.getNameAttr(), innerSym);
3754 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3755 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3756 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3757 reg->setAttr(
"firrtl.random_init_start", randomStart);
3758 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3759 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3762 if (
auto svAttrs = sv::getSVAttributes(op))
3763 sv::setSVAttributes(reg, svAttrs);
3766 (void)setLowering(op.getResult(),
reg);
3770LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3771 auto resultType =
lowerType(op.getResult().getType());
3774 if (resultType.isInteger(0))
3775 return setLowering(op.getResult(), Value());
3777 Value clockVal = getLoweredValue(op.getClockVal());
3778 Value resetSignal = getLoweredValue(op.getResetSignal());
3780 Value resetValue = getLoweredAndExtOrTruncValue(
3781 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3783 if (!clockVal || !resetSignal || !resetValue)
3787 auto innerSym = lowerInnerSymbol(op);
3788 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3789 Backedge inputEdge = backedgeBuilder.
get(resultType);
3791 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3792 resetSignal, resetValue, innerSym, isAsync);
3795 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3796 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3797 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3798 reg->setAttr(
"firrtl.random_init_start", randomStart);
3799 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3800 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3803 if (
auto svAttrs = sv::getSVAttributes(op))
3804 sv::setSVAttributes(reg, svAttrs);
3807 (void)setLowering(op.getResult(),
reg);
3812LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3815 if (type_isa<BundleType>(op.getDataType()))
3816 return op.emitOpError(
3817 "should have already been lowered from a ground type to an aggregate "
3818 "type using the LowerTypes pass. Use "
3819 "'firtool --lower-types' or 'circt-opt "
3820 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3826 auto memType = seq::FirMemType::get(
3829 : std::optional<uint32_t>());
3831 seq::FirMemInitAttr memInit;
3832 if (
auto init = op.getInitAttr())
3833 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3834 init.getIsBinary(), init.getIsInline());
3836 auto memDecl = seq::FirMemOp::create(
3839 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3842 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3844 if (!file.isDirectory())
3845 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3846 file.getDirectory());
3847 memDecl.setOutputFileAttr(dir);
3853 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3855 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3858 (void)setLowering(a, value);
3864 auto addInput = [&](StringRef field, Value backedge) {
3866 if (cast<FIRRTLBaseType>(
a.getType())
3868 .getBitWidthOrSentinel() > 0)
3869 (void)setLowering(a, backedge);
3875 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3879 Value backedge, portValue;
3881 portValue = getOrCreateXConstant(1);
3883 auto portType = IntegerType::get(op.getContext(), width);
3884 backedge = portValue = createBackedge(builder.getLoc(), portType);
3886 addInput(field, backedge);
3890 auto addClock = [&](StringRef field) -> Value {
3891 Type clockTy = seq::ClockType::get(op.getContext());
3892 Value portValue = createBackedge(builder.getLoc(), clockTy);
3893 addInput(field, portValue);
3897 auto memportKind = op.getPortKind(i);
3898 if (memportKind == MemOp::PortKind::Read) {
3899 auto addr = addInputPort(
"addr", op.getAddrBits());
3900 auto en = addInputPort(
"en", 1);
3901 auto clk = addClock(
"clk");
3902 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3904 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3905 auto addr = addInputPort(
"addr", op.getAddrBits());
3906 auto en = addInputPort(
"en", 1);
3907 auto clk = addClock(
"clk");
3910 auto mode = addInputPort(
"wmode", 1);
3912 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3919 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3923 auto addr = addInputPort(
"addr", op.getAddrBits());
3926 auto en = addInputPort(
"en", 1);
3930 auto clk = addClock(
"clk");
3944FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
3945 Operation *instanceOp,
3946 SmallVectorImpl<Value> &inputOperands) {
3948 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3949 auto &port = portInfo[portIndex];
3952 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
3957 if (portType.isInteger(0))
3961 if (port.isOutput())
3964 auto portResult = instanceOp->getResult(portIndex);
3965 assert(portResult &&
"invalid IR, couldn't find port");
3969 if (port.isInput()) {
3970 inputOperands.push_back(createBackedge(portResult, portType));
3976 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3977 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3979 auto loweredResult = getPossiblyInoutLoweredValue(source);
3980 inputOperands.push_back(loweredResult);
3981 (void)setLowering(portResult, loweredResult);
3990 "." + port.getName().str() +
".wire");
3994 (void)setLowering(portResult, wire);
3995 inputOperands.push_back(wire);
4001LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4002 Operation *oldModule =
4003 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4005 auto *newModule = circuitState.getNewModule(oldModule);
4007 oldInstance->emitOpError(
"could not find module [")
4008 << oldInstance.getModuleName() <<
"] referenced by instance";
4014 ArrayAttr parameters;
4015 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4020 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4024 SmallVector<Value, 8> operands;
4025 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4032 auto innerSym = oldInstance.getInnerSymAttr();
4033 if (oldInstance.getLowerToBind()) {
4036 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4039 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4040 innerSym.getSymName());
4043 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4044 bindOp->setAttr(
"output_file", outputFile);
4047 circuitState.addBind(bindOp);
4052 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4053 operands, parameters, innerSym);
4055 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4056 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4058 if (newInstance.getInnerSymAttr())
4059 if (
auto forceName = circuitState.instanceForceNames.lookup(
4060 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4061 newInstance.getInnerNameAttr()}))
4062 newInstance->setAttr(
"hw.verilogName", forceName);
4066 unsigned resultNo = 0;
4067 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4068 auto &port = portInfo[portIndex];
4072 Value resultVal = newInstance.getResult(resultNo);
4074 auto oldPortResult = oldInstance.getResult(portIndex);
4075 (void)setLowering(oldPortResult, resultVal);
4081LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4082 if (oldInstanceChoice.getInnerSymAttr()) {
4083 oldInstanceChoice->emitOpError(
4084 "instance choice with inner sym cannot be lowered");
4089 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4091 return oldInstanceChoice->emitOpError(
4092 "must have instance_macro attribute set before "
4096 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4097 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4100 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4101 auto *defaultModuleNode =
4102 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4104 Operation *defaultModule = defaultModuleNode->getModule();
4108 SmallVector<PortInfo, 8> portInfo =
4109 cast<FModuleLike>(defaultModule).getPorts();
4112 SmallVector<Value, 8> inputOperands;
4114 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4118 SmallVector<sv::WireOp, 8> outputWires;
4119 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4120 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4121 auto &port = portInfo[portIndex];
4125 if (!portType || portType.isInteger(0))
4128 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4129 outputWires.push_back(wire);
4130 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4134 auto optionName = oldInstanceChoice.getOptionNameAttr();
4137 auto createInstanceAndAssign = [&](Operation *oldMod,
4138 StringRef suffix) -> hw::InstanceOp {
4139 auto *newMod = circuitState.getNewModule(oldMod);
4141 ArrayAttr parameters;
4142 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4146 SmallString<64> instName;
4147 instName = oldInstanceChoice.getInstanceName();
4148 if (!suffix.empty()) {
4154 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4155 inputOperands, parameters,
nullptr);
4161 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4168 SmallVector<StringAttr> macroNames;
4169 SmallVector<Operation *> altModules;
4170 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4171 altModules.push_back(
4172 circuitState.getInstanceGraph()
4173 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4177 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4178 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4179 if (!optionCaseMacroRef)
4180 return oldInstanceChoice->emitOpError(
4181 "failed to get macro for option case");
4182 macroNames.push_back(optionCaseMacroRef.getAttr());
4186 sv::createNestedIfDefs(
4189 [&](StringRef macro, std::function<
void()> thenCtor,
4190 std::function<
void()> elseCtor) {
4191 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4195 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4196 sv::IfDefOp::create(
4197 builder, oldInstanceChoice.getLoc(), macroNames[i],
4199 SmallString<256> errorMessage;
4200 llvm::raw_svector_ostream os(errorMessage);
4201 os <<
"Multiple instance choice options defined for option '"
4202 << optionName.getValue() <<
"': '"
4203 << macroNames[index].getValue() <<
"' and '"
4204 << macroNames[i].getValue() <<
"'";
4205 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4206 builder.getStringAttr(errorMessage));
4212 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4214 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4216 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4217 builder.getStringAttr(
"{{0}}"),
4218 builder.getArrayAttr({hw::InnerRefAttr::get(
4219 theModule.getNameAttr(),
4220 inst.getInnerSymAttr().getSymName())}));
4224 SmallString<256> errorMessage;
4225 llvm::raw_svector_ostream os(errorMessage);
4226 os <<
"Required instance choice option '" << optionName.getValue()
4227 <<
"' not selected, must define one of: ";
4228 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4229 os <<
"'" << macro.getValue() <<
"'";
4231 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4232 builder.getStringAttr(errorMessage));
4238LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4239 SmallVector<Value> inputs;
4240 SmallVector<Type> types;
4241 for (
auto input : oldOp.getInputs()) {
4242 auto lowered = getLoweredValue(input);
4245 inputs.push_back(lowered);
4246 types.push_back(lowered.getType());
4249 auto newOp = verif::ContractOp::create(builder, types, inputs);
4250 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4251 auto &body = newOp.getBody().emplaceBlock();
4253 for (
auto [newResult, oldResult, oldArg] :
4254 llvm::zip(newOp.getResults(), oldOp.getResults(),
4255 oldOp.getBody().getArguments())) {
4256 if (failed(setLowering(oldResult, newResult)))
4258 if (failed(setLowering(oldArg, newResult)))
4262 body.getOperations().splice(body.end(),
4263 oldOp.getBody().front().getOperations());
4264 addToWorklist(body);
4274LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4275 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4280 return setLowering(op->getResult(0), operand);
4283LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4284 if (isa<ClockType>(op.getInput().getType()))
4285 return setLowering(op->getResult(0),
4286 getLoweredNonClockValue(op.getInput()));
4287 return lowerNoopCast(op);
4290LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4291 if (isa<ClockType>(op.getInput().getType()))
4292 return setLowering(op->getResult(0),
4293 getLoweredNonClockValue(op.getInput()));
4294 return lowerNoopCast(op);
4297LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4298 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4301LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4302 mlir::UnrealizedConversionCastOp op) {
4304 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4307 auto operand = op.getOperand(0);
4308 auto result = op.getResult(0);
4311 if (type_isa<FIRRTLType>(operand.getType()) &&
4312 type_isa<FIRRTLType>(result.getType()))
4313 return lowerNoopCast(op);
4317 if (!type_isa<FIRRTLType>(operand.getType())) {
4318 if (type_isa<FIRRTLType>(result.getType()))
4319 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4325 auto loweredResult = getLoweredValue(operand);
4326 if (!loweredResult) {
4329 if (operand.getType().isSignlessInteger(0)) {
4330 return setLowering(result, Value());
4337 result.replaceAllUsesWith(loweredResult);
4341LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4344 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4345 return setLowering(op, op.getOperand());
4349 auto result = getLoweredValue(op.getOperand());
4355 op.replaceAllUsesWith(result);
4359LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4360 auto operand = getLoweredValue(op.getOperand());
4363 auto resultType =
lowerType(op.getType());
4367 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4370LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4371 auto operand = getLoweredValue(op.getOperand());
4375 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4376 return setLowering(op, getOrCreateIntConstant(1, 0));
4378 return setLowering(op, Value());
4383 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4384 return setLowering(op, operand);
4387 auto zero = getOrCreateIntConstant(1, 0);
4388 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4391LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4392 auto operand = getLoweredValue(op.getInput());
4396 auto allOnes = getOrCreateIntConstant(
4397 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4398 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4401LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4404 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4408 auto resultType =
lowerType(op.getType());
4410 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4411 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4415LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4416 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4419 return setLowering(op, operand);
4422LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4423 auto operand = getLoweredValue(op.getInput());
4426 return setLowering(op, getOrCreateIntConstant(1, 0));
4431 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4435LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4436 auto operand = getLoweredValue(op.getInput());
4439 return setLowering(op, getOrCreateIntConstant(1, 1));
4444 return setLoweringTo<comb::ICmpOp>(
4445 op, ICmpPredicate::eq, operand,
4446 getOrCreateIntConstant(
4447 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4451LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4452 auto operand = getLoweredValue(op.getInput());
4455 return setLowering(op, getOrCreateIntConstant(1, 0));
4461 return setLoweringTo<comb::ICmpOp>(
4462 op, ICmpPredicate::ne, operand,
4463 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4471template <
typename ResultOpType>
4472LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4473 auto resultType = op->getResult(0).getType();
4474 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4475 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4479 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4485template <
typename ResultOpType>
4486LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4487 auto resultType = op->getResult(0).getType();
4488 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4489 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4500 auto intType = builder.getIntegerType(*bitwidth);
4501 auto retType = lhs.getType();
4504 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4505 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4510template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4511LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4513 auto resultType = op->getResult(0).getType();
4514 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4515 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4520 if (type_cast<IntType>(resultType).isSigned())
4521 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4522 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4527LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4528 ICmpPredicate unsignedOp) {
4530 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4531 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4532 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4536 if (cmpType.getWidth() == 0)
4537 cmpType = UIntType::get(builder.getContext(), 1);
4538 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4539 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4544 Type resultType = builder.getIntegerType(1);
4545 return setLoweringTo<comb::ICmpOp>(
4546 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4552template <
typename SignedOp,
typename Un
signedOp>
4553LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4557 auto opType = type_cast<IntType>(op->getResult(0).getType());
4558 if (opType.getWidth() == 0)
4559 return setLowering(op->getResult(0), Value());
4563 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4564 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4569 if (opType.isSigned())
4570 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4572 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4574 if (
auto *definingOp = result.getDefiningOp())
4577 if (resultType == opType)
4578 return setLowering(op->getResult(0), result);
4579 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4582LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4584 if (op.getInputs().empty())
4585 return setLowering(op, Value());
4587 SmallVector<Value> loweredOperands;
4590 for (
auto operand : op.getInputs()) {
4591 auto loweredOperand = getLoweredValue(operand);
4592 if (loweredOperand) {
4593 loweredOperands.push_back(loweredOperand);
4596 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4604 if (loweredOperands.empty())
4605 return setLowering(op, Value());
4608 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4615LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4616 auto input = getLoweredNonClockValue(op.getArg());
4620 if (!isa<IntType>(input.getType())) {
4621 auto srcType = op.getArg().getType();
4623 assert(bitwidth &&
"Unknown width");
4624 auto intType = builder.getIntegerType(*bitwidth);
4625 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4628 return setLoweringTo<comb::ICmpOp>(
4629 op, ICmpPredicate::ceq, input,
4630 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4633LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4634 auto operand = getLoweredValue(op.getInput());
4635 hw::WireOp::create(builder, operand);
4639LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4640 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4641 op.getFormatStringAttr());
4644LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4645 auto type =
lowerType(op.getResult().getType());
4649 auto valueOp = sim::PlusArgsValueOp::create(
4650 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4651 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4653 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4658LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4659 op.emitError(
"SizeOf should have been resolved.");
4663LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4665 if (op.getTestEnable())
4666 testEnable = getLoweredValue(op.getTestEnable());
4667 return setLoweringTo<seq::ClockGateOp>(
4668 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4669 testEnable, hw::InnerSymAttr{});
4672LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4673 auto operand = getLoweredValue(op.getInput());
4674 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4677LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4678 auto operand = getLoweredValue(op.getInput());
4679 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4682LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4683 return setLoweringToLTL<ltl::AndOp>(
4685 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4688LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4689 return setLoweringToLTL<ltl::OrOp>(
4691 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4694LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4695 return setLoweringToLTL<ltl::IntersectOp>(
4697 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4700LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4701 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4702 op.getDelayAttr(), op.getLengthAttr());
4705LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4706 return setLoweringToLTL<ltl::ConcatOp>(
4708 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4711LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4712 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4713 op.getBaseAttr(), op.getMoreAttr());
4716LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4717 return setLoweringToLTL<ltl::GoToRepeatOp>(
4718 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4721LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4722 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4723 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4726LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4727 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4730LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4731 return setLoweringToLTL<ltl::ImplicationOp>(
4733 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4736LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4737 return setLoweringToLTL<ltl::UntilOp>(
4739 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4742LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4743 return setLoweringToLTL<ltl::EventuallyOp>(op,
4744 getLoweredValue(op.getInput()));
4747LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4748 Value
clk = getLoweredNonClockValue(op.getClock());
4749 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4750 op.getDelayAttr(),
clk);
4753LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4754 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4755 ltl::ClockEdge::Pos,
4756 getLoweredNonClockValue(op.getClock()));
4759template <
typename TargetOp,
typename IntrinsicOp>
4760LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4761 auto property = getLoweredValue(op.getProperty());
4762 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4763 TargetOp::create(builder, property, enable, op.getLabelAttr());
4767LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4768 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4771LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4772 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4775LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4776 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4779LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4780 if (!isa<verif::ContractOp>(op->getParentOp()))
4781 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4782 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4785LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4786 if (!isa<verif::ContractOp>(op->getParentOp()))
4787 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4788 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4791LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4792 auto clock = getLoweredNonClockValue(op.getClock());
4793 auto reset = getLoweredValue(op.getReset());
4794 if (!clock || !reset)
4796 auto resetType = op.getReset().getType();
4797 auto uintResetType = dyn_cast<UIntType>(resetType);
4798 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4799 auto isAsync = isa<AsyncResetType>(resetType);
4800 if (!isAsync && !isSync) {
4801 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4802 "requires sync or async reset");
4803 d.attachNote() <<
"reset is of type " << resetType
4804 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4807 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4814LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4815 auto input = getLoweredValue(op.getInput());
4819 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4820 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4823LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4824 auto resultTy =
lowerType(op.getType());
4831 if (type_isa<AnalogType>(op.getType()))
4834 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4837 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4848 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4850 if (!type_isa<IntegerType>(resultTy))
4852 return setLowering(op, constant);
4856 op.emitOpError(
"unsupported type");
4860LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4861 auto input = getLoweredValue(op.getInput());
4864 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4865 if (op.getAmount() == 0)
4866 return setLowering(op, Value());
4867 Type resultType = builder.getIntegerType(op.getAmount());
4868 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4869 inWidth - op.getAmount());
4872LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4873 auto input = getLoweredValue(op.getInput());
4876 if (op.getAmount() == 0)
4878 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4883 if (op.getAmount() == 0)
4884 return setLowering(op, input);
4886 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4887 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4890LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4891 auto input = getLoweredValue(op.getInput());
4896 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4897 auto shiftAmount = op.getAmount();
4898 if (shiftAmount >= inWidth) {
4900 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4901 return setLowering(op, {});
4904 shiftAmount = inWidth - 1;
4907 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4908 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4911LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4912 auto input = getLoweredValue(op.getInput());
4916 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4917 if (inWidth == op.getAmount())
4918 return setLowering(op, Value());
4919 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4920 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4923LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4924 auto cond = getLoweredValue(op.getSel());
4925 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4926 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4927 if (!cond || !ifTrue || !ifFalse)
4930 if (isa<ClockType>(op.getType()))
4931 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4932 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4936LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4937 auto cond = getLoweredValue(op.getSel());
4938 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4939 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4940 if (!cond || !ifTrue || !ifFalse)
4943 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4945 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4948LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4949 auto sel = getLoweredValue(op.getSel());
4950 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4951 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4952 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4953 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4954 if (!sel || !v3 || !v2 || !v1 || !v0)
4956 Value array[] = {v3, v2, v1, v0};
4959 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4978Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4979 assert(op->getNumResults() == 1 &&
"only expect a single result");
4980 auto val = op->getResult(0);
4984 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4991 OpBuilder::InsertionGuard guard(builder);
4992 builder.setInsertionPoint(op);
4993 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4994 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4996 op->getContext(),
nullptr, 0,
4999 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5000 op->setOperand(idx, wire);
5005 sv::setSVAttributes(assignOp,
5006 sv::SVAttributeAttr::get(builder.getContext(),
5007 "synopsys infer_mux_override",
5012Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5014 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5019 if (!llvm::isPowerOf2_64(size)) {
5020 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5022 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5024 Value temp2[] = {ext.getResult(), array};
5030 return inBoundsRead;
5033LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5035 auto index = getLoweredAndExtOrTruncValue(
5037 UIntType::get(op.getContext(),
5042 SmallVector<Value> loweredInputs;
5043 loweredInputs.reserve(op.getInputs().size());
5044 for (
auto input : op.getInputs()) {
5045 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5048 loweredInputs.push_back(lowered);
5052 return setLowering(op, createArrayIndexing(array, index));
5055LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5056 auto resultTy =
lowerType(op.getType());
5060 SmallVector<Value, 4> operands;
5061 operands.reserve(op.getSubstitutions().size());
5062 for (
auto operand : op.getSubstitutions()) {
5063 auto lowered = getLoweredValue(operand);
5066 operands.push_back(lowered);
5069 ArrayAttr symbols = op.getSymbolsAttr();
5071 symbols = ArrayAttr::get(op.getContext(), {});
5073 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5077LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5081 Type baseType = op.getType().getType();
5084 if (isa<ClockType>(baseType))
5085 xmrType = builder.getIntegerType(1);
5089 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5090 op.getRef(), op.getVerbatimSuffixAttr());
5093LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5097 if (isa<ClockType>(op.getType()))
5098 xmrType = builder.getIntegerType(1);
5102 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5103 op.getRef(), op.getVerbatimSuffixAttr());
5104 auto readXmr = getReadValue(xmr);
5105 if (!isa<ClockType>(op.getType()))
5106 return setLowering(op, readXmr);
5107 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5112LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5113LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5121LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5133FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5134 auto srcType = srcVal.getType();
5135 auto dstType = destVal.getType();
5136 if (srcType != dstType &&
5137 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5140 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5141 .Case<hw::WireOp>([&](
auto op) {
5142 maybeUnused(op.getInput());
5143 op.getInputMutable().assign(srcVal);
5146 .Case<seq::FirRegOp>([&](
auto op) {
5147 maybeUnused(op.getNext());
5148 op.getNextMutable().assign(srcVal);
5151 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5154 op.emitOpError(
"used as connect destination");
5157 .Default([](
auto) {
return false; });
5160LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5161 auto dest = op.getDest();
5163 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5164 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5166 return handleZeroBit(op.getSrc(), []() { return success(); });
5168 auto destVal = getPossiblyInoutLoweredValue(dest);
5172 auto result = lowerConnect(destVal, srcVal);
5180 if (updateIfBackedge(destVal, srcVal))
5183 if (!isa<hw::InOutType>(destVal.getType()))
5184 return op.emitError(
"destination isn't an inout type");
5190LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5191 auto dest = op.getDest();
5192 auto srcVal = getLoweredValue(op.getSrc());
5194 return handleZeroBit(op.getSrc(), []() { return success(); });
5196 auto destVal = getPossiblyInoutLoweredValue(dest);
5200 auto result = lowerConnect(destVal, srcVal);
5208 if (updateIfBackedge(destVal, srcVal))
5211 if (!isa<hw::InOutType>(destVal.getType()))
5212 return op.emitError(
"destination isn't an inout type");
5218LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5219 if (circuitState.lowerToCore)
5220 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5222 auto srcVal = getLoweredValue(op.getSrc());
5226 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5230 if (!isa<hw::InOutType>(destVal.getType()))
5231 return op.emitError(
"destination isn't an inout type");
5234 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5235 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5236 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5241LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5242 if (circuitState.lowerToCore)
5243 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5245 auto src = getLoweredNonClockValue(op.getSrc());
5246 auto clock = getLoweredNonClockValue(op.getClock());
5247 auto pred = getLoweredValue(op.getPredicate());
5248 if (!src || !clock || !pred)
5251 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5256 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5257 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5258 addToAlwaysBlock(clock, [&]() {
5259 addIfProceduralBlock(
5260 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5265LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5266 if (circuitState.lowerToCore)
5267 return op.emitOpError(
5268 "lower-to-core does not support firrtl.ref.force_initial");
5270 auto src = getLoweredNonClockValue(op.getSrc());
5271 auto pred = getLoweredValue(op.getPredicate());
5275 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5280 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5281 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5282 addToInitialBlock([&]() {
5283 addIfProceduralBlock(
5284 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5289LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5290 if (circuitState.lowerToCore)
5291 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5293 auto clock = getLoweredNonClockValue(op.getClock());
5294 auto pred = getLoweredValue(op.getPredicate());
5295 if (!clock || !pred)
5298 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5303 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5304 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5305 addToAlwaysBlock(clock, [&]() {
5306 addIfProceduralBlock(pred,
5307 [&]() { sv::ReleaseOp::create(builder, destVal); });
5312LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5313 if (circuitState.lowerToCore)
5314 return op.emitOpError(
5315 "lower-to-core does not support firrtl.ref.release_initial");
5317 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5318 auto pred = getLoweredValue(op.getPredicate());
5319 if (!destVal || !pred)
5323 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5324 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5325 addToInitialBlock([&]() {
5326 addIfProceduralBlock(pred,
5327 [&]() { sv::ReleaseOp::create(builder, destVal); });
5335 StringRef originalFormatString,
5336 ValueRange operands,
5337 StringAttr &result) {
5340 SmallString<32> formatString;
5341 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5342 char c = originalFormatString[i];
5346 formatString.push_back(c);
5349 SmallString<6> width;
5350 c = originalFormatString[++i];
5353 c = originalFormatString[++i];
5364 formatString.append(width);
5370 formatString.push_back(c);
5377 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5378 formatString.push_back(c);
5382 auto substitution = operands[subIdx++];
5383 assert(type_isa<FStringType>(substitution.getType()) &&
5384 "the operand for a '{{}}' substitution must be an 'fstring' type");
5386 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5387 .template Case<TimeOp>([&](
auto) {
5388 formatString.append(
"%0t");
5391 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5392 formatString.append(
"%m");
5395 .Default([&](
auto) {
5396 emitError(loc,
"has a substitution with an unimplemented "
5398 .attachNote(substitution.getLoc())
5399 <<
"op with an unimplemented lowering is here";
5409 formatString.push_back(c);
5413 result = StringAttr::get(loc->getContext(), formatString);
5420LogicalResult FIRRTLLowering::visitPrintfLike(
5421 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5422 auto clock = getLoweredNonClockValue(op.getClock());
5423 auto cond = getLoweredValue(op.getCond());
5424 if (!clock || !cond)
5427 StringAttr formatString;
5429 op.getSubstitutions(), formatString)))
5432 auto fn = [&](Value fd) {
5433 SmallVector<Value> operands;
5434 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5436 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5440 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5444LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5445 if (!circuitState.lowerToCore)
5446 return visitPrintfLike(op, {},
true);
5448 auto clock = getLoweredValue(op.getClock());
5449 auto cond = getLoweredValue(op.getCond());
5450 if (!clock || !cond)
5454 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5455 if (failed(formatString))
5458 sim::TriggeredOp::create(builder, clock, cond, [&] {
5459 sim::PrintFormattedProcOp::create(builder, *formatString);
5464LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5465 if (circuitState.lowerToCore)
5466 return op.emitOpError(
"lower-to-core does not support firrtl.fprintf yet");
5468 StringAttr outputFileAttr;
5470 op.getOutputFileSubstitutions(),
5474 FileDescriptorInfo outputFile(outputFileAttr,
5475 op.getOutputFileSubstitutions());
5476 return visitPrintfLike(op, outputFile,
false);
5480LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5481 if (circuitState.lowerToCore)
5482 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5484 auto clock = getLoweredNonClockValue(op.getClock());
5485 auto cond = getLoweredValue(op.getCond());
5486 if (!clock || !cond)
5489 auto fn = [&](Value fd) {
5490 sv::FFlushOp::create(builder, op.getLoc(), fd);
5494 if (!op.getOutputFileAttr())
5495 return lowerStatementWithFd({}, clock, cond, fn,
false);
5499 StringAttr outputFileAttr;
5501 op.getOutputFileSubstitutions(),
5505 return lowerStatementWithFd(
5506 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5507 clock, cond, fn,
false);
5512LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5513 auto clock = getLoweredValue(op.getClock());
5514 auto cond = getLoweredValue(op.getCond());
5515 if (!clock || !cond)
5518 circuitState.usedStopCond =
true;
5519 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5522 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5523 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5525 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5526 op.getExitCode() == 0,
5535template <
typename... Args>
5537 StringRef opName, Args &&...args) {
5538 if (opName ==
"assert")
5539 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5540 if (opName ==
"assume")
5541 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5542 if (opName ==
"cover")
5543 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5544 llvm_unreachable(
"unknown verification op");
5550template <
typename... Args>
5552 StringRef opName, Args &&...args) {
5553 if (opName ==
"assert")
5554 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5555 if (opName ==
"assume")
5556 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5557 if (opName ==
"cover")
5558 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5559 llvm_unreachable(
"unknown verification op");
5563 switch (eventControl) {
5564 case EventControl::AtPosEdge:
5565 return verif::ClockEdge::Pos;
5566 case EventControl::AtEdge:
5567 return verif::ClockEdge::Both;
5568 case EventControl::AtNegEdge:
5569 return verif::ClockEdge::Neg;
5571 llvm_unreachable(
"unknown FIRRTL event control");
5574LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5575 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5576 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5577 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5578 if (guardsAttr && !guardsAttr.empty())
5579 return op->emitOpError(
5580 "lower-to-core does not support guarded verification statements");
5582 auto clock = getLoweredNonClockValue(opClock);
5583 auto enable = getLoweredValue(opEnable);
5584 auto predicate = getLoweredValue(opPredicate);
5585 if (!clock || !enable || !predicate)
5589 if (opNameAttr && !opNameAttr.getValue().empty())
5590 label = StringAttr::get(builder.getContext(),
5591 labelPrefix + opNameAttr.getValue());
5594 auto opName = op->getName().stripDialect();
5595 if (opName ==
"assert") {
5596 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5600 if (opName ==
"assume") {
5601 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5605 if (opName ==
"cover") {
5606 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5610 llvm_unreachable(
"unknown verification op");
5631LogicalResult FIRRTLLowering::lowerVerificationStatement(
5632 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5633 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5634 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5635 if (circuitState.lowerToCore)
5636 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5637 opPredicate, opEnable, opNameAttr,
5640 StringRef opName = op->getName().stripDialect();
5643 ArrayRef<Attribute> guards{};
5644 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5645 guards = guardsAttr.getValue();
5647 auto isCover = isa<CoverOp>(op);
5648 auto clock = getLoweredNonClockValue(opClock);
5649 auto enable = getLoweredValue(opEnable);
5650 auto predicate = getLoweredValue(opPredicate);
5651 if (!clock || !enable || !predicate)
5655 if (opNameAttr && !opNameAttr.getValue().empty())
5657 StringAttr prefixedLabel;
5660 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5663 SmallVector<Value> messageOps;
5667 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5668 flavor = VerificationFlavor::None;
5670 if (flavor == VerificationFlavor::None) {
5674 auto format = op->getAttrOfType<StringAttr>(
"format");
5676 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5677 if (!isa<AssertOp>(op))
5678 return op->emitError()
5679 <<
"ifElseFatal format cannot be used for non-assertions";
5680 flavor = VerificationFlavor::IfElseFatal;
5681 }
else if (isConcurrent)
5682 flavor = VerificationFlavor::SVA;
5684 flavor = VerificationFlavor::Immediate;
5687 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5691 opOperands, message)))
5694 if (failed(loweredFmtOperands(opOperands, messageOps)))
5697 if (flavor == VerificationFlavor::SVA) {
5702 for (
auto &loweredValue : messageOps)
5703 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5709 case VerificationFlavor::Immediate: {
5711 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5712 builder.getContext(), circt::sv::DeferAssert::Immediate);
5713 addToAlwaysBlock(clock, [&]() {
5714 addIfProceduralBlock(enable, [&]() {
5716 prefixedLabel, message, messageOps);
5721 case VerificationFlavor::IfElseFatal: {
5722 assert(isa<AssertOp>(op) &&
"only assert is expected");
5725 auto boolType = IntegerType::get(builder.getContext(), 1);
5726 predicate = comb::createOrFoldNot(builder, predicate,
true);
5727 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5729 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5730 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5731 addToAlwaysBlock(clock, [&]() {
5732 addIfProceduralBlock(predicate, [&]() {
5733 circuitState.usedStopCond =
true;
5734 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5736 circuitState.usedAssertVerboseCond =
true;
5737 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5739 addIfProceduralBlock(
5740 sv::MacroRefExprOp::create(builder, boolType,
5741 "ASSERT_VERBOSE_COND_"),
5743 sv::ErrorProceduralOp::create(builder, message, messageOps);
5745 addIfProceduralBlock(
5746 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5747 [&]() { sv::FatalProceduralOp::create(builder); });
5753 case VerificationFlavor::SVA: {
5758 comb::createOrFoldNot(builder, enable,
true);
5760 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5762 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5766 sv::EventControl event;
5767 switch (opEventControl) {
5768 case EventControl::AtPosEdge:
5769 event = circt::sv::EventControl::AtPosEdge;
5771 case EventControl::AtEdge:
5772 event = circt::sv::EventControl::AtEdge;
5774 case EventControl::AtNegEdge:
5775 event = circt::sv::EventControl::AtNegEdge;
5781 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5782 predicate, prefixedLabel, message, messageOps);
5785 case VerificationFlavor::None:
5787 "flavor `None` must be converted into one of concreate flavors");
5794 return emitGuards(op->getLoc(), guards,
emit);
5798LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5799 return lowerVerificationStatement(
5800 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5801 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5802 op.getIsConcurrent(), op.getEventControl());
5806LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5807 return lowerVerificationStatement(
5808 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5809 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5810 op.getIsConcurrent(), op.getEventControl());
5814LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5815 return lowerVerificationStatement(
5816 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5817 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5818 op.getIsConcurrent(), op.getEventControl());
5822LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5823 if (circuitState.lowerToCore) {
5824 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5825 if (guardsAttr && !guardsAttr.empty())
5826 return op.emitOpError(
5827 "lower-to-core does not support guarded verification statements");
5829 auto predicate = getLoweredValue(op.getPredicate());
5830 auto enable = getLoweredValue(op.getEnable());
5831 if (!predicate || !enable)
5834 auto label = op.getNameAttr();
5835 StringAttr assumeLabel;
5836 if (label && !label.empty())
5838 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5839 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5847 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5848 ArrayRef<Attribute> guards =
5849 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5851 auto label = op.getNameAttr();
5852 StringAttr assumeLabel;
5853 if (label && !label.empty())
5855 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5856 auto predicate = getLoweredValue(op.getPredicate());
5857 auto enable = getLoweredValue(op.getEnable());
5858 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5859 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5861 SmallVector<Value> messageOps;
5862 for (
auto operand : op.getSubstitutions()) {
5863 auto loweredValue = getLoweredValue(operand);
5864 if (!loweredValue) {
5868 loweredValue = getOrCreateIntConstant(1, 0);
5870 messageOps.push_back(loweredValue);
5872 return emitGuards(op.getLoc(), guards, [&]() {
5873 sv::AlwaysOp::create(
5874 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5876 if (op.getMessageAttr().getValue().empty())
5877 buildImmediateVerifOp(
5878 builder,
"assume", predicate,
5879 circt::sv::DeferAssertAttr::get(
5880 builder.getContext(), circt::sv::DeferAssert::Immediate),
5883 buildImmediateVerifOp(
5884 builder,
"assume", predicate,
5885 circt::sv::DeferAssertAttr::get(
5886 builder.getContext(), circt::sv::DeferAssert::Immediate),
5887 assumeLabel, op.getMessageAttr(), messageOps);
5892LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5894 if (op.getAttached().size() < 2)
5897 SmallVector<Value, 4> inoutValues;
5898 for (
auto v : op.getAttached()) {
5899 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5900 if (!inoutValues.back()) {
5904 inoutValues.pop_back();
5908 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5909 return op.emitError(
"operand isn't an inout type");
5912 if (inoutValues.size() < 2)
5920 if (circuitState.lowerToCore)
5921 return op.emitOpError(
5922 "lower-to-core does not support firrtl.attach that requires SV "
5928 bool isAttachInternalOnly =
5929 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5931 if (isAttachInternalOnly) {
5932 auto v0 = inoutValues.front();
5933 for (
auto v : inoutValues) {
5936 v.replaceAllUsesWith(v0);
5943 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5944 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5949 SmallVector<Value, 4> values;
5950 for (
auto inoutValue : inoutValues)
5951 values.push_back(getReadValue(inoutValue));
5953 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5954 for (
size_t i2 = 0; i2 != e; ++i2)
5962 sv::IfDefOp::create(
5963 builder,
"VERILATOR",
5965 sv::VerbatimOp::create(
5967 "`error \"Verilator does not support alias and thus "
5969 "arbitrarily connect bidirectional wires and ports\"");
5971 [&]() { sv::AliasOp::create(builder, inoutValues); });
5977LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5978 sv::BindOp::create(builder, op.getInstanceAttr());
5982LogicalResult FIRRTLLowering::fixupLTLOps() {
5983 if (ltlOpFixupWorklist.empty())
5985 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5989 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5990 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5991 if (isa<
hw::WireOp>(user))
5992 ltlOpFixupWorklist.insert(user);
5995 while (!ltlOpFixupWorklist.empty()) {
5996 auto *op = ltlOpFixupWorklist.pop_back_val();
5999 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6000 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6001 SmallVector<Type, 2> types;
6002 auto result = opIntf.inferReturnTypes(
6003 op->getContext(), op->getLoc(), op->getOperands(),
6004 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6008 assert(types.size() == op->getNumResults());
6012 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6013 if (result.getType() == type)
6015 LLVM_DEBUG(llvm::dbgs()
6016 <<
" - Result #" << result.getResultNumber() <<
" from "
6017 << result.getType() <<
" to " << type <<
"\n");
6018 result.setType(type);
6019 for (
auto *user : result.getUsers())
6021 ltlOpFixupWorklist.insert(user);
6026 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6027 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6028 wireOp.replaceAllUsesWith(wireOp.getInput());
6029 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6030 if (wireOp.use_empty())
6037 SmallPtrSet<Operation *, 4> usersReported;
6038 for (
auto *user : op->getUsers()) {
6039 if (!usersReported.insert(user).second)
6041 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6042 user->getDialect()))
6044 if (isa<hw::WireOp>(user))
6046 auto d = op->emitError(
6047 "verification operation used in a non-verification context");
6048 d.attachNote(user->getLoc())
6049 <<
"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