37#include "mlir/IR/BuiltinOps.h"
38#include "mlir/IR/BuiltinTypes.h"
39#include "mlir/IR/ImplicitLocOpBuilder.h"
40#include "mlir/IR/Threading.h"
41#include "mlir/Pass/Pass.h"
42#include "llvm/ADT/DenseMap.h"
43#include "llvm/Support/Debug.h"
44#include "llvm/Support/Mutex.h"
45#include "llvm/Support/Path.h"
47#define DEBUG_TYPE "lower-to-hw"
50#define GEN_PASS_DEF_LOWERFIRRTLTOHW
51#include "circt/Conversion/Passes.h.inc"
55using namespace firrtl;
56using circt::comb::ICmpPredicate;
65 auto ftype = dyn_cast<FIRRTLBaseType>(type);
66 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
73 for (
auto operand : op.getAttached()) {
75 operand.getDefiningOp<InstanceOp>())
79 if (!operand.hasOneUse() || singleSource)
81 singleSource = operand;
90 auto checkTypes = [](Operation *op) -> WalkResult {
92 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
93 return op->emitError(
"Found unhandled FIRRTL operation '")
94 << op->getName() <<
"'";
97 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
98 if (llvm::any_of(types, [](Type type) {
99 return isa<FIRRTLDialect>(type.getDialect());
101 return op->emitOpError(
"found unhandled FIRRTL type");
106 if (failed(checkTypeRange(op->getOperandTypes())) ||
107 failed(checkTypeRange(op->getResultTypes())))
108 return WalkResult::interrupt();
111 for (
auto ®ion : op->getRegions())
112 for (
auto &block : region)
113 if (failed(checkTypeRange(block.getArgumentTypes())))
114 return WalkResult::interrupt();
117 return WalkResult::advance();
120 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
127 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
128 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
134 ImplicitLocOpBuilder &builder) {
136 if (BundleType bundle = dyn_cast<BundleType>(type))
137 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
139 if (type != val.getType())
140 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
148 ImplicitLocOpBuilder &builder) {
150 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
152 val = mlir::UnrealizedConversionCastOp::create(
154 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
156 val = builder.createOrFold<HWStructCastOp>(type, val);
161 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
167 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
173 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
174 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
175 dst->setAttr(
"sv.namehint", attr);
181class FileDescriptorInfo {
183 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
184 : outputFileFormat(outputFileName), substitutions(substitutions) {
186 substitutions.empty() &&
187 "substitutions must be empty when output file name is empty");
190 FileDescriptorInfo() =
default;
193 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
196 bool isDefaultFd()
const {
return !outputFileFormat; }
198 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
199 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
203 StringAttr outputFileFormat = {};
206 mlir::ValueRange substitutions;
216struct FIRRTLModuleLowering;
219struct CircuitLoweringState {
221 std::atomic<bool> usedPrintf{
false};
222 std::atomic<bool> usedAssertVerboseCond{
false};
223 std::atomic<bool> usedStopCond{
false};
224 std::atomic<bool> usedFileDescriptorLib{
false};
226 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
231 : circuitOp(circuitOp), instanceGraph(instanceGraph),
232 enableAnnotationWarning(enableAnnotationWarning),
233 lowerToCore(lowerToCore), verificationFlavor(verificationFlavor),
234 nlaTable(nlaTable), macroTable(macroTable) {
235 auto *
context = circuitOp.getContext();
239 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
240 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
241 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
242 context, dirName.getValue(),
false,
true);
246 if (
auto module = dyn_cast<FModuleLike>(op)) {
258 testHarness =
nullptr;
259 }
else if (dut == testHarness) {
260 testHarness =
nullptr;
265 auto inDUT = [&](igraph::ModuleOpInterface child) {
267 if (
auto inst = instRec->getInstance<InstanceOp>())
268 return inst.getLowerToBind() || inst.getDoNotPrint();
271 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
272 return getInstanceGraph().isAncestor(child, parent, isPhony);
275 circuitOp->walk([&](FModuleLike moduleOp) {
277 dutModules.insert(moduleOp);
281 Operation *getNewModule(Operation *oldModule) {
282 auto it = oldToNewModuleMap.find(oldModule);
283 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
286 Operation *getOldModule(Operation *newModule) {
287 auto it = newToOldModuleMap.find(newModule);
288 return it != newToOldModuleMap.end() ? it->second :
nullptr;
291 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
292 oldToNewModuleMap[oldFMod] = newHWMod;
293 newToOldModuleMap[newHWMod] = oldFMod;
298 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
304 void addBind(sv::BindOp op) {
305 std::lock_guard<std::mutex> lock(bindsMutex);
311 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
314 auto hwAlias = typeAliases.getTypedecl(firAliasType);
317 assert(!typeAliases.isFrozen() &&
318 "type aliases cannot be generated after its frozen");
319 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
322 FModuleLike getDut() {
return dut; }
323 FModuleLike getTestHarness() {
return testHarness; }
329 bool isInDUT(igraph::ModuleOpInterface child) {
330 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
331 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
332 return dutModules.contains(child);
335 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
340 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
347 Type
lowerType(Type type, Location loc) {
348 return ::lowerType(type, loc,
349 [&](Type rawType, BaseTypeAliasType firrtlType,
350 Location typeLoc) -> hw::TypeAliasType {
351 return getTypeAlias(rawType, firrtlType, typeLoc);
357 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
358 auto it = verbatimSourcesByFileName.find(fileName);
359 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
364 void registerVerbatimSource(StringRef fileName,
366 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
367 verbatimSourcesByFileName[fileName] = verbatimOp;
371 emit::FileOp getEmitFileForFile(StringRef fileName) {
372 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
373 auto it = emitFilesByFileName.find(fileName);
374 return it != emitFilesByFileName.end() ? it->second :
nullptr;
379 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
380 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
381 emitFilesByFileName[fileName] = fileOp;
385 friend struct FIRRTLModuleLowering;
386 friend struct FIRRTLLowering;
387 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
388 void operator=(
const CircuitLoweringState &) =
delete;
391 DenseMap<Operation *, Operation *> oldToNewModuleMap;
394 DenseMap<Operation *, Operation *> newToOldModuleMap;
405 DenseSet<igraph::ModuleOpInterface> dutModules;
409 StringSet<> pendingAnnotations;
410 const bool enableAnnotationWarning;
411 std::mutex annotationPrintingMtx;
413 const bool lowerToCore;
418 SmallVector<sv::BindOp> binds;
421 std::mutex bindsMutex;
429 FModuleLike testHarness;
432 hw::OutputFileAttr testBenchDirectory;
436 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
439 SetVector<StringAttr> macroDeclNames;
440 std::mutex macroDeclMutex;
442 void addMacroDecl(StringAttr name) {
443 std::unique_lock<std::mutex> lock(macroDeclMutex);
444 macroDeclNames.insert(name);
449 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
450 llvm::sys::SmartMutex<true> fragmentsMutex;
454 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
457 void addFragment(
hw::HWModuleOp module, FlatSymbolRefAttr fragment) {
458 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
459 fragments[module].insert(fragment);
474 struct RecordTypeAlias {
476 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
478 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
479 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
480 if (iter != firrtlTypeToAliasTypeMap.end())
485 bool isFrozen() {
return frozen; }
487 void freeze() { frozen =
true; }
489 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
491 assert(!frozen &&
"Record already frozen, cannot be updated");
494 auto b = ImplicitLocOpBuilder::atBlockBegin(
496 &circuitOp->getParentRegion()->getBlocks().back());
498 b, b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"),
500 typeScope.getBodyRegion().push_back(
new Block());
502 auto typeName = firAlias.getName();
507 StringAttr::get(typeName.getContext(),
508 typeDeclNamespace.newName(typeName.getValue()));
510 auto typeScopeBuilder =
511 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
514 {}, rawType,
nullptr);
515 auto hwAlias = hw::TypeAliasType::get(
516 SymbolRefAttr::get(typeScope.getSymNameAttr(),
517 {FlatSymbolRefAttr::get(typeDecl)}),
519 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
520 assert(insert.second &&
"Entry already exists, insert failed");
521 return insert.first->second;
530 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
538 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
541 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
542 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
545 llvm::StringMap<emit::FileOp> emitFilesByFileName;
546 llvm::sys::SmartMutex<true> emitFilesMutex;
552void CircuitLoweringState::processRemainingAnnotations(
554 if (!enableAnnotationWarning || annoSet.
empty())
556 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
558 for (
auto a : annoSet) {
559 auto inserted = pendingAnnotations.insert(a.getClass());
560 if (!inserted.second)
581 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
586 extractGrandCentralAnnoClass,
589 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
590 extractCoverageAnnoClass,
594 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
595 blackBoxTargetDirAnnoClass))
598 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" + a.getClass() +
599 "' still remaining after LowerToHW");
605struct FIRRTLModuleLowering
606 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
608 void runOnOperation()
override;
609 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
610 void setLowerToCore() { lowerToCore =
true; }
612 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
615 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
617 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
618 SmallVectorImpl<hw::PortInfo> &ports,
619 Operation *moduleOp, StringRef moduleName,
621 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
623 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
626 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
628 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
631 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
634 Block *topLevelModule,
638 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
642 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
644 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
646 LogicalResult lowerFileBody(emit::FileOp op);
653std::unique_ptr<mlir::Pass>
657 auto pass = std::make_unique<FIRRTLModuleLowering>();
658 if (enableAnnotationWarning)
659 pass->setEnableAnnotationWarning();
661 pass->setLowerToCore();
662 pass->verificationFlavor = verificationFlavor;
668void FIRRTLModuleLowering::runOnOperation() {
672 auto *topLevelModule = getOperation().getBody();
676 for (
auto &op : *topLevelModule) {
677 if ((circuit = dyn_cast<CircuitOp>(&op)))
684 auto *circuitBody = circuit.getBodyBlock();
688 CircuitLoweringState state(circuit, enableAnnotationWarning, lowerToCore,
689 verificationFlavor, getAnalysis<InstanceGraph>(),
690 &getAnalysis<NLATable>(),
691 getAnalysis<InstanceChoiceMacroTable>());
693 SmallVector<Operation *, 32> opsToProcess;
696 state.processRemainingAnnotations(circuit, circuitAnno);
699 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
701 TypeSwitch<Operation *, LogicalResult>(&op)
702 .Case<FModuleOp>([&](
auto module) {
703 auto loweredMod = lowerModule(module, topLevelModule, state);
707 state.recordModuleMapping(&op, loweredMod);
708 opsToProcess.push_back(loweredMod);
710 module.walk([&](Operation *op) {
711 for (auto res : op->getResults()) {
713 type_dyn_cast<BaseTypeAliasType>(res.getType()))
714 state.lowerType(aliasType, op->getLoc());
717 return lowerModulePortsAndMoveBody(module, loweredMod, state);
719 .Case<FExtModuleOp>([&](
auto extModule) {
721 lowerExtModule(extModule, topLevelModule, state);
724 state.recordModuleMapping(&op, loweredMod);
727 .Case<FMemModuleOp>([&](
auto memModule) {
729 lowerMemModule(memModule, topLevelModule, state);
732 state.recordModuleMapping(&op, loweredMod);
735 .Case<FormalOp>([&](
auto oldOp) {
736 auto builder = OpBuilder::atBlockEnd(topLevelModule);
737 auto newOp = verif::FormalOp::create(
738 builder, oldOp.getLoc(), oldOp.getNameAttr(),
739 {}, oldOp.getParametersAttr());
740 newOp.getBody().emplaceBlock();
741 state.recordModuleMapping(oldOp, newOp);
742 opsToProcess.push_back(newOp);
745 .Case<SimulationOp>([&](
auto oldOp) {
746 auto loc = oldOp.getLoc();
747 auto builder = OpBuilder::atBlockEnd(topLevelModule);
748 auto newOp = verif::SimulationOp::create(
749 builder, loc, oldOp.getNameAttr(), {},
750 oldOp.getParametersAttr());
751 auto &body = newOp.getRegion().emplaceBlock();
752 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
753 body.addArgument(builder.getI1Type(), loc);
754 state.recordModuleMapping(oldOp, newOp);
755 opsToProcess.push_back(newOp);
758 .Case<emit::FileOp>([&](
auto fileOp) {
759 fileOp->moveBefore(topLevelModule, topLevelModule->end());
760 opsToProcess.push_back(fileOp);
763 .Case<OptionOp, OptionCaseOp>([&](
auto) {
767 .Default([&](Operation *op) {
772 op->moveBefore(topLevelModule, topLevelModule->end());
778 return signalPassFailure();
781 state.typeAliases.freeze();
786 SmallVector<Attribute> dutHierarchyFiles;
787 SmallVector<Attribute> testHarnessHierarchyFiles;
788 circuitAnno.removeAnnotations([&](
Annotation annotation) {
789 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
790 auto file = hw::OutputFileAttr::getFromFilename(
792 annotation.
getMember<StringAttr>(
"filename").getValue(),
794 dutHierarchyFiles.push_back(file);
797 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
798 auto file = hw::OutputFileAttr::getFromFilename(
800 annotation.
getMember<StringAttr>(
"filename").getValue(),
804 if (state.getTestHarness())
805 testHarnessHierarchyFiles.push_back(file);
807 dutHierarchyFiles.push_back(file);
813 if (!dutHierarchyFiles.empty())
814 state.getNewModule(state.getDut())
816 ArrayAttr::get(&getContext(), dutHierarchyFiles));
817 if (!testHarnessHierarchyFiles.empty())
818 state.getNewModule(state.getTestHarness())
820 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
824 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
828 return signalPassFailure();
831 for (
auto bind : state.binds) {
836 for (
auto &[module, fragments] : state.fragments)
838 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
841 for (
auto oldNew : state.oldToNewModuleMap)
842 oldNew.first->erase();
844 if (!state.macroDeclNames.empty()) {
845 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), circuit);
846 for (
auto name : state.macroDeclNames) {
847 sv::MacroDeclOp::create(b, name);
852 lowerFileHeader(circuit, state);
859void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
860 CircuitLoweringState &state) {
863 ImplicitLocOpBuilder b(UnknownLoc::get(&getContext()), op);
867 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
868 StringRef defineTrue =
"",
869 StringRef defineFalse = StringRef()) {
870 if (!defineFalse.data()) {
871 assert(defineTrue.data() &&
"didn't define anything");
873 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
878 if (defineTrue.data())
879 sv::MacroDefOp::create(b, defName, defineTrue);
881 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
886 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
888 b, guard, [] {}, body);
891 if (state.usedFileDescriptorLib)
892 sv::emitFileDescriptorRuntime(op->getParentOp(), b);
894 if (state.usedPrintf) {
895 sv::MacroDeclOp::create(b,
"PRINTF_COND");
896 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
897 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
898 sv::VerbatimOp::create(
899 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
901 emitGuard(
"PRINTF_COND_", [&]() {
902 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
907 if (state.usedAssertVerboseCond) {
908 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
909 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
910 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
911 sv::VerbatimOp::create(
912 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
913 "gate to assert error printing.");
914 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
915 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
916 "(`ASSERT_VERBOSE_COND)",
"1");
921 if (state.usedStopCond) {
922 sv::MacroDeclOp::create(b,
"STOP_COND");
923 sv::MacroDeclOp::create(b,
"STOP_COND_");
924 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
925 sv::VerbatimOp::create(
926 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
927 "to stop conditions.");
928 emitGuard(
"STOP_COND_", [&]() {
929 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
936FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
937 SmallVectorImpl<hw::PortInfo> &ports,
938 Operation *moduleOp, StringRef moduleName,
940 ports.reserve(firrtlPorts.size());
942 size_t numResults = 0;
943 for (
auto e :
llvm::enumerate(firrtlPorts)) {
945 size_t portNo = e.index();
950 if (firrtlPort.
sym.size() > 1 ||
951 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
952 return emitError(firrtlPort.
loc)
953 <<
"cannot lower aggregate port " << firrtlPort.
name
954 <<
" with field sensitive symbols, HW dialect does not support "
955 "per field symbols yet.";
956 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
958 if (hadDontTouch && !hwPort.
getSym()) {
959 if (hwPort.
type.isInteger(0)) {
960 if (enableAnnotationWarning) {
961 mlir::emitWarning(firrtlPort.
loc)
962 <<
"zero width port " << hwPort.
name
963 <<
" has dontTouch annotation, removing anyway";
969 hw::InnerSymAttr::get(StringAttr::get(
970 moduleOp->getContext(),
971 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
972 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
973 moduleOp->getContext());
978 moduleOp->emitError(
"cannot lower this port type to HW");
984 if (hwPort.
type.isInteger(0)) {
985 auto sym = hwPort.
getSym();
986 if (sym && !sym.empty()) {
987 return mlir::emitError(firrtlPort.
loc)
988 <<
"zero width port " << hwPort.
name
989 <<
" is referenced by name [" << sym
990 <<
"] (e.g. in an XMR) but must be removed";
997 hwPort.
dir = hw::ModulePort::Direction::Output;
998 hwPort.
argNum = numResults++;
999 }
else if (firrtlPort.
isInput()) {
1000 hwPort.
dir = hw::ModulePort::Direction::Input;
1001 hwPort.
argNum = numArgs++;
1005 hwPort.
type = hw::InOutType::get(hwPort.
type);
1006 hwPort.
dir = hw::ModulePort::Direction::InOut;
1007 hwPort.
argNum = numArgs++;
1009 hwPort.
loc = firrtlPort.
loc;
1010 ports.push_back(hwPort);
1020 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1021 return cast<ParamDeclAttr>(a);
1026 Builder builder(module);
1031 SmallVector<Attribute> newParams;
1032 for (
const ParamDeclAttr &entry : params) {
1033 auto name = entry.getName();
1034 auto type = entry.getType();
1035 auto value = ignoreValues ? Attribute() : entry.getValue();
1037 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1038 newParams.push_back(paramAttr);
1040 return builder.getArrayAttr(newParams);
1043bool FIRRTLModuleLowering::handleForceNameAnnos(
1046 bool failed =
false;
1049 if (!anno.
isClass(forceNameAnnoClass))
1052 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1059 auto diag = oldModule.emitOpError()
1060 <<
"contains a '" << forceNameAnnoClass
1061 <<
"' that is not a non-local annotation";
1062 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1073 auto diag = oldModule.emitOpError()
1074 <<
"contains a '" << forceNameAnnoClass
1075 <<
"' whose non-local symbol, '" << sym
1076 <<
"' does not exist in the circuit";
1077 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1090 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1092 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1093 if (!inserted.second &&
1094 (anno.
getMember(
"name") != (inserted.first->second))) {
1095 auto diag = oldModule.emitError()
1096 <<
"contained multiple '" << forceNameAnnoClass
1097 <<
"' with different names: " << inserted.first->second
1098 <<
" was not " << anno.
getMember(
"name");
1099 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1110 FExtModuleOp oldModule, Block *topLevelModule,
1121 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1122 SmallVector<hw::PortInfo, 8> ports;
1123 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1128 StringRef verilogName;
1129 if (
auto defName = oldModule.getDefname())
1130 verilogName = defName.value();
1132 verilogName = oldModule.getName();
1134 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1136 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1137 if (!filesAttr || filesAttr.empty()) {
1138 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1143 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1144 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1145 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1147 if (!primaryFileContent || !primaryOutputFile) {
1148 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1152 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1153 builder.getContext(), primaryOutputFile.getValue());
1155 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1156 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1159 SmallVector<Attribute> additionalFiles;
1163 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1164 auto file = cast<DictionaryAttr>(filesAttr[i]);
1165 auto content = file.getAs<StringAttr>(
"content");
1166 auto outputFile = file.getAs<StringAttr>(
"output_file");
1167 auto fileName = llvm::sys::path::filename(outputFile);
1169 if (!(content && outputFile)) {
1170 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1178 auto fileSymbolName = circuitNamespace.newName(fileName);
1179 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1180 outputFile.getValue(), fileSymbolName);
1181 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1182 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1183 builder.setInsertionPointAfter(
emitFile);
1186 auto ext = llvm::sys::path::extension(outputFile.getValue());
1187 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1188 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1189 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1190 emitFile->setAttr(
"output_file", outputFileAttr);
1194 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1200 parameters = builder.getArrayAttr({});
1202 if (!verbatimSource) {
1203 verbatimSource = sv::SVVerbatimSourceOp::create(
1204 builder, oldModule.getLoc(),
1205 circuitNamespace.newName(primaryFileName.str()), {},
1206 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1207 additionalFiles.empty() ?
nullptr
1208 : builder.getArrayAttr(additionalFiles),
1209 builder.getStringAttr(verilogName));
1211 SymbolTable::setSymbolVisibility(
1212 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1214 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1217 return verbatimSource;
1221FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1222 Block *topLevelModule,
1224 if (
auto verbatimMod =
1225 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
1231 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1232 SmallVector<hw::PortInfo, 8> ports;
1233 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1237 StringRef verilogName;
1238 if (
auto defName = oldModule.getDefname())
1239 verilogName = defName.value();
1242 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1243 auto nameAttr = builder.getStringAttr(oldModule.getName());
1248 auto newModule = hw::HWModuleExternOp::create(
1249 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1250 SymbolTable::setSymbolVisibility(newModule,
1251 SymbolTable::getSymbolVisibility(oldModule));
1253 bool hasOutputPort =
1254 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1255 if (!hasOutputPort &&
1257 internalVerifBlackBoxAnnoClass) &&
1259 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1262 if (
auto extReqs = oldModule.getExternalRequirements();
1263 extReqs && !extReqs.empty())
1264 newModule->setAttr(
"circt.external_requirements", extReqs);
1269 loweringState.processRemainingAnnotations(oldModule, annos);
1274 FExtModuleOp oldModule, Block *topLevelModule,
1279 auto verbatimSource =
1280 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1282 if (!verbatimSource)
1285 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1286 SmallVector<hw::PortInfo, 8> ports;
1287 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1291 StringRef verilogName;
1292 if (
auto defName = oldModule.getDefname())
1293 verilogName = defName.value();
1295 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1297 auto newModule = sv::SVVerbatimModuleOp::create(
1300 builder.getStringAttr(oldModule.getName()),
1302 FlatSymbolRefAttr::get(verbatimSource),
1303 parameters ? parameters : builder.getArrayAttr({}),
1304 verilogName.empty() ? StringAttr{}
1305 : builder.getStringAttr(verilogName));
1307 SymbolTable::setSymbolVisibility(newModule,
1308 SymbolTable::getSymbolVisibility(oldModule));
1310 bool hasOutputPort =
1311 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1312 if (!hasOutputPort &&
1314 internalVerifBlackBoxAnnoClass) &&
1316 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1319 if (
auto extReqs = oldModule.getExternalRequirements();
1320 extReqs && !extReqs.empty())
1321 newModule->setAttr(
"circt.external_requirements", extReqs);
1326 loweringState.processRemainingAnnotations(oldModule, annos);
1331FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1332 Block *topLevelModule,
1335 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1336 SmallVector<hw::PortInfo, 8> ports;
1337 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1342 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1343 auto newModule = hw::HWModuleExternOp::create(
1344 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1345 oldModule.getModuleNameAttr());
1353FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1356 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1357 SmallVector<hw::PortInfo, 8> ports;
1358 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1363 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1364 auto nameAttr = builder.getStringAttr(oldModule.getName());
1366 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1368 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1369 newModule.setCommentAttr(comment);
1372 SmallVector<StringRef, 13> attrNames = {
1377 oldModule.getSymNameAttrName(),
1384 mlir::SymbolOpInterface::getDefaultVisibilityAttrName(),
1387 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1388 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1390 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1391 return !attrSet.count(namedAttr.getName()) &&
1392 !newModule->getAttrDictionary().contains(namedAttr.getName());
1394 newAttrs.push_back(i);
1396 newModule->setAttrs(newAttrs);
1400 SymbolTable::setSymbolVisibility(newModule,
1401 SymbolTable::getSymbolVisibility(oldModule));
1407 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1411 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1413 if (!newModule->hasAttr(
"output_file"))
1414 newModule->setAttr(
"output_file", testBenchDir);
1415 newModule.setCommentAttr(
1416 builder.getStringAttr(
"VCS coverage exclude_file"));
1422 loweringState.processRemainingAnnotations(oldModule, annos);
1431 Operation *insertPoint) {
1432 if (!value.hasOneUse())
1435 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1436 if (!attach || attach.getNumOperands() != 2)
1440 auto loweredType =
lowerType(value.getType());
1441 if (loweredType.isInteger(0))
1446 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1447 auto *op = attachedValue.getDefiningOp();
1448 if (op && op->getBlock() == insertPoint->getBlock() &&
1449 !op->isBeforeInBlock(insertPoint))
1454 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1470 if (type_isa<AnalogType>(flipValue.getType()))
1473 Operation *connectOp =
nullptr;
1474 for (
auto &use : flipValue.getUses()) {
1477 if (use.getOperandNumber() != 0)
1479 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1485 connectOp = use.getOwner();
1495 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1496 if (loweredType.isInteger(0))
1501 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1503 auto connectSrc = connectOp->getOperand(1);
1506 if (!isa<FIRRTLType>(connectSrc.getType())) {
1512 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1514 mlir::UnrealizedConversionCastOp::create(
1516 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1522 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1524 if (destTy != connectSrc.getType() &&
1525 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1526 isa<BaseTypeAliasType>(destTy))) {
1528 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1530 if (!destTy.isGround()) {
1532 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1534 }
else if (destTy.getBitWidthOrSentinel() !=
1535 type_cast<FIRRTLBaseType>(connectSrc.getType())
1536 .getBitWidthOrSentinel()) {
1539 auto destWidth = destTy.getBitWidthOrSentinel();
1540 assert(destWidth != -1 &&
"must know integer widths");
1541 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1553 SmallVector<SubfieldOp> accesses;
1554 for (
auto *op : structValue.getUsers()) {
1555 assert(isa<SubfieldOp>(op));
1556 auto fieldAccess = cast<SubfieldOp>(op);
1558 fieldAccess.getInput().getType().base().getElementIndex(field);
1559 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1560 accesses.push_back(fieldAccess);
1568LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1571 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1578 bodyBuilder.setInsertionPoint(cursor);
1581 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1582 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1583 "port count mismatch");
1585 SmallVector<Value, 4> outputs;
1588 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1589 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1591 unsigned nextHWInputArg = 0;
1592 int hwPortIndex = -1;
1593 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1595 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1598 type_isa<FIRRTLBaseType>(port.type) &&
1599 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1603 if (!port.isOutput() && !isZeroWidth) {
1606 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1612 oldArg.replaceAllUsesWith(newArg);
1618 if (isZeroWidth && port.isInput()) {
1620 WireOp::create(bodyBuilder, port.type,
1621 "." + port.getName().str() +
".0width_input")
1623 oldArg.replaceAllUsesWith(newArg);
1631 outputs.push_back(value);
1632 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1638 auto newArg = WireOp::create(bodyBuilder, port.type,
1639 "." + port.getName().str() +
".output");
1642 oldArg.replaceAllUsesWith(newArg.getResult());
1645 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1646 if (!resultHWType.isInteger(0)) {
1649 outputs.push_back(output);
1652 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1653 newArg.setInnerSymAttr(sym);
1654 newModule.setPortSymbolAttr(hwPortIndex, {});
1660 outputOp->setOperands(outputs);
1663 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1664 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1665 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1666 oldBlockInstList.begin(), oldBlockInstList.end());
1677FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1679 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1684 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1685 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1686 auto oldModule = cast<FModuleOp>(
1687 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1688 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1691 SmallVector<Value> symbolicInputs;
1692 for (
auto arg : newModule.getBody().getArguments())
1693 symbolicInputs.push_back(
verif::SymbolicValueOp::create(
1694 builder, arg.
getLoc(), arg.getType(),
1698 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1699 newModule.getModuleNameAttr(), symbolicInputs);
1706FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1708 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1711 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1712 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1713 auto oldModule = cast<FModuleLike>(
1714 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1716 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1720 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1721 newOp.getBody()->args_end());
1722 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1723 newModule.getModuleNameAttr(), inputs);
1724 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1734struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1736 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1737 : theModule(module), circuitState(circuitState),
1738 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1739 backedgeBuilder(builder, module.
getLoc()) {}
1741 LogicalResult
run();
1744 Value getOrCreateClockConstant(seq::ClockConst clock);
1745 Value getOrCreateIntConstant(
const APInt &value);
1746 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1747 bool isSigned =
false) {
1748 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1750 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1751 Attribute getZeroAttributeForType(Type type);
1752 Value getZeroValueForType(Type type);
1753 Value getOrCreateXConstant(
unsigned numBits);
1754 Value getOrCreateZConstant(Type type);
1755 Value getPossiblyInoutLoweredValue(Value value);
1756 Value getLoweredValue(Value value);
1757 Value getLoweredNonClockValue(Value value);
1758 Value getLoweredAndExtendedValue(Value value, Type destType);
1759 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1760 LogicalResult setLowering(Value orig, Value result);
1761 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1762 template <
typename ResultOpType,
typename... CtorArgTypes>
1763 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1764 template <
typename ResultOpType,
typename... CtorArgTypes>
1765 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1766 Backedge createBackedge(Location loc, Type type);
1767 Backedge createBackedge(Value orig, Type type);
1768 bool updateIfBackedge(Value dest, Value src);
1771 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1776 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1777 if (forceable.isForceable())
1785 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1786 auto attr = op.getInnerSymAttr();
1790 if (requiresInnerSymbol(op))
1792 op.getContext(), attr, 0,
1800 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1801 Operation *instanceOp,
1802 SmallVectorImpl<Value> &inputOperands);
1804 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1808 Value getReadValue(Value v);
1810 Value getNonClockValue(Value v);
1812 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1813 sv::ResetType resetStyle, sv::EventControl resetEdge,
1814 Value reset,
const std::function<
void(
void)> &body = {},
1815 const std::function<void(
void)> &resetBody = {});
1816 void addToAlwaysBlock(Value clock,
1817 const std::function<
void(
void)> &body = {}) {
1818 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1819 sv::EventControl(), Value(), body,
1820 std::function<
void(
void)>());
1823 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1824 std::function<
void(
void)>
emit);
1825 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1826 std::function<
void(
void)> elseCtor = {});
1827 void addToInitialBlock(std::function<
void(
void)> body);
1828 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1829 std::function<
void(
void)> elseCtor = {});
1830 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1832 bool allowTruncate);
1833 Value createArrayIndexing(Value array, Value index);
1834 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1836 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1837 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1838 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1841 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1842 UnloweredOpResult handleUnloweredOp(Operation *op);
1843 LogicalResult visitExpr(ConstantOp op);
1844 LogicalResult visitExpr(SpecialConstantOp op);
1845 LogicalResult visitExpr(SubindexOp op);
1846 LogicalResult visitExpr(SubaccessOp op);
1847 LogicalResult visitExpr(SubfieldOp op);
1848 LogicalResult visitExpr(VectorCreateOp op);
1849 LogicalResult visitExpr(BundleCreateOp op);
1850 LogicalResult visitExpr(FEnumCreateOp op);
1851 LogicalResult visitExpr(AggregateConstantOp op);
1852 LogicalResult visitExpr(IsTagOp op);
1853 LogicalResult visitExpr(SubtagOp op);
1854 LogicalResult visitExpr(TagExtractOp op);
1857 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1858 return visitUnrealizedConversionCast(castOp);
1863 LogicalResult visitDecl(WireOp op);
1864 LogicalResult visitDecl(NodeOp op);
1865 LogicalResult visitDecl(RegOp op);
1866 LogicalResult visitDecl(RegResetOp op);
1867 LogicalResult visitDecl(MemOp op);
1868 LogicalResult visitDecl(InstanceOp oldInstance);
1869 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1870 LogicalResult visitDecl(VerbatimWireOp op);
1871 LogicalResult visitDecl(ContractOp op);
1874 LogicalResult lowerNoopCast(Operation *op);
1875 LogicalResult visitExpr(AsSIntPrimOp op);
1876 LogicalResult visitExpr(AsUIntPrimOp op);
1877 LogicalResult visitExpr(AsClockPrimOp op);
1878 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1880 LogicalResult visitExpr(HWStructCastOp op);
1881 LogicalResult visitExpr(BitCastOp op);
1883 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1884 LogicalResult visitExpr(CvtPrimOp op);
1885 LogicalResult visitExpr(NotPrimOp op);
1886 LogicalResult visitExpr(NegPrimOp op);
1887 LogicalResult visitExpr(PadPrimOp op);
1888 LogicalResult visitExpr(XorRPrimOp op);
1889 LogicalResult visitExpr(AndRPrimOp op);
1890 LogicalResult visitExpr(OrRPrimOp op);
1893 template <
typename ResultUnsignedOpType,
1894 typename ResultSignedOpType = ResultUnsignedOpType>
1895 LogicalResult lowerBinOp(Operation *op);
1896 template <
typename ResultOpType>
1897 LogicalResult lowerBinOpToVariadic(Operation *op);
1899 template <
typename ResultOpType>
1900 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1902 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1903 ICmpPredicate unsignedOp);
1904 template <
typename SignedOp,
typename Un
signedOp>
1905 LogicalResult lowerDivLikeOp(Operation *op);
1907 LogicalResult visitExpr(CatPrimOp op);
1909 LogicalResult visitExpr(AndPrimOp op) {
1910 return lowerBinOpToVariadic<comb::AndOp>(op);
1912 LogicalResult visitExpr(OrPrimOp op) {
1913 return lowerBinOpToVariadic<comb::OrOp>(op);
1915 LogicalResult visitExpr(XorPrimOp op) {
1916 return lowerBinOpToVariadic<comb::XorOp>(op);
1918 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1919 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1921 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1922 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1924 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1925 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1927 LogicalResult visitExpr(AddPrimOp op) {
1928 return lowerBinOpToVariadic<comb::AddOp>(op);
1930 LogicalResult visitExpr(EQPrimOp op) {
1931 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1933 LogicalResult visitExpr(NEQPrimOp op) {
1934 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1936 LogicalResult visitExpr(LTPrimOp op) {
1937 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1939 LogicalResult visitExpr(LEQPrimOp op) {
1940 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1942 LogicalResult visitExpr(GTPrimOp op) {
1943 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1945 LogicalResult visitExpr(GEQPrimOp op) {
1946 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1949 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1950 LogicalResult visitExpr(MulPrimOp op) {
1951 return lowerBinOpToVariadic<comb::MulOp>(op);
1953 LogicalResult visitExpr(DivPrimOp op) {
1954 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
1956 LogicalResult visitExpr(RemPrimOp op) {
1957 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
1961 LogicalResult visitExpr(IsXIntrinsicOp op);
1962 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
1963 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
1964 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
1965 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
1966 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
1967 LogicalResult visitExpr(SizeOfIntrinsicOp op);
1968 LogicalResult visitExpr(ClockGateIntrinsicOp op);
1969 LogicalResult visitExpr(LTLAndIntrinsicOp op);
1970 LogicalResult visitExpr(LTLOrIntrinsicOp op);
1971 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
1972 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
1973 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
1974 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
1975 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
1976 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
1977 LogicalResult visitExpr(LTLNotIntrinsicOp op);
1978 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
1979 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
1980 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
1981 LogicalResult visitExpr(LTLPastIntrinsicOp op);
1982 LogicalResult visitExpr(LTLClockIntrinsicOp op);
1984 template <
typename TargetOp,
typename IntrinsicOp>
1985 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
1986 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
1987 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
1988 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
1989 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
1990 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
1991 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
1992 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
1995 LogicalResult visitExpr(BitsPrimOp op);
1996 LogicalResult visitExpr(InvalidValueOp op);
1997 LogicalResult visitExpr(HeadPrimOp op);
1998 LogicalResult visitExpr(ShlPrimOp op);
1999 LogicalResult visitExpr(ShrPrimOp op);
2000 LogicalResult visitExpr(DShlPrimOp op) {
2001 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2003 LogicalResult visitExpr(DShrPrimOp op) {
2004 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2006 LogicalResult visitExpr(DShlwPrimOp op) {
2007 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2009 LogicalResult visitExpr(TailPrimOp op);
2010 LogicalResult visitExpr(MuxPrimOp op);
2011 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2012 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2013 LogicalResult visitExpr(MultibitMuxOp op);
2014 LogicalResult visitExpr(VerbatimExprOp op);
2015 LogicalResult visitExpr(XMRRefOp op);
2016 LogicalResult visitExpr(XMRDerefOp op);
2019 LogicalResult visitExpr(TimeOp op);
2020 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2023 LogicalResult lowerVerificationStatement(
2024 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2025 Value enable, StringAttr messageAttr, ValueRange operands,
2026 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2027 LogicalResult lowerVerificationStatementToCore(
2028 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2029 Value enable, StringAttr nameAttr, EventControl eventControl);
2031 LogicalResult visitStmt(SkipOp op);
2033 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2034 LogicalResult visitStmt(ConnectOp op);
2035 LogicalResult visitStmt(MatchingConnectOp op);
2036 LogicalResult visitStmt(ForceOp op);
2038 std::optional<Value> getLoweredFmtOperand(Value operand);
2039 LogicalResult loweredFmtOperands(ValueRange operands,
2040 SmallVectorImpl<Value> &loweredOperands);
2041 FailureOr<Value> lowerSimFormatString(StringRef originalFormatString,
2042 ValueRange operands);
2043 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2047 LogicalResult lowerStatementWithFd(
2048 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2049 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2053 LogicalResult visitPrintfLike(T op,
2054 const FileDescriptorInfo &fileDescriptorInfo,
2055 bool usePrintfCond);
2056 LogicalResult visitStmt(PrintFOp op);
2057 LogicalResult visitStmt(FPrintFOp op);
2058 LogicalResult visitStmt(FFlushOp op);
2059 LogicalResult visitStmt(StopOp op);
2060 LogicalResult visitStmt(AssertOp op);
2061 LogicalResult visitStmt(AssumeOp op);
2062 LogicalResult visitStmt(CoverOp op);
2063 LogicalResult visitStmt(AttachOp op);
2064 LogicalResult visitStmt(RefForceOp op);
2065 LogicalResult visitStmt(RefForceInitialOp op);
2066 LogicalResult visitStmt(RefReleaseOp op);
2067 LogicalResult visitStmt(RefReleaseInitialOp op);
2068 LogicalResult visitStmt(BindOp op);
2070 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2071 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2072 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2074 LogicalResult fixupLTLOps();
2077 return circuitState.lowerType(type, builder.getLoc());
2085 CircuitLoweringState &circuitState;
2088 ImplicitLocOpBuilder builder;
2093 DenseMap<Value, Value> valueMapping;
2097 DenseMap<Value, Value> fromClockMapping;
2101 DenseMap<Attribute, Value> hwConstantMap;
2102 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2106 DenseMap<unsigned, Value> hwConstantXMap;
2107 DenseMap<Type, Value> hwConstantZMap;
2113 DenseMap<Value, Value> readInOutCreated;
2117 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2118 sv::ResetType, sv::EventControl, Value>;
2142 DenseSet<Operation *> maybeUnusedValues;
2144 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2145 void maybeUnused(Value value) {
2146 if (
auto *op = value.getDefiningOp())
2158 SetVector<Operation *> ltlOpFixupWorklist;
2163 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2165 void addToWorklist(Block &block) {
2166 worklist.push_back({block.begin(), block.end()});
2168 void addToWorklist(Region ®ion) {
2169 for (
auto &block :
llvm::reverse(region))
2170 addToWorklist(block);
2181LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2182 OpBuilder b(&getContext());
2183 fileOp->walk([&](Operation *op) {
2184 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2185 b.setInsertionPointAfter(bindOp);
2186 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2194FIRRTLModuleLowering::lowerBody(Operation *op,
2196 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2198 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2200 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2202 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2203 return lowerFileBody(fileOp);
2208LogicalResult FIRRTLLowering::run() {
2211 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2212 if (failed(setLowering(arg, arg)))
2219 addToWorklist(theModule.getBody());
2220 SmallVector<Operation *, 16> opsToRemove;
2222 while (!worklist.empty()) {
2223 auto &[opsIt, opsEnd] = worklist.back();
2224 if (opsIt == opsEnd) {
2225 worklist.pop_back();
2228 Operation *op = &*opsIt++;
2230 builder.setInsertionPoint(op);
2231 builder.setLoc(op->getLoc());
2232 auto done = succeeded(dispatchVisitor(op));
2233 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2235 opsToRemove.push_back(op);
2237 switch (handleUnloweredOp(op)) {
2238 case AlreadyLowered:
2241 opsToRemove.push_back(op);
2243 case LoweringFailure:
2255 for (
auto &[backedge, value] : backedges) {
2256 SmallVector<Location> driverLocs;
2262 if (backedge == value) {
2263 Location edgeLoc = backedge.getLoc();
2264 if (driverLocs.empty()) {
2265 mlir::emitError(edgeLoc,
"sink does not have a driver");
2267 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2268 for (
auto loc : driverLocs)
2269 diag.attachNote(loc) <<
"through driver here";
2275 auto *it = backedges.find(value);
2276 if (it == backedges.end())
2279 driverLocs.push_back(value.getLoc());
2282 if (
auto *defOp = backedge.getDefiningOp())
2283 maybeUnusedValues.erase(defOp);
2284 backedge.replaceAllUsesWith(value);
2292 while (!opsToRemove.empty()) {
2293 auto *op = opsToRemove.pop_back_val();
2298 for (
auto result : op->getResults()) {
2302 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2304 builder.getIntegerType(0), 0);
2305 maybeUnusedValues.insert(zeroI0);
2307 result.replaceAllUsesWith(zeroI0);
2310 if (!op->use_empty()) {
2311 auto d = op->emitOpError(
2312 "still has uses; should remove ops in reverse order of visitation");
2313 SmallPtrSet<Operation *, 2> visited;
2314 for (
auto *user : op->getUsers())
2315 if (visited.insert(user).second)
2317 <<
"used by " << user->
getName() <<
" op";
2320 maybeUnusedValues.erase(op);
2326 SmallVector<Operation *> worklist(maybeUnusedValues.begin(),
2327 maybeUnusedValues.end());
2328 while (!worklist.empty()) {
2329 auto *op = worklist.pop_back_val();
2330 maybeUnusedValues.erase(op);
2331 if (!isOpTriviallyDead(op))
2333 for (
auto operand : op->getOperands())
2334 if (auto *defOp = operand.getDefiningOp())
2335 if (maybeUnusedValues.insert(defOp).second)
2336 worklist.push_back(defOp);
2342 if (failed(fixupLTLOps()))
2353Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2354 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2356 auto &entry = hwConstantMap[attr];
2360 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2361 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2367Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2368 auto attr = builder.getIntegerAttr(
2369 builder.getIntegerType(value.getBitWidth()), value);
2371 auto &entry = hwConstantMap[attr];
2375 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2382Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2385 if (hw::type_isa<IntegerType>(type))
2386 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2388 auto cache = hwAggregateConstantMap.lookup({value, type});
2393 SmallVector<Attribute> values;
2394 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2396 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2397 subType = array.getElementType();
2398 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2399 subType = structType.getElements()[e.index()].type;
2401 assert(
false &&
"type must be either array or struct");
2403 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2407 if (hw::type_isa<hw::ArrayType>(type))
2408 std::reverse(values.begin(), values.end());
2410 auto &entry = hwAggregateConstantMap[{value, type}];
2411 entry = builder.getArrayAttr(values);
2419 const std::function<LogicalResult()> &fn) {
2420 assert(failedOperand &&
"Should be called on the failed operand");
2428Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2430 auto &entry = hwConstantXMap[numBits];
2434 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2435 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2436 entryBuilder.getIntegerType(numBits));
2440Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2441 auto &entry = hwConstantZMap[type];
2443 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2444 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2458Attribute FIRRTLLowering::getZeroAttributeForType(Type type) {
2459 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2460 return builder.getIntegerAttr(intType, 0);
2461 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type)) {
2465 auto element = getZeroAttributeForType(array.getElementType());
2466 SmallVector<Attribute> values(array.getNumElements(), element);
2467 return builder.getArrayAttr(values);
2469 if (
auto structType = hw::type_dyn_cast<hw::StructType>(type)) {
2470 SmallVector<Attribute> values;
2471 values.reserve(structType.getElements().size());
2472 for (
auto &field : structType.getElements())
2473 values.push_back(getZeroAttributeForType(field.type));
2474 return builder.getArrayAttr(values);
2476 llvm_unreachable(
"unsupported lowered type for zero attribute");
2482Value FIRRTLLowering::getZeroValueForType(Type type) {
2483 if (
auto intType = hw::type_dyn_cast<IntegerType>(type))
2484 return getOrCreateIntConstant(intType.getWidth(), 0);
2485 return hw::AggregateConstantOp::create(
2486 builder, type, cast<ArrayAttr>(getZeroAttributeForType(type)));
2493Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2495 if (
auto lowering = valueMapping.lookup(value)) {
2496 assert(!isa<FIRRTLType>(lowering.getType()) &&
2497 "Lowered value should be a non-FIRRTL value");
2506Value FIRRTLLowering::getLoweredValue(Value value) {
2507 auto result = getPossiblyInoutLoweredValue(value);
2513 if (isa<hw::InOutType>(result.getType()))
2514 return getReadValue(result);
2520Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2521 auto result = getLoweredValue(value);
2525 if (hw::type_isa<seq::ClockType>(result.getType()))
2526 return getNonClockValue(result);
2534Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2537 bool allowTruncate) {
2538 SmallVector<Value> resultBuffer;
2543 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2544 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2545 auto resultType = builder.getIntegerType(destWidth);
2547 if (srcWidth == destWidth)
2550 if (srcWidth > destWidth) {
2554 builder.emitError(
"operand should not be a truncation");
2558 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2559 return comb::createOrFoldSExt(builder, value, resultType);
2560 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2568 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2569 .Case<FVectorType>([&](
auto srcVectorType) {
2570 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2571 unsigned size = resultBuffer.size();
2572 unsigned indexWidth =
2574 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2575 destVectorType.getNumElements());
2577 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2579 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2580 destVectorType.getElementType())))
2583 SmallVector<Value> temp(resultBuffer.begin() + size,
2584 resultBuffer.end());
2586 resultBuffer.resize(size);
2587 resultBuffer.push_back(array);
2590 .Case<BundleType>([&](BundleType srcStructType) {
2591 auto destStructType = firrtl::type_cast<BundleType>(destType);
2592 unsigned size = resultBuffer.size();
2595 if (destStructType.getNumElements() != srcStructType.getNumElements())
2598 for (
auto elem :
llvm::enumerate(destStructType)) {
2599 auto structExtract =
2601 if (failed(recurse(structExtract,
2602 srcStructType.getElementType(elem.index()),
2603 destStructType.getElementType(elem.index()))))
2606 SmallVector<Value> temp(resultBuffer.begin() + size,
2607 resultBuffer.end());
2610 resultBuffer.resize(size);
2611 resultBuffer.push_back(newStruct);
2614 .Case<IntType>([&](
auto) {
2615 if (
auto result = cast(src, srcType, destType)) {
2616 resultBuffer.push_back(result);
2621 .Default([&](
auto) {
return failure(); });
2624 if (failed(recurse(array, sourceType, destType)))
2627 assert(resultBuffer.size() == 1 &&
2628 "resultBuffer must only contain a result array if `success` is true");
2629 return resultBuffer[0];
2637Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2638 auto srcType = cast<FIRRTLBaseType>(src.getType());
2639 auto dstType = cast<FIRRTLBaseType>(target);
2640 auto loweredSrc = getLoweredValue(src);
2643 auto dstWidth = dstType.getBitWidthOrSentinel();
2659 return getOrCreateIntConstant(dstWidth, 0);
2662 auto loweredSrcType = loweredSrc.getType();
2663 auto loweredDstType =
lowerType(dstType);
2666 if (loweredSrcType == loweredDstType)
2670 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2672 if (loweredSrcType != loweredDstType &&
2673 (isa<hw::TypeAliasType>(loweredSrcType) ||
2674 isa<hw::TypeAliasType>(loweredDstType))) {
2675 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2680 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2681 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2684 if (isa<seq::ClockType>(loweredSrcType)) {
2685 builder.emitError(
"cannot use clock type as an integer");
2689 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2690 if (!intSourceType) {
2691 builder.emitError(
"operand of type ")
2692 << loweredSrcType <<
" cannot be used as an integer";
2696 auto loweredSrcWidth = intSourceType.getWidth();
2697 if (loweredSrcWidth ==
unsigned(dstWidth))
2700 if (loweredSrcWidth >
unsigned(dstWidth)) {
2701 builder.emitError(
"operand should not be a truncation");
2706 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2707 if (type_cast<IntType>(valueFIRType).isSigned())
2708 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2710 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2719Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2720 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2721 type_isa<FIRRTLBaseType>(destType) &&
2722 "input/output value should be FIRRTL");
2725 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2726 if (destWidth == -1)
2729 auto result = getLoweredValue(value);
2741 return getOrCreateIntConstant(destWidth, 0);
2745 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2747 if (destType == value.getType())
2750 return getExtOrTruncAggregateValue(
2751 result, type_cast<FIRRTLBaseType>(value.getType()),
2752 type_cast<FIRRTLBaseType>(destType),
2756 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2757 if (srcWidth ==
unsigned(destWidth))
2763 if (srcWidth >
unsigned(destWidth)) {
2764 auto resultType = builder.getIntegerType(destWidth);
2768 auto resultType = builder.getIntegerType(destWidth);
2772 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2773 if (type_cast<IntType>(valueFIRType).isSigned())
2774 return comb::createOrFoldSExt(builder, result, resultType);
2776 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2790std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2792 if (type_isa<FStringType>(operand.getType())) {
2793 if (isa<TimeOp>(operand.getDefiningOp()))
2794 return sv::TimeOp::create(builder);
2795 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2799 auto loweredValue = getLoweredValue(operand);
2800 if (!loweredValue) {
2804 loweredValue = getOrCreateIntConstant(1, 0);
2809 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2810 if (intTy.isSigned())
2811 loweredValue = sv::SystemFunctionOp::create(
2812 builder, loweredValue.getType(),
"signed", loweredValue);
2814 return loweredValue;
2818FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2819 SmallVectorImpl<Value> &loweredOperands) {
2820 for (
auto operand : operands) {
2821 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2826 loweredOperands.push_back(*loweredValue);
2832FIRRTLLowering::lowerSimFormatString(StringRef originalFormatString,
2833 ValueRange operands) {
2834 SmallVector<Value> fragments;
2836 auto emitLiteral = [&](StringRef text) {
2838 fragments.push_back(sim::FormatLiteralOp::create(builder, text));
2841 auto emitIntFormat = [&](Value operand,
char specifier,
2842 IntegerAttr widthAttr) -> FailureOr<Value> {
2844 if (type_isa<ClockType>(operand.getType()))
2845 loweredValue = getLoweredNonClockValue(operand);
2847 loweredValue = getLoweredValue(operand);
2848 if (!loweredValue) {
2851 loweredValue = getOrCreateIntConstant(1, 0);
2854 if (!mlir::isa<IntegerType>(loweredValue.getType())) {
2855 emitError(builder.getLoc(),
"lower-to-core requires integer printf "
2857 << specifier <<
"'";
2861 switch (specifier) {
2863 return sim::FormatBinOp::create(builder, loweredValue,
2864 builder.getBoolAttr(
false),
2865 builder.getI8IntegerAttr(
'0'), widthAttr)
2868 UnitAttr signedAttr;
2869 if (
auto intTy = dyn_cast<IntType>(operand.getType());
2870 intTy && intTy.isSigned())
2871 signedAttr = builder.getUnitAttr();
2872 return sim::FormatDecOp::create(
2873 builder, loweredValue, builder.getBoolAttr(
false),
2874 builder.getI8IntegerAttr(
' '), widthAttr, signedAttr)
2878 return sim::FormatHexOp::create(builder, loweredValue,
2879 builder.getBoolAttr(
false),
2880 builder.getBoolAttr(
false),
2881 builder.getI8IntegerAttr(
'0'), widthAttr)
2884 return sim::FormatCharOp::create(builder, loweredValue).getResult();
2886 llvm_unreachable(
"unsupported FIRRTL format specifier");
2890 SmallString<32> literal;
2891 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
2892 char c = originalFormatString[i];
2895 emitLiteral(literal);
2898 SmallString<6> width;
2899 c = originalFormatString[++i];
2902 c = originalFormatString[++i];
2905 IntegerAttr widthAttr;
2906 if (!width.empty()) {
2907 unsigned widthValue;
2908 if (StringRef(width).getAsInteger(10, widthValue)) {
2909 emitError(builder.getLoc(),
"invalid FIRRTL printf width");
2912 widthAttr = builder.getI32IntegerAttr(widthValue);
2916 if (!width.empty()) {
2917 emitError(builder.getLoc(),
2918 "literal percents ('%%') may not specify a width");
2921 literal.push_back(
'%');
2925 if (operands.size() <= subIdx) {
2926 emitError(builder.getLoc(),
"not enough operands for printf format");
2930 if (c ==
'c' && widthAttr) {
2931 emitError(builder.getLoc(),
"ASCII character format specifiers ('%c') "
2932 "may not specify a width");
2941 auto fragment = emitIntFormat(operands[subIdx++], c, widthAttr);
2942 if (failed(fragment))
2944 fragments.push_back(*fragment);
2948 emitError(builder.getLoc(),
"unknown printf substitution '%")
2949 << width << c <<
"'";
2955 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
2956 literal.push_back(c);
2960 emitLiteral(literal);
2963 if (operands.size() <= subIdx) {
2964 emitError(builder.getLoc(),
"not enough operands for printf format");
2968 auto substitution = operands[subIdx++];
2969 if (!type_isa<FStringType>(substitution.getType())) {
2970 emitError(builder.getLoc(),
"expected fstring operand for '{{}}' "
2976 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
2977 .template Case<HierarchicalModuleNameOp>([&](
auto) {
2978 fragments.push_back(sim::FormatHierPathOp::create(
2982 .
template Case<TimeOp>([&](
auto) {
2983 fragments.push_back(sim::FormatCurrentTimeOp::create(builder));
2986 .Default([&](
auto) {
2987 emitError(builder.getLoc(),
"has a substitution with "
2990 .attachNote(substitution.getLoc())
2991 <<
"op with an unimplemented lowering is here";
3000 literal.push_back(c);
3005 emitLiteral(literal);
3006 if (fragments.empty())
3007 return sim::FormatLiteralOp::create(builder,
"").getResult();
3008 if (fragments.size() == 1)
3009 return fragments.front();
3010 return sim::FormatStringConcatOp::create(builder, fragments).getResult();
3013LogicalResult FIRRTLLowering::lowerStatementWithFd(
3014 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
3015 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
3017 bool failed =
false;
3018 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
3019 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
3020 addToAlwaysBlock(clock, [&]() {
3023 circuitState.usedPrintf =
true;
3025 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
3028 Value ifCond = cond;
3029 if (usePrintfCond) {
3031 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
3032 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
3035 addIfProceduralBlock(ifCond, [&]() {
3039 if (fileDescriptor.isDefaultFd()) {
3041 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
3044 auto fdOrError = callFileDescriptorLib(fileDescriptor);
3045 if (llvm::failed(fdOrError)) {
3051 failed = llvm::failed(fn(fd));
3055 return failure(failed);
3059FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
3060 circuitState.usedFileDescriptorLib =
true;
3061 circuitState.addFragment(
3062 theModule, sv::getFileDescriptorFragmentRef(builder.getContext()));
3065 if (
info.isSubstitutionRequired()) {
3066 SmallVector<Value> fileNameOperands;
3067 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
3070 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
3075 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
3079 return sv::createProceduralFileDescriptorGetterCall(builder, builder.getLoc(),
3089LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
3090 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
3091 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
3092 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
3096 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
3099 if (srcWidth != -1) {
3101 assert((srcWidth != 0) &&
3102 "Lowering produced value for zero width source");
3104 assert((srcWidth == 0) &&
3105 "Lowering produced null value but source wasn't zero width");
3109 assert(result &&
"Lowering of foreign type produced null value");
3112 auto &slot = valueMapping[orig];
3113 assert(!slot &&
"value lowered multiple times");
3120LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
3124 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
3125 auto &entry = hwConstantMap[cst.getValueAttr()];
3136 cst->moveBefore(&theModule.getBodyBlock()->front());
3140 return setLowering(orig, result);
3145template <
typename ResultOpType,
typename... CtorArgTypes>
3146LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
3147 CtorArgTypes... args) {
3148 auto result = builder.createOrFold<ResultOpType>(args...);
3149 if (
auto *op = result.getDefiningOp())
3151 return setPossiblyFoldedLowering(orig->getResult(0), result);
3158template <
typename ResultOpType,
typename... CtorArgTypes>
3159LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
3160 CtorArgTypes... args) {
3161 auto result = builder.createOrFold<ResultOpType>(args...);
3162 if (
auto *op = result.getDefiningOp())
3163 ltlOpFixupWorklist.insert(op);
3164 return setPossiblyFoldedLowering(orig->getResult(0), result);
3173Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
3174 auto backedge = backedgeBuilder.
get(type, loc);
3175 backedges.insert({backedge, backedge});
3183Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3184 auto backedge = createBackedge(orig.getLoc(), type);
3185 (void)setLowering(orig, backedge);
3191bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3192 auto backedgeIt = backedges.find(dest);
3193 if (backedgeIt == backedges.end())
3195 backedgeIt->second = src;
3203void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3204 const std::function<
void(
void)> &fn, Region ®ion) {
3208 auto oldIP = builder.saveInsertionPoint();
3210 builder.setInsertionPointToEnd(®ion.front());
3212 builder.restoreInsertionPoint(oldIP);
3216Value FIRRTLLowering::getReadValue(Value v) {
3217 Value result = readInOutCreated.lookup(v);
3223 auto oldIP = builder.saveInsertionPoint();
3224 if (
auto *vOp = v.getDefiningOp()) {
3225 builder.setInsertionPointAfter(vOp);
3229 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3234 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3235 result = getReadValue(arrayIndexInout.getInput());
3237 arrayIndexInout.getIndex());
3242 builder.restoreInsertionPoint(oldIP);
3243 readInOutCreated.insert({v, result});
3247Value FIRRTLLowering::getNonClockValue(Value v) {
3248 auto it = fromClockMapping.try_emplace(v, Value{});
3250 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3251 builder.setInsertionPointAfterValue(v);
3252 it.first->second = seq::FromClockOp::create(builder, v);
3254 return it.first->second;
3257void FIRRTLLowering::addToAlwaysBlock(
3258 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3259 sv::EventControl resetEdge, Value reset,
3260 const std::function<
void(
void)> &body,
3261 const std::function<
void(
void)> &resetBody) {
3262 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3263 resetStyle, resetEdge, reset};
3264 sv::AlwaysOp alwaysOp;
3265 sv::IfOp insideIfOp;
3266 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3270 assert(resetStyle != sv::ResetType::NoReset);
3283 auto createIfOp = [&]() {
3286 insideIfOp = sv::IfOp::create(
3287 builder, reset, [] {}, [] {});
3289 if (resetStyle == sv::ResetType::AsyncReset) {
3290 sv::EventControl events[] = {clockEdge, resetEdge};
3291 Value clocks[] = {clock, reset};
3293 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3294 if (resetEdge == sv::EventControl::AtNegEdge)
3295 llvm_unreachable(
"negative edge for reset is not expected");
3299 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3303 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3304 insideIfOp =
nullptr;
3306 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3310 assert(insideIfOp &&
"reset body must be initialized before");
3311 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3312 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3314 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3320 alwaysOp->moveBefore(builder.getInsertionBlock(),
3321 builder.getInsertionPoint());
3324LogicalResult FIRRTLLowering::emitGuards(Location loc,
3325 ArrayRef<Attribute> guards,
3326 std::function<
void(
void)>
emit) {
3327 if (guards.empty()) {
3331 auto guard = dyn_cast<StringAttr>(guards[0]);
3333 return mlir::emitError(loc,
3334 "elements in `guards` array must be `StringAttr`");
3337 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3338 LogicalResult result = LogicalResult::failure();
3339 addToIfDefBlock(guard.getValue(), [&]() {
3340 result = emitGuards(loc, guards.drop_front(), emit);
3345void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3346 std::function<
void(
void)> thenCtor,
3347 std::function<
void(
void)> elseCtor) {
3348 auto condAttr = builder.getStringAttr(cond);
3349 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3351 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3352 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3357 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3359 ifdefBlocks[{builder.getBlock(), condAttr}] =
3360 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3364void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3365 auto op = initialBlocks.lookup(builder.getBlock());
3367 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3372 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3374 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3378void FIRRTLLowering::addIfProceduralBlock(Value cond,
3379 std::function<
void(
void)> thenCtor,
3380 std::function<
void(
void)> elseCtor) {
3383 auto insertIt = builder.getInsertionPoint();
3384 if (insertIt != builder.getBlock()->begin())
3385 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3386 if (ifOp.getCond() == cond) {
3387 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3388 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3393 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3405FIRRTLLowering::UnloweredOpResult
3406FIRRTLLowering::handleUnloweredOp(Operation *op) {
3408 if (!op->getRegions().empty() &&
3409 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3410 op->emitOpError(
"must explicitly handle its regions");
3411 return LoweringFailure;
3418 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3420 for (
auto ®ion : op->getRegions())
3421 addToWorklist(region);
3422 for (
auto &operand : op->getOpOperands())
3423 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3424 operand.set(lowered);
3425 for (
auto result : op->getResults())
3426 (void)setLowering(result, result);
3427 return AlreadyLowered;
3439 if (op->getNumResults() == 1) {
3440 auto resultType = op->getResult(0).getType();
3441 if (type_isa<FIRRTLBaseType>(resultType) &&
3443 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3445 (void)setLowering(op->getResult(0), Value());
3449 op->emitOpError(
"LowerToHW couldn't handle this operation");
3450 return LoweringFailure;
3453LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3456 return setLowering(op, Value());
3458 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3461LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3463 if (isa<ClockType>(op.getType())) {
3464 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3465 :
seq::ClockConst::Low);
3467 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3469 return setLowering(op, cst);
3472FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3473 auto iIdx = getOrCreateIntConstant(
3475 firrtl::type_cast<FVectorType>(op.getInput().getType())
3482 if (isa<sv::InOutType>(input.getType()))
3483 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3486 if (
auto *definingOp = result.getDefiningOp())
3491FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3492 Value valueIdx = getLoweredAndExtOrTruncValue(
3494 UIntType::get(op->getContext(),
3496 firrtl::type_cast<FVectorType>(op.getInput().getType())
3497 .getNumElements())));
3499 op->emitError() <<
"input lowering failed";
3506 if (isa<sv::InOutType>(input.getType()))
3507 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3509 result = createArrayIndexing(input, valueIdx);
3510 if (
auto *definingOp = result.getDefiningOp())
3515FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3516 auto resultType =
lowerType(op->getResult(0).getType());
3517 if (!resultType || !input) {
3518 op->emitError() <<
"subfield type lowering failed";
3524 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3525 .getElementName(op.getFieldIndex());
3527 if (isa<sv::InOutType>(input.getType()))
3528 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3531 if (
auto *definingOp = result.getDefiningOp())
3536LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3538 return setLowering(op, Value());
3540 auto input = getPossiblyInoutLoweredValue(op.getInput());
3542 return op.emitError() <<
"input lowering failed";
3544 auto result = lowerSubindex(op, input);
3547 return setLowering(op, *result);
3550LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3552 return setLowering(op, Value());
3554 auto input = getPossiblyInoutLoweredValue(op.getInput());
3556 return op.emitError() <<
"input lowering failed";
3558 auto result = lowerSubaccess(op, input);
3561 return setLowering(op, *result);
3564LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3567 if (getLoweredValue(op) || !op.getInput())
3571 return setLowering(op, Value());
3573 auto input = getPossiblyInoutLoweredValue(op.getInput());
3575 return op.emitError() <<
"input lowering failed";
3577 auto result = lowerSubfield(op, input);
3580 return setLowering(op, *result);
3583LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3584 auto resultType =
lowerType(op.getResult().getType());
3585 auto arrayType = cast<hw::ArrayType>(resultType);
3586 SmallVector<Value> operands;
3588 for (
auto oper :
llvm::reverse(op.getOperands())) {
3589 auto val = getLoweredValue(oper);
3594 val = getZeroValueForType(arrayType.getElementType());
3596 operands.push_back(val);
3598 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3601LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3602 auto resultType =
lowerType(op.getResult().getType());
3603 auto structType = cast<hw::StructType>(resultType);
3604 SmallVector<Value> operands;
3605 for (
auto [oper, field] :
3606 llvm::zip_equal(op.getOperands(), structType.getElements())) {
3607 auto val = getLoweredValue(oper);
3612 val = getZeroValueForType(field.type);
3614 operands.push_back(val);
3616 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3619LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3622 return setLowering(op, Value());
3624 auto input = getLoweredValue(op.getInput());
3625 auto tagName = op.getFieldNameAttr();
3626 auto oldType = op.getType().base();
3628 auto element = *oldType.getElement(op.getFieldNameAttr());
3630 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3636 input = getOrCreateIntConstant(0, 0);
3638 auto tagType = structType.getFieldType(
"tag");
3639 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3640 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3642 auto bodyType = structType.getFieldType(
"body");
3643 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3644 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3645 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3647 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3648 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3651LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3652 auto resultType =
lowerType(op.getResult().getType());
3654 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3656 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3657 cast<ArrayAttr>(attr));
3660LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3664 return setLowering(op, getOrCreateIntConstant(1, 1));
3666 auto tagName = op.getFieldNameAttr();
3667 auto lhs = getLoweredValue(op.getInput());
3668 if (isa<hw::StructType>(lhs.getType()))
3671 auto index = op.getFieldIndex();
3672 auto enumType = op.getInput().getType().base();
3673 auto tagValue = enumType.getElementValueAttr(index);
3674 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3675 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3676 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3677 loweredTagValue, tagName);
3679 Type resultType = builder.getIntegerType(1);
3680 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3684LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3687 return setLowering(op, Value());
3689 auto tagName = op.getFieldNameAttr();
3690 auto input = getLoweredValue(op.getInput());
3692 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3695LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3698 return setLowering(op, Value());
3700 auto input = getLoweredValue(op.getInput());
3706 if (isa<hw::StructType>(input.getType())) {
3707 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3712 return setLowering(op, input);
3719LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3720 auto origResultType = op.getResult().getType();
3724 if (!type_isa<FIRRTLType>(origResultType)) {
3725 createBackedge(op.getResult(), origResultType);
3729 auto resultType =
lowerType(origResultType);
3733 if (resultType.isInteger(0)) {
3734 if (op.getInnerSym())
3735 return op.emitError(
"zero width wire is referenced by name [")
3736 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3737 return setLowering(op.getResult(), Value());
3741 auto innerSym = lowerInnerSymbol(op);
3742 auto name = op.getNameAttr();
3745 auto wire = hw::WireOp::create(
3746 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3748 if (
auto svAttrs = sv::getSVAttributes(op))
3749 sv::setSVAttributes(wire, svAttrs);
3751 return setLowering(op.getResult(), wire);
3754LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3755 auto resultTy =
lowerType(op.getType());
3758 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3760 SmallVector<Value, 4> operands;
3761 operands.reserve(op.getSubstitutions().size());
3762 for (
auto operand : op.getSubstitutions()) {
3763 auto lowered = getLoweredValue(operand);
3766 operands.push_back(lowered);
3769 ArrayAttr symbols = op.getSymbolsAttr();
3771 symbols = ArrayAttr::get(op.getContext(), {});
3773 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3777LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3778 auto operand = getLoweredValue(op.getInput());
3780 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3781 if (op.getInnerSym())
3782 return op.emitError(
"zero width node is referenced by name [")
3783 << *op.getInnerSym()
3784 <<
"] (e.g. in an XMR) but must be "
3786 return setLowering(op.getResult(), Value());
3792 auto name = op.getNameAttr();
3793 auto innerSym = lowerInnerSymbol(op);
3796 operand = hw::WireOp::create(builder, operand, name, innerSym);
3799 if (
auto svAttrs = sv::getSVAttributes(op)) {
3801 operand = hw::WireOp::create(builder, operand, name);
3802 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3805 return setLowering(op.getResult(), operand);
3808LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3809 auto resultType =
lowerType(op.getResult().getType());
3812 if (resultType.isInteger(0))
3813 return setLowering(op.getResult(), Value());
3815 Value clockVal = getLoweredValue(op.getClockVal());
3820 Attribute presetAttr;
3821 if (
auto initial = op.getInitialAttr()) {
3822 auto intTy = dyn_cast<IntegerType>(resultType);
3823 assert(intTy &&
"'initial' must be integer type");
3824 presetAttr = builder.getIntegerAttr(
3825 intTy, initial.getValue().zextOrTrunc(intTy.getWidth()));
3829 auto innerSym = lowerInnerSymbol(op);
3830 Backedge inputEdge = backedgeBuilder.
get(resultType);
3831 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3832 op.getNameAttr(), innerSym, presetAttr);
3835 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3836 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3837 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3838 reg->setAttr(
"firrtl.random_init_start", randomStart);
3839 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3840 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3843 if (
auto svAttrs = sv::getSVAttributes(op))
3844 sv::setSVAttributes(reg, svAttrs);
3847 (void)setLowering(op.getResult(),
reg);
3851LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3852 auto resultType =
lowerType(op.getResult().getType());
3855 if (resultType.isInteger(0))
3856 return setLowering(op.getResult(), Value());
3858 Value clockVal = getLoweredValue(op.getClockVal());
3859 Value resetSignal = getLoweredValue(op.getResetSignal());
3861 Value resetValue = getLoweredAndExtOrTruncValue(
3862 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3864 if (!clockVal || !resetSignal || !resetValue)
3868 Attribute presetAttr;
3869 if (
auto initial = op.getInitialAttr()) {
3870 auto intTy = dyn_cast<IntegerType>(resultType);
3871 assert(intTy &&
"'initial' must be integer type");
3872 presetAttr = builder.getIntegerAttr(
3873 intTy, initial.getValue().zextOrTrunc(intTy.getWidth()));
3877 auto innerSym = lowerInnerSymbol(op);
3878 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3879 Backedge inputEdge = backedgeBuilder.
get(resultType);
3880 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3881 op.getNameAttr(), resetSignal, resetValue,
3882 innerSym, isAsync, presetAttr);
3885 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3886 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3887 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3888 reg->setAttr(
"firrtl.random_init_start", randomStart);
3889 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3890 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3893 if (
auto svAttrs = sv::getSVAttributes(op))
3894 sv::setSVAttributes(reg, svAttrs);
3897 (void)setLowering(op.getResult(),
reg);
3902LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3905 if (type_isa<BundleType>(op.getDataType()))
3906 return op.emitOpError(
3907 "should have already been lowered from a ground type to an aggregate "
3908 "type using the LowerTypes pass. Use "
3909 "'firtool --lower-types' or 'circt-opt "
3910 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3916 auto memType = seq::FirMemType::get(
3919 : std::optional<uint32_t>());
3921 seq::FirMemInitAttr memInit;
3922 if (
auto init = op.getInitAttr())
3923 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3924 init.getIsBinary(), init.getIsInline());
3926 auto memDecl = seq::FirMemOp::create(
3929 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3932 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3934 if (!file.isDirectory())
3935 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3936 file.getDirectory());
3937 memDecl.setOutputFileAttr(dir);
3943 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3945 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3948 (void)setLowering(a, value);
3954 auto addInput = [&](StringRef field, Value backedge) {
3956 if (cast<FIRRTLBaseType>(a.getType())
3958 .getBitWidthOrSentinel() > 0)
3959 (void)setLowering(a, backedge);
3965 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3969 Value backedge, portValue;
3971 portValue = getOrCreateXConstant(1);
3973 auto portType = IntegerType::get(op.getContext(), width);
3974 backedge = portValue = createBackedge(builder.getLoc(), portType);
3976 addInput(field, backedge);
3980 auto addClock = [&](StringRef field) -> Value {
3981 Type clockTy = seq::ClockType::get(op.getContext());
3982 Value portValue = createBackedge(builder.getLoc(), clockTy);
3983 addInput(field, portValue);
3987 auto memportKind = op.getPortKind(i);
3988 if (memportKind == MemOp::PortKind::Read) {
3989 auto addr = addInputPort(
"addr", op.getAddrBits());
3990 auto en = addInputPort(
"en", 1);
3991 auto clk = addClock(
"clk");
3992 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3994 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3995 auto addr = addInputPort(
"addr", op.getAddrBits());
3996 auto en = addInputPort(
"en", 1);
3997 auto clk = addClock(
"clk");
4000 auto mode = addInputPort(
"wmode", 1);
4002 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
4009 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
4013 auto addr = addInputPort(
"addr", op.getAddrBits());
4016 auto en = addInputPort(
"en", 1);
4020 auto clk = addClock(
"clk");
4034FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
4035 Operation *instanceOp,
4036 SmallVectorImpl<Value> &inputOperands) {
4038 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4039 auto &port = portInfo[portIndex];
4042 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
4047 if (portType.isInteger(0))
4051 if (port.isOutput())
4054 auto portResult = instanceOp->getResult(portIndex);
4055 assert(portResult &&
"invalid IR, couldn't find port");
4059 if (port.isInput()) {
4060 inputOperands.push_back(createBackedge(portResult, portType));
4066 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
4067 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
4069 auto loweredResult = getPossiblyInoutLoweredValue(source);
4070 inputOperands.push_back(loweredResult);
4071 (void)setLowering(portResult, loweredResult);
4080 "." + port.getName().str() +
".wire");
4084 (void)setLowering(portResult, wire);
4085 inputOperands.push_back(wire);
4091LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
4092 Operation *oldModule =
4093 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
4095 auto *newModule = circuitState.getNewModule(oldModule);
4097 oldInstance->emitOpError(
"could not find module [")
4098 << oldInstance.getModuleName() <<
"] referenced by instance";
4104 ArrayAttr parameters;
4105 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
4110 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
4114 SmallVector<Value, 8> operands;
4115 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
4122 auto innerSym = oldInstance.getInnerSymAttr();
4123 if (oldInstance.getLowerToBind()) {
4126 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
4129 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
4130 innerSym.getSymName());
4133 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
4134 bindOp->setAttr(
"output_file", outputFile);
4137 circuitState.addBind(bindOp);
4142 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
4143 operands, parameters, innerSym);
4145 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
4146 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
4148 if (newInstance.getInnerSymAttr())
4149 if (
auto forceName = circuitState.instanceForceNames.lookup(
4150 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
4151 newInstance.getInnerNameAttr()}))
4152 newInstance->setAttr(
"hw.verilogName", forceName);
4156 unsigned resultNo = 0;
4157 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4158 auto &port = portInfo[portIndex];
4162 Value resultVal = newInstance.getResult(resultNo);
4164 auto oldPortResult = oldInstance.getResult(portIndex);
4165 (void)setLowering(oldPortResult, resultVal);
4171LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
4172 if (oldInstanceChoice.getInnerSymAttr()) {
4173 oldInstanceChoice->emitOpError(
4174 "instance choice with inner sym cannot be lowered");
4179 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
4181 return oldInstanceChoice->emitOpError(
4182 "must have instance_macro attribute set before "
4186 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
4187 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
4190 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
4191 auto *defaultModuleNode =
4192 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
4194 Operation *defaultModule = defaultModuleNode->getModule();
4198 SmallVector<PortInfo, 8> portInfo =
4199 cast<FModuleLike>(defaultModule).getPorts();
4202 SmallVector<Value, 8> inputOperands;
4204 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
4208 SmallVector<sv::WireOp, 8> outputWires;
4209 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
4210 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
4211 auto &port = portInfo[portIndex];
4215 if (!portType || portType.isInteger(0))
4218 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4219 outputWires.push_back(wire);
4220 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4224 auto optionName = oldInstanceChoice.getOptionNameAttr();
4227 auto createInstanceAndAssign = [&](Operation *oldMod,
4228 StringRef suffix) -> hw::InstanceOp {
4229 auto *newMod = circuitState.getNewModule(oldMod);
4231 ArrayAttr parameters;
4232 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4236 SmallString<64> instName;
4237 instName = oldInstanceChoice.getInstanceName();
4238 if (!suffix.empty()) {
4244 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4245 inputOperands, parameters,
nullptr);
4251 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4258 SmallVector<StringAttr> macroNames;
4259 SmallVector<Operation *> altModules;
4260 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4261 altModules.push_back(
4262 circuitState.getInstanceGraph()
4263 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4267 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4268 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4269 if (!optionCaseMacroRef)
4270 return oldInstanceChoice->emitOpError(
4271 "failed to get macro for option case");
4272 macroNames.push_back(optionCaseMacroRef.getAttr());
4276 sv::createNestedIfDefs(
4279 [&](StringRef macro, std::function<
void()> thenCtor,
4280 std::function<
void()> elseCtor) {
4281 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4285 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4286 sv::IfDefOp::create(
4287 builder, oldInstanceChoice.getLoc(), macroNames[i],
4289 SmallString<256> errorMessage;
4290 llvm::raw_svector_ostream os(errorMessage);
4291 os <<
"Multiple instance choice options defined for option '"
4292 << optionName.getValue() <<
"': '"
4293 << macroNames[index].getValue() <<
"' and '"
4294 << macroNames[i].getValue() <<
"'";
4295 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4296 builder.getStringAttr(errorMessage));
4302 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4304 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4306 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4307 builder.getStringAttr(
"{{0}}"),
4308 builder.getArrayAttr({hw::InnerRefAttr::get(
4309 theModule.getNameAttr(),
4310 inst.getInnerSymAttr().getSymName())}));
4314 SmallString<256> errorMessage;
4315 llvm::raw_svector_ostream os(errorMessage);
4316 os <<
"Required instance choice option '" << optionName.getValue()
4317 <<
"' not selected, must define one of: ";
4318 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4319 os <<
"'" << macro.getValue() <<
"'";
4321 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4322 builder.getStringAttr(errorMessage));
4328LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4329 SmallVector<Value> inputs;
4330 SmallVector<Type> types;
4331 for (
auto input : oldOp.getInputs()) {
4332 auto lowered = getLoweredValue(input);
4335 inputs.push_back(lowered);
4336 types.push_back(lowered.getType());
4339 auto newOp = verif::ContractOp::create(builder, types, inputs);
4340 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4341 auto &body = newOp.getBody().emplaceBlock();
4343 for (
auto [newResult, oldResult, oldArg] :
4344 llvm::zip(newOp.getResults(), oldOp.getResults(),
4345 oldOp.getBody().getArguments())) {
4346 if (failed(setLowering(oldResult, newResult)))
4348 if (failed(setLowering(oldArg, newResult)))
4352 body.getOperations().splice(body.end(),
4353 oldOp.getBody().front().getOperations());
4354 addToWorklist(body);
4364LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4365 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4370 return setLowering(op->getResult(0), operand);
4373LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4374 if (isa<ClockType>(op.getInput().getType()))
4375 return setLowering(op->getResult(0),
4376 getLoweredNonClockValue(op.getInput()));
4377 return lowerNoopCast(op);
4380LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4381 if (isa<ClockType>(op.getInput().getType()))
4382 return setLowering(op->getResult(0),
4383 getLoweredNonClockValue(op.getInput()));
4384 return lowerNoopCast(op);
4387LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4388 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4391LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4392 mlir::UnrealizedConversionCastOp op) {
4394 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4397 auto operand = op.getOperand(0);
4398 auto result = op.getResult(0);
4401 if (type_isa<FIRRTLType>(operand.getType()) &&
4402 type_isa<FIRRTLType>(result.getType()))
4403 return lowerNoopCast(op);
4407 if (!type_isa<FIRRTLType>(operand.getType())) {
4408 if (type_isa<FIRRTLType>(result.getType()))
4409 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4415 auto loweredResult = getLoweredValue(operand);
4416 if (!loweredResult) {
4419 if (operand.getType().isSignlessInteger(0)) {
4420 return setLowering(result, Value());
4427 result.replaceAllUsesWith(loweredResult);
4431LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4434 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4435 return setLowering(op, op.getOperand());
4439 auto result = getLoweredValue(op.getOperand());
4445 op.replaceAllUsesWith(result);
4449LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4450 auto operand = getLoweredValue(op.getOperand());
4453 auto resultType =
lowerType(op.getType());
4457 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4460LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4461 auto operand = getLoweredValue(op.getOperand());
4465 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4466 return setLowering(op, getOrCreateIntConstant(1, 0));
4468 return setLowering(op, Value());
4473 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4474 return setLowering(op, operand);
4477 auto zero = getOrCreateIntConstant(1, 0);
4478 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4481LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4482 auto operand = getLoweredValue(op.getInput());
4486 auto allOnes = getOrCreateIntConstant(
4487 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4488 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4491LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4494 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4498 auto resultType =
lowerType(op.getType());
4500 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4501 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4505LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4506 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4509 return setLowering(op, operand);
4512LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4513 auto operand = getLoweredValue(op.getInput());
4516 return setLowering(op, getOrCreateIntConstant(1, 0));
4521 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4525LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4526 auto operand = getLoweredValue(op.getInput());
4529 return setLowering(op, getOrCreateIntConstant(1, 1));
4534 return setLoweringTo<comb::ICmpOp>(
4535 op, ICmpPredicate::eq, operand,
4536 getOrCreateIntConstant(
4537 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4541LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4542 auto operand = getLoweredValue(op.getInput());
4545 return setLowering(op, getOrCreateIntConstant(1, 0));
4551 return setLoweringTo<comb::ICmpOp>(
4552 op, ICmpPredicate::ne, operand,
4553 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4561template <
typename ResultOpType>
4562LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4563 auto resultType = op->getResult(0).getType();
4564 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4565 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4569 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4575template <
typename ResultOpType>
4576LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4577 auto resultType = op->getResult(0).getType();
4578 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4579 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4590 auto intType = builder.getIntegerType(*bitwidth);
4591 auto retType = lhs.getType();
4594 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4595 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4600template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4601LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4603 auto resultType = op->getResult(0).getType();
4604 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4605 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4610 if (type_cast<IntType>(resultType).isSigned())
4611 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4612 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4617LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4618 ICmpPredicate unsignedOp) {
4620 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4621 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4622 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4626 if (cmpType.getWidth() == 0)
4627 cmpType = UIntType::get(builder.getContext(), 1);
4628 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4629 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4634 Type resultType = builder.getIntegerType(1);
4635 return setLoweringTo<comb::ICmpOp>(
4636 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4642template <
typename SignedOp,
typename Un
signedOp>
4643LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4647 auto opType = type_cast<IntType>(op->getResult(0).getType());
4648 if (opType.getWidth() == 0)
4649 return setLowering(op->getResult(0), Value());
4653 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4654 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4659 if (opType.isSigned())
4660 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4662 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4664 if (
auto *definingOp = result.getDefiningOp())
4667 if (resultType == opType)
4668 return setLowering(op->getResult(0), result);
4669 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4672LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4674 if (op.getInputs().empty())
4675 return setLowering(op, Value());
4677 SmallVector<Value> loweredOperands;
4680 for (
auto operand : op.getInputs()) {
4681 auto loweredOperand = getLoweredValue(operand);
4682 if (loweredOperand) {
4683 loweredOperands.push_back(loweredOperand);
4686 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4694 if (loweredOperands.empty())
4695 return setLowering(op, Value());
4698 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4705LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4706 auto input = getLoweredNonClockValue(op.getArg());
4710 if (!isa<IntType>(input.getType())) {
4711 auto srcType = op.getArg().getType();
4713 assert(bitwidth &&
"Unknown width");
4714 auto intType = builder.getIntegerType(*bitwidth);
4715 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4718 return setLoweringTo<comb::ICmpOp>(
4719 op, ICmpPredicate::ceq, input,
4720 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4723LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4724 auto operand = getLoweredValue(op.getInput());
4725 hw::WireOp::create(builder, operand);
4729LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4730 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4731 op.getFormatStringAttr());
4734LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4735 auto type =
lowerType(op.getResult().getType());
4739 auto valueOp = sim::PlusArgsValueOp::create(
4740 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4741 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4743 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4748LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4749 op.emitError(
"sizeof intrinsic should have been resolved by the "
4750 "LowerSizeOf pass.");
4754LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4756 if (op.getTestEnable())
4757 testEnable = getLoweredValue(op.getTestEnable());
4758 return setLoweringTo<seq::ClockGateOp>(
4759 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4760 testEnable, hw::InnerSymAttr{});
4763LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4764 auto operand = getLoweredValue(op.getInput());
4765 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4768LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4769 auto operand = getLoweredValue(op.getInput());
4770 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4773LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4774 return setLoweringToLTL<ltl::AndOp>(
4776 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4779LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4780 return setLoweringToLTL<ltl::OrOp>(
4782 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4785LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4786 return setLoweringToLTL<ltl::IntersectOp>(
4788 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4791LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4792 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4793 op.getDelayAttr(), op.getLengthAttr());
4796LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4797 return setLoweringToLTL<ltl::ConcatOp>(
4799 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4802LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4803 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4804 op.getBaseAttr(), op.getMoreAttr());
4807LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4808 return setLoweringToLTL<ltl::GoToRepeatOp>(
4809 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4812LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4813 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4814 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4817LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4818 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4821LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4822 return setLoweringToLTL<ltl::ImplicationOp>(
4824 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4827LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4828 return setLoweringToLTL<ltl::UntilOp>(
4830 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4833LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4834 return setLoweringToLTL<ltl::EventuallyOp>(op,
4835 getLoweredValue(op.getInput()));
4838LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4839 Value
clk = getLoweredNonClockValue(op.getClock());
4840 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4841 op.getDelayAttr(),
clk);
4845 switch (eventControl) {
4846 case EventControl::AtPosEdge:
4847 return ltl::ClockEdge::Pos;
4848 case EventControl::AtEdge:
4849 return ltl::ClockEdge::Both;
4850 case EventControl::AtNegEdge:
4851 return ltl::ClockEdge::Neg;
4853 llvm_unreachable(
"unknown event control");
4856LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4857 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4859 getLoweredNonClockValue(op.getClock()));
4862template <
typename TargetOp,
typename IntrinsicOp>
4863LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4864 auto property = getLoweredValue(op.getProperty());
4865 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4866 TargetOp::create(builder, property, enable, op.getLabelAttr());
4870LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4871 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4874LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4875 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4878LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4879 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4882LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4883 if (!isa<verif::ContractOp>(op->getParentOp()))
4884 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4885 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4888LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4889 if (!isa<verif::ContractOp>(op->getParentOp()))
4890 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4891 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4894LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4895 auto clock = getLoweredNonClockValue(op.getClock());
4896 auto reset = getLoweredValue(op.getReset());
4897 if (!clock || !reset)
4899 auto resetType = op.getReset().getType();
4900 auto uintResetType = dyn_cast<UIntType>(resetType);
4901 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4902 auto isAsync = isa<AsyncResetType>(resetType);
4903 if (!isAsync && !isSync) {
4904 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4905 "requires sync or async reset");
4906 d.attachNote() <<
"reset is of type " << resetType
4907 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4910 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4917LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4918 auto input = getLoweredValue(op.getInput());
4922 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4923 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4926LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4927 auto resultTy =
lowerType(op.getType());
4934 if (type_isa<AnalogType>(op.getType()))
4937 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4940 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4951 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4953 if (!type_isa<IntegerType>(resultTy))
4955 return setLowering(op, constant);
4959 op.emitOpError(
"unsupported type");
4963LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4964 auto input = getLoweredValue(op.getInput());
4967 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4968 if (op.getAmount() == 0)
4969 return setLowering(op, Value());
4970 Type resultType = builder.getIntegerType(op.getAmount());
4971 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4972 inWidth - op.getAmount());
4975LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4976 auto input = getLoweredValue(op.getInput());
4979 if (op.getAmount() == 0)
4981 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4986 if (op.getAmount() == 0)
4987 return setLowering(op, input);
4989 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4990 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4993LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4994 auto input = getLoweredValue(op.getInput());
4999 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
5000 auto shiftAmount = op.getAmount();
5001 if (shiftAmount >= inWidth) {
5003 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
5004 return setLowering(op, {});
5007 shiftAmount = inWidth - 1;
5010 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
5011 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
5014LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
5015 auto input = getLoweredValue(op.getInput());
5019 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
5020 if (inWidth == op.getAmount())
5021 return setLowering(op, Value());
5022 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
5023 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
5026LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
5027 auto cond = getLoweredValue(op.getSel());
5028 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5029 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5030 if (!cond || !ifTrue || !ifFalse)
5033 if (isa<ClockType>(op.getType()))
5034 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
5035 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
5039LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
5040 auto cond = getLoweredValue(op.getSel());
5041 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5042 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5043 if (!cond || !ifTrue || !ifFalse)
5046 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
5048 return setLowering(op, createValueWithMuxAnnotation(val,
true));
5051LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
5052 auto sel = getLoweredValue(op.getSel());
5053 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
5054 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
5055 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
5056 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
5057 if (!sel || !v3 || !v2 || !v1 || !v0)
5059 Value array[] = {v3, v2, v1, v0};
5062 return setLowering(op, createValueWithMuxAnnotation(val,
false));
5081Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
5082 assert(op->getNumResults() == 1 &&
"only expect a single result");
5083 auto val = op->getResult(0);
5087 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5094 OpBuilder::InsertionGuard guard(builder);
5095 builder.setInsertionPoint(op);
5096 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5097 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
5099 op->getContext(),
nullptr, 0,
5102 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5103 op->setOperand(idx, wire);
5108 sv::setSVAttributes(assignOp,
5109 sv::SVAttributeAttr::get(builder.getContext(),
5110 "synopsys infer_mux_override",
5115Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5117 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5122 if (!llvm::isPowerOf2_64(size)) {
5123 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5125 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5127 Value temp2[] = {ext.getResult(), array};
5133 return inBoundsRead;
5136LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5138 auto index = getLoweredAndExtOrTruncValue(
5140 UIntType::get(op.getContext(),
5145 SmallVector<Value> loweredInputs;
5146 loweredInputs.reserve(op.getInputs().size());
5147 for (
auto input : op.getInputs()) {
5148 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5151 loweredInputs.push_back(lowered);
5155 return setLowering(op, createArrayIndexing(array, index));
5158LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5159 auto resultTy =
lowerType(op.getType());
5163 SmallVector<Value, 4> operands;
5164 operands.reserve(op.getSubstitutions().size());
5165 for (
auto operand : op.getSubstitutions()) {
5166 auto lowered = getLoweredValue(operand);
5169 operands.push_back(lowered);
5172 ArrayAttr symbols = op.getSymbolsAttr();
5174 symbols = ArrayAttr::get(op.getContext(), {});
5176 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5180LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5184 Type baseType = op.getType().getType();
5187 if (isa<ClockType>(baseType))
5188 xmrType = builder.getIntegerType(1);
5192 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5193 op.getRef(), op.getVerbatimSuffixAttr());
5196LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5200 if (isa<ClockType>(op.getType()))
5201 xmrType = builder.getIntegerType(1);
5205 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5206 op.getRef(), op.getVerbatimSuffixAttr());
5207 auto readXmr = getReadValue(xmr);
5208 if (!isa<ClockType>(op.getType()))
5209 return setLowering(op, readXmr);
5210 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5215LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5216LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5224LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5236FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5237 auto srcType = srcVal.getType();
5238 auto dstType = destVal.getType();
5239 if (srcType != dstType &&
5240 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5243 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5244 .Case<hw::WireOp>([&](
auto op) {
5245 maybeUnused(op.getInput());
5246 op.getInputMutable().assign(srcVal);
5249 .Case<seq::FirRegOp>([&](
auto op) {
5250 maybeUnused(op.getNext());
5251 op.getNextMutable().assign(srcVal);
5254 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5257 op.emitOpError(
"used as connect destination");
5260 .Default([](
auto) {
return false; });
5263LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5264 auto dest = op.getDest();
5266 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5267 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5269 return handleZeroBit(op.getSrc(), []() { return success(); });
5271 auto destVal = getPossiblyInoutLoweredValue(dest);
5275 auto result = lowerConnect(destVal, srcVal);
5283 if (updateIfBackedge(destVal, srcVal))
5286 if (!isa<hw::InOutType>(destVal.getType()))
5287 return op.emitError(
"destination isn't an inout type");
5293LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5294 auto dest = op.getDest();
5295 auto srcVal = getLoweredValue(op.getSrc());
5297 return handleZeroBit(op.getSrc(), []() { return success(); });
5299 auto destVal = getPossiblyInoutLoweredValue(dest);
5303 auto result = lowerConnect(destVal, srcVal);
5311 if (updateIfBackedge(destVal, srcVal))
5314 if (!isa<hw::InOutType>(destVal.getType()))
5315 return op.emitError(
"destination isn't an inout type");
5321LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5322 if (circuitState.lowerToCore)
5323 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5325 auto srcVal = getLoweredValue(op.getSrc());
5329 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5333 if (!isa<hw::InOutType>(destVal.getType()))
5334 return op.emitError(
"destination isn't an inout type");
5337 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5338 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5339 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5344LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5345 if (circuitState.lowerToCore)
5346 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5348 auto src = getLoweredNonClockValue(op.getSrc());
5349 auto clock = getLoweredNonClockValue(op.getClock());
5350 auto pred = getLoweredValue(op.getPredicate());
5351 if (!src || !clock || !pred)
5354 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5359 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5360 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5361 addToAlwaysBlock(clock, [&]() {
5362 addIfProceduralBlock(
5363 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5368LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5369 if (circuitState.lowerToCore)
5370 return op.emitOpError(
5371 "lower-to-core does not support firrtl.ref.force_initial");
5373 auto src = getLoweredNonClockValue(op.getSrc());
5374 auto pred = getLoweredValue(op.getPredicate());
5378 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5383 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5384 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5385 addToInitialBlock([&]() {
5386 addIfProceduralBlock(
5387 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5392LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5393 if (circuitState.lowerToCore)
5394 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5396 auto clock = getLoweredNonClockValue(op.getClock());
5397 auto pred = getLoweredValue(op.getPredicate());
5398 if (!clock || !pred)
5401 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5406 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5407 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5408 addToAlwaysBlock(clock, [&]() {
5409 addIfProceduralBlock(pred,
5410 [&]() { sv::ReleaseOp::create(builder, destVal); });
5415LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5416 if (circuitState.lowerToCore)
5417 return op.emitOpError(
5418 "lower-to-core does not support firrtl.ref.release_initial");
5420 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5421 auto pred = getLoweredValue(op.getPredicate());
5422 if (!destVal || !pred)
5426 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5427 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5428 addToInitialBlock([&]() {
5429 addIfProceduralBlock(pred,
5430 [&]() { sv::ReleaseOp::create(builder, destVal); });
5438 StringRef originalFormatString,
5439 ValueRange operands,
5440 StringAttr &result) {
5443 SmallString<32> formatString;
5444 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5445 char c = originalFormatString[i];
5449 formatString.push_back(c);
5452 SmallString<6> width;
5453 c = originalFormatString[++i];
5456 c = originalFormatString[++i];
5467 formatString.append(width);
5473 formatString.push_back(c);
5480 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5481 formatString.push_back(c);
5485 auto substitution = operands[subIdx++];
5486 assert(type_isa<FStringType>(substitution.getType()) &&
5487 "the operand for a '{{}}' substitution must be an 'fstring' type");
5489 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5490 .template Case<TimeOp>([&](
auto) {
5491 formatString.append(
"%0t");
5494 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5495 formatString.append(
"%m");
5498 .Default([&](
auto) {
5499 emitError(loc,
"has a substitution with an unimplemented "
5501 .attachNote(substitution.getLoc())
5502 <<
"op with an unimplemented lowering is here";
5512 formatString.push_back(c);
5516 result = StringAttr::get(loc->getContext(), formatString);
5523LogicalResult FIRRTLLowering::visitPrintfLike(
5524 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5525 auto clock = getLoweredNonClockValue(op.getClock());
5526 auto cond = getLoweredValue(op.getCond());
5527 if (!clock || !cond)
5530 StringAttr formatString;
5532 op.getSubstitutions(), formatString)))
5535 auto fn = [&](Value fd) {
5536 SmallVector<Value> operands;
5537 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5539 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5543 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5547LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5548 if (!circuitState.lowerToCore)
5549 return visitPrintfLike(op, {},
true);
5551 auto clock = getLoweredValue(op.getClock());
5552 auto cond = getLoweredValue(op.getCond());
5553 if (!clock || !cond)
5557 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5558 if (failed(formatString))
5561 auto stderrOp = sim::StderrStreamOp::create(builder);
5562 sim::TriggeredOp::create(builder, clock, cond, [&] {
5563 sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
5568LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5569 if (circuitState.lowerToCore) {
5570 auto clock = getLoweredValue(op.getClock());
5571 auto cond = getLoweredValue(op.getCond());
5572 if (!clock || !cond)
5575 auto fileFormatString = lowerSimFormatString(
5576 op.getOutputFileAttr(), op.getOutputFileSubstitutions());
5577 if (failed(fileFormatString))
5581 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5582 if (failed(formatString))
5585 sim::TriggeredOp::create(builder, clock, cond, [&] {
5586 auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
5587 sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
5592 StringAttr outputFileAttr;
5594 op.getOutputFileSubstitutions(),
5598 FileDescriptorInfo outputFile(outputFileAttr,
5599 op.getOutputFileSubstitutions());
5600 return visitPrintfLike(op, outputFile,
false);
5604LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5605 if (circuitState.lowerToCore)
5606 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5608 auto clock = getLoweredNonClockValue(op.getClock());
5609 auto cond = getLoweredValue(op.getCond());
5610 if (!clock || !cond)
5613 auto fn = [&](Value fd) {
5614 sv::FFlushOp::create(builder, op.getLoc(), fd);
5618 if (!op.getOutputFileAttr())
5619 return lowerStatementWithFd({}, clock, cond, fn,
false);
5623 StringAttr outputFileAttr;
5625 op.getOutputFileSubstitutions(),
5629 return lowerStatementWithFd(
5630 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5631 clock, cond, fn,
false);
5636LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5637 auto clock = getLoweredValue(op.getClock());
5638 auto cond = getLoweredValue(op.getCond());
5639 if (!clock || !cond)
5642 circuitState.usedStopCond =
true;
5643 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5646 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5647 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5649 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5650 op.getExitCode() == 0,
5659template <
typename... Args>
5661 StringRef opName, Args &&...args) {
5662 if (opName ==
"assert")
5663 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5664 if (opName ==
"assume")
5665 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5666 if (opName ==
"cover")
5667 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5668 llvm_unreachable(
"unknown verification op");
5674template <
typename... Args>
5676 StringRef opName, Args &&...args) {
5677 if (opName ==
"assert")
5678 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5679 if (opName ==
"assume")
5680 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5681 if (opName ==
"cover")
5682 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5683 llvm_unreachable(
"unknown verification op");
5687 switch (eventControl) {
5688 case EventControl::AtPosEdge:
5689 return verif::ClockEdge::Pos;
5690 case EventControl::AtEdge:
5691 return verif::ClockEdge::Both;
5692 case EventControl::AtNegEdge:
5693 return verif::ClockEdge::Neg;
5695 llvm_unreachable(
"unknown FIRRTL event control");
5698LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5699 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5700 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5701 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5702 if (guardsAttr && !guardsAttr.empty())
5703 return op->emitOpError(
5704 "lower-to-core does not support guarded verification statements");
5706 auto clock = getLoweredNonClockValue(opClock);
5707 auto enable = getLoweredValue(opEnable);
5708 auto predicate = getLoweredValue(opPredicate);
5709 if (!clock || !enable || !predicate)
5713 if (opNameAttr && !opNameAttr.getValue().empty())
5714 label = StringAttr::get(builder.getContext(),
5715 labelPrefix + opNameAttr.getValue());
5718 auto opName = op->getName().stripDialect();
5719 if (opName ==
"assert") {
5720 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5724 if (opName ==
"assume") {
5725 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5729 if (opName ==
"cover") {
5730 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5734 llvm_unreachable(
"unknown verification op");
5755LogicalResult FIRRTLLowering::lowerVerificationStatement(
5756 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5757 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5758 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5759 if (circuitState.lowerToCore)
5760 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5761 opPredicate, opEnable, opNameAttr,
5764 StringRef opName = op->getName().stripDialect();
5767 ArrayRef<Attribute> guards{};
5768 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5769 guards = guardsAttr.getValue();
5771 auto isCover = isa<CoverOp>(op);
5772 auto clock = getLoweredNonClockValue(opClock);
5773 auto enable = getLoweredValue(opEnable);
5774 auto predicate = getLoweredValue(opPredicate);
5775 if (!clock || !enable || !predicate)
5779 if (opNameAttr && !opNameAttr.getValue().empty())
5781 StringAttr prefixedLabel;
5784 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5787 SmallVector<Value> messageOps;
5791 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5792 flavor = VerificationFlavor::None;
5794 if (flavor == VerificationFlavor::None) {
5798 auto format = op->getAttrOfType<StringAttr>(
"format");
5800 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5801 if (!isa<AssertOp>(op))
5802 return op->emitError()
5803 <<
"ifElseFatal format cannot be used for non-assertions";
5804 flavor = VerificationFlavor::IfElseFatal;
5805 }
else if (isConcurrent)
5806 flavor = VerificationFlavor::SVA;
5808 flavor = VerificationFlavor::Immediate;
5811 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5815 opOperands, message)))
5818 if (failed(loweredFmtOperands(opOperands, messageOps)))
5821 if (flavor == VerificationFlavor::SVA) {
5826 for (
auto &loweredValue : messageOps)
5827 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5833 case VerificationFlavor::Immediate: {
5835 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5836 builder.getContext(), circt::sv::DeferAssert::Immediate);
5837 addToAlwaysBlock(clock, [&]() {
5838 addIfProceduralBlock(enable, [&]() {
5840 prefixedLabel, message, messageOps);
5845 case VerificationFlavor::IfElseFatal: {
5846 assert(isa<AssertOp>(op) &&
"only assert is expected");
5849 auto boolType = IntegerType::get(builder.getContext(), 1);
5850 predicate = comb::createOrFoldNot(builder, predicate,
true);
5851 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5853 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5854 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5855 addToAlwaysBlock(clock, [&]() {
5856 addIfProceduralBlock(predicate, [&]() {
5857 circuitState.usedStopCond =
true;
5858 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5860 circuitState.usedAssertVerboseCond =
true;
5861 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5863 addIfProceduralBlock(
5864 sv::MacroRefExprOp::create(builder, boolType,
5865 "ASSERT_VERBOSE_COND_"),
5867 sv::ErrorProceduralOp::create(builder, message, messageOps);
5869 addIfProceduralBlock(
5870 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5871 [&]() { sv::FatalProceduralOp::create(builder); });
5877 case VerificationFlavor::SVA: {
5882 comb::createOrFoldNot(builder, enable,
true);
5884 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5886 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5890 sv::EventControl event;
5891 switch (opEventControl) {
5892 case EventControl::AtPosEdge:
5893 event = circt::sv::EventControl::AtPosEdge;
5895 case EventControl::AtEdge:
5896 event = circt::sv::EventControl::AtEdge;
5898 case EventControl::AtNegEdge:
5899 event = circt::sv::EventControl::AtNegEdge;
5905 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5906 predicate, prefixedLabel, message, messageOps);
5909 case VerificationFlavor::None:
5911 "flavor `None` must be converted into one of concreate flavors");
5918 return emitGuards(op->getLoc(), guards,
emit);
5922LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5923 return lowerVerificationStatement(
5924 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5925 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5926 op.getIsConcurrent(), op.getEventControl());
5930LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5931 return lowerVerificationStatement(
5932 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5933 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5934 op.getIsConcurrent(), op.getEventControl());
5938LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5939 return lowerVerificationStatement(
5940 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5941 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5942 op.getIsConcurrent(), op.getEventControl());
5946LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5947 if (circuitState.lowerToCore) {
5948 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5949 if (guardsAttr && !guardsAttr.empty())
5950 return op.emitOpError(
5951 "lower-to-core does not support guarded verification statements");
5953 auto predicate = getLoweredValue(op.getPredicate());
5954 auto enable = getLoweredValue(op.getEnable());
5955 if (!predicate || !enable)
5958 auto label = op.getNameAttr();
5959 StringAttr assumeLabel;
5960 if (label && !label.empty())
5962 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5963 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5971 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5972 ArrayRef<Attribute> guards =
5973 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5975 auto label = op.getNameAttr();
5976 StringAttr assumeLabel;
5977 if (label && !label.empty())
5979 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5980 auto predicate = getLoweredValue(op.getPredicate());
5981 auto enable = getLoweredValue(op.getEnable());
5982 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5983 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5985 SmallVector<Value> messageOps;
5986 for (
auto operand : op.getSubstitutions()) {
5987 auto loweredValue = getLoweredValue(operand);
5988 if (!loweredValue) {
5992 loweredValue = getOrCreateIntConstant(1, 0);
5994 messageOps.push_back(loweredValue);
5996 return emitGuards(op.getLoc(), guards, [&]() {
5997 sv::AlwaysOp::create(
5998 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
6000 if (op.getMessageAttr().getValue().empty())
6001 buildImmediateVerifOp(
6002 builder,
"assume", predicate,
6003 circt::sv::DeferAssertAttr::get(
6004 builder.getContext(), circt::sv::DeferAssert::Immediate),
6007 buildImmediateVerifOp(
6008 builder,
"assume", predicate,
6009 circt::sv::DeferAssertAttr::get(
6010 builder.getContext(), circt::sv::DeferAssert::Immediate),
6011 assumeLabel, op.getMessageAttr(), messageOps);
6016LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
6018 if (op.getAttached().size() < 2)
6021 SmallVector<Value, 4> inoutValues;
6022 for (
auto v : op.getAttached()) {
6023 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
6024 if (!inoutValues.back()) {
6028 inoutValues.pop_back();
6032 if (!isa<hw::InOutType>(inoutValues.back().getType()))
6033 return op.emitError(
"operand isn't an inout type");
6036 if (inoutValues.size() < 2)
6044 if (circuitState.lowerToCore)
6045 return op.emitOpError(
6046 "lower-to-core does not support firrtl.attach that requires SV "
6052 bool isAttachInternalOnly =
6053 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
6055 if (isAttachInternalOnly) {
6056 auto v0 = inoutValues.front();
6057 for (
auto v : inoutValues) {
6060 v.replaceAllUsesWith(v0);
6067 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
6068 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
6073 SmallVector<Value, 4> values;
6074 for (
auto inoutValue : inoutValues)
6075 values.push_back(getReadValue(inoutValue));
6077 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
6078 for (
size_t i2 = 0; i2 != e; ++i2)
6086 sv::IfDefOp::create(
6087 builder,
"VERILATOR",
6089 sv::VerbatimOp::create(
6091 "`error \"Verilator does not support alias and thus "
6093 "arbitrarily connect bidirectional wires and ports\"");
6095 [&]() { sv::AliasOp::create(builder, inoutValues); });
6101LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
6102 sv::BindOp::create(builder, op.getInstanceAttr());
6106LogicalResult FIRRTLLowering::fixupLTLOps() {
6107 if (ltlOpFixupWorklist.empty())
6109 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6113 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6114 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6115 if (isa<
hw::WireOp>(user))
6116 ltlOpFixupWorklist.insert(user);
6119 while (!ltlOpFixupWorklist.empty()) {
6120 auto *op = ltlOpFixupWorklist.pop_back_val();
6123 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6124 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6125 SmallVector<Type, 2> types;
6126 auto result = opIntf.inferReturnTypes(
6127 op->getContext(), op->getLoc(), op->getOperands(),
6128 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6132 assert(types.size() == op->getNumResults());
6136 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6137 if (result.getType() == type)
6139 LLVM_DEBUG(llvm::dbgs()
6140 <<
" - Result #" << result.getResultNumber() <<
" from "
6141 << result.getType() <<
" to " << type <<
"\n");
6142 result.setType(type);
6143 for (
auto *user : result.getUsers())
6145 ltlOpFixupWorklist.insert(user);
6150 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6151 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6152 wireOp.replaceAllUsesWith(wireOp.getInput());
6153 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6154 if (wireOp.use_empty())
6161 SmallPtrSet<Operation *, 4> usersReported;
6162 for (
auto *user : op->getUsers()) {
6163 if (!usersReported.insert(user).second)
6165 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6166 user->getDialect()))
6168 if (isa<hw::WireOp>(user))
6170 auto d = op->emitError(
6171 "verification operation used in a non-verification context");
6172 d.attachNote(user->getLoc())
6173 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult emitFile(ArrayRef< Operation * > operations, StringRef filePath, raw_ostream &os)
Emits the given operation to a file represented by the passed ostream and file-path.
static void lowerModuleBody(FModuleOp mod, const DenseMap< StringAttr, PortConversion > &ports)
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static ltl::ClockEdge firrtlToLTLClockEdge(EventControl eventControl)
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static unsigned getBitWidthFromVectorSize(unsigned size)
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static verif::ClockEdge firrtlToVerifClockEdge(EventControl eventControl)
static void tryCopyName(Operation *dst, Operation *src)
static LogicalResult verifyOpLegality(Operation *op)
This verifies that the target operation has been lowered to a legal operation.
static Value castFromFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast from a FIRRTL type (potentially with a flip) to a standard type.
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
static Value tryEliminatingConnectsToValue(Value flipValue, Operation *insertPoint, CircuitLoweringState &loweringState)
Given a value of flip type, check to see if all of the uses of it are connects.
static LogicalResult resolveFormatString(Location loc, StringRef originalFormatString, ValueRange operands, StringAttr &result)
static Value getSingleNonInstanceOperand(AttachOp op)
static IntType getWidestIntType(Type t1, Type t2)
Given two FIRRTL integer types, return the widest one.
static FailureOr< VectorizeOp > lowerBody(VectorizeOp op)
Vectorizes the body of the given arc.vectorize operation if it is not already vectorized.
static Location getLoc(DefSlot slot)
static StringAttr getArgName(Operation *op, size_t idx)
static Block * getBodyBlock(FModuleLike mod)
std::shared_ptr< calyx::CalyxLoweringState > loweringState
Instantiate one of these and use it to build typed backedges.
void abandon()
Abandon the backedges, suppressing any diagnostics if they are still active upon destruction of the b...
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
mlir::LogicalResult clearOrEmitError()
Clear the backedges, erasing any remaining cursor ops.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
A namespace that is used to store existing names and generate new names in some scope within the IR.
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
bool removeAnnotation(Annotation anno)
Remove an annotation from this annotation set.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
FIRRTLVisitor allows you to visit all of the expr/stmt/decls with one class declaration.
ResultType visitInvalidOp(Operation *op, ExtraArgs... args)
visitInvalidOp is an override point for non-FIRRTL dialect operations.
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
visitUnhandledOp is an override point for FIRRTL dialect ops that the concrete visitor didn't bother ...
This graph tracks modules and where they are instantiated.
FModuleLike getTopLevelModule()
Get the module corresponding to the top-level module of a circuit.
This is the common base class between SIntType and UIntType.
This table tracks nlas and what modules participate in them.
The target of an inner symbol, the entity the symbol is a handle for.
This is an edge in the InstanceGraph.
create(elements, Type result_type=None)
create(str sym_name, Type type, str verilog_name=None)
create(data_type, name=None, sym_name=None)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
FIRRTLBaseType getBaseType(Type type)
If it is a base type, return it as is.
std::pair< hw::InnerSymAttr, StringAttr > getOrAddInnerSym(MLIRContext *context, hw::InnerSymAttr attr, uint64_t fieldID, llvm::function_ref< hw::InnerSymbolNamespace &()> getNamespace)
Ensure that the the InnerSymAttr has a symbol on the field specified.
bool hasDroppableName(Operation *op)
Return true if the name is droppable.
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
bool isExpression(Operation *op)
Return true if the specified operation is a firrtl expression.
std::optional< int64_t > getBitWidth(FIRRTLBaseType type, bool ignoreFlip=false)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerFIRRTLToHWPass(bool enableAnnotationWarning=false, firrtl::VerificationFlavor assertionFlavor=firrtl::VerificationFlavor::None, bool lowerToCore=false)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, List[str] cmdline_args=sys.argv)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
The namespace of a CircuitOp, generally inhabited by modules.
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
AnnotationSet annotations
bool isInput() const
Return true if this is a simple input-only port.
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const