36#include "mlir/IR/BuiltinOps.h"
37#include "mlir/IR/BuiltinTypes.h"
38#include "mlir/IR/ImplicitLocOpBuilder.h"
39#include "mlir/IR/Threading.h"
40#include "mlir/Pass/Pass.h"
41#include "llvm/ADT/DenseMap.h"
42#include "llvm/Support/Debug.h"
43#include "llvm/Support/Mutex.h"
44#include "llvm/Support/Path.h"
46#define DEBUG_TYPE "lower-to-hw"
49#define GEN_PASS_DEF_LOWERFIRRTLTOHW
50#include "circt/Conversion/Passes.h.inc"
54using namespace firrtl;
55using circt::comb::ICmpPredicate;
64 auto ftype = dyn_cast<FIRRTLBaseType>(type);
65 return ftype && ftype.getPassiveType().getBitWidthOrSentinel() == 0;
72 for (
auto operand : op.getAttached()) {
74 operand.getDefiningOp<InstanceOp>())
78 if (!operand.hasOneUse() || singleSource)
80 singleSource = operand;
89 auto checkTypes = [](Operation *op) -> WalkResult {
91 if (isa_and_nonnull<FIRRTLDialect>(op->getDialect()))
92 return op->emitError(
"Found unhandled FIRRTL operation '")
93 << op->getName() <<
"'";
96 auto checkTypeRange = [&](TypeRange types) -> LogicalResult {
97 if (llvm::any_of(types, [](Type type) {
98 return isa<FIRRTLDialect>(type.getDialect());
100 return op->emitOpError(
"found unhandled FIRRTL type");
105 if (failed(checkTypeRange(op->getOperandTypes())) ||
106 failed(checkTypeRange(op->getResultTypes())))
107 return WalkResult::interrupt();
110 for (
auto ®ion : op->getRegions())
111 for (
auto &block : region)
112 if (failed(checkTypeRange(block.getArgumentTypes())))
113 return WalkResult::interrupt();
116 return WalkResult::advance();
119 if (checkTypes(op).wasInterrupted() || op->walk(checkTypes).wasInterrupted())
126 auto t1c = type_cast<IntType>(t1), t2c = type_cast<IntType>(t2);
127 return t2c.getWidth() > t1c.getWidth() ? t2c : t1c;
133 ImplicitLocOpBuilder &builder) {
135 if (BundleType bundle = dyn_cast<BundleType>(type))
136 val = builder.createOrFold<HWStructCastOp>(bundle.getPassiveType(), val);
138 if (type != val.getType())
139 val = mlir::UnrealizedConversionCastOp::create(builder, type, val)
147 ImplicitLocOpBuilder &builder) {
149 if (hw::StructType structTy = dyn_cast<hw::StructType>(type)) {
151 val = mlir::UnrealizedConversionCastOp::create(
153 type_cast<FIRRTLBaseType>(val.getType()).getPassiveType(), val)
155 val = builder.createOrFold<HWStructCastOp>(type, val);
160 mlir::UnrealizedConversionCastOp::create(builder, type, val).getResult(0);
166 return size == 1 ? 1 : llvm::Log2_64_Ceil(size);
172 if (
auto attr = src->getAttrOfType<StringAttr>(
"name"))
173 if (!dst->hasAttr(
"sv.namehint") && !dst->hasAttr(
"name"))
174 dst->setAttr(
"sv.namehint", attr);
180class FileDescriptorInfo {
182 FileDescriptorInfo(StringAttr outputFileName, mlir::ValueRange substitutions)
183 : outputFileFormat(outputFileName), substitutions(substitutions) {
185 substitutions.empty() &&
186 "substitutions must be empty when output file name is empty");
189 FileDescriptorInfo() =
default;
192 bool isSubstitutionRequired()
const {
return !substitutions.empty(); }
195 bool isDefaultFd()
const {
return !outputFileFormat; }
197 StringAttr getOutputFileFormat()
const {
return outputFileFormat; }
198 mlir::ValueRange getSubstitutions()
const {
return substitutions; }
202 StringAttr outputFileFormat = {};
205 mlir::ValueRange substitutions;
215struct FIRRTLModuleLowering;
218struct CircuitLoweringState {
220 std::atomic<bool> usedPrintf{
false};
221 std::atomic<bool> usedAssertVerboseCond{
false};
222 std::atomic<bool> usedStopCond{
false};
223 std::atomic<bool> usedFileDescriptorLib{
false};
225 CircuitLoweringState(CircuitOp circuitOp,
bool enableAnnotationWarning,
229 : circuitOp(circuitOp), instanceGraph(instanceGraph),
230 enableAnnotationWarning(enableAnnotationWarning),
231 verificationFlavor(verificationFlavor), nlaTable(nlaTable),
232 macroTable(macroTable) {
233 auto *
context = circuitOp.getContext();
237 AnnotationSet(circuitOp).getAnnotation(testBenchDirAnnoClass)) {
238 auto dirName = tbAnno.getMember<StringAttr>(
"dirname");
239 testBenchDirectory = hw::OutputFileAttr::getAsDirectory(
240 context, dirName.getValue(),
false,
true);
244 if (
auto module = dyn_cast<FModuleLike>(op)) {
256 testHarness =
nullptr;
257 }
else if (dut == testHarness) {
258 testHarness =
nullptr;
263 auto inDUT = [&](igraph::ModuleOpInterface child) {
265 if (
auto inst = instRec->getInstance<InstanceOp>())
266 return inst.getLowerToBind() || inst.getDoNotPrint();
269 if (
auto parent = dyn_cast<igraph::ModuleOpInterface>(*dut))
270 return getInstanceGraph().isAncestor(child, parent, isPhony);
273 circuitOp->walk([&](FModuleLike moduleOp) {
275 dutModules.insert(moduleOp);
279 Operation *getNewModule(Operation *oldModule) {
280 auto it = oldToNewModuleMap.find(oldModule);
281 return it != oldToNewModuleMap.end() ? it->second :
nullptr;
284 Operation *getOldModule(Operation *newModule) {
285 auto it = newToOldModuleMap.find(newModule);
286 return it != newToOldModuleMap.end() ? it->second :
nullptr;
289 void recordModuleMapping(Operation *oldFMod, Operation *newHWMod) {
290 oldToNewModuleMap[oldFMod] = newHWMod;
291 newToOldModuleMap[newHWMod] = oldFMod;
296 void processRemainingAnnotations(Operation *op,
const AnnotationSet &annoSet);
302 void addBind(sv::BindOp op) {
303 std::lock_guard<std::mutex> lock(bindsMutex);
309 hw::TypeAliasType getTypeAlias(Type rawType, BaseTypeAliasType firAliasType,
312 auto hwAlias = typeAliases.getTypedecl(firAliasType);
315 assert(!typeAliases.isFrozen() &&
316 "type aliases cannot be generated after its frozen");
317 return typeAliases.addTypedecl(rawType, firAliasType, typeLoc);
320 FModuleLike getDut() {
return dut; }
321 FModuleLike getTestHarness() {
return testHarness; }
327 bool isInDUT(igraph::ModuleOpInterface child) {
328 if (
auto hwModule = dyn_cast<hw::HWModuleOp>(child.getOperation()))
329 child = cast<igraph::ModuleOpInterface>(getOldModule(hwModule));
330 return dutModules.contains(child);
333 hw::OutputFileAttr getTestBenchDirectory() {
return testBenchDirectory; }
338 bool isInTestHarness(igraph::ModuleOpInterface mod) {
return !isInDUT(mod); }
345 Type
lowerType(Type type, Location loc) {
346 return ::lowerType(type, loc,
347 [&](Type rawType, BaseTypeAliasType firrtlType,
348 Location typeLoc) -> hw::TypeAliasType {
349 return getTypeAlias(rawType, firrtlType, typeLoc);
355 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
356 auto it = verbatimSourcesByFileName.find(fileName);
357 return it != verbatimSourcesByFileName.end() ? it->second :
nullptr;
362 void registerVerbatimSource(StringRef fileName,
364 llvm::sys::SmartScopedLock<true> lock(verbatimSourcesMutex);
365 verbatimSourcesByFileName[fileName] = verbatimOp;
369 emit::FileOp getEmitFileForFile(StringRef fileName) {
370 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
371 auto it = emitFilesByFileName.find(fileName);
372 return it != emitFilesByFileName.end() ? it->second :
nullptr;
377 void registerEmitFile(StringRef fileName, emit::FileOp fileOp) {
378 llvm::sys::SmartScopedLock<true> lock(emitFilesMutex);
379 emitFilesByFileName[fileName] = fileOp;
383 friend struct FIRRTLModuleLowering;
384 friend struct FIRRTLLowering;
385 CircuitLoweringState(
const CircuitLoweringState &) =
delete;
386 void operator=(
const CircuitLoweringState &) =
delete;
389 DenseMap<Operation *, Operation *> oldToNewModuleMap;
392 DenseMap<Operation *, Operation *> newToOldModuleMap;
403 DenseSet<igraph::ModuleOpInterface> dutModules;
407 StringSet<> pendingAnnotations;
408 const bool enableAnnotationWarning;
409 std::mutex annotationPrintingMtx;
415 SmallVector<sv::BindOp> binds;
418 std::mutex bindsMutex;
426 FModuleLike testHarness;
429 hw::OutputFileAttr testBenchDirectory;
433 DenseMap<std::pair<Attribute, Attribute>, Attribute> instanceForceNames;
436 SetVector<StringAttr> macroDeclNames;
437 std::mutex macroDeclMutex;
439 void addMacroDecl(StringAttr name) {
440 std::unique_lock<std::mutex> lock(macroDeclMutex);
441 macroDeclNames.insert(name);
446 DenseMap<hw::HWModuleOp, SetVector<Attribute>> fragments;
447 llvm::sys::SmartMutex<true> fragmentsMutex;
450 llvm::sys::SmartScopedLock<true> lock(fragmentsMutex);
451 fragments[module].insert(
452 FlatSymbolRefAttr::get(circuitOp.getContext(), fragment));
467 struct RecordTypeAlias {
469 RecordTypeAlias(CircuitOp c) : circuitOp(c) {}
471 hw::TypeAliasType getTypedecl(BaseTypeAliasType firAlias)
const {
472 auto iter = firrtlTypeToAliasTypeMap.find(firAlias);
473 if (iter != firrtlTypeToAliasTypeMap.end())
478 bool isFrozen() {
return frozen; }
480 void freeze() { frozen =
true; }
482 hw::TypeAliasType addTypedecl(Type rawType, BaseTypeAliasType firAlias,
484 assert(!frozen &&
"Record already frozen, cannot be updated");
487 auto b = ImplicitLocOpBuilder::atBlockBegin(
489 &circuitOp->getParentRegion()->getBlocks().back());
491 b,
b.getStringAttr(circuitOp.getName() +
"__TYPESCOPE_"));
492 typeScope.getBodyRegion().push_back(
new Block());
494 auto typeName = firAlias.getName();
499 StringAttr::get(typeName.getContext(),
500 typeDeclNamespace.newName(typeName.getValue()));
502 auto typeScopeBuilder =
503 ImplicitLocOpBuilder::atBlockEnd(typeLoc, typeScope.getBodyBlock());
505 typeName, rawType,
nullptr);
506 auto hwAlias = hw::TypeAliasType::get(
507 SymbolRefAttr::get(typeScope.getSymNameAttr(),
508 {FlatSymbolRefAttr::get(typeDecl)}),
510 auto insert = firrtlTypeToAliasTypeMap.try_emplace(firAlias, hwAlias);
511 assert(insert.second &&
"Entry already exists, insert failed");
512 return insert.first->second;
521 DenseMap<Type, hw::TypeAliasType> firrtlTypeToAliasTypeMap;
529 RecordTypeAlias typeAliases = RecordTypeAlias(circuitOp);
532 llvm::StringMap<sv::SVVerbatimSourceOp> verbatimSourcesByFileName;
533 llvm::sys::SmartMutex<true> verbatimSourcesMutex;
536 llvm::StringMap<emit::FileOp> emitFilesByFileName;
537 llvm::sys::SmartMutex<true> emitFilesMutex;
543void CircuitLoweringState::processRemainingAnnotations(
545 if (!enableAnnotationWarning || annoSet.
empty())
547 std::lock_guard<std::mutex> lock(annotationPrintingMtx);
549 for (
auto a : annoSet) {
550 auto inserted = pendingAnnotations.insert(
a.getClass());
551 if (!inserted.second)
572 markDUTAnnoClass, metadataDirAnnoClass, testBenchDirAnnoClass,
577 extractGrandCentralAnnoClass,
580 extractAssertionsAnnoClass, extractAssumptionsAnnoClass,
581 extractCoverageAnnoClass,
585 moduleHierarchyAnnoClass, testHarnessHierarchyAnnoClass,
586 blackBoxTargetDirAnnoClass))
589 mlir::emitWarning(op->getLoc(),
"unprocessed annotation:'" +
a.getClass() +
590 "' still remaining after LowerToHW");
596struct FIRRTLModuleLowering
597 :
public circt::impl::LowerFIRRTLToHWBase<FIRRTLModuleLowering> {
599 void runOnOperation()
override;
600 void setEnableAnnotationWarning() { enableAnnotationWarning =
true; }
602 using LowerFIRRTLToHWBase<FIRRTLModuleLowering>::verificationFlavor;
605 void lowerFileHeader(CircuitOp op, CircuitLoweringState &
loweringState);
607 LogicalResult lowerPorts(ArrayRef<PortInfo> firrtlPorts,
608 SmallVectorImpl<hw::PortInfo> &ports,
609 Operation *moduleOp, StringRef moduleName,
611 bool handleForceNameAnnos(FModuleLike oldModule,
AnnotationSet &annos,
613 hw::HWModuleOp lowerModule(FModuleOp oldModule, Block *topLevelModule,
616 getVerbatimSourceForExtModule(FExtModuleOp oldModule, Block *topLevelModule,
618 hw::HWModuleLike lowerExtModule(FExtModuleOp oldModule, Block *topLevelModule,
621 lowerVerbatimExtModule(FExtModuleOp oldModule, Block *topLevelModule,
624 Block *topLevelModule,
628 lowerModulePortsAndMoveBody(FModuleOp oldModule,
hw::HWModuleOp newModule,
632 LogicalResult lowerFormalBody(verif::FormalOp formalOp,
634 LogicalResult lowerSimulationBody(verif::SimulationOp simulationOp,
636 LogicalResult lowerFileBody(emit::FileOp op);
644 bool enableAnnotationWarning,
646 auto pass = std::make_unique<FIRRTLModuleLowering>();
647 if (enableAnnotationWarning)
648 pass->setEnableAnnotationWarning();
649 pass->verificationFlavor = verificationFlavor;
655void FIRRTLModuleLowering::runOnOperation() {
659 auto *topLevelModule = getOperation().getBody();
663 for (
auto &op : *topLevelModule) {
664 if ((circuit = dyn_cast<CircuitOp>(&op)))
671 auto *circuitBody = circuit.getBodyBlock();
675 CircuitLoweringState state(circuit, enableAnnotationWarning,
676 verificationFlavor, getAnalysis<InstanceGraph>(),
677 &getAnalysis<NLATable>(),
678 getAnalysis<InstanceChoiceMacroTable>());
680 SmallVector<Operation *, 32> opsToProcess;
683 state.processRemainingAnnotations(circuit, circuitAnno);
686 for (
auto &op : make_early_inc_range(circuitBody->getOperations())) {
688 TypeSwitch<Operation *, LogicalResult>(&op)
689 .Case<FModuleOp>([&](
auto module) {
690 auto loweredMod = lowerModule(module, topLevelModule, state);
694 state.recordModuleMapping(&op, loweredMod);
695 opsToProcess.push_back(loweredMod);
697 module.walk([&](Operation *op) {
698 for (auto res : op->getResults()) {
700 type_dyn_cast<BaseTypeAliasType>(res.getType()))
701 state.lowerType(aliasType, op->getLoc());
704 return lowerModulePortsAndMoveBody(module, loweredMod, state);
706 .Case<FExtModuleOp>([&](
auto extModule) {
708 lowerExtModule(extModule, topLevelModule, state);
711 state.recordModuleMapping(&op, loweredMod);
714 .Case<FMemModuleOp>([&](
auto memModule) {
716 lowerMemModule(memModule, topLevelModule, state);
719 state.recordModuleMapping(&op, loweredMod);
722 .Case<FormalOp>([&](
auto oldOp) {
723 auto builder = OpBuilder::atBlockEnd(topLevelModule);
724 auto newOp = verif::FormalOp::create(builder, oldOp.getLoc(),
726 oldOp.getParametersAttr());
727 newOp.getBody().emplaceBlock();
728 state.recordModuleMapping(oldOp, newOp);
729 opsToProcess.push_back(newOp);
732 .Case<SimulationOp>([&](
auto oldOp) {
733 auto loc = oldOp.getLoc();
734 auto builder = OpBuilder::atBlockEnd(topLevelModule);
735 auto newOp = verif::SimulationOp::create(
736 builder, loc, oldOp.getNameAttr(), oldOp.getParametersAttr());
737 auto &body = newOp.getRegion().emplaceBlock();
738 body.addArgument(seq::ClockType::get(builder.getContext()), loc);
739 body.addArgument(builder.getI1Type(), loc);
740 state.recordModuleMapping(oldOp, newOp);
741 opsToProcess.push_back(newOp);
744 .Case<emit::FileOp>([&](
auto fileOp) {
745 fileOp->moveBefore(topLevelModule, topLevelModule->end());
746 opsToProcess.push_back(fileOp);
749 .Case<OptionOp, OptionCaseOp>([&](
auto) {
753 .Default([&](Operation *op) {
758 op->moveBefore(topLevelModule, topLevelModule->end());
764 return signalPassFailure();
767 state.typeAliases.freeze();
772 SmallVector<Attribute> dutHierarchyFiles;
773 SmallVector<Attribute> testHarnessHierarchyFiles;
774 circuitAnno.removeAnnotations([&](
Annotation annotation) {
775 if (annotation.
isClass(moduleHierarchyAnnoClass)) {
776 auto file = hw::OutputFileAttr::getFromFilename(
778 annotation.
getMember<StringAttr>(
"filename").getValue(),
780 dutHierarchyFiles.push_back(file);
783 if (annotation.
isClass(testHarnessHierarchyAnnoClass)) {
784 auto file = hw::OutputFileAttr::getFromFilename(
786 annotation.
getMember<StringAttr>(
"filename").getValue(),
790 if (state.getTestHarness())
791 testHarnessHierarchyFiles.push_back(file);
793 dutHierarchyFiles.push_back(file);
799 if (!dutHierarchyFiles.empty())
800 state.getNewModule(state.getDut())
802 ArrayAttr::get(&getContext(), dutHierarchyFiles));
803 if (!testHarnessHierarchyFiles.empty())
804 state.getNewModule(state.getTestHarness())
806 ArrayAttr::get(&getContext(), testHarnessHierarchyFiles));
810 mlir::failableParallelForEach(&getContext(), opsToProcess, [&](
auto op) {
814 return signalPassFailure();
817 for (
auto bind : state.binds) {
822 for (
auto &[module, fragments] : state.fragments)
824 ArrayAttr::
get(&getContext(), fragments.getArrayRef()));
827 for (
auto oldNew : state.oldToNewModuleMap)
828 oldNew.first->erase();
830 if (!state.macroDeclNames.empty()) {
831 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), circuit);
832 for (
auto name : state.macroDeclNames) {
833 sv::MacroDeclOp::create(b, name);
838 lowerFileHeader(circuit, state);
845void FIRRTLModuleLowering::lowerFileHeader(CircuitOp op,
846 CircuitLoweringState &state) {
849 ImplicitLocOpBuilder
b(UnknownLoc::get(&getContext()), op);
853 auto emitGuardedDefine = [&](StringRef guard, StringRef defName,
854 StringRef defineTrue =
"",
855 StringRef defineFalse = StringRef()) {
856 if (!defineFalse.data()) {
857 assert(defineTrue.data() &&
"didn't define anything");
859 b, guard, [&]() { sv::MacroDefOp::create(b, defName, defineTrue); });
864 if (defineTrue.data())
865 sv::MacroDefOp::create(b, defName, defineTrue);
867 [&]() { sv::MacroDefOp::create(b, defName, defineFalse); });
872 auto emitGuard = [&](
const char *guard, llvm::function_ref<void(
void)> body) {
874 b, guard, [] {}, body);
877 if (state.usedFileDescriptorLib) {
879 SmallVector<hw::ModulePort> ports;
883 namePort.
name =
b.getStringAttr(
"name");
884 namePort.
type = hw::StringType::get(
b.getContext());
885 namePort.
dir = hw::ModulePort::Direction::Input;
886 ports.push_back(namePort);
890 fdPort.
name =
b.getStringAttr(
"fd");
891 fdPort.
type =
b.getIntegerType(32);
892 fdPort.
dir = hw::ModulePort::Direction::Output;
893 ports.push_back(fdPort);
896 auto moduleType = hw::ModuleType::get(
b.getContext(), ports);
898 SmallVector<NamedAttribute> perArgumentsAttr;
899 perArgumentsAttr.push_back(
900 {sv::FuncOp::getExplicitlyReturnedAttrName(),
b.getUnitAttr()});
902 SmallVector<Attribute> argumentAttr = {
903 DictionaryAttr::get(
b.getContext(), {}),
904 DictionaryAttr::get(
b.getContext(), perArgumentsAttr)};
907 auto func = sv::FuncOp::create(
909 "__circt_lib_logging::FileDescriptor::get", moduleType,
912 {b.getDictionaryAttr({}),
b.getDictionaryAttr(perArgumentsAttr)}),
918 b.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"));
921 sv::MacroDeclOp::create(b,
"__CIRCT_LIB_LOGGING");
923 emit::FragmentOp::create(b,
"CIRCT_LIB_LOGGING_FRAGMENT", [&] {
924 emitGuard(
"SYNTHESIS", [&]() {
925 emitGuard(
"__CIRCT_LIB_LOGGING", [&]() {
926 sv::VerbatimOp::create(b, R
"(// CIRCT Logging Library
927package __circt_lib_logging;
928 class FileDescriptor;
929 static int global_id [string];
930 static function int get(string name);
931 if (global_id.exists(name) == 32'h0) begin
932 global_id[name] = $fopen(name, "w");
933 if (global_id[name] == 32'h0)
934 $error("Failed to open file %s", name);
936 return global_id[name];
942 sv::MacroDefOp::create(b, "__CIRCT_LIB_LOGGING",
"");
948 if (state.usedPrintf) {
949 sv::MacroDeclOp::create(b,
"PRINTF_COND");
950 sv::MacroDeclOp::create(b,
"PRINTF_COND_");
951 emit::FragmentOp::create(b,
"PRINTF_COND_FRAGMENT", [&] {
952 sv::VerbatimOp::create(
953 b,
"\n// Users can define 'PRINTF_COND' to add an extra gate to "
955 emitGuard(
"PRINTF_COND_", [&]() {
956 emitGuardedDefine(
"PRINTF_COND",
"PRINTF_COND_",
"(`PRINTF_COND)",
"1");
961 if (state.usedAssertVerboseCond) {
962 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND");
963 sv::MacroDeclOp::create(b,
"ASSERT_VERBOSE_COND_");
964 emit::FragmentOp::create(b,
"ASSERT_VERBOSE_COND_FRAGMENT", [&] {
965 sv::VerbatimOp::create(
966 b,
"\n// Users can define 'ASSERT_VERBOSE_COND' to add an extra "
967 "gate to assert error printing.");
968 emitGuard(
"ASSERT_VERBOSE_COND_", [&]() {
969 emitGuardedDefine(
"ASSERT_VERBOSE_COND",
"ASSERT_VERBOSE_COND_",
970 "(`ASSERT_VERBOSE_COND)",
"1");
975 if (state.usedStopCond) {
976 sv::MacroDeclOp::create(b,
"STOP_COND");
977 sv::MacroDeclOp::create(b,
"STOP_COND_");
978 emit::FragmentOp::create(b,
"STOP_COND_FRAGMENT", [&] {
979 sv::VerbatimOp::create(
980 b,
"\n// Users can define 'STOP_COND' to add an extra gate "
981 "to stop conditions.");
982 emitGuard(
"STOP_COND_", [&]() {
983 emitGuardedDefine(
"STOP_COND",
"STOP_COND_",
"(`STOP_COND)",
"1");
990FIRRTLModuleLowering::lowerPorts(ArrayRef<PortInfo> firrtlPorts,
991 SmallVectorImpl<hw::PortInfo> &ports,
992 Operation *moduleOp, StringRef moduleName,
994 ports.reserve(firrtlPorts.size());
996 size_t numResults = 0;
997 for (
auto e :
llvm::enumerate(firrtlPorts)) {
999 size_t portNo = e.index();
1004 if (firrtlPort.
sym.size() > 1 ||
1005 (firrtlPort.
sym.size() == 1 && !firrtlPort.
sym.getSymName()))
1006 return emitError(firrtlPort.
loc)
1007 <<
"cannot lower aggregate port " << firrtlPort.
name
1008 <<
" with field sensitive symbols, HW dialect does not support "
1009 "per field symbols yet.";
1010 hwPort.
setSym(firrtlPort.
sym, moduleOp->getContext());
1012 if (hadDontTouch && !hwPort.
getSym()) {
1013 if (hwPort.
type.isInteger(0)) {
1014 if (enableAnnotationWarning) {
1015 mlir::emitWarning(firrtlPort.
loc)
1016 <<
"zero width port " << hwPort.
name
1017 <<
" has dontTouch annotation, removing anyway";
1023 hw::InnerSymAttr::get(StringAttr::get(
1024 moduleOp->getContext(),
1025 Twine(
"__") + moduleName + Twine(
"__DONTTOUCH__") +
1026 Twine(portNo) + Twine(
"__") + firrtlPort.
name.strref())),
1027 moduleOp->getContext());
1032 moduleOp->emitError(
"cannot lower this port type to HW");
1038 if (hwPort.
type.isInteger(0)) {
1039 auto sym = hwPort.
getSym();
1040 if (sym && !sym.empty()) {
1041 return mlir::emitError(firrtlPort.
loc)
1042 <<
"zero width port " << hwPort.
name
1043 <<
" is referenced by name [" << sym
1044 <<
"] (e.g. in an XMR) but must be removed";
1051 hwPort.
dir = hw::ModulePort::Direction::Output;
1052 hwPort.
argNum = numResults++;
1053 }
else if (firrtlPort.
isInput()) {
1054 hwPort.
dir = hw::ModulePort::Direction::Input;
1055 hwPort.
argNum = numArgs++;
1059 hwPort.
type = hw::InOutType::get(hwPort.
type);
1060 hwPort.
dir = hw::ModulePort::Direction::InOut;
1061 hwPort.
argNum = numArgs++;
1063 hwPort.
loc = firrtlPort.
loc;
1064 ports.push_back(hwPort);
1074 auto params = llvm::map_range(module.getParameters(), [](Attribute a) {
1075 return cast<ParamDeclAttr>(a);
1080 Builder builder(module);
1085 SmallVector<Attribute> newParams;
1086 for (
const ParamDeclAttr &entry : params) {
1087 auto name = entry.getName();
1088 auto type = entry.getType();
1089 auto value = ignoreValues ? Attribute() : entry.getValue();
1091 hw::ParamDeclAttr::get(builder.getContext(), name, type, value);
1092 newParams.push_back(paramAttr);
1094 return builder.getArrayAttr(newParams);
1097bool FIRRTLModuleLowering::handleForceNameAnnos(
1100 bool failed =
false;
1103 if (!anno.
isClass(forceNameAnnoClass))
1106 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
1113 auto diag = oldModule.emitOpError()
1114 <<
"contains a '" << forceNameAnnoClass
1115 <<
"' that is not a non-local annotation";
1116 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1127 auto diag = oldModule.emitOpError()
1128 <<
"contains a '" << forceNameAnnoClass
1129 <<
"' whose non-local symbol, '" << sym
1130 <<
"' does not exist in the circuit";
1131 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict();
1144 cast<hw::InnerRefAttr>(nla.getNamepath().getValue().take_back(2)[0]);
1146 {{inst.getModule(), inst.getName()}, anno.
getMember(
"name")});
1147 if (!inserted.second &&
1148 (anno.
getMember(
"name") != (inserted.first->second))) {
1149 auto diag = oldModule.emitError()
1150 <<
"contained multiple '" << forceNameAnnoClass
1151 <<
"' with different names: " << inserted.first->second
1152 <<
" was not " << anno.
getMember(
"name");
1153 diag.attachNote() <<
"the erroneous annotation is '" << anno.
getDict()
1164 FExtModuleOp oldModule, Block *topLevelModule,
1175 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1176 SmallVector<hw::PortInfo, 8> ports;
1177 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1182 StringRef verilogName;
1183 if (
auto defName = oldModule.getDefname())
1184 verilogName = defName.value();
1186 verilogName = oldModule.getName();
1188 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1190 auto filesAttr = verbatimAnno.
getMember<ArrayAttr>(
"files");
1191 if (!filesAttr || filesAttr.empty()) {
1192 oldModule->emitError(
"VerbatimBlackBoxAnno missing or empty files array");
1197 auto primaryFile = cast<DictionaryAttr>(filesAttr[0]);
1198 auto primaryFileContent = primaryFile.getAs<StringAttr>(
"content");
1199 auto primaryOutputFile = primaryFile.getAs<StringAttr>(
"output_file");
1201 if (!primaryFileContent || !primaryOutputFile) {
1202 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1206 auto primaryOutputFileAttr = hw::OutputFileAttr::getFromFilename(
1207 builder.getContext(), primaryOutputFile.getValue());
1209 auto primaryFileName = llvm::sys::path::filename(primaryOutputFile);
1210 auto verbatimSource =
loweringState.getVerbatimSourceForFile(primaryFileName);
1213 SmallVector<Attribute> additionalFiles;
1217 for (
size_t i = 1; i < filesAttr.size(); ++i) {
1218 auto file = cast<DictionaryAttr>(filesAttr[i]);
1219 auto content = file.getAs<StringAttr>(
"content");
1220 auto outputFile = file.getAs<StringAttr>(
"output_file");
1221 auto fileName = llvm::sys::path::filename(outputFile);
1223 if (!(content && outputFile)) {
1224 oldModule->emitError(
"VerbatimBlackBoxAnno file missing fields");
1232 auto fileSymbolName = circuitNamespace.newName(fileName);
1233 emitFile = emit::FileOp::create(builder, oldModule.getLoc(),
1234 outputFile.getValue(), fileSymbolName);
1235 builder.setInsertionPointToStart(&
emitFile.getBodyRegion().front());
1236 emit::VerbatimOp::create(builder, oldModule.getLoc(), content);
1237 builder.setInsertionPointAfter(
emitFile);
1240 auto ext = llvm::sys::path::extension(outputFile.getValue());
1241 bool excludeFromFileList = (ext ==
".h" || ext ==
".vh" || ext ==
".svh");
1242 auto outputFileAttr = hw::OutputFileAttr::getFromFilename(
1243 builder.getContext(), outputFile.getValue(), excludeFromFileList);
1244 emitFile->setAttr(
"output_file", outputFileAttr);
1248 additionalFiles.push_back(FlatSymbolRefAttr::get(
emitFile));
1254 parameters = builder.getArrayAttr({});
1256 if (!verbatimSource) {
1257 verbatimSource = sv::SVVerbatimSourceOp::create(
1258 builder, oldModule.getLoc(),
1259 circuitNamespace.newName(primaryFileName.str()),
1260 primaryFileContent.getValue(), primaryOutputFileAttr, parameters,
1261 additionalFiles.empty() ?
nullptr
1262 : builder.getArrayAttr(additionalFiles),
1263 builder.getStringAttr(verilogName));
1265 SymbolTable::setSymbolVisibility(
1266 verbatimSource, SymbolTable::getSymbolVisibility(oldModule));
1268 loweringState.registerVerbatimSource(primaryFileName, verbatimSource);
1271 return verbatimSource;
1275FIRRTLModuleLowering::lowerExtModule(FExtModuleOp oldModule,
1276 Block *topLevelModule,
1278 if (
auto verbatimMod =
1279 lowerVerbatimExtModule(oldModule, topLevelModule,
loweringState))
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();
1296 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1297 auto nameAttr = builder.getStringAttr(oldModule.getName());
1302 auto newModule = hw::HWModuleExternOp::create(
1303 builder, oldModule.getLoc(), nameAttr, ports, verilogName, parameters);
1304 SymbolTable::setSymbolVisibility(newModule,
1305 SymbolTable::getSymbolVisibility(oldModule));
1307 bool hasOutputPort =
1308 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1309 if (!hasOutputPort &&
1311 internalVerifBlackBoxAnnoClass) &&
1313 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1316 if (
auto extReqs = oldModule.getExternalRequirements();
1317 extReqs && !extReqs.empty())
1318 newModule->setAttr(
"circt.external_requirements", extReqs);
1323 loweringState.processRemainingAnnotations(oldModule, annos);
1328 FExtModuleOp oldModule, Block *topLevelModule,
1333 auto verbatimSource =
1334 getVerbatimSourceForExtModule(oldModule, topLevelModule,
loweringState);
1336 if (!verbatimSource)
1339 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1340 SmallVector<hw::PortInfo, 8> ports;
1341 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1345 StringRef verilogName;
1346 if (
auto defName = oldModule.getDefname())
1347 verilogName = defName.value();
1349 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1351 auto newModule = sv::SVVerbatimModuleOp::create(
1354 builder.getStringAttr(oldModule.getName()),
1356 FlatSymbolRefAttr::get(verbatimSource),
1357 parameters ? parameters : builder.getArrayAttr({}),
1358 verilogName.empty() ? StringAttr{}
1359 : builder.getStringAttr(verilogName));
1361 SymbolTable::setSymbolVisibility(newModule,
1362 SymbolTable::getSymbolVisibility(oldModule));
1364 bool hasOutputPort =
1365 llvm::any_of(firrtlPorts, [&](
auto p) {
return p.isOutput(); });
1366 if (!hasOutputPort &&
1368 internalVerifBlackBoxAnnoClass) &&
1370 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1373 if (
auto extReqs = oldModule.getExternalRequirements();
1374 extReqs && !extReqs.empty())
1375 newModule->setAttr(
"circt.external_requirements", extReqs);
1380 loweringState.processRemainingAnnotations(oldModule, annos);
1385FIRRTLModuleLowering::lowerMemModule(FMemModuleOp oldModule,
1386 Block *topLevelModule,
1389 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1390 SmallVector<hw::PortInfo, 8> ports;
1391 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1396 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1397 auto newModule = hw::HWModuleExternOp::create(
1398 builder, oldModule.getLoc(), oldModule.getModuleNameAttr(), ports,
1399 oldModule.getModuleNameAttr());
1407FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
1410 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1411 SmallVector<hw::PortInfo, 8> ports;
1412 if (failed(lowerPorts(firrtlPorts, ports, oldModule, oldModule.getName(),
1417 auto builder = OpBuilder::atBlockEnd(topLevelModule);
1418 auto nameAttr = builder.getStringAttr(oldModule.getName());
1420 hw::HWModuleOp::create(builder, oldModule.getLoc(), nameAttr, ports);
1422 if (
auto comment = oldModule->getAttrOfType<StringAttr>(
"comment"))
1423 newModule.setCommentAttr(comment);
1426 SmallVector<StringRef, 13> attrNames = {
1427 "annotations",
"convention",
"layers",
1428 "portNames",
"sym_name",
"portDirections",
1429 "portTypes",
"portAnnotations",
"portSymbols",
1430 "portLocations",
"parameters", SymbolTable::getVisibilityAttrName(),
1433 DenseSet<StringRef> attrSet(attrNames.begin(), attrNames.end());
1434 SmallVector<NamedAttribute> newAttrs(newModule->getAttrs());
1436 llvm::make_filter_range(oldModule->getAttrs(), [&](auto namedAttr) {
1437 return !attrSet.count(namedAttr.getName()) &&
1438 !newModule->getAttrDictionary().contains(namedAttr.getName());
1440 newAttrs.push_back(i);
1442 newModule->setAttrs(newAttrs);
1446 SymbolTable::setSymbolVisibility(newModule,
1447 SymbolTable::getSymbolVisibility(oldModule));
1453 newModule->setAttr(
"firrtl.extract.cover.extra", builder.getUnitAttr());
1457 if (
auto testBenchDir =
loweringState.getTestBenchDirectory())
1459 if (!newModule->hasAttr(
"output_file"))
1460 newModule->setAttr(
"output_file", testBenchDir);
1461 newModule->setAttr(
"firrtl.extract.do_not_extract",
1462 builder.getUnitAttr());
1463 newModule.setCommentAttr(
1464 builder.getStringAttr(
"VCS coverage exclude_file"));
1470 loweringState.processRemainingAnnotations(oldModule, annos);
1479 Operation *insertPoint) {
1480 if (!value.hasOneUse())
1483 auto attach = dyn_cast<AttachOp>(*value.user_begin());
1484 if (!attach || attach.getNumOperands() != 2)
1488 auto loweredType =
lowerType(value.getType());
1489 if (loweredType.isInteger(0))
1494 auto attachedValue = attach.getOperand(attach.getOperand(0) == value);
1495 auto *op = attachedValue.getDefiningOp();
1496 if (op && op->getBlock() == insertPoint->getBlock() &&
1497 !op->isBeforeInBlock(insertPoint))
1502 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1518 if (type_isa<AnalogType>(flipValue.getType()))
1521 Operation *connectOp =
nullptr;
1522 for (
auto &use : flipValue.getUses()) {
1525 if (use.getOperandNumber() != 0)
1527 if (!isa<ConnectOp, MatchingConnectOp>(use.getOwner()))
1533 connectOp = use.getOwner();
1543 loweringState.lowerType(flipValue.getType(), flipValue.getLoc());
1544 if (loweredType.isInteger(0))
1549 ImplicitLocOpBuilder builder(insertPoint->getLoc(), insertPoint);
1551 auto connectSrc = connectOp->getOperand(1);
1554 if (!isa<FIRRTLType>(connectSrc.getType())) {
1560 if (!type_cast<FIRRTLBaseType>(connectSrc.getType()).isPassive())
1562 mlir::UnrealizedConversionCastOp::create(
1564 type_cast<FIRRTLBaseType>(connectSrc.getType()).getPassiveType(),
1570 auto destTy = type_cast<FIRRTLBaseType>(flipValue.getType()).getPassiveType();
1572 if (destTy != connectSrc.getType() &&
1573 (isa<BaseTypeAliasType>(connectSrc.getType()) ||
1574 isa<BaseTypeAliasType>(destTy))) {
1576 builder.createOrFold<BitCastOp>(flipValue.getType(), connectSrc);
1578 if (!destTy.isGround()) {
1580 if (destTy != type_cast<FIRRTLType>(connectSrc.getType()))
1582 }
else if (destTy.getBitWidthOrSentinel() !=
1583 type_cast<FIRRTLBaseType>(connectSrc.getType())
1584 .getBitWidthOrSentinel()) {
1587 auto destWidth = destTy.getBitWidthOrSentinel();
1588 assert(destWidth != -1 &&
"must know integer widths");
1589 connectSrc = builder.createOrFold<PadPrimOp>(destTy, connectSrc, destWidth);
1601 SmallVector<SubfieldOp> accesses;
1602 for (
auto *op : structValue.getUsers()) {
1603 assert(isa<SubfieldOp>(op));
1604 auto fieldAccess = cast<SubfieldOp>(op);
1606 fieldAccess.getInput().getType().base().getElementIndex(field);
1607 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
1608 accesses.push_back(fieldAccess);
1616LogicalResult FIRRTLModuleLowering::lowerModulePortsAndMoveBody(
1619 ImplicitLocOpBuilder bodyBuilder(oldModule.getLoc(), newModule.getBody());
1626 bodyBuilder.setInsertionPoint(cursor);
1629 SmallVector<PortInfo> firrtlPorts = oldModule.getPorts();
1630 assert(oldModule.getBody().getNumArguments() == firrtlPorts.size() &&
1631 "port count mismatch");
1633 SmallVector<Value, 4> outputs;
1636 auto *outputOp = newModule.getBodyBlock()->getTerminator();
1637 ImplicitLocOpBuilder outputBuilder(oldModule.getLoc(), outputOp);
1639 unsigned nextHWInputArg = 0;
1640 int hwPortIndex = -1;
1641 for (
auto [firrtlPortIndex, port] :
llvm::enumerate(firrtlPorts)) {
1643 auto oldArg = oldModule.getBody().getArgument(firrtlPortIndex);
1646 type_isa<FIRRTLBaseType>(port.type) &&
1647 type_cast<FIRRTLBaseType>(port.type).getBitWidthOrSentinel() == 0;
1651 if (!port.isOutput() && !isZeroWidth) {
1654 Value newArg = newModule.getBody().getArgument(nextHWInputArg++);
1660 oldArg.replaceAllUsesWith(newArg);
1666 if (isZeroWidth && port.isInput()) {
1668 WireOp::create(bodyBuilder, port.type,
1669 "." + port.getName().str() +
".0width_input")
1671 oldArg.replaceAllUsesWith(newArg);
1679 outputs.push_back(value);
1680 assert(oldArg.use_empty() &&
"should have removed all uses of oldArg");
1686 auto newArg = WireOp::create(bodyBuilder, port.type,
1687 "." + port.getName().str() +
".output");
1690 oldArg.replaceAllUsesWith(newArg.getResult());
1693 auto resultHWType =
loweringState.lowerType(port.type, port.loc);
1694 if (!resultHWType.isInteger(0)) {
1697 outputs.push_back(output);
1700 if (
auto sym = newModule.getPort(hwPortIndex).getSym()) {
1701 newArg.setInnerSymAttr(sym);
1702 newModule.setPortSymbolAttr(hwPortIndex, {});
1708 outputOp->setOperands(outputs);
1711 auto &oldBlockInstList = oldModule.getBodyBlock()->getOperations();
1712 auto &newBlockInstList = newModule.getBodyBlock()->getOperations();
1713 newBlockInstList.splice(Block::iterator(cursor), oldBlockInstList,
1714 oldBlockInstList.begin(), oldBlockInstList.end());
1725FIRRTLModuleLowering::lowerFormalBody(verif::FormalOp newOp,
1727 auto builder = OpBuilder::atBlockEnd(&newOp.getBody().front());
1732 auto oldOp = cast<FormalOp>(
loweringState.getOldModule(newOp));
1733 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1734 auto oldModule = cast<FModuleOp>(
1735 loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1736 auto newModule = cast<hw::HWModuleOp>(
loweringState.getNewModule(oldModule));
1739 SmallVector<Value> symbolicInputs;
1740 for (
auto arg : newModule.getBody().getArguments())
1741 symbolicInputs.push_back(
verif::SymbolicValueOp::create(
1742 builder, arg.
getLoc(), arg.getType(),
1746 hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1747 newModule.getNameAttr(), symbolicInputs);
1754FIRRTLModuleLowering::lowerSimulationBody(verif::SimulationOp newOp,
1756 auto builder = OpBuilder::atBlockEnd(newOp.getBody());
1759 auto oldOp = cast<SimulationOp>(
loweringState.getOldModule(newOp));
1760 auto moduleName = oldOp.getModuleNameAttr().getAttr();
1761 auto oldModule = cast<FModuleLike>(
1762 *
loweringState.getInstanceGraph().lookup(moduleName)->getModule());
1764 cast<hw::HWModuleLike>(
loweringState.getNewModule(oldModule));
1768 SmallVector<Value> inputs(newOp.getBody()->args_begin(),
1769 newOp.getBody()->args_end());
1770 auto instOp = hw::InstanceOp::create(builder, newOp.getLoc(), newModule,
1771 newModule.getNameAttr(), inputs);
1772 verif::YieldOp::create(builder, newOp.getLoc(), instOp.getResults());
1782struct FIRRTLLowering :
public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
1784 FIRRTLLowering(
hw::HWModuleOp module, CircuitLoweringState &circuitState)
1785 : theModule(module), circuitState(circuitState),
1786 builder(module.
getLoc(), module.getContext()), moduleNamespace(module),
1787 backedgeBuilder(builder, module.
getLoc()) {}
1789 LogicalResult
run();
1792 Value getOrCreateClockConstant(seq::ClockConst clock);
1793 Value getOrCreateIntConstant(
const APInt &value);
1794 Value getOrCreateIntConstant(
unsigned numBits, uint64_t val,
1795 bool isSigned =
false) {
1796 return getOrCreateIntConstant(APInt(numBits, val, isSigned));
1798 Attribute getOrCreateAggregateConstantAttribute(Attribute value, Type type);
1799 Value getOrCreateXConstant(
unsigned numBits);
1800 Value getOrCreateZConstant(Type type);
1801 Value getPossiblyInoutLoweredValue(Value value);
1802 Value getLoweredValue(Value value);
1803 Value getLoweredNonClockValue(Value value);
1804 Value getLoweredAndExtendedValue(Value value, Type destType);
1805 Value getLoweredAndExtOrTruncValue(Value value, Type destType);
1806 LogicalResult setLowering(Value orig, Value result);
1807 LogicalResult setPossiblyFoldedLowering(Value orig, Value result);
1808 template <
typename ResultOpType,
typename... CtorArgTypes>
1809 LogicalResult setLoweringTo(Operation *orig, CtorArgTypes... args);
1810 template <
typename ResultOpType,
typename... CtorArgTypes>
1811 LogicalResult setLoweringToLTL(Operation *orig, CtorArgTypes... args);
1812 Backedge createBackedge(Location loc, Type type);
1813 Backedge createBackedge(Value orig, Type type);
1814 bool updateIfBackedge(Value dest, Value src);
1817 bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
1822 if (
auto forceable = dyn_cast<Forceable>(op.getOperation()))
1823 if (forceable.isForceable())
1831 hw::InnerSymAttr lowerInnerSymbol(hw::InnerSymbolOpInterface op) {
1832 auto attr = op.getInnerSymAttr();
1836 if (requiresInnerSymbol(op))
1838 op.getContext(), attr, 0,
1846 LogicalResult prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
1847 Operation *instanceOp,
1848 SmallVectorImpl<Value> &inputOperands);
1850 void runWithInsertionPointAtEndOfBlock(
const std::function<
void(
void)> &fn,
1854 Value getReadValue(Value v);
1856 Value getNonClockValue(Value v);
1858 void addToAlwaysBlock(sv::EventControl clockEdge, Value clock,
1859 sv::ResetType resetStyle, sv::EventControl resetEdge,
1860 Value reset,
const std::function<
void(
void)> &body = {},
1861 const std::function<void(
void)> &resetBody = {});
1862 void addToAlwaysBlock(Value clock,
1863 const std::function<
void(
void)> &body = {}) {
1864 addToAlwaysBlock(sv::EventControl::AtPosEdge, clock, sv::ResetType(),
1865 sv::EventControl(), Value(), body,
1866 std::function<
void(
void)>());
1869 LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
1870 std::function<
void(
void)>
emit);
1871 void addToIfDefBlock(StringRef cond, std::function<
void(
void)> thenCtor,
1872 std::function<
void(
void)> elseCtor = {});
1873 void addToInitialBlock(std::function<
void(
void)> body);
1874 void addIfProceduralBlock(Value cond, std::function<
void(
void)> thenCtor,
1875 std::function<
void(
void)> elseCtor = {});
1876 Value getExtOrTruncAggregateValue(Value array,
FIRRTLBaseType sourceType,
1878 bool allowTruncate);
1879 Value createArrayIndexing(Value array, Value index);
1880 Value createValueWithMuxAnnotation(Operation *op,
bool isMux2);
1882 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitExpr;
1883 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitDecl;
1884 using FIRRTLVisitor<FIRRTLLowering, LogicalResult>::visitStmt;
1887 enum UnloweredOpResult { AlreadyLowered, NowLowered, LoweringFailure };
1888 UnloweredOpResult handleUnloweredOp(Operation *op);
1889 LogicalResult visitExpr(ConstantOp op);
1890 LogicalResult visitExpr(SpecialConstantOp op);
1891 LogicalResult visitExpr(SubindexOp op);
1892 LogicalResult visitExpr(SubaccessOp op);
1893 LogicalResult visitExpr(SubfieldOp op);
1894 LogicalResult visitExpr(VectorCreateOp op);
1895 LogicalResult visitExpr(BundleCreateOp op);
1896 LogicalResult visitExpr(FEnumCreateOp op);
1897 LogicalResult visitExpr(AggregateConstantOp op);
1898 LogicalResult visitExpr(IsTagOp op);
1899 LogicalResult visitExpr(SubtagOp op);
1900 LogicalResult visitExpr(TagExtractOp op);
1903 if (
auto castOp = dyn_cast<mlir::UnrealizedConversionCastOp>(op))
1904 return visitUnrealizedConversionCast(castOp);
1909 LogicalResult visitDecl(WireOp op);
1910 LogicalResult visitDecl(NodeOp op);
1911 LogicalResult visitDecl(RegOp op);
1912 LogicalResult visitDecl(RegResetOp op);
1913 LogicalResult visitDecl(MemOp op);
1914 LogicalResult visitDecl(InstanceOp oldInstance);
1915 LogicalResult visitDecl(InstanceChoiceOp oldInstanceChoice);
1916 LogicalResult visitDecl(VerbatimWireOp op);
1917 LogicalResult visitDecl(ContractOp op);
1920 LogicalResult lowerNoopCast(Operation *op);
1921 LogicalResult visitExpr(AsSIntPrimOp op);
1922 LogicalResult visitExpr(AsUIntPrimOp op);
1923 LogicalResult visitExpr(AsClockPrimOp op);
1924 LogicalResult visitExpr(AsAsyncResetPrimOp op) {
return lowerNoopCast(op); }
1926 LogicalResult visitExpr(HWStructCastOp op);
1927 LogicalResult visitExpr(BitCastOp op);
1929 visitUnrealizedConversionCast(mlir::UnrealizedConversionCastOp op);
1930 LogicalResult visitExpr(CvtPrimOp op);
1931 LogicalResult visitExpr(NotPrimOp op);
1932 LogicalResult visitExpr(NegPrimOp op);
1933 LogicalResult visitExpr(PadPrimOp op);
1934 LogicalResult visitExpr(XorRPrimOp op);
1935 LogicalResult visitExpr(AndRPrimOp op);
1936 LogicalResult visitExpr(OrRPrimOp op);
1939 template <
typename ResultUnsignedOpType,
1940 typename ResultSignedOpType = ResultUnsignedOpType>
1941 LogicalResult lowerBinOp(Operation *op);
1942 template <
typename ResultOpType>
1943 LogicalResult lowerBinOpToVariadic(Operation *op);
1945 template <
typename ResultOpType>
1946 LogicalResult lowerElementwiseLogicalOp(Operation *op);
1948 LogicalResult lowerCmpOp(Operation *op, ICmpPredicate signedOp,
1949 ICmpPredicate unsignedOp);
1950 template <
typename SignedOp,
typename Un
signedOp>
1951 LogicalResult lowerDivLikeOp(Operation *op);
1953 LogicalResult visitExpr(CatPrimOp op);
1955 LogicalResult visitExpr(AndPrimOp op) {
1956 return lowerBinOpToVariadic<comb::AndOp>(op);
1958 LogicalResult visitExpr(OrPrimOp op) {
1959 return lowerBinOpToVariadic<comb::OrOp>(op);
1961 LogicalResult visitExpr(XorPrimOp op) {
1962 return lowerBinOpToVariadic<comb::XorOp>(op);
1964 LogicalResult visitExpr(ElementwiseOrPrimOp op) {
1965 return lowerElementwiseLogicalOp<comb::OrOp>(op);
1967 LogicalResult visitExpr(ElementwiseAndPrimOp op) {
1968 return lowerElementwiseLogicalOp<comb::AndOp>(op);
1970 LogicalResult visitExpr(ElementwiseXorPrimOp op) {
1971 return lowerElementwiseLogicalOp<comb::XorOp>(op);
1973 LogicalResult visitExpr(AddPrimOp op) {
1974 return lowerBinOpToVariadic<comb::AddOp>(op);
1976 LogicalResult visitExpr(EQPrimOp op) {
1977 return lowerCmpOp(op, ICmpPredicate::eq, ICmpPredicate::eq);
1979 LogicalResult visitExpr(NEQPrimOp op) {
1980 return lowerCmpOp(op, ICmpPredicate::ne, ICmpPredicate::ne);
1982 LogicalResult visitExpr(LTPrimOp op) {
1983 return lowerCmpOp(op, ICmpPredicate::slt, ICmpPredicate::ult);
1985 LogicalResult visitExpr(LEQPrimOp op) {
1986 return lowerCmpOp(op, ICmpPredicate::sle, ICmpPredicate::ule);
1988 LogicalResult visitExpr(GTPrimOp op) {
1989 return lowerCmpOp(op, ICmpPredicate::sgt, ICmpPredicate::ugt);
1991 LogicalResult visitExpr(GEQPrimOp op) {
1992 return lowerCmpOp(op, ICmpPredicate::sge, ICmpPredicate::uge);
1995 LogicalResult visitExpr(SubPrimOp op) {
return lowerBinOp<comb::SubOp>(op); }
1996 LogicalResult visitExpr(MulPrimOp op) {
1997 return lowerBinOpToVariadic<comb::MulOp>(op);
1999 LogicalResult visitExpr(DivPrimOp op) {
2000 return lowerDivLikeOp<comb::DivSOp, comb::DivUOp>(op);
2002 LogicalResult visitExpr(RemPrimOp op) {
2003 return lowerDivLikeOp<comb::ModSOp, comb::ModUOp>(op);
2007 LogicalResult visitExpr(IsXIntrinsicOp op);
2008 LogicalResult visitExpr(PlusArgsTestIntrinsicOp op);
2009 LogicalResult visitExpr(PlusArgsValueIntrinsicOp op);
2010 LogicalResult visitStmt(FPGAProbeIntrinsicOp op);
2011 LogicalResult visitExpr(ClockInverterIntrinsicOp op);
2012 LogicalResult visitExpr(ClockDividerIntrinsicOp op);
2013 LogicalResult visitExpr(SizeOfIntrinsicOp op);
2014 LogicalResult visitExpr(ClockGateIntrinsicOp op);
2015 LogicalResult visitExpr(LTLAndIntrinsicOp op);
2016 LogicalResult visitExpr(LTLOrIntrinsicOp op);
2017 LogicalResult visitExpr(LTLIntersectIntrinsicOp op);
2018 LogicalResult visitExpr(LTLDelayIntrinsicOp op);
2019 LogicalResult visitExpr(LTLConcatIntrinsicOp op);
2020 LogicalResult visitExpr(LTLRepeatIntrinsicOp op);
2021 LogicalResult visitExpr(LTLGoToRepeatIntrinsicOp op);
2022 LogicalResult visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op);
2023 LogicalResult visitExpr(LTLNotIntrinsicOp op);
2024 LogicalResult visitExpr(LTLImplicationIntrinsicOp op);
2025 LogicalResult visitExpr(LTLUntilIntrinsicOp op);
2026 LogicalResult visitExpr(LTLEventuallyIntrinsicOp op);
2027 LogicalResult visitExpr(LTLPastIntrinsicOp op);
2028 LogicalResult visitExpr(LTLClockIntrinsicOp op);
2030 template <
typename TargetOp,
typename IntrinsicOp>
2031 LogicalResult lowerVerifIntrinsicOp(IntrinsicOp op);
2032 LogicalResult visitStmt(VerifAssertIntrinsicOp op);
2033 LogicalResult visitStmt(VerifAssumeIntrinsicOp op);
2034 LogicalResult visitStmt(VerifCoverIntrinsicOp op);
2035 LogicalResult visitStmt(VerifRequireIntrinsicOp op);
2036 LogicalResult visitStmt(VerifEnsureIntrinsicOp op);
2037 LogicalResult visitExpr(HasBeenResetIntrinsicOp op);
2038 LogicalResult visitStmt(UnclockedAssumeIntrinsicOp op);
2041 LogicalResult visitExpr(BitsPrimOp op);
2042 LogicalResult visitExpr(InvalidValueOp op);
2043 LogicalResult visitExpr(HeadPrimOp op);
2044 LogicalResult visitExpr(ShlPrimOp op);
2045 LogicalResult visitExpr(ShrPrimOp op);
2046 LogicalResult visitExpr(DShlPrimOp op) {
2047 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2049 LogicalResult visitExpr(DShrPrimOp op) {
2050 return lowerDivLikeOp<comb::ShrSOp, comb::ShrUOp>(op);
2052 LogicalResult visitExpr(DShlwPrimOp op) {
2053 return lowerDivLikeOp<comb::ShlOp, comb::ShlOp>(op);
2055 LogicalResult visitExpr(TailPrimOp op);
2056 LogicalResult visitExpr(MuxPrimOp op);
2057 LogicalResult visitExpr(Mux2CellIntrinsicOp op);
2058 LogicalResult visitExpr(Mux4CellIntrinsicOp op);
2059 LogicalResult visitExpr(MultibitMuxOp op);
2060 LogicalResult visitExpr(VerbatimExprOp op);
2061 LogicalResult visitExpr(XMRRefOp op);
2062 LogicalResult visitExpr(XMRDerefOp op);
2065 LogicalResult visitExpr(TimeOp op);
2066 LogicalResult visitExpr(HierarchicalModuleNameOp op);
2069 LogicalResult lowerVerificationStatement(
2070 Operation *op, StringRef labelPrefix, Value clock, Value predicate,
2071 Value enable, StringAttr messageAttr, ValueRange operands,
2072 StringAttr nameAttr,
bool isConcurrent, EventControl eventControl);
2074 LogicalResult visitStmt(SkipOp op);
2076 FailureOr<bool> lowerConnect(Value dest, Value srcVal);
2077 LogicalResult visitStmt(ConnectOp op);
2078 LogicalResult visitStmt(MatchingConnectOp op);
2079 LogicalResult visitStmt(ForceOp op);
2081 std::optional<Value> getLoweredFmtOperand(Value operand);
2082 LogicalResult loweredFmtOperands(ValueRange operands,
2083 SmallVectorImpl<Value> &loweredOperands);
2084 FailureOr<Value> callFileDescriptorLib(
const FileDescriptorInfo &info);
2088 LogicalResult lowerStatementWithFd(
2089 const FileDescriptorInfo &fileDescriptorInfo, Value clock, Value cond,
2090 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond);
2094 LogicalResult visitPrintfLike(T op,
2095 const FileDescriptorInfo &fileDescriptorInfo,
2096 bool usePrintfCond);
2097 LogicalResult visitStmt(PrintFOp op) {
return visitPrintfLike(op, {},
true); }
2098 LogicalResult visitStmt(FPrintFOp op);
2099 LogicalResult visitStmt(FFlushOp op);
2100 LogicalResult visitStmt(StopOp op);
2101 LogicalResult visitStmt(AssertOp op);
2102 LogicalResult visitStmt(AssumeOp op);
2103 LogicalResult visitStmt(CoverOp op);
2104 LogicalResult visitStmt(AttachOp op);
2105 LogicalResult visitStmt(RefForceOp op);
2106 LogicalResult visitStmt(RefForceInitialOp op);
2107 LogicalResult visitStmt(RefReleaseOp op);
2108 LogicalResult visitStmt(RefReleaseInitialOp op);
2109 LogicalResult visitStmt(BindOp op);
2111 FailureOr<Value> lowerSubindex(SubindexOp op, Value input);
2112 FailureOr<Value> lowerSubaccess(SubaccessOp op, Value input);
2113 FailureOr<Value> lowerSubfield(SubfieldOp op, Value input);
2115 LogicalResult fixupLTLOps();
2118 return circuitState.lowerType(type, builder.getLoc());
2126 CircuitLoweringState &circuitState;
2129 ImplicitLocOpBuilder builder;
2134 DenseMap<Value, Value> valueMapping;
2138 DenseMap<Value, Value> fromClockMapping;
2142 DenseMap<Attribute, Value> hwConstantMap;
2143 DenseMap<std::pair<Attribute, Type>, Attribute> hwAggregateConstantMap;
2147 DenseMap<unsigned, Value> hwConstantXMap;
2148 DenseMap<Type, Value> hwConstantZMap;
2154 DenseMap<Value, Value> readInOutCreated;
2157 DenseMap<StringAttr, sv::RegOp> fileNameToFileDescriptor;
2161 using AlwaysKeyType = std::tuple<
Block *, sv::EventControl, Value,
2162 sv::ResetType, sv::EventControl, Value>;
2179 llvm::MapVector<Value, Value> backedges;
2186 DenseSet<Operation *> maybeUnusedValues;
2188 void maybeUnused(Operation *op) { maybeUnusedValues.insert(op); }
2189 void maybeUnused(Value value) {
2190 if (
auto *op = value.getDefiningOp())
2202 SetVector<Operation *> ltlOpFixupWorklist;
2207 SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;
2209 void addToWorklist(Block &block) {
2210 worklist.push_back({block.begin(), block.end()});
2212 void addToWorklist(Region ®ion) {
2213 for (
auto &block :
llvm::reverse(region))
2214 addToWorklist(block);
2225LogicalResult FIRRTLModuleLowering::lowerFileBody(emit::FileOp fileOp) {
2226 OpBuilder
b(&getContext());
2227 fileOp->walk([&](Operation *op) {
2228 if (
auto bindOp = dyn_cast<BindOp>(op)) {
2229 b.setInsertionPointAfter(bindOp);
2230 sv::BindOp::create(b, bindOp.getLoc(), bindOp.getInstanceAttr());
2238FIRRTLModuleLowering::lowerBody(Operation *op,
2240 if (
auto moduleOp = dyn_cast<hw::HWModuleOp>(op))
2242 if (
auto formalOp = dyn_cast<verif::FormalOp>(op))
2244 if (
auto simulationOp = dyn_cast<verif::SimulationOp>(op))
2246 if (
auto fileOp = dyn_cast<emit::FileOp>(op))
2247 return lowerFileBody(fileOp);
2252LogicalResult FIRRTLLowering::run() {
2255 for (
auto arg : theModule.
getBodyBlock()->getArguments())
2256 if (failed(setLowering(arg, arg)))
2263 addToWorklist(theModule.getBody());
2264 SmallVector<Operation *, 16> opsToRemove;
2266 while (!worklist.empty()) {
2267 auto &[opsIt, opsEnd] = worklist.back();
2268 if (opsIt == opsEnd) {
2269 worklist.pop_back();
2272 Operation *op = &*opsIt++;
2274 builder.setInsertionPoint(op);
2275 builder.setLoc(op->getLoc());
2276 auto done = succeeded(dispatchVisitor(op));
2277 circuitState.processRemainingAnnotations(op,
AnnotationSet(op));
2279 opsToRemove.push_back(op);
2281 switch (handleUnloweredOp(op)) {
2282 case AlreadyLowered:
2285 opsToRemove.push_back(op);
2287 case LoweringFailure:
2299 for (
auto &[backedge, value] : backedges) {
2300 SmallVector<Location> driverLocs;
2306 if (backedge == value) {
2307 Location edgeLoc = backedge.getLoc();
2308 if (driverLocs.empty()) {
2309 mlir::emitError(edgeLoc,
"sink does not have a driver");
2311 auto diag = mlir::emitError(edgeLoc,
"sink in combinational loop");
2312 for (
auto loc : driverLocs)
2313 diag.attachNote(loc) <<
"through driver here";
2319 auto *it = backedges.find(value);
2320 if (it == backedges.end())
2323 driverLocs.push_back(value.getLoc());
2326 if (
auto *defOp = backedge.getDefiningOp())
2327 maybeUnusedValues.erase(defOp);
2328 backedge.replaceAllUsesWith(value);
2336 while (!opsToRemove.empty()) {
2337 auto *op = opsToRemove.pop_back_val();
2342 for (
auto result : op->getResults()) {
2346 auto builder = OpBuilder::atBlockBegin(theModule.getBodyBlock());
2348 builder.getIntegerType(0), 0);
2349 maybeUnusedValues.insert(zeroI0);
2351 result.replaceAllUsesWith(zeroI0);
2354 if (!op->use_empty()) {
2355 auto d = op->emitOpError(
2356 "still has uses; should remove ops in reverse order of visitation");
2357 SmallPtrSet<Operation *, 2> visited;
2358 for (
auto *user : op->getUsers())
2359 if (visited.insert(user).second)
2361 <<
"used by " << user->
getName() <<
" op";
2364 maybeUnusedValues.erase(op);
2369 while (!maybeUnusedValues.empty()) {
2370 auto it = maybeUnusedValues.begin();
2372 maybeUnusedValues.erase(it);
2373 if (!isOpTriviallyDead(op))
2375 for (
auto operand : op->getOperands())
2376 if (auto *defOp = operand.getDefiningOp())
2377 maybeUnusedValues.insert(defOp);
2383 if (failed(fixupLTLOps()))
2394Value FIRRTLLowering::getOrCreateClockConstant(seq::ClockConst clock) {
2395 auto attr = seq::ClockConstAttr::get(theModule.getContext(), clock);
2397 auto &entry = hwConstantMap[attr];
2401 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2402 entry = seq::ConstClockOp::create(entryBuilder, builder.getLoc(), attr);
2408Value FIRRTLLowering::getOrCreateIntConstant(
const APInt &value) {
2409 auto attr = builder.getIntegerAttr(
2410 builder.getIntegerType(value.getBitWidth()), value);
2412 auto &entry = hwConstantMap[attr];
2416 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2423Attribute FIRRTLLowering::getOrCreateAggregateConstantAttribute(Attribute value,
2426 if (hw::type_isa<IntegerType>(type))
2427 return builder.getIntegerAttr(type, cast<IntegerAttr>(value).getValue());
2429 auto cache = hwAggregateConstantMap.lookup({value, type});
2434 SmallVector<Attribute> values;
2435 for (
auto e :
llvm::enumerate(cast<ArrayAttr>(value))) {
2437 if (
auto array = hw::type_dyn_cast<hw::ArrayType>(type))
2438 subType = array.getElementType();
2439 else if (
auto structType = hw::type_dyn_cast<hw::StructType>(type))
2440 subType = structType.getElements()[e.index()].type;
2442 assert(
false &&
"type must be either array or struct");
2444 values.push_back(getOrCreateAggregateConstantAttribute(e.value(), subType));
2448 if (hw::type_isa<hw::ArrayType>(type))
2449 std::reverse(values.begin(), values.end());
2451 auto &entry = hwAggregateConstantMap[{value, type}];
2452 entry = builder.getArrayAttr(values);
2460 const std::function<LogicalResult()> &fn) {
2461 assert(failedOperand &&
"Should be called on the failed operand");
2469Value FIRRTLLowering::getOrCreateXConstant(
unsigned numBits) {
2471 auto &entry = hwConstantXMap[numBits];
2475 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2476 entry = sv::ConstantXOp::create(entryBuilder, builder.getLoc(),
2477 entryBuilder.getIntegerType(numBits));
2481Value FIRRTLLowering::getOrCreateZConstant(Type type) {
2482 auto &entry = hwConstantZMap[type];
2484 OpBuilder entryBuilder(&theModule.getBodyBlock()->front());
2485 entry = sv::ConstantZOp::create(entryBuilder, builder.getLoc(), type);
2494Value FIRRTLLowering::getPossiblyInoutLoweredValue(Value value) {
2496 if (
auto lowering = valueMapping.lookup(value)) {
2497 assert(!isa<FIRRTLType>(lowering.getType()) &&
2498 "Lowered value should be a non-FIRRTL value");
2507Value FIRRTLLowering::getLoweredValue(Value value) {
2508 auto result = getPossiblyInoutLoweredValue(value);
2514 if (isa<hw::InOutType>(result.getType()))
2515 return getReadValue(result);
2521Value FIRRTLLowering::getLoweredNonClockValue(Value value) {
2522 auto result = getLoweredValue(value);
2526 if (hw::type_isa<seq::ClockType>(result.getType()))
2527 return getNonClockValue(result);
2535Value FIRRTLLowering::getExtOrTruncAggregateValue(Value array,
2538 bool allowTruncate) {
2539 SmallVector<Value> resultBuffer;
2544 auto srcWidth = firrtl::type_cast<IntType>(sourceType).getWidthOrSentinel();
2545 auto destWidth = firrtl::type_cast<IntType>(destType).getWidthOrSentinel();
2546 auto resultType = builder.getIntegerType(destWidth);
2548 if (srcWidth == destWidth)
2551 if (srcWidth > destWidth) {
2555 builder.emitError(
"operand should not be a truncation");
2559 if (firrtl::type_cast<IntType>(sourceType).isSigned())
2560 return comb::createOrFoldSExt(builder, value, resultType);
2561 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2569 return TypeSwitch<FIRRTLBaseType, LogicalResult>(srcType)
2570 .Case<FVectorType>([&](
auto srcVectorType) {
2571 auto destVectorType = firrtl::type_cast<FVectorType>(destType);
2572 unsigned size = resultBuffer.size();
2573 unsigned indexWidth =
2575 for (
size_t i = 0, e = std::min(srcVectorType.getNumElements(),
2576 destVectorType.getNumElements());
2578 auto iIdx = getOrCreateIntConstant(indexWidth, i);
2580 if (failed(recurse(arrayIndex, srcVectorType.getElementType(),
2581 destVectorType.getElementType())))
2584 SmallVector<Value> temp(resultBuffer.begin() + size,
2585 resultBuffer.end());
2587 resultBuffer.resize(size);
2588 resultBuffer.push_back(array);
2591 .Case<BundleType>([&](BundleType srcStructType) {
2592 auto destStructType = firrtl::type_cast<BundleType>(destType);
2593 unsigned size = resultBuffer.size();
2596 if (destStructType.getNumElements() != srcStructType.getNumElements())
2599 for (
auto elem :
llvm::enumerate(destStructType)) {
2600 auto structExtract =
2602 if (failed(recurse(structExtract,
2603 srcStructType.getElementType(elem.index()),
2604 destStructType.getElementType(elem.index()))))
2607 SmallVector<Value> temp(resultBuffer.begin() + size,
2608 resultBuffer.end());
2611 resultBuffer.resize(size);
2612 resultBuffer.push_back(newStruct);
2615 .Case<IntType>([&](
auto) {
2616 if (
auto result = cast(src, srcType, destType)) {
2617 resultBuffer.push_back(result);
2622 .Default([&](
auto) {
return failure(); });
2625 if (failed(recurse(array, sourceType, destType)))
2628 assert(resultBuffer.size() == 1 &&
2629 "resultBuffer must only contain a result array if `success` is true");
2630 return resultBuffer[0];
2638Value FIRRTLLowering::getLoweredAndExtendedValue(Value src, Type target) {
2639 auto srcType = cast<FIRRTLBaseType>(src.getType());
2640 auto dstType = cast<FIRRTLBaseType>(target);
2641 auto loweredSrc = getLoweredValue(src);
2644 auto dstWidth = dstType.getBitWidthOrSentinel();
2660 return getOrCreateIntConstant(dstWidth, 0);
2663 auto loweredSrcType = loweredSrc.getType();
2664 auto loweredDstType =
lowerType(dstType);
2667 if (loweredSrcType == loweredDstType)
2671 if (dstWidth == srcType.getBitWidthOrSentinel()) {
2673 if (loweredSrcType != loweredDstType &&
2674 (isa<hw::TypeAliasType>(loweredSrcType) ||
2675 isa<hw::TypeAliasType>(loweredDstType))) {
2676 return builder.createOrFold<
hw::BitcastOp>(loweredDstType, loweredSrc);
2681 if (isa<hw::ArrayType, hw::StructType>(loweredSrcType))
2682 return getExtOrTruncAggregateValue(loweredSrc, srcType, dstType,
2685 if (isa<seq::ClockType>(loweredSrcType)) {
2686 builder.emitError(
"cannot use clock type as an integer");
2690 auto intSourceType = dyn_cast<IntegerType>(loweredSrcType);
2691 if (!intSourceType) {
2692 builder.emitError(
"operand of type ")
2693 << loweredSrcType <<
" cannot be used as an integer";
2697 auto loweredSrcWidth = intSourceType.getWidth();
2698 if (loweredSrcWidth ==
unsigned(dstWidth))
2701 if (loweredSrcWidth >
unsigned(dstWidth)) {
2702 builder.emitError(
"operand should not be a truncation");
2707 auto valueFIRType = type_cast<FIRRTLBaseType>(src.getType()).getPassiveType();
2708 if (type_cast<IntType>(valueFIRType).isSigned())
2709 return comb::createOrFoldSExt(builder, loweredSrc, loweredDstType);
2711 auto zero = getOrCreateIntConstant(dstWidth - loweredSrcWidth, 0);
2720Value FIRRTLLowering::getLoweredAndExtOrTruncValue(Value value, Type destType) {
2721 assert(type_isa<FIRRTLBaseType>(value.getType()) &&
2722 type_isa<FIRRTLBaseType>(destType) &&
2723 "input/output value should be FIRRTL");
2726 auto destWidth = type_cast<FIRRTLBaseType>(destType).getBitWidthOrSentinel();
2727 if (destWidth == -1)
2730 auto result = getLoweredValue(value);
2742 return getOrCreateIntConstant(destWidth, 0);
2746 if (isa<hw::ArrayType, hw::StructType>(result.getType())) {
2748 if (destType == value.getType())
2751 return getExtOrTruncAggregateValue(
2752 result, type_cast<FIRRTLBaseType>(value.getType()),
2753 type_cast<FIRRTLBaseType>(destType),
2757 auto srcWidth = type_cast<IntegerType>(result.getType()).getWidth();
2758 if (srcWidth ==
unsigned(destWidth))
2764 if (srcWidth >
unsigned(destWidth)) {
2765 auto resultType = builder.getIntegerType(destWidth);
2769 auto resultType = builder.getIntegerType(destWidth);
2773 type_cast<FIRRTLBaseType>(value.getType()).getPassiveType();
2774 if (type_cast<IntType>(valueFIRType).isSigned())
2775 return comb::createOrFoldSExt(builder, result, resultType);
2777 auto zero = getOrCreateIntConstant(destWidth - srcWidth, 0);
2791std::optional<Value> FIRRTLLowering::getLoweredFmtOperand(Value operand) {
2793 if (type_isa<FStringType>(operand.getType())) {
2794 if (isa<TimeOp>(operand.getDefiningOp()))
2795 return sv::TimeOp::create(builder);
2796 if (isa<HierarchicalModuleNameOp>(operand.getDefiningOp()))
2800 auto loweredValue = getLoweredValue(operand);
2801 if (!loweredValue) {
2805 loweredValue = getOrCreateIntConstant(1, 0);
2810 if (
auto intTy = firrtl::type_cast<IntType>(operand.getType()))
2811 if (intTy.isSigned())
2812 loweredValue = sv::SystemFunctionOp::create(
2813 builder, loweredValue.getType(),
"signed", loweredValue);
2815 return loweredValue;
2819FIRRTLLowering::loweredFmtOperands(mlir::ValueRange operands,
2820 SmallVectorImpl<Value> &loweredOperands) {
2821 for (
auto operand : operands) {
2822 std::optional<Value> loweredValue = getLoweredFmtOperand(operand);
2827 loweredOperands.push_back(*loweredValue);
2832LogicalResult FIRRTLLowering::lowerStatementWithFd(
2833 const FileDescriptorInfo &fileDescriptor, Value clock, Value cond,
2834 const std::function<LogicalResult(Value)> &fn,
bool usePrintfCond) {
2836 bool failed =
false;
2837 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
2838 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
2839 addToAlwaysBlock(clock, [&]() {
2842 circuitState.usedPrintf =
true;
2844 circuitState.addFragment(theModule,
"PRINTF_COND_FRAGMENT");
2847 Value ifCond = cond;
2848 if (usePrintfCond) {
2850 sv::MacroRefExprOp::create(builder, cond.getType(),
"PRINTF_COND_");
2851 ifCond = builder.createOrFold<
comb::AndOp>(ifCond, cond,
true);
2854 addIfProceduralBlock(ifCond, [&]() {
2858 if (fileDescriptor.isDefaultFd()) {
2860 fd = hw::ConstantOp::create(builder, APInt(32, 0x80000002));
2863 auto fdOrError = callFileDescriptorLib(fileDescriptor);
2864 if (llvm::failed(fdOrError)) {
2870 failed = llvm::failed(fn(fd));
2874 return failure(failed);
2878FIRRTLLowering::callFileDescriptorLib(
const FileDescriptorInfo &info) {
2879 circuitState.usedFileDescriptorLib =
true;
2880 circuitState.addFragment(theModule,
"CIRCT_LIB_LOGGING_FRAGMENT");
2883 if (
info.isSubstitutionRequired()) {
2884 SmallVector<Value> fileNameOperands;
2885 if (failed(loweredFmtOperands(
info.getSubstitutions(), fileNameOperands)))
2888 fileName = sv::SFormatFOp::create(builder,
info.getOutputFileFormat(),
2893 fileName = sv::ConstantStrOp::create(builder,
info.getOutputFileFormat())
2897 return sv::FuncCallProceduralOp::create(
2898 builder, mlir::TypeRange{builder.getIntegerType(32)},
2899 builder.getStringAttr(
"__circt_lib_logging::FileDescriptor::get"),
2900 ValueRange{fileName})
2910LogicalResult FIRRTLLowering::setLowering(Value orig, Value result) {
2911 if (
auto origType = dyn_cast<FIRRTLType>(orig.getType())) {
2912 assert((!result || !type_isa<FIRRTLType>(result.getType())) &&
2913 "Lowering didn't turn a FIRRTL value into a non-FIRRTL value");
2917 auto srcWidth = baseType.getPassiveType().getBitWidthOrSentinel();
2920 if (srcWidth != -1) {
2922 assert((srcWidth != 0) &&
2923 "Lowering produced value for zero width source");
2925 assert((srcWidth == 0) &&
2926 "Lowering produced null value but source wasn't zero width");
2930 assert(result &&
"Lowering of foreign type produced null value");
2933 auto &slot = valueMapping[orig];
2934 assert(!slot &&
"value lowered multiple times");
2941LogicalResult FIRRTLLowering::setPossiblyFoldedLowering(Value orig,
2945 if (
auto cst = dyn_cast_or_null<hw::ConstantOp>(result.getDefiningOp())) {
2946 auto &entry = hwConstantMap[cst.getValueAttr()];
2957 cst->moveBefore(&theModule.getBodyBlock()->front());
2961 return setLowering(orig, result);
2966template <
typename ResultOpType,
typename... CtorArgTypes>
2967LogicalResult FIRRTLLowering::setLoweringTo(Operation *orig,
2968 CtorArgTypes... args) {
2969 auto result = builder.createOrFold<ResultOpType>(args...);
2970 if (
auto *op = result.getDefiningOp())
2972 return setPossiblyFoldedLowering(orig->getResult(0), result);
2979template <
typename ResultOpType,
typename... CtorArgTypes>
2980LogicalResult FIRRTLLowering::setLoweringToLTL(Operation *orig,
2981 CtorArgTypes... args) {
2982 auto result = builder.createOrFold<ResultOpType>(args...);
2983 if (
auto *op = result.getDefiningOp())
2984 ltlOpFixupWorklist.insert(op);
2985 return setPossiblyFoldedLowering(orig->getResult(0), result);
2994Backedge FIRRTLLowering::createBackedge(Location loc, Type type) {
2995 auto backedge = backedgeBuilder.
get(type, loc);
2996 backedges.insert({backedge, backedge});
3004Backedge FIRRTLLowering::createBackedge(Value orig, Type type) {
3005 auto backedge = createBackedge(orig.getLoc(), type);
3006 (void)setLowering(orig, backedge);
3012bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
3013 auto backedgeIt = backedges.find(dest);
3014 if (backedgeIt == backedges.end())
3016 backedgeIt->second = src;
3024void FIRRTLLowering::runWithInsertionPointAtEndOfBlock(
3025 const std::function<
void(
void)> &fn, Region ®ion) {
3029 auto oldIP = builder.saveInsertionPoint();
3031 builder.setInsertionPointToEnd(®ion.front());
3033 builder.restoreInsertionPoint(oldIP);
3037Value FIRRTLLowering::getReadValue(Value v) {
3038 Value result = readInOutCreated.lookup(v);
3044 auto oldIP = builder.saveInsertionPoint();
3045 if (
auto *vOp = v.getDefiningOp()) {
3046 builder.setInsertionPointAfter(vOp);
3050 builder.setInsertionPoint(&theModule.getBodyBlock()->front());
3055 if (
auto arrayIndexInout = v.getDefiningOp<sv::ArrayIndexInOutOp>()) {
3056 result = getReadValue(arrayIndexInout.getInput());
3058 arrayIndexInout.getIndex());
3063 builder.restoreInsertionPoint(oldIP);
3064 readInOutCreated.insert({v, result});
3068Value FIRRTLLowering::getNonClockValue(Value v) {
3069 auto it = fromClockMapping.try_emplace(v, Value{});
3071 ImplicitLocOpBuilder builder(v.getLoc(), v.getContext());
3072 builder.setInsertionPointAfterValue(v);
3073 it.first->second = seq::FromClockOp::create(builder, v);
3075 return it.first->second;
3078void FIRRTLLowering::addToAlwaysBlock(
3079 sv::EventControl clockEdge, Value clock, sv::ResetType resetStyle,
3080 sv::EventControl resetEdge, Value reset,
3081 const std::function<
void(
void)> &body,
3082 const std::function<
void(
void)> &resetBody) {
3083 AlwaysKeyType key{builder.getBlock(), clockEdge, clock,
3084 resetStyle, resetEdge, reset};
3085 sv::AlwaysOp alwaysOp;
3086 sv::IfOp insideIfOp;
3087 std::tie(alwaysOp, insideIfOp) = alwaysBlocks.lookup(key);
3091 assert(resetStyle != sv::ResetType::NoReset);
3104 auto createIfOp = [&]() {
3107 insideIfOp = sv::IfOp::create(
3108 builder, reset, [] {}, [] {});
3110 if (resetStyle == sv::ResetType::AsyncReset) {
3111 sv::EventControl events[] = {clockEdge, resetEdge};
3112 Value clocks[] = {clock, reset};
3114 alwaysOp = sv::AlwaysOp::create(builder, events, clocks, [&]() {
3115 if (resetEdge == sv::EventControl::AtNegEdge)
3116 llvm_unreachable(
"negative edge for reset is not expected");
3120 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock, createIfOp);
3124 alwaysOp = sv::AlwaysOp::create(builder, clockEdge, clock);
3125 insideIfOp =
nullptr;
3127 alwaysBlocks[key] = {alwaysOp, insideIfOp};
3131 assert(insideIfOp &&
"reset body must be initialized before");
3132 runWithInsertionPointAtEndOfBlock(resetBody, insideIfOp.getThenRegion());
3133 runWithInsertionPointAtEndOfBlock(body, insideIfOp.getElseRegion());
3135 runWithInsertionPointAtEndOfBlock(body, alwaysOp.getBody());
3141 alwaysOp->moveBefore(builder.getInsertionBlock(),
3142 builder.getInsertionPoint());
3145LogicalResult FIRRTLLowering::emitGuards(Location loc,
3146 ArrayRef<Attribute> guards,
3147 std::function<
void(
void)>
emit) {
3148 if (guards.empty()) {
3152 auto guard = dyn_cast<StringAttr>(guards[0]);
3154 return mlir::emitError(loc,
3155 "elements in `guards` array must be `StringAttr`");
3158 circuitState.addMacroDecl(builder.getStringAttr(guard.getValue()));
3159 LogicalResult result = LogicalResult::failure();
3160 addToIfDefBlock(guard.getValue(), [&]() {
3161 result = emitGuards(loc, guards.drop_front(), emit);
3166void FIRRTLLowering::addToIfDefBlock(StringRef cond,
3167 std::function<
void(
void)> thenCtor,
3168 std::function<
void(
void)> elseCtor) {
3169 auto condAttr = builder.getStringAttr(cond);
3170 auto op = ifdefBlocks.lookup({builder.getBlock(), condAttr});
3172 runWithInsertionPointAtEndOfBlock(thenCtor, op.getThenRegion());
3173 runWithInsertionPointAtEndOfBlock(elseCtor, op.getElseRegion());
3178 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3180 ifdefBlocks[{builder.getBlock(), condAttr}] =
3181 sv::IfDefOp::create(builder, condAttr, thenCtor, elseCtor);
3185void FIRRTLLowering::addToInitialBlock(std::function<
void(
void)> body) {
3186 auto op = initialBlocks.lookup(builder.getBlock());
3188 runWithInsertionPointAtEndOfBlock(body, op.getBody());
3193 op->moveBefore(builder.getInsertionBlock(), builder.getInsertionPoint());
3195 initialBlocks[builder.getBlock()] = sv::InitialOp::create(builder, body);
3199void FIRRTLLowering::addIfProceduralBlock(Value cond,
3200 std::function<
void(
void)> thenCtor,
3201 std::function<
void(
void)> elseCtor) {
3204 auto insertIt = builder.getInsertionPoint();
3205 if (insertIt != builder.getBlock()->begin())
3206 if (
auto ifOp = dyn_cast<sv::IfOp>(*--insertIt)) {
3207 if (ifOp.getCond() == cond) {
3208 runWithInsertionPointAtEndOfBlock(thenCtor, ifOp.getThenRegion());
3209 runWithInsertionPointAtEndOfBlock(elseCtor, ifOp.getElseRegion());
3214 sv::IfOp::create(builder, cond, thenCtor, elseCtor);
3226FIRRTLLowering::UnloweredOpResult
3227FIRRTLLowering::handleUnloweredOp(Operation *op) {
3229 if (!op->getRegions().empty() &&
3230 isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3231 op->emitOpError(
"must explicitly handle its regions");
3232 return LoweringFailure;
3239 if (!isa_and_nonnull<FIRRTLDialect>(op->getDialect())) {
3241 for (
auto ®ion : op->getRegions())
3242 addToWorklist(region);
3243 for (
auto &operand : op->getOpOperands())
3244 if (auto lowered = getPossiblyInoutLoweredValue(operand.
get()))
3245 operand.set(lowered);
3246 for (
auto result : op->getResults())
3247 (void)setLowering(result, result);
3248 return AlreadyLowered;
3260 if (op->getNumResults() == 1) {
3261 auto resultType = op->getResult(0).getType();
3262 if (type_isa<FIRRTLBaseType>(resultType) &&
3264 (
isExpression(op) || isa<mlir::UnrealizedConversionCastOp>(op))) {
3266 (void)setLowering(op->getResult(0), Value());
3270 op->emitOpError(
"LowerToHW couldn't handle this operation");
3271 return LoweringFailure;
3274LogicalResult FIRRTLLowering::visitExpr(ConstantOp op) {
3277 return setLowering(op, Value());
3279 return setLowering(op, getOrCreateIntConstant(op.getValue()));
3282LogicalResult FIRRTLLowering::visitExpr(SpecialConstantOp op) {
3284 if (isa<ClockType>(op.getType())) {
3285 cst = getOrCreateClockConstant(op.getValue() ? seq::ClockConst::High
3286 :
seq::ClockConst::Low);
3288 cst = getOrCreateIntConstant(APInt( 1, op.getValue()));
3290 return setLowering(op, cst);
3293FailureOr<Value> FIRRTLLowering::lowerSubindex(SubindexOp op, Value input) {
3294 auto iIdx = getOrCreateIntConstant(
3296 firrtl::type_cast<FVectorType>(op.getInput().getType())
3303 if (isa<sv::InOutType>(input.getType()))
3304 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, iIdx);
3307 if (
auto *definingOp = result.getDefiningOp())
3312FailureOr<Value> FIRRTLLowering::lowerSubaccess(SubaccessOp op, Value input) {
3313 Value valueIdx = getLoweredAndExtOrTruncValue(
3315 UIntType::get(op->getContext(),
3317 firrtl::type_cast<FVectorType>(op.getInput().getType())
3318 .getNumElements())));
3320 op->emitError() <<
"input lowering failed";
3327 if (isa<sv::InOutType>(input.getType()))
3328 result = builder.createOrFold<sv::ArrayIndexInOutOp>(input, valueIdx);
3330 result = createArrayIndexing(input, valueIdx);
3331 if (
auto *definingOp = result.getDefiningOp())
3336FailureOr<Value> FIRRTLLowering::lowerSubfield(SubfieldOp op, Value input) {
3337 auto resultType =
lowerType(op->getResult(0).getType());
3338 if (!resultType || !input) {
3339 op->emitError() <<
"subfield type lowering failed";
3345 auto field = firrtl::type_cast<BundleType>(op.getInput().getType())
3346 .getElementName(op.getFieldIndex());
3348 if (isa<sv::InOutType>(input.getType()))
3349 result = builder.createOrFold<sv::StructFieldInOutOp>(input, field);
3352 if (
auto *definingOp = result.getDefiningOp())
3357LogicalResult FIRRTLLowering::visitExpr(SubindexOp op) {
3359 return setLowering(op, Value());
3361 auto input = getPossiblyInoutLoweredValue(op.getInput());
3363 return op.emitError() <<
"input lowering failed";
3365 auto result = lowerSubindex(op, input);
3368 return setLowering(op, *result);
3371LogicalResult FIRRTLLowering::visitExpr(SubaccessOp op) {
3373 return setLowering(op, Value());
3375 auto input = getPossiblyInoutLoweredValue(op.getInput());
3377 return op.emitError() <<
"input lowering failed";
3379 auto result = lowerSubaccess(op, input);
3382 return setLowering(op, *result);
3385LogicalResult FIRRTLLowering::visitExpr(SubfieldOp op) {
3388 if (getLoweredValue(op) || !op.getInput())
3392 return setLowering(op, Value());
3394 auto input = getPossiblyInoutLoweredValue(op.getInput());
3396 return op.emitError() <<
"input lowering failed";
3398 auto result = lowerSubfield(op, input);
3401 return setLowering(op, *result);
3404LogicalResult FIRRTLLowering::visitExpr(VectorCreateOp op) {
3405 auto resultType =
lowerType(op.getResult().getType());
3406 SmallVector<Value> operands;
3408 for (
auto oper :
llvm::reverse(op.getOperands())) {
3409 auto val = getLoweredValue(oper);
3412 operands.push_back(val);
3414 return setLoweringTo<hw::ArrayCreateOp>(op, resultType, operands);
3417LogicalResult FIRRTLLowering::visitExpr(BundleCreateOp op) {
3418 auto resultType =
lowerType(op.getResult().getType());
3419 SmallVector<Value> operands;
3420 for (
auto oper : op.getOperands()) {
3421 auto val = getLoweredValue(oper);
3424 operands.push_back(val);
3426 return setLoweringTo<hw::StructCreateOp>(op, resultType, operands);
3429LogicalResult FIRRTLLowering::visitExpr(FEnumCreateOp op) {
3432 return setLowering(op, Value());
3434 auto input = getLoweredValue(op.getInput());
3435 auto tagName = op.getFieldNameAttr();
3436 auto oldType = op.getType().base();
3438 auto element = *oldType.getElement(op.getFieldNameAttr());
3440 if (
auto structType = dyn_cast<hw::StructType>(newType)) {
3441 auto tagType = structType.getFieldType(
"tag");
3442 auto tagValue = IntegerAttr::get(tagType, element.value.getValue());
3443 auto tag = sv::LocalParamOp::create(builder, op.getLoc(), tagType, tagValue,
3445 auto bodyType = structType.getFieldType(
"body");
3446 auto body = hw::UnionCreateOp::create(builder, bodyType, tagName, input);
3447 SmallVector<Value> operands = {tag.getResult(), body.getResult()};
3448 return setLoweringTo<hw::StructCreateOp>(op, structType, operands);
3450 auto tagValue = IntegerAttr::get(newType, element.value.getValue());
3451 return setLoweringTo<sv::LocalParamOp>(op, newType, tagValue, tagName);
3454LogicalResult FIRRTLLowering::visitExpr(AggregateConstantOp op) {
3455 auto resultType =
lowerType(op.getResult().getType());
3457 getOrCreateAggregateConstantAttribute(op.getFieldsAttr(), resultType);
3459 return setLoweringTo<hw::AggregateConstantOp>(op, resultType,
3460 cast<ArrayAttr>(attr));
3463LogicalResult FIRRTLLowering::visitExpr(IsTagOp op) {
3465 auto tagName = op.getFieldNameAttr();
3466 auto lhs = getLoweredValue(op.getInput());
3467 if (isa<hw::StructType>(lhs.getType()))
3470 auto index = op.getFieldIndex();
3471 auto enumType = op.getInput().getType().base();
3472 auto tagValue = enumType.getElementValueAttr(index);
3473 auto tagValueType = IntegerType::get(op.getContext(), enumType.getTagWidth());
3474 auto loweredTagValue = IntegerAttr::get(tagValueType, tagValue.getValue());
3475 auto rhs = sv::LocalParamOp::create(builder, op.getLoc(), tagValueType,
3476 loweredTagValue, tagName);
3478 Type resultType = builder.getIntegerType(1);
3479 return setLoweringTo<comb::ICmpOp>(op, resultType, ICmpPredicate::eq, lhs,
3483LogicalResult FIRRTLLowering::visitExpr(SubtagOp op) {
3486 return setLowering(op, Value());
3488 auto tagName = op.getFieldNameAttr();
3489 auto input = getLoweredValue(op.getInput());
3491 return setLoweringTo<hw::UnionExtractOp>(op, field, tagName);
3494LogicalResult FIRRTLLowering::visitExpr(TagExtractOp op) {
3497 return setLowering(op, Value());
3499 auto input = getLoweredValue(op.getInput());
3505 if (isa<hw::StructType>(input.getType())) {
3506 return setLoweringTo<hw::StructExtractOp>(op, input,
"tag");
3511 return setLowering(op, input);
3518LogicalResult FIRRTLLowering::visitDecl(WireOp op) {
3519 auto origResultType = op.getResult().getType();
3523 if (!type_isa<FIRRTLType>(origResultType)) {
3524 createBackedge(op.getResult(), origResultType);
3528 auto resultType =
lowerType(origResultType);
3532 if (resultType.isInteger(0)) {
3533 if (op.getInnerSym())
3534 return op.emitError(
"zero width wire is referenced by name [")
3535 << *op.getInnerSym() <<
"] (e.g. in an XMR) but must be removed";
3536 return setLowering(op.getResult(), Value());
3540 auto innerSym = lowerInnerSymbol(op);
3541 auto name = op.getNameAttr();
3544 auto wire = hw::WireOp::create(
3545 builder, op.getLoc(), getOrCreateZConstant(resultType), name, innerSym);
3547 if (
auto svAttrs = sv::getSVAttributes(op))
3548 sv::setSVAttributes(wire, svAttrs);
3550 return setLowering(op.getResult(), wire);
3553LogicalResult FIRRTLLowering::visitDecl(VerbatimWireOp op) {
3554 auto resultTy =
lowerType(op.getType());
3557 resultTy = sv::InOutType::get(op.getContext(), resultTy);
3559 SmallVector<Value, 4> operands;
3560 operands.reserve(op.getSubstitutions().size());
3561 for (
auto operand : op.getSubstitutions()) {
3562 auto lowered = getLoweredValue(operand);
3565 operands.push_back(lowered);
3568 ArrayAttr symbols = op.getSymbolsAttr();
3570 symbols = ArrayAttr::get(op.getContext(), {});
3572 return setLoweringTo<sv::VerbatimExprSEOp>(op, resultTy, op.getTextAttr(),
3576LogicalResult FIRRTLLowering::visitDecl(NodeOp op) {
3577 auto operand = getLoweredValue(op.getInput());
3579 return handleZeroBit(op.getInput(), [&]() -> LogicalResult {
3580 if (op.getInnerSym())
3581 return op.emitError(
"zero width node is referenced by name [")
3582 << *op.getInnerSym()
3583 <<
"] (e.g. in an XMR) but must be "
3585 return setLowering(op.getResult(), Value());
3591 auto name = op.getNameAttr();
3592 auto innerSym = lowerInnerSymbol(op);
3595 operand = hw::WireOp::create(builder, operand, name, innerSym);
3598 if (
auto svAttrs = sv::getSVAttributes(op)) {
3600 operand = hw::WireOp::create(builder, operand, name);
3601 sv::setSVAttributes(operand.getDefiningOp(), svAttrs);
3604 return setLowering(op.getResult(), operand);
3607LogicalResult FIRRTLLowering::visitDecl(RegOp op) {
3608 auto resultType =
lowerType(op.getResult().getType());
3611 if (resultType.isInteger(0))
3612 return setLowering(op.getResult(), Value());
3614 Value clockVal = getLoweredValue(op.getClockVal());
3619 auto innerSym = lowerInnerSymbol(op);
3620 Backedge inputEdge = backedgeBuilder.
get(resultType);
3621 auto reg = seq::FirRegOp::create(builder, inputEdge, clockVal,
3622 op.getNameAttr(), innerSym);
3625 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3626 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3627 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3628 reg->setAttr(
"firrtl.random_init_start", randomStart);
3629 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3630 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3633 if (
auto svAttrs = sv::getSVAttributes(op))
3634 sv::setSVAttributes(reg, svAttrs);
3637 (void)setLowering(op.getResult(),
reg);
3641LogicalResult FIRRTLLowering::visitDecl(RegResetOp op) {
3642 auto resultType =
lowerType(op.getResult().getType());
3645 if (resultType.isInteger(0))
3646 return setLowering(op.getResult(), Value());
3648 Value clockVal = getLoweredValue(op.getClockVal());
3649 Value resetSignal = getLoweredValue(op.getResetSignal());
3651 Value resetValue = getLoweredAndExtOrTruncValue(
3652 op.getResetValue(), type_cast<FIRRTLBaseType>(op.getResult().getType()));
3654 if (!clockVal || !resetSignal || !resetValue)
3658 auto innerSym = lowerInnerSymbol(op);
3659 bool isAsync = type_isa<AsyncResetType>(op.getResetSignal().getType());
3660 Backedge inputEdge = backedgeBuilder.
get(resultType);
3662 seq::FirRegOp::create(builder, inputEdge, clockVal, op.getNameAttr(),
3663 resetSignal, resetValue, innerSym, isAsync);
3666 if (
auto randomRegister = op->getAttr(
"firrtl.random_init_register"))
3667 reg->setAttr(
"firrtl.random_init_register", randomRegister);
3668 if (
auto randomStart = op->getAttr(
"firrtl.random_init_start"))
3669 reg->setAttr(
"firrtl.random_init_start", randomStart);
3670 if (
auto randomEnd = op->getAttr(
"firrtl.random_init_end"))
3671 reg->setAttr(
"firrtl.random_init_end", randomEnd);
3674 if (
auto svAttrs = sv::getSVAttributes(op))
3675 sv::setSVAttributes(reg, svAttrs);
3678 (void)setLowering(op.getResult(),
reg);
3683LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
3686 if (type_isa<BundleType>(op.getDataType()))
3687 return op.emitOpError(
3688 "should have already been lowered from a ground type to an aggregate "
3689 "type using the LowerTypes pass. Use "
3690 "'firtool --lower-types' or 'circt-opt "
3691 "--pass-pipeline='firrtl.circuit(firrtl-lower-types)' "
3697 auto memType = seq::FirMemType::get(
3700 : std::optional<uint32_t>());
3702 seq::FirMemInitAttr memInit;
3703 if (
auto init = op.getInitAttr())
3704 memInit = seq::FirMemInitAttr::get(init.getContext(), init.getFilename(),
3705 init.getIsBinary(), init.getIsInline());
3707 auto memDecl = seq::FirMemOp::create(
3710 op.getInnerSymAttr(), memInit, op.getPrefixAttr(), Attribute{});
3713 if (
auto file = parent->getAttrOfType<hw::OutputFileAttr>(
"output_file")) {
3715 if (!file.isDirectory())
3716 dir = hw::OutputFileAttr::getAsDirectory(builder.getContext(),
3717 file.getDirectory());
3718 memDecl.setOutputFileAttr(dir);
3724 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
3726 auto addOutput = [&](StringRef field,
size_t width, Value value) {
3729 (void)setLowering(a, value);
3735 auto addInput = [&](StringRef field, Value backedge) {
3737 if (cast<FIRRTLBaseType>(
a.getType())
3739 .getBitWidthOrSentinel() > 0)
3740 (void)setLowering(a, backedge);
3746 auto addInputPort = [&](StringRef field,
size_t width) -> Value {
3750 Value backedge, portValue;
3752 portValue = getOrCreateXConstant(1);
3754 auto portType = IntegerType::get(op.getContext(), width);
3755 backedge = portValue = createBackedge(builder.getLoc(), portType);
3757 addInput(field, backedge);
3761 auto addClock = [&](StringRef field) -> Value {
3762 Type clockTy = seq::ClockType::get(op.getContext());
3763 Value portValue = createBackedge(builder.getLoc(), clockTy);
3764 addInput(field, portValue);
3768 auto memportKind = op.getPortKind(i);
3769 if (memportKind == MemOp::PortKind::Read) {
3770 auto addr = addInputPort(
"addr", op.getAddrBits());
3771 auto en = addInputPort(
"en", 1);
3772 auto clk = addClock(
"clk");
3773 auto data = seq::FirMemReadOp::create(builder, memDecl,
addr,
clk,
en);
3775 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
3776 auto addr = addInputPort(
"addr", op.getAddrBits());
3777 auto en = addInputPort(
"en", 1);
3778 auto clk = addClock(
"clk");
3781 auto mode = addInputPort(
"wmode", 1);
3783 mode = builder.createOrFold<
comb::AndOp>(mode, addInputPort(
"wmask", 1),
3790 auto rdata = seq::FirMemReadWriteOp::create(builder, memDecl,
addr,
clk,
3794 auto addr = addInputPort(
"addr", op.getAddrBits());
3797 auto en = addInputPort(
"en", 1);
3801 auto clk = addClock(
"clk");
3815FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
3816 Operation *instanceOp,
3817 SmallVectorImpl<Value> &inputOperands) {
3819 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3820 auto &port = portInfo[portIndex];
3823 instanceOp->emitOpError(
"could not lower type of port ") << port.name;
3828 if (portType.isInteger(0))
3832 if (port.isOutput())
3835 auto portResult = instanceOp->getResult(portIndex);
3836 assert(portResult &&
"invalid IR, couldn't find port");
3840 if (port.isInput()) {
3841 inputOperands.push_back(createBackedge(portResult, portType));
3847 if (type_isa<AnalogType>(portResult.getType()) && portResult.hasOneUse()) {
3848 if (
auto attach = dyn_cast<AttachOp>(*portResult.getUsers().begin())) {
3850 auto loweredResult = getPossiblyInoutLoweredValue(source);
3851 inputOperands.push_back(loweredResult);
3852 (void)setLowering(portResult, loweredResult);
3861 "." + port.getName().str() +
".wire");
3865 (void)setLowering(portResult, wire);
3866 inputOperands.push_back(wire);
3872LogicalResult FIRRTLLowering::visitDecl(InstanceOp oldInstance) {
3873 Operation *oldModule =
3874 oldInstance.getReferencedModule(circuitState.getInstanceGraph());
3876 auto *newModule = circuitState.getNewModule(oldModule);
3878 oldInstance->emitOpError(
"could not find module [")
3879 << oldInstance.getModuleName() <<
"] referenced by instance";
3885 ArrayAttr parameters;
3886 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldModule))
3891 SmallVector<PortInfo, 8> portInfo = cast<FModuleLike>(oldModule).getPorts();
3895 SmallVector<Value, 8> operands;
3896 if (failed(prepareInstanceOperands(portInfo, oldInstance, operands)))
3903 auto innerSym = oldInstance.getInnerSymAttr();
3904 if (oldInstance.getLowerToBind()) {
3907 oldInstance.getContext(), oldInstance.getInnerSymAttr(), 0,
3910 auto bindOp = sv::BindOp::create(builder, theModule.getNameAttr(),
3911 innerSym.getSymName());
3914 if (
auto outputFile = oldInstance->getAttr(
"output_file"))
3915 bindOp->setAttr(
"output_file", outputFile);
3918 circuitState.addBind(bindOp);
3923 hw::InstanceOp::create(builder, newModule, oldInstance.getNameAttr(),
3924 operands, parameters, innerSym);
3926 if (oldInstance.getLowerToBind() || oldInstance.getDoNotPrint())
3927 newInstance.setDoNotPrintAttr(builder.getUnitAttr());
3929 if (newInstance.getInnerSymAttr())
3930 if (
auto forceName = circuitState.instanceForceNames.lookup(
3931 {newInstance->getParentOfType<hw::HWModuleOp>().getNameAttr(),
3932 newInstance.getInnerNameAttr()}))
3933 newInstance->setAttr(
"hw.verilogName", forceName);
3937 unsigned resultNo = 0;
3938 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3939 auto &port = portInfo[portIndex];
3943 Value resultVal = newInstance.getResult(resultNo);
3945 auto oldPortResult = oldInstance.getResult(portIndex);
3946 (void)setLowering(oldPortResult, resultVal);
3952LogicalResult FIRRTLLowering::visitDecl(InstanceChoiceOp oldInstanceChoice) {
3953 if (oldInstanceChoice.getInnerSymAttr()) {
3954 oldInstanceChoice->emitOpError(
3955 "instance choice with inner sym cannot be lowered");
3960 FlatSymbolRefAttr instanceMacro = oldInstanceChoice.getInstanceMacroAttr();
3962 return oldInstanceChoice->emitOpError(
3963 "must have instance_macro attribute set before "
3967 auto moduleNames = oldInstanceChoice.getModuleNamesAttr();
3968 auto caseNames = oldInstanceChoice.getCaseNamesAttr();
3971 auto defaultModuleName = oldInstanceChoice.getDefaultTargetAttr();
3972 auto *defaultModuleNode =
3973 circuitState.getInstanceGraph().lookup(defaultModuleName.getAttr());
3975 Operation *defaultModule = defaultModuleNode->getModule();
3979 SmallVector<PortInfo, 8> portInfo =
3980 cast<FModuleLike>(defaultModule).getPorts();
3983 SmallVector<Value, 8> inputOperands;
3985 prepareInstanceOperands(portInfo, oldInstanceChoice, inputOperands)))
3989 SmallVector<sv::WireOp, 8> outputWires;
3990 StringRef wirePrefix = oldInstanceChoice.getInstanceName();
3991 for (
size_t portIndex = 0, e = portInfo.size(); portIndex != e; ++portIndex) {
3992 auto &port = portInfo[portIndex];
3996 if (!portType || portType.isInteger(0))
3999 builder, portType, wirePrefix.str() +
"." + port.getName().str());
4000 outputWires.push_back(wire);
4001 if (failed(setLowering(oldInstanceChoice.getResult(portIndex), wire)))
4005 auto optionName = oldInstanceChoice.getOptionNameAttr();
4008 auto createInstanceAndAssign = [&](Operation *oldMod,
4009 StringRef suffix) -> hw::InstanceOp {
4010 auto *newMod = circuitState.getNewModule(oldMod);
4012 ArrayAttr parameters;
4013 if (
auto oldExtModule = dyn_cast<FExtModuleOp>(oldMod))
4017 SmallString<64> instName;
4018 instName = oldInstanceChoice.getInstanceName();
4019 if (!suffix.empty()) {
4025 hw::InstanceOp::create(builder, newMod, builder.getStringAttr(instName),
4026 inputOperands, parameters,
nullptr);
4032 for (
unsigned i = 0; i < inst.getNumResults(); ++i)
4039 SmallVector<StringAttr> macroNames;
4040 SmallVector<Operation *> altModules;
4041 for (
size_t i = 0, e = caseNames.size(); i < e; ++i) {
4042 altModules.push_back(
4043 circuitState.getInstanceGraph()
4044 .lookup(cast<FlatSymbolRefAttr>(moduleNames[i + 1]).getAttr())
4048 auto optionCaseMacroRef = circuitState.macroTable.getMacro(
4049 optionName, cast<SymbolRefAttr>(caseNames[i]).getLeafReference());
4050 if (!optionCaseMacroRef)
4051 return oldInstanceChoice->emitOpError(
4052 "failed to get macro for option case");
4053 macroNames.push_back(optionCaseMacroRef.getAttr());
4057 sv::createNestedIfDefs(
4060 [&](StringRef macro, std::function<
void()> thenCtor,
4061 std::function<
void()> elseCtor) {
4062 addToIfDefBlock(macro, std::move(thenCtor), std::move(elseCtor));
4066 for (
size_t i = index + 1; i < macroNames.size(); ++i) {
4067 sv::IfDefOp::create(
4068 builder, oldInstanceChoice.getLoc(), macroNames[i],
4070 SmallString<256> errorMessage;
4071 llvm::raw_svector_ostream os(errorMessage);
4072 os <<
"Multiple instance choice options defined for option '"
4073 << optionName.getValue() <<
"': '"
4074 << macroNames[index].getValue() <<
"' and '"
4075 << macroNames[i].getValue() <<
"'";
4076 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4077 builder.getStringAttr(errorMessage));
4083 cast<SymbolRefAttr>(caseNames[index]).getLeafReference();
4085 createInstanceAndAssign(altModules[index], caseSymRef.getValue());
4087 sv::MacroDefOp::create(builder, inst.getLoc(), instanceMacro,
4088 builder.getStringAttr(
"{{0}}"),
4089 builder.getArrayAttr({hw::InnerRefAttr::get(
4090 theModule.getNameAttr(),
4091 inst.getInnerSymAttr().getSymName())}));
4095 SmallString<256> errorMessage;
4096 llvm::raw_svector_ostream os(errorMessage);
4097 os <<
"Required instance choice option '" << optionName.getValue()
4098 <<
"' not selected, must define one of: ";
4099 llvm::interleaveComma(macroNames, os, [&](StringAttr macro) {
4100 os <<
"'" << macro.getValue() <<
"'";
4102 sv::ErrorOp::create(builder, oldInstanceChoice.getLoc(),
4103 builder.getStringAttr(errorMessage));
4109LogicalResult FIRRTLLowering::visitDecl(ContractOp oldOp) {
4110 SmallVector<Value> inputs;
4111 SmallVector<Type> types;
4112 for (
auto input : oldOp.getInputs()) {
4113 auto lowered = getLoweredValue(input);
4116 inputs.push_back(lowered);
4117 types.push_back(lowered.getType());
4120 auto newOp = verif::ContractOp::create(builder, types, inputs);
4121 newOp->setDiscardableAttrs(oldOp->getDiscardableAttrDictionary());
4122 auto &body = newOp.getBody().emplaceBlock();
4124 for (
auto [newResult, oldResult, oldArg] :
4125 llvm::zip(newOp.getResults(), oldOp.getResults(),
4126 oldOp.getBody().getArguments())) {
4127 if (failed(setLowering(oldResult, newResult)))
4129 if (failed(setLowering(oldArg, newResult)))
4133 body.getOperations().splice(body.end(),
4134 oldOp.getBody().front().getOperations());
4135 addToWorklist(body);
4145LogicalResult FIRRTLLowering::lowerNoopCast(Operation *op) {
4146 auto operand = getPossiblyInoutLoweredValue(op->getOperand(0));
4151 return setLowering(op->getResult(0), operand);
4154LogicalResult FIRRTLLowering::visitExpr(AsSIntPrimOp op) {
4155 if (isa<ClockType>(op.getInput().getType()))
4156 return setLowering(op->getResult(0),
4157 getLoweredNonClockValue(op.getInput()));
4158 return lowerNoopCast(op);
4161LogicalResult FIRRTLLowering::visitExpr(AsUIntPrimOp op) {
4162 if (isa<ClockType>(op.getInput().getType()))
4163 return setLowering(op->getResult(0),
4164 getLoweredNonClockValue(op.getInput()));
4165 return lowerNoopCast(op);
4168LogicalResult FIRRTLLowering::visitExpr(AsClockPrimOp op) {
4169 return setLoweringTo<seq::ToClockOp>(op, getLoweredValue(op.getInput()));
4172LogicalResult FIRRTLLowering::visitUnrealizedConversionCast(
4173 mlir::UnrealizedConversionCastOp op) {
4175 if (op.getNumOperands() != 1 || op.getNumResults() != 1)
4178 auto operand = op.getOperand(0);
4179 auto result = op.getResult(0);
4182 if (type_isa<FIRRTLType>(operand.getType()) &&
4183 type_isa<FIRRTLType>(result.getType()))
4184 return lowerNoopCast(op);
4188 if (!type_isa<FIRRTLType>(operand.getType())) {
4189 if (type_isa<FIRRTLType>(result.getType()))
4190 return setLowering(result, getPossiblyInoutLoweredValue(operand));
4196 auto loweredResult = getLoweredValue(operand);
4197 if (!loweredResult) {
4200 if (operand.getType().isSignlessInteger(0)) {
4201 return setLowering(result, Value());
4208 result.replaceAllUsesWith(loweredResult);
4212LogicalResult FIRRTLLowering::visitExpr(HWStructCastOp op) {
4215 if (
auto opStructType = dyn_cast<hw::StructType>(op.getOperand().getType()))
4216 return setLowering(op, op.getOperand());
4220 auto result = getLoweredValue(op.getOperand());
4226 op.replaceAllUsesWith(result);
4230LogicalResult FIRRTLLowering::visitExpr(BitCastOp op) {
4231 auto operand = getLoweredValue(op.getOperand());
4234 auto resultType =
lowerType(op.getType());
4238 return setLoweringTo<hw::BitcastOp>(op, resultType, operand);
4241LogicalResult FIRRTLLowering::visitExpr(CvtPrimOp op) {
4242 auto operand = getLoweredValue(op.getOperand());
4246 if (type_cast<IntType>(op.getOperand().getType()).isUnsigned())
4247 return setLowering(op, getOrCreateIntConstant(1, 0));
4249 return setLowering(op, Value());
4254 if (type_cast<IntType>(op.getOperand().getType()).isSigned())
4255 return setLowering(op, operand);
4258 auto zero = getOrCreateIntConstant(1, 0);
4259 return setLoweringTo<comb::ConcatOp>(op, zero, operand);
4262LogicalResult FIRRTLLowering::visitExpr(NotPrimOp op) {
4263 auto operand = getLoweredValue(op.getInput());
4267 auto allOnes = getOrCreateIntConstant(
4268 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth()));
4269 return setLoweringTo<comb::XorOp>(op, operand, allOnes,
true);
4272LogicalResult FIRRTLLowering::visitExpr(NegPrimOp op) {
4275 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4279 auto resultType =
lowerType(op.getType());
4281 auto zero = getOrCreateIntConstant(resultType.getIntOrFloatBitWidth(), 0);
4282 return setLoweringTo<comb::SubOp>(op, zero, operand,
true);
4286LogicalResult FIRRTLLowering::visitExpr(PadPrimOp op) {
4287 auto operand = getLoweredAndExtendedValue(op.getInput(), op.getType());
4290 return setLowering(op, operand);
4293LogicalResult FIRRTLLowering::visitExpr(XorRPrimOp op) {
4294 auto operand = getLoweredValue(op.getInput());
4297 return setLowering(op, getOrCreateIntConstant(1, 0));
4302 return setLoweringTo<comb::ParityOp>(op, builder.getIntegerType(1), operand,
4306LogicalResult FIRRTLLowering::visitExpr(AndRPrimOp op) {
4307 auto operand = getLoweredValue(op.getInput());
4310 return setLowering(op, getOrCreateIntConstant(1, 1));
4315 return setLoweringTo<comb::ICmpOp>(
4316 op, ICmpPredicate::eq, operand,
4317 getOrCreateIntConstant(
4318 APInt::getAllOnes(operand.getType().getIntOrFloatBitWidth())),
4322LogicalResult FIRRTLLowering::visitExpr(OrRPrimOp op) {
4323 auto operand = getLoweredValue(op.getInput());
4326 return setLowering(op, getOrCreateIntConstant(1, 0));
4332 return setLoweringTo<comb::ICmpOp>(
4333 op, ICmpPredicate::ne, operand,
4334 getOrCreateIntConstant(operand.getType().getIntOrFloatBitWidth(), 0),
4342template <
typename ResultOpType>
4343LogicalResult FIRRTLLowering::lowerBinOpToVariadic(Operation *op) {
4344 auto resultType = op->getResult(0).getType();
4345 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4346 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4350 return setLoweringTo<ResultOpType>(op, lhs, rhs,
true);
4356template <
typename ResultOpType>
4357LogicalResult FIRRTLLowering::lowerElementwiseLogicalOp(Operation *op) {
4358 auto resultType = op->getResult(0).getType();
4359 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4360 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4371 auto intType = builder.getIntegerType(*bitwidth);
4372 auto retType = lhs.getType();
4375 auto result = builder.createOrFold<ResultOpType>(lhs, rhs,
true);
4376 return setLoweringTo<hw::BitcastOp>(op, retType, result);
4381template <
typename ResultUn
signedOpType,
typename ResultSignedOpType>
4382LogicalResult FIRRTLLowering::lowerBinOp(Operation *op) {
4384 auto resultType = op->getResult(0).getType();
4385 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4386 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4391 if (type_cast<IntType>(resultType).isSigned())
4392 return setLoweringTo<ResultSignedOpType>(op, lhs, rhs,
true);
4393 return setLoweringTo<ResultUnsignedOpType>(op, lhs, rhs,
true);
4398LogicalResult FIRRTLLowering::lowerCmpOp(Operation *op, ICmpPredicate signedOp,
4399 ICmpPredicate unsignedOp) {
4401 auto lhsIntType = type_cast<IntType>(op->getOperand(0).getType());
4402 auto rhsIntType = type_cast<IntType>(op->getOperand(1).getType());
4403 if (!lhsIntType.hasWidth() || !rhsIntType.hasWidth())
4407 if (cmpType.getWidth() == 0)
4408 cmpType = UIntType::get(builder.getContext(), 1);
4409 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), cmpType);
4410 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), cmpType);
4415 Type resultType = builder.getIntegerType(1);
4416 return setLoweringTo<comb::ICmpOp>(
4417 op, resultType, lhsIntType.isSigned() ? signedOp : unsignedOp, lhs, rhs,
4423template <
typename SignedOp,
typename Un
signedOp>
4424LogicalResult FIRRTLLowering::lowerDivLikeOp(Operation *op) {
4428 auto opType = type_cast<IntType>(op->getResult(0).getType());
4429 if (opType.getWidth() == 0)
4430 return setLowering(op->getResult(0), Value());
4434 auto lhs = getLoweredAndExtendedValue(op->getOperand(0), resultType);
4435 auto rhs = getLoweredAndExtendedValue(op->getOperand(1), resultType);
4440 if (opType.isSigned())
4441 result = builder.createOrFold<SignedOp>(lhs, rhs,
true);
4443 result = builder.createOrFold<UnsignedOp>(lhs, rhs,
true);
4445 if (
auto *definingOp = result.getDefiningOp())
4448 if (resultType == opType)
4449 return setLowering(op->getResult(0), result);
4450 return setLoweringTo<comb::ExtractOp>(op,
lowerType(opType), result, 0);
4453LogicalResult FIRRTLLowering::visitExpr(CatPrimOp op) {
4455 if (op.getInputs().empty())
4456 return setLowering(op, Value());
4458 SmallVector<Value> loweredOperands;
4461 for (
auto operand : op.getInputs()) {
4462 auto loweredOperand = getLoweredValue(operand);
4463 if (loweredOperand) {
4464 loweredOperands.push_back(loweredOperand);
4467 auto result =
handleZeroBit(operand, [&]() {
return success(); });
4475 if (loweredOperands.empty())
4476 return setLowering(op, Value());
4479 return setLoweringTo<comb::ConcatOp>(op, loweredOperands);
4486LogicalResult FIRRTLLowering::visitExpr(IsXIntrinsicOp op) {
4487 auto input = getLoweredNonClockValue(op.getArg());
4491 if (!isa<IntType>(input.getType())) {
4492 auto srcType = op.getArg().getType();
4494 assert(bitwidth &&
"Unknown width");
4495 auto intType = builder.getIntegerType(*bitwidth);
4496 input = builder.createOrFold<
hw::BitcastOp>(intType, input);
4499 return setLoweringTo<comb::ICmpOp>(
4500 op, ICmpPredicate::ceq, input,
4501 getOrCreateXConstant(input.getType().getIntOrFloatBitWidth()),
true);
4504LogicalResult FIRRTLLowering::visitStmt(FPGAProbeIntrinsicOp op) {
4505 auto operand = getLoweredValue(op.getInput());
4506 hw::WireOp::create(builder, operand);
4510LogicalResult FIRRTLLowering::visitExpr(PlusArgsTestIntrinsicOp op) {
4511 return setLoweringTo<sim::PlusArgsTestOp>(op, builder.getIntegerType(1),
4512 op.getFormatStringAttr());
4515LogicalResult FIRRTLLowering::visitExpr(PlusArgsValueIntrinsicOp op) {
4516 auto type =
lowerType(op.getResult().getType());
4520 auto valueOp = sim::PlusArgsValueOp::create(
4521 builder, builder.getIntegerType(1), type, op.getFormatStringAttr());
4522 if (failed(setLowering(op.getResult(), valueOp.getResult())))
4524 if (failed(setLowering(op.getFound(), valueOp.getFound())))
4529LogicalResult FIRRTLLowering::visitExpr(SizeOfIntrinsicOp op) {
4530 op.emitError(
"SizeOf should have been resolved.");
4534LogicalResult FIRRTLLowering::visitExpr(ClockGateIntrinsicOp op) {
4536 if (op.getTestEnable())
4537 testEnable = getLoweredValue(op.getTestEnable());
4538 return setLoweringTo<seq::ClockGateOp>(
4539 op, getLoweredValue(op.getInput()), getLoweredValue(op.getEnable()),
4540 testEnable, hw::InnerSymAttr{});
4543LogicalResult FIRRTLLowering::visitExpr(ClockInverterIntrinsicOp op) {
4544 auto operand = getLoweredValue(op.getInput());
4545 return setLoweringTo<seq::ClockInverterOp>(op, operand);
4548LogicalResult FIRRTLLowering::visitExpr(ClockDividerIntrinsicOp op) {
4549 auto operand = getLoweredValue(op.getInput());
4550 return setLoweringTo<seq::ClockDividerOp>(op, operand, op.getPow2());
4553LogicalResult FIRRTLLowering::visitExpr(LTLAndIntrinsicOp op) {
4554 return setLoweringToLTL<ltl::AndOp>(
4556 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4559LogicalResult FIRRTLLowering::visitExpr(LTLOrIntrinsicOp op) {
4560 return setLoweringToLTL<ltl::OrOp>(
4562 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4565LogicalResult FIRRTLLowering::visitExpr(LTLIntersectIntrinsicOp op) {
4566 return setLoweringToLTL<ltl::IntersectOp>(
4568 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4571LogicalResult FIRRTLLowering::visitExpr(LTLDelayIntrinsicOp op) {
4572 return setLoweringToLTL<ltl::DelayOp>(op, getLoweredValue(op.getInput()),
4573 op.getDelayAttr(), op.getLengthAttr());
4576LogicalResult FIRRTLLowering::visitExpr(LTLConcatIntrinsicOp op) {
4577 return setLoweringToLTL<ltl::ConcatOp>(
4579 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4582LogicalResult FIRRTLLowering::visitExpr(LTLRepeatIntrinsicOp op) {
4583 return setLoweringToLTL<ltl::RepeatOp>(op, getLoweredValue(op.getInput()),
4584 op.getBaseAttr(), op.getMoreAttr());
4587LogicalResult FIRRTLLowering::visitExpr(LTLGoToRepeatIntrinsicOp op) {
4588 return setLoweringToLTL<ltl::GoToRepeatOp>(
4589 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4592LogicalResult FIRRTLLowering::visitExpr(LTLNonConsecutiveRepeatIntrinsicOp op) {
4593 return setLoweringToLTL<ltl::NonConsecutiveRepeatOp>(
4594 op, getLoweredValue(op.getInput()), op.getBaseAttr(), op.getMoreAttr());
4597LogicalResult FIRRTLLowering::visitExpr(LTLNotIntrinsicOp op) {
4598 return setLoweringToLTL<ltl::NotOp>(op, getLoweredValue(op.getInput()));
4601LogicalResult FIRRTLLowering::visitExpr(LTLImplicationIntrinsicOp op) {
4602 return setLoweringToLTL<ltl::ImplicationOp>(
4604 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4607LogicalResult FIRRTLLowering::visitExpr(LTLUntilIntrinsicOp op) {
4608 return setLoweringToLTL<ltl::UntilOp>(
4610 ValueRange{getLoweredValue(op.getLhs()), getLoweredValue(op.getRhs())});
4613LogicalResult FIRRTLLowering::visitExpr(LTLEventuallyIntrinsicOp op) {
4614 return setLoweringToLTL<ltl::EventuallyOp>(op,
4615 getLoweredValue(op.getInput()));
4618LogicalResult FIRRTLLowering::visitExpr(LTLPastIntrinsicOp op) {
4621 clk = getLoweredNonClockValue(op.getClock());
4622 return setLoweringToLTL<ltl::PastOp>(op, getLoweredValue(op.getInput()),
4623 op.getDelayAttr(),
clk);
4626LogicalResult FIRRTLLowering::visitExpr(LTLClockIntrinsicOp op) {
4627 return setLoweringToLTL<ltl::ClockOp>(op, getLoweredValue(op.getInput()),
4628 ltl::ClockEdge::Pos,
4629 getLoweredNonClockValue(op.getClock()));
4632template <
typename TargetOp,
typename IntrinsicOp>
4633LogicalResult FIRRTLLowering::lowerVerifIntrinsicOp(IntrinsicOp op) {
4634 auto property = getLoweredValue(op.getProperty());
4635 auto enable = op.getEnable() ? getLoweredValue(op.getEnable()) : Value();
4636 TargetOp::create(builder, property, enable, op.getLabelAttr());
4640LogicalResult FIRRTLLowering::visitStmt(VerifAssertIntrinsicOp op) {
4641 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4644LogicalResult FIRRTLLowering::visitStmt(VerifAssumeIntrinsicOp op) {
4645 return lowerVerifIntrinsicOp<verif::AssumeOp>(op);
4648LogicalResult FIRRTLLowering::visitStmt(VerifCoverIntrinsicOp op) {
4649 return lowerVerifIntrinsicOp<verif::CoverOp>(op);
4652LogicalResult FIRRTLLowering::visitStmt(VerifRequireIntrinsicOp op) {
4653 if (!isa<verif::ContractOp>(op->getParentOp()))
4654 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4655 return lowerVerifIntrinsicOp<verif::RequireOp>(op);
4658LogicalResult FIRRTLLowering::visitStmt(VerifEnsureIntrinsicOp op) {
4659 if (!isa<verif::ContractOp>(op->getParentOp()))
4660 return lowerVerifIntrinsicOp<verif::AssertOp>(op);
4661 return lowerVerifIntrinsicOp<verif::EnsureOp>(op);
4664LogicalResult FIRRTLLowering::visitExpr(HasBeenResetIntrinsicOp op) {
4665 auto clock = getLoweredNonClockValue(op.getClock());
4666 auto reset = getLoweredValue(op.getReset());
4667 if (!clock || !reset)
4669 auto resetType = op.getReset().getType();
4670 auto uintResetType = dyn_cast<UIntType>(resetType);
4671 auto isSync = uintResetType && uintResetType.getWidth() == 1;
4672 auto isAsync = isa<AsyncResetType>(resetType);
4673 if (!isAsync && !isSync) {
4674 auto d = op.emitError(
"uninferred reset passed to 'has_been_reset'; "
4675 "requires sync or async reset");
4676 d.attachNote() <<
"reset is of type " << resetType
4677 <<
", should be '!firrtl.uint<1>' or '!firrtl.asyncreset'";
4680 return setLoweringTo<verif::HasBeenResetOp>(op, clock, reset, isAsync);
4687LogicalResult FIRRTLLowering::visitExpr(BitsPrimOp op) {
4688 auto input = getLoweredValue(op.getInput());
4692 Type resultType = builder.getIntegerType(op.getHi() - op.getLo() + 1);
4693 return setLoweringTo<comb::ExtractOp>(op, resultType, input, op.getLo());
4696LogicalResult FIRRTLLowering::visitExpr(InvalidValueOp op) {
4697 auto resultTy =
lowerType(op.getType());
4704 if (type_isa<AnalogType>(op.getType()))
4707 return setLoweringTo<sv::WireOp>(op, resultTy,
".invalid_analog");
4710 if (type_cast<FIRRTLBaseType>(op.getType()).containsAnalog())
4721 auto constant = getOrCreateIntConstant(*bitwidth, 0);
4723 if (!type_isa<IntegerType>(resultTy))
4725 return setLowering(op, constant);
4729 op.emitOpError(
"unsupported type");
4733LogicalResult FIRRTLLowering::visitExpr(HeadPrimOp op) {
4734 auto input = getLoweredValue(op.getInput());
4737 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4738 if (op.getAmount() == 0)
4739 return setLowering(op, Value());
4740 Type resultType = builder.getIntegerType(op.getAmount());
4741 return setLoweringTo<comb::ExtractOp>(op, resultType, input,
4742 inWidth - op.getAmount());
4745LogicalResult FIRRTLLowering::visitExpr(ShlPrimOp op) {
4746 auto input = getLoweredValue(op.getInput());
4749 if (op.getAmount() == 0)
4751 return setLowering(op, getOrCreateIntConstant(op.getAmount(), 0));
4756 if (op.getAmount() == 0)
4757 return setLowering(op, input);
4759 auto zero = getOrCreateIntConstant(op.getAmount(), 0);
4760 return setLoweringTo<comb::ConcatOp>(op, input, zero);
4763LogicalResult FIRRTLLowering::visitExpr(ShrPrimOp op) {
4764 auto input = getLoweredValue(op.getInput());
4769 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4770 auto shiftAmount = op.getAmount();
4771 if (shiftAmount >= inWidth) {
4773 if (type_cast<IntType>(op.getInput().getType()).isUnsigned())
4774 return setLowering(op, {});
4777 shiftAmount = inWidth - 1;
4780 Type resultType = builder.getIntegerType(inWidth - shiftAmount);
4781 return setLoweringTo<comb::ExtractOp>(op, resultType, input, shiftAmount);
4784LogicalResult FIRRTLLowering::visitExpr(TailPrimOp op) {
4785 auto input = getLoweredValue(op.getInput());
4789 auto inWidth = type_cast<IntegerType>(input.getType()).getWidth();
4790 if (inWidth == op.getAmount())
4791 return setLowering(op, Value());
4792 Type resultType = builder.getIntegerType(inWidth - op.getAmount());
4793 return setLoweringTo<comb::ExtractOp>(op, resultType, input, 0);
4796LogicalResult FIRRTLLowering::visitExpr(MuxPrimOp op) {
4797 auto cond = getLoweredValue(op.getSel());
4798 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4799 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4800 if (!cond || !ifTrue || !ifFalse)
4803 if (isa<ClockType>(op.getType()))
4804 return setLoweringTo<seq::ClockMuxOp>(op, cond, ifTrue, ifFalse);
4805 return setLoweringTo<comb::MuxOp>(op, ifTrue.getType(), cond, ifTrue, ifFalse,
4809LogicalResult FIRRTLLowering::visitExpr(Mux2CellIntrinsicOp op) {
4810 auto cond = getLoweredValue(op.getSel());
4811 auto ifTrue = getLoweredAndExtendedValue(op.getHigh(), op.getType());
4812 auto ifFalse = getLoweredAndExtendedValue(op.getLow(), op.getType());
4813 if (!cond || !ifTrue || !ifFalse)
4816 auto val = comb::MuxOp::create(builder, ifTrue.getType(), cond, ifTrue,
4818 return setLowering(op, createValueWithMuxAnnotation(val,
true));
4821LogicalResult FIRRTLLowering::visitExpr(Mux4CellIntrinsicOp op) {
4822 auto sel = getLoweredValue(op.getSel());
4823 auto v3 = getLoweredAndExtendedValue(op.getV3(), op.getType());
4824 auto v2 = getLoweredAndExtendedValue(op.getV2(), op.getType());
4825 auto v1 = getLoweredAndExtendedValue(op.getV1(), op.getType());
4826 auto v0 = getLoweredAndExtendedValue(op.getV0(), op.getType());
4827 if (!sel || !v3 || !v2 || !v1 || !v0)
4829 Value array[] = {v3, v2, v1, v0};
4832 return setLowering(op, createValueWithMuxAnnotation(val,
false));
4851Value FIRRTLLowering::createValueWithMuxAnnotation(Operation *op,
bool isMux2) {
4852 assert(op->getNumResults() == 1 &&
"only expect a single result");
4853 auto val = op->getResult(0);
4857 op, sv::SVAttributeAttr::get(builder.getContext(),
"cadence map_to_mux",
4864 OpBuilder::InsertionGuard guard(builder);
4865 builder.setInsertionPoint(op);
4866 StringRef namehint = isMux2 ?
"mux2cell_in" :
"mux4cell_in";
4867 for (
auto [idx, operand] :
llvm::enumerate(op->getOperands())) {
4869 op->getContext(),
nullptr, 0,
4872 hw::WireOp::create(builder, operand, namehint + Twine(idx), innerSym);
4873 op->setOperand(idx, wire);
4878 sv::setSVAttributes(assignOp,
4879 sv::SVAttributeAttr::get(builder.getContext(),
4880 "synopsys infer_mux_override",
4885Value FIRRTLLowering::createArrayIndexing(Value array, Value index) {
4887 auto size = hw::type_cast<hw::ArrayType>(array.getType()).getNumElements();
4892 if (!llvm::isPowerOf2_64(size)) {
4893 auto extElem = getOrCreateIntConstant(APInt(llvm::Log2_64_Ceil(size), 0));
4895 SmallVector<Value> temp(llvm::NextPowerOf2(size) - size, extValue);
4897 Value temp2[] = {ext.getResult(), array};
4903 return inBoundsRead;
4906LogicalResult FIRRTLLowering::visitExpr(MultibitMuxOp op) {
4908 auto index = getLoweredAndExtOrTruncValue(
4910 UIntType::get(op.getContext(),
4915 SmallVector<Value> loweredInputs;
4916 loweredInputs.reserve(op.getInputs().size());
4917 for (
auto input : op.getInputs()) {
4918 auto lowered = getLoweredAndExtendedValue(input, op.getType());
4921 loweredInputs.push_back(lowered);
4925 return setLowering(op, createArrayIndexing(array, index));
4928LogicalResult FIRRTLLowering::visitExpr(VerbatimExprOp op) {
4929 auto resultTy =
lowerType(op.getType());
4933 SmallVector<Value, 4> operands;
4934 operands.reserve(op.getSubstitutions().size());
4935 for (
auto operand : op.getSubstitutions()) {
4936 auto lowered = getLoweredValue(operand);
4939 operands.push_back(lowered);
4942 ArrayAttr symbols = op.getSymbolsAttr();
4944 symbols = ArrayAttr::get(op.getContext(), {});
4946 return setLoweringTo<sv::VerbatimExprOp>(op, resultTy, op.getTextAttr(),
4950LogicalResult FIRRTLLowering::visitExpr(XMRRefOp op) {
4954 Type baseType = op.getType().getType();
4957 if (isa<ClockType>(baseType))
4958 xmrType = builder.getIntegerType(1);
4962 return setLoweringTo<sv::XMRRefOp>(op, sv::InOutType::get(xmrType),
4963 op.getRef(), op.getVerbatimSuffixAttr());
4966LogicalResult FIRRTLLowering::visitExpr(XMRDerefOp op) {
4970 if (isa<ClockType>(op.getType()))
4971 xmrType = builder.getIntegerType(1);
4975 auto xmr = sv::XMRRefOp::create(builder, sv::InOutType::get(xmrType),
4976 op.getRef(), op.getVerbatimSuffixAttr());
4977 auto readXmr = getReadValue(xmr);
4978 if (!isa<ClockType>(op.getType()))
4979 return setLowering(op, readXmr);
4980 return setLoweringTo<seq::ToClockOp>(op, readXmr);
4985LogicalResult FIRRTLLowering::visitExpr(TimeOp op) {
return success(); }
4986LogicalResult FIRRTLLowering::visitExpr(HierarchicalModuleNameOp op) {
4994LogicalResult FIRRTLLowering::visitStmt(SkipOp op) {
5006FailureOr<bool> FIRRTLLowering::lowerConnect(Value destVal, Value srcVal) {
5007 auto srcType = srcVal.getType();
5008 auto dstType = destVal.getType();
5009 if (srcType != dstType &&
5010 (isa<hw::TypeAliasType>(srcType) || isa<hw::TypeAliasType>(dstType))) {
5013 return TypeSwitch<Operation *, FailureOr<bool>>(destVal.getDefiningOp())
5014 .Case<hw::WireOp>([&](
auto op) {
5015 maybeUnused(op.getInput());
5016 op.getInputMutable().assign(srcVal);
5019 .Case<seq::FirRegOp>([&](
auto op) {
5020 maybeUnused(op.getNext());
5021 op.getNextMutable().assign(srcVal);
5024 .Case<hw::StructExtractOp, hw::ArrayGetOp>([](
auto op) {
5027 op.emitOpError(
"used as connect destination");
5030 .Default([](
auto) {
return false; });
5033LogicalResult FIRRTLLowering::visitStmt(ConnectOp op) {
5034 auto dest = op.getDest();
5036 auto destType = type_cast<FIRRTLBaseType>(dest.getType()).getPassiveType();
5037 auto srcVal = getLoweredAndExtendedValue(op.getSrc(), destType);
5039 return handleZeroBit(op.getSrc(), []() { return success(); });
5041 auto destVal = getPossiblyInoutLoweredValue(dest);
5045 auto result = lowerConnect(destVal, srcVal);
5053 if (updateIfBackedge(destVal, srcVal))
5056 if (!isa<hw::InOutType>(destVal.getType()))
5057 return op.emitError(
"destination isn't an inout type");
5063LogicalResult FIRRTLLowering::visitStmt(MatchingConnectOp op) {
5064 auto dest = op.getDest();
5065 auto srcVal = getLoweredValue(op.getSrc());
5067 return handleZeroBit(op.getSrc(), []() { return success(); });
5069 auto destVal = getPossiblyInoutLoweredValue(dest);
5073 auto result = lowerConnect(destVal, srcVal);
5081 if (updateIfBackedge(destVal, srcVal))
5084 if (!isa<hw::InOutType>(destVal.getType()))
5085 return op.emitError(
"destination isn't an inout type");
5091LogicalResult FIRRTLLowering::visitStmt(ForceOp op) {
5092 auto srcVal = getLoweredValue(op.getSrc());
5096 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5100 if (!isa<hw::InOutType>(destVal.getType()))
5101 return op.emitError(
"destination isn't an inout type");
5104 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5105 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5106 addToInitialBlock([&]() { sv::ForceOp::create(builder, destVal, srcVal); });
5111LogicalResult FIRRTLLowering::visitStmt(RefForceOp op) {
5112 auto src = getLoweredNonClockValue(op.getSrc());
5113 auto clock = getLoweredNonClockValue(op.getClock());
5114 auto pred = getLoweredValue(op.getPredicate());
5115 if (!src || !clock || !pred)
5118 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5123 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5124 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5125 addToAlwaysBlock(clock, [&]() {
5126 addIfProceduralBlock(
5127 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5132LogicalResult FIRRTLLowering::visitStmt(RefForceInitialOp op) {
5133 auto src = getLoweredNonClockValue(op.getSrc());
5134 auto pred = getLoweredValue(op.getPredicate());
5138 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5143 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5144 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5145 addToInitialBlock([&]() {
5146 addIfProceduralBlock(
5147 pred, [&]() { sv::ForceOp::create(builder, destVal, src); });
5152LogicalResult FIRRTLLowering::visitStmt(RefReleaseOp op) {
5153 auto clock = getLoweredNonClockValue(op.getClock());
5154 auto pred = getLoweredValue(op.getPredicate());
5155 if (!clock || !pred)
5158 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5163 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5164 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5165 addToAlwaysBlock(clock, [&]() {
5166 addIfProceduralBlock(pred,
5167 [&]() { sv::ReleaseOp::create(builder, destVal); });
5172LogicalResult FIRRTLLowering::visitStmt(RefReleaseInitialOp op) {
5173 auto destVal = getPossiblyInoutLoweredValue(op.getDest());
5174 auto pred = getLoweredValue(op.getPredicate());
5175 if (!destVal || !pred)
5179 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5180 addToIfDefBlock(
"SYNTHESIS", std::function<
void()>(), [&]() {
5181 addToInitialBlock([&]() {
5182 addIfProceduralBlock(pred,
5183 [&]() { sv::ReleaseOp::create(builder, destVal); });
5191 StringRef originalFormatString,
5192 ValueRange operands,
5193 StringAttr &result) {
5196 SmallString<32> formatString;
5197 for (
size_t i = 0, e = originalFormatString.size(), subIdx = 0; i != e; ++i) {
5198 char c = originalFormatString[i];
5202 formatString.push_back(c);
5205 SmallString<6> width;
5206 c = originalFormatString[++i];
5209 c = originalFormatString[++i];
5220 formatString.append(width);
5226 formatString.push_back(c);
5233 if (originalFormatString.slice(i, i + 4) !=
"{{}}") {
5234 formatString.push_back(c);
5238 auto substitution = operands[subIdx++];
5239 assert(type_isa<FStringType>(substitution.getType()) &&
5240 "the operand for a '{{}}' substitution must be an 'fstring' type");
5242 TypeSwitch<Operation *, LogicalResult>(substitution.getDefiningOp())
5243 .template Case<TimeOp>([&](
auto) {
5244 formatString.append(
"%0t");
5247 .
template Case<HierarchicalModuleNameOp>([&](
auto) {
5248 formatString.append(
"%m");
5251 .Default([&](
auto) {
5252 emitError(loc,
"has a substitution with an unimplemented "
5254 .attachNote(substitution.getLoc())
5255 <<
"op with an unimplemented lowering is here";
5265 formatString.push_back(c);
5269 result = StringAttr::get(loc->getContext(), formatString);
5276LogicalResult FIRRTLLowering::visitPrintfLike(
5277 T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond) {
5278 auto clock = getLoweredNonClockValue(op.getClock());
5279 auto cond = getLoweredValue(op.getCond());
5280 if (!clock || !cond)
5283 StringAttr formatString;
5285 op.getSubstitutions(), formatString)))
5288 auto fn = [&](Value fd) {
5289 SmallVector<Value> operands;
5290 if (failed(loweredFmtOperands(op.getSubstitutions(), operands)))
5292 sv::FWriteOp::create(builder, op.getLoc(), fd, formatString, operands);
5296 return lowerStatementWithFd(fileDescriptorInfo, clock, cond, fn,
5300LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
5301 StringAttr outputFileAttr;
5303 op.getOutputFileSubstitutions(),
5307 FileDescriptorInfo outputFile(outputFileAttr,
5308 op.getOutputFileSubstitutions());
5309 return visitPrintfLike(op, outputFile,
false);
5313LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
5314 auto clock = getLoweredNonClockValue(op.getClock());
5315 auto cond = getLoweredValue(op.getCond());
5316 if (!clock || !cond)
5319 auto fn = [&](Value fd) {
5320 sv::FFlushOp::create(builder, op.getLoc(), fd);
5324 if (!op.getOutputFileAttr())
5325 return lowerStatementWithFd({}, clock, cond, fn,
false);
5329 StringAttr outputFileAttr;
5331 op.getOutputFileSubstitutions(),
5335 return lowerStatementWithFd(
5336 FileDescriptorInfo(outputFileAttr, op.getOutputFileSubstitutions()),
5337 clock, cond, fn,
false);
5342LogicalResult FIRRTLLowering::visitStmt(StopOp op) {
5343 auto clock = getLoweredValue(op.getClock());
5344 auto cond = getLoweredValue(op.getCond());
5345 if (!clock || !cond)
5348 circuitState.usedStopCond =
true;
5349 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5352 sv::MacroRefExprOp::create(builder, cond.getType(),
"STOP_COND_");
5353 Value exitCond = builder.createOrFold<
comb::AndOp>(stopCond, cond,
true);
5355 sim::ClockedTerminateOp::create(builder, clock, exitCond,
5356 op.getExitCode() == 0,
5365template <
typename... Args>
5367 StringRef opName, Args &&...args) {
5368 if (opName ==
"assert")
5369 return sv::AssertOp::create(builder, std::forward<Args>(args)...);
5370 if (opName ==
"assume")
5371 return sv::AssumeOp::create(builder, std::forward<Args>(args)...);
5372 if (opName ==
"cover")
5373 return sv::CoverOp::create(builder, std::forward<Args>(args)...);
5374 llvm_unreachable(
"unknown verification op");
5380template <
typename... Args>
5382 StringRef opName, Args &&...args) {
5383 if (opName ==
"assert")
5384 return sv::AssertConcurrentOp::create(builder, std::forward<Args>(args)...);
5385 if (opName ==
"assume")
5386 return sv::AssumeConcurrentOp::create(builder, std::forward<Args>(args)...);
5387 if (opName ==
"cover")
5388 return sv::CoverConcurrentOp::create(builder, std::forward<Args>(args)...);
5389 llvm_unreachable(
"unknown verification op");
5410LogicalResult FIRRTLLowering::lowerVerificationStatement(
5411 Operation *op, StringRef labelPrefix, Value opClock, Value opPredicate,
5412 Value opEnable, StringAttr opMessageAttr, ValueRange opOperands,
5413 StringAttr opNameAttr,
bool isConcurrent, EventControl opEventControl) {
5414 StringRef opName = op->getName().stripDialect();
5417 ArrayRef<Attribute> guards{};
5418 if (
auto guardsAttr = op->template getAttrOfType<ArrayAttr>(
"guards"))
5419 guards = guardsAttr.getValue();
5421 auto isCover = isa<CoverOp>(op);
5422 auto clock = getLoweredNonClockValue(opClock);
5423 auto enable = getLoweredValue(opEnable);
5424 auto predicate = getLoweredValue(opPredicate);
5425 if (!clock || !enable || !predicate)
5429 if (opNameAttr && !opNameAttr.getValue().empty())
5431 StringAttr prefixedLabel;
5434 StringAttr::get(builder.getContext(), labelPrefix + label.getValue());
5437 SmallVector<Value> messageOps;
5441 if (flavor == VerificationFlavor::IfElseFatal && !isa<AssertOp>(op))
5442 flavor = VerificationFlavor::None;
5444 if (flavor == VerificationFlavor::None) {
5448 auto format = op->getAttrOfType<StringAttr>(
"format");
5450 if (isConcurrent && format && format.getValue() ==
"ifElseFatal") {
5451 if (!isa<AssertOp>(op))
5452 return op->emitError()
5453 <<
"ifElseFatal format cannot be used for non-assertions";
5454 flavor = VerificationFlavor::IfElseFatal;
5455 }
else if (isConcurrent)
5456 flavor = VerificationFlavor::SVA;
5458 flavor = VerificationFlavor::Immediate;
5461 if (!isCover && opMessageAttr && !opMessageAttr.getValue().empty()) {
5465 opOperands, message)))
5468 if (failed(loweredFmtOperands(opOperands, messageOps)))
5471 if (flavor == VerificationFlavor::SVA) {
5476 for (
auto &loweredValue : messageOps)
5477 loweredValue =
sv::SampledOp::create(builder, loweredValue);
5483 case VerificationFlavor::Immediate: {
5485 auto deferImmediate = circt::sv::DeferAssertAttr::get(
5486 builder.getContext(), circt::sv::DeferAssert::Immediate);
5487 addToAlwaysBlock(clock, [&]() {
5488 addIfProceduralBlock(enable, [&]() {
5490 prefixedLabel, message, messageOps);
5495 case VerificationFlavor::IfElseFatal: {
5496 assert(isa<AssertOp>(op) &&
"only assert is expected");
5499 auto boolType = IntegerType::get(builder.getContext(), 1);
5500 predicate = comb::createOrFoldNot(builder, predicate,
true);
5501 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5503 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5504 addToIfDefBlock(
"SYNTHESIS", {}, [&]() {
5505 addToAlwaysBlock(clock, [&]() {
5506 addIfProceduralBlock(predicate, [&]() {
5507 circuitState.usedStopCond =
true;
5508 circuitState.addFragment(theModule,
"STOP_COND_FRAGMENT");
5510 circuitState.usedAssertVerboseCond =
true;
5511 circuitState.addFragment(theModule,
"ASSERT_VERBOSE_COND_FRAGMENT");
5513 addIfProceduralBlock(
5514 sv::MacroRefExprOp::create(builder, boolType,
5515 "ASSERT_VERBOSE_COND_"),
5517 sv::ErrorProceduralOp::create(builder, message, messageOps);
5519 addIfProceduralBlock(
5520 sv::MacroRefExprOp::create(builder, boolType,
"STOP_COND_"),
5521 [&]() { sv::FatalProceduralOp::create(builder); });
5527 case VerificationFlavor::SVA: {
5532 comb::createOrFoldNot(builder, enable,
true);
5534 builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5536 predicate = builder.createOrFold<
comb::AndOp>(enable, predicate,
true);
5540 sv::EventControl event;
5541 switch (opEventControl) {
5542 case EventControl::AtPosEdge:
5543 event = circt::sv::EventControl::AtPosEdge;
5545 case EventControl::AtEdge:
5546 event = circt::sv::EventControl::AtEdge;
5548 case EventControl::AtNegEdge:
5549 event = circt::sv::EventControl::AtNegEdge;
5555 circt::sv::EventControlAttr::get(builder.getContext(), event), clock,
5556 predicate, prefixedLabel, message, messageOps);
5559 case VerificationFlavor::None:
5561 "flavor `None` must be converted into one of concreate flavors");
5568 return emitGuards(op->getLoc(), guards,
emit);
5572LogicalResult FIRRTLLowering::visitStmt(AssertOp op) {
5573 return lowerVerificationStatement(
5574 op,
"assert__", op.getClock(), op.getPredicate(), op.getEnable(),
5575 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5576 op.getIsConcurrent(), op.getEventControl());
5580LogicalResult FIRRTLLowering::visitStmt(AssumeOp op) {
5581 return lowerVerificationStatement(
5582 op,
"assume__", op.getClock(), op.getPredicate(), op.getEnable(),
5583 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5584 op.getIsConcurrent(), op.getEventControl());
5588LogicalResult FIRRTLLowering::visitStmt(CoverOp op) {
5589 return lowerVerificationStatement(
5590 op,
"cover__", op.getClock(), op.getPredicate(), op.getEnable(),
5591 op.getMessageAttr(), op.getSubstitutions(), op.getNameAttr(),
5592 op.getIsConcurrent(), op.getEventControl());
5596LogicalResult FIRRTLLowering::visitStmt(UnclockedAssumeIntrinsicOp op) {
5601 auto guardsAttr = op->getAttrOfType<mlir::ArrayAttr>(
"guards");
5602 ArrayRef<Attribute> guards =
5603 guardsAttr ? guardsAttr.getValue() : ArrayRef<Attribute>();
5605 auto label = op.getNameAttr();
5606 StringAttr assumeLabel;
5607 if (label && !label.empty())
5609 StringAttr::get(builder.getContext(),
"assume__" + label.getValue());
5610 auto predicate = getLoweredValue(op.getPredicate());
5611 auto enable = getLoweredValue(op.getEnable());
5612 auto notEnable = comb::createOrFoldNot(builder, enable,
true);
5613 predicate = builder.createOrFold<
comb::OrOp>(notEnable, predicate,
true);
5615 SmallVector<Value> messageOps;
5616 for (
auto operand : op.getSubstitutions()) {
5617 auto loweredValue = getLoweredValue(operand);
5618 if (!loweredValue) {
5622 loweredValue = getOrCreateIntConstant(1, 0);
5624 messageOps.push_back(loweredValue);
5626 return emitGuards(op.getLoc(), guards, [&]() {
5627 sv::AlwaysOp::create(
5628 builder, ArrayRef(sv::EventControl::AtEdge), ArrayRef(predicate),
5630 if (op.getMessageAttr().getValue().empty())
5631 buildImmediateVerifOp(
5632 builder,
"assume", predicate,
5633 circt::sv::DeferAssertAttr::get(
5634 builder.getContext(), circt::sv::DeferAssert::Immediate),
5637 buildImmediateVerifOp(
5638 builder,
"assume", predicate,
5639 circt::sv::DeferAssertAttr::get(
5640 builder.getContext(), circt::sv::DeferAssert::Immediate),
5641 assumeLabel, op.getMessageAttr(), messageOps);
5646LogicalResult FIRRTLLowering::visitStmt(AttachOp op) {
5648 if (op.getAttached().size() < 2)
5651 SmallVector<Value, 4> inoutValues;
5652 for (
auto v : op.getAttached()) {
5653 inoutValues.push_back(getPossiblyInoutLoweredValue(v));
5654 if (!inoutValues.back()) {
5658 inoutValues.pop_back();
5662 if (!isa<hw::InOutType>(inoutValues.back().getType()))
5663 return op.emitError(
"operand isn't an inout type");
5666 if (inoutValues.size() < 2)
5677 bool isAttachInternalOnly =
5678 llvm::none_of(inoutValues, [](
auto v) {
return isa<BlockArgument>(v); });
5680 if (isAttachInternalOnly) {
5681 auto v0 = inoutValues.front();
5682 for (
auto v : inoutValues) {
5685 v.replaceAllUsesWith(v0);
5692 circuitState.addMacroDecl(builder.getStringAttr(
"SYNTHESIS"));
5693 circuitState.addMacroDecl(builder.getStringAttr(
"VERILATOR"));
5698 SmallVector<Value, 4> values;
5699 for (
auto inoutValue : inoutValues)
5700 values.push_back(getReadValue(inoutValue));
5702 for (
size_t i1 = 0, e = inoutValues.size(); i1 != e; ++i1) {
5703 for (
size_t i2 = 0; i2 != e; ++i2)
5711 sv::IfDefOp::create(
5712 builder,
"VERILATOR",
5714 sv::VerbatimOp::create(
5716 "`error \"Verilator does not support alias and thus "
5718 "arbitrarily connect bidirectional wires and ports\"");
5720 [&]() { sv::AliasOp::create(builder, inoutValues); });
5726LogicalResult FIRRTLLowering::visitStmt(BindOp op) {
5727 sv::BindOp::create(builder, op.getInstanceAttr());
5731LogicalResult FIRRTLLowering::fixupLTLOps() {
5732 if (ltlOpFixupWorklist.empty())
5734 LLVM_DEBUG(llvm::dbgs() <<
"Fixing up " << ltlOpFixupWorklist.size()
5738 for (
unsigned i = 0, e = ltlOpFixupWorklist.size(); i != e; ++i)
5739 for (
auto *user : ltlOpFixupWorklist[i]->getUsers())
5740 if (isa<
hw::WireOp>(user))
5741 ltlOpFixupWorklist.insert(user);
5744 while (!ltlOpFixupWorklist.empty()) {
5745 auto *op = ltlOpFixupWorklist.pop_back_val();
5748 if (
auto opIntf = dyn_cast_or_null<mlir::InferTypeOpInterface>(op)) {
5749 LLVM_DEBUG(llvm::dbgs() <<
"- Update " << *op <<
"\n");
5750 SmallVector<Type, 2> types;
5751 auto result = opIntf.inferReturnTypes(
5752 op->getContext(), op->getLoc(), op->getOperands(),
5753 op->getAttrDictionary(), op->getPropertiesStorage(), op->getRegions(),
5757 assert(types.size() == op->getNumResults());
5761 for (
auto [result, type] :
llvm::zip(op->getResults(), types)) {
5762 if (result.getType() == type)
5764 LLVM_DEBUG(llvm::dbgs()
5765 <<
" - Result #" << result.getResultNumber() <<
" from "
5766 << result.getType() <<
" to " << type <<
"\n");
5767 result.setType(type);
5768 for (
auto *user : result.getUsers())
5770 ltlOpFixupWorklist.insert(user);
5775 if (
auto wireOp = dyn_cast<hw::WireOp>(op)) {
5776 if (isa<ltl::SequenceType, ltl::PropertyType>(wireOp.getType())) {
5777 wireOp.replaceAllUsesWith(wireOp.getInput());
5778 LLVM_DEBUG(llvm::dbgs() <<
"- Remove " << wireOp <<
"\n");
5779 if (wireOp.use_empty())
5786 SmallPtrSet<Operation *, 4> usersReported;
5787 for (
auto *user : op->getUsers()) {
5788 if (!usersReported.insert(user).second)
5790 if (isa_and_nonnull<ltl::LTLDialect, verif::VerifDialect>(
5791 user->getDialect()))
5793 if (isa<hw::WireOp>(user))
5795 auto d = op->emitError(
5796 "verification operation used in a non-verification context");
5797 d.attachNote(user->getLoc())
5798 <<
"leaking outside verification context here";
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static LogicalResult emitFile(ArrayRef< Operation * > operations, StringRef filePath, raw_ostream &os)
Emits the given operation to a file represented by the passed ostream and file-path.
static void lowerModuleBody(FModuleOp mod, const DenseMap< StringAttr, PortConversion > &ports)
static Operation * buildImmediateVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build an immediate assert operation based on the original FIRRTL operation name.
static Operation * buildConcurrentVerifOp(ImplicitLocOpBuilder &builder, StringRef opName, Args &&...args)
Helper function to build a concurrent assert operation based on the original FIRRTL operation name.
static unsigned getBitWidthFromVectorSize(unsigned size)
static Value castToFIRRTLType(Value val, Type type, ImplicitLocOpBuilder &builder)
Cast a value to a desired target type.
static ArrayAttr getHWParameters(FExtModuleOp module, bool ignoreValues)
Map the parameter specifier on the specified extmodule into the HWModule representation for parameter...
static bool isZeroBitFIRRTLType(Type type)
Return true if the specified type is a sized FIRRTL type (Int or Analog) with zero bits.
static Value tryEliminatingAttachesToAnalogValue(Value value, Operation *insertPoint)
Given a value of analog type, check to see the only use of it is an attach.
static LogicalResult handleZeroBit(Value failedOperand, const std::function< LogicalResult()> &fn)
Zero bit operands end up looking like failures from getLoweredValue.
static const char moduleHierarchyFileAttrName[]
Attribute that indicates that the module hierarchy starting at the annotated module should be dumped ...
static 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)
This is the pass constructor.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
The namespace of a CircuitOp, generally inhabited by modules.
This holds the name and type that describes the module's ports.
bool isOutput() const
Return true if this is a simple output-only port.
AnnotationSet annotations
bool isInput() const
Return true if this is a simple input-only port.
This holds the name, type, direction of a module's ports.
size_t argNum
This is the argument index or the result index depending on the direction.
void setSym(InnerSymAttr sym, MLIRContext *ctx)
InnerSymAttr getSym() const