37#include "mlir/IR/BuiltinOps.h"
38#include "mlir/IR/BuiltinTypes.h"
39#include "mlir/IR/ImplicitLocOpBuilder.h"
40#include "mlir/IR/Threading.h"
41#include "mlir/Pass/Pass.h"
42#include "llvm/ADT/DenseMap.h"
43#include "llvm/Support/Debug.h"
44#include "llvm/Support/Mutex.h"
45#include "llvm/Support/Path.h"
47#define DEBUG_TYPE "lower-to-hw"
50#define GEN_PASS_DEF_LOWERFIRRTLTOHW
51#include "circt/Conversion/Passes.h.inc"
55using namespace firrtl;
56using circt::comb::ICmpPredicate;
65 auto ftype = dyn_cast<FIRRTLBaseType>(type);
66 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
73 for (
auto operand : op.getAttached()) {
75 operand.getDefiningOp<InstanceOp>())
79 if (!operand.hasOneUse() || singleSource)
81 singleSource = operand;
90 auto checkTypes = [](Operation *op) -> WalkResult {
92 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
93 return op->emitError(
"Found unhandled FIRRTL operation '")
94 << op->getName() <<
"'";
97 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
98 if (llvm::any_of(types, [](Type type) {
99 return isa<FIRRTLDialect>(type.getDialect());
101 return op->emitOpError(
"found unhandled FIRRTL type");
106 if (failed(checkTypeRange(op->getOperandTypes())) ||
107 failed(checkTypeRange(op->getResultTypes())))
108 return WalkResult::interrupt();
111 for (
auto ®ion : op->getRegions())
112 for (
auto &block : region)
113 if (failed(checkTypeRange(block.getArgumentTypes())))
114 return WalkResult::interrupt();
117 return WalkResult::advance();
120 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
127 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
128 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
134 ImplicitLocOpBuilder &builder) {
136 if (BundleType bundle = dyn_cast<BundleType>(type))
137 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
139 if (type != val.getType())
140 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
148 ImplicitLocOpBuilder &builder) {
150 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
152 val = mlir::UnrealizedConversionCastOp::create(
154 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
156 val = builder.createOrFold<HWStructCastOp>(type, val);
161 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
167 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
173 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
174 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
175 dst->setAttr(
"sv.namehint", attr);
181class FileDescriptorInfo {
183 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
184 : outputFileFormat(outputFileName), substitutions(substitutions) {
186 substitutions.empty() &&
187 "substitutions must be empty when output file name is empty");
190 FileDescriptorInfo() =
default;
193 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
196 bool isDefaultFd()
const {
return !outputFileFormat; }
198 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
199 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
203 StringAttr outputFileFormat = {};
206 mlir::ValueRange substitutions;
216struct FIRRTLModuleLowering;
219struct CircuitLoweringState {
221 std::atomic<bool> usedPrintf{
false};
222 std::atomic<bool> usedAssertVerboseCond{
false};
223 std::atomic<bool> usedStopCond{
false};
224 std::atomic<bool> usedFileDescriptorLib{
false};
226 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
231 : circuitOp(circuitOp), instanceGraph(instanceGraph),
232 enableAnnotationWarning(enableAnnotationWarning),
233 lowerToCore(lowerToCore), verificationFlavor(verificationFlavor),
234 nlaTable(nlaTable), macroTable(macroTable) {
235 auto *
context = circuitOp.getContext();
239 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
240 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
241 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
242 context, dirName.getValue(),
false,
true);
246 if (
auto module = dyn_cast<FModuleLike>(op)) {
258 testHarness =
nullptr;
259 }
else if (dut == testHarness) {
260 testHarness =
nullptr;
265 auto inDUT = [&](igraph::ModuleOpInterface child) {
267 if (
auto inst = instRec->getInstance<InstanceOp>())
268 return inst.getLowerToBind() || inst.getDoNotPrint();
271 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
272 return getInstanceGraph().isAncestor(child, parent, isPhony);
275 circuitOp->walk([&](FModuleLike moduleOp) {
277 dutModules.insert(moduleOp);
281 Operation *getNewModule(Operation *oldModule) {
282 auto it = oldToNewModuleMap.find(oldModule);
283 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
286 Operation *getOldModule(Operation *newModule) {
287 auto it = newToOldModuleMap.find(newModule);
288 return it != newToOldModuleMap.end() ? it->second :
nullptr;
291 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
292 oldToNewModuleMap[oldFMod] = newHWMod;
293 newToOldModuleMap[newHWMod] = oldFMod;
298 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
304 void addBind(sv::BindOp op) {
305 std::lock_guard<std::mutex> lock(bindsMutex);
311 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
314 auto hwAlias = typeAliases.getTypedecl(firAliasType);
317 assert(!typeAliases.isFrozen() &&
318 "type aliases cannot be generated after its frozen");
319 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
322 FModuleLike getDut() {
return dut; }
323 FModuleLike getTestHarness() {
return testHarness; }
329 bool isInDUT(igraph::ModuleOpInterface child) {
330 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
331 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
332 return dutModules.contains(child);
335 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
340 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
347 Type
lowerType(Type type, Location loc) {
348 return ::lowerType(type, loc,
349 [&](Type rawType, BaseTypeAliasType firrtlType,
350 Location typeLoc) -> hw::TypeAliasType {
351 return getTypeAlias(rawType, firrtlType, typeLoc);
357 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
358 auto it = verbatimSourcesByFileName.find(fileName);
359 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
364 void registerVerbatimSource(StringRef fileName,
366 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
367 verbatimSourcesByFileName[fileName] = verbatimOp;
371 emit::FileOp getEmitFileForFile(StringRef fileName) {
372 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
373 auto it = emitFilesByFileName.find(fileName);
374 return it != emitFilesByFileName.end() ? it->second :
nullptr;
379 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
380 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
381 emitFilesByFileName[fileName] = fileOp;
385 friend struct FIRRTLModuleLowering;
386 friend struct FIRRTLLowering;
387 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
388 void operator=(
const CircuitLoweringState &) =
delete;
391 DenseMap<Operation *, Operation *> oldToNewModuleMap;
394 DenseMap<Operation *, Operation *> newToOldModuleMap;
405 DenseSet<igraph::ModuleOpInterface> dutModules;
409 StringSet<> pendingAnnotations;
410 const bool enableAnnotationWarning;
411 std::mutex annotationPrintingMtx;
413 const bool lowerToCore;
418 SmallVector<sv::BindOp> binds;
421 std::mutex bindsMutex;
429 FModuleLike testHarness;
432 hw::OutputFileAttr testBenchDirectory;
436 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
439 SetVector<StringAttr> macroDeclNames;
440 std::mutex macroDeclMutex;
442 void addMacroDecl(StringAttr name) {
443 std::unique_lock<std::mutex> lock(macroDeclMutex);
444 macroDeclNames.insert(name);
449 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
450 llvm::sys::SmartMutex<true> fragmentsMutex;
454 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
457 void addFragment(
hw::HWModuleOp module, FlatSymbolRefAttr fragment) {
458 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
459 fragments[module].insert(fragment);
474 struct RecordTypeAlias {
476 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
478 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
479 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
480 if (iter != firrtlTypeToAliasTypeMap.end())
485 bool isFrozen() {
return frozen; }
487 void freeze() { frozen =
true; }
489 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
491 assert(!frozen &&
"Record already frozen, cannot be updated");
494 auto b = ImplicitLocOpBuilder::atBlockBegin(
496 &circuitOp->getParentRegion()->getBlocks().back());
498 b, b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
499 typeScope.getBodyRegion().push_back(
new Block());
501 auto typeName = firAlias.getName();
506 StringAttr::get(typeName.getContext(),
507 typeDeclNamespace.newName(typeName.getValue()));
509 auto typeScopeBuilder =
510 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
512 typeName, rawType,
nullptr);
513 auto hwAlias = hw::TypeAliasType::get(
514 SymbolRefAttr::get(typeScope.getSymNameAttr(),
515 {FlatSymbolRefAttr::get(typeDecl)}),
517 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
518 assert(insert.second &&
"Entry already exists, insert failed");
519 return insert.first->second;
528 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
536 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
539 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
540 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
543 llvm::StringMap<emit::FileOp> emitFilesByFileName;
544 llvm::sys::SmartMutex<true> emitFilesMutex;
550void CircuitLoweringState::processRemainingAnnotations(
552 if (!enableAnnotationWarning || annoSet.
empty())
554 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
556 for (
auto a : annoSet) {
557 auto inserted = pendingAnnotations.insert(a.getClass());
558 if (!inserted.second)
579 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
584 extractGrandCentralAnnoClass,
587 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
588 extractCoverageAnnoClass,
592 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
593 blackBoxTargetDirAnnoClass))
596 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
597 "' still remaining after LowerToHW");
603struct FIRRTLModuleLowering
604 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
606 void runOnOperation()
override;
607 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
608 void setLowerToCore() { lowerToCore =
true; }
610 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
613 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
615 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
616 SmallVectorImpl<hw::PortInfo> &ports,
617 Operation *moduleOp, StringRef moduleName,
619 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
621 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
624 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
626 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
629 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
632 Block *topLevelModule,
636 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
640 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
642 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
644 LogicalResult lowerFileBody(emit::FileOp op);
651std::unique_ptr<mlir::Pass>
655 auto pass = std::make_unique<FIRRTLModuleLowering>();
656 if (enableAnnotationWarning)
657 pass->setEnableAnnotationWarning();
659 pass->setLowerToCore();
660 pass->verificationFlavor = verificationFlavor;
666void FIRRTLModuleLowering::runOnOperation() {
670 auto *topLevelModule = getOperation().getBody();
674 for (
auto &op : *topLevelModule) {
675 if ((circuit = dyn_cast<CircuitOp>(&op)))
682 auto *circuitBody = circuit.getBodyBlock();
686 CircuitLoweringState state(circuit, enableAnnotationWarning, lowerToCore,
687 verificationFlavor, getAnalysis<InstanceGraph>(),
688 &getAnalysis<NLATable>(),
689 getAnalysis<InstanceChoiceMacroTable>());
691 SmallVector<Operation *, 32> opsToProcess;
694 state.processRemainingAnnotations(circuit, circuitAnno);
697 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
699 TypeSwitch<Operation *, LogicalResult>(&op)
700 .Case<FModuleOp>([&](
auto module) {
701 auto loweredMod = lowerModule(module, topLevelModule, state);
705 state.recordModuleMapping(&op, loweredMod);
706 opsToProcess.push_back(loweredMod);
708 module.walk([&](Operation *op) {
709 for (auto res : op->getResults()) {
711 type_dyn_cast<BaseTypeAliasType>(res.getType()))
712 state.lowerType(aliasType, op->getLoc());
715 return lowerModulePortsAndMoveBody(module, loweredMod, state);
717 .Case<FExtModuleOp>([&](
auto extModule) {
719 lowerExtModule(extModule, topLevelModule, state);
722 state.recordModuleMapping(&op, loweredMod);
725 .Case<FMemModuleOp>([&](
auto memModule) {
727 lowerMemModule(memModule, topLevelModule, state);
730 state.recordModuleMapping(&op, loweredMod);
733 .Case<FormalOp>([&](
auto oldOp) {
734 auto builder = OpBuilder::atBlockEnd(topLevelModule);
735 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
737 oldOp.getParametersAttr());
738 newOp.getBody().emplaceBlock();
739 state.recordModuleMapping(oldOp, newOp);
740 opsToProcess.push_back(newOp);
743 .Case<SimulationOp>([&](
auto oldOp) {
744 auto loc = oldOp.getLoc();
745 auto builder = OpBuilder::atBlockEnd(topLevelModule);
746 auto newOp = verif::SimulationOp::create(
747 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
748 auto &body = newOp.getRegion().emplaceBlock();
749 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
750 body.addArgument(builder.getI1Type(), loc);
751 state.recordModuleMapping(oldOp, newOp);
752 opsToProcess.push_back(newOp);
755 .Case<emit::FileOp>([&](
auto fileOp) {
756 fileOp->moveBefore(topLevelModule, topLevelModule->end());
757 opsToProcess.push_back(fileOp);
760 .Case<OptionOp, OptionCaseOp>([&](
auto) {
764 .Default([&](Operation *op) {
769 op->moveBefore(topLevelModule, topLevelModule->end());
775 return signalPassFailure();
778 state.typeAliases.freeze();
783 SmallVector<Attribute> dutHierarchyFiles;
784 SmallVector<Attribute> testHarnessHierarchyFiles;
785 circuitAnno.removeAnnotations([&](
Annotation annotation) {
786 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
787 auto file = hw::OutputFileAttr::getFromFilename(
789 annotation.
getMember<StringAttr>(
"filename").getValue(),
791 dutHierarchyFiles.push_back(file);
794 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
795 auto file = hw::OutputFileAttr::getFromFilename(
797 annotation.
getMember<StringAttr>(
"filename").getValue(),
801 if (state.getTestHarness())
802 testHarnessHierarchyFiles.push_back(file);
804 dutHierarchyFiles.push_back(file);
810 if (!dutHierarchyFiles.empty())
811 state.getNewModule(state.getDut())
813 ArrayAttr::get(&getContext(), dutHierarchyFiles));
814 if (!testHarnessHierarchyFiles.empty())
815 state.getNewModule(state.getTestHarness())
817 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
821 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
825 return signalPassFailure();
828 for (
auto bind : state.binds) {
833 for (
auto &[module, fragments] : state.fragments)
835 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
838 for (
auto oldNew : state.oldToNewModuleMap)
839 oldNew.first->erase();
841 if (!state.macroDeclNames.empty()) {
842 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
843 for (
auto name : state.macroDeclNames) {
844 sv::MacroDeclOp::create(b, name);
849 lowerFileHeader(circuit, state);
856void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
857 CircuitLoweringState &state) {
860 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
864 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
865 StringRef defineTrue =
"",
866 StringRef defineFalse = StringRef()) {
867 if (!defineFalse.data()) {
868 assert(defineTrue.data() &&
"didn't define anything");
870 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
875 if (defineTrue.data())
876 sv::MacroDefOp::create(b, defName, defineTrue);
878 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
883 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
885 b, guard, [] {}, body);
888 if (state.usedFileDescriptorLib)
889 sv::emitFileDescriptorRuntime(op->getParentOp(), b);
891 if (state.usedPrintf) {
892 sv::MacroDeclOp::create(b,
"PRINTF_COND");
893 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
894 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
895 sv::VerbatimOp::create(
896 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
898 emitGuard(
"PRINTF_COND_", [&]() {
899 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
904 if (state.usedAssertVerboseCond) {
905 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
906 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
907 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
908 sv::VerbatimOp::create(
909 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
910 "gate to assert error printing.");
911 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
912 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
913 "(`ASSERT_VERBOSE_COND)",
"1");
918 if (state.usedStopCond) {
919 sv::MacroDeclOp::create(b,
"STOP_COND");
920 sv::MacroDeclOp::create(b,
"STOP_COND_");
921 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
922 sv::VerbatimOp::create(
923 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
924 "to stop conditions.");
925 emitGuard(
"STOP_COND_", [&]() {
926 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
933FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
934 SmallVectorImpl<hw::PortInfo> &ports,
935 Operation *moduleOp, StringRef moduleName,
937 ports.reserve(firrtlPorts.size());
939 size_t numResults = 0;
940 for (
auto e :
llvm::enumerate(firrtlPorts)) {
942 size_t portNo = e.index();
947 if (firrtlPort.
sym.size() > 1 ||
948 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
949 return emitError(firrtlPort.
loc)
950 <<
"cannot lower aggregate port " << firrtlPort.
name
951 <<
" with field sensitive symbols, HW dialect does not support "
952 "per field symbols yet.";
953 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
955 if (hadDontTouch && !hwPort.
getSym()) {
956 if (hwPort.
type.isInteger(0)) {
957 if (enableAnnotationWarning) {
958 mlir::emitWarning(firrtlPort.
loc)
959 <<
"zero width port " << hwPort.
name
960 <<
" has dontTouch annotation, removing anyway";
966 hw::InnerSymAttr::get(StringAttr::get(
967 moduleOp->getContext(),
968 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
969 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
970 moduleOp->getContext());
975 moduleOp->emitError(
"cannot lower this port type to HW");
981 if (hwPort.
type.isInteger(0)) {
982 auto sym = hwPort.
getSym();
983 if (sym && !sym.empty()) {
984 return mlir::emitError(firrtlPort.
loc)
985 <<
"zero width port " << hwPort.
name
986 <<
" is referenced by name [" << sym
987 <<
"] (e.g. in an XMR) but must be removed";
994 hwPort.
dir = hw::ModulePort::Direction::Output;
995 hwPort.
argNum = numResults++;
996 }
else if (firrtlPort.
isInput()) {
997 hwPort.
dir = hw::ModulePort::Direction::Input;
998 hwPort.
argNum = numArgs++;
1002 hwPort.
type = hw::InOutType::get(hwPort.
type);
1003 hwPort.
dir = hw::ModulePort::Direction::InOut;
1004 hwPort.
argNum = numArgs++;
1006 hwPort.
loc = firrtlPort.
loc;
1007 ports.push_back(hwPort);
1017 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1018 return cast<ParamDeclAttr>(a);
1023 Builder builder(module);
1028 SmallVector<Attribute> newParams;
1029 for (
const ParamDeclAttr &entry : params) {
1030 auto name = entry.getName();
1031 auto type = entry.getType();
1032 auto value = ignoreValues ? Attribute() : entry.getValue();
1034 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1035 newParams.push_back(paramAttr);
1037 return builder.getArrayAttr(newParams);
1040bool FIRRTLModuleLowering::handleForceNameAnnos(
1043 bool failed =
false;
1046 if (!anno.
isClass(forceNameAnnoClass))
1049 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1056 auto diag = oldModule.emitOpError()
1057 <<
"contains a '" << forceNameAnnoClass
1058 <<
"' that is not a non-local annotation";
1059 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1070 auto diag = oldModule.emitOpError()
1071 <<
"contains a '" << forceNameAnnoClass
1072 <<
"' whose non-local symbol, '" << sym
1073 <<
"' does not exist in the circuit";
1074 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1087 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1089 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1090 if (!inserted.second &&
1091 (anno.
getMember(
"name") != (inserted.first->second))) {
1092 auto diag = oldModule.emitError()
1093 <<
"contained multiple '" << forceNameAnnoClass
1094 <<
"' with different names: " << inserted.first->second
1095 <<
" was not " << anno.
getMember(
"name");
1096 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1107 FExtModuleOp oldModule, Block *topLevelModule,
1118 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1119 SmallVector<hw::PortInfo, 8> ports;
1120 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1125 StringRef verilogName;
1126 if (
auto defName = oldModule.getDefname())
1127 verilogName = defName.value();
1129 verilogName = oldModule.getName();
1131 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1133 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1134 if (!filesAttr || filesAttr.empty()) {
1135 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1140 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1141 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1142 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1144 if (!primaryFileContent || !primaryOutputFile) {
1145 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1149 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1150 builder.getContext(), primaryOutputFile.getValue());
1152 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1153 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1156 SmallVector<Attribute> additionalFiles;
1160 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1161 auto file = cast<DictionaryAttr>(filesAttr[i]);
1162 auto content = file.getAs<StringAttr>(
"content");
1163 auto outputFile = file.getAs<StringAttr>(
"output_file");
1164 auto fileName = llvm::sys::path::filename(outputFile);
1166 if (!(content && outputFile)) {
1167 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1175 auto fileSymbolName = circuitNamespace.newName(fileName);
1176 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1177 outputFile.getValue(), fileSymbolName);
1178 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1179 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1180 builder.setInsertionPointAfter(
emitFile);
1183 auto ext = llvm::sys::path::extension(outputFile.getValue());
1184 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1185 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1186 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1187 emitFile->setAttr(
"output_file", outputFileAttr);
1191 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1197 parameters = builder.getArrayAttr({});
1199 if (!verbatimSource) {
1200 verbatimSource = sv::SVVerbatimSourceOp::create(
1201 builder, oldModule.getLoc(),
1202 circuitNamespace.newName(primaryFileName.str()),
1203 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1204 additionalFiles.empty() ?
nullptr
1205 : builder.getArrayAttr(additionalFiles),
1206 builder.getStringAttr(verilogName));
1208 SymbolTable::setSymbolVisibility(
1209 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1211 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1214 return verbatimSource;
1218FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1219 Block *topLevelModule,
1221 if (
auto verbatimMod =
1222 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1228 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1229 SmallVector<hw::PortInfo, 8> ports;
1230 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1234 StringRef verilogName;
1235 if (
auto defName = oldModule.getDefname())
1236 verilogName = defName.value();
1239 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1240 auto nameAttr = builder.getStringAttr(oldModule.getName());
1245 auto newModule = hw::HWModuleExternOp::create(
1246 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1247 SymbolTable::setSymbolVisibility(newModule,
1248 SymbolTable::getSymbolVisibility(oldModule));
1250 bool hasOutputPort =
1251 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1252 if (!hasOutputPort &&
1254 internalVerifBlackBoxAnnoClass) &&
1256 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1259 if (
auto extReqs = oldModule.getExternalRequirements();
1260 extReqs && !extReqs.empty())
1261 newModule->setAttr(
"circt.external_requirements", extReqs);
1266 loweringState.processRemainingAnnotations(oldModule, annos);
1271 FExtModuleOp oldModule, Block *topLevelModule,
1276 auto verbatimSource =
1277 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1279 if (!verbatimSource)
1282 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1283 SmallVector<hw::PortInfo, 8> ports;
1284 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1288 StringRef verilogName;
1289 if (
auto defName = oldModule.getDefname())
1290 verilogName = defName.value();
1292 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1294 auto newModule = sv::SVVerbatimModuleOp::create(
1297 builder.getStringAttr(oldModule.getName()),
1299 FlatSymbolRefAttr::get(verbatimSource),
1300 parameters ? parameters : builder.getArrayAttr({}),
1301 verilogName.empty() ? StringAttr{}
1302 : builder.getStringAttr(verilogName));
1304 SymbolTable::setSymbolVisibility(newModule,
1305 SymbolTable::getSymbolVisibility(oldModule));
1307 bool hasOutputPort =
1308 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1309 if (!hasOutputPort &&
1311 internalVerifBlackBoxAnnoClass) &&
1313 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1316 if (
auto extReqs = oldModule.getExternalRequirements();
1317 extReqs && !extReqs.empty())
1318 newModule->setAttr(
"circt.external_requirements", extReqs);
1323 loweringState.processRemainingAnnotations(oldModule, annos);
1328FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1329 Block *topLevelModule,
1332 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1333 SmallVector<hw::PortInfo, 8> ports;
1334 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1339 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1340 auto newModule = hw::HWModuleExternOp::create(
1341 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1342 oldModule.getModuleNameAttr());
1350FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1353 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1354 SmallVector<hw::PortInfo, 8> ports;
1355 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1360 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1361 auto nameAttr = builder.getStringAttr(oldModule.getName());
1363 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1365 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1366 newModule.setCommentAttr(comment);
1369 SmallVector<StringRef, 13> attrNames = {
1370 "annotations",
"convention",
"layers",
1371 "portNames",
"sym_name",
"portDirections",
1372 "portTypes",
"portAnnotations",
"portSymbols",
1373 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1376 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1377 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1379 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1380 return !attrSet.count(namedAttr.getName()) &&
1381 !newModule->getAttrDictionary().contains(namedAttr.getName());
1383 newAttrs.push_back(i);
1385 newModule->setAttrs(newAttrs);
1389 SymbolTable::setSymbolVisibility(newModule,
1390 SymbolTable::getSymbolVisibility(oldModule));
1396 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1400 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1402 if (!newModule->hasAttr(
"output_file"))
1403 newModule->setAttr(
"output_file", testBenchDir);
1404 newModule->setAttr(
"firrtl.extract.do_not_extract",
1405 builder.getUnitAttr());
1406 newModule.setCommentAttr(
1407 builder.getStringAttr(
"VCS coverage exclude_file"));
1413 loweringState.processRemainingAnnotations(oldModule, annos);
1422 Operation *insertPoint) {
1423 if (!value.hasOneUse())
1426 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1427 if (!attach || attach.getNumOperands() != 2)
1431 auto loweredType =
lowerType(value.getType());
1432 if (loweredType.isInteger(0))
1437 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1438 auto *op = attachedValue.getDefiningOp();
1439 if (op && op->getBlock() == insertPoint->getBlock() &&
1440 !op->isBeforeInBlock(insertPoint))
1445 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1461 if (type_isa<AnalogType>(flipValue.getType()))
1464 Operation *connectOp =
nullptr;
1465 for (
auto &use : flipValue.getUses()) {
1468 if (use.getOperandNumber() != 0)
1470 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1476 connectOp = use.getOwner();
1486 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1487 if (loweredType.isInteger(0))
1492 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1494 auto connectSrc = connectOp->getOperand(1);
1497 if (!isa<FIRRTLType>(connectSrc.getType())) {
1503 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1505 mlir::UnrealizedConversionCastOp::create(
1507 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1513 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1515 if (destTy != connectSrc.getType() &&
1516 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1517 isa<BaseTypeAliasType>(destTy))) {
1519 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1521 if (!destTy.isGround()) {
1523 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1525 }
else if (destTy.getBitWidthOrSentinel() !=
1526 type_cast<FIRRTLBaseType>(connectSrc.getType())
1527 .getBitWidthOrSentinel()) {
1530 auto destWidth = destTy.getBitWidthOrSentinel();
1531 assert(destWidth != -1 &&
"must know integer widths");
1532 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1544 SmallVector<SubfieldOp> accesses;
1545 for (
auto *op : structValue.getUsers()) {
1546 assert(isa<SubfieldOp>(op));
1547 auto fieldAccess = cast<SubfieldOp>(op);
1549 fieldAccess.getInput().getType().base().getElementIndex(field);
1550 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1551 accesses.push_back(fieldAccess);
1559LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1562 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1569 bodyBuilder.setInsertionPoint(cursor);
1572 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1573 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1574 "port count mismatch");
1576 SmallVector<Value, 4> outputs;
1579 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1580 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1582 unsigned nextHWInputArg = 0;
1583 int hwPortIndex = -1;
1584 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1586 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1589 type_isa<FIRRTLBaseType>(port.type) &&
1590 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1594 if (!port.isOutput() && !isZeroWidth) {
1597 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1603 oldArg.replaceAllUsesWith(newArg);
1609 if (isZeroWidth && port.isInput()) {
1611 WireOp::create(bodyBuilder, port.type,
1612 "." + port.getName().str() +
".0width_input")
1614 oldArg.replaceAllUsesWith(newArg);
1622 outputs.push_back(value);
1623 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1629 auto newArg = WireOp::create(bodyBuilder, port.type,
1630 "." + port.getName().str() +
".output");
1633 oldArg.replaceAllUsesWith(newArg.getResult());
1636 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1637 if (!resultHWType.isInteger(0)) {
1640 outputs.push_back(output);
1643 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1644 newArg.setInnerSymAttr(sym);
1645 newModule.setPortSymbolAttr(hwPortIndex, {});
1651 outputOp->setOperands(outputs);
1654 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1655 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1656 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1657 oldBlockInstList.begin(), oldBlockInstList.end());
1668FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1670 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1675 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1676 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1677 auto oldModule = cast<FModuleOp>(
1678 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1679 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1682 SmallVector<Value> symbolicInputs;
1683 for (
auto arg : newModule.getBody().getArguments())
1684 symbolicInputs.push_back(
verif::SymbolicValueOp::create(
1685 builder, arg.
getLoc(), arg.getType(),
1689 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1690 newModule.getNameAttr(), symbolicInputs);
1697FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1699 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1702 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1703 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1704 auto oldModule = cast<FModuleLike>(
1705 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1707 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1711 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1712 newOp.getBody()->args_end());
1713 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1714 newModule.getNameAttr(), inputs);
1715 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1725struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1727 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1728 : theModule(module), circuitState(circuitState),
1729 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1730 backedgeBuilder(builder, module.
getLoc()) {}
1732 LogicalResult
run();
1735 Value getOrCreateClockConstant(seq::ClockConst clock);
1736 Value getOrCreateIntConstant(
const APInt &value);
1737 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1738 bool isSigned =
false) {
1739 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1741 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1742 Attribute getZeroAttributeForType(Type type);
1743 Value getZeroValueForType(Type type);
1744 Value getOrCreateXConstant(
unsigned numBits);
1745 Value getOrCreateZConstant(Type type);
1746 Value getPossiblyInoutLoweredValue(Value value);
1747 Value getLoweredValue(Value value);
1748 Value getLoweredNonClockValue(Value value);
1749 Value getLoweredAndExtendedValue(Value value, Type destType);
1750 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1751 LogicalResult setLowering(Value orig, Value result);
1752 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1753 template <
typename ResultOpType,
typename... CtorArgTypes>
1754 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1755 template <
typename ResultOpType,
typename... CtorArgTypes>
1756 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1757 Backedge createBackedge(Location loc, Type type);
1758 Backedge createBackedge(Value orig, Type type);
1759 bool updateIfBackedge(Value dest, Value src);
1762 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1767 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1768 if (forceable.isForceable())
1776 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1777 auto attr = op.getInnerSymAttr();
1781 if (requiresInnerSymbol(op))
1783 op.getContext(), attr, 0,
1791 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1792 Operation *instanceOp,
1793 SmallVectorImpl<Value> &inputOperands);
1795 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1799 Value getReadValue(Value v);
1801 Value getNonClockValue(Value v);
1803 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1804 sv::ResetType resetStyle, sv::EventControl resetEdge,
1805 Value reset,
const std::function<
void(
void)> &body = {},
1806 const std::function<void(
void)> &resetBody = {});
1807 void addToAlwaysBlock(Value clock,
1808 const std::function<
void(
void)> &body = {}) {
1809 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1810 sv::EventControl(), Value(), body,
1811 std::function<
void(
void)>());
1814 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1815 std::function<
void(
void)>
emit);
1816 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1817 std::function<
void(
void)> elseCtor = {});
1818 void addToInitialBlock(std::function<
void(
void)> body);
1819 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1820 std::function<
void(
void)> elseCtor = {});
1821 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1823 bool allowTruncate);
1824 Value createArrayIndexing(Value array, Value index);
1825 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1827 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1828 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1829 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1832 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1833 UnloweredOpResult handleUnloweredOp(Operation *op);
1834 LogicalResult visitExpr(ConstantOp op);
1835 LogicalResult visitExpr(SpecialConstantOp op);
1836 LogicalResult visitExpr(SubindexOp op);
1837 LogicalResult visitExpr(SubaccessOp op);
1838 LogicalResult visitExpr(SubfieldOp op);
1839 LogicalResult visitExpr(VectorCreateOp op);
1840 LogicalResult visitExpr(BundleCreateOp op);
1841 LogicalResult visitExpr(FEnumCreateOp op);
1842 LogicalResult visitExpr(AggregateConstantOp op);
1843 LogicalResult visitExpr(IsTagOp op);
1844 LogicalResult visitExpr(SubtagOp op);
1845 LogicalResult visitExpr(TagExtractOp op);
1848 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1849 return visitUnrealizedConversionCast(castOp);
1854 LogicalResult visitDecl(WireOp op);
1855 LogicalResult visitDecl(NodeOp op);
1856 LogicalResult visitDecl(RegOp op);
1857 LogicalResult visitDecl(RegResetOp op);
1858 LogicalResult visitDecl(MemOp op);
1859 LogicalResult visitDecl(InstanceOp oldInstance);
1860 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1861 LogicalResult visitDecl(VerbatimWireOp op);
1862 LogicalResult visitDecl(ContractOp op);
1865 LogicalResult lowerNoopCast(Operation *op);
1866 LogicalResult visitExpr(AsSIntPrimOp op);
1867 LogicalResult visitExpr(AsUIntPrimOp op);
1868 LogicalResult visitExpr(AsClockPrimOp op);
1869 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1871 LogicalResult visitExpr(HWStructCastOp op);
1872 LogicalResult visitExpr(BitCastOp op);
1874 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1875 LogicalResult visitExpr(CvtPrimOp op);
1876 LogicalResult visitExpr(NotPrimOp op);
1877 LogicalResult visitExpr(NegPrimOp op);
1878 LogicalResult visitExpr(PadPrimOp op);
1879 LogicalResult visitExpr(XorRPrimOp op);
1880 LogicalResult visitExpr(AndRPrimOp op);
1881 LogicalResult visitExpr(OrRPrimOp op);
1884 template <
typename ResultUnsignedOpType,
1885 typename ResultSignedOpType = ResultUnsignedOpType>
1886 LogicalResult lowerBinOp(Operation *op);
1887 template <
typename ResultOpType>
1888 LogicalResult lowerBinOpToVariadic(Operation *op);
1890 template <
typename ResultOpType>
1891 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1893 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1894 ICmpPredicate unsignedOp);
1895 template <
typename SignedOp,
typename Un
signedOp>
1896 LogicalResult lowerDivLikeOp(Operation *op);
1898 LogicalResult visitExpr(CatPrimOp op);
1900 LogicalResult visitExpr(AndPrimOp op) {
1901 return lowerBinOpToVariadic<comb::AndOp>(op);
1903 LogicalResult visitExpr(OrPrimOp op) {
1904 return lowerBinOpToVariadic<comb::OrOp>(op);
1906 LogicalResult visitExpr(XorPrimOp op) {
1907 return lowerBinOpToVariadic<comb::XorOp>(op);
1909 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1910 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1912 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1913 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1915 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1916 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1918 LogicalResult visitExpr(AddPrimOp op) {
1919 return lowerBinOpToVariadic<comb::AddOp>(op);
1921 LogicalResult visitExpr(EQPrimOp op) {
1922 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1924 LogicalResult visitExpr(NEQPrimOp op) {
1925 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1927 LogicalResult visitExpr(LTPrimOp op) {
1928 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1930 LogicalResult visitExpr(LEQPrimOp op) {
1931 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1933 LogicalResult visitExpr(GTPrimOp op) {
1934 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1936 LogicalResult visitExpr(GEQPrimOp op) {
1937 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1940 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1941 LogicalResult visitExpr(MulPrimOp op) {
1942 return lowerBinOpToVariadic<comb::MulOp>(op);
1944 LogicalResult visitExpr(DivPrimOp op) {
1945 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1947 LogicalResult visitExpr(RemPrimOp op) {
1948 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1952 LogicalResult visitExpr(IsXIntrinsicOp op);
1953 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1954 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1955 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1956 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1957 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1958 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1959 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1960 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1961 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1962 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1963 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1964 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1965 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1966 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1967 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1968 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1969 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1970 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1971 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1972 LogicalResult visitExpr(LTLPastIntrinsicOp op);
1973 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1975 template <
typename TargetOp,
typename IntrinsicOp>
1976 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1977 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1978 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1979 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1980 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1981 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1982 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1983 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1986 LogicalResult visitExpr(BitsPrimOp op);
1987 LogicalResult visitExpr(InvalidValueOp op);
1988 LogicalResult visitExpr(HeadPrimOp op);
1989 LogicalResult visitExpr(ShlPrimOp op);
1990 LogicalResult visitExpr(ShrPrimOp op);
1991 LogicalResult visitExpr(DShlPrimOp op) {
1992 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
1994 LogicalResult visitExpr(DShrPrimOp op) {
1995 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
1997 LogicalResult visitExpr(DShlwPrimOp op) {
1998 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2000 LogicalResult visitExpr(TailPrimOp op);
2001 LogicalResult visitExpr(MuxPrimOp op);
2002 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2003 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2004 LogicalResult visitExpr(MultibitMuxOp op);
2005 LogicalResult visitExpr(VerbatimExprOp op);
2006 LogicalResult visitExpr(XMRRefOp op);
2007 LogicalResult visitExpr(XMRDerefOp op);
2010 LogicalResult visitExpr(TimeOp op);
2011 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2014 LogicalResult lowerVerificationStatement(
2015 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2016 Value enable, StringAttr messageAttr, ValueRange operands,
2017 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2018 LogicalResult lowerVerificationStatementToCore(
2019 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2020 Value enable, StringAttr nameAttr, EventControl eventControl);
2022 LogicalResult visitStmt(SkipOp op);
2024 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2025 LogicalResult visitStmt(ConnectOp op);
2026 LogicalResult visitStmt(MatchingConnectOp op);
2027 LogicalResult visitStmt(ForceOp op);
2029 std::optional<Value> getLoweredFmtOperand(Value operand);
2030 LogicalResult loweredFmtOperands(ValueRange operands,
2031 SmallVectorImpl<Value> &loweredOperands);
2032 FailureOr<Value> lowerSimFormatString(StringRef originalFormatString,
2033 ValueRange operands);
2034 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2038 LogicalResult lowerStatementWithFd(
2039 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2040 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2044 LogicalResult visitPrintfLike(T op,
2045 const FileDescriptorInfo &fileDescriptorInfo,
2046 bool usePrintfCond);
2047 LogicalResult visitStmt(PrintFOp op);
2048 LogicalResult visitStmt(FPrintFOp op);
2049 LogicalResult visitStmt(FFlushOp op);
2050 LogicalResult visitStmt(StopOp op);
2051 LogicalResult visitStmt(AssertOp op);
2052 LogicalResult visitStmt(AssumeOp op);
2053 LogicalResult visitStmt(CoverOp op);
2054 LogicalResult visitStmt(AttachOp op);
2055 LogicalResult visitStmt(RefForceOp op);
2056 LogicalResult visitStmt(RefForceInitialOp op);
2057 LogicalResult visitStmt(RefReleaseOp op);
2058 LogicalResult visitStmt(RefReleaseInitialOp op);
2059 LogicalResult visitStmt(BindOp op);
2061 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2062 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2063 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2065 LogicalResult fixupLTLOps();
2068 return circuitState.lowerType(type, builder.getLoc());
2076 CircuitLoweringState &circuitState;
2079 ImplicitLocOpBuilder builder;
2084 DenseMap<Value, Value> valueMapping;
2088 DenseMap<Value, Value> fromClockMapping;
2092 DenseMap<Attribute, Value> hwConstantMap;
2093 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2097 DenseMap<unsigned, Value> hwConstantXMap;
2098 DenseMap<Type, Value> hwConstantZMap;
2104 DenseMap<Value, Value> readInOutCreated;
2108 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2109 sv::ResetType, sv::EventControl, Value>;
2133 DenseSet<Operation *> maybeUnusedValues;
2135 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2136 void maybeUnused(Value value) {
2137 if (
auto *op = value.getDefiningOp())
2149 SetVector<Operation *> ltlOpFixupWorklist;
2154 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2156 void addToWorklist(Block &block) {
2157 worklist.push_back({block.begin(), block.end()});
2159 void addToWorklist(Region ®ion) {
2160 for (
auto &block :
llvm::reverse(region))
2161 addToWorklist(block);
2172LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2173 OpBuilder b(&getContext());
2174 fileOp->walk([&](Operation *op) {
2175 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2176 b.setInsertionPointAfter(bindOp);
2177 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2185FIRRTLModuleLowering::lowerBody(Operation *op,
2187 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2189 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2191 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2193 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2194 return lowerFileBody(fileOp);
2199LogicalResult FIRRTLLowering::run() {
2202 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2203 if (failed(setLowering(arg, arg)))
2210 addToWorklist(theModule.getBody());
2211 SmallVector<Operation *, 16> opsToRemove;
2213 while (!worklist.empty()) {
2214 auto &[opsIt, opsEnd] = worklist.back();
2215 if (opsIt == opsEnd) {
2216 worklist.pop_back();
2219 Operation *op = &*opsIt++;
2221 builder.setInsertionPoint(op);
2222 builder.setLoc(op->getLoc());
2223 auto done = succeeded(dispatchVisitor(op));
2224 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2226 opsToRemove.push_back(op);
2228 switch (handleUnloweredOp(op)) {
2229 case AlreadyLowered:
2232 opsToRemove.push_back(op);
2234 case LoweringFailure:
2246 for (
auto &[backedge, value] : backedges) {
2247 SmallVector<Location> driverLocs;
2253 if (backedge == value) {
2254 Location edgeLoc = backedge.getLoc();
2255 if (driverLocs.empty()) {
2256 mlir::emitError(edgeLoc,
"sink does not have a driver");
2258 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2259 for (
auto loc : driverLocs)
2260 diag.attachNote(loc) <<
"through driver here";
2266 auto *it = backedges.find(value);
2267 if (it == backedges.end())
2270 driverLocs.push_back(value.getLoc());
2273 if (
auto *defOp = backedge.getDefiningOp())
2274 maybeUnusedValues.erase(defOp);
2275 backedge.replaceAllUsesWith(value);
2283 while (!opsToRemove.empty()) {
2284 auto *op = opsToRemove.pop_back_val();
2289 for (
auto result : op->getResults()) {
2293 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2295 builder.getIntegerType(0), 0);
2296 maybeUnusedValues.insert(zeroI0);
2298 result.replaceAllUsesWith(zeroI0);
2301 if (!op->use_empty()) {
2302 auto d = op->emitOpError(
2303 "still has uses; should remove ops in reverse order of visitation");
2304 SmallPtrSet<Operation *, 2> visited;
2305 for (
auto *user : op->getUsers())
2306 if (visited.insert(user).second)
2308 <<
"used by " << user->
getName() <<
" op";
2311 maybeUnusedValues.erase(op);
2317 SmallVector<Operation *> worklist(maybeUnusedValues.begin(),
2318 maybeUnusedValues.end());
2319 while (!worklist.empty()) {
2320 auto *op = worklist.pop_back_val();
2321 maybeUnusedValues.erase(op);
2322 if (!isOpTriviallyDead(op))
2324 for (
auto operand : op->getOperands())
2325 if (auto *defOp = operand.getDefiningOp())
2326 if (maybeUnusedValues.insert(defOp).second)
2327 worklist.push_back(defOp);
2333 if (failed(fixupLTLOps()))
2344Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2345 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2347 auto &entry = hwConstantMap[attr];
2351 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2352 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2358Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2359 auto attr = builder.getIntegerAttr(
2360 builder.getIntegerType(value.getBitWidth()), value);
2362 auto &entry = hwConstantMap[attr];
2366 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2373Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2376 if (hw::type_isa<IntegerType>(type))
2377 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2379 auto cache = hwAggregateConstantMap.lookup({value, type});
2384 SmallVector<Attribute> values;
2385 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2387 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2388 subType = array.getElementType();
2389 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2390 subType = structType.getElements()[e.index()].type;
2392 assert(
false &&
"type must be either array or struct");
2394 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2398 if (hw::type_isa<hw::ArrayType>(type))
2399 std::reverse(values.begin(), values.end());
2401 auto &entry = hwAggregateConstantMap[{value, type}];
2402 entry = builder.getArrayAttr(values);
2410 const std::function<LogicalResult()> &fn) {
2411 assert(failedOperand &&
"Should be called on the failed operand");
2419Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2421 auto &entry = hwConstantXMap[numBits];
2425 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2426 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2427 entryBuilder.getIntegerType(numBits));
2431Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2432 auto &entry = hwConstantZMap[type];
2434 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2435 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2449Attribute FIRRTLLowering::getZeroAttributeForType(Type type) {
2450 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2451 return builder.getIntegerAttr(intType, 0);
2452 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type)) {
2456 auto element = getZeroAttributeForType(array.getElementType());
2457 SmallVector<Attribute> values(array.getNumElements(), element);
2458 return builder.getArrayAttr(values);
2460 if (
auto structType = hw::type_dyn_cast<hw::StructType>(type)) {
2461 SmallVector<Attribute> values;
2462 values.reserve(structType.getElements().size());
2463 for (
auto &field : structType.getElements())
2464 values.push_back(getZeroAttributeForType(field.type));
2465 return builder.getArrayAttr(values);
2467 llvm_unreachable(
"unsupported lowered type for zero attribute");
2473Value FIRRTLLowering::getZeroValueForType(Type type) {
2474 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2475 return getOrCreateIntConstant(intType.getWidth(), 0);
2476 return hw::AggregateConstantOp::create(
2477 builder, type, cast<ArrayAttr>(getZeroAttributeForType(type)));
2484Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2486 if (
auto lowering = valueMapping.lookup(value)) {
2487 assert(!isa<FIRRTLType>(lowering.getType()) &&
2488 "Lowered value should be a non-FIRRTL value");
2497Value FIRRTLLowering::getLoweredValue(Value value) {
2498 auto result = getPossiblyInoutLoweredValue(value);
2504 if (isa<hw::InOutType>(result.getType()))
2505 return getReadValue(result);
2511Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2512 auto result = getLoweredValue(value);
2516 if (hw::type_isa<seq::ClockType>(result.getType()))
2517 return getNonClockValue(result);
2525Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2528 bool allowTruncate) {
2529 SmallVector<Value> resultBuffer;
2534 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2535 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2536 auto resultType = builder.getIntegerType(destWidth);
2538 if (srcWidth == destWidth)
2541 if (srcWidth > destWidth) {
2545 builder.emitError(
"operand should not be a truncation");
2549 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2550 return comb::createOrFoldSExt(builder, value, resultType);
2551 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2559 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2560 .Case<FVectorType>([&](
auto srcVectorType) {
2561 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2562 unsigned size = resultBuffer.size();
2563 unsigned indexWidth =
2565 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2566 destVectorType.getNumElements());
2568 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2570 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2571 destVectorType.getElementType())))
2574 SmallVector<Value> temp(resultBuffer.begin() + size,
2575 resultBuffer.end());
2577 resultBuffer.resize(size);
2578 resultBuffer.push_back(array);
2581 .Case<BundleType>([&](BundleType srcStructType) {
2582 auto destStructType = firrtl::type_cast<BundleType>(destType);
2583 unsigned size = resultBuffer.size();
2586 if (destStructType.getNumElements() != srcStructType.getNumElements())
2589 for (
auto elem :
llvm::enumerate(destStructType)) {
2590 auto structExtract =
2592 if (failed(recurse(structExtract,
2593 srcStructType.getElementType(elem.index()),
2594 destStructType.getElementType(elem.index()))))
2597 SmallVector<Value> temp(resultBuffer.begin() + size,
2598 resultBuffer.end());
2601 resultBuffer.resize(size);
2602 resultBuffer.push_back(newStruct);
2605 .Case<IntType>([&](
auto) {
2606 if (
auto result = cast(src, srcType, destType)) {
2607 resultBuffer.push_back(result);
2612 .Default([&](
auto) {
return failure(); });
2615 if (failed(recurse(array, sourceType, destType)))
2618 assert(resultBuffer.size() == 1 &&
2619 "resultBuffer must only contain a result array if `success` is true");
2620 return resultBuffer[0];
2628Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2629 auto srcType = cast<FIRRTLBaseType>(src.getType());
2630 auto dstType = cast<FIRRTLBaseType>(target);
2631 auto loweredSrc = getLoweredValue(src);
2634 auto dstWidth = dstType.getBitWidthOrSentinel();
2650 return getOrCreateIntConstant(dstWidth, 0);
2653 auto loweredSrcType = loweredSrc.getType();
2654 auto loweredDstType =
lowerType(dstType);
2657 if (loweredSrcType == loweredDstType)
2661 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2663 if (loweredSrcType != loweredDstType &&
2664 (isa<hw::TypeAliasType>(loweredSrcType) ||
2665 isa<hw::TypeAliasType>(loweredDstType))) {
2666 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2671 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2672 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2675 if (isa<seq::ClockType>(loweredSrcType)) {
2676 builder.emitError(
"cannot use clock type as an integer");
2680 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2681 if (!intSourceType) {
2682 builder.emitError(
"operand of type ")
2683 << loweredSrcType <<
" cannot be used as an integer";
2687 auto loweredSrcWidth = intSourceType.getWidth();
2688 if (loweredSrcWidth ==
unsigned(dstWidth))
2691 if (loweredSrcWidth >
unsigned(dstWidth)) {
2692 builder.emitError(
"operand should not be a truncation");
2697 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2698 if (type_cast<IntType>(valueFIRType).isSigned())
2699 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2701 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2710Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2711 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2712 type_isa<FIRRTLBaseType>(destType) &&
2713 "input/output value should be FIRRTL");
2716 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2717 if (destWidth == -1)
2720 auto result = getLoweredValue(value);
2732 return getOrCreateIntConstant(destWidth, 0);
2736 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2738 if (destType == value.getType())
2741 return getExtOrTruncAggregateValue(
2742 result, type_cast<FIRRTLBaseType>(value.getType()),
2743 type_cast<FIRRTLBaseType>(destType),
2747 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2748 if (srcWidth ==
unsigned(destWidth))
2754 if (srcWidth >
unsigned(destWidth)) {
2755 auto resultType = builder.getIntegerType(destWidth);
2759 auto resultType = builder.getIntegerType(destWidth);
2763 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2764 if (type_cast<IntType>(valueFIRType).isSigned())
2765 return comb::createOrFoldSExt(builder, result, resultType);
2767 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2781std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2783 if (type_isa<FStringType>(operand.getType())) {
2784 if (isa<TimeOp>(operand.getDefiningOp()))
2785 return sv::TimeOp::create(builder);
2786 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2790 auto loweredValue = getLoweredValue(operand);
2791 if (!loweredValue) {
2795 loweredValue = getOrCreateIntConstant(1, 0);
2800 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2801 if (intTy.isSigned())
2802 loweredValue = sv::SystemFunctionOp::create(
2803 builder, loweredValue.getType(),
"signed", loweredValue);
2805 return loweredValue;
2809FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2810 SmallVectorImpl<Value> &loweredOperands) {
2811 for (
auto operand : operands) {
2812 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2817 loweredOperands.push_back(*loweredValue);
2823FIRRTLLowering::lowerSimFormatString(StringRef originalFormatString,
2824 ValueRange operands) {
2825 SmallVector<Value> fragments;
2827 auto emitLiteral = [&](StringRef text) {
2829 fragments.push_back(sim::FormatLiteralOp::create(builder, text));
2832 auto emitIntFormat = [&](Value operand,
char specifier,
2833 IntegerAttr widthAttr) -> FailureOr<Value> {
2835 if (type_isa<ClockType>(operand.getType()))
2836 loweredValue = getLoweredNonClockValue(operand);
2838 loweredValue = getLoweredValue(operand);
2839 if (!loweredValue) {
2842 loweredValue = getOrCreateIntConstant(1, 0);
2845 if (!mlir::isa<IntegerType>(loweredValue.getType())) {
2846 emitError(builder.getLoc(),
"lower-to-core requires integer printf "
2848 << specifier <<
"'";
2852 switch (specifier) {
2854 return sim::FormatBinOp::create(builder, loweredValue,
2855 builder.getBoolAttr(
false),
2856 builder.getI8IntegerAttr(
'0'), widthAttr)
2859 UnitAttr signedAttr;
2860 if (
auto intTy = dyn_cast<IntType>(operand.getType());
2861 intTy && intTy.isSigned())
2862 signedAttr = builder.getUnitAttr();
2863 return sim::FormatDecOp::create(
2864 builder, loweredValue, builder.getBoolAttr(
false),
2865 builder.getI8IntegerAttr(
' '), widthAttr, signedAttr)
2869 return sim::FormatHexOp::create(builder, loweredValue,
2870 builder.getBoolAttr(
false),
2871 builder.getBoolAttr(
false),
2872 builder.getI8IntegerAttr(
'0'), widthAttr)
2875 return sim::FormatCharOp::create(builder, loweredValue).getResult();
2877 llvm_unreachable(
"unsupported FIRRTL format specifier");
2881 SmallString<32> literal;
2882 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
2883 char c = originalFormatString[i];
2886 emitLiteral(literal);
2889 SmallString<6> width;
2890 c = originalFormatString[++i];
2893 c = originalFormatString[++i];
2896 IntegerAttr widthAttr;
2897 if (!width.empty()) {
2898 unsigned widthValue;
2899 if (StringRef(width).getAsInteger(10, widthValue)) {
2900 emitError(builder.getLoc(),
"invalid FIRRTL printf width");
2903 widthAttr = builder.getI32IntegerAttr(widthValue);
2907 if (!width.empty()) {
2908 emitError(builder.getLoc(),
2909 "literal percents ('%%') may not specify a width");
2912 literal.push_back(
'%');
2916 if (operands.size() <= subIdx) {
2917 emitError(builder.getLoc(),
"not enough operands for printf format");
2921 if (c ==
'c' && widthAttr) {
2922 emitError(builder.getLoc(),
"ASCII character format specifiers ('%c') "
2923 "may not specify a width");
2932 auto fragment = emitIntFormat(operands[subIdx++], c, widthAttr);
2933 if (failed(fragment))
2935 fragments.push_back(*fragment);
2939 emitError(builder.getLoc(),
"unknown printf substitution '%")
2940 << width << c <<
"'";
2946 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
2947 literal.push_back(c);
2951 emitLiteral(literal);
2954 if (operands.size() <= subIdx) {
2955 emitError(builder.getLoc(),
"not enough operands for printf format");
2959 auto substitution = operands[subIdx++];
2960 if (!type_isa<FStringType>(substitution.getType())) {
2961 emitError(builder.getLoc(),
"expected fstring operand for '{{}}' "
2967 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
2968 .template Case<HierarchicalModuleNameOp>([&](
auto) {
2969 fragments.push_back(sim::FormatHierPathOp::create(
2973 .
template Case<TimeOp>([&](
auto) {
2974 fragments.push_back(sim::FormatCurrentTimeOp::create(builder));
2977 .Default([&](
auto) {
2978 emitError(builder.getLoc(),
"has a substitution with "
2981 .attachNote(substitution.getLoc())
2982 <<
"op with an unimplemented lowering is here";
2991 literal.push_back(c);
2996 emitLiteral(literal);
2997 if (fragments.empty())
2998 return sim::FormatLiteralOp::create(builder,
"").getResult();
2999 if (fragments.size() == 1)
3000 return fragments.front();
3001 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
3004LogicalResult FIRRTLLowering::lowerStatementWithFd(
3005 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
3006 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
3008 bool failed =
false;
3009 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
3010 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
3011 addToAlwaysBlock(clock, [&]() {
3014 circuitState.usedPrintf =
true;
3016 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
3019 Value ifCond = cond;
3020 if (usePrintfCond) {
3022 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
3023 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
3026 addIfProceduralBlock(ifCond, [&]() {
3030 if (fileDescriptor.isDefaultFd()) {
3032 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
3035 auto fdOrError = callFileDescriptorLib(fileDescriptor);
3036 if (llvm::failed(fdOrError)) {
3042 failed = llvm::failed(fn(fd));
3046 return failure(failed);
3050FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3051 circuitState.usedFileDescriptorLib =
true;
3052 circuitState.addFragment(
3053 theModule, sv::getFileDescriptorFragmentRef(builder.getContext()));
3056 if (
info.isSubstitutionRequired()) {
3057 SmallVector<Value> fileNameOperands;
3058 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3061 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3066 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3070 return sv::createProceduralFileDescriptorGetterCall(builder, builder.getLoc(),
3080LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3081 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3082 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3083 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3087 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3090 if (srcWidth != -1) {
3092 assert((srcWidth != 0) &&
3093 "Lowering produced value for zero width source");
3095 assert((srcWidth == 0) &&
3096 "Lowering produced null value but source wasn't zero width");
3100 assert(result &&
"Lowering of foreign type produced null value");
3103 auto &slot = valueMapping[orig];
3104 assert(!slot &&
"value lowered multiple times");
3111LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3115 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3116 auto &entry = hwConstantMap[cst.getValueAttr()];
3127 cst->moveBefore(&theModule.getBodyBlock()->front());
3131 return setLowering(orig, result);
3136template <
typename ResultOpType,
typename... CtorArgTypes>
3137LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3138 CtorArgTypes... args) {
3139 auto result = builder.createOrFold<ResultOpType>(args...);
3140 if (
auto *op = result.getDefiningOp())
3142 return setPossiblyFoldedLowering(orig->getResult(0), result);
3149template <
typename ResultOpType,
typename... CtorArgTypes>
3150LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3151 CtorArgTypes... args) {
3152 auto result = builder.createOrFold<ResultOpType>(args...);
3153 if (
auto *op = result.getDefiningOp())
3154 ltlOpFixupWorklist.insert(op);
3155 return setPossiblyFoldedLowering(orig->getResult(0), result);
3164Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3165 auto backedge = backedgeBuilder.
get(type, loc);
3166 backedges.insert({backedge, backedge});
3174Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3175 auto backedge = createBackedge(orig.getLoc(), type);
3176 (void)setLowering(orig, backedge);
3182bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3183 auto backedgeIt = backedges.find(dest);
3184 if (backedgeIt == backedges.end())
3186 backedgeIt->second = src;
3194void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3195 const std::function<
void(
void)> &fn, Region ®ion) {
3199 auto oldIP = builder.saveInsertionPoint();
3201 builder.setInsertionPointToEnd(®ion.front());
3203 builder.restoreInsertionPoint(oldIP);
3207Value FIRRTLLowering::getReadValue(Value v) {
3208 Value result = readInOutCreated.lookup(v);
3214 auto oldIP = builder.saveInsertionPoint();
3215 if (
auto *vOp = v.getDefiningOp()) {
3216 builder.setInsertionPointAfter(vOp);
3220 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3225 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3226 result = getReadValue(arrayIndexInout.getInput());
3228 arrayIndexInout.getIndex());
3233 builder.restoreInsertionPoint(oldIP);
3234 readInOutCreated.insert({v, result});
3238Value FIRRTLLowering::getNonClockValue(Value v) {
3239 auto it = fromClockMapping.try_emplace(v, Value{});
3241 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3242 builder.setInsertionPointAfterValue(v);
3243 it.first->second = seq::FromClockOp::create(builder, v);
3245 return it.first->second;
3248void FIRRTLLowering::addToAlwaysBlock(
3249 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3250 sv::EventControl resetEdge, Value reset,
3251 const std::function<
void(
void)> &body,
3252 const std::function<
void(
void)> &resetBody) {
3253 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3254 resetStyle, resetEdge, reset};
3255 sv::AlwaysOp alwaysOp;
3256 sv::IfOp insideIfOp;
3257 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3261 assert(resetStyle != sv::ResetType::NoReset);
3274 auto createIfOp = [&]() {
3277 insideIfOp = sv::IfOp::create(
3278 builder, reset, [] {}, [] {});
3280 if (resetStyle == sv::ResetType::AsyncReset) {
3281 sv::EventControl events[] = {clockEdge, resetEdge};
3282 Value clocks[] = {clock, reset};
3284 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3285 if (resetEdge == sv::EventControl::AtNegEdge)
3286 llvm_unreachable(
"negative edge for reset is not expected");
3290 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3294 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3295 insideIfOp =
nullptr;
3297 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3301 assert(insideIfOp &&
"reset body must be initialized before");
3302 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3303 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3305 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3311 alwaysOp->moveBefore(builder.getInsertionBlock(),
3312 builder.getInsertionPoint());
3315LogicalResult FIRRTLLowering::emitGuards(Location loc,
3316 ArrayRef<Attribute> guards,
3317 std::function<
void(
void)>
emit) {
3318 if (guards.empty()) {
3322 auto guard = dyn_cast<StringAttr>(guards[0]);
3324 return mlir::emitError(loc,
3325 "elements in `guards` array must be `StringAttr`");
3328 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3329 LogicalResult result = LogicalResult::failure();
3330 addToIfDefBlock(guard.getValue(), [&]() {
3331 result = emitGuards(loc, guards.drop_front(), emit);
3336void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3337 std::function<
void(
void)> thenCtor,
3338 std::function<
void(
void)> elseCtor) {
3339 auto condAttr = builder.getStringAttr(cond);
3340 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3342 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3343 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3348 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3350 ifdefBlocks[{builder.getBlock(), condAttr}] =
3351 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3355void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3356 auto op = initialBlocks.lookup(builder.getBlock());
3358 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3363 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3365 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3369void FIRRTLLowering::addIfProceduralBlock(Value cond,
3370 std::function<
void(
void)> thenCtor,
3371 std::function<
void(
void)> elseCtor) {
3374 auto insertIt = builder.getInsertionPoint();
3375 if (insertIt != builder.getBlock()->begin())
3376 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3377 if (ifOp.getCond() == cond) {
3378 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3379 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3384 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3396FIRRTLLowering::UnloweredOpResult
3397FIRRTLLowering::handleUnloweredOp(Operation *op) {
3399 if (!op->getRegions().empty() &&
3400 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3401 op->emitOpError(
"must explicitly handle its regions");
3402 return LoweringFailure;
3409 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3411 for (
auto ®ion : op->getRegions())
3412 addToWorklist(region);
3413 for (
auto &operand : op->getOpOperands())
3414 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3415 operand.set(lowered);
3416 for (
auto result : op->getResults())
3417 (void)setLowering(result, result);
3418 return AlreadyLowered;
3430 if (op->getNumResults() == 1) {
3431 auto resultType = op->getResult(0).getType();
3432 if (type_isa<FIRRTLBaseType>(resultType) &&
3434 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3436 (void)setLowering(op->getResult(0), Value());
3440 op->emitOpError(
"LowerToHW couldn't handle this operation");
3441 return LoweringFailure;
3444LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3447 return setLowering(op, Value());
3449 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3452LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3454 if (isa<ClockType>(op.getType())) {
3455 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3456 :
seq::ClockConst::Low);
3458 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3460 return setLowering(op, cst);
3463FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3464 auto iIdx = getOrCreateIntConstant(
3466 firrtl::type_cast<FVectorType>(op.getInput().getType())
3473 if (isa<sv::InOutType>(input.getType()))
3474 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3477 if (
auto *definingOp = result.getDefiningOp())
3482FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3483 Value valueIdx = getLoweredAndExtOrTruncValue(
3485 UIntType::get(op->getContext(),
3487 firrtl::type_cast<FVectorType>(op.getInput().getType())
3488 .getNumElements())));
3490 op->emitError() <<
"input lowering failed";
3497 if (isa<sv::InOutType>(input.getType()))
3498 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3500 result = createArrayIndexing(input, valueIdx);
3501 if (
auto *definingOp = result.getDefiningOp())
3506FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3507 auto resultType =
lowerType(op->getResult(0).getType());
3508 if (!resultType || !input) {
3509 op->emitError() <<
"subfield type lowering failed";
3515 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3516 .getElementName(op.getFieldIndex());
3518 if (isa<sv::InOutType>(input.getType()))
3519 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3522 if (
auto *definingOp = result.getDefiningOp())
3527LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3529 return setLowering(op, Value());
3531 auto input = getPossiblyInoutLoweredValue(op.getInput());
3533 return op.emitError() <<
"input lowering failed";
3535 auto result = lowerSubindex(op, input);
3538 return setLowering(op, *result);
3541LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3543 return setLowering(op, Value());
3545 auto input = getPossiblyInoutLoweredValue(op.getInput());
3547 return op.emitError() <<
"input lowering failed";
3549 auto result = lowerSubaccess(op, input);
3552 return setLowering(op, *result);
3555LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3558 if (getLoweredValue(op) || !op.getInput())
3562 return setLowering(op, Value());
3564 auto input = getPossiblyInoutLoweredValue(op.getInput());
3566 return op.emitError() <<
"input lowering failed";
3568 auto result = lowerSubfield(op, input);
3571 return setLowering(op, *result);
3574LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3575 auto resultType =
lowerType(op.getResult().getType());
3576 auto arrayType = cast<hw::ArrayType>(resultType);
3577 SmallVector<Value> operands;
3579 for (
auto oper :
llvm::reverse(op.getOperands())) {
3580 auto val = getLoweredValue(oper);
3585 val = getZeroValueForType(arrayType.getElementType());
3587 operands.push_back(val);
3589 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3592LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3593 auto resultType =
lowerType(op.getResult().getType());
3594 auto structType = cast<hw::StructType>(resultType);
3595 SmallVector<Value> operands;
3596 for (
auto [oper, field] :
3597 llvm::zip_equal(op.getOperands(), structType.getElements())) {
3598 auto val = getLoweredValue(oper);
3603 val = getZeroValueForType(field.type);
3605 operands.push_back(val);
3607 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3610LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3613 return setLowering(op, Value());
3615 auto input = getLoweredValue(op.getInput());
3616 auto tagName = op.getFieldNameAttr();
3617 auto oldType = op.getType().base();
3619 auto element = *oldType.getElement(op.getFieldNameAttr());
3621 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3627 input = getOrCreateIntConstant(0, 0);
3629 auto tagType = structType.getFieldType(
"tag");
3630 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3631 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3633 auto bodyType = structType.getFieldType(
"body");
3634 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3635 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3636 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3638 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3639 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3642LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3643 auto resultType =
lowerType(op.getResult().getType());
3645 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3647 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3648 cast<ArrayAttr>(attr));
3651LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3655 return setLowering(op, getOrCreateIntConstant(1, 1));
3657 auto tagName = op.getFieldNameAttr();
3658 auto lhs = getLoweredValue(op.getInput());
3659 if (isa<hw::StructType>(lhs.getType()))
3662 auto index = op.getFieldIndex();
3663 auto enumType = op.getInput().getType().base();
3664 auto tagValue = enumType.getElementValueAttr(index);
3665 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3666 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3667 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3668 loweredTagValue, tagName);
3670 Type resultType = builder.getIntegerType(1);
3671 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3675LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3678 return setLowering(op, Value());
3680 auto tagName = op.getFieldNameAttr();
3681 auto input = getLoweredValue(op.getInput());
3683 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3686LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3689 return setLowering(op, Value());
3691 auto input = getLoweredValue(op.getInput());
3697 if (isa<hw::StructType>(input.getType())) {
3698 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3703 return setLowering(op, input);
3710LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3711 auto origResultType = op.getResult().getType();
3715 if (!type_isa<FIRRTLType>(origResultType)) {
3716 createBackedge(op.getResult(), origResultType);
3720 auto resultType =
lowerType(origResultType);
3724 if (resultType.isInteger(0)) {
3725 if (op.getInnerSym())
3726 return op.emitError(
"zero width wire is referenced by name [")
3727 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3728 return setLowering(op.getResult(), Value());
3732 auto innerSym = lowerInnerSymbol(op);
3733 auto name = op.getNameAttr();
3736 auto wire = hw::WireOp::create(
3737 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3739 if (
auto svAttrs = sv::getSVAttributes(op))
3740 sv::setSVAttributes(wire, svAttrs);
3742 return setLowering(op.getResult(), wire);
3745LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3746 auto resultTy =
lowerType(op.getType());
3749 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3751 SmallVector<Value, 4> operands;
3752 operands.reserve(op.getSubstitutions().size());
3753 for (
auto operand : op.getSubstitutions()) {
3754 auto lowered = getLoweredValue(operand);
3757 operands.push_back(lowered);
3760 ArrayAttr symbols = op.getSymbolsAttr();
3762 symbols = ArrayAttr::get(op.getContext(), {});
3764 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3768LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3769 auto operand = getLoweredValue(op.getInput());
3771 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3772 if (op.getInnerSym())
3773 return op.emitError(
"zero width node is referenced by name [")
3774 << *op.getInnerSym()
3775 <<
"] (e.g. in an XMR) but must be "
3777 return setLowering(op.getResult(), Value());
3783 auto name = op.getNameAttr();
3784 auto innerSym = lowerInnerSymbol(op);
3787 operand = hw::WireOp::create(builder, operand, name, innerSym);
3790 if (
auto svAttrs = sv::getSVAttributes(op)) {
3792 operand = hw::WireOp::create(builder, operand, name);
3793 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3796 return setLowering(op.getResult(), operand);
3799LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3800 auto resultType =
lowerType(op.getResult().getType());
3803 if (resultType.isInteger(0))
3804 return setLowering(op.getResult(), Value());
3806 Value clockVal = getLoweredValue(op.getClockVal());
3811 Attribute presetAttr;
3812 if (
auto initial = op.getInitialAttr()) {
3813 auto intTy = dyn_cast<IntegerType>(resultType);
3814 assert(intTy &&
"'initial' must be integer type");
3815 presetAttr = builder.getIntegerAttr(
3816 intTy, initial.getValue().zextOrTrunc(intTy.getWidth()));
3820 auto innerSym = lowerInnerSymbol(op);
3821 Backedge inputEdge = backedgeBuilder.
get(resultType);
3822 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3823 op.getNameAttr(), innerSym, presetAttr);
3826 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3827 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3828 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3829 reg->setAttr(
"firrtl.random_init_start", randomStart);
3830 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3831 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3834 if (
auto svAttrs = sv::getSVAttributes(op))
3835 sv::setSVAttributes(reg, svAttrs);
3838 (void)setLowering(op.getResult(),
reg);
3842LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3843 auto resultType =
lowerType(op.getResult().getType());
3846 if (resultType.isInteger(0))
3847 return setLowering(op.getResult(), Value());
3849 Value clockVal = getLoweredValue(op.getClockVal());
3850 Value resetSignal = getLoweredValue(op.getResetSignal());
3852 Value resetValue = getLoweredAndExtOrTruncValue(
3853 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3855 if (!clockVal || !resetSignal || !resetValue)
3859 Attribute presetAttr;
3860 if (
auto initial = op.getInitialAttr()) {
3861 auto intTy = dyn_cast<IntegerType>(resultType);
3862 assert(intTy &&
"'initial' must be integer type");
3863 presetAttr = builder.getIntegerAttr(
3864 intTy, initial.getValue().zextOrTrunc(intTy.getWidth()));
3868 auto innerSym = lowerInnerSymbol(op);
3869 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3870 Backedge inputEdge = backedgeBuilder.
get(resultType);
3871 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3872 op.getNameAttr(), resetSignal, resetValue,
3873 innerSym, isAsync, presetAttr);
3876 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3877 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3878 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3879 reg->setAttr(
"firrtl.random_init_start", randomStart);
3880 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3881 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3884 if (
auto svAttrs = sv::getSVAttributes(op))
3885 sv::setSVAttributes(reg, svAttrs);
3888 (void)setLowering(op.getResult(),
reg);
3893LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3896 if (type_isa<BundleType>(op.getDataType()))
3897 return op.emitOpError(
3898 "should have already been lowered from a ground type to an aggregate "
3899 "type using the LowerTypes pass. Use "
3900 "'firtool --lower-types' or 'circt-opt "
3901 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3907 auto memType = seq::FirMemType::get(
3910 : std::optional<uint32_t>());
3912 seq::FirMemInitAttr memInit;
3913 if (
auto init = op.getInitAttr())
3914 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3915 init.getIsBinary(), init.getIsInline());
3917 auto memDecl = seq::FirMemOp::create(
3920 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3923 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3925 if (!file.isDirectory())
3926 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3927 file.getDirectory());
3928 memDecl.setOutputFileAttr(dir);
3934 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3936 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3939 (void)setLowering(a, value);
3945 auto addInput = [&](StringRef field, Value backedge) {
3947 if (cast<FIRRTLBaseType>(a.getType())
3949 .getBitWidthOrSentinel() > 0)
3950 (void)setLowering(a, backedge);
3956 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3960 Value backedge, portValue;
3962 portValue = getOrCreateXConstant(1);
3964 auto portType = IntegerType::get(op.getContext(), width);
3965 backedge = portValue = createBackedge(builder.getLoc(), portType);
3967 addInput(field, backedge);
3971 auto addClock = [&](StringRef field) -> Value {
3972 Type clockTy = seq::ClockType::get(op.getContext());
3973 Value portValue = createBackedge(builder.getLoc(), clockTy);
3974 addInput(field, portValue);
3978 auto memportKind = op.getPortKind(i);
3979 if (memportKind == MemOp::PortKind::Read) {
3980 auto addr = addInputPort(
"addr", op.getAddrBits());
3981 auto en = addInputPort(
"en", 1);
3982 auto clk = addClock(
"clk");
3983 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3985 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3986 auto addr = addInputPort(
"addr", op.getAddrBits());
3987 auto en = addInputPort(
"en", 1);
3988 auto clk = addClock(
"clk");
3991 auto mode = addInputPort(
"wmode", 1);
3993 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
4000 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
4004 auto addr = addInputPort(
"addr", op.getAddrBits());
4007 auto en = addInputPort(
"en", 1);
4011 auto clk = addClock(
"clk");
4025FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
4026 Operation *instanceOp,
4027 SmallVectorImpl<Value> &inputOperands) {
4029 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4030 auto &port = portInfo[portIndex];
4033 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
4038 if (portType.isInteger(0))
4042 if (port.isOutput())
4045 auto portResult = instanceOp->getResult(portIndex);
4046 assert(portResult &&
"invalid IR, couldn't find port");
4050 if (port.isInput()) {
4051 inputOperands.push_back(createBackedge(portResult, portType));
4057 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
4058 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
4060 auto loweredResult = getPossiblyInoutLoweredValue(source);
4061 inputOperands.push_back(loweredResult);
4062 (void)setLowering(portResult, loweredResult);
4071 "." + port.getName().str() +
".wire");
4075 (void)setLowering(portResult, wire);
4076 inputOperands.push_back(wire);
4082LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4083 Operation *oldModule =
4084 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4086 auto *newModule = circuitState.getNewModule(oldModule);
4088 oldInstance->emitOpError(
"could not find module [")
4089 << oldInstance.getModuleName() <<
"] referenced by instance";
4095 ArrayAttr parameters;
4096 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4101 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4105 SmallVector<Value, 8> operands;
4106 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4113 auto innerSym = oldInstance.getInnerSymAttr();
4114 if (oldInstance.getLowerToBind()) {
4117 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4120 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4121 innerSym.getSymName());
4124 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4125 bindOp->setAttr(
"output_file", outputFile);
4128 circuitState.addBind(bindOp);
4133 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4134 operands, parameters, innerSym);
4136 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4137 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4139 if (newInstance.getInnerSymAttr())
4140 if (
auto forceName = circuitState.instanceForceNames.lookup(
4141 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4142 newInstance.getInnerNameAttr()}))
4143 newInstance->setAttr(
"hw.verilogName", forceName);
4147 unsigned resultNo = 0;
4148 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4149 auto &port = portInfo[portIndex];
4153 Value resultVal = newInstance.getResult(resultNo);
4155 auto oldPortResult = oldInstance.getResult(portIndex);
4156 (void)setLowering(oldPortResult, resultVal);
4162LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4163 if (oldInstanceChoice.getInnerSymAttr()) {
4164 oldInstanceChoice->emitOpError(
4165 "instance choice with inner sym cannot be lowered");
4170 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4172 return oldInstanceChoice->emitOpError(
4173 "must have instance_macro attribute set before "
4177 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4178 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4181 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4182 auto *defaultModuleNode =
4183 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4185 Operation *defaultModule = defaultModuleNode->getModule();
4189 SmallVector<PortInfo, 8> portInfo =
4190 cast<FModuleLike>(defaultModule).getPorts();
4193 SmallVector<Value, 8> inputOperands;
4195 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4199 SmallVector<sv::WireOp, 8> outputWires;
4200 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4201 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4202 auto &port = portInfo[portIndex];
4206 if (!portType || portType.isInteger(0))
4209 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4210 outputWires.push_back(wire);
4211 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4215 auto optionName = oldInstanceChoice.getOptionNameAttr();
4218 auto createInstanceAndAssign = [&](Operation *oldMod,
4219 StringRef suffix) -> hw::InstanceOp {
4220 auto *newMod = circuitState.getNewModule(oldMod);
4222 ArrayAttr parameters;
4223 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4227 SmallString<64> instName;
4228 instName = oldInstanceChoice.getInstanceName();
4229 if (!suffix.empty()) {
4235 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4236 inputOperands, parameters,
nullptr);
4242 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4249 SmallVector<StringAttr> macroNames;
4250 SmallVector<Operation *> altModules;
4251 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4252 altModules.push_back(
4253 circuitState.getInstanceGraph()
4254 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4258 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4259 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4260 if (!optionCaseMacroRef)
4261 return oldInstanceChoice->emitOpError(
4262 "failed to get macro for option case");
4263 macroNames.push_back(optionCaseMacroRef.getAttr());
4267 sv::createNestedIfDefs(
4270 [&](StringRef macro, std::function<
void()> thenCtor,
4271 std::function<
void()> elseCtor) {
4272 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4276 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4277 sv::IfDefOp::create(
4278 builder, oldInstanceChoice.getLoc(), macroNames[i],
4280 SmallString<256> errorMessage;
4281 llvm::raw_svector_ostream os(errorMessage);
4282 os <<
"Multiple instance choice options defined for option '"
4283 << optionName.getValue() <<
"': '"
4284 << macroNames[index].getValue() <<
"' and '"
4285 << macroNames[i].getValue() <<
"'";
4286 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4287 builder.getStringAttr(errorMessage));
4293 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4295 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4297 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4298 builder.getStringAttr(
"{{0}}"),
4299 builder.getArrayAttr({hw::InnerRefAttr::get(
4300 theModule.getNameAttr(),
4301 inst.getInnerSymAttr().getSymName())}));
4305 SmallString<256> errorMessage;
4306 llvm::raw_svector_ostream os(errorMessage);
4307 os <<
"Required instance choice option '" << optionName.getValue()
4308 <<
"' not selected, must define one of: ";
4309 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4310 os <<
"'" << macro.getValue() <<
"'";
4312 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4313 builder.getStringAttr(errorMessage));
4319LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4320 SmallVector<Value> inputs;
4321 SmallVector<Type> types;
4322 for (
auto input : oldOp.getInputs()) {
4323 auto lowered = getLoweredValue(input);
4326 inputs.push_back(lowered);
4327 types.push_back(lowered.getType());
4330 auto newOp = verif::ContractOp::create(builder, types, inputs);
4331 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4332 auto &body = newOp.getBody().emplaceBlock();
4334 for (
auto [newResult, oldResult, oldArg] :
4335 llvm::zip(newOp.getResults(), oldOp.getResults(),
4336 oldOp.getBody().getArguments())) {
4337 if (failed(setLowering(oldResult, newResult)))
4339 if (failed(setLowering(oldArg, newResult)))
4343 body.getOperations().splice(body.end(),
4344 oldOp.getBody().front().getOperations());
4345 addToWorklist(body);
4355LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4356 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4361 return setLowering(op->getResult(0), operand);
4364LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4365 if (isa<ClockType>(op.getInput().getType()))
4366 return setLowering(op->getResult(0),
4367 getLoweredNonClockValue(op.getInput()));
4368 return lowerNoopCast(op);
4371LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4372 if (isa<ClockType>(op.getInput().getType()))
4373 return setLowering(op->getResult(0),
4374 getLoweredNonClockValue(op.getInput()));
4375 return lowerNoopCast(op);
4378LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4379 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4382LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4383 mlir::UnrealizedConversionCastOp op) {
4385 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4388 auto operand = op.getOperand(0);
4389 auto result = op.getResult(0);
4392 if (type_isa<FIRRTLType>(operand.getType()) &&
4393 type_isa<FIRRTLType>(result.getType()))
4394 return lowerNoopCast(op);
4398 if (!type_isa<FIRRTLType>(operand.getType())) {
4399 if (type_isa<FIRRTLType>(result.getType()))
4400 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4406 auto loweredResult = getLoweredValue(operand);
4407 if (!loweredResult) {
4410 if (operand.getType().isSignlessInteger(0)) {
4411 return setLowering(result, Value());
4418 result.replaceAllUsesWith(loweredResult);
4422LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4425 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4426 return setLowering(op, op.getOperand());
4430 auto result = getLoweredValue(op.getOperand());
4436 op.replaceAllUsesWith(result);
4440LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4441 auto operand = getLoweredValue(op.getOperand());
4444 auto resultType =
lowerType(op.getType());
4448 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4451LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4452 auto operand = getLoweredValue(op.getOperand());
4456 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4457 return setLowering(op, getOrCreateIntConstant(1, 0));
4459 return setLowering(op, Value());
4464 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4465 return setLowering(op, operand);
4468 auto zero = getOrCreateIntConstant(1, 0);
4469 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4472LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4473 auto operand = getLoweredValue(op.getInput());
4477 auto allOnes = getOrCreateIntConstant(
4478 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4479 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4482LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4485 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4489 auto resultType =
lowerType(op.getType());
4491 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4492 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4496LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4497 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4500 return setLowering(op, operand);
4503LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4504 auto operand = getLoweredValue(op.getInput());
4507 return setLowering(op, getOrCreateIntConstant(1, 0));
4512 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4516LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4517 auto operand = getLoweredValue(op.getInput());
4520 return setLowering(op, getOrCreateIntConstant(1, 1));
4525 return setLoweringTo<comb::ICmpOp>(
4526 op, ICmpPredicate::eq, operand,
4527 getOrCreateIntConstant(
4528 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4532LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4533 auto operand = getLoweredValue(op.getInput());
4536 return setLowering(op, getOrCreateIntConstant(1, 0));
4542 return setLoweringTo<comb::ICmpOp>(
4543 op, ICmpPredicate::ne, operand,
4544 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4552template <
typename ResultOpType>
4553LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4554 auto resultType = op->getResult(0).getType();
4555 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4556 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4560 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4566template <
typename ResultOpType>
4567LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4568 auto resultType = op->getResult(0).getType();
4569 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4570 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4581 auto intType = builder.getIntegerType(*bitwidth);
4582 auto retType = lhs.getType();
4585 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4586 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4591template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4592LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4594 auto resultType = op->getResult(0).getType();
4595 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4596 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4601 if (type_cast<IntType>(resultType).isSigned())
4602 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4603 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4608LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4609 ICmpPredicate unsignedOp) {
4611 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4612 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4613 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4617 if (cmpType.getWidth() == 0)
4618 cmpType = UIntType::get(builder.getContext(), 1);
4619 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4620 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4625 Type resultType = builder.getIntegerType(1);
4626 return setLoweringTo<comb::ICmpOp>(
4627 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4633template <
typename SignedOp,
typename Un
signedOp>
4634LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4638 auto opType = type_cast<IntType>(op->getResult(0).getType());
4639 if (opType.getWidth() == 0)
4640 return setLowering(op->getResult(0), Value());
4644 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4645 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4650 if (opType.isSigned())
4651 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4653 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4655 if (
auto *definingOp = result.getDefiningOp())
4658 if (resultType == opType)
4659 return setLowering(op->getResult(0), result);
4660 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4663LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4665 if (op.getInputs().empty())
4666 return setLowering(op, Value());
4668 SmallVector<Value> loweredOperands;
4671 for (
auto operand : op.getInputs()) {
4672 auto loweredOperand = getLoweredValue(operand);
4673 if (loweredOperand) {
4674 loweredOperands.push_back(loweredOperand);
4677 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4685 if (loweredOperands.empty())
4686 return setLowering(op, Value());
4689 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4696LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4697 auto input = getLoweredNonClockValue(op.getArg());
4701 if (!isa<IntType>(input.getType())) {
4702 auto srcType = op.getArg().getType();
4704 assert(bitwidth &&
"Unknown width");
4705 auto intType = builder.getIntegerType(*bitwidth);
4706 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4709 return setLoweringTo<comb::ICmpOp>(
4710 op, ICmpPredicate::ceq, input,
4711 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4714LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4715 auto operand = getLoweredValue(op.getInput());
4716 hw::WireOp::create(builder, operand);
4720LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4721 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4722 op.getFormatStringAttr());
4725LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4726 auto type =
lowerType(op.getResult().getType());
4730 auto valueOp = sim::PlusArgsValueOp::create(
4731 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4732 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4734 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4739LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4740 op.emitError(
"SizeOf should have been resolved.");
4744LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4746 if (op.getTestEnable())
4747 testEnable = getLoweredValue(op.getTestEnable());
4748 return setLoweringTo<seq::ClockGateOp>(
4749 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4750 testEnable, hw::InnerSymAttr{});
4753LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4754 auto operand = getLoweredValue(op.getInput());
4755 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4758LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4759 auto operand = getLoweredValue(op.getInput());
4760 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4763LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4764 return setLoweringToLTL<ltl::AndOp>(
4766 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4769LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4770 return setLoweringToLTL<ltl::OrOp>(
4772 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4775LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4776 return setLoweringToLTL<ltl::IntersectOp>(
4778 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4781LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4782 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4783 op.getDelayAttr(), op.getLengthAttr());
4786LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4787 return setLoweringToLTL<ltl::ConcatOp>(
4789 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4792LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4793 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4794 op.getBaseAttr(), op.getMoreAttr());
4797LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4798 return setLoweringToLTL<ltl::GoToRepeatOp>(
4799 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4802LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4803 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4804 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4807LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4808 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4811LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4812 return setLoweringToLTL<ltl::ImplicationOp>(
4814 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4817LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4818 return setLoweringToLTL<ltl::UntilOp>(
4820 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4823LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4824 return setLoweringToLTL<ltl::EventuallyOp>(op,
4825 getLoweredValue(op.getInput()));
4828LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4829 Value
clk = getLoweredNonClockValue(op.getClock());
4830 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4831 op.getDelayAttr(),
clk);
4835 switch (eventControl) {
4836 case EventControl::AtPosEdge:
4837 return ltl::ClockEdge::Pos;
4838 case EventControl::AtEdge:
4839 return ltl::ClockEdge::Both;
4840 case EventControl::AtNegEdge:
4841 return ltl::ClockEdge::Neg;
4843 llvm_unreachable(
"unknown event control");
4846LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4847 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4849 getLoweredNonClockValue(op.getClock()));
4852template <
typename TargetOp,
typename IntrinsicOp>
4853LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4854 auto property = getLoweredValue(op.getProperty());
4855 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4856 TargetOp::create(builder, property, enable, op.getLabelAttr());
4860LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4861 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4864LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4865 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4868LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4869 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4872LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4873 if (!isa<verif::ContractOp>(op->getParentOp()))
4874 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4875 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4878LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4879 if (!isa<verif::ContractOp>(op->getParentOp()))
4880 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4881 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4884LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4885 auto clock = getLoweredNonClockValue(op.getClock());
4886 auto reset = getLoweredValue(op.getReset());
4887 if (!clock || !reset)
4889 auto resetType = op.getReset().getType();
4890 auto uintResetType = dyn_cast<UIntType>(resetType);
4891 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4892 auto isAsync = isa<AsyncResetType>(resetType);
4893 if (!isAsync && !isSync) {
4894 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4895 "requires sync or async reset");
4896 d.attachNote() <<
"reset is of type " << resetType
4897 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4900 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4907LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4908 auto input = getLoweredValue(op.getInput());
4912 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4913 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4916LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4917 auto resultTy =
lowerType(op.getType());
4924 if (type_isa<AnalogType>(op.getType()))
4927 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4930 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4941 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4943 if (!type_isa<IntegerType>(resultTy))
4945 return setLowering(op, constant);
4949 op.emitOpError(
"unsupported type");
4953LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4954 auto input = getLoweredValue(op.getInput());
4957 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4958 if (op.getAmount() == 0)
4959 return setLowering(op, Value());
4960 Type resultType = builder.getIntegerType(op.getAmount());
4961 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4962 inWidth - op.getAmount());
4965LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4966 auto input = getLoweredValue(op.getInput());
4969 if (op.getAmount() == 0)
4971 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4976 if (op.getAmount() == 0)
4977 return setLowering(op, input);
4979 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4980 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4983LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4984 auto input = getLoweredValue(op.getInput());
4989 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4990 auto shiftAmount = op.getAmount();
4991 if (shiftAmount >= inWidth) {
4993 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4994 return setLowering(op, {});
4997 shiftAmount = inWidth - 1;
5000 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
5001 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
5004LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
5005 auto input = getLoweredValue(op.getInput());
5009 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
5010 if (inWidth == op.getAmount())
5011 return setLowering(op, Value());
5012 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
5013 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
5016LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
5017 auto cond = getLoweredValue(op.getSel());
5018 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5019 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5020 if (!cond || !ifTrue || !ifFalse)
5023 if (isa<ClockType>(op.getType()))
5024 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
5025 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
5029LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
5030 auto cond = getLoweredValue(op.getSel());
5031 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5032 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5033 if (!cond || !ifTrue || !ifFalse)
5036 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
5038 return setLowering(op, createValueWithMuxAnnotation(val,
true));
5041LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
5042 auto sel = getLoweredValue(op.getSel());
5043 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
5044 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
5045 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
5046 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
5047 if (!sel || !v3 || !v2 || !v1 || !v0)
5049 Value array[] = {v3, v2, v1, v0};
5052 return setLowering(op, createValueWithMuxAnnotation(val,
false));
5071Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
5072 assert(op->getNumResults() == 1 &&
"only expect a single result");
5073 auto val = op->getResult(0);
5077 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5084 OpBuilder::InsertionGuard guard(builder);
5085 builder.setInsertionPoint(op);
5086 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5087 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
5089 op->getContext(),
nullptr, 0,
5092 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5093 op->setOperand(idx, wire);
5098 sv::setSVAttributes(assignOp,
5099 sv::SVAttributeAttr::get(builder.getContext(),
5100 "synopsys infer_mux_override",
5105Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5107 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5112 if (!llvm::isPowerOf2_64(size)) {
5113 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5115 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5117 Value temp2[] = {ext.getResult(), array};
5123 return inBoundsRead;
5126LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5128 auto index = getLoweredAndExtOrTruncValue(
5130 UIntType::get(op.getContext(),
5135 SmallVector<Value> loweredInputs;
5136 loweredInputs.reserve(op.getInputs().size());
5137 for (
auto input : op.getInputs()) {
5138 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5141 loweredInputs.push_back(lowered);
5145 return setLowering(op, createArrayIndexing(array, index));
5148LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5149 auto resultTy =
lowerType(op.getType());
5153 SmallVector<Value, 4> operands;
5154 operands.reserve(op.getSubstitutions().size());
5155 for (
auto operand : op.getSubstitutions()) {
5156 auto lowered = getLoweredValue(operand);
5159 operands.push_back(lowered);
5162 ArrayAttr symbols = op.getSymbolsAttr();
5164 symbols = ArrayAttr::get(op.getContext(), {});
5166 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5170LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5174 Type baseType = op.getType().getType();
5177 if (isa<ClockType>(baseType))
5178 xmrType = builder.getIntegerType(1);
5182 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5183 op.getRef(), op.getVerbatimSuffixAttr());
5186LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5190 if (isa<ClockType>(op.getType()))
5191 xmrType = builder.getIntegerType(1);
5195 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5196 op.getRef(), op.getVerbatimSuffixAttr());
5197 auto readXmr = getReadValue(xmr);
5198 if (!isa<ClockType>(op.getType()))
5199 return setLowering(op, readXmr);
5200 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5205LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5206LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5214LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5226FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5227 auto srcType = srcVal.getType();
5228 auto dstType = destVal.getType();
5229 if (srcType != dstType &&
5230 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5233 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5234 .Case<hw::WireOp>([&](
auto op) {
5235 maybeUnused(op.getInput());
5236 op.getInputMutable().assign(srcVal);
5239 .Case<seq::FirRegOp>([&](
auto op) {
5240 maybeUnused(op.getNext());
5241 op.getNextMutable().assign(srcVal);
5244 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5247 op.emitOpError(
"used as connect destination");
5250 .Default([](
auto) {
return false; });
5253LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5254 auto dest = op.getDest();
5256 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5257 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5259 return handleZeroBit(op.getSrc(), []() { return success(); });
5261 auto destVal = getPossiblyInoutLoweredValue(dest);
5265 auto result = lowerConnect(destVal, srcVal);
5273 if (updateIfBackedge(destVal, srcVal))
5276 if (!isa<hw::InOutType>(destVal.getType()))
5277 return op.emitError(
"destination isn't an inout type");
5283LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5284 auto dest = op.getDest();
5285 auto srcVal = getLoweredValue(op.getSrc());
5287 return handleZeroBit(op.getSrc(), []() { return success(); });
5289 auto destVal = getPossiblyInoutLoweredValue(dest);
5293 auto result = lowerConnect(destVal, srcVal);
5301 if (updateIfBackedge(destVal, srcVal))
5304 if (!isa<hw::InOutType>(destVal.getType()))
5305 return op.emitError(
"destination isn't an inout type");
5311LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5312 if (circuitState.lowerToCore)
5313 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5315 auto srcVal = getLoweredValue(op.getSrc());
5319 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5323 if (!isa<hw::InOutType>(destVal.getType()))
5324 return op.emitError(
"destination isn't an inout type");
5327 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5328 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5329 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5334LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5335 if (circuitState.lowerToCore)
5336 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5338 auto src = getLoweredNonClockValue(op.getSrc());
5339 auto clock = getLoweredNonClockValue(op.getClock());
5340 auto pred = getLoweredValue(op.getPredicate());
5341 if (!src || !clock || !pred)
5344 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5349 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5350 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5351 addToAlwaysBlock(clock, [&]() {
5352 addIfProceduralBlock(
5353 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5358LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5359 if (circuitState.lowerToCore)
5360 return op.emitOpError(
5361 "lower-to-core does not support firrtl.ref.force_initial");
5363 auto src = getLoweredNonClockValue(op.getSrc());
5364 auto pred = getLoweredValue(op.getPredicate());
5368 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5373 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5374 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5375 addToInitialBlock([&]() {
5376 addIfProceduralBlock(
5377 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5382LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5383 if (circuitState.lowerToCore)
5384 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5386 auto clock = getLoweredNonClockValue(op.getClock());
5387 auto pred = getLoweredValue(op.getPredicate());
5388 if (!clock || !pred)
5391 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5396 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5397 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5398 addToAlwaysBlock(clock, [&]() {
5399 addIfProceduralBlock(pred,
5400 [&]() { sv::ReleaseOp::create(builder, destVal); });
5405LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5406 if (circuitState.lowerToCore)
5407 return op.emitOpError(
5408 "lower-to-core does not support firrtl.ref.release_initial");
5410 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5411 auto pred = getLoweredValue(op.getPredicate());
5412 if (!destVal || !pred)
5416 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5417 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5418 addToInitialBlock([&]() {
5419 addIfProceduralBlock(pred,
5420 [&]() { sv::ReleaseOp::create(builder, destVal); });
5428 StringRef originalFormatString,
5429 ValueRange operands,
5430 StringAttr &result) {
5433 SmallString<32> formatString;
5434 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5435 char c = originalFormatString[i];
5439 formatString.push_back(c);
5442 SmallString<6> width;
5443 c = originalFormatString[++i];
5446 c = originalFormatString[++i];
5457 formatString.append(width);
5463 formatString.push_back(c);
5470 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5471 formatString.push_back(c);
5475 auto substitution = operands[subIdx++];
5476 assert(type_isa<FStringType>(substitution.getType()) &&
5477 "the operand for a '{{}}' substitution must be an 'fstring' type");
5479 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5480 .template Case<TimeOp>([&](
auto) {
5481 formatString.append(
"%0t");
5484 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5485 formatString.append(
"%m");
5488 .Default([&](
auto) {
5489 emitError(loc,
"has a substitution with an unimplemented "
5491 .attachNote(substitution.getLoc())
5492 <<
"op with an unimplemented lowering is here";
5502 formatString.push_back(c);
5506 result = StringAttr::get(loc->getContext(), formatString);
5513LogicalResult FIRRTLLowering::visitPrintfLike(
5514 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5515 auto clock = getLoweredNonClockValue(op.getClock());
5516 auto cond = getLoweredValue(op.getCond());
5517 if (!clock || !cond)
5520 StringAttr formatString;
5522 op.getSubstitutions(), formatString)))
5525 auto fn = [&](Value fd) {
5526 SmallVector<Value> operands;
5527 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5529 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5533 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5537LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5538 if (!circuitState.lowerToCore)
5539 return visitPrintfLike(op, {},
true);
5541 auto clock = getLoweredValue(op.getClock());
5542 auto cond = getLoweredValue(op.getCond());
5543 if (!clock || !cond)
5547 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5548 if (failed(formatString))
5551 auto stderrOp = sim::StderrStreamOp::create(builder);
5552 sim::TriggeredOp::create(builder, clock, cond, [&] {
5553 sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
5558LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5559 if (circuitState.lowerToCore) {
5560 auto clock = getLoweredValue(op.getClock());
5561 auto cond = getLoweredValue(op.getCond());
5562 if (!clock || !cond)
5565 auto fileFormatString = lowerSimFormatString(
5566 op.getOutputFileAttr(), op.getOutputFileSubstitutions());
5567 if (failed(fileFormatString))
5571 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5572 if (failed(formatString))
5575 sim::TriggeredOp::create(builder, clock, cond, [&] {
5576 auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
5577 sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
5582 StringAttr outputFileAttr;
5584 op.getOutputFileSubstitutions(),
5588 FileDescriptorInfo outputFile(outputFileAttr,
5589 op.getOutputFileSubstitutions());
5590 return visitPrintfLike(op, outputFile,
false);
5594LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5595 if (circuitState.lowerToCore)
5596 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5598 auto clock = getLoweredNonClockValue(op.getClock());
5599 auto cond = getLoweredValue(op.getCond());
5600 if (!clock || !cond)
5603 auto fn = [&](Value fd) {
5604 sv::FFlushOp::create(builder, op.getLoc(), fd);
5608 if (!op.getOutputFileAttr())
5609 return lowerStatementWithFd({}, clock, cond, fn,
false);
5613 StringAttr outputFileAttr;
5615 op.getOutputFileSubstitutions(),
5619 return lowerStatementWithFd(
5620 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5621 clock, cond, fn,
false);
5626LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5627 auto clock = getLoweredValue(op.getClock());
5628 auto cond = getLoweredValue(op.getCond());
5629 if (!clock || !cond)
5632 circuitState.usedStopCond =
true;
5633 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5636 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5637 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5639 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5640 op.getExitCode() == 0,
5649template <
typename... Args>
5651 StringRef opName, Args &&...args) {
5652 if (opName ==
"assert")
5653 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5654 if (opName ==
"assume")
5655 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5656 if (opName ==
"cover")
5657 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5658 llvm_unreachable(
"unknown verification op");
5664template <
typename... Args>
5666 StringRef opName, Args &&...args) {
5667 if (opName ==
"assert")
5668 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5669 if (opName ==
"assume")
5670 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5671 if (opName ==
"cover")
5672 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5673 llvm_unreachable(
"unknown verification op");
5677 switch (eventControl) {
5678 case EventControl::AtPosEdge:
5679 return verif::ClockEdge::Pos;
5680 case EventControl::AtEdge:
5681 return verif::ClockEdge::Both;
5682 case EventControl::AtNegEdge:
5683 return verif::ClockEdge::Neg;
5685 llvm_unreachable(
"unknown FIRRTL event control");
5688LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5689 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5690 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5691 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5692 if (guardsAttr && !guardsAttr.empty())
5693 return op->emitOpError(
5694 "lower-to-core does not support guarded verification statements");
5696 auto clock = getLoweredNonClockValue(opClock);
5697 auto enable = getLoweredValue(opEnable);
5698 auto predicate = getLoweredValue(opPredicate);
5699 if (!clock || !enable || !predicate)
5703 if (opNameAttr && !opNameAttr.getValue().empty())
5704 label = StringAttr::get(builder.getContext(),
5705 labelPrefix + opNameAttr.getValue());
5708 auto opName = op->getName().stripDialect();
5709 if (opName ==
"assert") {
5710 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5714 if (opName ==
"assume") {
5715 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5719 if (opName ==
"cover") {
5720 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5724 llvm_unreachable(
"unknown verification op");
5745LogicalResult FIRRTLLowering::lowerVerificationStatement(
5746 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5747 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5748 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5749 if (circuitState.lowerToCore)
5750 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5751 opPredicate, opEnable, opNameAttr,
5754 StringRef opName = op->getName().stripDialect();
5757 ArrayRef<Attribute> guards{};
5758 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5759 guards = guardsAttr.getValue();
5761 auto isCover = isa<CoverOp>(op);
5762 auto clock = getLoweredNonClockValue(opClock);
5763 auto enable = getLoweredValue(opEnable);
5764 auto predicate = getLoweredValue(opPredicate);
5765 if (!clock || !enable || !predicate)
5769 if (opNameAttr && !opNameAttr.getValue().empty())
5771 StringAttr prefixedLabel;
5774 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5777 SmallVector<Value> messageOps;
5781 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5782 flavor = VerificationFlavor::None;
5784 if (flavor == VerificationFlavor::None) {
5788 auto format = op->getAttrOfType<StringAttr>(
"format");
5790 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5791 if (!isa<AssertOp>(op))
5792 return op->emitError()
5793 <<
"ifElseFatal format cannot be used for non-assertions";
5794 flavor = VerificationFlavor::IfElseFatal;
5795 }
else if (isConcurrent)
5796 flavor = VerificationFlavor::SVA;
5798 flavor = VerificationFlavor::Immediate;
5801 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5805 opOperands, message)))
5808 if (failed(loweredFmtOperands(opOperands, messageOps)))
5811 if (flavor == VerificationFlavor::SVA) {
5816 for (
auto &loweredValue : messageOps)
5817 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5823 case VerificationFlavor::Immediate: {
5825 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5826 builder.getContext(), circt::sv::DeferAssert::Immediate);
5827 addToAlwaysBlock(clock, [&]() {
5828 addIfProceduralBlock(enable, [&]() {
5830 prefixedLabel, message, messageOps);
5835 case VerificationFlavor::IfElseFatal: {
5836 assert(isa<AssertOp>(op) &&
"only assert is expected");
5839 auto boolType = IntegerType::get(builder.getContext(), 1);
5840 predicate = comb::createOrFoldNot(builder, predicate,
true);
5841 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5843 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5844 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5845 addToAlwaysBlock(clock, [&]() {
5846 addIfProceduralBlock(predicate, [&]() {
5847 circuitState.usedStopCond =
true;
5848 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5850 circuitState.usedAssertVerboseCond =
true;
5851 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5853 addIfProceduralBlock(
5854 sv::MacroRefExprOp::create(builder, boolType,
5855 "ASSERT_VERBOSE_COND_"),
5857 sv::ErrorProceduralOp::create(builder, message, messageOps);
5859 addIfProceduralBlock(
5860 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5861 [&]() { sv::FatalProceduralOp::create(builder); });
5867 case VerificationFlavor::SVA: {
5872 comb::createOrFoldNot(builder, enable,
true);
5874 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5876 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5880 sv::EventControl event;
5881 switch (opEventControl) {
5882 case EventControl::AtPosEdge:
5883 event = circt::sv::EventControl::AtPosEdge;
5885 case EventControl::AtEdge:
5886 event = circt::sv::EventControl::AtEdge;
5888 case EventControl::AtNegEdge:
5889 event = circt::sv::EventControl::AtNegEdge;
5895 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5896 predicate, prefixedLabel, message, messageOps);
5899 case VerificationFlavor::None:
5901 "flavor `None` must be converted into one of concreate flavors");
5908 return emitGuards(op->getLoc(), guards,
emit);
5912LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5913 return lowerVerificationStatement(
5914 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5915 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5916 op.getIsConcurrent(), op.getEventControl());
5920LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5921 return lowerVerificationStatement(
5922 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5923 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5924 op.getIsConcurrent(), op.getEventControl());
5928LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5929 return lowerVerificationStatement(
5930 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5931 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5932 op.getIsConcurrent(), op.getEventControl());
5936LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5937 if (circuitState.lowerToCore) {
5938 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5939 if (guardsAttr && !guardsAttr.empty())
5940 return op.emitOpError(
5941 "lower-to-core does not support guarded verification statements");
5943 auto predicate = getLoweredValue(op.getPredicate());
5944 auto enable = getLoweredValue(op.getEnable());
5945 if (!predicate || !enable)
5948 auto label = op.getNameAttr();
5949 StringAttr assumeLabel;
5950 if (label && !label.empty())
5952 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5953 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5961 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5962 ArrayRef<Attribute> guards =
5963 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5965 auto label = op.getNameAttr();
5966 StringAttr assumeLabel;
5967 if (label && !label.empty())
5969 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5970 auto predicate = getLoweredValue(op.getPredicate());
5971 auto enable = getLoweredValue(op.getEnable());
5972 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5973 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5975 SmallVector<Value> messageOps;
5976 for (
auto operand : op.getSubstitutions()) {
5977 auto loweredValue = getLoweredValue(operand);
5978 if (!loweredValue) {
5982 loweredValue = getOrCreateIntConstant(1, 0);
5984 messageOps.push_back(loweredValue);
5986 return emitGuards(op.getLoc(), guards, [&]() {
5987 sv::AlwaysOp::create(
5988 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5990 if (op.getMessageAttr().getValue().empty())
5991 buildImmediateVerifOp(
5992 builder,
"assume", predicate,
5993 circt::sv::DeferAssertAttr::get(
5994 builder.getContext(), circt::sv::DeferAssert::Immediate),
5997 buildImmediateVerifOp(
5998 builder,
"assume", predicate,
5999 circt::sv::DeferAssertAttr::get(
6000 builder.getContext(), circt::sv::DeferAssert::Immediate),
6001 assumeLabel, op.getMessageAttr(), messageOps);
6006LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
6008 if (op.getAttached().size() < 2)
6011 SmallVector<Value, 4> inoutValues;
6012 for (
auto v : op.getAttached()) {
6013 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
6014 if (!inoutValues.back()) {
6018 inoutValues.pop_back();
6022 if (!isa<hw::InOutType>(inoutValues.back().getType()))
6023 return op.emitError(
"operand isn't an inout type");
6026 if (inoutValues.size() < 2)
6034 if (circuitState.lowerToCore)
6035 return op.emitOpError(
6036 "lower-to-core does not support firrtl.attach that requires SV "
6042 bool isAttachInternalOnly =
6043 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
6045 if (isAttachInternalOnly) {
6046 auto v0 = inoutValues.front();
6047 for (
auto v : inoutValues) {
6050 v.replaceAllUsesWith(v0);
6057 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
6058 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
6063 SmallVector<Value, 4> values;
6064 for (
auto inoutValue : inoutValues)
6065 values.push_back(getReadValue(inoutValue));
6067 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
6068 for (
size_t i2 = 0; i2 != e; ++i2)
6076 sv::IfDefOp::create(
6077 builder,
"VERILATOR",
6079 sv::VerbatimOp::create(
6081 "`error \"Verilator does not support alias and thus "
6083 "arbitrarily connect bidirectional wires and ports\"");
6085 [&]() { sv::AliasOp::create(builder, inoutValues); });
6091LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
6092 sv::BindOp::create(builder, op.getInstanceAttr());
6096LogicalResult FIRRTLLowering::fixupLTLOps() {
6097 if (ltlOpFixupWorklist.empty())
6099 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6103 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6104 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6105 if (isa<
hw::WireOp>(user))
6106 ltlOpFixupWorklist.insert(user);
6109 while (!ltlOpFixupWorklist.empty()) {
6110 auto *op = ltlOpFixupWorklist.pop_back_val();
6113 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6114 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6115 SmallVector<Type, 2> types;
6116 auto result = opIntf.inferReturnTypes(
6117 op->getContext(), op->getLoc(), op->getOperands(),
6118 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6122 assert(types.size() == op->getNumResults());
6126 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6127 if (result.getType() == type)
6129 LLVM_DEBUG(llvm::dbgs()
6130 <<
" - Result #" << result.getResultNumber() <<
" from "
6131 << result.getType() <<
" to " << type <<
"\n");
6132 result.setType(type);
6133 for (
auto *user : result.getUsers())
6135 ltlOpFixupWorklist.insert(user);
6140 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6141 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6142 wireOp.replaceAllUsesWith(wireOp.getInput());
6143 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6144 if (wireOp.use_empty())
6151 SmallPtrSet<Operation *, 4> usersReported;
6152 for (
auto *user : op->getUsers()) {
6153 if (!usersReported.insert(user).second)
6155 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6156 user->getDialect()))
6158 if (isa<hw::WireOp>(user))
6160 auto d = op->emitError(
6161 "verification operation used in a non-verification context");
6162 d.attachNote(user->getLoc())
6163 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult emitFile(ArrayRef< Operation * > operations, StringRef filePath, raw_ostream &os)
Emits the given operation to a file represented by the passed ostream and file-path.
static void lowerModuleBody(FModuleOp mod, const DenseMap< StringAttr, PortConversion > &ports)
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static ltl::ClockEdge firrtlToLTLClockEdge(EventControl eventControl)
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static unsigned getBitWidthFromVectorSize(unsigned size)
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static verif::ClockEdge firrtlToVerifClockEdge(EventControl eventControl)
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static LogicalResult resolveFormatString(Location loc, StringRef originalFormatString, ValueRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static StringAttr getArgName(Operation *op, size_t idx)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
The target of an inner symbol, the entity the symbol is a handle for.
This is an edge in the InstanceGraph.
create(elements, Type result_type=None)
create(str sym_name, Type type, str verilog_name=None)
create(data_type, name=None, sym_name=None)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None, bool lowerToCore=false)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
The namespace of a CircuitOp, generally inhabited by modules.
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
AnnotationSet annotations
bool isInput() const
Return true if this is a simple input-only port.
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const