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 should have been resolved.");
4753LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4755 if (op.getTestEnable())
4756 testEnable = getLoweredValue(op.getTestEnable());
4757 return setLoweringTo<seq::ClockGateOp>(
4758 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4759 testEnable, hw::InnerSymAttr{});
4762LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4763 auto operand = getLoweredValue(op.getInput());
4764 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4767LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4768 auto operand = getLoweredValue(op.getInput());
4769 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4772LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4773 return setLoweringToLTL<ltl::AndOp>(
4775 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4778LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4779 return setLoweringToLTL<ltl::OrOp>(
4781 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4784LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4785 return setLoweringToLTL<ltl::IntersectOp>(
4787 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4790LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4791 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4792 op.getDelayAttr(), op.getLengthAttr());
4795LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4796 return setLoweringToLTL<ltl::ConcatOp>(
4798 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4801LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4802 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4803 op.getBaseAttr(), op.getMoreAttr());
4806LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4807 return setLoweringToLTL<ltl::GoToRepeatOp>(
4808 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4811LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4812 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4813 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4816LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4817 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4820LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4821 return setLoweringToLTL<ltl::ImplicationOp>(
4823 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4826LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4827 return setLoweringToLTL<ltl::UntilOp>(
4829 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4832LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4833 return setLoweringToLTL<ltl::EventuallyOp>(op,
4834 getLoweredValue(op.getInput()));
4837LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4838 Value
clk = getLoweredNonClockValue(op.getClock());
4839 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4840 op.getDelayAttr(),
clk);
4844 switch (eventControl) {
4845 case EventControl::AtPosEdge:
4846 return ltl::ClockEdge::Pos;
4847 case EventControl::AtEdge:
4848 return ltl::ClockEdge::Both;
4849 case EventControl::AtNegEdge:
4850 return ltl::ClockEdge::Neg;
4852 llvm_unreachable(
"unknown event control");
4855LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4856 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4858 getLoweredNonClockValue(op.getClock()));
4861template <
typename TargetOp,
typename IntrinsicOp>
4862LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4863 auto property = getLoweredValue(op.getProperty());
4864 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4865 TargetOp::create(builder, property, enable, op.getLabelAttr());
4869LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4870 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4873LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4874 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4877LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4878 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4881LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4882 if (!isa<verif::ContractOp>(op->getParentOp()))
4883 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4884 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4887LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4888 if (!isa<verif::ContractOp>(op->getParentOp()))
4889 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4890 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4893LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4894 auto clock = getLoweredNonClockValue(op.getClock());
4895 auto reset = getLoweredValue(op.getReset());
4896 if (!clock || !reset)
4898 auto resetType = op.getReset().getType();
4899 auto uintResetType = dyn_cast<UIntType>(resetType);
4900 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4901 auto isAsync = isa<AsyncResetType>(resetType);
4902 if (!isAsync && !isSync) {
4903 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4904 "requires sync or async reset");
4905 d.attachNote() <<
"reset is of type " << resetType
4906 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4909 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4916LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4917 auto input = getLoweredValue(op.getInput());
4921 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4922 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4925LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4926 auto resultTy =
lowerType(op.getType());
4933 if (type_isa<AnalogType>(op.getType()))
4936 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4939 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4950 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4952 if (!type_isa<IntegerType>(resultTy))
4954 return setLowering(op, constant);
4958 op.emitOpError(
"unsupported type");
4962LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4963 auto input = getLoweredValue(op.getInput());
4966 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4967 if (op.getAmount() == 0)
4968 return setLowering(op, Value());
4969 Type resultType = builder.getIntegerType(op.getAmount());
4970 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4971 inWidth - op.getAmount());
4974LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4975 auto input = getLoweredValue(op.getInput());
4978 if (op.getAmount() == 0)
4980 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4985 if (op.getAmount() == 0)
4986 return setLowering(op, input);
4988 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4989 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4992LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4993 auto input = getLoweredValue(op.getInput());
4998 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4999 auto shiftAmount = op.getAmount();
5000 if (shiftAmount >= inWidth) {
5002 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
5003 return setLowering(op, {});
5006 shiftAmount = inWidth - 1;
5009 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
5010 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
5013LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
5014 auto input = getLoweredValue(op.getInput());
5018 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
5019 if (inWidth == op.getAmount())
5020 return setLowering(op, Value());
5021 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
5022 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
5025LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
5026 auto cond = getLoweredValue(op.getSel());
5027 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5028 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5029 if (!cond || !ifTrue || !ifFalse)
5032 if (isa<ClockType>(op.getType()))
5033 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
5034 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
5038LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
5039 auto cond = getLoweredValue(op.getSel());
5040 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
5041 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
5042 if (!cond || !ifTrue || !ifFalse)
5045 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
5047 return setLowering(op, createValueWithMuxAnnotation(val,
true));
5050LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
5051 auto sel = getLoweredValue(op.getSel());
5052 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
5053 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
5054 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
5055 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
5056 if (!sel || !v3 || !v2 || !v1 || !v0)
5058 Value array[] = {v3, v2, v1, v0};
5061 return setLowering(op, createValueWithMuxAnnotation(val,
false));
5080Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
5081 assert(op->getNumResults() == 1 &&
"only expect a single result");
5082 auto val = op->getResult(0);
5086 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
5093 OpBuilder::InsertionGuard guard(builder);
5094 builder.setInsertionPoint(op);
5095 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
5096 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
5098 op->getContext(),
nullptr, 0,
5101 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
5102 op->setOperand(idx, wire);
5107 sv::setSVAttributes(assignOp,
5108 sv::SVAttributeAttr::get(builder.getContext(),
5109 "synopsys infer_mux_override",
5114Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
5116 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
5121 if (!llvm::isPowerOf2_64(size)) {
5122 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
5124 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
5126 Value temp2[] = {ext.getResult(), array};
5132 return inBoundsRead;
5135LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
5137 auto index = getLoweredAndExtOrTruncValue(
5139 UIntType::get(op.getContext(),
5144 SmallVector<Value> loweredInputs;
5145 loweredInputs.reserve(op.getInputs().size());
5146 for (
auto input : op.getInputs()) {
5147 auto lowered = getLoweredAndExtendedValue(input, op.getType());
5150 loweredInputs.push_back(lowered);
5154 return setLowering(op, createArrayIndexing(array, index));
5157LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
5158 auto resultTy =
lowerType(op.getType());
5162 SmallVector<Value, 4> operands;
5163 operands.reserve(op.getSubstitutions().size());
5164 for (
auto operand : op.getSubstitutions()) {
5165 auto lowered = getLoweredValue(operand);
5168 operands.push_back(lowered);
5171 ArrayAttr symbols = op.getSymbolsAttr();
5173 symbols = ArrayAttr::get(op.getContext(), {});
5175 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
5179LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
5183 Type baseType = op.getType().getType();
5186 if (isa<ClockType>(baseType))
5187 xmrType = builder.getIntegerType(1);
5191 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
5192 op.getRef(), op.getVerbatimSuffixAttr());
5195LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
5199 if (isa<ClockType>(op.getType()))
5200 xmrType = builder.getIntegerType(1);
5204 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
5205 op.getRef(), op.getVerbatimSuffixAttr());
5206 auto readXmr = getReadValue(xmr);
5207 if (!isa<ClockType>(op.getType()))
5208 return setLowering(op, readXmr);
5209 return setLoweringTo<seq::ToClockOp>(op, readXmr);
5214LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
5215LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
5223LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5235FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5236 auto srcType = srcVal.getType();
5237 auto dstType = destVal.getType();
5238 if (srcType != dstType &&
5239 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5242 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5243 .Case<hw::WireOp>([&](
auto op) {
5244 maybeUnused(op.getInput());
5245 op.getInputMutable().assign(srcVal);
5248 .Case<seq::FirRegOp>([&](
auto op) {
5249 maybeUnused(op.getNext());
5250 op.getNextMutable().assign(srcVal);
5253 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5256 op.emitOpError(
"used as connect destination");
5259 .Default([](
auto) {
return false; });
5262LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5263 auto dest = op.getDest();
5265 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5266 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5268 return handleZeroBit(op.getSrc(), []() { return success(); });
5270 auto destVal = getPossiblyInoutLoweredValue(dest);
5274 auto result = lowerConnect(destVal, srcVal);
5282 if (updateIfBackedge(destVal, srcVal))
5285 if (!isa<hw::InOutType>(destVal.getType()))
5286 return op.emitError(
"destination isn't an inout type");
5292LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5293 auto dest = op.getDest();
5294 auto srcVal = getLoweredValue(op.getSrc());
5296 return handleZeroBit(op.getSrc(), []() { return success(); });
5298 auto destVal = getPossiblyInoutLoweredValue(dest);
5302 auto result = lowerConnect(destVal, srcVal);
5310 if (updateIfBackedge(destVal, srcVal))
5313 if (!isa<hw::InOutType>(destVal.getType()))
5314 return op.emitError(
"destination isn't an inout type");
5320LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5321 if (circuitState.lowerToCore)
5322 return op.emitOpError(
"lower-to-core does not support firrtl.force");
5324 auto srcVal = getLoweredValue(op.getSrc());
5328 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5332 if (!isa<hw::InOutType>(destVal.getType()))
5333 return op.emitError(
"destination isn't an inout type");
5336 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5337 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5338 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5343LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5344 if (circuitState.lowerToCore)
5345 return op.emitOpError(
"lower-to-core does not support firrtl.ref.force");
5347 auto src = getLoweredNonClockValue(op.getSrc());
5348 auto clock = getLoweredNonClockValue(op.getClock());
5349 auto pred = getLoweredValue(op.getPredicate());
5350 if (!src || !clock || !pred)
5353 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5358 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5359 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5360 addToAlwaysBlock(clock, [&]() {
5361 addIfProceduralBlock(
5362 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5367LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5368 if (circuitState.lowerToCore)
5369 return op.emitOpError(
5370 "lower-to-core does not support firrtl.ref.force_initial");
5372 auto src = getLoweredNonClockValue(op.getSrc());
5373 auto pred = getLoweredValue(op.getPredicate());
5377 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5382 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5383 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5384 addToInitialBlock([&]() {
5385 addIfProceduralBlock(
5386 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5391LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5392 if (circuitState.lowerToCore)
5393 return op.emitOpError(
"lower-to-core does not support firrtl.ref.release");
5395 auto clock = getLoweredNonClockValue(op.getClock());
5396 auto pred = getLoweredValue(op.getPredicate());
5397 if (!clock || !pred)
5400 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5405 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5406 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5407 addToAlwaysBlock(clock, [&]() {
5408 addIfProceduralBlock(pred,
5409 [&]() { sv::ReleaseOp::create(builder, destVal); });
5414LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5415 if (circuitState.lowerToCore)
5416 return op.emitOpError(
5417 "lower-to-core does not support firrtl.ref.release_initial");
5419 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5420 auto pred = getLoweredValue(op.getPredicate());
5421 if (!destVal || !pred)
5425 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5426 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5427 addToInitialBlock([&]() {
5428 addIfProceduralBlock(pred,
5429 [&]() { sv::ReleaseOp::create(builder, destVal); });
5437 StringRef originalFormatString,
5438 ValueRange operands,
5439 StringAttr &result) {
5442 SmallString<32> formatString;
5443 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5444 char c = originalFormatString[i];
5448 formatString.push_back(c);
5451 SmallString<6> width;
5452 c = originalFormatString[++i];
5455 c = originalFormatString[++i];
5466 formatString.append(width);
5472 formatString.push_back(c);
5479 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5480 formatString.push_back(c);
5484 auto substitution = operands[subIdx++];
5485 assert(type_isa<FStringType>(substitution.getType()) &&
5486 "the operand for a '{{}}' substitution must be an 'fstring' type");
5488 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5489 .template Case<TimeOp>([&](
auto) {
5490 formatString.append(
"%0t");
5493 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5494 formatString.append(
"%m");
5497 .Default([&](
auto) {
5498 emitError(loc,
"has a substitution with an unimplemented "
5500 .attachNote(substitution.getLoc())
5501 <<
"op with an unimplemented lowering is here";
5511 formatString.push_back(c);
5515 result = StringAttr::get(loc->getContext(), formatString);
5522LogicalResult FIRRTLLowering::visitPrintfLike(
5523 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5524 auto clock = getLoweredNonClockValue(op.getClock());
5525 auto cond = getLoweredValue(op.getCond());
5526 if (!clock || !cond)
5529 StringAttr formatString;
5531 op.getSubstitutions(), formatString)))
5534 auto fn = [&](Value fd) {
5535 SmallVector<Value> operands;
5536 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5538 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5542 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5546LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
5547 if (!circuitState.lowerToCore)
5548 return visitPrintfLike(op, {},
true);
5550 auto clock = getLoweredValue(op.getClock());
5551 auto cond = getLoweredValue(op.getCond());
5552 if (!clock || !cond)
5556 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5557 if (failed(formatString))
5560 auto stderrOp = sim::StderrStreamOp::create(builder);
5561 sim::TriggeredOp::create(builder, clock, cond, [&] {
5562 sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
5567LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5568 if (circuitState.lowerToCore) {
5569 auto clock = getLoweredValue(op.getClock());
5570 auto cond = getLoweredValue(op.getCond());
5571 if (!clock || !cond)
5574 auto fileFormatString = lowerSimFormatString(
5575 op.getOutputFileAttr(), op.getOutputFileSubstitutions());
5576 if (failed(fileFormatString))
5580 lowerSimFormatString(op.getFormatString(), op.getSubstitutions());
5581 if (failed(formatString))
5584 sim::TriggeredOp::create(builder, clock, cond, [&] {
5585 auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
5586 sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
5591 StringAttr outputFileAttr;
5593 op.getOutputFileSubstitutions(),
5597 FileDescriptorInfo outputFile(outputFileAttr,
5598 op.getOutputFileSubstitutions());
5599 return visitPrintfLike(op, outputFile,
false);
5603LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5604 if (circuitState.lowerToCore)
5605 return op.emitOpError(
"lower-to-core does not support firrtl.fflush yet");
5607 auto clock = getLoweredNonClockValue(op.getClock());
5608 auto cond = getLoweredValue(op.getCond());
5609 if (!clock || !cond)
5612 auto fn = [&](Value fd) {
5613 sv::FFlushOp::create(builder, op.getLoc(), fd);
5617 if (!op.getOutputFileAttr())
5618 return lowerStatementWithFd({}, clock, cond, fn,
false);
5622 StringAttr outputFileAttr;
5624 op.getOutputFileSubstitutions(),
5628 return lowerStatementWithFd(
5629 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5630 clock, cond, fn,
false);
5635LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5636 auto clock = getLoweredValue(op.getClock());
5637 auto cond = getLoweredValue(op.getCond());
5638 if (!clock || !cond)
5641 circuitState.usedStopCond =
true;
5642 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5645 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5646 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5648 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5649 op.getExitCode() == 0,
5658template <
typename... Args>
5660 StringRef opName, Args &&...args) {
5661 if (opName ==
"assert")
5662 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5663 if (opName ==
"assume")
5664 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5665 if (opName ==
"cover")
5666 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5667 llvm_unreachable(
"unknown verification op");
5673template <
typename... Args>
5675 StringRef opName, Args &&...args) {
5676 if (opName ==
"assert")
5677 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5678 if (opName ==
"assume")
5679 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5680 if (opName ==
"cover")
5681 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5682 llvm_unreachable(
"unknown verification op");
5686 switch (eventControl) {
5687 case EventControl::AtPosEdge:
5688 return verif::ClockEdge::Pos;
5689 case EventControl::AtEdge:
5690 return verif::ClockEdge::Both;
5691 case EventControl::AtNegEdge:
5692 return verif::ClockEdge::Neg;
5694 llvm_unreachable(
"unknown FIRRTL event control");
5697LogicalResult FIRRTLLowering::lowerVerificationStatementToCore(
5698 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5699 Value opEnable, StringAttr opNameAttr, EventControl opEventControl) {
5700 auto guardsAttr = op->getAttrOfType<ArrayAttr>(
"guards");
5701 if (guardsAttr && !guardsAttr.empty())
5702 return op->emitOpError(
5703 "lower-to-core does not support guarded verification statements");
5705 auto clock = getLoweredNonClockValue(opClock);
5706 auto enable = getLoweredValue(opEnable);
5707 auto predicate = getLoweredValue(opPredicate);
5708 if (!clock || !enable || !predicate)
5712 if (opNameAttr && !opNameAttr.getValue().empty())
5713 label = StringAttr::get(builder.getContext(),
5714 labelPrefix + opNameAttr.getValue());
5717 auto opName = op->getName().stripDialect();
5718 if (opName ==
"assert") {
5719 verif::ClockedAssertOp::create(builder, predicate, edge, clock, enable,
5723 if (opName ==
"assume") {
5724 verif::ClockedAssumeOp::create(builder, predicate, edge, clock, enable,
5728 if (opName ==
"cover") {
5729 verif::ClockedCoverOp::create(builder, predicate, edge, clock, enable,
5733 llvm_unreachable(
"unknown verification op");
5754LogicalResult FIRRTLLowering::lowerVerificationStatement(
5755 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5756 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5757 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5758 if (circuitState.lowerToCore)
5759 return lowerVerificationStatementToCore(op, labelPrefix, opClock,
5760 opPredicate, opEnable, opNameAttr,
5763 StringRef opName = op->getName().stripDialect();
5766 ArrayRef<Attribute> guards{};
5767 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5768 guards = guardsAttr.getValue();
5770 auto isCover = isa<CoverOp>(op);
5771 auto clock = getLoweredNonClockValue(opClock);
5772 auto enable = getLoweredValue(opEnable);
5773 auto predicate = getLoweredValue(opPredicate);
5774 if (!clock || !enable || !predicate)
5778 if (opNameAttr && !opNameAttr.getValue().empty())
5780 StringAttr prefixedLabel;
5783 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5786 SmallVector<Value> messageOps;
5790 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5791 flavor = VerificationFlavor::None;
5793 if (flavor == VerificationFlavor::None) {
5797 auto format = op->getAttrOfType<StringAttr>(
"format");
5799 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5800 if (!isa<AssertOp>(op))
5801 return op->emitError()
5802 <<
"ifElseFatal format cannot be used for non-assertions";
5803 flavor = VerificationFlavor::IfElseFatal;
5804 }
else if (isConcurrent)
5805 flavor = VerificationFlavor::SVA;
5807 flavor = VerificationFlavor::Immediate;
5810 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5814 opOperands, message)))
5817 if (failed(loweredFmtOperands(opOperands, messageOps)))
5820 if (flavor == VerificationFlavor::SVA) {
5825 for (
auto &loweredValue : messageOps)
5826 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5832 case VerificationFlavor::Immediate: {
5834 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5835 builder.getContext(), circt::sv::DeferAssert::Immediate);
5836 addToAlwaysBlock(clock, [&]() {
5837 addIfProceduralBlock(enable, [&]() {
5839 prefixedLabel, message, messageOps);
5844 case VerificationFlavor::IfElseFatal: {
5845 assert(isa<AssertOp>(op) &&
"only assert is expected");
5848 auto boolType = IntegerType::get(builder.getContext(), 1);
5849 predicate = comb::createOrFoldNot(builder, predicate,
true);
5850 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5852 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5853 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5854 addToAlwaysBlock(clock, [&]() {
5855 addIfProceduralBlock(predicate, [&]() {
5856 circuitState.usedStopCond =
true;
5857 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5859 circuitState.usedAssertVerboseCond =
true;
5860 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5862 addIfProceduralBlock(
5863 sv::MacroRefExprOp::create(builder, boolType,
5864 "ASSERT_VERBOSE_COND_"),
5866 sv::ErrorProceduralOp::create(builder, message, messageOps);
5868 addIfProceduralBlock(
5869 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5870 [&]() { sv::FatalProceduralOp::create(builder); });
5876 case VerificationFlavor::SVA: {
5881 comb::createOrFoldNot(builder, enable,
true);
5883 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5885 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5889 sv::EventControl event;
5890 switch (opEventControl) {
5891 case EventControl::AtPosEdge:
5892 event = circt::sv::EventControl::AtPosEdge;
5894 case EventControl::AtEdge:
5895 event = circt::sv::EventControl::AtEdge;
5897 case EventControl::AtNegEdge:
5898 event = circt::sv::EventControl::AtNegEdge;
5904 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5905 predicate, prefixedLabel, message, messageOps);
5908 case VerificationFlavor::None:
5910 "flavor `None` must be converted into one of concreate flavors");
5917 return emitGuards(op->getLoc(), guards,
emit);
5921LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5922 return lowerVerificationStatement(
5923 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5924 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5925 op.getIsConcurrent(), op.getEventControl());
5929LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5930 return lowerVerificationStatement(
5931 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5932 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5933 op.getIsConcurrent(), op.getEventControl());
5937LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5938 return lowerVerificationStatement(
5939 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5940 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5941 op.getIsConcurrent(), op.getEventControl());
5945LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5946 if (circuitState.lowerToCore) {
5947 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5948 if (guardsAttr && !guardsAttr.empty())
5949 return op.emitOpError(
5950 "lower-to-core does not support guarded verification statements");
5952 auto predicate = getLoweredValue(op.getPredicate());
5953 auto enable = getLoweredValue(op.getEnable());
5954 if (!predicate || !enable)
5957 auto label = op.getNameAttr();
5958 StringAttr assumeLabel;
5959 if (label && !label.empty())
5961 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5962 verif::AssumeOp::create(builder, predicate, enable, assumeLabel);
5970 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5971 ArrayRef<Attribute> guards =
5972 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5974 auto label = op.getNameAttr();
5975 StringAttr assumeLabel;
5976 if (label && !label.empty())
5978 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5979 auto predicate = getLoweredValue(op.getPredicate());
5980 auto enable = getLoweredValue(op.getEnable());
5981 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5982 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5984 SmallVector<Value> messageOps;
5985 for (
auto operand : op.getSubstitutions()) {
5986 auto loweredValue = getLoweredValue(operand);
5987 if (!loweredValue) {
5991 loweredValue = getOrCreateIntConstant(1, 0);
5993 messageOps.push_back(loweredValue);
5995 return emitGuards(op.getLoc(), guards, [&]() {
5996 sv::AlwaysOp::create(
5997 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5999 if (op.getMessageAttr().getValue().empty())
6000 buildImmediateVerifOp(
6001 builder,
"assume", predicate,
6002 circt::sv::DeferAssertAttr::get(
6003 builder.getContext(), circt::sv::DeferAssert::Immediate),
6006 buildImmediateVerifOp(
6007 builder,
"assume", predicate,
6008 circt::sv::DeferAssertAttr::get(
6009 builder.getContext(), circt::sv::DeferAssert::Immediate),
6010 assumeLabel, op.getMessageAttr(), messageOps);
6015LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
6017 if (op.getAttached().size() < 2)
6020 SmallVector<Value, 4> inoutValues;
6021 for (
auto v : op.getAttached()) {
6022 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
6023 if (!inoutValues.back()) {
6027 inoutValues.pop_back();
6031 if (!isa<hw::InOutType>(inoutValues.back().getType()))
6032 return op.emitError(
"operand isn't an inout type");
6035 if (inoutValues.size() < 2)
6043 if (circuitState.lowerToCore)
6044 return op.emitOpError(
6045 "lower-to-core does not support firrtl.attach that requires SV "
6051 bool isAttachInternalOnly =
6052 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
6054 if (isAttachInternalOnly) {
6055 auto v0 = inoutValues.front();
6056 for (
auto v : inoutValues) {
6059 v.replaceAllUsesWith(v0);
6066 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
6067 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
6072 SmallVector<Value, 4> values;
6073 for (
auto inoutValue : inoutValues)
6074 values.push_back(getReadValue(inoutValue));
6076 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
6077 for (
size_t i2 = 0; i2 != e; ++i2)
6085 sv::IfDefOp::create(
6086 builder,
"VERILATOR",
6088 sv::VerbatimOp::create(
6090 "`error \"Verilator does not support alias and thus "
6092 "arbitrarily connect bidirectional wires and ports\"");
6094 [&]() { sv::AliasOp::create(builder, inoutValues); });
6100LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
6101 sv::BindOp::create(builder, op.getInstanceAttr());
6105LogicalResult FIRRTLLowering::fixupLTLOps() {
6106 if (ltlOpFixupWorklist.empty())
6108 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
6112 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
6113 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
6114 if (isa<
hw::WireOp>(user))
6115 ltlOpFixupWorklist.insert(user);
6118 while (!ltlOpFixupWorklist.empty()) {
6119 auto *op = ltlOpFixupWorklist.pop_back_val();
6122 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
6123 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
6124 SmallVector<Type, 2> types;
6125 auto result = opIntf.inferReturnTypes(
6126 op->getContext(), op->getLoc(), op->getOperands(),
6127 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
6131 assert(types.size() == op->getNumResults());
6135 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
6136 if (result.getType() == type)
6138 LLVM_DEBUG(llvm::dbgs()
6139 <<
" - Result #" << result.getResultNumber() <<
" from "
6140 << result.getType() <<
" to " << type <<
"\n");
6141 result.setType(type);
6142 for (
auto *user : result.getUsers())
6144 ltlOpFixupWorklist.insert(user);
6149 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
6150 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
6151 wireOp.replaceAllUsesWith(wireOp.getInput());
6152 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
6153 if (wireOp.use_empty())
6160 SmallPtrSet<Operation *, 4> usersReported;
6161 for (
auto *user : op->getUsers()) {
6162 if (!usersReported.insert(user).second)
6164 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
6165 user->getDialect()))
6167 if (isa<hw::WireOp>(user))
6169 auto d = op->emitError(
6170 "verification operation used in a non-verification context");
6171 d.attachNote(user->getLoc())
6172 <<
"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