38#include "mlir/IR/BuiltinOps.h"
39#include "mlir/IR/BuiltinTypes.h"
40#include "mlir/IR/ImplicitLocOpBuilder.h"
41#include "mlir/IR/Threading.h"
42#include "mlir/Pass/Pass.h"
43#include "llvm/ADT/DenseMap.h"
44#include "llvm/Support/Debug.h"
45#include "llvm/Support/Mutex.h"
46#include "llvm/Support/Path.h"
48#define DEBUG_TYPE "lower-to-hw"
51#define GEN_PASS_DEF_LOWERFIRRTLTOHW
52#include "circt/Conversion/Passes.h.inc"
57using circt::comb::ICmpPredicate;
66 auto ftype = dyn_cast<FIRRTLBaseType>(type);
67 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
74 for (
auto operand : op.getAttached()) {
76 operand.getDefiningOp<InstanceOp>())
80 if (!operand.hasOneUse() || singleSource)
82 singleSource = operand;
91 auto checkTypes = [](Operation *op) -> WalkResult {
93 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
94 return op->emitError(
"Found unhandled FIRRTL operation '")
95 << op->getName() <<
"'";
98 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
99 if (llvm::any_of(types, [](Type type) {
100 return isa<FIRRTLDialect>(type.getDialect());
102 return op->emitOpError(
"found unhandled FIRRTL type");
107 if (failed(checkTypeRange(op->getOperandTypes())) ||
108 failed(checkTypeRange(op->getResultTypes())))
109 return WalkResult::interrupt();
112 for (
auto ®ion : op->getRegions())
113 for (
auto &block : region)
114 if (failed(checkTypeRange(block.getArgumentTypes())))
115 return WalkResult::interrupt();
118 return WalkResult::advance();
121 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
129 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
135 ImplicitLocOpBuilder &builder) {
137 if (BundleType bundle = dyn_cast<BundleType>(type))
138 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
140 if (type != val.getType())
141 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
149 ImplicitLocOpBuilder &builder) {
151 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
153 val = mlir::UnrealizedConversionCastOp::create(
157 val = builder.createOrFold<HWStructCastOp>(type, val);
162 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
168 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
174 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
175 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
176 dst->setAttr(
"sv.namehint", attr);
182class FileDescriptorInfo {
184 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
185 : outputFileFormat(outputFileName), substitutions(substitutions) {
187 substitutions.empty() &&
188 "substitutions must be empty when output file name is empty");
191 FileDescriptorInfo() =
default;
194 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
197 bool isDefaultFd()
const {
return !outputFileFormat; }
199 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
200 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
204 StringAttr outputFileFormat = {};
207 mlir::ValueRange substitutions;
217struct FIRRTLModuleLowering;
220struct CircuitLoweringState {
222 std::atomic<bool> usedPrintf{
false};
223 std::atomic<bool> usedAssertVerboseCond{
false};
224 std::atomic<bool> usedStopCond{
false};
225 std::atomic<bool> usedFileDescriptorLib{
false};
227 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
230 InstanceGraph &instanceGraph, NLATable *nlaTable,
231 const InstanceChoiceMacroTable ¯oTable)
232 : circuitOp(circuitOp), instanceGraph(instanceGraph),
233 enableAnnotationWarning(enableAnnotationWarning),
234 lowerToCore(lowerToCore), verificationFlavor(verificationFlavor),
235 nlaTable(nlaTable), macroTable(macroTable) {
236 auto *
context = circuitOp.getContext();
240 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
241 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
242 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
243 context, dirName.getValue(),
false,
true);
246 for (
auto &op : *circuitOp.getBodyBlock()) {
247 if (auto module = dyn_cast<FModuleLike>(op)) {
248 if (AnnotationSet::removeAnnotations(module, markDUTAnnoClass))
256 testHarness = instanceGraph.getTopLevelModule();
259 testHarness =
nullptr;
260 }
else if (dut == testHarness) {
261 testHarness =
nullptr;
266 auto inDUT = [&](igraph::ModuleOpInterface child) {
267 auto isPhony = [](igraph::InstanceRecord *instRec) {
268 if (
auto inst = instRec->getInstance<InstanceOp>())
269 return inst.getLowerToBind() || inst.getDoNotPrint();
272 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
273 return getInstanceGraph().isAncestor(child, parent, isPhony);
276 circuitOp->walk([&](FModuleLike moduleOp) {
278 dutModules.insert(moduleOp);
282 Operation *getNewModule(Operation *oldModule) {
283 auto it = oldToNewModuleMap.find(oldModule);
284 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
287 Operation *getOldModule(Operation *newModule) {
288 auto it = newToOldModuleMap.find(newModule);
289 return it != newToOldModuleMap.end() ? it->second :
nullptr;
292 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
293 oldToNewModuleMap[oldFMod] = newHWMod;
294 newToOldModuleMap[newHWMod] = oldFMod;
299 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
305 void addBind(sv::BindOp op) {
306 std::lock_guard<std::mutex> lock(bindsMutex);
312 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
315 auto hwAlias = typeAliases.getTypedecl(firAliasType);
318 assert(!typeAliases.isFrozen() &&
319 "type aliases cannot be generated after its frozen");
320 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
323 FModuleLike getDut() {
return dut; }
324 FModuleLike getTestHarness() {
return testHarness; }
330 bool isInDUT(igraph::ModuleOpInterface child) {
331 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
332 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
333 return dutModules.contains(child);
336 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
341 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
348 Type
lowerType(Type type, Location loc) {
349 return ::lowerType(type, loc,
350 [&](Type rawType, BaseTypeAliasType firrtlType,
351 Location typeLoc) -> hw::TypeAliasType {
352 return getTypeAlias(rawType, firrtlType, typeLoc);
358 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
359 auto it = verbatimSourcesByFileName.find(fileName);
360 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
365 void registerVerbatimSource(StringRef fileName,
367 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
368 verbatimSourcesByFileName[fileName] = verbatimOp;
372 emit::FileOp getEmitFileForFile(StringRef fileName) {
373 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
374 auto it = emitFilesByFileName.find(fileName);
375 return it != emitFilesByFileName.end() ? it->second :
nullptr;
380 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
381 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
382 emitFilesByFileName[fileName] = fileOp;
386 friend struct FIRRTLModuleLowering;
387 friend struct FIRRTLLowering;
388 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
389 void operator=(
const CircuitLoweringState &) =
delete;
392 DenseMap<Operation *, Operation *> oldToNewModuleMap;
395 DenseMap<Operation *, Operation *> newToOldModuleMap;
406 DenseSet<igraph::ModuleOpInterface> dutModules;
410 StringSet<> pendingAnnotations;
411 const bool enableAnnotationWarning;
412 std::mutex annotationPrintingMtx;
414 const bool lowerToCore;
419 SmallVector<sv::BindOp> binds;
422 std::mutex bindsMutex;
430 FModuleLike testHarness;
433 hw::OutputFileAttr testBenchDirectory;
437 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
440 SetVector<StringAttr> macroDeclNames;
441 std::mutex macroDeclMutex;
443 void addMacroDecl(StringAttr name) {
444 std::unique_lock<std::mutex> lock(macroDeclMutex);
445 macroDeclNames.insert(name);
450 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
451 llvm::sys::SmartMutex<true> fragmentsMutex;
455 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
458 void addFragment(
hw::HWModuleOp module, FlatSymbolRefAttr fragment) {
459 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
460 fragments[module].insert(fragment);
475 struct RecordTypeAlias {
477 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
479 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
480 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
481 if (iter != firrtlTypeToAliasTypeMap.end())
486 bool isFrozen() {
return frozen; }
488 void freeze() { frozen =
true; }
490 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
492 assert(!frozen &&
"Record already frozen, cannot be updated");
495 auto b = ImplicitLocOpBuilder::atBlockBegin(
497 &circuitOp->getParentRegion()->getBlocks().back());
499 b, b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"),
501 typeScope.getBodyRegion().push_back(
new Block());
503 auto typeName = firAlias.getName();
508 StringAttr::get(typeName.getContext(),
509 typeDeclNamespace.newName(typeName.getValue()));
511 auto typeScopeBuilder =
512 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
515 {}, rawType,
nullptr);
516 auto hwAlias = hw::TypeAliasType::get(
517 SymbolRefAttr::get(typeScope.getSymNameAttr(),
518 {FlatSymbolRefAttr::get(typeDecl)}),
520 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
521 assert(insert.second &&
"Entry already exists, insert failed");
522 return insert.first->second;
528 hw::TypeScopeOp typeScope;
531 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
534 Namespace typeDeclNamespace;
539 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
542 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
543 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
546 llvm::StringMap<emit::FileOp> emitFilesByFileName;
547 llvm::sys::SmartMutex<true> emitFilesMutex;
553void CircuitLoweringState::processRemainingAnnotations(
555 if (!enableAnnotationWarning || annoSet.
empty())
557 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
559 for (
auto a : annoSet) {
560 auto inserted = pendingAnnotations.insert(a.getClass());
561 if (!inserted.second)
582 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
587 verbatimBlackBoxAnnoClass,
589 extractGrandCentralAnnoClass,
592 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
593 extractCoverageAnnoClass,
597 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
598 blackBoxTargetDirAnnoClass))
601 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
602 "' still remaining after LowerToHW");
608struct FIRRTLModuleLowering
609 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
611 void runOnOperation()
override;
612 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
613 void setLowerToCore() { lowerToCore =
true; }
615 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
618 void lowerFileHeader(CircuitOp op, CircuitLoweringState &loweringState);
620 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
621 SmallVectorImpl<hw::PortInfo> &ports,
622 Operation *moduleOp, StringRef moduleName,
623 CircuitLoweringState &loweringState);
624 bool handleForceNameAnnos(FModuleLike oldModule, AnnotationSet &annos,
625 CircuitLoweringState &loweringState);
626 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
627 CircuitLoweringState &loweringState);
628 sv::SVVerbatimSourceOp
629 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
630 CircuitLoweringState &loweringState);
631 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
632 CircuitLoweringState &loweringState);
633 sv::SVVerbatimModuleOp
634 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
635 CircuitLoweringState &loweringState);
636 hw::HWModuleExternOp lowerMemModule(FMemModuleOp oldModule,
637 Block *topLevelModule,
638 CircuitLoweringState &loweringState);
641 lowerModulePortsAndMoveBody(FModuleOp oldModule, hw::HWModuleOp newModule,
642 CircuitLoweringState &loweringState);
644 CircuitLoweringState &loweringState);
645 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
646 CircuitLoweringState &loweringState);
647 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
648 CircuitLoweringState &loweringState);
649 LogicalResult lowerFileBody(emit::FileOp op);
650 LogicalResult
lowerBody(Operation *op, CircuitLoweringState &loweringState);
656std::unique_ptr<mlir::Pass>
660 auto pass = std::make_unique<FIRRTLModuleLowering>();
661 if (enableAnnotationWarning)
662 pass->setEnableAnnotationWarning();
664 pass->setLowerToCore();
665 pass->verificationFlavor = verificationFlavor;
671void FIRRTLModuleLowering::runOnOperation() {
675 auto *topLevelModule = getOperation().getBody();
679 for (
auto &op : *topLevelModule) {
680 if ((circuit = dyn_cast<CircuitOp>(&op)))
687 auto *circuitBody = circuit.getBodyBlock();
691 CircuitLoweringState state(circuit, enableAnnotationWarning, lowerToCore,
692 verificationFlavor, getAnalysis<InstanceGraph>(),
693 &getAnalysis<NLATable>(),
694 getAnalysis<InstanceChoiceMacroTable>());
696 SmallVector<Operation *, 32> opsToProcess;
698 AnnotationSet circuitAnno(circuit);
699 state.processRemainingAnnotations(circuit, circuitAnno);
702 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
704 TypeSwitch<Operation *, LogicalResult>(&op)
705 .Case<FModuleOp>([&](
auto module) {
706 auto loweredMod = lowerModule(module, topLevelModule, state);
710 state.recordModuleMapping(&op, loweredMod);
711 opsToProcess.push_back(loweredMod);
713 module.walk([&](Operation *op) {
714 for (auto res : op->getResults()) {
716 type_dyn_cast<BaseTypeAliasType>(res.getType()))
717 state.lowerType(aliasType, op->getLoc());
720 return lowerModulePortsAndMoveBody(module, loweredMod, state);
722 .Case<FExtModuleOp>([&](
auto extModule) {
724 lowerExtModule(extModule, topLevelModule, state);
727 state.recordModuleMapping(&op, loweredMod);
730 .Case<FMemModuleOp>([&](
auto memModule) {
732 lowerMemModule(memModule, topLevelModule, state);
735 state.recordModuleMapping(&op, loweredMod);
738 .Case<FormalOp>([&](
auto oldOp) {
739 auto builder = OpBuilder::atBlockEnd(topLevelModule);
740 auto newOp = verif::FormalOp::create(
741 builder, oldOp.getLoc(), oldOp.getNameAttr(),
742 {}, oldOp.getParametersAttr());
743 newOp.getBody().emplaceBlock();
744 state.recordModuleMapping(oldOp, newOp);
745 opsToProcess.push_back(newOp);
748 .Case<SimulationOp>([&](
auto oldOp) {
749 auto loc = oldOp.getLoc();
750 auto builder = OpBuilder::atBlockEnd(topLevelModule);
751 auto newOp = verif::SimulationOp::create(
752 builder, loc, oldOp.getNameAttr(), {},
753 oldOp.getParametersAttr());
754 auto &body = newOp.getRegion().emplaceBlock();
755 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
756 body.addArgument(builder.getI1Type(), loc);
757 state.recordModuleMapping(oldOp, newOp);
758 opsToProcess.push_back(newOp);
761 .Case<emit::FileOp>([&](
auto fileOp) {
762 fileOp->moveBefore(topLevelModule, topLevelModule->end());
763 opsToProcess.push_back(fileOp);
766 .Case<OptionOp, OptionCaseOp>([&](
auto) {
770 .Default([&](Operation *op) {
775 op->moveBefore(topLevelModule, topLevelModule->end());
781 return signalPassFailure();
784 state.typeAliases.freeze();
789 SmallVector<Attribute> dutHierarchyFiles;
790 SmallVector<Attribute> testHarnessHierarchyFiles;
791 circuitAnno.removeAnnotations([&](Annotation annotation) {
792 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
793 auto file = hw::OutputFileAttr::getFromFilename(
795 annotation.
getMember<StringAttr>(
"filename").getValue(),
797 dutHierarchyFiles.push_back(file);
800 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
801 auto file = hw::OutputFileAttr::getFromFilename(
803 annotation.
getMember<StringAttr>(
"filename").getValue(),
807 if (state.getTestHarness())
808 testHarnessHierarchyFiles.push_back(file);
810 dutHierarchyFiles.push_back(file);
816 if (!dutHierarchyFiles.empty())
817 state.getNewModule(state.getDut())
819 ArrayAttr::get(&getContext(), dutHierarchyFiles));
820 if (!testHarnessHierarchyFiles.empty())
821 state.getNewModule(state.getTestHarness())
823 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
827 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
831 return signalPassFailure();
834 for (
auto bind : state.binds) {
835 bind->moveBefore(bind->getParentOfType<hw::HWModuleOp>());
839 for (
auto &[module, fragments] : state.fragments)
840 module->setAttr(emit::getFragmentsAttrName(),
841 ArrayAttr::get(&getContext(), fragments.getArrayRef()));
844 for (
auto oldNew : state.oldToNewModuleMap)
845 oldNew.first->erase();
847 if (!state.macroDeclNames.empty()) {
848 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
849 for (
auto name : state.macroDeclNames) {
850 sv::MacroDeclOp::create(b, name);
855 lowerFileHeader(circuit, state);
862void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
863 CircuitLoweringState &state) {
866 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
870 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
871 StringRef defineTrue =
"",
872 StringRef defineFalse = StringRef()) {
873 if (!defineFalse.data()) {
874 assert(defineTrue.data() &&
"didn't define anything");
876 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
881 if (defineTrue.data())
882 sv::MacroDefOp::create(b, defName, defineTrue);
884 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
889 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
891 b, guard, [] {}, body);
894 if (state.usedFileDescriptorLib)
895 sv::emitFileDescriptorRuntime(op->getParentOp(), b);
897 if (state.usedPrintf) {
898 sv::MacroDeclOp::create(b,
"PRINTF_COND");
899 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
900 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
901 sv::VerbatimOp::create(
902 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
904 emitGuard(
"PRINTF_COND_", [&]() {
905 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
910 if (state.usedAssertVerboseCond) {
911 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
912 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
913 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
914 sv::VerbatimOp::create(
915 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
916 "gate to assert error printing.");
917 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
918 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
919 "(`ASSERT_VERBOSE_COND)",
"1");
924 if (state.usedStopCond) {
925 sv::MacroDeclOp::create(b,
"STOP_COND");
926 sv::MacroDeclOp::create(b,
"STOP_COND_");
927 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
928 sv::VerbatimOp::create(
929 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
930 "to stop conditions.");
931 emitGuard(
"STOP_COND_", [&]() {
932 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
939FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
940 SmallVectorImpl<hw::PortInfo> &ports,
941 Operation *moduleOp, StringRef moduleName,
942 CircuitLoweringState &loweringState) {
943 ports.reserve(firrtlPorts.size());
945 size_t numResults = 0;
946 for (
auto e : llvm::enumerate(firrtlPorts)) {
947 PortInfo firrtlPort = e.value();
948 size_t portNo = e.index();
951 hwPort.
type = loweringState.lowerType(firrtlPort.
type, firrtlPort.
loc);
953 if (firrtlPort.
sym.size() > 1 ||
954 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
955 return emitError(firrtlPort.
loc)
956 <<
"cannot lower aggregate port " << firrtlPort.
name
957 <<
" with field sensitive symbols, HW dialect does not support "
958 "per field symbols yet.";
959 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
961 if (hadDontTouch && !hwPort.
getSym()) {
962 if (hwPort.
type.isInteger(0)) {
963 if (enableAnnotationWarning) {
964 mlir::emitWarning(firrtlPort.
loc)
965 <<
"zero width port " << hwPort.
name
966 <<
" has dontTouch annotation, removing anyway";
972 hw::InnerSymAttr::get(StringAttr::get(
973 moduleOp->getContext(),
974 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
975 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
976 moduleOp->getContext());
981 moduleOp->emitError(
"cannot lower this port type to HW");
987 if (hwPort.
type.isInteger(0)) {
988 auto sym = hwPort.
getSym();
989 if (sym && !sym.empty()) {
990 return mlir::emitError(firrtlPort.
loc)
991 <<
"zero width port " << hwPort.
name
992 <<
" is referenced by name [" << sym
993 <<
"] (e.g. in an XMR) but must be removed";
1000 hwPort.
dir = hw::ModulePort::Direction::Output;
1001 hwPort.
argNum = numResults++;
1002 }
else if (firrtlPort.
isInput()) {
1003 hwPort.
dir = hw::ModulePort::Direction::Input;
1004 hwPort.
argNum = numArgs++;
1008 hwPort.
type = sv::NetType::get(hwPort.
type);
1009 hwPort.
dir = hw::ModulePort::Direction::Input;
1010 hwPort.
argNum = numArgs++;
1012 hwPort.
loc = firrtlPort.
loc;
1013 ports.push_back(hwPort);
1014 loweringState.processRemainingAnnotations(moduleOp, firrtlPort.
annotations);
1023 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1024 return cast<ParamDeclAttr>(a);
1029 Builder builder(module);
1034 SmallVector<Attribute> newParams;
1035 for (
const ParamDeclAttr &entry : params) {
1036 auto name = entry.getName();
1037 auto type = entry.getType();
1038 auto value = ignoreValues ? Attribute() : entry.getValue();
1040 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1041 newParams.push_back(paramAttr);
1043 return builder.getArrayAttr(newParams);
1046bool FIRRTLModuleLowering::handleForceNameAnnos(
1047 FModuleLike oldModule, AnnotationSet &annos,
1048 CircuitLoweringState &loweringState) {
1049 bool failed =
false;
1052 if (!anno.
isClass(forceNameAnnoClass))
1055 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1062 auto diag = oldModule.emitOpError()
1063 <<
"contains a '" << forceNameAnnoClass
1064 <<
"' that is not a non-local annotation";
1065 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1071 auto nla = loweringState.nlaTable->
getNLA(sym.getAttr());
1076 auto diag = oldModule.emitOpError()
1077 <<
"contains a '" << forceNameAnnoClass
1078 <<
"' whose non-local symbol, '" << sym
1079 <<
"' does not exist in the circuit";
1080 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1093 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1094 auto inserted = loweringState.instanceForceNames.insert(
1095 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1096 if (!inserted.second &&
1097 (anno.
getMember(
"name") != (inserted.first->second))) {
1098 auto diag = oldModule.emitError()
1099 <<
"contained multiple '" << forceNameAnnoClass
1100 <<
"' with different names: " << inserted.first->second
1101 <<
" was not " << anno.
getMember(
"name");
1102 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1112sv::SVVerbatimSourceOp FIRRTLModuleLowering::getVerbatimSourceForExtModule(
1113 FExtModuleOp oldModule, Block *topLevelModule,
1114 CircuitLoweringState &loweringState) {
1115 CircuitNamespace circuitNamespace(loweringState.circuitOp);
1118 AnnotationSet annos(oldModule);
1119 Annotation verbatimAnno = annos.
getAnnotation(verbatimBlackBoxAnnoClass);
1124 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1125 SmallVector<hw::PortInfo, 8> ports;
1126 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1131 StringRef verilogName;
1132 if (
auto defName = oldModule.getDefname())
1133 verilogName = defName.value();
1135 verilogName = oldModule.getName();
1137 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1139 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1140 if (!filesAttr || filesAttr.empty()) {
1141 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1146 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1147 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1148 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1150 if (!primaryFileContent || !primaryOutputFile) {
1151 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1155 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1156 builder.getContext(), primaryOutputFile.getValue());
1158 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1159 auto verbatimSource = loweringState.getVerbatimSourceForFile(primaryFileName);
1162 SmallVector<Attribute> additionalFiles;
1166 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1167 auto file = cast<DictionaryAttr>(filesAttr[i]);
1168 auto content = file.getAs<StringAttr>(
"content");
1169 auto outputFile = file.getAs<StringAttr>(
"output_file");
1170 auto fileName = llvm::sys::path::filename(outputFile);
1172 if (!(content && outputFile)) {
1173 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1178 auto emitFile = loweringState.getEmitFileForFile(fileName);
1181 auto fileSymbolName = circuitNamespace.newName(fileName);
1182 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1183 outputFile.getValue(), fileSymbolName);
1184 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1185 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1186 builder.setInsertionPointAfter(
emitFile);
1187 loweringState.registerEmitFile(fileName,
emitFile);
1189 auto ext = llvm::sys::path::extension(outputFile.getValue());
1190 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1191 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1192 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1193 emitFile->setAttr(
"output_file", outputFileAttr);
1197 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1203 parameters = builder.getArrayAttr({});
1205 if (!verbatimSource) {
1206 verbatimSource = sv::SVVerbatimSourceOp::create(
1207 builder, oldModule.getLoc(),
1208 circuitNamespace.newName(primaryFileName.str()), {},
1209 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1210 additionalFiles.empty() ?
nullptr
1211 : builder.getArrayAttr(additionalFiles),
1212 builder.getStringAttr(verilogName));
1214 SymbolTable::setSymbolVisibility(
1215 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1217 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1220 return verbatimSource;
1224FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1225 Block *topLevelModule,
1226 CircuitLoweringState &loweringState) {
1227 if (
auto verbatimMod =
1228 lowerVerbatimExtModule(oldModule, topLevelModule, loweringState))
1231 AnnotationSet annos(oldModule);
1234 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1235 SmallVector<hw::PortInfo, 8> ports;
1236 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1240 StringRef verilogName;
1241 if (
auto defName = oldModule.getDefname())
1242 verilogName = defName.value();
1245 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1246 auto nameAttr = builder.getStringAttr(oldModule.getName());
1251 auto newModule = hw::HWModuleExternOp::create(
1252 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1253 SymbolTable::setSymbolVisibility(newModule,
1254 SymbolTable::getSymbolVisibility(oldModule));
1256 bool hasOutputPort =
1257 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1258 if (!hasOutputPort &&
1260 internalVerifBlackBoxAnnoClass) &&
1261 loweringState.isInDUT(oldModule))
1262 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1265 if (
auto extReqs = oldModule.getExternalRequirements();
1266 extReqs && !extReqs.empty())
1267 newModule->setAttr(
"circt.external_requirements", extReqs);
1269 if (handleForceNameAnnos(oldModule, annos, loweringState))
1272 loweringState.processRemainingAnnotations(oldModule, annos);
1276sv::SVVerbatimModuleOp FIRRTLModuleLowering::lowerVerbatimExtModule(
1277 FExtModuleOp oldModule, Block *topLevelModule,
1278 CircuitLoweringState &loweringState) {
1280 AnnotationSet annos(oldModule);
1282 auto verbatimSource =
1283 getVerbatimSourceForExtModule(oldModule, topLevelModule, loweringState);
1285 if (!verbatimSource)
1288 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1289 SmallVector<hw::PortInfo, 8> ports;
1290 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1294 StringRef verilogName;
1295 if (
auto defName = oldModule.getDefname())
1296 verilogName = defName.value();
1298 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1300 auto newModule = sv::SVVerbatimModuleOp::create(
1303 builder.getStringAttr(oldModule.getName()),
1305 FlatSymbolRefAttr::get(verbatimSource),
1306 parameters ? parameters : builder.getArrayAttr({}),
1307 verilogName.empty() ? StringAttr{}
1308 : builder.getStringAttr(verilogName));
1310 SymbolTable::setSymbolVisibility(newModule,
1311 SymbolTable::getSymbolVisibility(oldModule));
1313 bool hasOutputPort =
1314 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1315 if (!hasOutputPort &&
1317 internalVerifBlackBoxAnnoClass) &&
1318 loweringState.isInDUT(oldModule))
1319 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1322 if (
auto extReqs = oldModule.getExternalRequirements();
1323 extReqs && !extReqs.empty())
1324 newModule->setAttr(
"circt.external_requirements", extReqs);
1326 if (handleForceNameAnnos(oldModule, annos, loweringState))
1329 loweringState.processRemainingAnnotations(oldModule, annos);
1334FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1335 Block *topLevelModule,
1336 CircuitLoweringState &loweringState) {
1338 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1339 SmallVector<hw::PortInfo, 8> ports;
1340 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1345 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1346 auto newModule = hw::HWModuleExternOp::create(
1347 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1348 oldModule.getModuleNameAttr());
1349 loweringState.processRemainingAnnotations(oldModule,
1350 AnnotationSet(oldModule));
1356FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1357 CircuitLoweringState &loweringState) {
1359 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1360 SmallVector<hw::PortInfo, 8> ports;
1361 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1366 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1367 auto nameAttr = builder.getStringAttr(oldModule.getName());
1369 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1371 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1372 newModule.setCommentAttr(comment);
1375 SmallVector<StringRef, 13> attrNames = {
1380 oldModule.getSymNameAttrName(),
1387 mlir::SymbolOpInterface::getDefaultVisibilityAttrName(),
1390 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1391 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1393 llvm::make_filter_range(oldModule->getAttrs(), [&](
auto namedAttr) {
1394 return !attrSet.count(namedAttr.getName()) &&
1395 !newModule->getAttrDictionary().contains(namedAttr.getName());
1397 newAttrs.push_back(i);
1399 newModule->setAttrs(newAttrs);
1403 SymbolTable::setSymbolVisibility(newModule,
1404 SymbolTable::getSymbolVisibility(oldModule));
1407 AnnotationSet annos(oldModule);
1410 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1414 if (
auto testBenchDir = loweringState.getTestBenchDirectory())
1415 if (loweringState.isInTestHarness(oldModule)) {
1416 if (!newModule->hasAttr(
"output_file"))
1417 newModule->setAttr(
"output_file", testBenchDir);
1418 newModule.setCommentAttr(
1419 builder.getStringAttr(
"VCS coverage exclude_file"));
1422 if (handleForceNameAnnos(oldModule, annos, loweringState))
1425 loweringState.processRemainingAnnotations(oldModule, annos);
1434 Operation *insertPoint) {
1435 if (!value.hasOneUse())
1438 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1439 if (!attach || attach.getNumOperands() != 2)
1443 auto loweredType =
lowerType(value.getType());
1444 if (loweredType.isInteger(0))
1449 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1450 auto *op = attachedValue.getDefiningOp();
1451 if (op && op->getBlock() == insertPoint->getBlock() &&
1452 !op->isBeforeInBlock(insertPoint))
1457 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1471 CircuitLoweringState &loweringState) {
1476 Operation *connectOp =
nullptr;
1477 for (
auto &use : flipValue.getUses()) {
1480 if (use.getOperandNumber() != 0)
1482 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1488 connectOp = use.getOwner();
1498 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1499 if (loweredType.isInteger(0))
1504 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1506 auto connectSrc = connectOp->getOperand(1);
1509 if (!isa<FIRRTLType>(connectSrc.getType())) {
1517 mlir::UnrealizedConversionCastOp::create(
1527 if (destTy != connectSrc.getType() &&
1528 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1529 isa<BaseTypeAliasType>(destTy))) {
1531 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1533 if (!destTy.isGround()) {
1537 }
else if (destTy.getBitWidthOrSentinel() !=
1539 .getBitWidthOrSentinel()) {
1542 auto destWidth = destTy.getBitWidthOrSentinel();
1543 assert(destWidth != -1 &&
"must know integer widths");
1544 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1556 SmallVector<SubfieldOp> accesses;
1557 for (
auto *op : structValue.getUsers()) {
1558 assert(isa<SubfieldOp>(op));
1559 auto fieldAccess = cast<SubfieldOp>(op);
1561 fieldAccess.getInput().getType().base().getElementIndex(field);
1562 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1563 accesses.push_back(fieldAccess);
1571LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1572 FModuleOp oldModule, hw::HWModuleOp newModule,
1573 CircuitLoweringState &loweringState) {
1574 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1581 bodyBuilder.setInsertionPoint(cursor);
1584 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1585 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1586 "port count mismatch");
1588 SmallVector<Value, 4> outputs;
1591 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1592 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1594 unsigned nextHWInputArg = 0;
1595 int hwPortIndex = -1;
1596 for (
auto [firrtlPortIndex, port] : llvm::enumerate(firrtlPorts)) {
1598 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1606 if (!port.isOutput() && !isZeroWidth) {
1609 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1615 oldArg.replaceAllUsesWith(newArg);
1621 if (isZeroWidth && port.isInput()) {
1623 WireOp::create(bodyBuilder, port.type,
1624 "." + port.getName().str() +
".0width_input")
1626 oldArg.replaceAllUsesWith(newArg);
1634 outputs.push_back(value);
1635 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1641 auto newArg = WireOp::create(bodyBuilder, port.type,
1642 "." + port.getName().str() +
".output");
1645 oldArg.replaceAllUsesWith(newArg.getResult());
1648 auto resultHWType = loweringState.lowerType(port.type, port.loc);
1649 if (!resultHWType.isInteger(0)) {
1652 outputs.push_back(output);
1655 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1656 newArg.setInnerSymAttr(sym);
1657 newModule.setPortSymbolAttr(hwPortIndex, {});
1663 outputOp->setOperands(outputs);
1666 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1667 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1668 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1669 oldBlockInstList.begin(), oldBlockInstList.end());
1680FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1681 CircuitLoweringState &loweringState) {
1682 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1687 auto oldOp = cast<FormalOp>(loweringState.getOldModule(newOp));
1688 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1689 auto oldModule = cast<FModuleOp>(
1690 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1691 auto newModule = cast<hw::HWModuleOp>(loweringState.getNewModule(oldModule));
1694 SmallVector<Value> symbolicInputs;
1695 for (
auto arg : newModule.getBody().getArguments())
1696 symbolicInputs.push_back(verif::SymbolicValueOp::create(
1697 builder, arg.getLoc(), arg.getType(),
1698 newModule.getArgName(arg.getArgNumber())));
1701 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1702 newModule.getModuleNameAttr(), symbolicInputs);
1709FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1710 CircuitLoweringState &loweringState) {
1711 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1714 auto oldOp = cast<SimulationOp>(loweringState.getOldModule(newOp));
1715 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1716 auto oldModule = cast<FModuleLike>(
1717 *loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1719 cast<hw::HWModuleLike>(loweringState.getNewModule(oldModule));
1723 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1724 newOp.getBody()->args_end());
1725 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1726 newModule.getModuleNameAttr(), inputs);
1727 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1737struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1739 FIRRTLLowering(hw::HWModuleOp module, CircuitLoweringState &circuitState)
1740 : theModule(module), circuitState(circuitState),
1741 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1742 backedgeBuilder(builder, module.
getLoc()) {}
1744 LogicalResult
run();
1747 Value getOrCreateClockConstant(seq::ClockConst clock);
1748 Value getOrCreateIntConstant(
const APInt &value);
1749 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1750 bool isSigned =
false) {
1751 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1753 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1754 Attribute getZeroAttributeForType(Type type);
1755 Value getZeroValueForType(Type type);
1756 Value getOrCreateXConstant(
unsigned numBits);
1757 Value getOrCreateZConstant(Type type);
1758 Value getPossiblyInoutLoweredValue(Value value);
1759 Value getLoweredValue(Value value);
1760 Value getLoweredNonClockValue(Value value);
1761 Value getLoweredAndExtendedValue(Value value, Type destType);
1762 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1763 LogicalResult setLowering(Value orig, Value result);
1764 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1765 template <
typename ResultOpType,
typename... CtorArgTypes>
1766 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1767 template <
typename ResultOpType,
typename... CtorArgTypes>
1768 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1769 Backedge createBackedge(Location loc, Type type);
1770 Backedge createBackedge(Value orig, Type type);
1771 bool updateIfBackedge(Value dest, Value src);
1774 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1779 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1780 if (forceable.isForceable())
1788 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1789 auto attr = op.getInnerSymAttr();
1793 if (requiresInnerSymbol(op))
1795 op.getContext(), attr, 0,
1796 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
1803 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1804 Operation *instanceOp,
1805 SmallVectorImpl<Value> &inputOperands);
1807 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1811 Value getReadValue(Value v);
1813 Value getNonClockValue(Value v);
1815 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1816 sv::ResetType resetStyle, sv::EventControl resetEdge,
1817 Value reset,
const std::function<
void(
void)> &body = {},
1818 const std::function<void(
void)> &resetBody = {});
1819 void addToAlwaysBlock(Value clock,
1820 const std::function<
void(
void)> &body = {}) {
1821 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1822 sv::EventControl(), Value(), body,
1823 std::function<
void(
void)>());
1826 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1827 std::function<
void(
void)> emit);
1828 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1829 std::function<
void(
void)> elseCtor = {});
1830 void addToInitialBlock(std::function<
void(
void)> body);
1831 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1832 std::function<
void(
void)> elseCtor = {});
1833 Value getExtOrTruncAggregateValue(Value array, FIRRTLBaseType sourceType,
1834 FIRRTLBaseType destType,
1835 bool allowTruncate);
1836 Value createArrayIndexing(Value array, Value index);
1837 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1839 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1840 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1841 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1844 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1845 UnloweredOpResult handleUnloweredOp(Operation *op);
1846 LogicalResult visitExpr(ConstantOp op);
1847 LogicalResult visitExpr(SpecialConstantOp op);
1848 LogicalResult visitExpr(SubindexOp op);
1849 LogicalResult visitExpr(SubaccessOp op);
1850 LogicalResult visitExpr(SubfieldOp op);
1851 LogicalResult visitExpr(VectorCreateOp op);
1852 LogicalResult visitExpr(BundleCreateOp op);
1853 LogicalResult visitExpr(FEnumCreateOp op);
1854 LogicalResult visitExpr(AggregateConstantOp op);
1855 LogicalResult visitExpr(IsTagOp op);
1856 LogicalResult visitExpr(SubtagOp op);
1857 LogicalResult visitExpr(TagExtractOp op);
1858 LogicalResult visitUnhandledOp(Operation *op) {
return failure(); }
1859 LogicalResult visitInvalidOp(Operation *op) {
1860 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1861 return visitUnrealizedConversionCast(castOp);
1866 LogicalResult visitDecl(WireOp op);
1867 LogicalResult visitDecl(NodeOp op);
1868 LogicalResult visitDecl(RegOp op);
1869 LogicalResult visitDecl(RegResetOp op);
1870 LogicalResult visitDecl(MemOp op);
1871 LogicalResult visitDecl(InstanceOp oldInstance);
1872 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1873 LogicalResult visitDecl(VerbatimWireOp op);
1874 LogicalResult visitDecl(ContractOp op);
1877 LogicalResult lowerNoopCast(Operation *op);
1878 LogicalResult visitExpr(AsSIntPrimOp op);
1879 LogicalResult visitExpr(AsUIntPrimOp op);
1880 LogicalResult visitExpr(AsClockPrimOp op);
1881 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1883 LogicalResult visitExpr(HWStructCastOp op);
1884 LogicalResult visitExpr(BitCastOp op);
1886 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1887 LogicalResult visitExpr(CvtPrimOp op);
1888 LogicalResult visitExpr(NotPrimOp op);
1889 LogicalResult visitExpr(NegPrimOp op);
1890 LogicalResult visitExpr(PadPrimOp op);
1891 LogicalResult visitExpr(XorRPrimOp op);
1892 LogicalResult visitExpr(AndRPrimOp op);
1893 LogicalResult visitExpr(OrRPrimOp op);
1896 template <
typename ResultUnsignedOpType,
1897 typename ResultSignedOpType = ResultUnsignedOpType>
1898 LogicalResult lowerBinOp(Operation *op);
1899 template <
typename ResultOpType>
1900 LogicalResult lowerBinOpToVariadic(Operation *op);
1902 template <
typename ResultOpType>
1903 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1905 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1906 ICmpPredicate unsignedOp);
1907 template <
typename SignedOp,
typename Un
signedOp>
1908 LogicalResult lowerDivLikeOp(Operation *op);
1910 LogicalResult visitExpr(CatPrimOp op);
1912 LogicalResult visitExpr(AndPrimOp op) {
1913 return lowerBinOpToVariadic<comb::AndOp>(op);
1915 LogicalResult visitExpr(OrPrimOp op) {
1916 return lowerBinOpToVariadic<comb::OrOp>(op);
1918 LogicalResult visitExpr(XorPrimOp op) {
1919 return lowerBinOpToVariadic<comb::XorOp>(op);
1921 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1922 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1924 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1925 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1927 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1928 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1930 LogicalResult visitExpr(AddPrimOp op) {
1931 return lowerBinOpToVariadic<comb::AddOp>(op);
1933 LogicalResult visitExpr(EQPrimOp op) {
1934 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1936 LogicalResult visitExpr(NEQPrimOp op) {
1937 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1939 LogicalResult visitExpr(LTPrimOp op) {
1940 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1942 LogicalResult visitExpr(LEQPrimOp op) {
1943 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1945 LogicalResult visitExpr(GTPrimOp op) {
1946 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1948 LogicalResult visitExpr(GEQPrimOp op) {
1949 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1952 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1953 LogicalResult visitExpr(MulPrimOp op) {
1954 return lowerBinOpToVariadic<comb::MulOp>(op);
1956 LogicalResult visitExpr(DivPrimOp op) {
1957 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1959 LogicalResult visitExpr(RemPrimOp op) {
1960 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1964 LogicalResult visitExpr(IsXIntrinsicOp op);
1965 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1966 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1967 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1968 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1969 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1970 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1971 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1972 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1973 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1974 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1975 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1976 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1977 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1978 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1979 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1980 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1981 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1982 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1983 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1984 LogicalResult visitExpr(LTLAcceptOnIntrinsicOp op);
1985 LogicalResult visitExpr(LTLPastIntrinsicOp op);
1986 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1988 template <
typename TargetOp,
typename IntrinsicOp>
1989 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1990 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1991 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1992 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1993 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1994 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1995 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1996 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1999 LogicalResult visitExpr(BitsPrimOp op);
2000 LogicalResult visitExpr(InvalidValueOp op);
2001 LogicalResult visitExpr(HeadPrimOp op);
2002 LogicalResult visitExpr(ShlPrimOp op);
2003 LogicalResult visitExpr(ShrPrimOp op);
2004 LogicalResult visitExpr(DShlPrimOp op) {
2005 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2007 LogicalResult visitExpr(DShrPrimOp op) {
2008 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2010 LogicalResult visitExpr(DShlwPrimOp op) {
2011 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2013 LogicalResult visitExpr(TailPrimOp op);
2014 LogicalResult visitExpr(MuxPrimOp op);
2015 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2016 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2017 LogicalResult visitExpr(MultibitMuxOp op);
2018 LogicalResult visitExpr(VerbatimExprOp op);
2019 LogicalResult visitExpr(XMRRefOp op);
2020 LogicalResult visitExpr(XMRDerefOp op);
2023 LogicalResult visitExpr(TimeOp op);
2024 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2027 LogicalResult lowerVerificationStatement(
2028 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2029 Value enable, StringAttr messageAttr, ValueRange operands,
2030 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2031 LogicalResult lowerVerificationStatementToCore(
2032 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2033 Value enable, StringAttr nameAttr, EventControl eventControl);
2035 LogicalResult visitStmt(SkipOp op);
2037 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2038 LogicalResult visitStmt(ConnectOp op);
2039 LogicalResult visitStmt(MatchingConnectOp op);
2040 LogicalResult visitStmt(ForceOp op);
2042 std::optional<Value> getLoweredFmtOperand(Value operand);
2043 LogicalResult loweredFmtOperands(ValueRange operands,
2044 SmallVectorImpl<Value> &loweredOperands);
2045 FailureOr<Value> lowerSimFormatString(StringRef originalFormatString,
2046 ValueRange operands);
2047 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2051 LogicalResult lowerStatementWithFd(
2052 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2053 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2057 LogicalResult visitPrintfLike(T op,
2058 const FileDescriptorInfo &fileDescriptorInfo,
2059 bool usePrintfCond);
2060 LogicalResult visitStmt(PrintFOp op);
2061 LogicalResult visitStmt(FPrintFOp op);
2062 LogicalResult visitStmt(FFlushOp op);
2063 LogicalResult visitStmt(StopOp op);
2064 LogicalResult visitStmt(AssertOp op);
2065 LogicalResult visitStmt(AssumeOp op);
2066 LogicalResult visitStmt(CoverOp op);
2067 LogicalResult visitStmt(AttachOp op);
2068 LogicalResult visitStmt(RefForceOp op);
2069 LogicalResult visitStmt(RefForceInitialOp op);
2070 LogicalResult visitStmt(RefReleaseOp op);
2071 LogicalResult visitStmt(RefReleaseInitialOp op);
2072 LogicalResult visitStmt(BindOp op);
2074 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2075 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2076 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2078 LogicalResult fixupLTLOps();
2081 return circuitState.lowerType(type, builder.getLoc());
2086 hw::HWModuleOp theModule;
2089 CircuitLoweringState &circuitState;
2092 ImplicitLocOpBuilder builder;
2097 DenseMap<Value, Value> valueMapping;
2101 DenseMap<Value, Value> fromClockMapping;
2105 DenseMap<Attribute, Value> hwConstantMap;
2106 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2110 DenseMap<unsigned, Value> hwConstantXMap;
2111 DenseMap<Type, Value> hwConstantZMap;
2117 DenseMap<Value, Value> readInOutCreated;
2121 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2122 sv::ResetType, sv::EventControl, Value>;
2123 llvm::SmallDenseMap<AlwaysKeyType, std::pair<sv::AlwaysOp, sv::IfOp>>
2125 llvm::SmallDenseMap<std::pair<Block *, Attribute>, sv::IfDefOp> ifdefBlocks;
2126 llvm::SmallDenseMap<Block *, sv::InitialOp> initialBlocks;
2130 hw::InnerSymbolNamespace moduleNamespace;
2134 BackedgeBuilder backedgeBuilder;
2139 llvm::MapVector<Value, Value> backedges;
2146 DenseSet<Operation *> maybeUnusedValues;
2148 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2149 void maybeUnused(Value value) {
2150 if (
auto *op = value.getDefiningOp())
2162 SetVector<Operation *> ltlOpFixupWorklist;
2167 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2169 void addToWorklist(Block &block) {
2170 worklist.push_back({block.begin(), block.end()});
2172 void addToWorklist(Region ®ion) {
2173 for (
auto &block : llvm::reverse(region))
2174 addToWorklist(block);
2180FIRRTLModuleLowering::lowerModuleBody(hw::HWModuleOp module,
2181 CircuitLoweringState &loweringState) {
2182 return FIRRTLLowering(module, loweringState).run();
2185LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2186 OpBuilder b(&getContext());
2187 fileOp->walk([&](Operation *op) {
2188 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2189 b.setInsertionPointAfter(bindOp);
2190 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2198FIRRTLModuleLowering::lowerBody(Operation *op,
2199 CircuitLoweringState &loweringState) {
2200 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2202 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2203 return lowerFormalBody(formalOp, loweringState);
2204 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2205 return lowerSimulationBody(simulationOp, loweringState);
2206 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2207 return lowerFileBody(fileOp);
2212LogicalResult FIRRTLLowering::run() {
2215 for (
auto arg : theModule.getBodyBlock()->getArguments())
2216 if (failed(setLowering(arg, arg)))
2223 addToWorklist(theModule.getBody());
2224 SmallVector<Operation *, 16> opsToRemove;
2226 while (!worklist.empty()) {
2227 auto &[opsIt, opsEnd] = worklist.back();
2228 if (opsIt == opsEnd) {
2229 worklist.pop_back();
2232 Operation *op = &*opsIt++;
2234 builder.setInsertionPoint(op);
2235 builder.setLoc(op->getLoc());
2236 auto done = succeeded(dispatchVisitor(op));
2237 circuitState.processRemainingAnnotations(op, AnnotationSet(op));
2239 opsToRemove.push_back(op);
2241 switch (handleUnloweredOp(op)) {
2242 case AlreadyLowered:
2245 opsToRemove.push_back(op);
2247 case LoweringFailure:
2259 for (
auto &[backedge, value] : backedges) {
2260 SmallVector<Location> driverLocs;
2266 if (backedge == value) {
2267 Location edgeLoc = backedge.getLoc();
2268 if (driverLocs.empty()) {
2269 mlir::emitError(edgeLoc,
"sink does not have a driver");
2271 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2272 for (
auto loc : driverLocs)
2273 diag.attachNote(loc) <<
"through driver here";
2279 auto *it = backedges.find(value);
2280 if (it == backedges.end())
2283 driverLocs.push_back(value.getLoc());
2286 if (
auto *defOp = backedge.getDefiningOp())
2287 maybeUnusedValues.erase(defOp);
2288 backedge.replaceAllUsesWith(value);
2295 hw::ConstantOp zeroI0;
2296 while (!opsToRemove.empty()) {
2297 auto *op = opsToRemove.pop_back_val();
2302 for (
auto result : op->getResults()) {
2306 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2308 builder.getIntegerType(0), 0);
2309 maybeUnusedValues.insert(zeroI0);
2311 result.replaceAllUsesWith(zeroI0);
2314 if (!op->use_empty()) {
2315 auto d = op->emitOpError(
2316 "still has uses; should remove ops in reverse order of visitation");
2317 SmallPtrSet<Operation *, 2> visited;
2318 for (
auto *user : op->getUsers())
2319 if (visited.insert(user).second)
2320 d.attachNote(user->getLoc())
2321 <<
"used by " << user->getName() <<
" op";
2324 maybeUnusedValues.erase(op);
2330 SmallVector<Operation *> worklist(maybeUnusedValues.begin(),
2331 maybeUnusedValues.end());
2332 while (!worklist.empty()) {
2333 auto *op = worklist.pop_back_val();
2334 maybeUnusedValues.erase(op);
2335 if (!isOpTriviallyDead(op))
2337 for (
auto operand : op->getOperands())
2338 if (
auto *defOp = operand.getDefiningOp())
2339 if (maybeUnusedValues.insert(defOp).second)
2340 worklist.push_back(defOp);
2346 if (failed(fixupLTLOps()))
2357Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2358 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2360 auto &entry = hwConstantMap[attr];
2364 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2365 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2371Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2372 auto attr = builder.getIntegerAttr(
2373 builder.getIntegerType(value.getBitWidth()), value);
2375 auto &entry = hwConstantMap[attr];
2379 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2386Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2389 if (hw::type_isa<IntegerType>(type))
2390 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2392 auto cache = hwAggregateConstantMap.lookup({value, type});
2397 SmallVector<Attribute> values;
2398 for (
auto e : llvm::enumerate(cast<ArrayAttr>(value))) {
2400 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2401 subType = array.getElementType();
2402 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2403 subType = structType.getElements()[e.index()].type;
2405 assert(
false &&
"type must be either array or struct");
2407 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2411 if (hw::type_isa<hw::ArrayType>(type))
2412 std::reverse(values.begin(), values.end());
2414 auto &entry = hwAggregateConstantMap[{value, type}];
2415 entry = builder.getArrayAttr(values);
2423 const std::function<LogicalResult()> &fn) {
2424 assert(failedOperand &&
"Should be called on the failed operand");
2432Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2434 auto &entry = hwConstantXMap[numBits];
2438 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2439 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2440 entryBuilder.getIntegerType(numBits));
2444Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2445 auto &entry = hwConstantZMap[type];
2447 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2448 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2462Attribute FIRRTLLowering::getZeroAttributeForType(Type type) {
2463 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2464 return builder.getIntegerAttr(intType, 0);
2465 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type)) {
2469 auto element = getZeroAttributeForType(array.getElementType());
2470 SmallVector<Attribute> values(array.getNumElements(), element);
2471 return builder.getArrayAttr(values);
2473 if (
auto structType = hw::type_dyn_cast<hw::StructType>(type)) {
2474 SmallVector<Attribute> values;
2475 values.reserve(structType.getElements().size());
2476 for (
auto &field : structType.getElements())
2477 values.push_back(getZeroAttributeForType(field.type));
2478 return builder.getArrayAttr(values);
2480 llvm_unreachable(
"unsupported lowered type for zero attribute");
2486Value FIRRTLLowering::getZeroValueForType(Type type) {
2487 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2488 return getOrCreateIntConstant(intType.getWidth(), 0);
2489 return hw::AggregateConstantOp::create(
2490 builder, type, cast<ArrayAttr>(getZeroAttributeForType(type)));
2497Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2499 if (
auto lowering = valueMapping.lookup(value)) {
2500 assert(!isa<FIRRTLType>(lowering.getType()) &&
2501 "Lowered value should be a non-FIRRTL value");
2510Value FIRRTLLowering::getLoweredValue(Value value) {
2511 auto result = getPossiblyInoutLoweredValue(value);
2517 if (sv::getHandleElementType(result.getType()))
2518 return getReadValue(result);
2524Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2525 auto result = getLoweredValue(value);
2529 if (hw::type_isa<seq::ClockType>(result.getType()))
2530 return getNonClockValue(result);
2538Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2539 FIRRTLBaseType sourceType,
2540 FIRRTLBaseType destType,
2541 bool allowTruncate) {
2542 SmallVector<Value> resultBuffer;
2545 auto cast = [&](Value value, FIRRTLBaseType sourceType,
2546 FIRRTLBaseType destType) {
2549 auto resultType = builder.getIntegerType(destWidth);
2551 if (srcWidth == destWidth)
2554 if (srcWidth > destWidth) {
2556 return builder.createOrFold<comb::ExtractOp>(resultType, value, 0);
2558 builder.emitError(
"operand should not be a truncation");
2563 return comb::createOrFoldSExt(builder, value, resultType);
2564 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2565 return builder.createOrFold<comb::ConcatOp>(zero, value);
2569 std::function<LogicalResult(Value, FIRRTLBaseType, FIRRTLBaseType)> recurse =
2570 [&](Value src, FIRRTLBaseType srcType,
2571 FIRRTLBaseType destType) -> LogicalResult {
2572 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2573 .Case<FVectorType>([&](
auto srcVectorType) {
2575 unsigned size = resultBuffer.size();
2576 unsigned indexWidth =
2578 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2579 destVectorType.getNumElements());
2581 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2583 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2584 destVectorType.getElementType())))
2587 SmallVector<Value> temp(resultBuffer.begin() + size,
2588 resultBuffer.end());
2589 auto array = builder.createOrFold<hw::ArrayCreateOp>(temp);
2590 resultBuffer.resize(size);
2591 resultBuffer.push_back(array);
2594 .Case<BundleType>([&](BundleType srcStructType) {
2596 unsigned size = resultBuffer.size();
2599 if (destStructType.getNumElements() != srcStructType.getNumElements())
2602 for (
auto elem : llvm::enumerate(destStructType)) {
2603 auto structExtract =
2605 if (failed(recurse(structExtract,
2606 srcStructType.getElementType(elem.index()),
2607 destStructType.getElementType(elem.index()))))
2610 SmallVector<Value> temp(resultBuffer.begin() + size,
2611 resultBuffer.end());
2612 auto newStruct = builder.createOrFold<hw::StructCreateOp>(
2614 resultBuffer.resize(size);
2615 resultBuffer.push_back(newStruct);
2618 .Case<IntType>([&](
auto) {
2619 if (
auto result = cast(src, srcType, destType)) {
2620 resultBuffer.push_back(result);
2625 .Default([&](
auto) {
return failure(); });
2628 if (failed(recurse(array, sourceType, destType)))
2631 assert(resultBuffer.size() == 1 &&
2632 "resultBuffer must only contain a result array if `success` is true");
2633 return resultBuffer[0];
2641Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2642 auto srcType = cast<FIRRTLBaseType>(src.getType());
2643 auto dstType = cast<FIRRTLBaseType>(target);
2644 auto loweredSrc = getLoweredValue(src);
2647 auto dstWidth = dstType.getBitWidthOrSentinel();
2663 return getOrCreateIntConstant(dstWidth, 0);
2666 auto loweredSrcType = loweredSrc.getType();
2667 auto loweredDstType =
lowerType(dstType);
2670 if (loweredSrcType == loweredDstType)
2674 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2676 if (loweredSrcType != loweredDstType &&
2677 (isa<hw::TypeAliasType>(loweredSrcType) ||
2678 isa<hw::TypeAliasType>(loweredDstType))) {
2679 return builder.createOrFold<hw::BitcastOp>(loweredDstType, loweredSrc);
2684 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2685 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2688 if (isa<seq::ClockType>(loweredSrcType)) {
2689 builder.emitError(
"cannot use clock type as an integer");
2693 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2694 if (!intSourceType) {
2695 builder.emitError(
"operand of type ")
2696 << loweredSrcType <<
" cannot be used as an integer";
2700 auto loweredSrcWidth = intSourceType.getWidth();
2701 if (loweredSrcWidth ==
unsigned(dstWidth))
2704 if (loweredSrcWidth >
unsigned(dstWidth)) {
2705 builder.emitError(
"operand should not be a truncation");
2712 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2714 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2715 return builder.createOrFold<comb::ConcatOp>(zero, loweredSrc);
2723Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2726 "input/output value should be FIRRTL");
2730 if (destWidth == -1)
2733 auto result = getLoweredValue(value);
2745 return getOrCreateIntConstant(destWidth, 0);
2749 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2751 if (destType == value.getType())
2754 return getExtOrTruncAggregateValue(
2761 if (srcWidth ==
unsigned(destWidth))
2767 if (srcWidth >
unsigned(destWidth)) {
2768 auto resultType = builder.getIntegerType(destWidth);
2769 return builder.createOrFold<comb::ExtractOp>(resultType, result, 0);
2772 auto resultType = builder.getIntegerType(destWidth);
2778 return comb::createOrFoldSExt(builder, result, resultType);
2780 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2781 return builder.createOrFold<comb::ConcatOp>(zero, result);
2794std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2797 if (isa<TimeOp>(operand.getDefiningOp()))
2798 return sv::TimeOp::create(builder);
2799 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2803 auto loweredValue = getLoweredValue(operand);
2804 if (!loweredValue) {
2808 loweredValue = getOrCreateIntConstant(1, 0);
2814 if (intTy.isSigned())
2815 loweredValue = sv::SystemFunctionOp::create(
2816 builder, loweredValue.getType(),
"signed", loweredValue);
2818 return loweredValue;
2822FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2823 SmallVectorImpl<Value> &loweredOperands) {
2824 for (
auto operand : operands) {
2825 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2830 loweredOperands.push_back(*loweredValue);
2836FIRRTLLowering::lowerSimFormatString(StringRef originalFormatString,
2837 ValueRange operands) {
2838 SmallVector<Value> fragments;
2840 auto emitLiteral = [&](StringRef text) {
2842 fragments.push_back(sim::FormatLiteralOp::create(builder, text));
2845 auto emitIntFormat = [&](Value operand,
char specifier,
2846 IntegerAttr widthAttr) -> FailureOr<Value> {
2849 loweredValue = getLoweredNonClockValue(operand);
2851 loweredValue = getLoweredValue(operand);
2852 if (!loweredValue) {
2855 loweredValue = getOrCreateIntConstant(1, 0);
2858 if (!mlir::isa<IntegerType>(loweredValue.getType())) {
2859 emitError(builder.getLoc(),
"lower-to-core requires integer printf "
2861 << specifier <<
"'";
2865 switch (specifier) {
2867 return sim::FormatBinOp::create(builder, loweredValue,
2868 builder.getBoolAttr(
false),
2869 builder.getI8IntegerAttr(
'0'), widthAttr)
2872 UnitAttr signedAttr;
2873 if (
auto intTy = dyn_cast<IntType>(operand.getType());
2874 intTy && intTy.isSigned())
2875 signedAttr = builder.getUnitAttr();
2876 return sim::FormatDecOp::create(
2877 builder, loweredValue, builder.getBoolAttr(
false),
2878 builder.getI8IntegerAttr(
' '), widthAttr, signedAttr)
2882 return sim::FormatHexOp::create(builder, loweredValue,
2883 builder.getBoolAttr(
false),
2884 builder.getBoolAttr(
false),
2885 builder.getI8IntegerAttr(
'0'), widthAttr)
2888 return sim::FormatCharOp::create(builder, loweredValue).getResult();
2890 llvm_unreachable(
"unsupported FIRRTL format specifier");
2894 SmallString<32> literal;
2895 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
2896 char c = originalFormatString[i];
2899 emitLiteral(literal);
2902 SmallString<6> width;
2903 c = originalFormatString[++i];
2906 c = originalFormatString[++i];
2909 IntegerAttr widthAttr;
2910 if (!width.empty()) {
2911 unsigned widthValue;
2912 if (StringRef(width).getAsInteger(10, widthValue)) {
2913 emitError(builder.getLoc(),
"invalid FIRRTL printf width");
2916 widthAttr = builder.getI32IntegerAttr(widthValue);
2920 if (!width.empty()) {
2921 emitError(builder.getLoc(),
2922 "literal percents ('%%') may not specify a width");
2925 literal.push_back(
'%');
2929 if (operands.size() <= subIdx) {
2930 emitError(builder.getLoc(),
"not enough operands for printf format");
2934 if (c ==
'c' && widthAttr) {
2935 emitError(builder.getLoc(),
"ASCII character format specifiers ('%c') "
2936 "may not specify a width");
2945 auto fragment = emitIntFormat(operands[subIdx++], c, widthAttr);
2946 if (failed(fragment))
2948 fragments.push_back(*fragment);
2952 emitError(builder.getLoc(),
"unknown printf substitution '%")
2953 << width << c <<
"'";
2959 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
2960 literal.push_back(c);
2964 emitLiteral(literal);
2967 if (operands.size() <= subIdx) {
2968 emitError(builder.getLoc(),
"not enough operands for printf format");
2972 auto substitution = operands[subIdx++];
2974 emitError(builder.getLoc(),
"expected fstring operand for '{{}}' "
2980 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
2981 .template Case<HierarchicalModuleNameOp>([&](
auto) {
2982 fragments.push_back(sim::FormatHierPathOp::create(
2986 .
template Case<TimeOp>([&](
auto) {
2987 fragments.push_back(sim::FormatCurrentTimeOp::create(builder));
2990 .Default([&](
auto) {
2991 emitError(builder.getLoc(),
"has a substitution with "
2994 .attachNote(substitution.getLoc())
2995 <<
"op with an unimplemented lowering is here";
3004 literal.push_back(c);
3009 emitLiteral(literal);
3010 if (fragments.empty())
3011 return sim::FormatLiteralOp::create(builder,
"").getResult();
3012 if (fragments.size() == 1)
3013 return fragments.front();
3014 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
3017LogicalResult FIRRTLLowering::lowerStatementWithFd(
3018 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
3019 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
3021 bool failed =
false;
3022 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
3023 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
3024 addToAlwaysBlock(clock, [&]() {
3027 circuitState.usedPrintf =
true;
3029 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
3032 Value ifCond = cond;
3033 if (usePrintfCond) {
3035 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
3036 ifCond = builder.createOrFold<comb::AndOp>(ifCond, cond,
true);
3039 addIfProceduralBlock(ifCond, [&]() {
3043 if (fileDescriptor.isDefaultFd()) {
3048 auto fdOrError = callFileDescriptorLib(fileDescriptor);
3049 if (llvm::failed(fdOrError)) {
3055 failed = llvm::failed(fn(fd));
3063FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3064 circuitState.usedFileDescriptorLib =
true;
3065 circuitState.addFragment(
3066 theModule, sv::getFileDescriptorFragmentRef(builder.getContext()));
3069 if (
info.isSubstitutionRequired()) {
3070 SmallVector<Value> fileNameOperands;
3071 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3074 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3079 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3083 return sv::createProceduralFileDescriptorGetterCall(builder, builder.getLoc(),
3093LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3094 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3096 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3100 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3103 if (srcWidth != -1) {
3105 assert((srcWidth != 0) &&
3106 "Lowering produced value for zero width source");
3108 assert((srcWidth == 0) &&
3109 "Lowering produced null value but source wasn't zero width");
3113 assert(result &&
"Lowering of foreign type produced null value");
3116 auto &slot = valueMapping[orig];
3117 assert(!slot &&
"value lowered multiple times");
3124LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3128 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3129 auto &entry = hwConstantMap[cst.getValueAttr()];
3140 cst->moveBefore(&theModule.getBodyBlock()->front());
3144 return setLowering(orig, result);
3149template <
typename ResultOpType,
typename... CtorArgTypes>
3150LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3151 CtorArgTypes... args) {
3152 auto result = builder.createOrFold<ResultOpType>(args...);
3153 if (
auto *op = result.getDefiningOp())
3155 return setPossiblyFoldedLowering(orig->getResult(0), result);
3162template <
typename ResultOpType,
typename... CtorArgTypes>
3163LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3164 CtorArgTypes... args) {
3165 auto result = builder.createOrFold<ResultOpType>(args...);
3166 if (
auto *op = result.getDefiningOp())
3167 ltlOpFixupWorklist.insert(op);
3168 return setPossiblyFoldedLowering(orig->getResult(0), result);
3177Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3178 auto backedge = backedgeBuilder.
get(type, loc);
3179 backedges.insert({backedge, backedge});
3187Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3188 auto backedge = createBackedge(orig.getLoc(), type);
3189 (void)setLowering(orig, backedge);
3195bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3196 auto *backedgeIt = backedges.find(dest);
3197 if (backedgeIt == backedges.end())
3199 backedgeIt->second = src;
3207void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3208 const std::function<
void(
void)> &fn, Region ®ion) {
3212 auto oldIP = builder.saveInsertionPoint();
3214 builder.setInsertionPointToEnd(®ion.front());
3216 builder.restoreInsertionPoint(oldIP);
3220Value FIRRTLLowering::getReadValue(Value v) {
3221 Value result = readInOutCreated.lookup(v);
3227 auto oldIP = builder.saveInsertionPoint();
3228 if (
auto *vOp = v.getDefiningOp()) {
3229 builder.setInsertionPointAfter(vOp);
3233 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3238 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3239 result = getReadValue(arrayIndexInout.getInput());
3240 result = builder.createOrFold<hw::ArrayGetOp>(result,
3241 arrayIndexInout.getIndex());
3244 result = builder.createOrFold<sv::ReadInOutOp>(v);
3246 builder.restoreInsertionPoint(oldIP);
3247 readInOutCreated.insert({v, result});
3251Value FIRRTLLowering::getNonClockValue(Value v) {
3252 auto it = fromClockMapping.try_emplace(v, Value{});
3254 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3255 builder.setInsertionPointAfterValue(v);
3256 it.first->second = seq::FromClockOp::create(builder, v);
3258 return it.first->second;
3261void FIRRTLLowering::addToAlwaysBlock(
3262 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3263 sv::EventControl resetEdge, Value reset,
3264 const std::function<
void(
void)> &body,
3265 const std::function<
void(
void)> &resetBody) {
3266 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3267 resetStyle, resetEdge, reset};
3268 sv::AlwaysOp alwaysOp;
3269 sv::IfOp insideIfOp;
3270 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3274 assert(resetStyle != sv::ResetType::NoReset);
3287 auto createIfOp = [&]() {
3290 insideIfOp = sv::IfOp::create(
3291 builder, reset, [] {}, [] {});
3293 if (resetStyle == sv::ResetType::AsyncReset) {
3294 sv::EventControl events[] = {clockEdge, resetEdge};
3295 Value clocks[] = {clock, reset};
3297 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3298 if (resetEdge == sv::EventControl::AtNegEdge)
3299 llvm_unreachable(
"negative edge for reset is not expected");
3303 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3307 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3308 insideIfOp =
nullptr;
3310 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3314 assert(insideIfOp &&
"reset body must be initialized before");
3315 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3316 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3318 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3324 alwaysOp->moveBefore(builder.getInsertionBlock(),
3325 builder.getInsertionPoint());
3328LogicalResult FIRRTLLowering::emitGuards(Location loc,
3329 ArrayRef<Attribute> guards,
3330 std::function<
void(
void)> emit) {
3331 if (guards.empty()) {
3335 auto guard = dyn_cast<StringAttr>(guards[0]);
3337 return mlir::emitError(loc,
3338 "elements in `guards` array must be `StringAttr`");
3341 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3342 LogicalResult result = LogicalResult::failure();
3343 addToIfDefBlock(guard.getValue(), [&]() {
3344 result = emitGuards(loc, guards.drop_front(), emit);
3349void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3350 std::function<
void(
void)> thenCtor,
3351 std::function<
void(
void)> elseCtor) {
3352 auto condAttr = builder.getStringAttr(cond);
3353 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3355 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3356 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3361 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3363 ifdefBlocks[{builder.getBlock(), condAttr}] =
3364 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3368void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3369 auto op = initialBlocks.lookup(builder.getBlock());
3371 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3376 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3378 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3382void FIRRTLLowering::addIfProceduralBlock(Value cond,
3383 std::function<
void(
void)> thenCtor,
3384 std::function<
void(
void)> elseCtor) {
3387 auto insertIt = builder.getInsertionPoint();
3388 if (insertIt != builder.getBlock()->begin())
3389 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3390 if (ifOp.getCond() == cond) {
3391 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3392 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3397 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3409FIRRTLLowering::UnloweredOpResult
3410FIRRTLLowering::handleUnloweredOp(Operation *op) {
3412 if (!op->getRegions().empty() &&
3413 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3414 op->emitOpError(
"must explicitly handle its regions");
3415 return LoweringFailure;
3422 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3424 for (
auto ®ion : op->getRegions())
3425 addToWorklist(region);
3426 for (
auto &operand : op->getOpOperands())
3427 if (
auto lowered = getPossiblyInoutLoweredValue(operand.get()))
3428 operand.set(lowered);
3429 for (
auto result : op->getResults())
3430 (void)setLowering(result, result);
3431 return AlreadyLowered;
3443 if (op->getNumResults() == 1) {
3444 auto resultType = op->getResult(0).getType();
3447 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3449 (void)setLowering(op->getResult(0), Value());
3453 op->emitOpError(
"LowerToHW couldn't handle this operation");
3454 return LoweringFailure;
3457LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3460 return setLowering(op, Value());
3462 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3465LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3467 if (isa<ClockType>(op.getType())) {
3468 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3469 : seq::ClockConst::Low);
3471 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3473 return setLowering(op, cst);
3476FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3477 auto iIdx = getOrCreateIntConstant(
3486 if (sv::getHandleElementType(input.getType()))
3487 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3489 result = builder.createOrFold<hw::ArrayGetOp>(input, iIdx);
3490 if (
auto *definingOp = result.getDefiningOp())
3495FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3496 Value valueIdx = getLoweredAndExtOrTruncValue(
3498 UIntType::get(op->getContext(),
3501 .getNumElements())));
3503 op->emitError() <<
"input lowering failed";
3510 if (sv::getHandleElementType(input.getType()))
3511 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3513 result = createArrayIndexing(input, valueIdx);
3514 if (
auto *definingOp = result.getDefiningOp())
3519FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3520 auto resultType =
lowerType(op->getResult(0).getType());
3521 if (!resultType || !input) {
3522 op->emitError() <<
"subfield type lowering failed";
3529 .getElementName(op.getFieldIndex());
3531 if (sv::getHandleElementType(input.getType()))
3532 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3534 result = builder.createOrFold<hw::StructExtractOp>(input, field);
3535 if (
auto *definingOp = result.getDefiningOp())
3540LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3542 return setLowering(op, Value());
3544 auto input = getPossiblyInoutLoweredValue(op.getInput());
3546 return op.emitError() <<
"input lowering failed";
3548 auto result = lowerSubindex(op, input);
3551 return setLowering(op, *result);
3554LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3556 return setLowering(op, Value());
3558 auto input = getPossiblyInoutLoweredValue(op.getInput());
3560 return op.emitError() <<
"input lowering failed";
3562 auto result = lowerSubaccess(op, input);
3565 return setLowering(op, *result);
3568LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3571 if (getLoweredValue(op) || op->getNumOperands() == 0)
3575 return setLowering(op, Value());
3577 auto input = getPossiblyInoutLoweredValue(op.getInput());
3579 return op.emitError() <<
"input lowering failed";
3581 auto result = lowerSubfield(op, input);
3584 return setLowering(op, *result);
3587LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3588 auto resultType =
lowerType(op.getResult().getType());
3589 auto arrayType = cast<hw::ArrayType>(resultType);
3590 SmallVector<Value> operands;
3592 for (
auto oper : llvm::reverse(op.getOperands())) {
3593 auto val = getLoweredValue(oper);
3598 val = getZeroValueForType(arrayType.getElementType());
3600 operands.push_back(val);
3602 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3605LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3606 auto resultType =
lowerType(op.getResult().getType());
3607 auto structType = cast<hw::StructType>(resultType);
3608 SmallVector<Value> operands;
3609 for (
auto [oper, field] :
3610 llvm::zip_equal(op.getOperands(), structType.getElements())) {
3611 auto val = getLoweredValue(oper);
3616 val = getZeroValueForType(field.type);
3618 operands.push_back(val);
3620 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3623LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3626 return setLowering(op, Value());
3628 auto input = getLoweredValue(op.getInput());
3629 auto tagName = op.getFieldNameAttr();
3630 auto oldType = op.getType().base();
3632 auto element = *oldType.getElement(op.getFieldNameAttr());
3634 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3640 input = getOrCreateIntConstant(0, 0);
3642 auto tagType = structType.getFieldType(
"tag");
3643 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3644 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3646 auto bodyType = structType.getFieldType(
"body");
3647 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3648 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3649 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3651 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3652 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3655LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3656 auto resultType =
lowerType(op.getResult().getType());
3658 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3660 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3661 cast<ArrayAttr>(attr));
3664LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3668 return setLowering(op, getOrCreateIntConstant(1, 1));
3670 auto tagName = op.getFieldNameAttr();
3671 auto lhs = getLoweredValue(op.getInput());
3672 if (isa<hw::StructType>(lhs.getType()))
3675 auto index = op.getFieldIndex();
3676 auto enumType = op.getInput().getType().base();
3677 auto tagValue = enumType.getElementValueAttr(index);
3678 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3679 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3680 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3681 loweredTagValue, tagName);
3683 Type resultType = builder.getIntegerType(1);
3684 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3688LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3691 return setLowering(op, Value());
3693 auto tagName = op.getFieldNameAttr();
3694 auto input = getLoweredValue(op.getInput());
3696 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3699LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3702 return setLowering(op, Value());
3704 auto input = getLoweredValue(op.getInput());
3710 if (isa<hw::StructType>(input.getType())) {
3711 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3716 return setLowering(op, input);
3723LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3724 auto origResultType = op.getResult().getType();
3729 createBackedge(op.getResult(), origResultType);
3733 auto resultType =
lowerType(origResultType);
3737 if (resultType.isInteger(0)) {
3738 if (op.getInnerSym())
3739 return op.emitError(
"zero width wire is referenced by name [")
3740 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3741 return setLowering(op.getResult(), Value());
3745 auto innerSym = lowerInnerSymbol(op);
3746 auto name = op.getNameAttr();
3749 auto wire = hw::WireOp::create(
3750 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3752 if (
auto svAttrs = sv::getSVAttributes(op))
3753 sv::setSVAttributes(wire, svAttrs);
3755 return setLowering(op.getResult(), wire);
3758LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3759 auto resultTy =
lowerType(op.getType());
3762 resultTy = sv::NetType::get(op.getContext(), resultTy);
3764 SmallVector<Value, 4> operands;
3765 operands.reserve(op.getSubstitutions().size());
3766 for (
auto operand : op.getSubstitutions()) {
3767 auto lowered = getLoweredValue(operand);
3770 operands.push_back(lowered);
3773 ArrayAttr symbols = op.getSymbolsAttr();
3775 symbols = ArrayAttr::get(op.getContext(), {});
3777 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3781LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3782 auto operand = getLoweredValue(op.getInput());
3784 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3785 if (op.getInnerSym())
3786 return op.emitError(
"zero width node is referenced by name [")
3787 << *op.getInnerSym()
3788 <<
"] (e.g. in an XMR) but must be "
3790 return setLowering(op.getResult(), Value());
3796 auto name = op.getNameAttr();
3797 auto innerSym = lowerInnerSymbol(op);
3800 operand = hw::WireOp::create(builder, operand, name, innerSym);
3803 if (
auto svAttrs = sv::getSVAttributes(op)) {
3805 operand = hw::WireOp::create(builder, operand, name);
3806 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3809 return setLowering(op.getResult(), operand);
3812LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3813 auto resultType =
lowerType(op.getResult().getType());
3816 if (resultType.isInteger(0))
3817 return setLowering(op.getResult(), Value());
3819 Value clockVal = getLoweredValue(op.getClockVal());
3824 Attribute presetAttr;
3825 if (
auto initial = op.getInitialAttr()) {
3826 auto intTy = dyn_cast<IntegerType>(resultType);
3827 assert(intTy &&
"'initial' must be integer type");
3828 presetAttr = builder.getIntegerAttr(
3829 intTy, initial.getValue().zextOrTrunc(intTy.getWidth()));
3833 auto innerSym = lowerInnerSymbol(op);
3834 Backedge inputEdge = backedgeBuilder.
get(resultType);
3835 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3836 op.getNameAttr(), innerSym, presetAttr);
3839 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3840 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3841 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3842 reg->setAttr(
"firrtl.random_init_start", randomStart);
3843 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3844 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3847 if (
auto svAttrs = sv::getSVAttributes(op))
3848 sv::setSVAttributes(reg, svAttrs);
3851 (void)setLowering(op.getResult(), reg);
3855LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3856 auto resultType =
lowerType(op.getResult().getType());
3859 if (resultType.isInteger(0))
3860 return setLowering(op.getResult(), Value());
3862 Value clockVal = getLoweredValue(op.getClockVal());
3863 Value resetSignal = getLoweredValue(op.getResetSignal());
3865 Value resetValue = getLoweredAndExtOrTruncValue(
3868 if (!clockVal || !resetSignal || !resetValue)
3872 Attribute presetAttr;
3873 if (
auto initial = op.getInitialAttr()) {
3874 auto intTy = dyn_cast<IntegerType>(resultType);
3875 assert(intTy &&
"'initial' must be integer type");
3876 presetAttr = builder.getIntegerAttr(
3877 intTy, initial.getValue().zextOrTrunc(intTy.getWidth()));
3881 auto innerSym = lowerInnerSymbol(op);
3883 Backedge inputEdge = backedgeBuilder.
get(resultType);
3884 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3885 op.getNameAttr(), resetSignal, resetValue,
3886 innerSym, isAsync, presetAttr);
3889 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3890 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3891 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3892 reg->setAttr(
"firrtl.random_init_start", randomStart);
3893 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3894 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3897 if (
auto svAttrs = sv::getSVAttributes(op))
3898 sv::setSVAttributes(reg, svAttrs);
3901 (void)setLowering(op.getResult(), reg);
3906LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3910 return op.emitOpError(
3911 "should have already been lowered from a ground type to an aggregate "
3912 "type using the LowerTypes pass. Use "
3913 "'firtool --lower-types' or 'circt-opt "
3914 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3917 FirMemory memSummary = op.getSummary();
3920 auto memType = seq::FirMemType::get(
3923 : std::optional<uint32_t>());
3925 seq::FirMemInitAttr memInit;
3926 if (
auto init = op.getInitAttr())
3927 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3928 init.getIsBinary(), init.getIsInline());
3930 auto memDecl = seq::FirMemOp::create(
3933 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3935 if (
auto parent = op->getParentOfType<hw::HWModuleOp>()) {
3936 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3938 if (!file.isDirectory())
3939 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3940 file.getDirectory());
3941 memDecl.setOutputFileAttr(dir);
3947 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3949 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3952 (void)setLowering(a, value);
3958 auto addInput = [&](StringRef field, Value backedge) {
3960 if (cast<FIRRTLBaseType>(a.getType())
3962 .getBitWidthOrSentinel() > 0)
3963 (void)setLowering(a, backedge);
3969 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3973 Value backedge, portValue;
3975 portValue = getOrCreateXConstant(1);
3977 auto portType = IntegerType::get(op.getContext(), width);
3978 backedge = portValue = createBackedge(builder.getLoc(), portType);
3980 addInput(field, backedge);
3984 auto addClock = [&](StringRef field) -> Value {
3985 Type clockTy = seq::ClockType::get(op.getContext());
3986 Value portValue = createBackedge(builder.getLoc(), clockTy);
3987 addInput(field, portValue);
3991 auto memportKind = op.getPortKind(i);
3992 if (memportKind == MemOp::PortKind::Read) {
3993 auto addr = addInputPort(
"addr", op.getAddrBits());
3994 auto en = addInputPort(
"en", 1);
3995 auto clk = addClock(
"clk");
3996 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3998 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3999 auto addr = addInputPort(
"addr", op.getAddrBits());
4000 auto en = addInputPort(
"en", 1);
4001 auto clk = addClock(
"clk");
4004 auto mode = addInputPort(
"wmode", 1);
4006 mode = builder.createOrFold<comb::AndOp>(mode, addInputPort(
"wmask", 1),
4013 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
4017 auto addr = addInputPort(
"addr", op.getAddrBits());
4020 auto en = addInputPort(
"en", 1);
4022 en = builder.createOrFold<comb::AndOp>(
en, addInputPort(
"mask", 1),
4024 auto clk = addClock(
"clk");
4038FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
4039 Operation *instanceOp,
4040 SmallVectorImpl<Value> &inputOperands) {
4042 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4043 auto &port = portInfo[portIndex];
4046 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
4051 if (portType.isInteger(0))
4055 if (port.isOutput())
4058 auto portResult = instanceOp->getResult(portIndex);
4059 assert(portResult &&
"invalid IR, couldn't find port");
4063 if (port.isInput()) {
4064 inputOperands.push_back(createBackedge(portResult, portType));
4071 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
4073 auto loweredResult = getPossiblyInoutLoweredValue(source);
4074 inputOperands.push_back(loweredResult);
4075 (void)setLowering(portResult, loweredResult);
4084 "." + port.getName().str() +
".wire");
4088 (void)setLowering(portResult, wire);
4089 inputOperands.push_back(wire);
4095LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4096 Operation *oldModule =
4097 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4099 auto *newModule = circuitState.getNewModule(oldModule);
4101 oldInstance->emitOpError(
"could not find module [")
4102 << oldInstance.getModuleName() <<
"] referenced by instance";
4108 ArrayAttr parameters;
4109 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4114 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4118 SmallVector<Value, 8> operands;
4119 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4126 auto innerSym = oldInstance.getInnerSymAttr();
4127 if (oldInstance.getLowerToBind()) {
4130 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4131 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4133 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4134 innerSym.getSymName());
4137 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4138 bindOp->setAttr(
"output_file", outputFile);
4141 circuitState.addBind(bindOp);
4146 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4147 operands, parameters, innerSym);
4149 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4150 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4152 if (newInstance.getInnerSymAttr())
4153 if (
auto forceName = circuitState.instanceForceNames.lookup(
4154 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4155 newInstance.getInnerNameAttr()}))
4156 newInstance->setAttr(
"hw.verilogName", forceName);
4160 unsigned resultNo = 0;
4161 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4162 auto &port = portInfo[portIndex];
4166 Value resultVal = newInstance.getResult(resultNo);
4168 auto oldPortResult = oldInstance.getResult(portIndex);
4169 (void)setLowering(oldPortResult, resultVal);
4175LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4176 if (oldInstanceChoice.getInnerSymAttr()) {
4177 oldInstanceChoice->emitOpError(
4178 "instance choice with inner sym cannot be lowered");
4183 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4185 return oldInstanceChoice->emitOpError(
4186 "must have instance_macro attribute set before "
4190 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4191 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4194 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4195 auto *defaultModuleNode =
4196 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4198 Operation *defaultModule = defaultModuleNode->getModule();
4202 SmallVector<PortInfo, 8> portInfo =
4203 cast<FModuleLike>(defaultModule).getPorts();
4206 SmallVector<Value, 8> inputOperands;
4208 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4212 SmallVector<sv::WireOp, 8> outputWires;
4213 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4214 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4215 auto &port = portInfo[portIndex];
4219 if (!portType || portType.isInteger(0))
4223 wirePrefix.str() +
"." + port.getName().str());
4224 outputWires.push_back(wire);
4225 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4229 auto optionName = oldInstanceChoice.getOptionNameAttr();
4232 auto createInstanceAndAssign = [&](Operation *oldMod,
4233 StringRef suffix) -> hw::InstanceOp {
4234 auto *newMod = circuitState.getNewModule(oldMod);
4236 ArrayAttr parameters;
4237 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4241 SmallString<64> instName;
4242 instName = oldInstanceChoice.getInstanceName();
4243 if (!suffix.empty()) {
4249 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4250 inputOperands, parameters,
nullptr);
4252 hw::InnerSymTarget(inst.getOperation()),
4253 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
4256 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4263 SmallVector<StringAttr> macroNames;
4264 SmallVector<Operation *> altModules;
4265 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4266 altModules.push_back(
4267 circuitState.getInstanceGraph()
4268 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4272 auto optionCaseMacroRef = circuitState.macroTable.
getMacro(
4273 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4274 if (!optionCaseMacroRef)
4275 return oldInstanceChoice->emitOpError(
4276 "failed to get macro for option case");
4277 macroNames.push_back(optionCaseMacroRef.getAttr());
4281 sv::createNestedIfDefs(
4284 [&](StringRef macro, std::function<
void()> thenCtor,
4285 std::function<
void()> elseCtor) {
4286 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4290 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4291 sv::IfDefOp::create(
4292 builder, oldInstanceChoice.getLoc(), macroNames[i],
4294 SmallString<256> errorMessage;
4295 llvm::raw_svector_ostream os(errorMessage);
4296 os <<
"Multiple instance choice options defined for option '"
4297 << optionName.getValue() <<
"': '"
4298 << macroNames[index].getValue() <<
"' and '"
4299 << macroNames[i].getValue() <<
"'";
4300 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4301 builder.getStringAttr(errorMessage));
4307 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4309 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4311 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4312 builder.getStringAttr(
"{{0}}"),
4313 builder.getArrayAttr({hw::InnerRefAttr::get(
4314 theModule.getNameAttr(),
4315 inst.getInnerSymAttr().getSymName())}));
4319 SmallString<256> errorMessage;
4320 llvm::raw_svector_ostream os(errorMessage);
4321 os <<
"Required instance choice option '" << optionName.getValue()
4322 <<
"' not selected, must define one of: ";
4323 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4324 os <<
"'" << macro.getValue() <<
"'";
4326 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4327 builder.getStringAttr(errorMessage));
4333LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4334 SmallVector<Value> inputs;
4335 SmallVector<Type> types;
4336 for (
auto input : oldOp.getInputs()) {
4337 auto lowered = getLoweredValue(input);
4340 inputs.push_back(lowered);
4341 types.push_back(lowered.getType());
4344 auto newOp = verif::ContractOp::create(builder, types, inputs);
4345 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4346 auto &body = newOp.getBody().emplaceBlock();
4348 for (
auto [newResult, oldResult, oldArg] :
4349 llvm::zip(newOp.getResults(), oldOp.getResults(),
4350 oldOp.getBody().getArguments())) {
4351 if (failed(setLowering(oldResult, newResult)))
4353 if (failed(setLowering(oldArg, newResult)))
4357 body.getOperations().splice(body.end(),
4358 oldOp.getBody().front().getOperations());
4359 addToWorklist(body);
4369LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4370 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4375 return setLowering(op->getResult(0), operand);
4378LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4379 if (isa<ClockType>(op.getInput().getType()))
4380 return setLowering(op->getResult(0),
4381 getLoweredNonClockValue(op.getInput()));
4382 return lowerNoopCast(op);
4385LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4386 if (isa<ClockType>(op.getInput().getType()))
4387 return setLowering(op->getResult(0),
4388 getLoweredNonClockValue(op.getInput()));
4389 return lowerNoopCast(op);
4392LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4393 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4396LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4397 mlir::UnrealizedConversionCastOp op) {
4399 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4402 auto operand = op.getOperand(0);
4403 auto result = op.getResult(0);
4408 return lowerNoopCast(op);
4414 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4420 auto loweredResult = getLoweredValue(operand);
4421 if (!loweredResult) {
4424 if (operand.getType().isSignlessInteger(0)) {
4425 return setLowering(result, Value());
4432 result.replaceAllUsesWith(loweredResult);
4436LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4439 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4440 return setLowering(op, op.getOperand());
4444 auto result = getLoweredValue(op.getOperand());
4450 op.replaceAllUsesWith(result);
4454LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4455 auto operand = getLoweredValue(op.getOperand());
4458 auto resultType =
lowerType(op.getType());
4462 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4465LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4466 auto operand = getLoweredValue(op.getOperand());
4470 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4471 return setLowering(op, getOrCreateIntConstant(1, 0));
4473 return setLowering(op, Value());
4479 return setLowering(op, operand);
4482 auto zero = getOrCreateIntConstant(1, 0);
4483 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4486LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4487 auto operand = getLoweredValue(op.getInput());
4491 auto allOnes = getOrCreateIntConstant(
4492 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4493 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4496LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4499 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4503 auto resultType =
lowerType(op.getType());
4505 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4506 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4510LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4511 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4514 return setLowering(op, operand);
4517LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4518 auto operand = getLoweredValue(op.getInput());
4521 return setLowering(op, getOrCreateIntConstant(1, 0));
4526 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4530LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4531 auto operand = getLoweredValue(op.getInput());
4534 return setLowering(op, getOrCreateIntConstant(1, 1));
4539 return setLoweringTo<comb::ICmpOp>(
4540 op, ICmpPredicate::eq, operand,
4541 getOrCreateIntConstant(
4542 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4546LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4547 auto operand = getLoweredValue(op.getInput());
4550 return setLowering(op, getOrCreateIntConstant(1, 0));
4556 return setLoweringTo<comb::ICmpOp>(
4557 op, ICmpPredicate::ne, operand,
4558 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4566template <
typename ResultOpType>
4567LogicalResult FIRRTLLowering::lowerBinOpToVariadic(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);
4574 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4580template <
typename ResultOpType>
4581LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4582 auto resultType = op->getResult(0).getType();
4583 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4584 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4595 auto intType = builder.getIntegerType(*bitwidth);
4596 auto retType = lhs.getType();
4597 lhs = builder.createOrFold<hw::BitcastOp>(intType, lhs);
4598 rhs = builder.createOrFold<hw::BitcastOp>(intType, rhs);
4599 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4600 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4605template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4606LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4608 auto resultType = op->getResult(0).getType();
4609 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4610 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4616 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4617 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4622LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4623 ICmpPredicate unsignedOp) {
4627 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4631 if (cmpType.getWidth() == 0)
4632 cmpType = UIntType::get(builder.getContext(), 1);
4633 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4634 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4639 Type resultType = builder.getIntegerType(1);
4640 return setLoweringTo<comb::ICmpOp>(
4641 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4647template <
typename SignedOp,
typename Un
signedOp>
4648LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4653 if (opType.getWidth() == 0)
4654 return setLowering(op->getResult(0), Value());
4658 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4659 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4664 if (opType.isSigned())
4665 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4667 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4669 if (
auto *definingOp = result.getDefiningOp())
4672 if (resultType == opType)
4673 return setLowering(op->getResult(0), result);
4674 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4677LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4679 if (op.getInputs().empty())
4680 return setLowering(op, Value());
4682 SmallVector<Value> loweredOperands;
4685 for (
auto operand : op.getInputs()) {
4686 auto loweredOperand = getLoweredValue(operand);
4687 if (loweredOperand) {
4688 loweredOperands.push_back(loweredOperand);
4699 if (loweredOperands.empty())
4700 return setLowering(op, Value());
4703 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4710LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4711 auto input = getLoweredNonClockValue(op.getArg());
4715 if (!isa<IntType>(input.getType())) {
4716 auto srcType = op.getArg().getType();
4718 assert(bitwidth &&
"Unknown width");
4719 auto intType = builder.getIntegerType(*bitwidth);
4720 input = builder.createOrFold<hw::BitcastOp>(intType, input);
4723 return setLoweringTo<comb::ICmpOp>(
4724 op, ICmpPredicate::ceq, input,
4725 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4728LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4729 auto operand = getLoweredValue(op.getInput());
4730 hw::WireOp::create(builder, operand);
4734LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4735 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4736 op.getFormatStringAttr());
4739LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4740 auto type =
lowerType(op.getResult().getType());
4744 auto valueOp = sim::PlusArgsValueOp::create(
4745 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4746 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4748 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4753LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4754 op.emitError(
"sizeof intrinsic should have been resolved by the "
4755 "LowerSizeOf pass.");
4759LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4761 if (op.getTestEnable())
4762 testEnable = getLoweredValue(op.getTestEnable());
4763 return setLoweringTo<seq::ClockGateOp>(
4764 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4765 testEnable, hw::InnerSymAttr{});
4768LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4769 auto operand = getLoweredValue(op.getInput());
4770 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4773LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4774 auto operand = getLoweredValue(op.getInput());
4775 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4778LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4779 return setLoweringToLTL<ltl::AndOp>(
4781 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4784LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4785 return setLoweringToLTL<ltl::OrOp>(
4787 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4790LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4791 return setLoweringToLTL<ltl::IntersectOp>(
4793 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4796LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4797 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4798 op.getDelayAttr(), op.getLengthAttr());
4801LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4802 return setLoweringToLTL<ltl::ConcatOp>(
4804 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4807LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4808 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4809 op.getBaseAttr(), op.getMoreAttr());
4812LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4813 return setLoweringToLTL<ltl::GoToRepeatOp>(
4814 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4817LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4818 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4819 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4822LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4823 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4826LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4827 return setLoweringToLTL<ltl::ImplicationOp>(
4829 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4832LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4833 return setLoweringToLTL<ltl::UntilOp>(
4835 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4838LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4839 return setLoweringToLTL<ltl::EventuallyOp>(op,
4840 getLoweredValue(op.getInput()));
4843LogicalResult FIRRTLLowering::visitExpr(LTLAcceptOnIntrinsicOp op) {
4844 return setLoweringToLTL<ltl::AcceptOnOp>(op, getLoweredValue(op.getInput()),
4845 getLoweredValue(op.getCondition()),
4849LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4850 Value
clk = getLoweredNonClockValue(op.getClock());
4851 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4852 op.getDelayAttr(),
clk);
4856 switch (eventControl) {
4857 case EventControl::AtPosEdge:
4858 return ltl::ClockEdge::Pos;
4859 case EventControl::AtEdge:
4860 return ltl::ClockEdge::Both;
4861 case EventControl::AtNegEdge:
4862 return ltl::ClockEdge::Neg;
4864 llvm_unreachable(
"unknown event control");
4867LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4868 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4870 getLoweredNonClockValue(op.getClock()));
4873template <
typename TargetOp,
typename IntrinsicOp>
4874LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4875 auto property = getLoweredValue(op.getProperty());
4876 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4877 TargetOp::create(builder, property, enable, op.getLabelAttr());
4881LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4882 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4885LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4886 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4889LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4890 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4893LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4894 if (!isa<verif::ContractOp>(op->getParentOp()))
4895 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4896 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4899LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4900 if (!isa<verif::ContractOp>(op->getParentOp()))
4901 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4902 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4905LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4906 auto clock = getLoweredNonClockValue(op.getClock());
4907 auto reset = getLoweredValue(op.getReset());
4908 if (!clock || !reset)
4910 auto resetType = op.getReset().getType();
4911 auto uintResetType = dyn_cast<UIntType>(resetType);
4912 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4913 auto isAsync = isa<AsyncResetType>(resetType);
4914 if (!isAsync && !isSync) {
4915 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4916 "requires sync or async reset");
4917 d.attachNote() <<
"reset is of type " << resetType
4918 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4921 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4928LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4929 auto input = getLoweredValue(op.getInput());
4933 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4934 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4937LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4938 auto resultTy =
lowerType(op.getType());
4948 return setLoweringTo<sv::WireOp>(op, sv::NetType::get(resultTy),
4963 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4967 return setLowering(op, constant);
4971 op.emitOpError(
"unsupported type");
4975LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4976 auto input = getLoweredValue(op.getInput());
4980 if (op.getAmount() == 0)
4981 return setLowering(op, Value());
4982 Type resultType = builder.getIntegerType(op.getAmount());
4983 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4984 inWidth - op.getAmount());
4987LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4988 auto input = getLoweredValue(op.getInput());
4991 if (op.getAmount() == 0)
4993 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4998 if (op.getAmount() == 0)
4999 return setLowering(op, input);
5001 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
5002 return setLoweringTo<comb::ConcatOp>(op, input, zero);
5005LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
5006 auto input = getLoweredValue(op.getInput());
5012 auto shiftAmount = op.getAmount();
5013 if (shiftAmount >= inWidth) {
5016 return setLowering(op, {});
5019 shiftAmount = inWidth - 1;
5022 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
5023 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
5026LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
5027 auto input = getLoweredValue(op.getInput());
5032 if (inWidth == op.getAmount())
5033 return setLowering(op, Value());
5034 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
5035 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
5038LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
5039 auto cond = getLoweredValue(op.getSel());
5040 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5041 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5042 if (!cond || !ifTrue || !ifFalse)
5045 if (isa<ClockType>(op.getType()))
5046 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
5047 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
5051LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
5052 auto cond = getLoweredValue(op.getSel());
5053 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5054 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5055 if (!cond || !ifTrue || !ifFalse)
5058 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
5060 return setLowering(op, createValueWithMuxAnnotation(val,
true));
5063LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
5064 auto sel = getLoweredValue(op.getSel());
5065 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
5066 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
5067 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
5068 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
5069 if (!sel || !v3 || !v2 || !v1 || !v0)
5071 Value array[] = {v3, v2, v1, v0};
5074 return setLowering(op, createValueWithMuxAnnotation(val,
false));
5093Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
5094 assert(op->getNumResults() == 1 &&
"only expect a single result");
5095 auto val = op->getResult(0);
5099 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5106 OpBuilder::InsertionGuard guard(builder);
5107 builder.setInsertionPoint(op);
5108 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5109 for (
auto [idx, operand] : llvm::enumerate(op->getOperands())) {
5111 op->getContext(),
nullptr, 0,
5112 [&]() -> hw::InnerSymbolNamespace & { return moduleNamespace; });
5114 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5115 op->setOperand(idx, wire);
5120 sv::setSVAttributes(assignOp,
5121 sv::SVAttributeAttr::get(builder.getContext(),
5122 "synopsys infer_mux_override",
5127Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5129 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5134 if (!llvm::isPowerOf2_64(size)) {
5135 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5137 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5139 Value temp2[] = {ext.getResult(), array};
5145 return inBoundsRead;
5148LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5150 auto index = getLoweredAndExtOrTruncValue(
5152 UIntType::get(op.getContext(),
5157 SmallVector<Value> loweredInputs;
5158 loweredInputs.reserve(op.getInputs().size());
5159 for (
auto input : op.getInputs()) {
5160 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5163 loweredInputs.push_back(lowered);
5167 return setLowering(op, createArrayIndexing(array, index));
5170LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5171 auto resultTy =
lowerType(op.getType());
5175 SmallVector<Value, 4> operands;
5176 operands.reserve(op.getSubstitutions().size());
5177 for (
auto operand : op.getSubstitutions()) {
5178 auto lowered = getLoweredValue(operand);
5181 operands.push_back(lowered);
5184 ArrayAttr symbols = op.getSymbolsAttr();
5186 symbols = ArrayAttr::get(op.getContext(), {});
5188 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5192LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5196 Type baseType = op.getType().getType();
5199 if (isa<ClockType>(baseType))
5200 xmrType = builder.getIntegerType(1);
5204 return setLoweringTo<sv::XMRRefOp>(op, sv::NetType::get(xmrType), op.getRef(),
5205 op.getVerbatimSuffixAttr());
5208LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5212 if (isa<ClockType>(op.getType()))
5213 xmrType = builder.getIntegerType(1);
5217 auto xmr = sv::XMRRefOp::create(builder, sv::NetType::get(xmrType),
5218 op.getRef(), op.getVerbatimSuffixAttr());
5219 auto readXmr = getReadValue(xmr);
5220 if (!isa<ClockType>(op.getType()))
5221 return setLowering(op, readXmr);
5222 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5227LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5228LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5236LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5248FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5249 auto srcType = srcVal.getType();
5250 auto dstType = destVal.getType();
5251 if (srcType != dstType &&
5252 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5255 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5256 .Case<hw::WireOp>([&](
auto op) {
5257 maybeUnused(op.getInput());
5258 op.getInputMutable().assign(srcVal);
5261 .Case<seq::FirRegOp>([&](
auto op) {
5262 maybeUnused(op.getNext());
5263 op.getNextMutable().assign(srcVal);
5266 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5269 op.emitOpError(
"used as connect destination");
5272 .Default([](
auto) {
return false; });
5275LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5276 auto dest = op.getDest();
5279 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5281 return handleZeroBit(op.getSrc(), []() { return success(); });
5283 auto destVal = getPossiblyInoutLoweredValue(dest);
5287 auto result = lowerConnect(destVal, srcVal);
5295 if (updateIfBackedge(destVal, srcVal))
5298 if (!isa<sv::NetType>(destVal.getType()))
5299 return op.emitError(
"destination isn't a net handle type");
5305LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5306 auto dest = op.getDest();
5307 auto srcVal = getLoweredValue(op.getSrc());
5309 return handleZeroBit(op.getSrc(), []() { return success(); });
5311 auto destVal = getPossiblyInoutLoweredValue(dest);
5315 auto result = lowerConnect(destVal, srcVal);
5323 if (updateIfBackedge(destVal, srcVal))
5326 if (!isa<sv::NetType>(destVal.getType()))
5327 return op.emitError(
"destination isn't a net handle type");
5333LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5334 if (circuitState.lowerToCore)
5335 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5337 auto srcVal = getLoweredValue(op.getSrc());
5341 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5345 if (!sv::getHandleElementType(destVal.getType()))
5346 return op.emitError(
"destination isn't a net or variable handle type");
5349 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5350 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5351 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5356LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5357 if (circuitState.lowerToCore)
5358 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5360 auto src = getLoweredNonClockValue(op.getSrc());
5361 auto clock = getLoweredNonClockValue(op.getClock());
5362 auto pred = getLoweredValue(op.getPredicate());
5363 if (!src || !clock || !pred)
5366 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5371 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5372 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5373 addToAlwaysBlock(clock, [&]() {
5374 addIfProceduralBlock(
5375 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5380LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5381 if (circuitState.lowerToCore)
5382 return op.emitOpError(
5383 "lower-to-core does not support firrtl.ref.force_initial");
5385 auto src = getLoweredNonClockValue(op.getSrc());
5386 auto pred = getLoweredValue(op.getPredicate());
5390 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5395 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5396 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5397 addToInitialBlock([&]() {
5398 addIfProceduralBlock(
5399 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5404LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5405 if (circuitState.lowerToCore)
5406 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5408 auto clock = getLoweredNonClockValue(op.getClock());
5409 auto pred = getLoweredValue(op.getPredicate());
5410 if (!clock || !pred)
5413 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5418 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5419 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5420 addToAlwaysBlock(clock, [&]() {
5421 addIfProceduralBlock(pred,
5422 [&]() { sv::ReleaseOp::create(builder, destVal); });
5427LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5428 if (circuitState.lowerToCore)
5429 return op.emitOpError(
5430 "lower-to-core does not support firrtl.ref.release_initial");
5432 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5433 auto pred = getLoweredValue(op.getPredicate());
5434 if (!destVal || !pred)
5438 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5439 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5440 addToInitialBlock([&]() {
5441 addIfProceduralBlock(pred,
5442 [&]() { sv::ReleaseOp::create(builder, destVal); });
5450 StringRef originalFormatString,
5451 ValueRange operands,
5452 StringAttr &result) {
5455 SmallString<32> formatString;
5456 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5457 char c = originalFormatString[i];
5461 formatString.push_back(c);
5464 SmallString<6> width;
5465 c = originalFormatString[++i];
5468 c = originalFormatString[++i];
5479 formatString.append(width);
5485 formatString.push_back(c);
5492 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5493 formatString.push_back(c);
5497 auto substitution = operands[subIdx++];
5499 "the operand for a '{{}}' substitution must be an 'fstring' type");
5501 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5502 .template Case<TimeOp>([&](
auto) {
5503 formatString.append(
"%0t");
5506 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5507 formatString.append(
"%m");
5510 .Default([&](
auto) {
5511 emitError(loc,
"has a substitution with an unimplemented "
5513 .attachNote(substitution.getLoc())
5514 <<
"op with an unimplemented lowering is here";
5524 formatString.push_back(c);
5528 result = StringAttr::get(loc->getContext(), formatString);
5535LogicalResult FIRRTLLowering::visitPrintfLike(
5536 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5537 auto clock = getLoweredNonClockValue(op.getClock());
5538 auto cond = getLoweredValue(op.getCond());
5539 if (!clock || !cond)
5542 StringAttr formatString;
5544 op.getSubstitutions(), formatString)))
5547 auto fn = [&](Value fd) {
5548 SmallVector<Value> operands;
5549 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5551 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5555 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5559LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5560 if (!circuitState.lowerToCore)
5561 return visitPrintfLike(op, {},
true);
5563 auto clock = getLoweredValue(op.getClock());
5564 auto cond = getLoweredValue(op.getCond());
5565 if (!clock || !cond)
5569 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5570 if (failed(formatString))
5573 auto stderrOp = sim::StderrStreamOp::create(builder);
5574 sim::TriggeredOp::create(builder, clock, cond, [&] {
5575 sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
5580LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5581 if (circuitState.lowerToCore) {
5582 auto clock = getLoweredValue(op.getClock());
5583 auto cond = getLoweredValue(op.getCond());
5584 if (!clock || !cond)
5587 auto fileFormatString = lowerSimFormatString(
5588 op.getOutputFileAttr(), op.getOutputFileSubstitutions());
5589 if (failed(fileFormatString))
5593 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5594 if (failed(formatString))
5597 sim::TriggeredOp::create(builder, clock, cond, [&] {
5598 auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
5599 sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
5604 StringAttr outputFileAttr;
5606 op.getOutputFileSubstitutions(),
5610 FileDescriptorInfo outputFile(outputFileAttr,
5611 op.getOutputFileSubstitutions());
5612 return visitPrintfLike(op, outputFile,
false);
5616LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5617 if (circuitState.lowerToCore)
5618 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5620 auto clock = getLoweredNonClockValue(op.getClock());
5621 auto cond = getLoweredValue(op.getCond());
5622 if (!clock || !cond)
5625 auto fn = [&](Value fd) {
5626 sv::FFlushOp::create(builder, op.getLoc(), fd);
5630 if (!op.getOutputFileAttr())
5631 return lowerStatementWithFd({}, clock, cond, fn,
false);
5635 StringAttr outputFileAttr;
5637 op.getOutputFileSubstitutions(),
5641 return lowerStatementWithFd(
5642 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5643 clock, cond, fn,
false);
5648LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5649 auto clock = getLoweredValue(op.getClock());
5650 auto cond = getLoweredValue(op.getCond());
5651 if (!clock || !cond)
5654 circuitState.usedStopCond =
true;
5655 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5658 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5659 Value exitCond = builder.createOrFold<comb::AndOp>(stopCond, cond,
true);
5661 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5662 op.getExitCode() == 0,
5671template <
typename... Args>
5673 StringRef opName, Args &&...args) {
5674 if (opName ==
"assert")
5675 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5676 if (opName ==
"assume")
5677 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5678 if (opName ==
"cover")
5679 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5680 llvm_unreachable(
"unknown verification op");
5686template <
typename... Args>
5688 StringRef opName, Args &&...args) {
5689 if (opName ==
"assert")
5690 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5691 if (opName ==
"assume")
5692 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5693 if (opName ==
"cover")
5694 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5695 llvm_unreachable(
"unknown verification op");
5699 switch (eventControl) {
5700 case EventControl::AtPosEdge:
5701 return verif::ClockEdge::Pos;
5702 case EventControl::AtEdge:
5703 return verif::ClockEdge::Both;
5704 case EventControl::AtNegEdge:
5705 return verif::ClockEdge::Neg;
5707 llvm_unreachable(
"unknown FIRRTL event control");
5710LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5711 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5712 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5713 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5714 if (guardsAttr && !guardsAttr.empty())
5715 return op->emitOpError(
5716 "lower-to-core does not support guarded verification statements");
5718 auto clock = getLoweredNonClockValue(opClock);
5719 auto enable = getLoweredValue(opEnable);
5720 auto predicate = getLoweredValue(opPredicate);
5721 if (!clock || !enable || !predicate)
5725 if (opNameAttr && !opNameAttr.getValue().empty())
5726 label = StringAttr::get(builder.getContext(),
5727 labelPrefix + opNameAttr.getValue());
5730 auto opName = op->getName().stripDialect();
5731 if (opName ==
"assert") {
5732 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5736 if (opName ==
"assume") {
5737 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5741 if (opName ==
"cover") {
5742 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5746 llvm_unreachable(
"unknown verification op");
5767LogicalResult FIRRTLLowering::lowerVerificationStatement(
5768 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5769 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5770 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5771 if (circuitState.lowerToCore)
5772 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5773 opPredicate, opEnable, opNameAttr,
5776 StringRef opName = op->getName().stripDialect();
5779 ArrayRef<Attribute> guards{};
5780 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5781 guards = guardsAttr.getValue();
5783 auto isCover = isa<CoverOp>(op);
5784 auto clock = getLoweredNonClockValue(opClock);
5785 auto enable = getLoweredValue(opEnable);
5786 auto predicate = getLoweredValue(opPredicate);
5787 if (!clock || !enable || !predicate)
5791 if (opNameAttr && !opNameAttr.getValue().empty())
5793 StringAttr prefixedLabel;
5796 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5799 SmallVector<Value> messageOps;
5803 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5804 flavor = VerificationFlavor::None;
5806 if (flavor == VerificationFlavor::None) {
5810 auto format = op->getAttrOfType<StringAttr>(
"format");
5812 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5813 if (!isa<AssertOp>(op))
5814 return op->emitError()
5815 <<
"ifElseFatal format cannot be used for non-assertions";
5816 flavor = VerificationFlavor::IfElseFatal;
5817 }
else if (isConcurrent)
5818 flavor = VerificationFlavor::SVA;
5820 flavor = VerificationFlavor::Immediate;
5823 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5827 opOperands, message)))
5830 if (failed(loweredFmtOperands(opOperands, messageOps)))
5833 if (flavor == VerificationFlavor::SVA) {
5838 for (
auto &loweredValue : messageOps)
5839 loweredValue = sv::SampledOp::create(builder, loweredValue);
5845 case VerificationFlavor::Immediate: {
5847 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5848 builder.getContext(), circt::sv::DeferAssert::Immediate);
5849 addToAlwaysBlock(clock, [&]() {
5850 addIfProceduralBlock(enable, [&]() {
5852 prefixedLabel, message, messageOps);
5857 case VerificationFlavor::IfElseFatal: {
5858 assert(isa<AssertOp>(op) &&
"only assert is expected");
5861 auto boolType = IntegerType::get(builder.getContext(), 1);
5862 predicate = comb::createOrFoldNot(builder, predicate,
true);
5863 predicate = builder.createOrFold<comb::AndOp>(enable, predicate,
true);
5865 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5866 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5867 addToAlwaysBlock(clock, [&]() {
5868 addIfProceduralBlock(predicate, [&]() {
5869 circuitState.usedStopCond =
true;
5870 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5872 circuitState.usedAssertVerboseCond =
true;
5873 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5875 addIfProceduralBlock(
5876 sv::MacroRefExprOp::create(builder, boolType,
5877 "ASSERT_VERBOSE_COND_"),
5879 sv::ErrorProceduralOp::create(builder, message, messageOps);
5881 addIfProceduralBlock(
5882 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5883 [&]() { sv::FatalProceduralOp::create(builder); });
5889 case VerificationFlavor::SVA: {
5894 comb::createOrFoldNot(builder, enable,
true);
5896 builder.createOrFold<comb::OrOp>(notEnable, predicate,
true);
5898 predicate = builder.createOrFold<comb::AndOp>(enable, predicate,
true);
5902 sv::EventControl event;
5903 switch (opEventControl) {
5904 case EventControl::AtPosEdge:
5905 event = circt::sv::EventControl::AtPosEdge;
5907 case EventControl::AtEdge:
5908 event = circt::sv::EventControl::AtEdge;
5910 case EventControl::AtNegEdge:
5911 event = circt::sv::EventControl::AtNegEdge;
5917 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5918 predicate, prefixedLabel, message, messageOps);
5921 case VerificationFlavor::None:
5923 "flavor `None` must be converted into one of concreate flavors");
5930 return emitGuards(op->getLoc(), guards, emit);
5934LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5935 return lowerVerificationStatement(
5936 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5937 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5938 op.getIsConcurrent(), op.getEventControl());
5942LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5943 return lowerVerificationStatement(
5944 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5945 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5946 op.getIsConcurrent(), op.getEventControl());
5950LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5951 return lowerVerificationStatement(
5952 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5953 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5954 op.getIsConcurrent(), op.getEventControl());
5958LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5959 if (circuitState.lowerToCore) {
5960 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5961 if (guardsAttr && !guardsAttr.empty())
5962 return op.emitOpError(
5963 "lower-to-core does not support guarded verification statements");
5965 auto predicate = getLoweredValue(op.getPredicate());
5966 auto enable = getLoweredValue(op.getEnable());
5967 if (!predicate || !enable)
5970 auto label = op.getNameAttr();
5971 StringAttr assumeLabel;
5972 if (label && !label.empty())
5974 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5975 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5983 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5984 ArrayRef<Attribute> guards =
5985 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5987 auto label = op.getNameAttr();
5988 StringAttr assumeLabel;
5989 if (label && !label.empty())
5991 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5992 auto predicate = getLoweredValue(op.getPredicate());
5993 auto enable = getLoweredValue(op.getEnable());
5994 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5995 predicate = builder.createOrFold<comb::OrOp>(notEnable, predicate,
true);
5997 SmallVector<Value> messageOps;
5998 for (
auto operand : op.getSubstitutions()) {
5999 auto loweredValue = getLoweredValue(operand);
6000 if (!loweredValue) {
6004 loweredValue = getOrCreateIntConstant(1, 0);
6006 messageOps.push_back(loweredValue);
6008 return emitGuards(op.getLoc(), guards, [&]() {
6009 sv::AlwaysOp::create(
6010 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
6012 if (op.getMessageAttr().getValue().empty())
6013 buildImmediateVerifOp(
6014 builder,
"assume", predicate,
6015 circt::sv::DeferAssertAttr::get(
6016 builder.getContext(), circt::sv::DeferAssert::Immediate),
6019 buildImmediateVerifOp(
6020 builder,
"assume", predicate,
6021 circt::sv::DeferAssertAttr::get(
6022 builder.getContext(), circt::sv::DeferAssert::Immediate),
6023 assumeLabel, op.getMessageAttr(), messageOps);
6028LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
6030 if (op.getAttached().size() < 2)
6033 SmallVector<Value, 4> inoutValues;
6034 for (
auto v : op.getAttached()) {
6035 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
6036 if (!inoutValues.back()) {
6040 inoutValues.pop_back();
6044 if (!isa<sv::NetType>(inoutValues.back().getType()))
6045 return op.emitError(
"operand isn't a net handle type");
6048 if (inoutValues.size() < 2)
6056 if (circuitState.lowerToCore)
6057 return op.emitOpError(
6058 "lower-to-core does not support firrtl.attach that requires SV "
6064 bool isAttachInternalOnly =
6065 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
6067 if (isAttachInternalOnly) {
6068 auto v0 = inoutValues.front();
6069 for (
auto v : inoutValues) {
6072 v.replaceAllUsesWith(v0);
6079 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
6080 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
6085 SmallVector<Value, 4> values;
6086 for (
auto inoutValue : inoutValues)
6087 values.push_back(getReadValue(inoutValue));
6089 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
6090 for (
size_t i2 = 0; i2 != e; ++i2)
6098 sv::IfDefOp::create(
6099 builder,
"VERILATOR",
6101 sv::VerbatimOp::create(
6103 "`error \"Verilator does not support alias and thus "
6105 "arbitrarily connect bidirectional wires and ports\"");
6107 [&]() { sv::AliasOp::create(builder, inoutValues); });
6113LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
6114 sv::BindOp::create(builder, op.getInstanceAttr());
6118LogicalResult FIRRTLLowering::fixupLTLOps() {
6119 if (ltlOpFixupWorklist.empty())
6121 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6125 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6126 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6127 if (isa<hw::WireOp>(user))
6128 ltlOpFixupWorklist.insert(user);
6131 while (!ltlOpFixupWorklist.empty()) {
6132 auto *op = ltlOpFixupWorklist.pop_back_val();
6135 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6136 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6137 SmallVector<Type, 2> types;
6138 auto result = opIntf.inferReturnTypes(
6139 op->getContext(), op->getLoc(), op->getOperands(),
6140 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6144 assert(types.size() == op->getNumResults());
6148 for (
auto [result, type] : llvm::zip(op->getResults(), types)) {
6149 if (result.getType() == type)
6151 LLVM_DEBUG(llvm::dbgs()
6152 <<
" - Result #" << result.getResultNumber() <<
" from "
6153 << result.getType() <<
" to " << type <<
"\n");
6154 result.setType(type);
6155 for (
auto *user : result.getUsers())
6157 ltlOpFixupWorklist.insert(user);
6162 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6163 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6164 wireOp.replaceAllUsesWith(wireOp.getInput());
6165 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6166 if (wireOp.use_empty())
6173 SmallPtrSet<Operation *, 4> usersReported;
6174 for (
auto *user : op->getUsers()) {
6175 if (!usersReported.insert(user).second)
6177 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6178 user->getDialect()))
6180 if (isa<hw::WireOp>(user))
6182 auto d = op->emitError(
6183 "verification operation used in a non-verification context");
6184 d.attachNote(user->getLoc())
6185 <<
"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)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
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.
void setValue(mlir::Value)
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.
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.
FlatSymbolRefAttr getMacro(StringAttr optionName, StringAttr caseName) const
This graph tracks modules and where they are instantiated.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
hw::HierPathOp getNLA(StringAttr name)
Resolve a symbol to an NLA.
create(elements, Type result_type=None)
create(str sym_name, Type type, str verilog_name=None)
create(data_type, name=None, sym_name=None)
BaseTy type_cast(Type type)
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)
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)
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.
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